diff options
1518 files changed, 120038 insertions, 97654 deletions
diff --git a/.gitignore b/.gitignore index 6eb3b0a398..ebbd0838c4 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ ipch/ *.sdf *.opensdf -/options.gypi +/config.mk +/config.gypi *-nodegyp* /gyp-mac-tool /dist-osx @@ -261,8 +261,8 @@ Brandon Benvie <brandon@bbenvie.com> Nicolas LaCasse <nlacasse@borderstylo.com> Dan VerWeire <dverweire@gmail.com> Matthew Fitzsimmons <matt@joyent.com> -Paddy Byers <paddy.byers@gmail.com> Philip Tellis <philip.tellis@gmail.com> Christopher Jeffrey <chjjeffrey@gmail.com> +Paddy Byers <paddy.byers@gmail.com> Seth Fitzsimmons <seth@mojodna.net> Einar Otto Stangvik <einaros@gmail.com> @@ -1,4 +1,103 @@ -2012.02.17 Version 0.6.11 (stable) +2012.02.14, Version 0.7.4 (unstable) + +* Upgrade V8 to 3.9.5 + +* Upgrade npm to 1.1.1 + +* build: Detect host_arch better (Karl Skomski) + +* debugger: export `debug_port` to `process` (Fedor Indutny) + +* api docs: CSS bug fixes (isaacs) + +* build: use -fPIC for native addons on UNIX (Nathan Rajlich) + +* Re-add top-level v8::Locker (Marcel Laverdet) + +* Move images out of the dist tarballs (isaacs) + +* libuv: Remove uv_export and uv_import (Ben Noordhuis) + +* build: Support x64 build on Windows (Igor Zinkovsky) + + +2012.02.07, Version 0.7.3 (unstable), 99059aad8d654acda4abcfaa68df182b50f2ec90 + +* Upgrade V8 to 3.9.2 + +* Revert support for isolates. (Ben Noordhuis) + +* cluster: Cleanup docs, event handling, and process.disconnect (Andreas Madsen) + +* gyp_addon: link with node.lib on Windows (Nathan Rajlich) + +* http: fix case where http-parser is freed twice (koichik) + +* Windows: disable RTTI and exceptions (Bert Belder) + + +2012.02.01, Version 0.7.2 (unstable), ec79acb3a6166e30f0bf271fbbfda1fb575b3321 + +* Update V8 to 3.8.9 + +* Support for sharing streams across Isolates (Igor Zinkovsky) + +* #2636 - Fix case where http_parsers are freed too early (koichik) + +* url: Support for IPv6 addresses in URLs (Åukasz Walukiewicz) + +* child_process: Add disconnect() method to child processes (Andreas Madsen) + +* fs: add O_EXCL support, exclusive open file (Ben Noordhuis) + +* fs: more specific error messages (Tj Holowaychuk) + +* tty: emit 'unknown' key event if key sequence not found (Dan VerWeire, Nathan Rajlich) + +* build: compile release build too if BUILDTYPE=Debug (Ben Noordhuis) + +* module: fix --debug-brk on symlinked scripts (Fedor Indutny) + +* zlib: fix `Failed to set dictionary` issue (Fedor Indutny) + +* waf: predict target arch for OS X (Fedor Indutny) + + +2012.01.23, Version 0.7.1 (unstable), a74354735ab5d5b0fa35a1e4ff7e653757d2069b + +* Update V8 to 3.8.8 + +* Install node-waf by default (Fedor Indutny) + +* crypto: Add ability to turn off PKCS padding (Ingmar Runge) + +* v8: implement VirtualMemory class on SunOS (Ben Noordhuis) + +* Add cluster.setupMaster (Andreas Madsen) + +* move `path.exists*` to `fs.exists*` (Maciej MaÅ‚ecki) + +* typed arrays: set class name (Ben Noordhuis) + +* libuv bug fixes (Igor Zinkovsky, Ben Noordhuis, Dan VerWeire) + + +2012.01.16, Version 0.7.0 (unstable), 9cc55dca6f67a6096c858b841c677b0593404321 + +* Upgrade V8 to 3.8.6 + +* Use GYP build system on unix (Ben Noordhuis) + +* Experimenetal isolates support (Ben Noordhuis) + +* Improvements to Cluster API (Andreas Madsen) + +* Use isolates for internal debugger (Fedor Indutny) + +* Bug fixes + + +2012.02.17 Version 0.6.11 (stable), 1eb1fe32250fc88cb5b0a97cddf3e02be02e3f4a * http: allow multiple WebSocket RFC6455 headers (Einar Otto Stangvik) @@ -1,36 +1,51 @@ +-include config.mk + +BUILDTYPE ?= Release PYTHON ?= python -WAF = $(PYTHON) tools/waf-light -web_root = node@nodejs.org:~/web/nodejs.org/ +# BUILDTYPE=Debug builds both release and debug builds. If you want to compile +# just the debug build, run `make -C out BUILDTYPE=Debug` instead. +ifeq ($(BUILDTYPE),Release) +all: out/Makefile node +else +all: out/Makefile node node_g +endif -# -# Because we recursively call make from waf we need to make sure that we are -# using the correct make. Not all makes are GNU Make, but this likely only -# works with gnu make. To deal with this we remember how the user invoked us -# via a make builtin variable and use that in all subsequent operations -# -export NODE_MAKE := $(MAKE) +# The .PHONY is needed to ensure that we recursively use the out/Makefile +# to check for changes. +.PHONY: node node_g -all: program - @-[ -f out/Release/node ] && ls -lh out/Release/node +node: config.gypi + $(MAKE) -C out BUILDTYPE=Release + ln -fs out/Release/node node -all-progress: - @$(WAF) -p build +node_g: config.gypi + $(MAKE) -C out BUILDTYPE=Debug + ln -fs out/Debug/node node_g -program: - @$(WAF) --product-type=program build +config.gypi: configure + ./configure -staticlib: - @$(WAF) --product-type=cstaticlib build +out/Debug/node: + $(MAKE) -C out BUILDTYPE=Debug -dynamiclib: - @$(WAF) --product-type=cshlib build +out/Makefile: common.gypi deps/uv/uv.gyp deps/http_parser/http_parser.gyp deps/zlib/zlib.gyp deps/v8/build/common.gypi deps/v8/tools/gyp/v8.gyp node.gyp config.gypi + tools/gyp_node -f make -install: - @$(WAF) install +install: all + out/Release/node tools/installer.js ./config.gypi install uninstall: - @$(WAF) uninstall + out/Release/node tools/installer.js ./config.gypi uninstall + +clean: + -rm -rf out/Makefile node node_g out/$(BUILDTYPE)/node + -find out/ -name '*.o' -o -name '*.a' | xargs rm -rf + +distclean: + -rm -rf out + -rm -f config.gypi + -rm -f config.mk test: all $(PYTHON) tools/test.py --mode=release simple message @@ -42,8 +57,8 @@ test-valgrind: all $(PYTHON) tools/test.py --mode=release --valgrind simple message test-all: all - $(PYTHON) tools/test.py --mode=debug,release - make test-npm + python tools/test.py --mode=debug,release + $(MAKE) test-npm test-all-http1: all $(PYTHON) tools/test.py --mode=debug,release --use-http1 @@ -69,21 +84,21 @@ test-pummel: all test-internet: all $(PYTHON) tools/test.py internet -test-npm: all +test-npm: node ./node deps/npm/test/run.js -test-npm-publish: all +test-npm-publish: node npm_package_config_publishtest=true ./node deps/npm/test/run.js -out/Release/node: all - apidoc_sources = $(wildcard doc/api/*.markdown) apidocs = $(addprefix out/,$(apidoc_sources:.markdown=.html)) -apidoc_dirs = out/doc out/doc/api/ out/doc/api/assets out/doc/about out/doc/community out/doc/logos +apidoc_dirs = out/doc out/doc/api/ out/doc/api/assets out/doc/about out/doc/community out/doc/logos out/doc/images apiassets = $(subst api_assets,api/assets,$(addprefix out/,$(wildcard doc/api_assets/*))) +doc_images = $(addprefix out/,$(wildcard doc/images/* doc/*.jpg doc/*.png)) + website_files = \ out/doc/index.html \ out/doc/v0.4_announcement.html \ @@ -92,35 +107,14 @@ website_files = \ out/doc/sh_javascript.min.js \ out/doc/sh_vim-dark.css \ out/doc/sh.css \ - out/doc/logo.png \ out/doc/favicon.ico \ out/doc/pipe.css \ out/doc/about/index.html \ - out/doc/close-downloads.png \ out/doc/community/index.html \ - out/doc/community/not-invented-here.png \ out/doc/logos/index.html \ - out/doc/microsoft-logo.png \ - out/doc/ryan-speaker.jpg \ - out/doc/download-logo.png \ - out/doc/ebay-logo.png \ - out/doc/footer-logo-alt.png \ - out/doc/footer-logo.png \ - out/doc/icons-interior.png \ - out/doc/icons.png \ - out/doc/home-icons.png \ - out/doc/joyent-logo_orange_nodeorg-01.png \ - out/doc/linkedin-logo.png \ - out/doc/logo-light.png \ - out/doc/mac_osx_nodejs_installer_logo.png \ - out/doc/microsoft-logo.png \ - out/doc/platform-icons.png \ - out/doc/sponsored.png \ - out/doc/twitter-bird.png \ - out/doc/community-icons.png \ - out/doc/yahoo-logo.png - -doc docs: out/Release/node $(apidoc_dirs) $(website_files) $(apiassets) $(apidocs) + $(doc_images) + +doc: node $(apidoc_dirs) $(website_files) $(apiassets) $(apidocs) $(apidoc_dirs): mkdir -p $@ @@ -129,9 +123,9 @@ out/doc/api/assets/%: doc/api_assets/% out/doc/api/assets/ cp $< $@ out/doc/%: doc/% - cp $< $@ + cp -r $< $@ -out/doc/api/%.html: doc/api/%.markdown out/Release/node $(apidoc_dirs) $(apiassets) tools/doctool/doctool.js +out/doc/api/%.html: doc/api/%.markdown node $(apidoc_dirs) $(apiassets) tools/doctool/doctool.js out/Release/node tools/doctool/doctool.js doc/template.html $< > $@ out/doc/%: @@ -145,27 +139,13 @@ docopen: out/doc/api/all.html docclean: -rm -rf out/doc -clean: - $(WAF) clean - -find tools -name "*.pyc" | xargs rm -f - -distclean: docclean - -find tools -name "*.pyc" | xargs rm -f - -rm -rf dist-osx - -rm -rf out/ node node_g - -check: - @tools/waf-light check - VERSION=v$(shell $(PYTHON) tools/getnodeversion.py) TARNAME=node-$(VERSION) TARBALL=$(TARNAME).tar.gz PKG=out/$(TARNAME).pkg - packagemaker=/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker -#dist: doc/node.1 doc/api -dist: $(TARBALL) $(PKG) +dist: doc $(TARBALL) $(PKG) PKGDIR=out/dist-osx @@ -173,20 +153,29 @@ pkg: $(PKG) $(PKG): -rm -rf $(PKGDIR) - $(WAF) configure --prefix=/usr/local --without-snapshot --dest-cpu=ia32 - CFLAGS=-m32 DESTDIR=$(PKGDIR) $(WAF) install + ./configure --prefix=$(PKGDIR)/usr/local --without-snapshot + $(MAKE) install $(packagemaker) \ --id "org.nodejs.NodeJS-$(VERSION)" \ --doc tools/osx-pkg.pmdoc \ --out $(PKG) -$(TARBALL): out/doc +$(TARBALL): node out/doc + @if [ $(shell ./node --version) = "$(VERSION)" ]; then \ + exit 0; \ + else \ + echo "" >&2 ; \ + echo "$(shell ./node --version) doesn't match $(VERSION)." >&2 ; \ + echo "Did you remember to update src/node_version.cc?" >&2 ; \ + echo "" >&2 ; \ + exit 1 ; \ + fi git archive --format=tar --prefix=$(TARNAME)/ HEAD | tar xf - mkdir -p $(TARNAME)/doc cp doc/node.1 $(TARNAME)/doc/node.1 cp -r out/doc/api $(TARNAME)/doc/api rm -rf $(TARNAME)/deps/v8/test # too big - rm -rf $(TARNAME)/doc/logos # too big + rm -rf $(TARNAME)/doc/images # too big tar -cf $(TARNAME).tar $(TARNAME) rm -rf $(TARNAME) gzip -f -9 $(TARNAME).tar @@ -212,4 +201,4 @@ cpplint: lint: jslint cpplint -.PHONY: lint cpplint jslint bench clean docopen docclean doc dist distclean dist-upload check uninstall install all program staticlib dynamiclib test test-all website-upload +.PHONY: lint cpplint jslint bench clean docopen docclean doc dist distclean check uninstall install install-includes install-bin all program staticlib dynamiclib test test-all website-upload pkg diff --git a/Makefile-gyp b/Makefile-gyp deleted file mode 100644 index f8dbeaffd4..0000000000 --- a/Makefile-gyp +++ /dev/null @@ -1,319 +0,0 @@ -BUILDTYPE ?= Release - -all: out/Makefile - tools/gyp_node -f make - $(MAKE) -C out BUILDTYPE=$(BUILDTYPE) - -ln -fs out/Release/node node - -ln -fs out/Debug/node node_g - -out/Release/node: all - -out/Makefile: node.gyp deps/uv/uv.gyp - -clean: - rm -rf out - -distclean: - rm -rf out - -test: all - python tools/test.py --mode=release simple message - -test-http1: all - python tools/test.py --mode=release --use-http1 simple message - -test-valgrind: all - python tools/test.py --mode=release --valgrind simple message - -test-all: all - python tools/test.py --mode=debug,release - -test-all-http1: all - python tools/test.py --mode=debug,release --use-http1 - -test-all-valgrind: all - python tools/test.py --mode=debug,release --valgrind - -test-release: all - python tools/test.py --mode=release - -test-debug: all - python tools/test.py --mode=debug - -test-message: all - python tools/test.py message - -test-simple: all - python tools/test.py simple - -test-pummel: all - python tools/test.py pummel - -test-internet: all - python tools/test.py internet - -UVTEST += simple/test-assert -UVTEST += simple/test-buffer -UVTEST += simple/test-c-ares -UVTEST += simple/test-chdir -UVTEST += simple/test-delayed-require -UVTEST += simple/test-eio-race2 -UVTEST += simple/test-eio-race4 -UVTEST += simple/test-event-emitter-add-listeners -UVTEST += simple/test-event-emitter-modify-in-emit -UVTEST += simple/test-event-emitter-num-args -UVTEST += simple/test-event-emitter-once -UVTEST += simple/test-event-emitter-remove-all-listeners -UVTEST += simple/test-event-emitter-remove-listeners -UVTEST += simple/test-exception-handler -UVTEST += simple/test-exception-handler2 -UVTEST += simple/test-exception-handler -UVTEST += simple/test-executable-path -UVTEST += simple/test-file-read-noexist -UVTEST += simple/test-file-write-stream -UVTEST += simple/test-fs-fsync -UVTEST += simple/test-fs-open -UVTEST += simple/test-fs-readfile-empty -UVTEST += simple/test-fs-read-file-sync -UVTEST += simple/test-fs-read-file-sync-hostname -UVTEST += simple/test-fs-sir-writes-alot -UVTEST += simple/test-fs-write -UVTEST += simple/test-fs-write-buffer -UVTEST += simple/test-fs-write-file -UVTEST += simple/test-fs-write-file-buffer -UVTEST += simple/test-fs-write-stream -UVTEST += simple/test-fs-write-stream-end -UVTEST += simple/test-fs-write-sync -UVTEST += simple/test-global -UVTEST += simple/test-http -UVTEST += simple/test-http-1.0 -UVTEST += simple/test-http-abort-client -UVTEST += simple/test-http-allow-req-after-204-res -UVTEST += simple/test-http-blank-header -UVTEST += simple/test-http-buffer-sanity -UVTEST += simple/test-http-cat -UVTEST += simple/test-http-chunked -UVTEST += simple/test-http-client-abort -UVTEST += simple/test-http-client-parse-error -UVTEST += simple/test-http-client-race -UVTEST += simple/test-http-client-race-2 -UVTEST += simple/test-http-client-upload -UVTEST += simple/test-http-client-upload-buf -UVTEST += simple/test-http-contentLength0 -UVTEST += simple/test-http-default-encoding -UVTEST += simple/test-http-dns-fail -UVTEST += simple/test-http-eof-on-connect -UVTEST += simple/test-http-exceptions -UVTEST += simple/test-http-expect-continue -UVTEST += simple/test-http-extra-response -UVTEST += simple/test-http-head-request -UVTEST += simple/test-http-head-response-has-no-body -UVTEST += simple/test-http-keep-alive -UVTEST += simple/test-http-keep-alive-close-on-header -UVTEST += simple/test-http-malformed-request -UVTEST += simple/test-http-many-keep-alive-connections -UVTEST += simple/test-http-mutable-headers -UVTEST += simple/test-http-parser -UVTEST += simple/test-http-proxy -UVTEST += simple/test-http-request-end -UVTEST += simple/test-http-response-close -UVTEST += simple/test-http-response-readable -UVTEST += simple/test-http-unix-socket -UVTEST += simple/test-http-server -UVTEST += simple/test-http-server-multiheaders -UVTEST += simple/test-http-set-cookies -UVTEST += simple/test-http-set-timeout -UVTEST += simple/test-http-set-trailers -UVTEST += simple/test-http-upgrade-agent -UVTEST += simple/test-http-upgrade-client -UVTEST += simple/test-http-upgrade-client2 -UVTEST += simple/test-http-upgrade-server -UVTEST += simple/test-http-upgrade-server2 -UVTEST += simple/test-http-wget -UVTEST += simple/test-http-write-empty-string -UVTEST += simple/test-http-wget -UVTEST += simple/test-mkdir-rmdir -UVTEST += simple/test-net-binary -UVTEST += simple/test-net-pingpong -UVTEST += simple/test-net-can-reset-timeout -UVTEST += simple/test-net-connect-buffer -UVTEST += simple/test-net-connect-timeout -UVTEST += simple/test-net-create-connection -UVTEST += simple/test-net-eaddrinuse -UVTEST += simple/test-net-isip -UVTEST += simple/test-net-keepalive -UVTEST += simple/test-net-pingpong -UVTEST += simple/test-net-reconnect -UVTEST += simple/test-net-remote-address-port -UVTEST += simple/test-net-server-bind -UVTEST += simple/test-net-server-max-connections -UVTEST += simple/test-net-server-try-ports -UVTEST += simple/test-net-stream -UVTEST += simple/test-net-socket-timeout -UVTEST += simple/test-next-tick -UVTEST += simple/test-next-tick-doesnt-hang -UVTEST += simple/test-next-tick-errors -UVTEST += simple/test-next-tick-ordering -UVTEST += simple/test-next-tick-ordering2 -UVTEST += simple/test-next-tick-starvation -UVTEST += simple/test-module-load-list -UVTEST += simple/test-path -UVTEST += simple/test-pipe-stream -UVTEST += simple/test-pump-file2tcp -UVTEST += simple/test-pump-file2tcp-noexist -UVTEST += simple/test-punycode -UVTEST += simple/test-querystring -UVTEST += simple/test-readdir -UVTEST += simple/test-readdouble -UVTEST += simple/test-readfloat -UVTEST += simple/test-readint -UVTEST += simple/test-readuint -UVTEST += simple/test-regress-GH-819 -UVTEST += simple/test-regress-GH-897 -UVTEST += simple/test-regression-object-prototype -UVTEST += simple/test-require-cache -UVTEST += simple/test-require-cache-without-stat -UVTEST += simple/test-require-exceptions -UVTEST += simple/test-require-resolve -UVTEST += simple/test-script-context -UVTEST += simple/test-script-new -UVTEST += simple/test-script-static-context -UVTEST += simple/test-script-static-new -UVTEST += simple/test-script-static-this -UVTEST += simple/test-script-this -UVTEST += simple/test-stream-pipe-cleanup -UVTEST += simple/test-stream-pipe-error-handling -UVTEST += simple/test-stream-pipe-event -UVTEST += simple/test-stream-pipe-multi -UVTEST += simple/test-string-decoder -UVTEST += simple/test-sys -UVTEST += simple/test-tcp-wrap -UVTEST += simple/test-tcp-wrap-connect -UVTEST += simple/test-tcp-wrap-listen -UVTEST += simple/test-timers-linked-list -UVTEST += simple/test-tty-stdout-end -UVTEST += simple/test-url -UVTEST += simple/test-utf8-scripts -UVTEST += simple/test-vm-create-context-circular-reference -UVTEST += simple/test-writedouble -UVTEST += simple/test-writefloat -UVTEST += simple/test-writeint -UVTEST += simple/test-writeuint -UVTEST += simple/test-zerolengthbufferbug -UVTEST += pummel/test-http-client-reconnect-bug -UVTEST += pummel/test-http-upload-timeout -UVTEST += pummel/test-net-many-clients -UVTEST += pummel/test-net-pause -UVTEST += pummel/test-net-pingpong-delay -UVTEST += pummel/test-net-timeout -UVTEST += pummel/test-timers -UVTEST += pummel/test-timer-wrap -UVTEST += pummel/test-timer-wrap2 -UVTEST += pummel/test-vm-memleak -UVTEST += internet/test-dns -UVTEST += simple/test-tls-client-abort -UVTEST += simple/test-tls-client-verify -UVTEST += simple/test-tls-connect -#UVTEST += simple/test-tls-ext-key-usage # broken -UVTEST += simple/test-tls-junk-closes-server -UVTEST += simple/test-tls-npn-server-client -UVTEST += simple/test-tls-request-timeout -#UVTEST += simple/test-tls-securepair-client # broken -UVTEST += simple/test-tls-securepair-server -#UVTEST += simple/test-tls-server-verify # broken -UVTEST += simple/test-tls-set-encoding - -# child_process -UVTEST += simple/test-child-process-exit-code -UVTEST += simple/test-child-process-buffering -UVTEST += simple/test-child-process-exec-cwd -UVTEST += simple/test-child-process-cwd -UVTEST += simple/test-child-process-env -UVTEST += simple/test-child-process-stdin -UVTEST += simple/test-child-process-ipc -UVTEST += simple/test-child-process-deprecated-api - - -test-uv: all - NODE_USE_UV=1 python tools/test.py $(UVTEST) - -test-uv-debug: all - NODE_USE_UV=1 python tools/test.py --mode=debug $(UVTEST) - - -apidoc_sources = $(wildcard doc/api/*.markdown) -apidocs = $(addprefix out/,$(apidoc_sources:.markdown=.html)) - -apidoc_dirs = out/doc out/doc/api/ out/doc/api/assets - -apiassets = $(subst api_assets,api/assets,$(addprefix out/,$(wildcard doc/api_assets/*))) - -website_files = \ - out/doc/index.html \ - out/doc/v0.4_announcement.html \ - out/doc/cla.html \ - out/doc/sh_main.js \ - out/doc/sh_javascript.min.js \ - out/doc/sh_vim-dark.css \ - out/doc/logo.png \ - out/doc/sponsored.png \ - out/doc/favicon.ico \ - out/doc/pipe.css - -doc: out/Release/node $(apidoc_dirs) $(website_files) $(apiassets) $(apidocs) - -$(apidoc_dirs): - mkdir -p $@ - -out/doc/api/assets/%: doc/api_assets/% out/doc/api/assets/ - cp $< $@ - -out/doc/%: doc/% - cp $< $@ - -out/doc/api/%.html: doc/api/%.markdown out/Release/node $(apidoc_dirs) $(apiassets) tools/doctool/doctool.js - out/Release/node tools/doctool/doctool.js doc/template.html $< > $@ - -out/doc/%: - -website-upload: doc - scp -r out/doc/* $(web_root) - -docopen: out/doc/api/all.html - -google-chrome out/doc/api/all.html - -docclean: - -rm -rf out/doc - -VERSION=$(shell git describe) -TARNAME=node-$(VERSION) - -#dist: doc/node.1 doc/api -dist: doc - git archive --format=tar --prefix=$(TARNAME)/ HEAD | tar xf - - mkdir -p $(TARNAME)/doc - cp doc/node.1 $(TARNAME)/doc/node.1 - cp -r out/doc/api $(TARNAME)/doc/api - rm -rf $(TARNAME)/deps/v8/test # too big - rm -rf $(TARNAME)/doc/logos # too big - tar -cf $(TARNAME).tar $(TARNAME) - rm -rf $(TARNAME) - gzip -f -9 $(TARNAME).tar - -bench: - benchmark/http_simple_bench.sh - -bench-idle: - ./node benchmark/idle_server.js & - sleep 1 - ./node benchmark/idle_clients.js & - -jslint: - PYTHONPATH=tools/closure_linter/ python tools/closure_linter/closure_linter/gjslint.py --unix_mode --strict --nojsdoc -r lib/ -r src/ -r test/ - -cpplint: - @python tools/cpplint.py $(wildcard src/*.cc src/*.h src/*.c) - -lint: jslint cpplint - -.PHONY: lint cpplint jslint bench clean docopen docclean doc dist distclean check uninstall install all program staticlib dynamiclib test test-all website-upload diff --git a/common.gypi b/common.gypi index 7060608329..6640578c7e 100644 --- a/common.gypi +++ b/common.gypi @@ -1,6 +1,9 @@ { 'variables': { 'visibility%': 'hidden', # V8's visibility setting + 'target_arch%': 'ia32', # set v8's target architecture + 'host_arch%': 'ia32', # set v8's host architecture + 'want_separate_host_toolset': 0, # V8 should not build target and host 'library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds 'component%': 'static_library', # NB. these names match with what V8 expects 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way @@ -19,13 +22,7 @@ ], 'msvs_settings': { 'VCCLCompilerTool': { - 'target_conditions': [ - ['library=="static_library"', { - 'RuntimeLibrary': 1, # static debug - }, { - 'RuntimeLibrary': 3, # DLL debug - }], - ], + 'RuntimeLibrary': 1, # static debug 'Optimization': 0, # /Od, no optimization 'MinimalRebuild': 'true', 'OmitFramePointers': 'false', @@ -37,8 +34,13 @@ }, }, 'Release': { - 'defines': [ 'NDEBUG' ], - 'cflags': [ '-O3', '-fomit-frame-pointer', '-fdata-sections', '-ffunction-sections' ], + 'conditions': [ + [ 'OS!="solaris"', { + 'cflags': [ '-fomit-frame-pointer' ] + }], + ], + # 'defines': [ 'NDEBUG' ], + 'cflags': [ '-O3', '-fdata-sections', '-ffunction-sections' ], 'conditions': [ ['target_arch=="x64"', { 'msvs_configuration_platform': 'x64', @@ -46,13 +48,7 @@ ], 'msvs_settings': { 'VCCLCompilerTool': { - 'target_conditions': [ - ['library=="static_library"', { - 'RuntimeLibrary': 0, # static release - }, { - 'RuntimeLibrary': 2, # debug release - }], - ], + 'RuntimeLibrary': 0, # static release 'Optimization': 3, # /Ox, full optimization 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible @@ -60,6 +56,8 @@ 'OmitFramePointers': 'true', 'EnableFunctionLevelLinking': 'true', 'EnableIntrinsicFunctions': 'true', + 'RuntimeTypeInfo': 'false', + 'ExceptionHandling': '0', 'AdditionalOptions': [ '/MP', # compile across multiple CPUs ], @@ -122,6 +120,11 @@ 'BUILDING_V8_SHARED=1', 'BUILDING_UV_SHARED=1', ], + }, { + 'defines': [ + '_LARGEFILE_SOURCE', + '_FILE_OFFSET_BITS=64', + ], }], [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { 'cflags': [ '-Wall', '-pthread', ], @@ -134,9 +137,7 @@ }], [ 'OS=="linux"', { 'cflags': [ '-ansi' ], - }], - [ 'visibility=="hidden"', { - 'cflags': [ '-fvisibility=hidden' ], + 'ldflags': [ '-rdynamic' ], }], ], }], @@ -149,13 +150,9 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings - # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden - 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', - 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics 'GCC_VERSION': '4.2', 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof - 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4 'PREBINDING': 'NO', # No -Wl,-prebind 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ @@ -1,25 +1,275 @@ -#! /bin/sh +#!/usr/bin/env python -# v8 doesn't like ccache -if [ ! -z "`echo $CC | grep ccache`" ]; then - echo "Error: V8 doesn't like ccache. Please set your CC env var to 'gcc'" - echo " (ba)sh: export CC=gcc" - exit 1 -fi +import optparse +import os +import pprint +import subprocess +import sys -if [ -z "$PYTHON" ]; then - PYTHON=python -fi +root_dir = os.path.dirname(__file__) +sys.path.insert(0, os.path.join(root_dir, 'deps', 'v8', 'tools')) -CUR_DIR=$PWD +# parse our options +parser = optparse.OptionParser() -#possible relative path -WORKINGDIR=`dirname $0` -cd "$WORKINGDIR" -#abs path -WORKINGDIR=`pwd` -cd "$CUR_DIR" +parser.add_option("--debug", + action="store_true", + dest="debug", + help="Also build debug build") -"$PYTHON" "${WORKINGDIR}/tools/waf-light" --jobs=1 configure $* +parser.add_option("--prefix", + action="store", + dest="prefix", + help="Select the install prefix (defaults to /usr/local)") -exit $? +parser.add_option("--without-npm", + action="store_true", + dest="without_npm", + help="Don\'t install the bundled npm package manager") + +parser.add_option("--without-waf", + action="store_true", + dest="without_waf", + help="Don\'t install node-waf") + +parser.add_option("--without-ssl", + action="store_true", + dest="without_ssl", + help="Build without SSL") + +parser.add_option("--without-snapshot", + action="store_true", + dest="without_snapshot", + help="Build without snapshotting V8 libraries. You might want to set" + " this for cross-compiling. [Default: False]") + +parser.add_option("--shared-v8", + action="store_true", + dest="shared_v8", + help="Link to a shared V8 DLL instead of static linking") + +parser.add_option("--shared-v8-includes", + action="store", + dest="shared_v8_includes", + help="Directory containing V8 header files") + +parser.add_option("--shared-v8-libpath", + action="store", + dest="shared_v8_libpath", + help="A directory to search for the shared V8 DLL") + +parser.add_option("--shared-v8-libname", + action="store", + dest="shared_v8_libname", + help="Alternative lib name to link to (default: 'v8')") + +parser.add_option("--openssl-use-sys", + action="store", + dest="openssl_use_sys", + help="Use the system OpenSSL instead of one included with Node") + +parser.add_option("--openssl-includes", + action="store", + dest="openssl_includes", + help="A directory to search for the OpenSSL includes") + +parser.add_option("--openssl-libpath", + action="store", + dest="openssl_libpath", + help="A directory to search for the OpenSSL libraries") + +parser.add_option("--no-ssl2", + action="store_true", + dest="no_ssl2", + help="Disable OpenSSL v2") + +parser.add_option("--shared-cares", + action="store_true", + dest="shared_cares", + help="Link to a shared C-Ares DLL instead of static linking") + +parser.add_option("--shared-cares-includes", + action="store", + dest="shared_cares_includes", + help="Directory containing C-Ares header files") + +parser.add_option("--shared-cares-libpath", + action="store", + dest="shared_cares_libpath", + help="A directory to search for the shared C-Ares DLL") + +parser.add_option("--with-dtrace", + action="store_true", + dest="with_dtrace", + help="Build with DTrace (experimental)") + +# CHECKME does this still work with recent releases of V8? +parser.add_option("--gdb", + action="store_true", + dest="gdb", + help="add gdb support") + +parser.add_option("--dest-cpu", + action="store", + dest="dest_cpu", + help="CPU architecture to build for. Valid values are: arm, ia32, x64") + +(options, args) = parser.parse_args() + + +def b(value): + """Returns the string 'true' if value is truthy, 'false' otherwise.""" + if value: + return 'true' + else: + return 'false' + + +def pkg_config(pkg): + cmd = os.popen('pkg-config --libs %s' % pkg, 'r') + libs = cmd.readline().strip() + ret = cmd.close() + if (ret): return None + + cmd = os.popen('pkg-config --cflags %s' % pkg, 'r') + cflags = cmd.readline().strip() + ret = cmd.close() + if (ret): return None + + return (libs, cflags) + + +def uname(switch): + f = os.popen('uname %s' % switch) + s = f.read().strip() + f.close() + return s + + +def host_arch(): + """Host architecture. One of arm, ia32 or x64.""" + arch = uname('-p') + arches = { + 'arm': 'arm', + 'x86': 'ia32', + 'i386': 'ia32', + 'x86_64': 'x64', + } + + if arches.get(arch) == None: + arch = uname('-m') + + return arches.get(arch, 'ia32') + + +def target_arch(): + return host_arch() + + +def configure_node(o): + # TODO add gdb + o['variables']['node_prefix'] = options.prefix if options.prefix else '' + o['variables']['node_use_dtrace'] = b(options.with_dtrace) + o['variables']['node_install_npm'] = b(not options.without_npm) + o['variables']['node_install_waf'] = b(not options.without_waf) + o['variables']['host_arch'] = host_arch() + o['variables']['target_arch'] = options.dest_cpu or target_arch() + + # TODO move to node.gyp + if sys.platform == 'sunos5': + o['variables']['visibility'] = '' # FIXME -fvisibility=hidden, should be a gcc check + + +def configure_libz(o): + o['libraries'] += ['-lz'] + + +def configure_v8(o): + o['variables']['v8_use_snapshot'] = b(not options.without_snapshot) + o['variables']['node_shared_v8'] = b(options.shared_v8) + + # assume shared_v8 if one of these is set? + if options.shared_v8_libpath: + o['libraries'] += ['-L%s' % options.shared_v8_libpath] + if options.shared_v8_libname: + o['libraries'] += ['-l%s' % options.shared_v8_libname] + if options.shared_v8_includes: + o['include_dirs'] += [options.shared_v8_includes] + + +def configure_cares(o): + o['variables']['node_shared_cares'] = b(options.shared_cares) + + # assume shared_cares if one of these is set? + if options.shared_cares_libpath: + o['libraries'] += ['-L%s' % options.shared_cares_libpath] + if options.shared_cares_includes: + o['include_dirs'] += [options.shared_cares_includes] + + +def configure_openssl(o): + o['variables']['node_use_openssl'] = b(not options.without_ssl) + + if options.without_ssl: + return + + if options.no_ssl2: + o['defines'] += ['OPENSSL_NO_SSL2=1'] + + if not options.openssl_use_sys: + o['variables']['node_use_system_openssl'] = b(False) + else: + out = pkg_config('openssl') + (libs, cflags) = out if out else ('', '') + + if options.openssl_libpath: + o['libraries'] += ['-L%s' % options.openssl_libpath, '-lssl', '-lcrypto'] + else: + o['libraries'] += libs.split() + + if options.openssl_includes: + o['include_dirs'] += [options.openssl_includes] + else: + o['cflags'] += cflags.split() + + o['variables']['node_use_system_openssl'] = b( + libs or cflags or options.openssl_libpath or options.openssl_includes) + + +output = { + 'variables': {}, + 'include_dirs': [], + 'libraries': [], + 'defines': [], + 'cflags': [], +} + +configure_node(output) +configure_libz(output) +configure_v8(output) +configure_cares(output) +configure_openssl(output) + +# variables should be a root level element, +# move everything else to target_defaults +variables = output['variables'] +del output['variables'] +output = { + 'variables': variables, + 'target_defaults': output +} +pprint.pprint(output, indent=2) + +def write(filename, data): + filename = os.path.join(root_dir, filename) + print "creating ", filename + with open(filename, 'w+') as f: + f.write(data) + +write('config.gypi', "# Do not edit. Generated by the configure script.\n" + + pprint.pformat(output, indent=2)) + +write('config.mk', "# Do not edit. Generated by the configure script.\n" + + ("BUILDTYPE=%s\n" % ('Debug' if options.debug else 'Release'))) + +subprocess.call(['tools/gyp_node','-f', 'make']) diff --git a/configure-gyp b/configure-gyp deleted file mode 100755 index 0ec745c939..0000000000 --- a/configure-gyp +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python - -import optparse -import os -import json -import sys - -root_dir = os.path.dirname(__file__) -sys.path.insert(0, os.path.join(root_dir, 'deps', 'v8', 'tools')) -import utils # GuessArchitecture - -# parse our options -parser = optparse.OptionParser() - -parser.add_option("--debug", - action="store_true", - dest="debug", - help="Also build debug build") - -parser.add_option("--prefix", - action="store", - dest="prefix", - help="Select the install prefix (defaults to /usr/local)") - -parser.add_option("--without-ssl", - action="store_true", - dest="without_ssl", - help="Build without SSL") - -parser.add_option("--without-snapshot", - action="store_true", - dest="without_snapshot", - help="Build without snapshotting V8 libraries. You might want to set" - " this for cross-compiling. [Default: False]") - -parser.add_option("--shared-v8", - action="store_true", - dest="shared_v8", - help="Link to a shared V8 DLL instead of static linking") - -parser.add_option("--shared-v8-includes", - action="store", - dest="shared_v8_includes", - help="Directory containing V8 header files") - -parser.add_option("--shared-v8-libpath", - action="store", - dest="shared_v8_libpath", - help="A directory to search for the shared V8 DLL") - -parser.add_option("--shared-v8-libname", - action="store", - dest="shared_v8_libname", - help="Alternative lib name to link to (default: 'v8')") - -parser.add_option("--openssl-includes", - action="store", - dest="openssl_includes", - help="A directory to search for the OpenSSL includes") - -parser.add_option("--openssl-libpath", - action="store", - dest="openssl_libpath", - help="A directory to search for the OpenSSL libraries") - -parser.add_option("--no-ssl2", - action="store_true", - dest="no_ssl2", - help="Disable OpenSSL v2") - -parser.add_option("--shared-cares", - action="store_true", - dest="shared_cares", - help="Link to a shared C-Ares DLL instead of static linking") - -parser.add_option("--shared-cares-includes", - action="store", - dest="shared_cares_includes", - help="Directory containing C-Ares header files") - -parser.add_option("--shared-cares-libpath", - action="store", - dest="shared_cares_libpath", - help="A directory to search for the shared C-Ares DLL") - -parser.add_option("--with-dtrace", - action="store_true", - dest="with_dtrace", - help="Build with DTrace (experimental)") - -# CHECKME does this still work with recent releases of V8? -parser.add_option("--gdb", - action="store_true", - dest="gdb", - help="add gdb support") - -parser.add_option("--dest-cpu", - action="store", - dest="dest_cpu", - help="CPU architecture to build for. Valid values are: arm, ia32, x64") - -(options, args) = parser.parse_args() - - -def pkg_config(pkg): - cmd = os.popen('pkg-config --libs %s' % pkg, 'r') - libs = cmd.readline().strip() - ret = cmd.close() - if (ret): return None - - cmd = os.popen('pkg-config --cflags %s' % pkg, 'r') - cflags = cmd.readline().strip() - ret = cmd.close() - if (ret): return None - - return (libs, cflags) - - -def uname(switch): - f = os.popen('uname %s' % switch) - s = f.read().strip() - f.close() - return s - - -def host_arch(): - """Host architecture. One of arm, ia32 or x64.""" - arch = uname('-p') - - if arch == 'unknown': - arch = uname('-m') - - return { - 'arm': 'arm', - 'x86': 'ia32', - 'i386': 'ia32', - 'x86_64': 'x64', - }.get(arch, 'ia32') - - -def target_arch(): - # TODO act on options.dest_cpu - return host_arch() - - -def configure_node(o): - # TODO add gdb and dest_cpu - o['variables']['node_debug'] = 'true' if options.debug else 'false' - o['variables']['node_prefix'] = options.prefix if options.prefix else '' - o['variables']['node_use_dtrace'] = 'true' if options.with_dtrace else 'false' - o['variables']['host_arch'] = host_arch() - o['variables']['target_arch'] = target_arch() - - # TODO move to node.gyp - if sys.platform == 'sunos5': - o['variables']['visibility'] = '' # FIXME -fvisibility=hidden, should be a gcc check - - -def configure_libz(o): - o['libraries'] += ['-lz'] - - -def configure_v8(o): - o['variables']['v8_use_snapshot'] = 'true' if not options.without_snapshot else 'false' - o['variables']['node_shared_v8'] = 'true' if options.shared_v8 else 'false' - - # assume shared_v8 if one of these is set? - if options.shared_v8_libpath: - o['libraries'] += ['-L%s' % options.shared_v8_libpath] - if options.shared_v8_libname: - o['libraries'] += ['-l%s' % options.shared_v8_libname] - if options.shared_v8_includes: - o['include_dirs'] += [options.shared_v8_includes] - - -def configure_cares(o): - o['variables']['node_shared_cares'] = 'true' if options.shared_cares else 'false' - - # assume shared_cares if one of these is set? - if options.shared_cares_libpath: - o['libraries'] += ['-L%s' % options.shared_cares_libpath] - if options.shared_cares_includes: - o['include_dirs'] += [options.shared_cares_includes] - - -def configure_openssl(o): - o['variables']['node_use_openssl'] = 'false' if options.without_ssl else 'true' - - if options.without_ssl: - return - - if options.no_ssl2: - o['defines'] += ['OPENSSL_NO_SSL2=1'] - - out = pkg_config('openssl') - (libs, cflags) = out if out else ('', '') - - if options.openssl_libpath: - o['libraries'] += ['-L%s' % options.openssl_libpath, '-lssl', '-lcrypto'] - else: - o['libraries'] += libs.split() - - if options.openssl_includes: - o['include_dirs'] += [options.openssl_includes] - else: - o['cflags'] += cflags.split() - - if libs or cflags or options.openssl_libpath or options.openssl_includes: - o['variables']['node_use_system_openssl'] = 'true' - else: - o['variables']['node_use_system_openssl'] = 'false' - - -print "configure options:", options - -output = { - 'variables': {}, - 'include_dirs': [], - 'libraries': [], - 'defines': [], - 'cflags': [], -} - -configure_node(output) -configure_libz(output) -configure_v8(output) -configure_cares(output) -configure_openssl(output) - -# variables should be a root level element, -# move everything else to target_defaults -variables = output['variables'] -del output['variables'] -output = { - 'variables': variables, - 'target_defaults': output -} - -fn = os.path.join(root_dir, 'options.gypi') -print "creating ", fn - -f = open(fn, 'w+') -f.write("# Do not edit. Generated by the configure script.\n") -json.dump(output, f, indent=2, skipkeys=True) -f.write("\n") -f.close() - diff --git a/deps/openssl/config/piii/openssl/opensslconf-posix.h b/deps/openssl/config/piii/openssl/opensslconf-posix.h index 7594f33f62..9f87f271f0 100644 --- a/deps/openssl/config/piii/openssl/opensslconf-posix.h +++ b/deps/openssl/config/piii/openssl/opensslconf-posix.h @@ -187,11 +187,19 @@ * for debuging the bignum libraries */ #undef SIXTY_FOUR_BIT_LONG #undef SIXTY_FOUR_BIT -#define THIRTY_TWO_BIT +#undef THIRTY_TWO_BIT #undef SIXTEEN_BIT #undef EIGHT_BIT + +/* Let's hope ARM never releases a 64 bits CPU... */ +#if __x86_64__ +# define SIXTY_FOUR_BIT +#else +# define THIRTY_TWO_BIT #endif +#endif /* defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H) */ + #if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H) #define CONFIG_HEADER_RC4_LOCL_H /* if this is defined data[i] is used instead of *data, this is a %20 @@ -213,6 +221,10 @@ #define DES_PTR #endif +#if __x86_64__ +#undef DES_PTR +#endif + /* This helps C compiler generate the correct code for multiple functional * units. It reduces register dependancies at the expense of 2 more * registers */ diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 727417f28f..9578ac4df0 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -38,7 +38,7 @@ 'TERMIO', ], }], - ['target_arch=="ia32"', { + ['target_arch=="ia32" or target_arch=="x64"', { 'include_dirs': [ 'config/piii', ], diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 9af6eb6afa..2d98623b27 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -10,3 +10,4 @@ Saúl Ibarra Corretgé <saghul@gmail.com> Yuki OKUMURA <mjt@cltn.org> Frank Denis <github@pureftpd.org> Ryan Emery <seebees@gmail.com> +Yasuhiro Matsumoto <mattn.jp@gmail.com> diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 56c20e77ef..37452ac892 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -37,3 +37,9 @@ Shimon Doodkin <helpmepro1@gmail.com> Ryan Emery <seebees@gmail.com> Bruce Mitchener <bruce.mitchener@gmail.com> Maciej MaÅ‚ecki <maciej.malecki@notimplemented.org> +Yasuhiro Matsumoto <mattn.jp@gmail.com> +Daisuke Murase <typester@cpan.org> +Paddy Byers <paddy.byers@gmail.com> +Dan VerWeire <dverweire@gmail.com> +Brandon Benvie <brandon@bbenvie.com> +Brandon Philips <brandon.philips@rackspace.com> diff --git a/deps/uv/config-mingw.mk b/deps/uv/config-mingw.mk index 5ef189bd73..12e9578397 100644 --- a/deps/uv/config-mingw.mk +++ b/deps/uv/config-mingw.mk @@ -24,7 +24,7 @@ CC = $(PREFIX)gcc AR = $(PREFIX)ar E=.exe -CFLAGS=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0501 -Isrc/ares/config_win32 +CFLAGS=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0600 -Isrc/ares/config_win32 LINKFLAGS=-lm CARES_OBJS += src/ares/windows_port.o diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 8fe7254cfd..045987fe5a 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -33,6 +33,7 @@ OBJS += src/unix/fs.o OBJS += src/unix/cares.o OBJS += src/unix/udp.o OBJS += src/unix/error.o +OBJS += src/unix/thread.o OBJS += src/unix/process.o OBJS += src/unix/tcp.o OBJS += src/unix/pipe.o @@ -69,7 +70,7 @@ ifeq (FreeBSD,$(uname_S)) EV_CONFIG=config_freebsd.h EIO_CONFIG=config_freebsd.h CPPFLAGS += -Isrc/ares/config_freebsd -LINKFLAGS+= +LINKFLAGS+=-lkvm OBJS += src/unix/freebsd.o OBJS += src/unix/kqueue.o endif diff --git a/deps/uv/include/uv-private/eio.h b/deps/uv/include/uv-private/eio.h index 450df6ba29..aab9988b81 100644 --- a/deps/uv/include/uv-private/eio.h +++ b/deps/uv/include/uv-private/eio.h @@ -206,6 +206,28 @@ enum { EIO_PRI_DEFAULT = 0 }; +#define ETP_PRI_MIN EIO_PRI_MIN +#define ETP_PRI_MAX EIO_PRI_MAX + +#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) + +#define ETP_REQ eio_req + +/* + * a somewhat faster data structure might be nice, but + * with 8 priorities this actually needs <20 insns + * per shift, the most expensive operation. + */ +typedef struct { + ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */ + int size; +} etp_reqq; + +typedef struct { + etp_reqq res_queue; /* queue of outstanding responses for this channel */ + void *data; /* use this for what you want */ +} eio_channel; + /* eio request structure */ /* this structure is mostly read-only */ /* when initialising it, all members must be zero-initialised */ @@ -227,6 +249,8 @@ struct eio_req long int3; /* chown, fchown: gid */ int errorno; /* errno value on syscall return */ + eio_channel *channel; /* data used to direct poll callbacks arising from this req */ + #if __i386 || __amd64 unsigned char cancelled; #else @@ -261,11 +285,14 @@ enum { * and eio_poll_cb needs to be invoked (it MUST NOT call eio_poll_cb itself). * done_poll is called when the need to poll is gone. */ -int eio_init (void (*want_poll)(void), void (*done_poll)(void)); +int eio_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)); + +/* initialises a channel */ +void eio_channel_init(eio_channel *, void *data); /* must be called regularly to handle pending requests */ /* returns 0 if all requests were handled, -1 if not, or the value of EIO_FINISH if != 0 */ -int eio_poll (void); +int eio_poll (eio_channel *channel); /* stop polling if poll took longer than duration seconds */ void eio_set_max_poll_time (eio_tstamp nseconds); @@ -289,55 +316,55 @@ unsigned int eio_nthreads (void); /* number of worker threads in use currently * /* convenience wrappers */ #ifndef EIO_NO_WRAPPERS -eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */ -eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ties a thread for this long, simulating busyness */ -eio_req *eio_sync (int pri, eio_cb cb, void *data); -eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); -eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); -eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data); -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data); -eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data); -eio_req *eio_close (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); -eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data); -eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); -eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data); -eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data); -eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); -eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data); +eio_req *eio_nop (int pri, eio_cb cb, void *data, eio_channel *channel); /* does nothing except go through the whole process */ +eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data, eio_channel *channel); /* ties a thread for this long, simulating busyness */ +eio_req *eio_sync (int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_close (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data, eio_channel *channel); #endif /*****************************************************************************/ /* groups */ -eio_req *eio_grp (eio_cb cb, void *data); +eio_req *eio_grp (eio_cb cb, void *data, eio_channel *channel); void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit); void eio_grp_limit (eio_req *grp, int limit); void eio_grp_add (eio_req *grp, eio_req *req); diff --git a/deps/uv/include/uv-private/ngx-queue.h b/deps/uv/include/uv-private/ngx-queue.h index f8576d67dc..8c5e461762 100644 --- a/deps/uv/include/uv-private/ngx-queue.h +++ b/deps/uv/include/uv-private/ngx-queue.h @@ -99,4 +99,8 @@ struct ngx_queue_s { (type *) ((unsigned char *) q - offsetof(type, link)) +#define ngx_queue_foreach(q, h) \ + for ((q) = ngx_queue_head(h); (q) != (h); (q) = ngx_queue_next(q)) + + #endif /* _NGX_QUEUE_H_INCLUDED_ */ diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 21078fe363..24ef37cb9d 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -34,6 +34,7 @@ #include <arpa/inet.h> #include <netdb.h> #include <termios.h> +#include <pthread.h> /* Note: May be cast to struct iovec. See writev(2). */ typedef struct { @@ -43,6 +44,13 @@ typedef struct { typedef int uv_file; +#define UV_ONCE_INIT PTHREAD_ONCE_INIT + +typedef pthread_once_t uv_once_t; +typedef pthread_t uv_thread_t; +typedef pthread_mutex_t uv_mutex_t; +typedef pthread_rwlock_t uv_rwlock_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef void* uv_lib_t; #define UV_DYNAMIC /* empty */ @@ -55,6 +63,8 @@ typedef void* uv_lib_t; * definition of ares_timeout(). \ */ \ ev_timer timer; \ + /* Poll result queue */ \ + eio_channel uv_eio_channel; \ struct ev_loop* ev; #define UV_REQ_BUFSML_SIZE (4) diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index e620f8b084..31336351e4 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -23,6 +23,7 @@ # define _WIN32_WINNT 0x0502 #endif +#include <process.h> #include <stdint.h> #include <winsock2.h> #include <mswsock.h> @@ -102,6 +103,9 @@ LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwFlags); + + typedef PVOID RTL_SRWLOCK; + typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; #endif typedef int (WSAAPI* LPFN_WSARECV) @@ -137,6 +141,31 @@ typedef struct uv_buf_t { typedef int uv_file; +typedef HANDLE uv_thread_t; + +typedef CRITICAL_SECTION uv_mutex_t; + +typedef union { + /* srwlock_ has type SRWLOCK, but not all toolchains define this type in */ + /* windows.h. */ + SRWLOCK srwlock_; + struct { + uv_mutex_t read_mutex_; + uv_mutex_t write_mutex_; + unsigned int num_readers_; + } fallback_; +} uv_rwlock_t; + +#define UV_ONCE_INIT { 0, NULL, NULL } + +typedef struct uv_once_s { + unsigned char ran; + /* The actual event handle must be aligned to sizeof(HANDLE), so in */ + /* practice it might overlap padding a little. */ + HANDLE event; + HANDLE padding; +} uv_once_t; + /* Platform-specific definitions for uv_dlopen support. */ typedef HMODULE uv_lib_t; #define UV_DYNAMIC FAR WINAPI @@ -171,7 +200,11 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); uv_idle_t* next_idle_handle; \ ares_channel ares_chan; \ int ares_active_sockets; \ - uv_timer_t ares_polling_timer; + uv_timer_t ares_polling_timer; \ + /* Counter to keep track of active tcp streams */ \ + unsigned int active_tcp_streams; \ + /* Counter to keep track of active udp streams */ \ + unsigned int active_udp_streams; #define UV_REQ_TYPE_PRIVATE \ /* TODO: remove the req suffix */ \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index b6ce8ec2e9..f4ee49cc28 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -41,8 +41,9 @@ extern "C" { # define UV_EXTERN /* nothing */ # define CARES_STATICLIB 1 # endif +#elif __GNUC__ >= 4 +# define UV_EXTERN __attribute__((visibility("default"))) #else - /* Unix. TODO: symbol hiding */ # define UV_EXTERN /* nothing */ #endif @@ -181,6 +182,8 @@ typedef struct uv_async_s uv_async_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; typedef struct uv_process_s uv_process_t; typedef struct uv_counters_s uv_counters_t; +typedef struct uv_cpu_info_s uv_cpu_info_t; +typedef struct uv_interface_address_s uv_interface_address_t; /* Request types */ typedef struct uv_req_s uv_req_t; typedef struct uv_shutdown_s uv_shutdown_t; @@ -220,6 +223,11 @@ UV_EXTERN uv_loop_t* uv_default_loop(void); UV_EXTERN int uv_run (uv_loop_t*); /* + * This function polls for new events without blocking. + */ +UV_EXTERN int uv_run_once (uv_loop_t*); + +/* * Manually modify the event loop's reference count. Useful if the user wants * to have a handle or timeout that doesn't keep the loop alive. */ @@ -231,15 +239,27 @@ UV_EXTERN int64_t uv_now(uv_loop_t*); /* - * The status parameter is 0 if the request completed successfully, - * and should be -1 if the request was cancelled or failed. - * Error details can be obtained by calling uv_last_error(). + * Should return a buffer that libuv can use to read data into. * - * In the case of uv_read_cb the uv_buf_t returned should be freed by the - * user. + * `suggested_size` is a hint. Returning a buffer that is smaller is perfectly + * okay as long as `buf.len > 0`. */ typedef uv_buf_t (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size); + +/* + * `nread` is > 0 if there is data available, 0 if libuv is done reading for now + * or -1 on error. + * + * Error details can be obtained by calling uv_last_error(). UV_EOF indicates + * that the stream has been closed. + * + * The callee is responsible for closing the stream when an error happens. + * Trying to read from the stream again is undefined. + * + * The callee is responsible for freeing the buffer, libuv does not reuse it. + */ typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); + /* * Just like the uv_read_cb except that if the pending parameter is true * then you can use uv_accept() to pull the new handle into the process. @@ -247,6 +267,7 @@ typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); */ typedef void (*uv_read2_cb)(uv_pipe_t* pipe, ssize_t nread, uv_buf_t buf, uv_handle_type pending); + typedef void (*uv_write_cb)(uv_write_t* req, int status); typedef void (*uv_connect_cb)(uv_connect_t* req, int status); typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); @@ -376,6 +397,21 @@ UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); UV_EXTERN uv_buf_t uv_buf_init(char* base, size_t len); +/* + * Utility function. Copies up to `size` characters from `src` to `dst` + * and ensures that `dst` is properly NUL terminated unless `size` is zero. + */ +UV_EXTERN size_t uv_strlcpy(char* dst, const char* src, size_t size); + +/* + * Utility function. Appends `src` to `dst` and ensures that `dst` is + * properly NUL terminated unless `size` is zero or `dst` does not + * contain a NUL byte. `size` is the total length of `dst` so at most + * `size - strlen(dst) - 1` characters will be copied from `src`. + */ +UV_EXTERN size_t uv_strlcat(char* dst, const char* src, size_t size); + + #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ size_t write_queue_size; \ @@ -1083,7 +1119,48 @@ UV_EXTERN int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb); +struct uv_cpu_info_s { + char* model; + int speed; + struct uv_cpu_times_s { + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t irq; + } cpu_times; +}; + +struct uv_interface_address_s { + char* name; + int is_internal; + union { + struct sockaddr_in address4; + struct sockaddr_in6 address6; + } address; +}; +UV_EXTERN char** uv_setup_args(int argc, char** argv); +UV_EXTERN uv_err_t uv_get_process_title(char* buffer, size_t size); +UV_EXTERN uv_err_t uv_set_process_title(const char* title); +UV_EXTERN uv_err_t uv_resident_set_memory(size_t* rss); +UV_EXTERN uv_err_t uv_uptime(double* uptime); + +/* + * This allocates cpu_infos array, and sets count. The array + * is freed using uv_free_cpu_info(). + */ +UV_EXTERN uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); +UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); + +/* + * This allocates addresses array, and sets count. The array + * is freed using uv_free_interface_addresses(). + */ +UV_EXTERN uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count); +UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count); /* * File System Methods. @@ -1322,6 +1399,37 @@ UV_EXTERN uv_err_t uv_dlclose(uv_lib_t library); */ UV_EXTERN uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr); +/* + * The mutex functions return 0 on success, -1 on error + * (unless the return type is void, of course). + */ +UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); +UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); +UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); +UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); +UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle); + +/* + * Same goes for the read/write lock functions. + */ +UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock); +UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock); +UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock); +UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock); + +/* Runs a function once and only once. Concurrent calls to uv_once() with the + * same guard will block all callers except one (it's unspecified which one). + * The guard should be initialized statically with the UV_ONCE_INIT macro. + */ +UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); + +UV_EXTERN int uv_thread_create(uv_thread_t *tid, + void (*entry)(void *arg), void *arg); +UV_EXTERN int uv_thread_join(uv_thread_t *tid); /* the presence of these unions force similar struct layout */ union uv_any_handle { diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 40fb3d95e4..af39fc80dd 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -64,7 +64,6 @@ static void uv__finish_close(uv_handle_t* handle); void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - uv_udp_t* udp; uv_async_t* async; uv_timer_t* timer; uv_stream_t* stream; @@ -97,11 +96,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { break; case UV_UDP: - udp = (uv_udp_t*)handle; - uv__udp_watcher_stop(udp, &udp->read_watcher); - uv__udp_watcher_stop(udp, &udp->write_watcher); - uv__close(udp->fd); - udp->fd = -1; + uv__udp_start_close((uv_udp_t*)handle); break; case UV_PREPARE: @@ -152,10 +147,31 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { } -uv_loop_t* uv_loop_new(void) { - uv_loop_t* loop = calloc(1, sizeof(uv_loop_t)); - loop->ev = ev_loop_new(0); +static int uv__loop_init(uv_loop_t* loop, + struct ev_loop *(ev_loop_new)(unsigned int flags)) { + memset(loop, 0, sizeof(*loop)); +#if HAVE_KQUEUE + loop->ev = ev_loop_new(EVBACKEND_KQUEUE); +#else + loop->ev = ev_loop_new(EVFLAG_AUTO); +#endif ev_set_userdata(loop->ev, loop); + eio_channel_init(&loop->uv_eio_channel, loop); + return 0; +} + + +uv_loop_t* uv_loop_new(void) { + uv_loop_t* loop; + + if ((loop = malloc(sizeof(*loop))) == NULL) + return NULL; + + if (uv__loop_init(loop, ev_loop_new)) { + free(loop); + return NULL; + } + return loop; } @@ -181,16 +197,13 @@ int uv_loop_refcount(const uv_loop_t* loop) { uv_loop_t* uv_default_loop(void) { - if (!default_loop_ptr) { - default_loop_ptr = &default_loop_struct; -#if HAVE_KQUEUE - default_loop_struct.ev = ev_default_loop(EVBACKEND_KQUEUE); -#else - default_loop_struct.ev = ev_default_loop(EVFLAG_AUTO); -#endif - ev_set_userdata(default_loop_struct.ev, default_loop_ptr); - } - assert(default_loop_ptr->ev == EV_DEFAULT_UC); + if (default_loop_ptr) + return default_loop_ptr; + + if (uv__loop_init(&default_loop_struct, ev_default_loop)) + return NULL; + + default_loop_ptr = &default_loop_struct; return default_loop_ptr; } @@ -201,6 +214,12 @@ int uv_run(uv_loop_t* loop) { } +int uv_run_once(uv_loop_t* loop) { + ev_run(loop->ev, EVRUN_ONCE); + return 0; +} + + void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type) { loop->counters.handle_init++; @@ -210,7 +229,6 @@ void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, handle->flags = 0; ev_init(&handle->next_watcher, uv__next); - handle->next_watcher.data = handle; /* Ref the loop until this handle is closed. See uv__finish_close. */ ev_ref(loop->ev); @@ -255,10 +273,7 @@ void uv__finish_close(uv_handle_t* handle) { break; case UV_UDP: - assert(!ev_is_active(&((uv_udp_t*)handle)->read_watcher)); - assert(!ev_is_active(&((uv_udp_t*)handle)->write_watcher)); - assert(((uv_udp_t*)handle)->fd == -1); - uv__udp_destroy((uv_udp_t*)handle); + uv__udp_finish_close((uv_udp_t*)handle); break; case UV_PROCESS: @@ -283,9 +298,9 @@ void uv__finish_close(uv_handle_t* handle) { } -void uv__next(EV_P_ ev_idle* watcher, int revents) { - uv_handle_t* handle = watcher->data; - assert(watcher == &handle->next_watcher); +void uv__next(EV_P_ ev_idle* w, int revents) { + uv_handle_t* handle = container_of(w, uv_handle_t, next_watcher); + assert(revents == EV_IDLE); /* For now this function is only to handle the closing event, but we might @@ -323,7 +338,7 @@ void uv__req_init(uv_loop_t* loop, uv_req_t* req) { static void uv__prepare(EV_P_ ev_prepare* w, int revents) { - uv_prepare_t* prepare = w->data; + uv_prepare_t* prepare = container_of(w, uv_prepare_t, prepare_watcher); if (prepare->prepare_cb) { prepare->prepare_cb(prepare, 0); @@ -336,8 +351,6 @@ int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) { loop->counters.prepare_init++; ev_prepare_init(&prepare->prepare_watcher, uv__prepare); - prepare->prepare_watcher.data = prepare; - prepare->prepare_cb = NULL; return 0; @@ -373,7 +386,7 @@ int uv_prepare_stop(uv_prepare_t* prepare) { static void uv__check(EV_P_ ev_check* w, int revents) { - uv_check_t* check = w->data; + uv_check_t* check = container_of(w, uv_check_t, check_watcher); if (check->check_cb) { check->check_cb(check, 0); @@ -386,8 +399,6 @@ int uv_check_init(uv_loop_t* loop, uv_check_t* check) { loop->counters.check_init++; ev_check_init(&check->check_watcher, uv__check); - check->check_watcher.data = check; - check->check_cb = NULL; return 0; @@ -423,7 +434,7 @@ int uv_check_stop(uv_check_t* check) { static void uv__idle(EV_P_ ev_idle* w, int revents) { - uv_idle_t* idle = (uv_idle_t*)(w->data); + uv_idle_t* idle = container_of(w, uv_idle_t, idle_watcher); if (idle->idle_cb) { idle->idle_cb(idle, 0); @@ -437,8 +448,6 @@ int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) { loop->counters.idle_init++; ev_idle_init(&idle->idle_watcher, uv__idle); - idle->idle_watcher.data = idle; - idle->idle_cb = NULL; return 0; @@ -493,7 +502,7 @@ int uv_is_active(uv_handle_t* handle) { static void uv__async(EV_P_ ev_async* w, int revents) { - uv_async_t* async = w->data; + uv_async_t* async = container_of(w, uv_async_t, async_watcher); if (async->async_cb) { async->async_cb(async, 0); @@ -506,8 +515,6 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) { loop->counters.async_init++; ev_async_init(&async->async_watcher, uv__async); - async->async_watcher.data = async; - async->async_cb = async_cb; /* Note: This does not have symmetry with the other libev wrappers. */ @@ -525,7 +532,7 @@ int uv_async_send(uv_async_t* async) { static void uv__timer_cb(EV_P_ ev_timer* w, int revents) { - uv_timer_t* timer = w->data; + uv_timer_t* timer = container_of(w, uv_timer_t, timer_watcher); if (!ev_is_active(w)) { ev_ref(EV_A); @@ -542,7 +549,6 @@ int uv_timer_init(uv_loop_t* loop, uv_timer_t* timer) { loop->counters.timer_init++; ev_init(&timer->timer_watcher, uv__timer_cb); - timer->timer_watcher.data = timer; return 0; } @@ -685,7 +691,7 @@ int uv_getaddrinfo(uv_loop_t* loop, uv_ref(loop); req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, - uv_getaddrinfo_done, handle); + uv_getaddrinfo_done, handle, &loop->uv_eio_channel); assert(req); assert(req->data == handle); @@ -766,23 +772,6 @@ int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) { } -int uv__close(int fd) { - int status; - - /* - * Retry on EINTR. You may think this is academic but on linux - * and probably other Unices too, close(2) is interruptible. - * Failing to handle EINTR is a common source of fd leaks. - */ - do { - status = close(fd); - } - while (status == -1 && errno == EINTR); - - return status; -} - - int uv__nonblock(int fd, int set) { #if FIONBIO return ioctl(fd, FIONBIO, &set); @@ -836,6 +825,24 @@ int uv__cloexec(int fd, int set) { } +/* This function is not execve-safe, there is a race window + * between the call to dup() and fcntl(FD_CLOEXEC). + */ +int uv__dup(int fd) { + fd = dup(fd); + + if (fd == -1) + return -1; + + if (uv__cloexec(fd, 1)) { + SAVE_ERRNO(uv__close(fd)); + return -1; + } + + return fd; +} + + /* TODO move to uv-common.c? */ size_t uv__strlcpy(char* dst, const char* src, size_t size) { const char *org; diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index 675e0ad488..e6deb3017b 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -25,7 +25,15 @@ #include <stdint.h> #include <errno.h> +#include <ifaddrs.h> +#include <net/if.h> + +#include <TargetConditionals.h> + +#if !TARGET_OS_IPHONE #include <CoreServices/CoreServices.h> +#endif + #include <mach/mach.h> #include <mach/mach_time.h> #include <mach-o/dyld.h> /* _NSGetExecutablePath */ @@ -33,7 +41,26 @@ #include <sys/sysctl.h> #include <unistd.h> /* sysconf */ +static char *process_title; + +#if TARGET_OS_IPHONE +/* see: http://developer.apple.com/library/mac/#qa/qa1398/_index.html */ +uint64_t uv_hrtime() { + uint64_t time; + uint64_t enano; + static mach_timebase_info_data_t sTimebaseInfo; + + time = mach_absolute_time(); + if (0 == sTimebaseInfo.denom) { + (void)mach_timebase_info(&sTimebaseInfo); + } + + enano = time * sTimebaseInfo.numer / sTimebaseInfo.denom; + + return enano; +} +#else uint64_t uv_hrtime() { uint64_t time; Nanoseconds enano; @@ -41,7 +68,7 @@ uint64_t uv_hrtime() { enano = AbsoluteToNanoseconds(*(AbsoluteTime *)&time); return (*(uint64_t *)&enano); } - +#endif int uv_exepath(char* buffer, size_t* size) { uint32_t usize; @@ -71,6 +98,7 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { vm_statistics_data_t info; mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); @@ -83,6 +111,7 @@ uint64_t uv_get_free_memory(void) { return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); } + uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_MEMSIZE}; @@ -95,6 +124,7 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -106,3 +136,206 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + /* TODO implement me */ + return uv__new_artificial_error(UV_ENOSYS); +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + int r = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count); + + if (r != KERN_SUCCESS) { + return uv__new_sys_error(errno); + } + + *rss = t_info.resident_size; + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + + return uv_ok_; +} + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks); + char model[512]; + uint64_t cpuspeed; + size_t size; + unsigned int i; + natural_t numcpus; + mach_msg_type_number_t msg_type; + processor_cpu_load_info_data_t *info; + uv_cpu_info_t* cpu_info; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + size = sizeof(cpuspeed); + if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, + (processor_info_array_t*)&info, + &msg_type) != KERN_SUCCESS) { + return uv__new_sys_error(errno); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier; + cpu_info->cpu_times.irq = 0; + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed/1000000; + } + vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == AF_LINK)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + bzero(&ip, sizeof (ip)); + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { + continue; + } + + if (ent->ifa_addr == NULL) { + continue; + } + + /* + * On Mac OS X getifaddrs returns information related to Mac Addresses for + * various devices, such as firewire, etc. These are not relevant here. + */ + if (ent->ifa_addr->sa_family == AF_LINK) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/deps/uv/src/unix/eio/config_linux.h b/deps/uv/src/unix/eio/config_linux.h index 606301faf2..e7a0d6e7ae 100644 --- a/deps/uv/src/unix/eio/config_linux.h +++ b/deps/uv/src/unix/eio/config_linux.h @@ -13,8 +13,8 @@ /* utimes(2) is available */ #define HAVE_UTIMES 1 -/* futimes(2) is available */ -#define HAVE_FUTIMES 1 +/* futimes(2) is available but we make the syscall directly. */ +#undef HAVE_FUTIMES /* Define to 1 if you have the <inttypes.h> header file. */ #define HAVE_INTTYPES_H 1 @@ -56,6 +56,9 @@ /* Define to 1 if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 +/* Define to 1 if you have the <sys/syscall.h> header file. */ +#define HAVE_SYS_SYSCALL_H 1 + /* Define to 1 if you have the <unistd.h> header file. */ #define HAVE_UNISTD_H 1 diff --git a/deps/uv/src/unix/eio/eio.c b/deps/uv/src/unix/eio/eio.c index 75abd9bb69..248af9e2f6 100644 --- a/deps/uv/src/unix/eio/eio.c +++ b/deps/uv/src/unix/eio/eio.c @@ -362,12 +362,8 @@ static int gettimeofday(struct timeval *tv, struct timezone *tz) #define EIO_TICKS ((1000000 + 1023) >> 10) -#define ETP_PRI_MIN EIO_PRI_MIN -#define ETP_PRI_MAX EIO_PRI_MAX - struct etp_worker; -#define ETP_REQ eio_req #define ETP_DESTROY(req) eio_destroy (req) static int eio_finish (eio_req *req); #define ETP_FINISH(req) eio_finish (req) @@ -376,8 +372,6 @@ static void eio_execute (struct etp_worker *self, eio_req *req); /*****************************************************************************/ -#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) - /* calculate time difference in ~1/EIO_TICKS of a second */ ecb_inline int tvdiff (struct timeval *tv1, struct timeval *tv2) @@ -388,8 +382,8 @@ tvdiff (struct timeval *tv1, struct timeval *tv2) static unsigned int started, idle, wanted = 4; -static void (*want_poll_cb) (void); -static void (*done_poll_cb) (void); +static void (*want_poll_cb) (eio_channel *); +static void (*done_poll_cb) (eio_channel *); static unsigned int max_poll_time; /* reslock */ static unsigned int max_poll_reqs; /* reslock */ @@ -506,18 +500,8 @@ etp_nthreads (void) return retval; } -/* - * a somewhat faster data structure might be nice, but - * with 8 priorities this actually needs <20 insns - * per shift, the most expensive operation. - */ -typedef struct { - ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */ - int size; -} etp_reqq; - static etp_reqq req_queue; -static etp_reqq res_queue; +static eio_channel default_channel; static void ecb_noinline ecb_cold reqq_init (etp_reqq *q) @@ -574,7 +558,7 @@ reqq_shift (etp_reqq *q) } static int ecb_cold -etp_init (void (*want_poll)(void), void (*done_poll)(void)) +etp_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)) { X_MUTEX_CREATE (wrklock); X_MUTEX_CREATE (reslock); @@ -582,7 +566,7 @@ etp_init (void (*want_poll)(void), void (*done_poll)(void)) X_COND_CREATE (reqwait); reqq_init (&req_queue); - reqq_init (&res_queue); + eio_channel_init (&default_channel, 0); wrk_first.next = wrk_first.prev = &wrk_first; @@ -656,12 +640,19 @@ etp_end_thread (void) X_UNLOCK (wrklock); } +void +eio_channel_init(eio_channel *channel, void *data) { + reqq_init(&channel->res_queue); + channel->data = data; +} + static int -etp_poll (void) +etp_poll (eio_channel *channel) { unsigned int maxreqs; unsigned int maxtime; struct timeval tv_start, tv_now; + if(!channel) channel = &default_channel; X_LOCK (reslock); maxreqs = max_poll_reqs; @@ -678,14 +669,14 @@ etp_poll (void) etp_maybe_start_thread (); X_LOCK (reslock); - req = reqq_shift (&res_queue); + req = reqq_shift (&channel->res_queue); if (req) { --npending; - if (!res_queue.size && done_poll_cb) - done_poll_cb (); + if (!channel->res_queue.size && done_poll_cb) + done_poll_cb (channel); } X_UNLOCK (reslock); @@ -752,8 +743,8 @@ etp_submit (ETP_REQ *req) ++npending; - if (!reqq_push (&res_queue, req) && want_poll_cb) - want_poll_cb (); + if (!reqq_push (&req->channel->res_queue, req) && want_poll_cb) + want_poll_cb (req->channel); X_UNLOCK (reslock); } @@ -970,9 +961,9 @@ eio_set_max_parallel (unsigned int nthreads) etp_set_max_parallel (nthreads); } -int eio_poll (void) +int eio_poll (eio_channel *channel) { - return etp_poll (); + return etp_poll (channel); } /*****************************************************************************/ @@ -1048,8 +1039,15 @@ eio__utimes (const char *filename, const struct timeval times[2]) static int eio__futimes (int fd, const struct timeval tv[2]) { +#if defined(__linux) && defined(__NR_utimensat) + struct timespec ts[2]; + ts[0].tv_sec = tv[0].tv_sec, ts[0].tv_nsec = tv[0].tv_usec * 1000; + ts[1].tv_sec = tv[1].tv_sec, ts[1].tv_nsec = tv[1].tv_usec * 1000; + return syscall(__NR_utimensat, fd, NULL, ts, 0); +#else errno = ENOSYS; return -1; +#endif } #endif @@ -2092,8 +2090,8 @@ X_THREAD_PROC (etp_proc) ++npending; - if (!reqq_push (&res_queue, req) && want_poll_cb) - want_poll_cb (); + if (!reqq_push (&req->channel->res_queue, req) && want_poll_cb) + want_poll_cb (req->channel); self->req = 0; etp_worker_clear (self); @@ -2112,7 +2110,7 @@ quit: /*****************************************************************************/ int ecb_cold -eio_init (void (*want_poll)(void), void (*done_poll)(void)) +eio_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)) { #if !HAVE_PREADWRITE X_MUTEX_CREATE (preadwritelock); @@ -2138,7 +2136,8 @@ eio_api_destroy (eio_req *req) req->pri = pri; \ req->finish = cb; \ req->data = data; \ - req->destroy = eio_api_destroy; + req->destroy = eio_api_destroy; \ + req->channel = channel #define SEND eio_submit (req); return req @@ -2294,209 +2293,209 @@ eio_execute (etp_worker *self, eio_req *req) #ifndef EIO_NO_WRAPPERS -eio_req *eio_nop (int pri, eio_cb cb, void *data) +eio_req *eio_nop (int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_NOP); SEND; } -eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data) +eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_BUSY); req->nv1 = delay; SEND; } -eio_req *eio_sync (int pri, eio_cb cb, void *data) +eio_req *eio_sync (int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNC); SEND; } -eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSYNC); req->int1 = fd; SEND; } -eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FDATASYNC); req->int1 = fd; SEND; } -eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNCFS); req->int1 = fd; SEND; } -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; } -eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } -eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MLOCK); req->ptr2 = addr; req->size = length; SEND; } -eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) +eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MLOCKALL); req->int1 = flags; SEND; } -eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; } -eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_close (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CLOSE); req->int1 = fd; SEND; } -eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READAHEAD); req->int1 = fd; req->offs = offset; req->size = length; SEND; } -eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READ); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; } -eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_WRITE); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; } -eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSTAT); req->int1 = fd; SEND; } -eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSTATVFS); req->int1 = fd; SEND; } -eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data) +eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FUTIME); req->int1 = fd; req->nv1 = atime; req->nv2 = mtime; SEND; } -eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND; } -eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; } -eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) +eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_DUP2); req->int1 = fd; req->int2 = fd2; SEND; } -eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND; } -eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND; } -eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data) +eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_UTIME); PATH; req->nv1 = atime; req->nv2 = mtime; SEND; } -eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; } -eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND; } -eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND; } static eio_req * -eio__1path (int type, const char *path, int pri, eio_cb cb, void *data) +eio__1path (int type, const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (type); PATH; SEND; } -eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_READLINK, path, pri, cb, data); + return eio__1path (EIO_READLINK, path, pri, cb, data, channel); } -eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_REALPATH, path, pri, cb, data); + return eio__1path (EIO_REALPATH, path, pri, cb, data, channel); } -eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_STAT, path, pri, cb, data); + return eio__1path (EIO_STAT, path, pri, cb, data, channel); } -eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_LSTAT, path, pri, cb, data); + return eio__1path (EIO_LSTAT, path, pri, cb, data, channel); } -eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_STATVFS, path, pri, cb, data); + return eio__1path (EIO_STATVFS, path, pri, cb, data, channel); } -eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_UNLINK, path, pri, cb, data); + return eio__1path (EIO_UNLINK, path, pri, cb, data, channel); } -eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_RMDIR, path, pri, cb, data); + return eio__1path (EIO_RMDIR, path, pri, cb, data, channel); } -eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READDIR); PATH; req->int1 = flags; SEND; } -eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND; } static eio_req * -eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (type); PATH; @@ -2511,29 +2510,29 @@ eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb SEND; } -eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_LINK, path, new_path, pri, cb, data); + return eio__2path (EIO_LINK, path, new_path, pri, cb, data, channel); } -eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data); + return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data, channel); } -eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); + return eio__2path (EIO_RENAME, path, new_path, pri, cb, data, channel); } -eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CUSTOM); req->feed = execute; SEND; } #endif -eio_req *eio_grp (eio_cb cb, void *data) +eio_req *eio_grp (eio_cb cb, void *data, eio_channel *channel) { const int pri = EIO_PRI_MAX; diff --git a/deps/uv/src/unix/error.c b/deps/uv/src/unix/error.c index 93c5ac1f56..b167f7af1a 100644 --- a/deps/uv/src/unix/error.c +++ b/deps/uv/src/unix/error.c @@ -90,7 +90,5 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EXDEV: return UV_EXDEV; default: return UV_UNKNOWN; } - - assert(0 && "unreachable"); - return -1; + UNREACHABLE(); } diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index a519f86d49..9b83d3421f 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -19,22 +19,31 @@ */ #include "uv.h" +#include "internal.h" #include <assert.h> #include <string.h> #include <errno.h> +#include <kvm.h> +#include <paths.h> +#include <sys/user.h> #include <sys/types.h> #include <sys/resource.h> #include <sys/sysctl.h> #include <vm/vm_param.h> /* VM_LOADAVG */ #include <time.h> +#include <stdlib.h> #include <unistd.h> /* sysconf */ +#include <fcntl.h> #undef NANOSEC #define NANOSEC 1000000000 +static char *process_title; + + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -72,6 +81,7 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { int freecount; size_t size = sizeof(freecount); @@ -84,6 +94,7 @@ uint64_t uv_get_free_memory(void) { } + uint64_t uv_get_total_memory(void) { unsigned long info; int which[] = {CTL_HW, HW_PHYSMEM}; @@ -97,6 +108,7 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -108,3 +120,172 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + if (process_title) free(process_title); + process_title = strdup(title); + setproctitle(title); + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + kvm_t *kd = NULL; + struct kinfo_proc *kinfo = NULL; + pid_t pid; + int nprocs; + size_t page_size = getpagesize(); + + pid = getpid(); + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + if (kd == NULL) goto error; + + kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->ki_rssize * page_size; + + kvm_close(kd); + + return uv_ok_; + +error: + if (kd) kvm_close(kd); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus, + cur = 0; + uv_cpu_info_t* cpu_info; + char model[512]; + long* cp_times; + int numcpus; + size_t size; + int i; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + size = sizeof(cpuspeed); + if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of ncpu */ + size = sizeof(maxcpus); + if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + size = maxcpus * CPUSTATES * sizeof(long); + + cp_times = malloc(size); + if (cp_times == NULL) { + free(*cpu_infos); + return uv__new_sys_error(ENOMEM); + } + + if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) { + free(cp_times); + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; + cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + + cur+=CPUSTATES; + } + + free(cp_times); + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + /* TODO: implement */ + *addresses = NULL; + *count = 0; + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { +} diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index 436e54c680..6615516885 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -44,7 +44,7 @@ uv_fs_req_init(loop, req, type, path, cb); \ if (cb) { \ /* async */ \ - req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req); \ + req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); \ if (!req->eio) { \ uv__set_sys_error(loop, ENOMEM); \ return -1; \ @@ -191,7 +191,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, if (cb) { /* async */ uv_ref(loop); - req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -222,7 +222,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf, /* async */ uv_ref(loop); req->eio = eio_read(fd, buf, length, offset, EIO_PRI_DEFAULT, - uv__fs_after, req); + uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); @@ -260,7 +260,7 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, /* async */ uv_ref(loop); req->eio = eio_write(file, buf, length, offset, EIO_PRI_DEFAULT, - uv__fs_after, req); + uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -307,7 +307,7 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, if (cb) { /* async */ uv_ref(loop); - req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -377,7 +377,7 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); free(pathdup); @@ -411,7 +411,7 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); @@ -550,7 +550,7 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); free(pathdup); @@ -598,7 +598,7 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_req_init(loop, req, UV_FS_READLINK, path, cb); if (cb) { - if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) { + if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel))) { uv_ref(loop); return 0; } else { @@ -692,7 +692,7 @@ int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, req->work_cb = work_cb; req->after_work_cb = after_work_cb; - req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req); + req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 3ae5d0542a..3591090d7a 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -25,6 +25,8 @@ #include "uv-common.h" #include "uv-eio.h" +#include <assert.h> +#include <stdlib.h> /* abort */ #include <stddef.h> /* offsetof */ #if __STRICT_ANSI__ @@ -133,13 +135,20 @@ inline static int sys_accept4(int fd, #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) -#define SAVE_ERRNO(block) \ - do { \ - int _saved_errno = errno; \ - do { block; } while (0); \ - errno = _saved_errno; \ - } \ - while (0); +#define UNREACHABLE() \ + do { \ + assert(0 && "unreachable code"); \ + abort(); \ + } \ + while (0) + +#define SAVE_ERRNO(block) \ + do { \ + int _saved_errno = errno; \ + do { block; } while (0); \ + errno = _saved_errno; \ + } \ + while (0) /* flags */ enum { @@ -154,15 +163,19 @@ enum { UV_TCP_KEEPALIVE = 0x100 /* Turn on keep-alive. */ }; -size_t uv__strlcpy(char* dst, const char* src, size_t size); - -int uv__close(int fd); +/* core */ void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); - - int uv__nonblock(int fd, int set) __attribute__((unused)); int uv__cloexec(int fd, int set) __attribute__((unused)); int uv__socket(int domain, int type, int protocol); +int uv__dup(int fd); + +/* We used to handle EINTR in uv__close() but linux 2.6 will have closed the + * file descriptor anyway, even on EINTR. Retrying in that case isn't merely + * useless, it's actively harmful - the file descriptor may have been acquired + * by another thread. + */ +#define uv__close(fd) close(fd) /* error */ uv_err_code uv_translate_sys_error(int sys_errno); @@ -193,10 +206,15 @@ void uv__pipe_accept(EV_P_ ev_io* watcher, int revents); int uv_pipe_cleanup(uv_pipe_t* handle); /* udp */ -void uv__udp_destroy(uv_udp_t* handle); -void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w); +void uv__udp_start_close(uv_udp_t* handle); +void uv__udp_finish_close(uv_udp_t* handle); /* fs */ void uv__fs_event_destroy(uv_fs_event_t* handle); +#define UV__F_IPC (1 << 0) +#define UV__F_NONBLOCK (1 << 1) +int uv__make_socketpair(int fds[2], int flags); +int uv__make_pipe(int fds[2], int flags); + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 58a6816d8a..68d064dad9 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -31,6 +31,7 @@ #include <sys/sysctl.h> #include <sys/types.h> #include <sys/event.h> +#include <unistd.h> #include <fcntl.h> #include <time.h> @@ -140,13 +141,13 @@ int uv_fs_event_init(uv_loop_t* loop, void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "unreachable"); + UNREACHABLE(); } /* Called by libev, don't touch. */ void uv__kqueue_hack(EV_P_ int fflags, ev_io *w) { - assert(0 && "unreachable"); + UNREACHABLE(); } #endif /* HAVE_KQUEUE */ diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c index 3ed6565316..2f04cb8df9 100644 --- a/deps/uv/src/unix/linux.c +++ b/deps/uv/src/unix/linux.c @@ -22,11 +22,15 @@ #include "internal.h" #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <errno.h> +#include <ifaddrs.h> +#include <net/if.h> +#include <sys/param.h> #include <sys/sysinfo.h> #include <unistd.h> #include <fcntl.h> @@ -120,6 +124,14 @@ inline static int inotify_rm_watch(int fd, uint32_t wd) { #endif /* HAVE_INOTIFY_RM_WATCH */ +static char buf[MAXPATHLEN + 1]; + +static struct { + char *str; + size_t len; +} process_title; + + /* Don't look aghast, this is exactly how glibc's basename() works. */ static char* basename_r(const char* path) { char* s = strrchr(path, '/'); @@ -137,6 +149,7 @@ uint64_t uv_hrtime() { return (ts.tv_sec * NANOSEC + ts.tv_nsec); } + void uv_loadavg(double avg[3]) { struct sysinfo info; @@ -159,14 +172,368 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); } + uint64_t uv_get_total_memory(void) { return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); } + +char** uv_setup_args(int argc, char** argv) { + char **new_argv; + char **new_env; + size_t size; + int envc; + char *s; + int i; + + for (envc = 0; environ[envc]; envc++); + + s = envc ? environ[envc - 1] : argv[argc - 1]; + + process_title.str = argv[0]; + process_title.len = s + strlen(s) + 1 - argv[0]; + + size = process_title.len; + size += (argc + 1) * sizeof(char **); + size += (envc + 1) * sizeof(char **); + + if ((s = (char *) malloc(size)) == NULL) { + process_title.str = NULL; + process_title.len = 0; + return argv; + } + + new_argv = (char **) s; + new_env = new_argv + argc + 1; + s = (char *) (new_env + envc + 1); + memcpy(s, process_title.str, process_title.len); + + for (i = 0; i < argc; i++) + new_argv[i] = s + (argv[i] - argv[0]); + new_argv[argc] = NULL; + + s += environ[0] - argv[0]; + + for (i = 0; i < envc; i++) + new_env[i] = s + (environ[i] - environ[0]); + new_env[envc] = NULL; + + environ = new_env; + return new_argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + /* No need to terminate, last char is always '\0'. */ + if (process_title.len) + strncpy(process_title.str, title, process_title.len - 1); + + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title.str) { + strncpy(buffer, process_title.str, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + FILE* f; + int itmp; + char ctmp; + unsigned int utmp; + size_t page_size = getpagesize(); + char *cbuf; + int foundExeEnd; + + f = fopen("/proc/self/stat", "r"); + if (!f) return uv__new_sys_error(errno); + + /* PID */ + if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Exec file */ + cbuf = buf; + foundExeEnd = 0; + if (fscanf (f, "%c", cbuf++) == 0) goto error; + while (1) { + if (fscanf(f, "%c", cbuf) == 0) goto error; + if (*cbuf == ')') { + foundExeEnd = 1; + } else if (foundExeEnd && *cbuf == ' ') { + *cbuf = 0; + break; + } + + cbuf++; + } + /* State */ + if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */ + /* Parent process */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Process group */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Session id */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* TTY */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* TTY owner process group */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Flags */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Minor faults (no memory page) */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Minor faults, children */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Major faults (memory page faults) */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Major faults, children */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* utime */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* stime */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* utime, children */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* stime, children */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies remaining in current time slice */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* 'nice' value */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies until next timeout */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies until next SIGALRM */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* start time (jiffies since system boot) */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + + /* Virtual memory size */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + + /* Resident set size */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + *rss = (size_t) utmp * page_size; + + /* rlim */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Start of text */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* End of text */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + /* Start of stack */ + if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + + fclose (f); + return uv_ok_; + +error: + fclose (f); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { +#ifdef CLOCK_MONOTONIC + struct timespec now; + if (0 == clock_gettime(CLOCK_MONOTONIC, &now)) { + *uptime = now.tv_sec; + *uptime += (double)now.tv_nsec / 1000000000.0; + return uv_ok_; + } + return uv__new_sys_error(errno); +#else + struct sysinfo info; + if (sysinfo(&info) < 0) { + return uv__new_sys_error(errno); + } + *uptime = (double)info.uptime; + return uv_ok_; +#endif +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + int numcpus = 0, i = 0; + unsigned long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr; + char line[512], speedPath[256], model[512]; + FILE *fpStat = fopen("/proc/stat", "r"); + FILE *fpModel = fopen("/proc/cpuinfo", "r"); + FILE *fpSpeed; + uv_cpu_info_t* cpu_info; + + if (fpModel) { + while (fgets(line, 511, fpModel) != NULL) { + if (strncmp(line, "model name", 10) == 0) { + numcpus++; + if (numcpus == 1) { + char *p = strchr(line, ':') + 2; + strcpy(model, p); + model[strlen(model)-1] = 0; + } + } else if (strncmp(line, "cpu MHz", 7) == 0) { + if (numcpus == 1) { + sscanf(line, "%*s %*s : %u", &cpuspeed); + } + } + } + fclose(fpModel); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + cpu_info = *cpu_infos; + + if (fpStat) { + while (fgets(line, 511, fpStat) != NULL) { + if (strncmp(line, "cpu ", 4) == 0) { + continue; + } else if (strncmp(line, "cpu", 3) != 0) { + break; + } + + sscanf(line, "%*s %lu %lu %lu %lu %*s %lu", + &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr); + snprintf(speedPath, sizeof(speedPath), + "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i); + + fpSpeed = fopen(speedPath, "r"); + + if (fpSpeed) { + if (fgets(line, 511, fpSpeed) != NULL) { + sscanf(line, "%u", &cpuspeed); + cpuspeed /= 1000; + } + fclose(fpSpeed); + } + + cpu_info->cpu_times.user = ticks_user * multiplier; + cpu_info->cpu_times.nice = ticks_nice * multiplier; + cpu_info->cpu_times.sys = ticks_sys * multiplier; + cpu_info->cpu_times.idle = ticks_idle * multiplier; + cpu_info->cpu_times.irq = ticks_intr * multiplier; + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + + cpu_info++; + } + fclose(fpStat); + } + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == PF_PACKET)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + bzero(&ip, sizeof (ip)); + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { + continue; + } + + if (ent->ifa_addr == NULL) { + continue; + } + + /* + * On Linux getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information. + */ + if (ent->ifa_addr->sa_family == PF_PACKET) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} + #if HAVE_INOTIFY_INIT || HAVE_INOTIFY_INIT1 static int new_inotify_fd(void) { @@ -312,8 +679,7 @@ int uv_fs_event_init(uv_loop_t* loop, void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "unreachable"); - abort(); + UNREACHABLE(); } #endif /* HAVE_INOTIFY_INIT || HAVE_INOTIFY_INIT1 */ diff --git a/deps/uv/src/unix/openbsd.c b/deps/uv/src/unix/openbsd.c index 55f8ceb0de..80e934ccbc 100644 --- a/deps/uv/src/unix/openbsd.c +++ b/deps/uv/src/unix/openbsd.c @@ -33,12 +33,16 @@ #define NANOSEC 1000000000 +static char *process_title; + + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * NANOSEC + ts.tv_nsec); } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -51,6 +55,7 @@ void uv_loadavg(double avg[3]) { avg[2] = (double) info.ldavg[2] / info.fscale; } + int uv_exepath(char* buffer, size_t* size) { int mib[4]; char **argsbuf = NULL; @@ -98,6 +103,7 @@ out: return status; } + uint64_t uv_get_free_memory(void) { struct uvmexp info; size_t size = sizeof(info); @@ -110,6 +116,7 @@ uint64_t uv_get_free_memory(void) { return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } + uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_PHYSMEM64}; @@ -121,3 +128,160 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + if (process_title) free(process_title); + process_title = strdup(title); + setproctitle(title); + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + kvm_t *kd = NULL; + struct kinfo_proc2 *kinfo = NULL; + pid_t pid; + int nprocs, max_size = sizeof(struct kinfo_proc2); + size_t page_size = getpagesize(); + + pid = getpid(); + + kd = kvm_open(NULL, _PATH_MEM, NULL, O_RDONLY, "kvm_open"); + if (kd == NULL) goto error; + + kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->p_vm_rssize * page_size; + + kvm_close(kd); + + return uv_ok_; + +error: + if (kd) kvm_close(kd); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + uint64_t info[CPUSTATES]; + char model[512]; + int numcpus = 1; + static int which[] = {CTL_HW, HW_MODEL, NULL}; + size_t size; + uv_cpu_info_t* cpu_info; + + size = sizeof(model); + if (sysctl(which, 2, &model, &size, NULL, 0) < 0) { + return -1; + } + which[1] = HW_NCPU; + size = sizeof(numcpus); + if (sysctl(which, 2, &numcpus, &size, NULL, 0) < 0) { + return -1; + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + which[1] = HW_CPUSPEED; + size = sizeof(cpuspeed); + if (sysctl(which, 2, &cpuspeed, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + size = sizeof(info); + which[0] = CTL_KERN; + which[1] = KERN_CPTIME2; + for (int i = 0; i < numcpus; i++) { + which[2] = i; + size = sizeof(info); + if (sysctl(which, 3, &info, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier); + cpu_info->cpu_times.nice = ((uint64_t)(info[CP_NICE]) * multiplier); + cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier)); + cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier)); + cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier)); + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + } + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].brand); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + /* TODO: implement */ + *addresses = NULL; + *count = 0; + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { +} diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index f1be9e972b..205011bfd2 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -74,7 +74,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { } memset(&saddr, 0, sizeof saddr); - uv__strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + uv_strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); saddr.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { @@ -197,7 +197,7 @@ void uv_pipe_connect(uv_connect_t* req, } memset(&saddr, 0, sizeof saddr); - uv__strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + uv_strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); saddr.sun_family = AF_UNIX; /* We don't check for EINPROGRESS. Think about it: the socket diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 5581d8b809..becf60fe3e 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -63,10 +63,7 @@ static void uv__chld(EV_P_ ev_child* watcher, int revents) { } -#define UV__F_IPC (1 << 0) -#define UV__F_NONBLOCK (1 << 1) - -static int uv__make_socketpair(int fds[2], int flags) { +int uv__make_socketpair(int fds[2], int flags) { #ifdef SOCK_NONBLOCK int fl; @@ -103,7 +100,7 @@ static int uv__make_socketpair(int fds[2], int flags) { } -static int uv__make_pipe(int fds[2], int flags) { +int uv__make_pipe(int fds[2], int flags) { #if HAVE_SYS_PIPE2 int fl; diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index dbdad3ce7b..a87a2343a8 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -28,6 +28,11 @@ #include <assert.h> #include <errno.h> +#ifdef SUNOS_HAVE_IFADDRS +# include <ifaddrs.h> +#endif +#include <net/if.h> + #include <sys/loadavg.h> #include <sys/time.h> #include <unistd.h> @@ -38,6 +43,19 @@ # include <port.h> #endif +#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64) +#define PROCFS_FILE_OFFSET_BITS_HACK 1 +#undef _FILE_OFFSET_BITS +#else +#define PROCFS_FILE_OFFSET_BITS_HACK 0 +#endif + +#include <procfs.h> + +#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1) +#define _FILE_OFFSET_BITS 64 +#endif + uint64_t uv_hrtime() { return (gethrtime()); @@ -196,7 +214,256 @@ int uv_fs_event_init(uv_loop_t* loop, void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "unreachable"); /* should never be called */ + UNREACHABLE(); } #endif /* HAVE_PORTS_FS */ + + +char** uv_setup_args(int argc, char** argv) { + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (size > 0) { + buffer[0] = '\0'; + } + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + pid_t pid = getpid(); + psinfo_t psinfo; + char pidpath[1024]; + FILE *f; + + sprintf(pidpath, "/proc/%d/psinfo", (int)pid); + + f = fopen(pidpath, "r"); + if (!f) return uv__new_sys_error(errno); + + if (fread(&psinfo, sizeof(psinfo_t), 1, f) != 1) { + fclose (f); + return uv__new_sys_error(errno); + } + + /* XXX correct? */ + + *rss = (size_t) psinfo.pr_rssize * 1024; + + fclose (f); + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + + long hz = sysconf(_SC_CLK_TCK); + + if ((kc = kstat_open()) == NULL) + return uv__new_sys_error(errno); + + ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); + + if (kstat_read(kc, ksp, NULL) == -1) { + *uptime = -1; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clk_intr"); + *uptime = knp->value.ul / hz; + } + + kstat_close(kc); + + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + int lookup_instance; + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + uv_cpu_info_t* cpu_info; + + if ((kc = kstat_open()) == NULL) { + return uv__new_sys_error(errno); + } + + /* Get count of cpus */ + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL))) { + lookup_instance++; + } + + *cpu_infos = (uv_cpu_info_t*) + malloc(lookup_instance * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = lookup_instance; + + cpu_info = *cpu_infos; + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL))) { + if (kstat_read(kc, ksp, NULL) == -1) { + /* + * It is deeply annoying, but some kstats can return errors + * under otherwise routine conditions. (ACPI is one + * offender; there are surely others.) To prevent these + * fouled kstats from completely ruining our day, we assign + * an "error" member to the return value that consists of + * the strerror(). + */ + cpu_info->speed = 0; + cpu_info->model = NULL; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz"); + assert(knp->data_type == KSTAT_DATA_INT32); + cpu_info->speed = knp->value.i32; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand"); + assert(knp->data_type == KSTAT_DATA_STRING); + cpu_info->model = KSTAT_NAMED_STR_PTR(knp); + } + + lookup_instance++; + cpu_info++; + } + + cpu_info = *cpu_infos; + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu", lookup_instance, (char *)"sys"))){ + + if (kstat_read(kc, ksp, NULL) == -1) { + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.nice = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_user"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.user = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_kernel"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.sys = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_idle"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.idle = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"intr"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.irq = knp->value.ui64; + cpu_info->cpu_times.nice = 0; + } + + lookup_instance++; + cpu_info++; + } + + kstat_close(kc); + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { +#ifndef SUNOS_HAVE_IFADDRS + return uv__new_artificial_error(UV_ENOSYS); +#else + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == PF_PACKET)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + bzero(&ip, sizeof (ip)); + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { + continue; + } + + if (ent->ifa_addr == NULL) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags & + IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +#endif /* SUNOS_HAVE_IFADDRS */ +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index ee94ab3ed8..c05dd5df3b 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -22,6 +22,7 @@ #include "uv.h" #include "internal.h" +#include <unistd.h> #include <assert.h> #include <errno.h> diff --git a/deps/uv/src/unix/thread.c b/deps/uv/src/unix/thread.c new file mode 100644 index 0000000000..4a987a7159 --- /dev/null +++ b/deps/uv/src/unix/thread.c @@ -0,0 +1,154 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <pthread.h> +#include <assert.h> +#include <errno.h> + +#ifdef NDEBUG +# define CHECK(r) ((void) (r)) +#else +# include <stdio.h> +# include <stdlib.h> +# define CHECK(r) \ + do { \ + int __r = (r); \ + if (__r) errno = __r, perror(#r), abort(); \ + } \ + while (0) +#endif + + +int uv_thread_join(uv_thread_t *tid) { + if (pthread_join(*tid, NULL)) + return -1; + else + return 0; +} + + +int uv_mutex_init(uv_mutex_t* mutex) { + if (pthread_mutex_init(mutex, NULL)) + return -1; + else + return 0; +} + + +void uv_mutex_destroy(uv_mutex_t* mutex) { + CHECK(pthread_mutex_destroy(mutex)); +} + + +void uv_mutex_lock(uv_mutex_t* mutex) { + CHECK(pthread_mutex_lock(mutex)); +} + + +int uv_mutex_trylock(uv_mutex_t* mutex) { + int r; + + r = pthread_mutex_trylock(mutex); + + if (r && r != EAGAIN) + CHECK(r); + + if (r) + return -1; + else + return 0; +} + + +void uv_mutex_unlock(uv_mutex_t* mutex) { + CHECK(pthread_mutex_unlock(mutex)); +} + + +int uv_rwlock_init(uv_rwlock_t* rwlock) { + if (pthread_rwlock_init(rwlock, NULL)) + return -1; + else + return 0; +} + + +void uv_rwlock_destroy(uv_rwlock_t* rwlock) { + CHECK(pthread_rwlock_destroy(rwlock)); +} + + +void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { + CHECK(pthread_rwlock_rdlock(rwlock)); +} + + +int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { + int r; + + r = pthread_rwlock_tryrdlock(rwlock); + + if (r && r != EAGAIN) + CHECK(r); + + if (r) + return -1; + else + return 0; +} + + +void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { + CHECK(pthread_rwlock_unlock(rwlock)); +} + + +void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { + CHECK(pthread_rwlock_wrlock(rwlock)); +} + + +int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { + int r; + + r = pthread_rwlock_trywrlock(rwlock); + + if (r && r != EAGAIN) + CHECK(r); + + if (r) + return -1; + else + return 0; +} + + +void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { + CHECK(pthread_rwlock_unlock(rwlock)); +} + + +void uv_once(uv_once_t* guard, void (*callback)(void)) { + CHECK(pthread_once(guard, callback)); +} diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 349bfae3cd..ab495eba9e 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -26,63 +26,81 @@ #include <string.h> #include <errno.h> #include <stdlib.h> +#include <unistd.h> -static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w); static void uv__udp_run_completed(uv_udp_t* handle); static void uv__udp_run_pending(uv_udp_t* handle); -static void uv__udp_recvmsg(uv_udp_t* handle); -static void uv__udp_sendmsg(uv_udp_t* handle); -static void uv__udp_io(EV_P_ ev_io* w, int events); +static void uv__udp_recvmsg(EV_P_ ev_io* w, int revents); +static void uv__udp_sendmsg(EV_P_ ev_io* w, int revents); static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain); static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], int bufcnt, struct sockaddr* addr, socklen_t addrlen, uv_udp_send_cb send_cb); -static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w) { - int flags; +static void uv__udp_start_watcher(uv_udp_t* handle, + ev_io* w, + void (*cb)(EV_P_ ev_io*, int), + int flags) { + if (ev_is_active(w)) return; + ev_set_cb(w, cb); + ev_io_set(w, handle->fd, flags); + ev_io_start(handle->loop->ev, w); + ev_unref(handle->loop->ev); +} - if (ev_is_active(w)) { - return; - } - assert(w == &handle->read_watcher - || w == &handle->write_watcher); +static void uv__udp_stop_watcher(uv_udp_t* handle, ev_io* w) { + if (!ev_is_active(w)) return; + ev_ref(handle->loop->ev); + ev_io_stop(handle->loop->ev, w); + ev_io_set(w, -1, 0); + ev_set_cb(w, NULL); +} - flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); - w->data = handle; - ev_set_cb(w, uv__udp_io); - ev_io_set(w, handle->fd, flags); - ev_io_start(handle->loop->ev, w); - ev_unref(handle->loop->ev); +static void uv__udp_start_read_watcher(uv_udp_t* handle) { + uv__udp_start_watcher(handle, + &handle->read_watcher, + uv__udp_recvmsg, + EV_READ); } -void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w) { - int flags; +static void uv__udp_start_write_watcher(uv_udp_t* handle) { + uv__udp_start_watcher(handle, + &handle->write_watcher, + uv__udp_sendmsg, + EV_WRITE); +} - if (!ev_is_active(w)) { - return; - } - assert(w == &handle->read_watcher - || w == &handle->write_watcher); +static void uv__udp_stop_read_watcher(uv_udp_t* handle) { + uv__udp_stop_watcher(handle, &handle->read_watcher); +} - flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); - ev_ref(handle->loop->ev); - ev_io_stop(handle->loop->ev, w); - ev_io_set(w, -1, flags); - ev_set_cb(w, NULL); - w->data = (void*)0xDEADBABE; +static void uv__udp_stop_write_watcher(uv_udp_t* handle) { + uv__udp_stop_watcher(handle, &handle->write_watcher); +} + + +void uv__udp_start_close(uv_udp_t* handle) { + uv__udp_stop_write_watcher(handle); + uv__udp_stop_read_watcher(handle); + uv__close(handle->fd); + handle->fd = -1; } -void uv__udp_destroy(uv_udp_t* handle) { +void uv__udp_finish_close(uv_udp_t* handle) { uv_udp_send_t* req; ngx_queue_t* q; + assert(!ev_is_active(&handle->write_watcher)); + assert(!ev_is_active(&handle->read_watcher)); + assert(handle->fd == -1); + uv__udp_run_completed(handle); while (!ngx_queue_empty(&handle->write_queue)) { @@ -102,14 +120,6 @@ void uv__udp_destroy(uv_udp_t* handle) { handle->recv_cb = NULL; handle->alloc_cb = NULL; /* but _do not_ touch close_cb */ - - if (handle->fd != -1) { - uv__close(handle->fd); - handle->fd = -1; - } - - uv__udp_watcher_stop(handle, &handle->read_watcher); - uv__udp_watcher_stop(handle, &handle->write_watcher); } @@ -202,13 +212,18 @@ static void uv__udp_run_completed(uv_udp_t* handle) { } -static void uv__udp_recvmsg(uv_udp_t* handle) { +static void uv__udp_recvmsg(EV_P_ ev_io* w, int revents) { struct sockaddr_storage peer; struct msghdr h; + uv_udp_t* handle; ssize_t nread; uv_buf_t buf; int flags; + handle = container_of(w, uv_udp_t, read_watcher); + assert(handle->type == UV_UDP); + assert(revents & EV_READ); + assert(handle->recv_cb != NULL); assert(handle->alloc_cb != NULL); @@ -259,7 +274,13 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { } -static void uv__udp_sendmsg(uv_udp_t* handle) { +static void uv__udp_sendmsg(EV_P_ ev_io* w, int revents) { + uv_udp_t* handle; + + handle = container_of(w, uv_udp_t, write_watcher); + assert(handle->type == UV_UDP); + assert(revents & EV_WRITE); + assert(!ngx_queue_empty(&handle->write_queue) || !ngx_queue_empty(&handle->write_completed_queue)); @@ -275,28 +296,11 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { } else if (ngx_queue_empty(&handle->write_queue)) { /* Pending queue and completion queue empty, stop watcher. */ - uv__udp_watcher_stop(handle, &handle->write_watcher); + uv__udp_stop_write_watcher(handle); } } -static void uv__udp_io(EV_P_ ev_io* w, int events) { - uv_udp_t* handle; - - handle = w->data; - assert(handle != NULL); - assert(handle->type == UV_UDP); - assert(handle->fd >= 0); - assert(!(events & ~(EV_READ|EV_WRITE))); - - if (events & EV_READ) - uv__udp_recvmsg(handle); - - if (events & EV_WRITE) - uv__udp_sendmsg(handle); -} - - static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, @@ -452,7 +456,7 @@ static int uv__udp_send(uv_udp_send_t* req, memcpy(req->bufs, bufs, bufcnt * sizeof(bufs[0])); ngx_queue_insert_tail(&handle->write_queue, &req->queue); - uv__udp_watcher_start(handle, &handle->write_watcher); + uv__udp_start_write_watcher(handle); return 0; } @@ -654,14 +658,14 @@ int uv_udp_recv_start(uv_udp_t* handle, handle->alloc_cb = alloc_cb; handle->recv_cb = recv_cb; - uv__udp_watcher_start(handle, &handle->read_watcher); + uv__udp_start_read_watcher(handle); return 0; } int uv_udp_recv_stop(uv_udp_t* handle) { - uv__udp_watcher_stop(handle, &handle->read_watcher); + uv__udp_stop_read_watcher(handle); handle->alloc_cb = NULL; handle->recv_cb = NULL; return 0; diff --git a/deps/uv/src/unix/uv-eio.c b/deps/uv/src/unix/uv-eio.c index 84afe09b74..517d119142 100644 --- a/deps/uv/src/unix/uv-eio.c +++ b/deps/uv/src/unix/uv-eio.c @@ -27,16 +27,12 @@ #include <stdio.h> -/* TODO remove me! */ -static uv_loop_t* main_loop; - - static void uv_eio_do_poll(uv_idle_t* watcher, int status) { assert(watcher == &(watcher->loop->uv_eio_poller)); /* printf("uv_eio_poller\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) watcher)) { + if (eio_poll(&watcher->loop->uv_eio_channel) != -1 && uv_is_active((uv_handle_t*) watcher)) { /* printf("uv_eio_poller stop\n"); */ uv_idle_stop(watcher); uv_unref(watcher->loop); @@ -52,7 +48,7 @@ static void uv_eio_want_poll_notifier_cb(uv_async_t* watcher, int status) { /* printf("want poll notifier\n"); */ - if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { + if (eio_poll(&watcher->loop->uv_eio_channel) == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller start\n"); */ uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); uv_ref(loop); @@ -67,7 +63,7 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { /* printf("done poll notifier\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { + if (eio_poll(&watcher->loop->uv_eio_channel) != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller stop\n"); */ uv_idle_stop(&loop->uv_eio_poller); uv_unref(loop); @@ -79,7 +75,7 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { * uv_eio_want_poll() is called from the EIO thread pool each time an EIO * request (that is, one of the node.fs.* functions) has completed. */ -static void uv_eio_want_poll(void) { +static void uv_eio_want_poll(eio_channel *channel) { /* Signal the main thread that eio_poll need to be processed. */ /* @@ -87,25 +83,30 @@ static void uv_eio_want_poll(void) { * uv_eio_want_poll_notifier. */ - uv_async_send(&main_loop->uv_eio_want_poll_notifier); + uv_async_send(&((uv_loop_t *)channel->data)->uv_eio_want_poll_notifier); } -static void uv_eio_done_poll(void) { +static void uv_eio_done_poll(eio_channel *channel) { /* * Signal the main thread that we should stop calling eio_poll(). * from the idle watcher. */ - uv_async_send(&main_loop->uv_eio_done_poll_notifier); + uv_async_send(&((uv_loop_t *)channel->data)->uv_eio_done_poll_notifier); } +static void uv__eio_init(void) { + eio_init(uv_eio_want_poll, uv_eio_done_poll); +} + +static uv_once_t uv__eio_init_once_guard = UV_ONCE_INIT; + + void uv_eio_init(uv_loop_t* loop) { if (loop->counters.eio_init == 0) { loop->counters.eio_init++; - main_loop = loop; - uv_idle_init(loop, &loop->uv_eio_poller); uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); @@ -118,17 +119,6 @@ void uv_eio_init(uv_loop_t* loop) { uv_eio_done_poll_notifier_cb); uv_unref(loop); - eio_init(uv_eio_want_poll, uv_eio_done_poll); - /* - * Don't handle more than 10 reqs on each eio_poll(). This is to avoid - * race conditions. See Node's test/simple/test-eio-race.js - */ - eio_set_max_poll_reqs(10); - } else { - /* - * If this assertion breaks then Ryan hasn't implemented support for - * receiving thread pool requests back to multiple threads. - */ - assert(main_loop == loop); + uv_once(&uv__eio_init_once_guard, uv__eio_init); } } diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 3143bd2d80..0d1c3633c7 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -24,6 +24,7 @@ #include <assert.h> #include <stddef.h> /* NULL */ +#include <stdlib.h> /* malloc */ #include <string.h> /* memset */ /* use inet_pton from c-ares if necessary */ @@ -32,11 +33,38 @@ #include "ares/inet_ntop.h" -static uv_counters_t counters; +size_t uv_strlcpy(char* dst, const char* src, size_t size) { + size_t n; + if (size == 0) + return 0; -uv_counters_t* uv_counters() { - return &counters; + for (n = 0; n < (size - 1) && *src != '\0'; n++) + *dst++ = *src++; + + *dst = '\0'; + + return n; +} + + +size_t uv_strlcat(char* dst, const char* src, size_t size) { + size_t n; + + if (size == 0) + return 0; + + for (n = 0; n < size && *dst != '\0'; n++, dst++); + + if (n == size) + return n; + + while (n < (size - 1) && *src != '\0') + n++, *dst++ = *src++; + + *dst = '\0'; + + return n; } @@ -261,3 +289,53 @@ int uv_tcp_connect6(uv_connect_t* req, return uv__tcp_connect6(req, handle, address, cb); } + + +#ifdef _WIN32 +static UINT __stdcall uv__thread_start(void *ctx_v) +#else +static void *uv__thread_start(void *ctx_v) +#endif +{ + void (*entry)(void *arg); + void *arg; + + struct { + void (*entry)(void *arg); + void *arg; + } *ctx; + + ctx = ctx_v; + arg = ctx->arg; + entry = ctx->entry; + free(ctx); + entry(arg); + + return 0; +} + + +int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + struct { + void (*entry)(void *arg); + void *arg; + } *ctx; + + if ((ctx = malloc(sizeof *ctx)) == NULL) + return -1; + + ctx->entry = entry; + ctx->arg = arg; + +#ifdef _WIN32 + *tid = (HANDLE) _beginthreadex(NULL, 0, uv__thread_start, ctx, 0, NULL); + if (*tid == 0) { +#else + if (pthread_create(tid, NULL, uv__thread_start, ctx)) { +#endif + free(ctx); + return -1; + } + + return 0; +} diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 5d9903677f..bb0aba6ed5 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -29,7 +29,7 @@ #include "uv.h" -#define COUNTOF(a) (sizeof(a) / sizeof(a[0])) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) struct uv_ares_task_s { diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index b55c3cea48..8b44540a1a 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -86,6 +86,9 @@ static void uv_loop_init(uv_loop_t* loop) { loop->ares_active_sockets = 0; loop->ares_chan = NULL; + loop->active_tcp_streams = 0; + loop->active_udp_streams = 0; + loop->last_err = uv_ok_; } @@ -106,13 +109,26 @@ uv_loop_t* uv_default_loop(void) { uv_loop_t* uv_loop_new(void) { - assert(0 && "implement me"); - return NULL; + uv_loop_t* loop; + + /* Initialize libuv itself first */ + uv_once(&uv_init_guard_, uv_init); + + loop = (uv_loop_t*)malloc(sizeof(uv_loop_t)); + + if (!loop) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + uv_loop_init(loop); + return loop; } void uv_loop_delete(uv_loop_t* loop) { - assert(0 && "implement me"); + if (loop != &uv_default_loop_) { + free(loop); + } } @@ -181,7 +197,7 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, - COUNTOF(overlappeds), + ARRAY_SIZE(overlappeds), &count, timeout, FALSE); @@ -197,9 +213,8 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { } } - -#define UV_LOOP(loop, poll) \ - while ((loop)->refs > 0) { \ +#define UV_LOOP_ONCE(loop, poll) \ + do { \ uv_update_time((loop)); \ uv_process_timers((loop)); \ \ @@ -224,9 +239,24 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { (loop)->refs > 0); \ \ uv_check_invoke((loop)); \ + } while (0); + +#define UV_LOOP(loop, poll) \ + while ((loop)->refs > 0) { \ + UV_LOOP_ONCE(loop, poll) \ } +int uv_run_once(uv_loop_t* loop) { + if (pGetQueuedCompletionStatusEx) { + UV_LOOP_ONCE(loop, uv_poll_ex); + } else { + UV_LOOP_ONCE(loop, uv_poll); + } + return 0; +} + + int uv_run(uv_loop_t* loop) { if (pGetQueuedCompletionStatusEx) { UV_LOOP(loop, uv_poll_ex); diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c index 5a25e9d647..4fc9369389 100644 --- a/deps/uv/src/win/fs-event.c +++ b/deps/uv/src/win/fs-event.c @@ -178,7 +178,7 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, */ /* Convert to short path. */ - if (!GetShortPathNameW(filenamew, short_path, COUNTOF(short_path))) { + if (!GetShortPathNameW(filenamew, short_path, ARRAY_SIZE(short_path))) { last_error = GetLastError(); goto error; } diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 336349b1fa..0dc551dbaa 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -341,22 +341,4 @@ extern int uv_allow_ipv6; extern struct sockaddr_in uv_addr_ip4_any_; extern struct sockaddr_in6 uv_addr_ip6_any_; - -/* - * Threads and synchronization - */ -typedef struct uv_once_s { - unsigned char ran; - /* The actual event handle must be aligned to sizeof(HANDLE), so in */ - /* practice it might overlap padding a little. */ - HANDLE event; - HANDLE padding; -} uv_once_t; - -#define UV_ONCE_INIT \ - { 0, NULL, NULL } - -void uv_once(uv_once_t* guard, void (*callback)(void)); - - #endif /* UV_WIN_INTERNAL_H_ */ diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 3d64983545..84781d6314 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -545,12 +545,12 @@ wchar_t* make_program_env(char** env_block) { for (env = env_block; *env; env++) { check_required_vars_contains_var(required_vars, - COUNTOF(required_vars), + ARRAY_SIZE(required_vars), *env); env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(wchar_t)); } - for (i = 0; i < COUNTOF(required_vars); ++i) { + for (i = 0; i < ARRAY_SIZE(required_vars); ++i) { if (!required_vars[i].supplied) { env_len += required_vars[i].len * sizeof(wchar_t); var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); @@ -577,7 +577,7 @@ wchar_t* make_program_env(char** env_block) { } } - for (i = 0; i < COUNTOF(required_vars); ++i) { + for (i = 0; i < ARRAY_SIZE(required_vars); ++i) { if (!required_vars[i].supplied) { wcscpy(ptr, required_vars[i].wide); ptr += required_vars[i].len - 1; @@ -675,7 +675,7 @@ static void close_child_stdio(uv_process_t* process) { int i; HANDLE handle; - for (i = 0; i < COUNTOF(process->child_stdio); i++) { + for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) { handle = process->child_stdio[i]; if (handle != NULL && handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); @@ -1048,7 +1048,7 @@ done: /* We're keeping the handles open, the thread pool is going to have */ /* it's way with them. But at least make them non-inheritable. */ int i; - for (i = 0; i < COUNTOF(process->child_stdio); i++) { + for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) { SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0); } } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 7965f73aa4..f810913f30 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -42,10 +42,6 @@ const unsigned int uv_simultaneous_server_accepts = 32; /* A zero-size buffer for use by uv_tcp_read */ static char uv_zero_[] = ""; -/* Counter to keep track of active tcp streams */ -static unsigned int active_tcp_streams = 0; - - static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) { if (setsockopt(socket, IPPROTO_TCP, @@ -217,7 +213,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { handle->close_cb((uv_handle_t*)handle); } - active_tcp_streams--; + loop->active_tcp_streams--; uv_unref(loop); } @@ -399,7 +395,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { * Preallocate a read buffer if the number of active streams is below * the threshold. */ - if (active_tcp_streams < uv_active_tcp_streams_threshold) { + if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->read_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); assert(handle->read_buffer.len > 0); @@ -559,7 +555,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { } } - active_tcp_streams++; + loop->active_tcp_streams++; return rv; } @@ -1007,7 +1003,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, NULL, 0) == 0) { uv_connection_init((uv_stream_t*)handle); - active_tcp_streams++; + loop->active_tcp_streams++; ((uv_connect_cb)req->cb)(req, 0); } else { uv__set_sys_error(loop, WSAGetLastError()); diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c new file mode 100644 index 0000000000..d6d3ce8f39 --- /dev/null +++ b/deps/uv/src/win/thread.c @@ -0,0 +1,332 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" +#include <assert.h> + +#define HAVE_SRWLOCK_API() (pTryAcquireSRWLockShared != NULL) + +#ifdef _MSC_VER /* msvc */ +# define inline __inline +# define NOINLINE __declspec (noinline) +#else /* gcc */ +# define inline inline +# define NOINLINE __attribute__ ((noinline)) +#endif + + +inline static int uv__rwlock_srwlock_init(uv_rwlock_t* rwlock); +inline static void uv__rwlock_srwlock_destroy(uv_rwlock_t* rwlock); +inline static void uv__rwlock_srwlock_rdlock(uv_rwlock_t* rwlock); +inline static int uv__rwlock_srwlock_tryrdlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_srwlock_rdunlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_srwlock_wrlock(uv_rwlock_t* rwlock); +inline static int uv__rwlock_srwlock_trywrlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_srwlock_wrunlock(uv_rwlock_t* rwlock); + +inline static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock); +inline static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock); +inline static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock); +inline static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock); +inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock); +inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock); + + +static NOINLINE void uv__once_inner(uv_once_t* guard, + void (*callback)(void)) { + DWORD result; + HANDLE existing_event, created_event; + HANDLE* event_ptr; + + /* Fetch and align event_ptr */ + event_ptr = (HANDLE*) (((uintptr_t) &guard->event + (sizeof(HANDLE) - 1)) & + ~(sizeof(HANDLE) - 1)); + + created_event = CreateEvent(NULL, 1, 0, NULL); + if (created_event == 0) { + /* Could fail in a low-memory situation? */ + uv_fatal_error(GetLastError(), "CreateEvent"); + } + + existing_event = InterlockedCompareExchangePointer(event_ptr, + created_event, + NULL); + + if (existing_event == NULL) { + /* We won the race */ + callback(); + + result = SetEvent(created_event); + assert(result); + guard->ran = 1; + + } else { + /* We lost the race. Destroy the event we created and wait for the */ + /* existing one to become signaled. */ + CloseHandle(created_event); + result = WaitForSingleObject(existing_event, INFINITE); + assert(result == WAIT_OBJECT_0); + } +} + + +void uv_once(uv_once_t* guard, void (*callback)(void)) { + /* Fast case - avoid WaitForSingleObject. */ + if (guard->ran) { + return; + } + + uv__once_inner(guard, callback); +} + + +int uv_thread_join(uv_thread_t *tid) { + if (WaitForSingleObject(*tid, INFINITE)) + return -1; + else { + CloseHandle(*tid); + *tid = 0; + return 0; + } +} + + +int uv_mutex_init(uv_mutex_t* mutex) { + InitializeCriticalSection(mutex); + return 0; +} + + +void uv_mutex_destroy(uv_mutex_t* mutex) { + DeleteCriticalSection(mutex); +} + + +void uv_mutex_lock(uv_mutex_t* mutex) { + EnterCriticalSection(mutex); +} + + +int uv_mutex_trylock(uv_mutex_t* mutex) { + if (TryEnterCriticalSection(mutex)) + return 0; + else + return -1; +} + + +void uv_mutex_unlock(uv_mutex_t* mutex) { + LeaveCriticalSection(mutex); +} + + +int uv_rwlock_init(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + return uv__rwlock_srwlock_init(rwlock); + else + return uv__rwlock_fallback_init(rwlock); +} + + +void uv_rwlock_destroy(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + uv__rwlock_srwlock_destroy(rwlock); + else + uv__rwlock_fallback_destroy(rwlock); +} + + +void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + uv__rwlock_srwlock_rdlock(rwlock); + else + uv__rwlock_fallback_rdlock(rwlock); +} + + +int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + return uv__rwlock_srwlock_tryrdlock(rwlock); + else + return uv__rwlock_fallback_tryrdlock(rwlock); +} + + +void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + uv__rwlock_srwlock_rdunlock(rwlock); + else + uv__rwlock_fallback_rdunlock(rwlock); +} + + +void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + uv__rwlock_srwlock_wrlock(rwlock); + else + uv__rwlock_fallback_wrlock(rwlock); +} + + +int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + return uv__rwlock_srwlock_trywrlock(rwlock); + else + return uv__rwlock_fallback_trywrlock(rwlock); +} + + +void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { + if (HAVE_SRWLOCK_API()) + uv__rwlock_srwlock_wrunlock(rwlock); + else + uv__rwlock_fallback_wrunlock(rwlock); +} + + +inline static int uv__rwlock_srwlock_init(uv_rwlock_t* rwlock) { + pInitializeSRWLock(&rwlock->srwlock_); + return 0; +} + + +inline static void uv__rwlock_srwlock_destroy(uv_rwlock_t* rwlock) { + (void) rwlock; +} + + +inline static void uv__rwlock_srwlock_rdlock(uv_rwlock_t* rwlock) { + pAcquireSRWLockShared(&rwlock->srwlock_); +} + + +inline static int uv__rwlock_srwlock_tryrdlock(uv_rwlock_t* rwlock) { + if (pTryAcquireSRWLockShared(&rwlock->srwlock_)) + return 0; + else + return -1; +} + + +inline static void uv__rwlock_srwlock_rdunlock(uv_rwlock_t* rwlock) { + pReleaseSRWLockShared(&rwlock->srwlock_); +} + + +inline static void uv__rwlock_srwlock_wrlock(uv_rwlock_t* rwlock) { + pAcquireSRWLockExclusive(&rwlock->srwlock_); +} + + +inline static int uv__rwlock_srwlock_trywrlock(uv_rwlock_t* rwlock) { + if (pTryAcquireSRWLockExclusive(&rwlock->srwlock_)) + return 0; + else + return -1; +} + + +inline static void uv__rwlock_srwlock_wrunlock(uv_rwlock_t* rwlock) { + pReleaseSRWLockExclusive(&rwlock->srwlock_); +} + + +inline static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock) { + if (uv_mutex_init(&rwlock->fallback_.read_mutex_)) + return -1; + + if (uv_mutex_init(&rwlock->fallback_.write_mutex_)) { + uv_mutex_destroy(&rwlock->fallback_.read_mutex_); + return -1; + } + + rwlock->fallback_.num_readers_ = 0; + + return 0; +} + + +inline static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock) { + uv_mutex_destroy(&rwlock->fallback_.read_mutex_); + uv_mutex_destroy(&rwlock->fallback_.write_mutex_); +} + + +inline static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock) { + uv_mutex_lock(&rwlock->fallback_.read_mutex_); + + if (++rwlock->fallback_.num_readers_ == 1) + uv_mutex_lock(&rwlock->fallback_.write_mutex_); + + uv_mutex_unlock(&rwlock->fallback_.read_mutex_); +} + + +inline static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock) { + int ret; + + ret = -1; + + if (uv_mutex_trylock(&rwlock->fallback_.read_mutex_)) + goto out; + + if (rwlock->fallback_.num_readers_ == 0) + ret = uv_mutex_trylock(&rwlock->fallback_.write_mutex_); + else + ret = 0; + + if (ret == 0) + rwlock->fallback_.num_readers_++; + + uv_mutex_unlock(&rwlock->fallback_.read_mutex_); + +out: + return ret; +} + + +inline static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock) { + uv_mutex_lock(&rwlock->fallback_.read_mutex_); + + if (--rwlock->fallback_.num_readers_ == 0) + uv_mutex_unlock(&rwlock->fallback_.write_mutex_); + + uv_mutex_unlock(&rwlock->fallback_.read_mutex_); +} + + +inline static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock) { + uv_mutex_lock(&rwlock->fallback_.write_mutex_); +} + + +inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock) { + return uv_mutex_trylock(&rwlock->fallback_.write_mutex_); +} + + +inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock) { + uv_mutex_unlock(&rwlock->fallback_.write_mutex_); +} diff --git a/deps/uv/src/win/threads.c b/deps/uv/src/win/threads.c deleted file mode 100644 index 1fc6b73f87..0000000000 --- a/deps/uv/src/win/threads.c +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <assert.h> - -#include "uv.h" -#include "../uv-common.h" -#include "internal.h" - - -#ifdef _MSC_VER /* msvc */ -# define NOINLINE __declspec (noinline) -#else /* gcc */ -# define NOINLINE __attribute__ ((noinline)) -#endif - - -static NOINLINE void uv__once_inner(uv_once_t* guard, - void (*callback)(void)) { - DWORD result; - HANDLE existing_event, created_event; - HANDLE* event_ptr; - - /* Fetch and align event_ptr */ - event_ptr = (HANDLE*) (((uintptr_t) &guard->event + (sizeof(HANDLE) - 1)) & - ~(sizeof(HANDLE) - 1)); - - created_event = CreateEvent(NULL, 1, 0, NULL); - if (created_event == 0) { - /* Could fail in a low-memory situation? */ - uv_fatal_error(GetLastError(), "CreateEvent"); - } - - existing_event = InterlockedCompareExchangePointer(event_ptr, - created_event, - NULL); - - if (existing_event == NULL) { - /* We won the race */ - callback(); - - result = SetEvent(created_event); - assert(result); - guard->ran = 1; - - } else { - /* We lost the race. Destroy the event we created and wait for the */ - /* existing one to become signaled. */ - CloseHandle(created_event); - result = WaitForSingleObject(existing_event, INFINITE); - assert(result == WAIT_OBJECT_0); - } -} - - -void uv_once(uv_once_t* guard, void (*callback)(void)) { - /* Fast case - avoid WaitForSingleObject. */ - if (guard->ran) { - return; - } - - uv__once_inner(guard, callback); -} diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index 556875d433..dd2fdbcabc 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -1401,7 +1401,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, /* We were not currently parsing a number */ /* Check for too many arguments */ - if (handle->ansi_csi_argc >= COUNTOF(handle->ansi_csi_argv)) { + if (handle->ansi_csi_argc >= ARRAY_SIZE(handle->ansi_csi_argv)) { ansi_parser_state |= ANSI_IGNORE; continue; } @@ -1437,7 +1437,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, /* If ANSI_IN_ARG is not set, add another argument and */ /* default it to 0. */ /* Check for too many arguments */ - if (handle->ansi_csi_argc >= COUNTOF(handle->ansi_csi_argv)) { + if (handle->ansi_csi_argc >= ARRAY_SIZE(handle->ansi_csi_argv)) { ansi_parser_state |= ANSI_IGNORE; continue; } @@ -1617,7 +1617,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, /* If a \n immediately follows a \r or vice versa, ignore it. */ if (previous_eol == 0 || utf8_codepoint == previous_eol) { /* If there's no room in the utf16 buf, flush it first. */ - if (2 > COUNTOF(utf16_buf) - utf16_buf_used) { + if (2 > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); utf16_buf_used = 0; } @@ -1634,7 +1634,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, /* Encode character into utf-16 buffer. */ /* If there's no room in the utf16 buf, flush it first. */ - if (1 > COUNTOF(utf16_buf) - utf16_buf_used) { + if (1 > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); utf16_buf_used = 0; } diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 39221de6f7..ad05164dff 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -34,10 +34,6 @@ const unsigned int uv_active_udp_streams_threshold = 0; /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; -/* Counter to keep track of active udp streams */ -static unsigned int active_udp_streams = 0; - - int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen) { uv_loop_t* loop = handle->loop; @@ -267,7 +263,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { * Preallocate a read buffer if the number of active streams is below * the threshold. */ - if (active_udp_streams < uv_active_udp_streams_threshold) { + if (loop->active_udp_streams < uv_active_udp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); @@ -355,7 +351,7 @@ int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, } handle->flags |= UV_HANDLE_READING; - active_udp_streams++; + loop->active_udp_streams++; handle->recv_cb = recv_cb; handle->alloc_cb = alloc_cb; @@ -372,7 +368,7 @@ int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, int uv_udp_recv_stop(uv_udp_t* handle) { if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; - active_udp_streams--; + handle->loop->active_udp_streams--; } return 0; diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 0fcfb9c3d2..15618e928d 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -22,13 +22,33 @@ #include <assert.h> #include <direct.h> #include <malloc.h> +#include <stdio.h> #include <string.h> #include <time.h> #include "uv.h" #include "internal.h" #include "tlhelp32.h" +#include "psapi.h" +#include "iphlpapi.h" + + +/* + * Max title length; the only thing MSDN tells us about the maximum length + * of the console title is that it is smaller than 64K. However in practice + * it is much smaller, and there is no way to figure out what the exact length + * of the title is or can be, at least not on XP. To make it even more + * annoying, GetConsoleTitle failes when the buffer to be read into is bigger + * than the actual maximum length. So we make a conservative guess here; + * just don't put the novel you're writing in the title, unless the plot + * survives truncation. + */ +#define MAX_TITLE_LENGTH 8192 + +static char *process_title; +static uv_once_t uv_process_title_init_guard_ = UV_ONCE_INIT; +static CRITICAL_SECTION process_title_lock; int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size) { @@ -239,6 +259,343 @@ int uv_parent_pid() { } +char** uv_setup_args(int argc, char** argv) { + return argv; +} + + +static void uv_process_title_init(void) { + InitializeCriticalSection(&process_title_lock); +} + + +uv_err_t uv_set_process_title(const char* title) { + uv_err_t err; + int length; + wchar_t* title_w = NULL; + + uv_once(&uv_process_title_init_guard_, uv_process_title_init); + + /* Find out how big the buffer for the wide-char title must be */ + length = uv_utf8_to_utf16(title, NULL, 0); + if (!length) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + /* Convert to wide-char string */ + title_w = (wchar_t*)malloc(sizeof(wchar_t) * length); + if (!title_w) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + length = uv_utf8_to_utf16(title, title_w, length); + if (!length) { + err = uv__new_sys_error(GetLastError()); + goto done; + }; + + /* If the title must be truncated insert a \0 terminator there */ + if (length > MAX_TITLE_LENGTH) { + title_w[MAX_TITLE_LENGTH - 1] = L'\0'; + } + + if (!SetConsoleTitleW(title_w)) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + EnterCriticalSection(&process_title_lock); + free(process_title); + process_title = strdup(title); + LeaveCriticalSection(&process_title_lock); + + err = uv_ok_; + +done: + free(title_w); + return err; +} + + +static int uv__get_process_title() { + wchar_t title_w[MAX_TITLE_LENGTH]; + int length; + + if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { + return -1; + } + + /* Find out what the size of the buffer is that we need */ + length = uv_utf16_to_utf8(title_w, -1, NULL, 0); + if (!length) { + return -1; + } + + assert(!process_title); + process_title = (char*)malloc(length); + if (!process_title) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + /* Do utf16 -> utf8 conversion here */ + if (!uv_utf16_to_utf8(title_w, -1, process_title, length)) { + free(process_title); + return -1; + } + + return 0; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + uv_once(&uv_process_title_init_guard_, uv_process_title_init); + + EnterCriticalSection(&process_title_lock); + /* + * If the process_title was never read before nor explicitly set, + * we must query it with getConsoleTitleW + */ + if (!process_title && uv__get_process_title() == -1) { + return uv__new_sys_error(GetLastError()); + } + + assert(process_title); + strncpy(buffer, process_title, size); + LeaveCriticalSection(&process_title_lock); + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + HANDLE current_process; + PROCESS_MEMORY_COUNTERS pmc; + + current_process = GetCurrentProcess(); + + if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { + return uv__new_sys_error(GetLastError()); + } + + *rss = pmc.WorkingSetSize; + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + *uptime = (double)GetTickCount()/1000.0; + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + uv_err_t err; + char key[128]; + HKEY processor_key = NULL; + DWORD cpu_speed = 0; + DWORD cpu_speed_length = sizeof(cpu_speed); + char cpu_brand[256]; + DWORD cpu_brand_length = sizeof(cpu_brand); + SYSTEM_INFO system_info; + uv_cpu_info_t* cpu_info; + unsigned int i; + + GetSystemInfo(&system_info); + + *cpu_infos = (uv_cpu_info_t*)malloc(system_info.dwNumberOfProcessors * + sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + *count = 0; + + for (i = 0; i < system_info.dwNumberOfProcessors; i++) { + _snprintf(key, sizeof(key), "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", i); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, + &processor_key) != ERROR_SUCCESS) { + if (i == 0) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + continue; + } + + if (RegQueryValueEx(processor_key, "~MHz", NULL, NULL, + (LPBYTE)&cpu_speed, &cpu_speed_length) + != ERROR_SUCCESS) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + if (RegQueryValueEx(processor_key, "ProcessorNameString", NULL, NULL, + (LPBYTE)&cpu_brand, &cpu_brand_length) + != ERROR_SUCCESS) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + RegCloseKey(processor_key); + processor_key = NULL; + + cpu_info = &(*cpu_infos)[i]; + + /* $TODO: find times on windows */ + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.nice = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + + cpu_info->model = strdup(cpu_brand); + cpu_info->speed = cpu_speed; + + (*count)++; + } + + err = uv_ok_; + +done: + if (processor_key) { + RegCloseKey(processor_key); + } + + if (err.code != UV_OK) { + free(*cpu_infos); + *cpu_infos = NULL; + *count = 0; + } + + return err; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + unsigned long size = 0; + IP_ADAPTER_ADDRESSES* adapter_addresses; + IP_ADAPTER_ADDRESSES* adapter_address; + IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address; + uv_interface_address_t* address; + struct sockaddr* sock_addr; + int length; + char* name; + + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size) + != ERROR_BUFFER_OVERFLOW) { + return uv__new_sys_error(GetLastError()); + } + + adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(size); + if (!adapter_addresses) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapter_addresses, &size) + != ERROR_SUCCESS) { + return uv__new_sys_error(GetLastError()); + } + + /* Count the number of interfaces */ + *count = 0; + + for (adapter_address = adapter_addresses; + adapter_address != NULL; + adapter_address = adapter_address->Next) { + unicast_address = adapter_address->FirstUnicastAddress; + while (unicast_address) { + (*count)++; + unicast_address = unicast_address->Next; + } + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + address = *addresses; + + for (adapter_address = adapter_addresses; + adapter_address != NULL; + adapter_address = adapter_address->Next) { + name = NULL; + unicast_address = adapter_address->FirstUnicastAddress; + + while (unicast_address) { + sock_addr = unicast_address->Address.lpSockaddr; + if (sock_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)sock_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)sock_addr); + } + + address->is_internal = + adapter_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK ? 1 : 0; + + if (!name) { + /* Convert FriendlyName to utf8 */ + length = uv_utf16_to_utf8(adapter_address->FriendlyName, -1, NULL, 0); + if (length) { + name = (char*)malloc(length); + if (!name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf16_to_utf8(adapter_address->FriendlyName, -1, name, + length)) { + free(name); + name = NULL; + } + } + } + + assert(name); + address->name = name; + + unicast_address = unicast_address->Next; + address++; + } + } + + free(adapter_addresses); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + char* freed_name = NULL; + + for (i = 0; i < count; i++) { + if (freed_name != addresses[i].name) { + freed_name = addresses[i].name; + free(freed_name); + } + } + + free(addresses); +} + + void uv_filetime_to_time_t(FILETIME* file_time, time_t* stat_time) { FILETIME local_time; SYSTEMTIME system_time; diff --git a/deps/uv/src/win/winapi.c b/deps/uv/src/win/winapi.c index cc21361bc7..ff6912d0e8 100644 --- a/deps/uv/src/win/winapi.c +++ b/deps/uv/src/win/winapi.c @@ -33,6 +33,13 @@ sNtSetInformationFile pNtSetInformationFile; sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; sCreateSymbolicLinkW pCreateSymbolicLinkW; +sInitializeSRWLock pInitializeSRWLock; +sAcquireSRWLockShared pAcquireSRWLockShared; +sAcquireSRWLockExclusive pAcquireSRWLockExclusive; +sTryAcquireSRWLockShared pTryAcquireSRWLockShared; +sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive; +sReleaseSRWLockShared pReleaseSRWLockShared; +sReleaseSRWLockExclusive pReleaseSRWLockExclusive; void uv_winapi_init() { @@ -86,4 +93,25 @@ void uv_winapi_init() { pCreateSymbolicLinkW = (sCreateSymbolicLinkW) GetProcAddress(kernel32_module, "CreateSymbolicLinkW"); + + pInitializeSRWLock = (sInitializeSRWLock) + GetProcAddress(kernel32_module, "InitializeSRWLock"); + + pAcquireSRWLockShared = (sAcquireSRWLockShared) + GetProcAddress(kernel32_module, "AcquireSRWLockShared"); + + pAcquireSRWLockExclusive = (sAcquireSRWLockExclusive) + GetProcAddress(kernel32_module, "AcquireSRWLockExclusive"); + + pTryAcquireSRWLockShared = (sTryAcquireSRWLockShared) + GetProcAddress(kernel32_module, "TryAcquireSRWLockShared"); + + pTryAcquireSRWLockExclusive = (sTryAcquireSRWLockExclusive) + GetProcAddress(kernel32_module, "TryAcquireSRWLockExclusive"); + + pReleaseSRWLockShared = (sReleaseSRWLockShared) + GetProcAddress(kernel32_module, "ReleaseSRWLockShared"); + + pReleaseSRWLockExclusive = (sReleaseSRWLockExclusive) + GetProcAddress(kernel32_module, "ReleaseSRWLockExclusive"); } diff --git a/deps/uv/src/win/winapi.h b/deps/uv/src/win/winapi.h index b65ef0d030..c9552ed241 100644 --- a/deps/uv/src/win/winapi.h +++ b/deps/uv/src/win/winapi.h @@ -4367,6 +4367,27 @@ typedef BOOLEAN (WINAPI* sCreateSymbolicLinkW) LPCWSTR lpTargetFileName, DWORD dwFlags); +typedef VOID (WINAPI* sInitializeSRWLock) + (PSRWLOCK SRWLock); + +typedef VOID (WINAPI* sAcquireSRWLockShared) + (PSRWLOCK SRWLock); + +typedef VOID (WINAPI* sAcquireSRWLockExclusive) + (PSRWLOCK SRWLock); + +typedef BOOL (WINAPI* sTryAcquireSRWLockShared) + (PSRWLOCK SRWLock); + +typedef BOOL (WINAPI* sTryAcquireSRWLockExclusive) + (PSRWLOCK SRWLock); + +typedef VOID (WINAPI* sReleaseSRWLockShared) + (PSRWLOCK SRWLock); + +typedef VOID (WINAPI* sReleaseSRWLockExclusive) + (PSRWLOCK SRWLock); + /* Ntapi function pointers */ @@ -4380,5 +4401,12 @@ extern sNtSetInformationFile pNtSetInformationFile; extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; extern sCreateSymbolicLinkW pCreateSymbolicLinkW; +extern sInitializeSRWLock pInitializeSRWLock; +extern sAcquireSRWLockShared pAcquireSRWLockShared; +extern sAcquireSRWLockExclusive pAcquireSRWLockExclusive; +extern sTryAcquireSRWLockShared pTryAcquireSRWLockShared; +extern sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive; +extern sReleaseSRWLockShared pReleaseSRWLockShared; +extern sReleaseSRWLockExclusive pReleaseSRWLockExclusive; #endif /* UV_WIN_WINAPI_H_ */ diff --git a/deps/uv/src/win/winsock.h b/deps/uv/src/win/winsock.h index 954ad5ce16..79b9e6b2c4 100644 --- a/deps/uv/src/win/winsock.h +++ b/deps/uv/src/win/winsock.h @@ -23,6 +23,7 @@ #define UV_WIN_WINSOCK_H_ #include <winsock2.h> +#include <iptypes.h> #include <mswsock.h> #include <ws2tcpip.h> #include <windows.h> @@ -111,4 +112,27 @@ typedef struct _AFD_RECV_INFO { #define IOCTL_AFD_RECEIVE_DATAGRAM \ _AFD_CONTROL_CODE(AFD_RECEIVE_DATAGRAM, METHOD_NEITHER) +#if defined(__MINGW32__) && !defined(__MINGW64__) + +typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP { + /* FIXME: __C89_NAMELESS was removed */ + /* __C89_NAMELESS */ union { + ULONGLONG Alignment; + /* __C89_NAMELESS */ struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next; + SOCKET_ADDRESS Address; + IP_PREFIX_ORIGIN PrefixOrigin; + IP_SUFFIX_ORIGIN SuffixOrigin; + IP_DAD_STATE DadState; + ULONG ValidLifetime; + ULONG PreferredLifetime; + ULONG LeaseLifetime; +} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP; + +#endif + #endif /* UV_WIN_WINSOCK_H_ */ diff --git a/deps/uv/test/benchmark-list.h b/deps/uv/test/benchmark-list.h index 0a72fb9861..deb5ed1f74 100644 --- a/deps/uv/test/benchmark-list.h +++ b/deps/uv/test/benchmark-list.h @@ -43,6 +43,7 @@ BENCHMARK_DECLARE (udp_packet_storm_1000v1000) BENCHMARK_DECLARE (gethostbyname) BENCHMARK_DECLARE (getaddrinfo) BENCHMARK_DECLARE (spawn) +BENCHMARK_DECLARE (thread_create) HELPER_DECLARE (tcp4_blackhole_server) HELPER_DECLARE (tcp_pump_server) HELPER_DECLARE (pipe_pump_server) @@ -100,4 +101,5 @@ TASK_LIST_START BENCHMARK_ENTRY (getaddrinfo) BENCHMARK_ENTRY (spawn) + BENCHMARK_ENTRY (thread_create) TASK_LIST_END diff --git a/deps/uv/test/benchmark-tcp-write-batch.c b/deps/uv/test/benchmark-tcp-write-batch.c index 77bb0191b1..0b15f44b0a 100644 --- a/deps/uv/test/benchmark-tcp-write-batch.c +++ b/deps/uv/test/benchmark-tcp-write-batch.c @@ -23,15 +23,11 @@ #include "task.h" #include <stdio.h> -#include <stddef.h> #include <stdlib.h> #define WRITE_REQ_DATA "Hello, world." #define NUM_WRITE_REQS (1000 * 1000) -#define container_of(ptr, type, member) \ - ((type *) ((char *) (ptr) - offsetof(type, member))) - typedef struct { uv_write_t req; uv_buf_t buf; diff --git a/deps/uv/test/benchmark-thread.c b/deps/uv/test/benchmark-thread.c new file mode 100644 index 0000000000..cc7fd938aa --- /dev/null +++ b/deps/uv/test/benchmark-thread.c @@ -0,0 +1,64 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include <stdio.h> +#include <stdlib.h> + +#define NUM_THREADS (100 * 1000) + +static volatile int num_threads; + + +static void thread_entry(void* arg) { + ASSERT(arg == (void *) 42); + num_threads++; + /* FIXME write barrier? */ +} + + +BENCHMARK_IMPL(thread_create) { + uint64_t start_time; + double duration; + uv_thread_t tid; + int i, r; + + start_time = uv_hrtime(); + + for (i = 0; i < NUM_THREADS; i++) { + r = uv_thread_create(&tid, thread_entry, (void *) 42); + ASSERT(r == 0); + + r = uv_thread_join(&tid); + ASSERT(r == 0); + } + + duration = (uv_hrtime() - start_time) / 1e9; + + ASSERT(num_threads == NUM_THREADS); + + printf("%d threads created in %.2f seconds (%.0f/s)\n", + NUM_THREADS, duration, NUM_THREADS / duration); + + return 0; +} diff --git a/deps/uv/test/benchmark-udp-packet-storm.c b/deps/uv/test/benchmark-udp-packet-storm.c index 24a9e1b920..5ffa4e05e1 100644 --- a/deps/uv/test/benchmark-udp-packet-storm.c +++ b/deps/uv/test/benchmark-udp-packet-storm.c @@ -35,8 +35,6 @@ #define BASE_PORT 12345 -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) - static uv_loop_t* loop; static int n_senders_; diff --git a/deps/uv/test/blackhole-server.c b/deps/uv/test/blackhole-server.c index 85a0efc26b..765bb32178 100644 --- a/deps/uv/test/blackhole-server.c +++ b/deps/uv/test/blackhole-server.c @@ -23,12 +23,8 @@ #include "task.h" #include <stdio.h> -#include <stddef.h> #include <stdlib.h> -#define container_of(ptr, type, member) \ - ((type *) ((char *) (ptr) - offsetof(type, member))) - typedef struct { uv_tcp_t handle; uv_shutdown_t shutdown_req; diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index a3e08fc096..a101305bf5 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -38,6 +38,8 @@ static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { platform_init(argc, argv); + argv = uv_setup_args(argc, argv); + switch (argc) { case 1: return run_tests(TEST_TIMEOUT, 0); case 2: return maybe_run_test(argc, argv); @@ -202,8 +204,8 @@ static int stdio_over_pipes_helper() { "\n" }; - uv_write_t write_req[COUNTOF(buffers)]; - uv_buf_t buf[COUNTOF(buffers)]; + uv_write_t write_req[ARRAY_SIZE(buffers)]; + uv_buf_t buf[ARRAY_SIZE(buffers)]; int r, i; uv_loop_t* loop = uv_default_loop(); @@ -222,11 +224,11 @@ static int stdio_over_pipes_helper() { uv_unref(loop); uv_unref(loop); - for (i = 0; i < COUNTOF(buffers); i++) { + for (i = 0; i < ARRAY_SIZE(buffers); i++) { buf[i] = uv_buf_init((char*)buffers[i], strlen(buffers[i])); } - for (i = 0; i < COUNTOF(buffers); i++) { + for (i = 0; i < ARRAY_SIZE(buffers); i++) { r = uv_write(&write_req[i], (uv_stream_t*)&stdout_pipe, &buf[i], 1, after_pipe_write); ASSERT(r == 0); diff --git a/deps/uv/test/runner.c b/deps/uv/test/runner.c index fdd8168411..8c7bd7f053 100644 --- a/deps/uv/test/runner.c +++ b/deps/uv/test/runner.c @@ -191,8 +191,10 @@ out: } /* Show error and output from processes if the test failed. */ - if (status != 0) { - LOGF("\n`%s` failed: %s\n", test, errmsg); + if (status != 0 || task->show_output) { + if (status != 0) { + LOGF("\n`%s` failed: %s\n", test, errmsg); + } for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { diff --git a/deps/uv/test/runner.h b/deps/uv/test/runner.h index 3b93ffe991..5cee695ed2 100644 --- a/deps/uv/test/runner.h +++ b/deps/uv/test/runner.h @@ -40,6 +40,7 @@ typedef struct { char *process_name; int (*main)(); int is_helper; + int show_output; } task_entry_t, bench_entry_t; @@ -57,19 +58,22 @@ typedef struct { int run_test_##name(); #define TEST_ENTRY(name) \ - { #name, #name, &run_test_##name, 0 }, + { #name, #name, &run_test_##name, 0, 0 }, + +#define TEST_OUTPUT_ENTRY(name) \ + { #name, #name, &run_test_##name, 0, 1 }, #define BENCHMARK_DECLARE(name) \ int run_benchmark_##name(); #define BENCHMARK_ENTRY(name) \ - { #name, #name, &run_benchmark_##name, 0 }, + { #name, #name, &run_benchmark_##name, 0, 0 }, #define HELPER_DECLARE(name) \ int run_helper_##name(); #define HELPER_ENTRY(task_name, name) \ - { #task_name, #name, &run_helper_##name, 1 }, + { #task_name, #name, &run_helper_##name, 1, 0 }, #define TEST_HELPER HELPER_ENTRY #define BENCHMARK_HELPER HELPER_ENTRY diff --git a/deps/uv/test/task.h b/deps/uv/test/task.h index b553f862fc..a7938e6064 100644 --- a/deps/uv/test/task.h +++ b/deps/uv/test/task.h @@ -22,9 +22,9 @@ #ifndef TASK_H_ #define TASK_H_ - -#include <stdint.h> #include <stdio.h> +#include <stddef.h> +#include <stdint.h> #include <stdlib.h> #define TEST_PORT 9123 @@ -38,7 +38,10 @@ # define TEST_PIPENAME_2 "/tmp/uv-test-sock2" #endif -#define COUNTOF(a) (sizeof(a) / sizeof(a[0])) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) typedef enum { TCP = 0, diff --git a/deps/uv/test/test-eio-overflow.c b/deps/uv/test/test-eio-overflow.c new file mode 100644 index 0000000000..de3d65b754 --- /dev/null +++ b/deps/uv/test/test-eio-overflow.c @@ -0,0 +1,90 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include <stdio.h> +#include <stdlib.h> + + +static uv_idle_t idle; + +static const int max_opened = 10000; +static const int max_delta = 4000; + +static int opened = 0; +static int closed = 0; + + +void work_cb(uv_work_t* work) { + /* continue as fast as possible */ +} + + +void after_work_cb(uv_work_t* work) { + free(work); + closed++; +} + + +void make_eio_req(void) { + uv_work_t* w; + + opened++; + + w = (uv_work_t*) malloc(sizeof(*w)); + ASSERT(w != NULL); + + uv_queue_work(uv_default_loop(), w, work_cb, after_work_cb); +} + + +void idle_cb(uv_idle_t* idle, int status) { + ASSERT(opened - closed < max_delta); + if (opened <= max_opened) { + int i; + for (i = 0; i < 30; i++) { + make_eio_req(); + } + } else { + int r; + + r = uv_idle_stop(idle); + uv_unref(uv_default_loop()); + ASSERT(r == 0); + } +} + + +TEST_IMPL(eio_overflow) { + int r; + + r = uv_idle_init(uv_default_loop(), &idle); + ASSERT(r == 0); + + r = uv_idle_start(&idle, idle_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + return 0; +} diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 7f02e68a77..871d0e7d2e 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -99,14 +99,34 @@ static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, uv_close((uv_handle_t*)handle, close_cb); } +static void timber_cb_close_handle(uv_timer_t* timer, int status) { + uv_handle_t* handle; + + ASSERT(timer != NULL); + ASSERT(status == 0); + handle = timer->data; + + uv_close((uv_handle_t*)timer, NULL); + uv_close((uv_handle_t*)handle, close_cb); +} + static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { + ASSERT(fs_event_cb_called == 0); ++fs_event_cb_called; + ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE); ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); - uv_close((uv_handle_t*)handle, close_cb); + + /* Regression test for SunOS: touch should generate just one event. */ + { + static uv_timer_t timer; + uv_timer_init(handle->loop, &timer); + timer.data = handle; + uv_timer_start(&timer, timber_cb_close_handle, 250, 0); + } } static void timer_cb_dir(uv_timer_t* handle, int status) { diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 1371371776..99932da5dc 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -19,6 +19,7 @@ * IN THE SOFTWARE. */ +TEST_DECLARE (platform_output) TEST_DECLARE (tty) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (ipc_listen_before_write) @@ -88,6 +89,7 @@ TEST_DECLARE (pipe_ref3) TEST_DECLARE (process_ref) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) +TEST_DECLARE (process_title) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) TEST_DECLARE (hrtime) @@ -102,6 +104,7 @@ TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (spawn_and_kill_with_std) TEST_DECLARE (spawn_and_ping) TEST_DECLARE (kill) TEST_DECLARE (fs_file_noent) @@ -130,6 +133,13 @@ TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) TEST_DECLARE (fs_rename_to_existing_file) TEST_DECLARE (threadpool_queue_work_simple) +TEST_DECLARE (threadpool_multiple_event_loops) +TEST_DECLARE (eio_overflow) +TEST_DECLARE (thread_mutex) +TEST_DECLARE (thread_rwlock) +TEST_DECLARE (thread_create) +TEST_DECLARE (strlcpy) +TEST_DECLARE (strlcat) TEST_DECLARE (counters_init) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) @@ -146,6 +156,8 @@ HELPER_DECLARE (pipe_echo_server) TASK_LIST_START + TEST_OUTPUT_ENTRY (platform_output) + TEST_ENTRY (pipe_connect_bad_name) TEST_ENTRY (tty) @@ -244,6 +256,8 @@ TASK_LIST_START TEST_ENTRY (get_currentexe) + TEST_ENTRY (process_title) + TEST_ENTRY (cwd_and_chdir) TEST_ENTRY (get_memory) @@ -265,6 +279,7 @@ TASK_LIST_START TEST_ENTRY (spawn_stdout) TEST_ENTRY (spawn_stdin) TEST_ENTRY (spawn_and_kill) + TEST_ENTRY (spawn_and_kill_with_std) TEST_ENTRY (spawn_and_ping) TEST_ENTRY (kill) #ifdef _WIN32 @@ -301,8 +316,14 @@ TASK_LIST_START TEST_ENTRY (fs_open_dir) TEST_ENTRY (fs_rename_to_existing_file) TEST_ENTRY (threadpool_queue_work_simple) + TEST_ENTRY (threadpool_multiple_event_loops) + TEST_ENTRY (eio_overflow) + TEST_ENTRY (thread_mutex) + TEST_ENTRY (thread_rwlock) + TEST_ENTRY (thread_create) + TEST_ENTRY (strlcpy) + TEST_ENTRY (strlcat) TEST_ENTRY (counters_init) - #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) diff --git a/deps/uv/test/test-mutexes.c b/deps/uv/test/test-mutexes.c new file mode 100644 index 0000000000..896f46bbed --- /dev/null +++ b/deps/uv/test/test-mutexes.c @@ -0,0 +1,63 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include <stdio.h> +#include <stdlib.h> + + +/* The mutex and rwlock tests are really poor. + * They're very basic sanity checks and nothing more. + * Apologies if that rhymes. + */ + +TEST_IMPL(thread_mutex) { + uv_mutex_t mutex; + int r; + + r = uv_mutex_init(&mutex); + ASSERT(r == 0); + + uv_mutex_lock(&mutex); + uv_mutex_unlock(&mutex); + uv_mutex_destroy(&mutex); + + return 0; +} + + +TEST_IMPL(thread_rwlock) { + uv_rwlock_t rwlock; + int r; + + r = uv_rwlock_init(&rwlock); + ASSERT(r == 0); + + uv_rwlock_rdlock(&rwlock); + uv_rwlock_rdunlock(&rwlock); + uv_rwlock_wrlock(&rwlock); + uv_rwlock_wrunlock(&rwlock); + uv_rwlock_destroy(&rwlock); + + return 0; +} diff --git a/deps/uv/test/test-platform-output.c b/deps/uv/test/test-platform-output.c new file mode 100644 index 0000000000..99c0551baa --- /dev/null +++ b/deps/uv/test/test-platform-output.c @@ -0,0 +1,83 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include <string.h> + + +TEST_IMPL(platform_output) { + char buffer[512]; + size_t rss; + double uptime; + uv_cpu_info_t* cpus; + uv_interface_address_t* interfaces; + int count; + int i; + uv_err_t err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_get_process_title: %s\n", buffer); + + err = uv_resident_set_memory(&rss); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_resident_set_memory: %d\n", rss); + + err = uv_uptime(&uptime); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_uptime: %f\n", uptime); + + err = uv_cpu_info(&cpus, &count); + ASSERT(UV_OK == err.code); + + fprintf(stderr, "uv_cpu_info:\n"); + for (i = 0; i < count; i++) { + fprintf(stderr, " model: %s\n", cpus[i].model); + fprintf(stderr, " speed: %d\n", cpus[i].speed); + fprintf(stderr, " times.sys: %llu\n", cpus[i].cpu_times.sys); + fprintf(stderr, " times.user: %llu\n", cpus[i].cpu_times.user); + fprintf(stderr, " times.idle: %llu\n", cpus[i].cpu_times.idle); + fprintf(stderr, " times.irq: %llu\n", cpus[i].cpu_times.irq); + fprintf(stderr, " times.nice: %llu\n", cpus[i].cpu_times.nice); + } + uv_free_cpu_info(cpus, count); + + err = uv_interface_addresses(&interfaces, &count); + ASSERT(UV_OK == err.code); + + fprintf(stderr, "uv_interface_addresses:\n"); + for (i = 0; i < count; i++) { + fprintf(stderr, " name: %s\n", interfaces[i].name); + fprintf(stderr, " internal: %d\n", interfaces[i].is_internal); + + if (interfaces[i].address.address4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].address.address4, buffer, sizeof(buffer)); + } else if (interfaces[i].address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].address.address6, buffer, sizeof(buffer)); + } + + fprintf(stderr, " address: %s\n", buffer); + } + uv_free_interface_addresses(interfaces, count); + + return 0; +} diff --git a/deps/uv/test/test-process-title.c b/deps/uv/test/test-process-title.c new file mode 100644 index 0000000000..59fceda31b --- /dev/null +++ b/deps/uv/test/test-process-title.c @@ -0,0 +1,42 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include <string.h> + +TEST_IMPL(process_title) { + char buffer[512]; + uv_err_t err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + + err = uv_set_process_title("new title"); + ASSERT(UV_OK == err.code); + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + + ASSERT(strcmp(buffer, "new title") == 0); + + return 0; +} diff --git a/deps/uv/test/test-run-once.c b/deps/uv/test/test-run-once.c new file mode 100644 index 0000000000..c03ab88e8f --- /dev/null +++ b/deps/uv/test/test-run-once.c @@ -0,0 +1,44 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static idle_counter = 0; + +static void idle_cb(uv_idle_t* handle, int status) { + ASSERT(handle != NULL); + ASSERT(status == 0); + idle_counter ++; +} + + +TEST_IMPL(run_once) { + int n; + uv_idle_t h; + uv_idle_init(uv_default_loop(), &h); + uv_idle_start(&h, idle_cb); + for (n = 0; n < 500; n++) { + uv_run_once(uv_default_loop()); + } + ASSERT(n == 500); + return 0; +} diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 68720114b7..a4d694373c 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -231,6 +231,37 @@ TEST_IMPL(spawn_and_kill) { } +TEST_IMPL(spawn_and_kill_with_std) { + int r; + uv_pipe_t out; + uv_pipe_t in; + + init_process_options("spawn_helper4", kill_cb); + + uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdout_stream = &out; + options.stdin_stream = ∈ + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ + + return 0; +} + + TEST_IMPL(spawn_and_ping) { uv_write_t write_req; uv_pipe_t in, out; diff --git a/deps/uv/test/test-thread.c b/deps/uv/test/test-thread.c new file mode 100644 index 0000000000..1766637235 --- /dev/null +++ b/deps/uv/test/test-thread.c @@ -0,0 +1,183 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> /* memset */ + +struct getaddrinfo_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_getaddrinfo_t handle; +}; + + +struct fs_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_fs_t handle; +}; + + +struct thread { + uv_thread_t thread_id; + volatile int thread_called; +}; + +static void getaddrinfo_do(struct getaddrinfo_req* req); +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res); +static void fs_do(struct fs_req* req); +static void fs_cb(uv_fs_t* handle); + +static volatile int thread_called; + + +static void getaddrinfo_do(struct getaddrinfo_req* req) { + int r; + + r = uv_getaddrinfo(req->loop, + &req->handle, + getaddrinfo_cb, + "localhost", + NULL, + NULL); + ASSERT(r == 0); +} + + +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res) { + struct getaddrinfo_req* req; + + ASSERT(status == 0); + + req = container_of(handle, struct getaddrinfo_req, handle); + uv_freeaddrinfo(res); + + if (--req->counter) + getaddrinfo_do(req); +} + + +static void fs_do(struct fs_req* req) { + int r; + + r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb); + ASSERT(r == 0); +} + + +static void fs_cb(uv_fs_t* handle) { + struct fs_req* req = container_of(handle, struct fs_req, handle); + + uv_fs_req_cleanup(handle); + + if (--req->counter) + fs_do(req); +} + + +static void do_work(void* arg) { + struct getaddrinfo_req getaddrinfo_reqs[16]; + struct fs_req fs_reqs[16]; + uv_loop_t* loop; + size_t i; + int r; + struct thread* thread = arg; + + loop = uv_loop_new(); + ASSERT(loop != NULL); + + for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) { + struct getaddrinfo_req* req = getaddrinfo_reqs + i; + req->counter = 16; + req->loop = loop; + getaddrinfo_do(req); + } + + for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) { + struct fs_req* req = fs_reqs + i; + req->counter = 16; + req->loop = loop; + fs_do(req); + } + + r = uv_run(loop); + ASSERT(r == 0); + + uv_loop_delete(loop); + thread->thread_called = 1; +} + + +static void thread_entry(void* arg) { + ASSERT(arg == (void *) 42); + thread_called++; +} + + +TEST_IMPL(thread_create) { + uv_thread_t tid; + int r; + + r = uv_thread_create(&tid, thread_entry, (void *) 42); + ASSERT(r == 0); + + r = uv_thread_join(&tid); + ASSERT(r == 0); + + ASSERT(thread_called == 1); + + return 0; +} + + +/* Hilariously bad test name. Run a lot of tasks in the thread pool and verify + * that each "finished" callback is run in its originating thread. + */ +TEST_IMPL(threadpool_multiple_event_loops) { + struct thread threads[8]; + size_t i; + int r; + + memset(threads, 0, sizeof(threads)); + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]); + ASSERT(r == 0); + } + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_join(&threads[i].thread_id); + ASSERT(r == 0); + ASSERT(threads[i].thread_called); + } + + return 0; +} diff --git a/deps/uv/test/test-timer.c b/deps/uv/test/test-timer.c index 17bcb84b77..1a0aabd60b 100644 --- a/deps/uv/test/test-timer.c +++ b/deps/uv/test/test-timer.c @@ -37,8 +37,6 @@ static void once_close_cb(uv_handle_t* handle) { ASSERT(handle != NULL); once_close_cb_called++; - - free(handle); } @@ -86,6 +84,7 @@ static void never_cb(uv_timer_t* handle, int status) { TEST_IMPL(timer) { + uv_timer_t once_timers[10]; uv_timer_t *once; uv_timer_t repeat, never; int i, r; @@ -94,9 +93,8 @@ TEST_IMPL(timer) { ASSERT(0 < start_time); /* Let 10 timers time out in 500 ms total. */ - for (i = 0; i < 10; i++) { - once = (uv_timer_t*)malloc(sizeof(*once)); - ASSERT(once != NULL); + for (i = 0; i < ARRAY_SIZE(once_timers); i++) { + once = once_timers + i; r = uv_timer_init(uv_default_loop(), once); ASSERT(r == 0); r = uv_timer_start(once, once_cb, i * 50, 0); diff --git a/deps/uv/test/test-util.c b/deps/uv/test/test-util.c new file mode 100644 index 0000000000..d61d3b1285 --- /dev/null +++ b/deps/uv/test/test-util.c @@ -0,0 +1,97 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include <string.h> + +#define memeq(a, b, c) (memcmp((a), (b), (c)) == 0) + + +TEST_IMPL(strlcpy) { + size_t r; + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "", 0); + ASSERT(r == 0); + ASSERT(memeq(dst, "A", 1)); + } + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "B", 1); + ASSERT(r == 0); + ASSERT(memeq(dst, "", 1)); + } + + { + char dst[2] = "A"; + r = uv_strlcpy(dst, "B", 2); + ASSERT(r == 1); + ASSERT(memeq(dst, "B", 2)); + } + + { + char dst[3] = "AB"; + r = uv_strlcpy(dst, "CD", 3); + ASSERT(r == 2); + ASSERT(memeq(dst, "CD", 3)); + } + + return 0; +} + + +TEST_IMPL(strlcat) { + size_t r; + + { + char dst[2] = "A"; + r = uv_strlcat(dst, "B", 1); + ASSERT(r == 1); + ASSERT(memeq(dst, "A", 2)); + } + + { + char dst[2] = "A"; + r = uv_strlcat(dst, "B", 2); + ASSERT(r == 1); + ASSERT(memeq(dst, "A", 2)); + } + + { + char dst[3] = "A"; + r = uv_strlcat(dst, "B", 3); + ASSERT(r == 2); + ASSERT(memeq(dst, "AB", 3)); + } + + { + char dst[5] = "AB"; + r = uv_strlcat(dst, "CD", 5); + ASSERT(r == 4); + ASSERT(memeq(dst, "ABCD", 5)); + } + + return 0; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 07ea07a817..75f3634ef7 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -115,7 +115,7 @@ 'src/ares/config_win32' ], 'defines': [ - '_WIN32_WINNT=0x0502', + '_WIN32_WINNT=0x0600', 'EIO_STACKSIZE=262144', '_GNU_SOURCE', ], @@ -139,13 +139,13 @@ 'src/win/internal.h', 'src/win/loop-watcher.c', 'src/win/pipe.c', + 'src/win/thread.c', 'src/win/process.c', 'src/win/req.c', 'src/win/stream.c', 'src/win/tcp.c', 'src/win/tty.c', 'src/win/threadpool.c', - 'src/win/threads.c', 'src/win/timer.c', 'src/win/udp.c', 'src/win/util.c', @@ -157,6 +157,8 @@ 'link_settings': { 'libraries': [ '-lws2_32.lib', + '-lpsapi.lib', + '-liphlpapi.lib' ], }, }, { # Not Windows i.e. POSIX @@ -185,6 +187,7 @@ 'src/unix/cares.c', 'src/unix/dl.c', 'src/unix/error.c', + 'src/unix/thread.c', 'src/unix/process.c', 'src/unix/internal.h', 'src/unix/eio/ecb.h', @@ -247,6 +250,11 @@ 'EV_CONFIG_H="config_freebsd.h"', 'EIO_CONFIG_H="config_freebsd.h"', ], + 'direct_dependent_settings': { + 'libraries': [ + '-lkvm', + ], + }, }], [ 'OS=="openbsd"', { 'include_dirs': [ 'src/ares/config_openbsd' ], @@ -274,12 +282,14 @@ 'test/runner.h', 'test/test-get-loadavg.c', 'test/task.h', + 'test/test-util.c', 'test/test-async.c', 'test/test-error.c', 'test/test-callback-stack.c', 'test/test-connection-fail.c', 'test/test-cwd-and-chdir.c', 'test/test-delayed-accept.c', + 'test/test-eio-overflow.c', 'test/test-fail-always.c', 'test/test-fs.c', 'test/test-fs-event.c', @@ -298,6 +308,8 @@ 'test/test-ping-pong.c', 'test/test-pipe-bind-error.c', 'test/test-pipe-connect-error.c', + 'test/test-platform-output.c', + 'test/test-process-title.c', 'test/test-ref.c', 'test/test-shutdown-eof.c', 'test/test-spawn.c', @@ -312,6 +324,8 @@ 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', 'test/test-threadpool.c', + 'test/test-mutexes.c', + 'test/test-thread.c', 'test/test-timer-again.c', 'test/test-timer.c', 'test/test-tty.c', @@ -364,6 +378,7 @@ 'test/benchmark-pump.c', 'test/benchmark-sizes.c', 'test/benchmark-spawn.c', + 'test/benchmark-thread.c', 'test/benchmark-tcp-write-batch.c', 'test/benchmark-udp-packet-storm.c', 'test/dns-server.c', diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index fcb5c205a6..1ff5ff604d 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -8,6 +8,7 @@ Sigma Designs Inc. ARM Ltd. Hewlett-Packard Development Company, LP Igalia, S.L. +Joyent, Inc. Akinori MUSHA <knu@FreeBSD.org> Alexander Botero-Lowry <alexbl@FreeBSD.org> @@ -42,6 +43,7 @@ Rodolph Perfetta <rodolph.perfetta@arm.com> Ryan Dahl <coldredlemur@gmail.com> Sanjoy Das <sanjoy@playingwithpointers.com> Subrato K De <subratokde@codeaurora.org> +Tobias Burnus <burnus@net-b.de> Vlad Burlik <vladbph@gmail.com> Yuqiang Xian <yuqiang.xian@intel.com> Zaheer Ahmad <zahmad@codeaurora.org> diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 99495dd46b..482cca8946 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,429 @@ +2012-02-09: Version 3.9.5 + + Removed unused command line flags. + + Performance and stability improvements on all platforms. + + +2012-02-08: Version 3.9.4 + + Properly initialize element-transitioning array literals on ARM. + (issue 1930) + + Bug fixes on all platforms. + + +2012-02-07: Version 3.9.3 + + When rethrowing an exception, print the stack trace of its original + site instead of rethrow site (Chromium issue 60240). + + Increased size of small stacks from 32k to 64k to avoid hitting limits + in Chromium (Chromium issue 112843). + + +2012-02-06: Version 3.9.2 + + Add timestamp to --trace-gc output. (issue 1932) + + Heap profiler reports implicit references. + + Optionally export metadata with libv8 to enable debuggers to inspect V8 + state. + + +2012-02-02: Version 3.9.1 + + Fixed memory leak in NativeObjectsExplorer::FindOrAddGroupInfo + (Chromium issue 112315). + + Fixed a crash in dev tools (Chromium issue 107996). + + Added 'dependencies_traverse': 1 to v8 GYP target. + + Performance and stability improvements on all platforms. + + +2012-02-01: Version 3.9.0 + + Reduce memory use immediately after starting V8. + + Stability fixes and performance improvements on all platforms. + + +2012-01-26: Version 3.8.9 + + Flush number string cache on GC (issue 1605). + + Provide access to function inferred name with + v8::Function::GetInferredName in V8 public API. + + Fix building with Clang (issue 1912). + + Reduce the space used by the stack for the profiling thread. + + Fix misleading documentation of v8::Locker (issue 542). + + Introduce readbinary function in d8 to read binary files. + + Performance and stability improvements on all platforms. + + +2012-01-23: Version 3.8.8 + + Limited number of loop iterations in Heap::ReserveSpace + (Chromium issue 99027). + + Fixed solaris build (VirtualMemory) (issue 1761). + + Fixed strict vs. non-strict handling of function proxies in + higher-order array and string methods. + + Enabled asynchronous remote debugging with d8 (issue 1691). + + Stability and performance improvements on all platforms. + + +2012-01-19: Version 3.8.7 + + Ensure that LRandom restores rsi after call to the C function on x64. + (Chromium issue http://crbug.com/110509) + + Fixing include issues on *bsd when building with scons. + (issue 1897) + + Provide a switch to specify -fno-strict-aliasing + (issue 1887) + + Move WIN32 define from standalone.gypi to common.gypi + (issue 1760) + + Fix corner-case in heap size estimation. + (issue 1893) + + Fix and enable NEW_NON_STRICT_FAST ArgumentsAccess stub on x64. + (issue 1903) + + Performance improvements and bug fixes. + + +2012-01-16: Version 3.8.6 + + Add primitive WebGL array support to d8. + + Improve heap size estimation (issue 1893). + + Hash collision DOS workaround extended from string keys + to numeric keys. + + Provide an API for iterating through all external strings referenced + from the JS heap. + + Adjust position recorded for call expressions. http://crbug.com/109195 + + Fix GC crash related to instanceof. http://crbug.com/109448 + + Performance improvements and bug fixes. + + +2012-01-05: Version 3.8.5 + + Fix broken test that assumes that no GC can clear the regexp cache (GC + can happen at any time due to Crankshaft). + + Fix handling of bogus receivers for Harmony collections. (issue 1884) + + Add netbsd support to gyp build. + + Determine page size at runtime on posix platforms. + + Ensure that store buffer filtering hash sets are cleared after + StoreBuffer::Filter. + + Randomize the seed used for string hashing. This helps guard against + CPU-eating DOS attacks against node.js servers. Based on code from + Bert Belder. This version only solves the issue for those that compile + V8 themselves or those that do not use snapshots. A snapshot-based + precompiled V8 will still have predictable string hash codes. + + Implement callback when script finishes running in V8 API. + + Improve performance of Math.min and Math.max for the case of two + arguments. (issue 1325) + + +2012-01-02: Version 3.8.4 + + Performance improvements for large Smi-only arrays. + + Fixed InternalArrays construction. (issue 1878) + + +2011-12-27: Version 3.8.3 + + Avoid embedding new space objects into code objects in the lithium gap + resolver. (chromium:108296) + + Bug fixes and performance optimizations on all platforms. + + +2011-12-21: Version 3.8.2 + + Add max optimization flag to v8 gyp build to ensure V8 is always built + fully optimized in Chrome. + + MIPS: Bring MIPS to parity with other platforms. + + Optimizations and stability improvements on all platforms. + + +2011-12-19: Version 3.8.1 + + Fixed GCC 4.7 warnings. Patch from Tobias Burnus. + + Stability improvements on all platforms. + + +2011-12-13: Version 3.8.0 + + Fixed handling of arrays in DefineOwnProperty. (issue 1756) + + Sync parser and preparser on do-while and return statements. + (issue 1856) + + Fixed another corner case for DefineOwnProperty on arrays (issue 1756). + + Stability and performance improvements on all platforms. + + +2011-12-01: Version 3.7.12 + + Increase tick interval for the android platform. + + Fix a bug in the register allocator. (chromium:105112) + + Fix handling of recompiling code. (chromium:105375, v8:1782) + + Start incremental marking on idle notification. (v8:1458) + + Build fixes for various platforms. + + Various performance improvements. + + +2011-11-29: Version 3.7.11 + + Fixed bug when generating padding to ensure space for lazy + deoptimization. + (issue 1846) + + Further reduced pause times due to GC. + + Stability and performance improvements on all platforms. + + +2011-11-23: Version 3.7.10 + + Set maximum length of FixedArray in terms of elements instead an + absolute number of bytes. + (Chromium issue 103103) + + Stability and performance improvements on all platforms. + + +2011-11-21: Version 3.7.9 + + Removed exit-time destructors. + + Stability and performance improvements on all platforms. + + +2011-11-17: Version 3.7.8 + + Removed hidden prototype from builtins, i.e., deleting an overridden + function on builtins will not make the original function reappear. + + Added NetBSD support for scons build. + + Performance improvements on all platforms. + + +2011-11-14: Version 3.7.7 + + Fix missing fast property accessors in heap snapshots. + (issue 1818) + + +2011-11-11: Version 3.7.6 + + Fixed filtering of store buffer for large object pages. + (issue 1817) + + Fixed generated hash function on all platforms. + (issue 1808) + + Fixed Heap::Shrink to ensure that it does not free pages that are + still in use. + (Chromium issue 100414) + + Stability and performance improvements on all platforms. + + +2011-11-10: Version 3.7.5 + + Added initial gyp infrastructure for MIPS. + + Implemented performance improvements to the incremental garbage + collector. + + Added optimizations and stability improvements on all platforms. + + +2011-11-07: Version 3.7.4 + + Proper "libv8.so.3.7.4" SONAME for Linux shared library (issue 1786). + + Fix Harmony sets and maps to allow null and undefined as keys + (still hidden behind --harmony flag) (issue 1622). + + Implement VirtualMemory on FreeBSD to fix build (issue 1807). + + Enable VFP instructions for Android. + + Fix error handling in Date.prototype.toISOString (issue 1792). + + Bug fixes and performance improvements for all platforms. + + Not officially supported but noteworthy: Crankshaft for MIPS :-) + + +2011-10-28: Version 3.7.3 + + Slight deoptimization as a workaround for issue with jslint: Issue + 1789. + + +2011-10-27: Version 3.7.2 + + Fix bug in deoptimization. Known issue with jslint: Issue 1789. + + +2011-10-26: Version 3.7.1 + + Achieved 33% speedup in debug-mode tests. + + Removed special casing of calls to RegExp test and exec methods with no + argument. Now matches new JSC behaviour. crbug.com/75740. + + Return the empty string on cyclic references in toString (ES5 + conformance). + + Fixed bug triggered by JSBeautifier. crbug.com/100409. + + Made Math.random state per-context instead of per-process (issue 864). + + Fixed stack traces to skip native functions. + + Make snapshots (new contexts) smaller and faster. + + Fixed handling of Function.apply for non-array arguments. + + Fixed evaluation order in defineProperties to match FireFox. + + Fixed handling of non-object receivers for array builtins, + crbug.com/100702. + + Multiple fixes to improve compliance with test262. + + Fixed compatibility with older Android releases. + + Fixed compilation with gcc-4.5.3. + + Improved performance of WriteUtf8, issue 1665. + + Made native syntax an early error in the preparser. + + Fixed issues 793 and 893 relating to Function.prototype.bind. + + Improved let, const, Set and Map support and other Harmony features + (behind the --harmony flag). + + Changed evaluation order for > and <= to match ES5 instead of ES3. + + Bug fixes and performance improvements on all platforms. + + +2011-10-13: Version 3.7.0 + + Fixed array handling for Object.defineOwnProperty (ES5 conformance). + + Fixed issue 1757 (string slices of external strings). + + Fixed issue 1759 (ARM). + + Added flag --noclever-optimizations to disable some things that + caused trouble in the past. + + Added flag --stress-compaction for testing. + + Added flag --harmony to activate all experimental Harmony features. + + +2011-10-10: Version 3.6.6 + + Added a GC pause visualization tool. + + Added presubmit=no and werror=no flags to Makefile. + + ES5/Test262 conformance improvements. + + Fixed compilation issues with GCC 4.5.x (issue 1743). + + Bug fixes and performance improvements on all platforms. + + +2011-10-05: Version 3.6.5 + + New incremental garbage collector. + + Removed the hard heap size limit (soft heap size limit is still + 700/1400Mbytes by default). + + Implemented ES5 generic Array.prototype.toString (Issue 1361). + + V8 now allows surrogate pair codes in decodeURIComponent (Issue 1415). + + Fixed x64 RegExp start-of-string bug (Issues 1746, 1748). + + Fixed propertyIsEnumerable for numeric properties (Issue 1692). + + Fixed the MinGW and Windows 2000 builds. + + Fixed "Prototype chain is not searched if named property handler does + not set a property" (Issue 1636). + + Made the RegExp.prototype object be a RegExp object (Issue 1217). + + Disallowed future reserved words as labels in strict mode. + + Fixed string split to correctly coerce the separator to a string + (Issue 1711). + + API: Added an optional source length field to the Extension + constructor. + + API: Added Debug::DisableAgent to match existing Debug::EnableAgent + (Issue 1573). + + Added "native" target to Makefile for the benefit of Linux distros. + + Fixed: debugger stops stepping outside evaluate (Issue 1639). + + More work on ES-Harmony proxies. Still hidden behind a flag. + + Bug fixes and performance improvements on all platforms. + + 2011-09-15: Version 3.6.4 Fixed d8's broken readline history. @@ -194,7 +620,7 @@ Fix the debugger for strict-mode functions. (Chromium issue 89236) - Add GetPropertyAttribute method for Object in the API. (Patch by + Add GetPropertyAttribute method for Object in the API. (Patch by Peter Varga) Fix -Wunused-but-set-variable for gcc-4.6 on x64. (Issue 1291) diff --git a/deps/v8/LICENSE b/deps/v8/LICENSE index e435050e90..2e516bab62 100644 --- a/deps/v8/LICENSE +++ b/deps/v8/LICENSE @@ -14,7 +14,9 @@ are: - Strongtalk assembler, the basis of the files assembler-arm-inl.h, assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, - assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h. + assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h, + assembler-x64.cc, assembler-x64.h, assembler-mips-inl.h, + assembler-mips.cc, assembler-mips.h, assembler.cc and assembler.h. This code is copyrighted by Sun Microsystems Inc. and released under a 3-clause BSD license. @@ -24,7 +26,7 @@ are: These libraries have their own licenses; we recommend you read them, as their terms may differ from the terms below. -Copyright 2006-2011, the V8 project authors. All rights reserved. +Copyright 2006-2012, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/deps/v8/Makefile b/deps/v8/Makefile index a7b27317a3..73e84216b4 100644 --- a/deps/v8/Makefile +++ b/deps/v8/Makefile @@ -1,4 +1,4 @@ -# Copyright 2011 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -27,11 +27,14 @@ # Variable default definitions. Override them by exporting them in your shell. -CXX ?= "g++" # For distcc: export CXX="distcc g++" -LINK ?= "g++" +CXX ?= g++ +LINK ?= g++ OUTDIR ?= out TESTJOBS ?= -j16 GYPFLAGS ?= +TESTFLAGS ?= +ANDROID_NDK_ROOT ?= +ANDROID_TOOL_PREFIX = $(ANDROID_NDK_ROOT)/toolchain/bin/arm-linux-androideabi # Special build flags. Use them like this: "make library=shared" @@ -50,6 +53,10 @@ endif ifeq ($(disassembler), on) GYPFLAGS += -Dv8_enable_disassembler=1 endif +# objectprint=on +ifeq ($(objectprint), on) + GYPFLAGS += -Dv8_object_print=1 +endif # snapshot=off ifeq ($(snapshot), off) GYPFLAGS += -Dv8_use_snapshot='false' @@ -72,14 +79,28 @@ endif ifdef soname_version GYPFLAGS += -Dsoname_version=$(soname_version) endif +# werror=no +ifeq ($(werror), no) + GYPFLAGS += -Dwerror='' +endif +# presubmit=no +ifeq ($(presubmit), no) + TESTFLAGS += --no-presubmit +endif +# strictaliasing=off (workaround for GCC-4.5) +ifeq ($(strictaliasing), off) + GYPFLAGS += -Dv8_no_strict_aliasing=1 +endif # ----------------- available targets: -------------------- # - "dependencies": pulls in external dependencies (currently: GYP) # - any arch listed in ARCHES (see below) # - any mode listed in MODES # - every combination <arch>.<mode>, e.g. "ia32.release" +# - "native": current host's architecture, release mode # - any of the above with .check appended, e.g. "ia32.release.check" -# - default (no target specified): build all ARCHES and MODES +# - "android": cross-compile for Android/ARM (release mode) +# - default (no target specified): build all DEFAULT_ARCHES and MODES # - "check": build all targets and run all tests # - "<arch>.clean" for any <arch> in ARCHES # - "clean": clean all ARCHES @@ -88,7 +109,8 @@ endif # Architectures and modes to be compiled. Consider these to be internal # variables, don't override them (use the targets instead). -ARCHES = ia32 x64 arm +ARCHES = ia32 x64 arm mips +DEFAULT_ARCHES = ia32 x64 arm MODES = release debug # List of files that trigger Makefile regeneration: @@ -103,16 +125,17 @@ CHECKS = $(addsuffix .check,$(BUILDS)) # File where previously used GYPFLAGS are stored. ENVFILE = $(OUTDIR)/environment -.PHONY: all check clean dependencies $(ENVFILE).new \ +.PHONY: all check clean dependencies $(ENVFILE).new native \ $(ARCHES) $(MODES) $(BUILDS) $(CHECKS) $(addsuffix .clean,$(ARCHES)) \ - $(addsuffix .check,$(MODES)) $(addsuffix .check,$(ARCHES)) + $(addsuffix .check,$(MODES)) $(addsuffix .check,$(ARCHES)) \ + must-set-ANDROID_NDK_ROOT # Target definitions. "all" is the default. all: $(MODES) # Compile targets. MODES and ARCHES are convenience targets. .SECONDEXPANSION: -$(MODES): $(addsuffix .$$@,$(ARCHES)) +$(MODES): $(addsuffix .$$@,$(DEFAULT_ARCHES)) $(ARCHES): $(addprefix $$@.,$(MODES)) @@ -124,21 +147,44 @@ $(BUILDS): $(OUTDIR)/Makefile-$$(basename $$@) python -c "print raw_input().capitalize()") \ builddir="$(shell pwd)/$(OUTDIR)/$@" +native: $(OUTDIR)/Makefile-native + @$(MAKE) -C "$(OUTDIR)" -f Makefile-native \ + CXX="$(CXX)" LINK="$(LINK)" BUILDTYPE=Release \ + builddir="$(shell pwd)/$(OUTDIR)/$@" + +# TODO(jkummerow): add "android.debug" when we need it. +android android.release: $(OUTDIR)/Makefile-android + @$(MAKE) -C "$(OUTDIR)" -f Makefile-android \ + CXX="$(ANDROID_TOOL_PREFIX)-g++" \ + AR="$(ANDROID_TOOL_PREFIX)-ar" \ + RANLIB="$(ANDROID_TOOL_PREFIX)-ranlib" \ + CC="$(ANDROID_TOOL_PREFIX)-gcc" \ + LD="$(ANDROID_TOOL_PREFIX)-ld" \ + LINK="$(ANDROID_TOOL_PREFIX)-g++" \ + BUILDTYPE=Release \ + builddir="$(shell pwd)/$(OUTDIR)/android.release" + # Test targets. check: all - @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR) + @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR) \ + --arch=$(shell echo $(DEFAULT_ARCHES) | sed -e 's/ /,/g') \ + $(TESTFLAGS) $(addsuffix .check,$(MODES)): $$(basename $$@) @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR) \ - --mode=$(basename $@) + --mode=$(basename $@) $(TESTFLAGS) $(addsuffix .check,$(ARCHES)): $$(basename $$@) @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR) \ - --arch=$(basename $@) + --arch=$(basename $@) $(TESTFLAGS) $(CHECKS): $$(basename $$@) @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR) \ - --arch-and-mode=$(basename $@) + --arch-and-mode=$(basename $@) $(TESTFLAGS) + +native.check: native + @tools/test-wrapper-gypbuild.py $(TESTJOBS) --outdir=$(OUTDIR)/native \ + --arch-and-mode=. $(TESTFLAGS) # Clean targets. You can clean each architecture individually, or everything. $(addsuffix .clean,$(ARCHES)): @@ -147,7 +193,17 @@ $(addsuffix .clean,$(ARCHES)): rm -rf $(OUTDIR)/$(basename $@).debug find $(OUTDIR) -regex '.*\(host\|target\)-$(basename $@)\.mk' -delete -clean: $(addsuffix .clean,$(ARCHES)) +native.clean: + rm -f $(OUTDIR)/Makefile-native + rm -rf $(OUTDIR)/native + find $(OUTDIR) -regex '.*\(host\|target\)-native\.mk' -delete + +android.clean: + rm -f $(OUTDIR)/Makefile-android + rm -rf $(OUTDIR)/android.release + find $(OUTDIR) -regex '.*\(host\|target\)-android\.mk' -delete + +clean: $(addsuffix .clean,$(ARCHES)) native.clean # GYP file generation targets. $(OUTDIR)/Makefile-ia32: $(GYPFILES) $(ENVFILE) @@ -160,11 +216,32 @@ $(OUTDIR)/Makefile-x64: $(GYPFILES) $(ENVFILE) -Ibuild/standalone.gypi --depth=. -Dtarget_arch=x64 \ -S-x64 $(GYPFLAGS) -$(OUTDIR)/Makefile-arm: $(GYPFILES) $(ENVFILE) +$(OUTDIR)/Makefile-arm: $(GYPFILES) $(ENVFILE) build/armu.gypi build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \ -Ibuild/standalone.gypi --depth=. -Ibuild/armu.gypi \ -S-arm $(GYPFLAGS) +$(OUTDIR)/Makefile-mips: $(GYPFILES) $(ENVFILE) build/mipsu.gypi + build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \ + -Ibuild/standalone.gypi --depth=. -Ibuild/mipsu.gypi \ + -S-mips $(GYPFLAGS) + +$(OUTDIR)/Makefile-native: $(GYPFILES) $(ENVFILE) + build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \ + -Ibuild/standalone.gypi --depth=. -S-native $(GYPFLAGS) + +$(OUTDIR)/Makefile-android: $(GYPFILES) $(ENVFILE) build/android.gypi \ + must-set-ANDROID_NDK_ROOT + CC="${ANDROID_TOOL_PREFIX}-gcc" \ + build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \ + -Ibuild/standalone.gypi --depth=. -Ibuild/android.gypi \ + -S-android $(GYPFLAGS) + +must-set-ANDROID_NDK_ROOT: +ifndef ANDROID_NDK_ROOT + $(error ANDROID_NDK_ROOT is not set) +endif + # Replaces the old with the new environment file if they're different, which # will trigger GYP to regenerate Makefiles. $(ENVFILE): $(ENVFILE).new diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct index fc67dc5e42..d4eaebef8d 100644 --- a/deps/v8/SConstruct +++ b/deps/v8/SConstruct @@ -33,6 +33,7 @@ import os from os.path import join, dirname, abspath from types import DictType, StringTypes root_dir = dirname(File('SConstruct').rfile().abspath) +src_dir = join(root_dir, 'src') sys.path.insert(0, join(root_dir, 'tools')) import js2c, utils @@ -53,7 +54,7 @@ GCC_DTOA_EXTRA_CCFLAGS = [] LIBRARY_FLAGS = { 'all': { - 'CPPPATH': [join(root_dir, 'src')], + 'CPPPATH': [src_dir], 'regexp:interpreted': { 'CPPDEFINES': ['V8_INTERPRETED_REGEXP'] }, @@ -111,13 +112,13 @@ LIBRARY_FLAGS = { } }, 'os:freebsd': { - 'CPPPATH' : ['/usr/local/include'], + 'CPPPATH' : [src_dir, '/usr/local/include'], 'LIBPATH' : ['/usr/local/lib'], 'CCFLAGS': ['-ansi'], 'LIBS': ['execinfo'] }, 'os:openbsd': { - 'CPPPATH' : ['/usr/local/include'], + 'CPPPATH' : [src_dir, '/usr/local/include'], 'LIBPATH' : ['/usr/local/lib'], 'CCFLAGS': ['-ansi'], }, @@ -125,9 +126,13 @@ LIBRARY_FLAGS = { # On Solaris, to get isinf, INFINITY, fpclassify and other macros one # needs to define __C99FEATURES__. 'CPPDEFINES': ['__C99FEATURES__'], - 'CPPPATH' : ['/usr/local/include'], + 'CPPPATH' : [src_dir, '/usr/local/include'], 'LIBPATH' : ['/usr/local/lib'], - 'CCFLAGS': ['-ansi', '-fno-omit-frame-pointer'], + 'CCFLAGS': ['-ansi'], + }, + 'os:netbsd': { + 'CPPPATH' : [src_dir, '/usr/pkg/include'], + 'LIBPATH' : ['/usr/pkg/lib'], }, 'os:win32': { 'CCFLAGS': ['-DWIN32'], @@ -288,6 +293,7 @@ V8_EXTRA_FLAGS = { 'gcc': { 'all': { 'WARNINGFLAGS': ['-Wall', + '-Werror', '-W', '-Wno-unused-parameter', '-Wnon-virtual-dtor'] @@ -363,6 +369,9 @@ MKSNAPSHOT_EXTRA_FLAGS = { 'os:win32': { 'LIBS': ['winmm', 'ws2_32'], }, + 'os:netbsd': { + 'LIBS': ['execinfo', 'pthread'] + }, 'compress_startup_data:bz2': { 'os:linux': { 'LIBS': ['bz2'] @@ -381,7 +390,7 @@ MKSNAPSHOT_EXTRA_FLAGS = { DTOA_EXTRA_FLAGS = { 'gcc': { 'all': { - 'WARNINGFLAGS': ['-Wno-uninitialized'], + 'WARNINGFLAGS': ['-Werror', '-Wno-uninitialized'], 'CCFLAGS': GCC_DTOA_EXTRA_CCFLAGS } }, @@ -395,7 +404,7 @@ DTOA_EXTRA_FLAGS = { CCTEST_EXTRA_FLAGS = { 'all': { - 'CPPPATH': [join(root_dir, 'src')], + 'CPPPATH': [src_dir], 'library:shared': { 'CPPDEFINES': ['USING_V8_SHARED'] }, @@ -427,6 +436,9 @@ CCTEST_EXTRA_FLAGS = { 'os:win32': { 'LIBS': ['winmm', 'ws2_32'] }, + 'os:netbsd': { + 'LIBS': ['execinfo', 'pthread'] + }, 'arch:arm': { 'LINKFLAGS': ARM_LINK_FLAGS }, @@ -449,7 +461,7 @@ CCTEST_EXTRA_FLAGS = { SAMPLE_FLAGS = { 'all': { - 'CPPPATH': [join(abspath('.'), 'include')], + 'CPPPATH': [join(root_dir, 'include')], 'library:shared': { 'CPPDEFINES': ['USING_V8_SHARED'] }, @@ -486,6 +498,10 @@ SAMPLE_FLAGS = { 'os:win32': { 'LIBS': ['winmm', 'ws2_32'] }, + 'os:netbsd': { + 'LIBPATH' : ['/usr/pkg/lib'], + 'LIBS': ['execinfo', 'pthread'] + }, 'arch:arm': { 'LINKFLAGS': ARM_LINK_FLAGS, 'armeabi:soft' : { @@ -628,7 +644,7 @@ SAMPLE_FLAGS = { PREPARSER_FLAGS = { 'all': { - 'CPPPATH': [join(abspath('.'), 'include'), join(abspath('.'), 'src')], + 'CPPPATH': [join(root_dir, 'include'), src_dir], 'library:shared': { 'CPPDEFINES': ['USING_V8_SHARED'] }, @@ -817,6 +833,9 @@ D8_FLAGS = { 'os:win32': { 'LIBS': ['winmm', 'ws2_32'], }, + 'os:netbsd': { + 'LIBS': ['pthread'], + }, 'arch:arm': { 'LINKFLAGS': ARM_LINK_FLAGS }, @@ -950,7 +969,7 @@ PLATFORM_OPTIONS = { 'help': 'the architecture to build for' }, 'os': { - 'values': ['freebsd', 'linux', 'macos', 'win32', 'openbsd', 'solaris', 'cygwin'], + 'values': ['freebsd', 'linux', 'macos', 'win32', 'openbsd', 'solaris', 'cygwin', 'netbsd'], 'guess': GuessOS, 'help': 'the os to build for' }, diff --git a/deps/v8/benchmarks/spinning-balls/index.html b/deps/v8/benchmarks/spinning-balls/index.html new file mode 100644 index 0000000000..d01f31f373 --- /dev/null +++ b/deps/v8/benchmarks/spinning-balls/index.html @@ -0,0 +1,11 @@ +<html> +<head> + <style> + body { text-align: center; } + </style> +</head> +<body> + <script type="text/javascript" src="splay-tree.js"></script> + <script type="text/javascript" src="v.js"></script> +</body> +</html> diff --git a/deps/v8/benchmarks/spinning-balls/splay-tree.js b/deps/v8/benchmarks/spinning-balls/splay-tree.js new file mode 100644 index 0000000000..a88e4cbce1 --- /dev/null +++ b/deps/v8/benchmarks/spinning-balls/splay-tree.js @@ -0,0 +1,326 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/** + * Constructs a Splay tree. A splay tree is a self-balancing binary + * search tree with the additional property that recently accessed + * elements are quick to access again. It performs basic operations + * such as insertion, look-up and removal in O(log(n)) amortized time. + * + * @constructor + */ +function SplayTree() { +}; + + +/** + * Pointer to the root node of the tree. + * + * @type {SplayTree.Node} + * @private + */ +SplayTree.prototype.root_ = null; + + +/** + * @return {boolean} Whether the tree is empty. + */ +SplayTree.prototype.isEmpty = function() { + return !this.root_; +}; + + +/** + * Inserts a node into the tree with the specified key and value if + * the tree does not already contain a node with the specified key. If + * the value is inserted, it becomes the root of the tree. + * + * @param {number} key Key to insert into the tree. + * @param {*} value Value to insert into the tree. + */ +SplayTree.prototype.insert = function(key, value) { + if (this.isEmpty()) { + this.root_ = new SplayTree.Node(key, value); + return; + } + // Splay on the key to move the last node on the search path for + // the key to the root of the tree. + this.splay_(key); + if (this.root_.key == key) { + return; + } + var node = new SplayTree.Node(key, value); + if (key > this.root_.key) { + node.left = this.root_; + node.right = this.root_.right; + this.root_.right = null; + } else { + node.right = this.root_; + node.left = this.root_.left; + this.root_.left = null; + } + this.root_ = node; +}; + + +/** + * Removes a node with the specified key from the tree if the tree + * contains a node with this key. The removed node is returned. If the + * key is not found, an exception is thrown. + * + * @param {number} key Key to find and remove from the tree. + * @return {SplayTree.Node} The removed node. + */ +SplayTree.prototype.remove = function(key) { + if (this.isEmpty()) { + throw Error('Key not found: ' + key); + } + this.splay_(key); + if (this.root_.key != key) { + throw Error('Key not found: ' + key); + } + var removed = this.root_; + if (!this.root_.left) { + this.root_ = this.root_.right; + } else { + var right = this.root_.right; + this.root_ = this.root_.left; + // Splay to make sure that the new root has an empty right child. + this.splay_(key); + // Insert the original right child as the right child of the new + // root. + this.root_.right = right; + } + return removed; +}; + + +/** + * Returns the node having the specified key or null if the tree doesn't contain + * a node with the specified key. + * + * @param {number} key Key to find in the tree. + * @return {SplayTree.Node} Node having the specified key. + */ +SplayTree.prototype.find = function(key) { + if (this.isEmpty()) { + return null; + } + this.splay_(key); + return this.root_.key == key ? this.root_ : null; +}; + + +/** + * @return {SplayTree.Node} Node having the maximum key value. + */ +SplayTree.prototype.findMax = function(opt_startNode) { + if (this.isEmpty()) { + return null; + } + var current = opt_startNode || this.root_; + while (current.right) { + current = current.right; + } + return current; +}; + + +/** + * @return {SplayTree.Node} Node having the maximum key value that + * is less than the specified key value. + */ +SplayTree.prototype.findGreatestLessThan = function(key) { + if (this.isEmpty()) { + return null; + } + // Splay on the key to move the node with the given key or the last + // node on the search path to the top of the tree. + this.splay_(key); + // Now the result is either the root node or the greatest node in + // the left subtree. + if (this.root_.key < key) { + return this.root_; + } else if (this.root_.left) { + return this.findMax(this.root_.left); + } else { + return null; + } +}; + + +/** + * @return {Array<*>} An array containing all the keys of tree's nodes. + */ +SplayTree.prototype.exportKeys = function() { + var result = []; + if (!this.isEmpty()) { + this.root_.traverse_(function(node) { result.push(node.key); }); + } + return result; +}; + + +/** + * Perform the splay operation for the given key. Moves the node with + * the given key to the top of the tree. If no node has the given + * key, the last node on the search path is moved to the top of the + * tree. This is the simplified top-down splaying algorithm from: + * "Self-adjusting Binary Search Trees" by Sleator and Tarjan + * + * @param {number} key Key to splay the tree on. + * @private + */ +SplayTree.prototype.splay_ = function(key) { + if (this.isEmpty()) { + return; + } + // Create a dummy node. The use of the dummy node is a bit + // counter-intuitive: The right child of the dummy node will hold + // the L tree of the algorithm. The left child of the dummy node + // will hold the R tree of the algorithm. Using a dummy node, left + // and right will always be nodes and we avoid special cases. + var dummy, left, right; + dummy = left = right = new SplayTree.Node(null, null); + var current = this.root_; + while (true) { + if (key < current.key) { + if (!current.left) { + break; + } + if (key < current.left.key) { + // Rotate right. + var tmp = current.left; + current.left = tmp.right; + tmp.right = current; + current = tmp; + if (!current.left) { + break; + } + } + // Link right. + right.left = current; + right = current; + current = current.left; + } else if (key > current.key) { + if (!current.right) { + break; + } + if (key > current.right.key) { + // Rotate left. + var tmp = current.right; + current.right = tmp.left; + tmp.left = current; + current = tmp; + if (!current.right) { + break; + } + } + // Link left. + left.right = current; + left = current; + current = current.right; + } else { + break; + } + } + // Assemble. + left.right = current.left; + right.left = current.right; + current.left = dummy.right; + current.right = dummy.left; + this.root_ = current; +}; + + +/** + * Constructs a Splay tree node. + * + * @param {number} key Key. + * @param {*} value Value. + */ +SplayTree.Node = function(key, value) { + this.key = key; + this.value = value; +}; + + +/** + * @type {SplayTree.Node} + */ +SplayTree.Node.prototype.left = null; + + +/** + * @type {SplayTree.Node} + */ +SplayTree.Node.prototype.right = null; + + +/** + * Performs an ordered traversal of the subtree starting at + * this SplayTree.Node. + * + * @param {function(SplayTree.Node)} f Visitor function. + * @private + */ +SplayTree.Node.prototype.traverse_ = function(f) { + var current = this; + while (current) { + var left = current.left; + if (left) left.traverse_(f); + f(current); + current = current.right; + } +}; + +SplayTree.prototype.traverseBreadthFirst = function (f) { + if (f(this.root_.value)) return; + + var stack = [this.root_]; + var length = 1; + + while (length > 0) { + var new_stack = new Array(stack.length * 2); + var new_length = 0; + for (var i = 0; i < length; i++) { + var n = stack[i]; + var l = n.left; + var r = n.right; + if (l) { + if (f(l.value)) return; + new_stack[new_length++] = l; + } + if (r) { + if (f(r.value)) return; + new_stack[new_length++] = r; + } + } + stack = new_stack; + length = new_length; + } +}; diff --git a/deps/v8/benchmarks/spinning-balls/v.js b/deps/v8/benchmarks/spinning-balls/v.js new file mode 100644 index 0000000000..5ae11948d6 --- /dev/null +++ b/deps/v8/benchmarks/spinning-balls/v.js @@ -0,0 +1,498 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +/** + * This function provides requestAnimationFrame in a cross browser way. + * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + */ +if ( !window.requestAnimationFrame ) { + window.requestAnimationFrame = ( function() { + return window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback, element) { + window.setTimeout( callback, 1000 / 60 ); + }; + } )(); +} + +var kNPoints = 8000; +var kNModifications = 20; +var kNVisiblePoints = 200; +var kDecaySpeed = 20; + +var kPointRadius = 4; +var kInitialLifeForce = 100; + +var livePoints = void 0; +var dyingPoints = void 0; +var scene = void 0; +var renderingStartTime = void 0; +var scene = void 0; +var pausePlot = void 0; +var splayTree = void 0; +var numberOfFrames = 0; +var sumOfSquaredPauses = 0; +var benchmarkStartTime = void 0; +var benchmarkTimeLimit = void 0; +var autoScale = void 0; +var pauseDistribution = []; + + +function Point(x, y, z, payload) { + this.x = x; + this.y = y; + this.z = z; + + this.next = null; + this.prev = null; + this.payload = payload; + this.lifeForce = kInitialLifeForce; +} + + +Point.prototype.color = function () { + return "rgba(0, 0, 0, " + (this.lifeForce / kInitialLifeForce) + ")"; +}; + + +Point.prototype.decay = function () { + this.lifeForce -= kDecaySpeed; + return this.lifeForce <= 0; +}; + + +function PointsList() { + this.head = null; + this.count = 0; +} + + +PointsList.prototype.add = function (point) { + if (this.head !== null) this.head.prev = point; + point.next = this.head; + this.head = point; + this.count++; +} + + +PointsList.prototype.remove = function (point) { + if (point.next !== null) { + point.next.prev = point.prev; + } + if (point.prev !== null) { + point.prev.next = point.next; + } else { + this.head = point.next; + } + this.count--; +} + + +function GeneratePayloadTree(depth, tag) { + if (depth == 0) { + return { + array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], + string : 'String for key ' + tag + ' in leaf node' + }; + } else { + return { + left: GeneratePayloadTree(depth - 1, tag), + right: GeneratePayloadTree(depth - 1, tag) + }; + } +} + + +// To make the benchmark results predictable, we replace Math.random +// with a 100% deterministic alternative. +Math.random = (function() { + var seed = 49734321; + return function() { + // Robert Jenkins' 32 bit integer hash function. + seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; + seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; + seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; + seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; + seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; + seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; + return (seed & 0xfffffff) / 0x10000000; + }; +})(); + + +function GenerateKey() { + // The benchmark framework guarantees that Math.random is + // deterministic; see base.js. + return Math.random(); +} + +function CreateNewPoint() { + // Insert new node with a unique key. + var key; + do { key = GenerateKey(); } while (splayTree.find(key) != null); + + var point = new Point(Math.random() * 40 - 20, + Math.random() * 40 - 20, + Math.random() * 40 - 20, + GeneratePayloadTree(5, "" + key)); + + livePoints.add(point); + + splayTree.insert(key, point); + return key; +} + +function ModifyPointsSet() { + if (livePoints.count < kNPoints) { + for (var i = 0; i < kNModifications; i++) { + CreateNewPoint(); + } + } else if (kNModifications === 20) { + kNModifications = 80; + kDecay = 30; + } + + for (var i = 0; i < kNModifications; i++) { + var key = CreateNewPoint(); + var greatest = splayTree.findGreatestLessThan(key); + if (greatest == null) { + var point = splayTree.remove(key).value; + } else { + var point = splayTree.remove(greatest.key).value; + } + livePoints.remove(point); + point.payload = null; + dyingPoints.add(point); + } +} + + +function PausePlot(width, height, size, scale) { + var canvas = document.createElement("canvas"); + canvas.width = this.width = width; + canvas.height = this.height = height; + document.body.appendChild(canvas); + + this.ctx = canvas.getContext('2d'); + + if (typeof scale !== "number") { + this.autoScale = true; + this.maxPause = 0; + } else { + this.autoScale = false; + this.maxPause = scale; + } + + this.size = size; + + // Initialize cyclic buffer for pauses. + this.pauses = new Array(this.size); + this.start = this.size; + this.idx = 0; +} + + +PausePlot.prototype.addPause = function (p) { + if (this.idx === this.size) { + this.idx = 0; + } + + if (this.idx === this.start) { + this.start++; + } + + if (this.start === this.size) { + this.start = 0; + } + + this.pauses[this.idx++] = p; +}; + + +PausePlot.prototype.iteratePauses = function (f) { + if (this.start < this.idx) { + for (var i = this.start; i < this.idx; i++) { + f.call(this, i - this.start, this.pauses[i]); + } + } else { + for (var i = this.start; i < this.size; i++) { + f.call(this, i - this.start, this.pauses[i]); + } + + var offs = this.size - this.start; + for (var i = 0; i < this.idx; i++) { + f.call(this, i + offs, this.pauses[i]); + } + } +}; + + +PausePlot.prototype.draw = function () { + var first = null; + + if (this.autoScale) { + this.iteratePauses(function (i, v) { + if (first === null) { + first = v; + } + this.maxPause = Math.max(v, this.maxPause); + }); + } + + var dx = this.width / this.size; + var dy = this.height / this.maxPause; + + this.ctx.save(); + this.ctx.clearRect(0, 0, this.width, this.height); + this.ctx.beginPath(); + this.ctx.moveTo(1, dy * this.pauses[this.start]); + var p = first; + this.iteratePauses(function (i, v) { + var delta = v - p; + var x = 1 + dx * i; + var y = dy * v; + this.ctx.lineTo(x, y); + if (delta > 2 * (p / 3)) { + this.ctx.font = "bold 12px sans-serif"; + this.ctx.textBaseline = "bottom"; + this.ctx.fillText(v + "ms", x + 2, y); + } + p = v; + }); + this.ctx.strokeStyle = "black"; + this.ctx.stroke(); + this.ctx.restore(); +} + + +function Scene(width, height) { + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + document.body.appendChild(canvas); + + this.ctx = canvas.getContext('2d'); + this.width = canvas.width; + this.height = canvas.height; + + // Projection configuration. + this.x0 = canvas.width / 2; + this.y0 = canvas.height / 2; + this.z0 = 100; + this.f = 1000; // Focal length. + + // Camera is rotating around y-axis. + this.angle = 0; +} + + +Scene.prototype.drawPoint = function (x, y, z, color) { + // Rotate the camera around y-axis. + var rx = x * Math.cos(this.angle) - z * Math.sin(this.angle); + var ry = y; + var rz = x * Math.sin(this.angle) + z * Math.cos(this.angle); + + // Perform perspective projection. + var px = (this.f * rx) / (rz - this.z0) + this.x0; + var py = (this.f * ry) / (rz - this.z0) + this.y0; + + this.ctx.save(); + this.ctx.fillStyle = color + this.ctx.beginPath(); + this.ctx.arc(px, py, kPointRadius, 0, 2 * Math.PI, true); + this.ctx.fill(); + this.ctx.restore(); +}; + + +Scene.prototype.drawDyingPoints = function () { + var point_next = null; + for (var point = dyingPoints.head; point !== null; point = point_next) { + // Rotate the scene around y-axis. + scene.drawPoint(point.x, point.y, point.z, point.color()); + + point_next = point.next; + + // Decay the current point and remove it from the list + // if it's life-force ran out. + if (point.decay()) { + dyingPoints.remove(point); + } + } +}; + + +Scene.prototype.draw = function () { + this.ctx.save(); + this.ctx.clearRect(0, 0, this.width, this.height); + this.drawDyingPoints(); + this.ctx.restore(); + + this.angle += Math.PI / 90.0; +}; + + +function updateStats(pause) { + numberOfFrames++; + if (pause > 20) { + sumOfSquaredPauses += (pause - 20) * (pause - 20); + } + pauseDistribution[Math.floor(pause / 10)] |= 0; + pauseDistribution[Math.floor(pause / 10)]++; +} + + +function renderStats() { + var msg = document.createElement("p"); + msg.innerHTML = "Score " + + Math.round(numberOfFrames * 1000 / sumOfSquaredPauses); + var table = document.createElement("table"); + table.align = "center"; + for (var i = 0; i < pauseDistribution.length; i++) { + if (pauseDistribution[i] > 0) { + var row = document.createElement("tr"); + var time = document.createElement("td"); + var count = document.createElement("td"); + time.innerHTML = i*10 + "-" + (i+1)*10 + "ms"; + count.innerHTML = " => " + pauseDistribution[i]; + row.appendChild(time); + row.appendChild(count); + table.appendChild(row); + } + } + div.appendChild(msg); + div.appendChild(table); +} + + +function render() { + if (typeof renderingStartTime === 'undefined') { + renderingStartTime = Date.now(); + benchmarkStartTime = renderingStartTime; + } + + ModifyPointsSet(); + + scene.draw(); + + var renderingEndTime = Date.now(); + var pause = renderingEndTime - renderingStartTime; + pausePlot.addPause(pause); + renderingStartTime = renderingEndTime; + + pausePlot.draw(); + + updateStats(pause); + + div.innerHTML = + livePoints.count + "/" + dyingPoints.count + " " + + pause + "(max = " + pausePlot.maxPause + ") ms " + + numberOfFrames + " frames"; + + if (renderingEndTime < benchmarkStartTime + benchmarkTimeLimit) { + // Schedule next frame. + requestAnimationFrame(render); + } else { + renderStats(); + } +} + + +function Form() { + function create(tag) { return document.createElement(tag); } + function text(value) { return document.createTextNode(value); } + + this.form = create("form"); + this.form.setAttribute("action", "javascript:start()"); + + var table = create("table"); + table.setAttribute("style", "margin-left: auto; margin-right: auto;"); + + function col(a) { + var td = create("td"); + td.appendChild(a); + return td; + } + + function row(a, b) { + var tr = create("tr"); + tr.appendChild(col(a)); + tr.appendChild(col(b)); + return tr; + } + + this.timelimit = create("input"); + this.timelimit.setAttribute("value", "60"); + + table.appendChild(row(text("Time limit in seconds"), this.timelimit)); + + this.autoscale = create("input"); + this.autoscale.setAttribute("type", "checkbox"); + this.autoscale.setAttribute("checked", "true"); + table.appendChild(row(text("Autoscale pauses plot"), this.autoscale)); + + var button = create("input"); + button.setAttribute("type", "submit"); + button.setAttribute("value", "Start"); + this.form.appendChild(table); + this.form.appendChild(button); + + document.body.appendChild(this.form); +} + + +Form.prototype.remove = function () { + document.body.removeChild(this.form); +}; + + +function init() { + livePoints = new PointsList; + dyingPoints = new PointsList; + + splayTree = new SplayTree(); + + scene = new Scene(640, 480); + + div = document.createElement("div"); + document.body.appendChild(div); + + pausePlot = new PausePlot(480, autoScale ? 240 : 500, 160, autoScale ? void 0 : 500); +} + +function start() { + benchmarkTimeLimit = form.timelimit.value * 1000; + autoScale = form.autoscale.checked; + form.remove(); + init(); + render(); +} + +var form = new Form(); diff --git a/deps/v8/build/android.gypi b/deps/v8/build/android.gypi new file mode 100644 index 0000000000..ffd06484f7 --- /dev/null +++ b/deps/v8/build/android.gypi @@ -0,0 +1,225 @@ +# Copyright 2012 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Definitions for building standalone V8 binaries to run on Android. +# This is mostly excerpted from: +# http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi + +{ + 'variables': { + # Location of Android NDK. + 'variables': { + 'variables': { + 'android_ndk_root%': '<!(/bin/echo -n $ANDROID_NDK_ROOT)', + 'android_target_arch%': 'arm', # target_arch in android terms. + + # Switch between different build types, currently only '0' is + # supported. + 'android_build_type%': 0, + }, + 'android_ndk_root%': '<(android_ndk_root)', + 'android_ndk_sysroot': '<(android_ndk_root)/platforms/android-9/arch-<(android_target_arch)', + 'android_build_type%': '<(android_build_type)', + }, + 'android_ndk_root%': '<(android_ndk_root)', + 'android_ndk_sysroot': '<(android_ndk_sysroot)', + 'android_ndk_include': '<(android_ndk_sysroot)/usr/include', + 'android_ndk_lib': '<(android_ndk_sysroot)/usr/lib', + # Enable to use the system stlport, otherwise statically + # link the NDK one? + 'use_system_stlport%': '<(android_build_type)', + 'android_stlport_library': 'stlport_static', + # Copy it out one scope. + 'android_build_type%': '<(android_build_type)', + + 'OS': 'android', + 'target_arch': 'arm', + 'v8_target_arch': 'arm', + 'armv7': 1, + 'arm_neon': 0, + 'arm_fpu': 'vfpv3', + }, # variables + 'target_defaults': { + 'defines': [ + 'ANDROID', + 'V8_ANDROID_LOG_STDOUT', + ], + 'configurations': { + 'Release': { + 'cflags!': [ + '-O2', + '-Os', + ], + 'cflags': [ + '-fdata-sections', + '-ffunction-sections', + '-fomit-frame-pointer', + '-O3', + ], + }, # Release + }, # configurations + 'cflags': [ '-Wno-abi', '-Wall', '-W', '-Wno-unused-parameter', + '-Wnon-virtual-dtor', '-fno-rtti', '-fno-exceptions', ], + 'target_conditions': [ + ['_toolset=="target"', { + 'cflags!': [ + '-pthread', # Not supported by Android toolchain. + ], + 'cflags': [ + '-U__linux__', # Don't allow toolchain to claim -D__linux__ + '-ffunction-sections', + '-funwind-tables', + '-fstack-protector', + '-fno-short-enums', + '-finline-limit=64', + '-Wa,--noexecstack', + '-Wno-error=non-virtual-dtor', # TODO(michaelbai): Fix warnings. + # Note: This include is in cflags to ensure that it comes after + # all of the includes. + '-I<(android_ndk_include)', + '-march=armv7-a', + '-mtune=cortex-a8', + '-mfpu=vfp3', + ], + 'defines': [ + 'ANDROID', + #'__GNU_SOURCE=1', # Necessary for clone() + 'USE_STLPORT=1', + '_STLP_USE_PTR_SPECIALIZATIONS=1', + 'HAVE_OFF64_T', + 'HAVE_SYS_UIO_H', + 'ANDROID_BINSIZE_HACK', # Enable temporary hacks to reduce binsize. + ], + 'ldflags!': [ + '-pthread', # Not supported by Android toolchain. + ], + 'ldflags': [ + '-nostdlib', + '-Wl,--no-undefined', + '-Wl,--icf=safe', # Enable identical code folding to reduce size + # Don't export symbols from statically linked libraries. + '-Wl,--exclude-libs=ALL', + ], + 'libraries!': [ + '-lrt', # librt is built into Bionic. + # Not supported by Android toolchain. + # Where do these come from? Can't find references in + # any Chromium gyp or gypi file. Maybe they come from + # gyp itself? + '-lpthread', '-lnss3', '-lnssutil3', '-lsmime3', '-lplds4', '-lplc4', '-lnspr4', + ], + 'libraries': [ + '-l<(android_stlport_library)', + # Manually link the libgcc.a that the cross compiler uses. + '<!($CC -print-libgcc-file-name)', + '-lc', + '-ldl', + '-lstdc++', + '-lm', + ], + 'conditions': [ + ['android_build_type==0', { + 'ldflags': [ + '-Wl,-rpath-link=<(android_ndk_lib)', + '-L<(android_ndk_lib)', + ], + }], + # NOTE: The stlport header include paths below are specified in + # cflags rather than include_dirs because they need to come + # after include_dirs. Think of them like system headers, but + # don't use '-isystem' because the arm-linux-androideabi-4.4.3 + # toolchain (circa Gingerbread) will exhibit strange errors. + # The include ordering here is important; change with caution. + ['use_system_stlport==0', { + 'cflags': [ + '-I<(android_ndk_root)/sources/cxx-stl/stlport/stlport', + ], + 'conditions': [ + ['target_arch=="arm" and armv7==1', { + 'ldflags': [ + '-L<(android_ndk_root)/sources/cxx-stl/stlport/libs/armeabi-v7a', + ], + }], + ['target_arch=="arm" and armv7==0', { + 'ldflags': [ + '-L<(android_ndk_root)/sources/cxx-stl/stlport/libs/armeabi', + ], + }], + ['target_arch=="ia32"', { + 'ldflags': [ + '-L<(android_ndk_root)/sources/cxx-stl/stlport/libs/x86', + ], + }], + ], + }], + ['target_arch=="ia32"', { + # The x86 toolchain currently has problems with stack-protector. + 'cflags!': [ + '-fstack-protector', + ], + 'cflags': [ + '-fno-stack-protector', + ], + }], + ], + 'target_conditions': [ + ['_type=="executable"', { + 'ldflags': [ + '-Bdynamic', + '-Wl,-dynamic-linker,/system/bin/linker', + '-Wl,--gc-sections', + '-Wl,-z,nocopyreloc', + # crtbegin_dynamic.o should be the last item in ldflags. + '<(android_ndk_lib)/crtbegin_dynamic.o', + ], + 'libraries': [ + # crtend_android.o needs to be the last item in libraries. + # Do not add any libraries after this! + '<(android_ndk_lib)/crtend_android.o', + ], + }], + ['_type=="shared_library"', { + 'ldflags': [ + '-Wl,-shared,-Bsymbolic', + ], + }], + ], + }], # _toolset=="target" + # Settings for building host targets using the system toolchain. + ['_toolset=="host"', { + 'cflags': [ '-m32', '-pthread' ], + 'ldflags': [ '-m32', '-pthread' ], + 'ldflags!': [ + '-Wl,-z,noexecstack', + '-Wl,--gc-sections', + '-Wl,-O1', + '-Wl,--as-needed', + ], + }], + ], # target_conditions + }, # target_defaults +}
\ No newline at end of file diff --git a/deps/v8/build/common.gypi b/deps/v8/build/common.gypi index 4e896e019a..3637082bc1 100644 --- a/deps/v8/build/common.gypi +++ b/deps/v8/build/common.gypi @@ -1,4 +1,4 @@ -# Copyright 2011 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -50,21 +50,32 @@ # probing when running on the target. 'v8_can_use_vfp_instructions%': 'false', + # Similar to vfp but on MIPS. + 'v8_can_use_fpu_instructions%': 'true', + # Setting v8_use_arm_eabi_hardfloat to true will turn on V8 support for ARM # EABI calling convention where double arguments are passed in VFP # registers. Note that the GCC flag '-mfloat-abi=hard' should be used as # well when compiling for the ARM target. 'v8_use_arm_eabi_hardfloat%': 'false', + # Similar to the ARM hard float ABI but on MIPS. + 'v8_use_mips_abi_hardfloat%': 'true', + 'v8_enable_debugger_support%': 1, 'v8_enable_disassembler%': 0, + 'v8_object_print%': 0, + 'v8_enable_gdbjit%': 0, # Enable profiling support. Only required on Windows. 'v8_enable_prof%': 0, + # Some versions of GCC 4.5 seem to need -fno-strict-aliasing. + 'v8_no_strict_aliasing%': 0, + # Chrome needs this definition unconditionally. For standalone V8 builds, # it's handled in build/standalone.gypi. 'want_separate_host_toolset%': 1, @@ -72,6 +83,12 @@ 'v8_use_snapshot%': 'true', 'host_os%': '<(OS)', 'v8_use_liveobjectlist%': 'false', + 'werror%': '-Werror', + + # With post mortem support enabled, metadata is embedded into libv8 that + # describes various parameters of the VM for use by debuggers. See + # tools/gen-postmortem-metadata.py for details. + 'v8_postmortem_support%': 'false', # For a shared library build, results in "libv8-<(soname_version).so". 'soname_version%': '', @@ -84,6 +101,9 @@ ['v8_enable_disassembler==1', { 'defines': ['ENABLE_DISASSEMBLER',], }], + ['v8_object_print==1', { + 'defines': ['OBJECT_PRINT',], + }], ['v8_enable_gdbjit==1', { 'defines': ['ENABLE_GDB_JIT_INTERFACE',], }], @@ -129,7 +149,7 @@ }], # The ARM assembler assumes the host is 32 bits, # so force building 32-bit host tools. - ['host_arch=="x64"', { + ['host_arch=="x64" or OS=="android"', { 'target_conditions': [ ['_toolset=="host"', { 'cflags': ['-m32'], @@ -148,6 +168,58 @@ 'defines': [ 'V8_TARGET_ARCH_MIPS', ], + 'conditions': [ + [ 'target_arch=="mips"', { + 'target_conditions': [ + ['_toolset=="target"', { + 'cflags': ['-EL'], + 'ldflags': ['-EL'], + 'conditions': [ + [ 'v8_use_mips_abi_hardfloat=="true"', { + 'cflags': ['-mhard-float'], + 'ldflags': ['-mhard-float'], + }, { + 'cflags': ['-msoft-float'], + 'ldflags': ['-msoft-float'], + }], + ['mips_arch_variant=="mips32r2"', { + 'cflags': ['-mips32r2', '-Wa,-mips32r2'], + }, { + 'cflags': ['-mips32', '-Wa,-mips32'], + }], + ], + }], + ], + }], + [ 'v8_can_use_fpu_instructions=="true"', { + 'defines': [ + 'CAN_USE_FPU_INSTRUCTIONS', + ], + }], + [ 'v8_use_mips_abi_hardfloat=="true"', { + 'defines': [ + '__mips_hard_float=1', + 'CAN_USE_FPU_INSTRUCTIONS', + ], + }, { + 'defines': [ + '__mips_soft_float=1' + ], + }], + ['mips_arch_variant=="mips32r2"', { + 'defines': ['_MIPS_ARCH_MIPS32R2',], + }], + # The MIPS assembler assumes the host is 32 bits, + # so force building 32-bit host tools. + ['host_arch=="x64"', { + 'target_conditions': [ + ['_toolset=="host"', { + 'cflags': ['-m32'], + 'ldflags': ['-m32'], + }], + ], + }], + ], }], ['v8_target_arch=="x64"', { 'defines': [ @@ -169,6 +241,11 @@ 'COMPRESS_STARTUP_DATA_BZ2', ], }], + ['OS=="win"', { + 'defines': [ + 'WIN32', + ], + }], ['OS=="win" and v8_enable_prof==1', { 'msvs_settings': { 'VCLinkerTool': { @@ -176,15 +253,22 @@ }, }, }], - ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \ + or OS=="netbsd"', { 'conditions': [ [ 'target_arch=="ia32"', { 'cflags': [ '-m32' ], 'ldflags': [ '-m32' ], }], - ], + [ 'v8_no_strict_aliasing==1', { + 'cflags': [ '-fno-strict-aliasing' ], + }], + ], # conditions + }], + ['OS=="solaris"', { + 'defines': [ '__C99FEATURES__=1' ], # isinf() etc. }], - ], + ], # conditions 'configurations': { 'Debug': { 'defines': [ @@ -217,15 +301,19 @@ ['OS=="freebsd" or OS=="openbsd"', { 'cflags': [ '-I/usr/local/include' ], }], - ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { - 'cflags': [ '-Wall', '-Werror', '-W', '-Wno-unused-parameter', + ['OS=="netbsd"', { + 'cflags': [ '-I/usr/pkg/include' ], + }], + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', { + 'cflags': [ '-Wall', '<(werror)', '-W', '-Wno-unused-parameter', '-Wnon-virtual-dtor' ], }], ], - }, + }, # Debug 'Release': { 'conditions': [ - ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd" \ + or OS=="android"', { 'cflags!': [ '-O2', '-Os', @@ -237,7 +325,7 @@ '-O3', ], 'conditions': [ - [ 'gcc_version==44', { + [ 'gcc_version==44 and clang==0', { 'cflags': [ # Avoid crashes with gcc 4.4 in the v8 test suite. '-fno-tree-vrp', @@ -248,6 +336,9 @@ ['OS=="freebsd" or OS=="openbsd"', { 'cflags': [ '-I/usr/local/include' ], }], + ['OS=="netbsd"', { + 'cflags': [ '-I/usr/pkg/include' ], + }], ['OS=="mac"', { 'xcode_settings': { 'GCC_OPTIMIZATION_LEVEL': '3', # -O3 @@ -258,10 +349,9 @@ # is specified explicitly. 'GCC_STRICT_ALIASING': 'YES', }, - }], + }], # OS=="mac" ['OS=="win"', { 'msvs_configuration_attributes': { - 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)', 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)', 'CharacterSet': '1', }, @@ -293,9 +383,9 @@ # 'StackReserveSize': '297152', }, }, - }], - ], - }, - }, - }, + }], # OS=="win" + ], # conditions + }, # Release + }, # configurations + }, # target_defaults } diff --git a/deps/v8/build/gyp_v8 b/deps/v8/build/gyp_v8 index dfdbe3f1fd..4293e7637e 100755 --- a/deps/v8/build/gyp_v8 +++ b/deps/v8/build/gyp_v8 @@ -171,3 +171,8 @@ if __name__ == '__main__': gyp_args.append('-I' + v8_root + '/build/armu.gypi') gyp_args.append('-S-armu') run_gyp(gyp_args) + + gyp_args = list(args) + gyp_args.append('-I' + v8_root + '/build/mipsu.gypi') + gyp_args.append('-S-mipsu') + run_gyp(gyp_args) diff --git a/deps/v8/build/mipsu.gypi b/deps/v8/build/mipsu.gypi new file mode 100644 index 0000000000..306f105dbd --- /dev/null +++ b/deps/v8/build/mipsu.gypi @@ -0,0 +1,34 @@ +# Copyright 2011 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +{ + 'variables': { + 'target_arch': 'ia32', + 'v8_target_arch': 'mips', + 'mips_arch_variant': 'mips32r2', + }, +} diff --git a/deps/v8/build/standalone.gypi b/deps/v8/build/standalone.gypi index cb5e133039..e9b056580d 100644 --- a/deps/v8/build/standalone.gypi +++ b/deps/v8/build/standalone.gypi @@ -1,4 +1,4 @@ -# Copyright 2011 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -35,27 +35,36 @@ 'msvs_multi_core_compile%': '1', 'variables': { 'variables': { - 'conditions': [ - [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { - # This handles the Linux platforms we generally deal with. Anything - # else gets passed through, which probably won't work very well; such - # hosts should pass an explicit target_arch to gyp. - 'host_arch%': - '<!(uname -m | sed -e "s/i.86/ia32/;s/x86_64/x64/;s/amd64/x64/;s/arm.*/arm/")', - }, { # OS!="linux" and OS!="freebsd" and OS!="openbsd" - 'host_arch%': 'ia32', - }], - ], + 'variables': { + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', { + # This handles the Linux platforms we generally deal with. + # Anything else gets passed through, which probably won't work + # very well; such hosts should pass an explicit target_arch + # to gyp. + 'host_arch%': + '<!(uname -m | sed -e "s/i.86/ia32/;\ + s/x86_64/x64/;s/amd64/x64/;s/arm.*/arm/;s/mips.*/mips/")', + }, { + # OS!="linux" and OS!="freebsd" and OS!="openbsd" and OS!="netbsd" + 'host_arch%': 'ia32', + }], + ], + }, + 'host_arch%': '<(host_arch)', + 'target_arch%': '<(host_arch)', }, 'host_arch%': '<(host_arch)', - 'target_arch%': '<(host_arch)', + 'target_arch%': '<(target_arch)', 'v8_target_arch%': '<(target_arch)', }, 'host_arch%': '<(host_arch)', 'target_arch%': '<(target_arch)', 'v8_target_arch%': '<(v8_target_arch)', + 'werror%': '-Werror', 'conditions': [ ['(v8_target_arch=="arm" and host_arch!="arm") or \ + (v8_target_arch=="mips" and host_arch!="mips") or \ (v8_target_arch=="x64" and host_arch!="x64")', { 'want_separate_host_toolset': 1, }, { @@ -72,9 +81,10 @@ }, }, 'conditions': [ - [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \ + or OS=="netbsd"', { 'target_defaults': { - 'cflags': [ '-Wall', '-Werror', '-W', '-Wno-unused-parameter', + 'cflags': [ '-Wall', '<(werror)', '-W', '-Wno-unused-parameter', '-Wnon-virtual-dtor', '-pthread', '-fno-rtti', '-fno-exceptions', '-pedantic' ], 'ldflags': [ '-pthread', ], @@ -90,11 +100,12 @@ }], ], }, - }], # 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"' + }], + # 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" + # or OS=="netbsd"' ['OS=="win"', { 'target_defaults': { 'defines': [ - 'WIN32', '_CRT_SECURE_NO_DEPRECATE', '_CRT_NONSTDC_NO_DEPRECATE', ], diff --git a/deps/v8/include/v8-debug.h b/deps/v8/include/v8-debug.h index 504cbfed59..9e85dc462c 100644..100755 --- a/deps/v8/include/v8-debug.h +++ b/deps/v8/include/v8-debug.h @@ -340,6 +340,11 @@ class EXPORT Debug { bool wait_for_connection = false); /** + * Disable the V8 builtin debug agent. The TCP/IP connection will be closed. + */ + static void DisableAgent(); + + /** * Makes V8 process all pending debug messages. * * From V8 point of view all debug messages come asynchronously (e.g. from diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index f67646f54e..5a3a40ff6f 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -219,8 +219,9 @@ class V8EXPORT HeapGraphEdge { // (e.g. parts of a ConsString). kHidden = 4, // A link that is needed for proper sizes // calculation, but may be hidden from user. - kShortcut = 5 // A link that must not be followed during + kShortcut = 5, // A link that must not be followed during // sizes calculation. + kWeak = 6 // A weak reference (ignored by the GC). }; /** Returns edge type (see HeapGraphEdge::Type). */ @@ -254,7 +255,9 @@ class V8EXPORT HeapGraphNode { kClosure = 5, // Function closure. kRegExp = 6, // RegExp. kHeapNumber = 7, // Number stored in the heap. - kNative = 8 // Native object (not from V8 heap). + kNative = 8, // Native object (not from V8 heap). + kSynthetic = 9 // Synthetic object, usualy used for grouping + // snapshot items together. }; /** Returns node type (see HeapGraphNode::Type). */ @@ -475,12 +478,23 @@ class V8EXPORT RetainedObjectInfo { // NOLINT virtual intptr_t GetHash() = 0; /** - * Returns human-readable label. It must be a NUL-terminated UTF-8 + * Returns human-readable label. It must be a null-terminated UTF-8 * encoded string. V8 copies its contents during a call to GetLabel. */ virtual const char* GetLabel() = 0; /** + * Returns human-readable group label. It must be a null-terminated UTF-8 + * encoded string. V8 copies its contents during a call to GetGroupLabel. + * Heap snapshot generator will collect all the group names, create + * top level entries with these names and attach the objects to the + * corresponding top level group objects. There is a default + * implementation which is required because embedders don't have their + * own implementation yet. + */ + virtual const char* GetGroupLabel() { return GetLabel(); } + + /** * Returns element count in case if a global handle retains * a subgraph by holding one of its nodes. */ diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 4b7f6e735f..66a649d1e0 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1171,7 +1171,8 @@ class String : public Primitive { * Get the ExternalAsciiStringResource for an external ASCII string. * Returns NULL if IsExternalAscii() doesn't return true. */ - V8EXPORT ExternalAsciiStringResource* GetExternalAsciiStringResource() const; + V8EXPORT const ExternalAsciiStringResource* GetExternalAsciiStringResource() + const; static inline String* Cast(v8::Value* obj); @@ -1731,13 +1732,28 @@ class Function : public Object { V8EXPORT Handle<Value> GetName() const; /** + * Name inferred from variable or property assignment of this function. + * Used to facilitate debugging and profiling of JavaScript code written + * in an OO style, where many functions are anonymous but are assigned + * to object properties. + */ + V8EXPORT Handle<Value> GetInferredName() const; + + /** * Returns zero based line number of function body and * kLineOffsetNotFound if no information available. */ V8EXPORT int GetScriptLineNumber() const; + /** + * Returns zero based column number of function body and + * kLineOffsetNotFound if no information available. + */ + V8EXPORT int GetScriptColumnNumber() const; + V8EXPORT Handle<Value> GetScriptId() const; V8EXPORT ScriptOrigin GetScriptOrigin() const; static inline Function* Cast(Value* obj); V8EXPORT static const int kLineOffsetNotFound; + private: V8EXPORT Function(); V8EXPORT static void CheckCast(Value* obj); @@ -2451,24 +2467,42 @@ class V8EXPORT TypeSwitch : public Data { // --- Extensions --- +class V8EXPORT ExternalAsciiStringResourceImpl + : public String::ExternalAsciiStringResource { + public: + ExternalAsciiStringResourceImpl() : data_(0), length_(0) {} + ExternalAsciiStringResourceImpl(const char* data, size_t length) + : data_(data), length_(length) {} + const char* data() const { return data_; } + size_t length() const { return length_; } + + private: + const char* data_; + size_t length_; +}; /** * Ignore */ class V8EXPORT Extension { // NOLINT public: + // Note that the strings passed into this constructor must live as long + // as the Extension itself. Extension(const char* name, const char* source = 0, int dep_count = 0, - const char** deps = 0); + const char** deps = 0, + int source_length = -1); virtual ~Extension() { } virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(v8::Handle<v8::String> name) { return v8::Handle<v8::FunctionTemplate>(); } - const char* name() { return name_; } - const char* source() { return source_; } + const char* name() const { return name_; } + size_t source_length() const { return source_length_; } + const String::ExternalAsciiStringResource* source() const { + return &source_; } int dependency_count() { return dep_count_; } const char** dependencies() { return deps_; } void set_auto_enable(bool value) { auto_enable_ = value; } @@ -2476,7 +2510,8 @@ class V8EXPORT Extension { // NOLINT private: const char* name_; - const char* source_; + size_t source_length_; // expected to initialize before source_ + ExternalAsciiStringResourceImpl source_; int dep_count_; const char** deps_; bool auto_enable_; @@ -2608,6 +2643,9 @@ typedef void (*MemoryAllocationCallback)(ObjectSpace space, AllocationAction action, int size); +// --- Leave Script Callback --- +typedef void (*CallCompletedCallback)(); + // --- Failed Access Check Callback --- typedef void (*FailedAccessCheckCallback)(Local<Object> target, AccessType type, @@ -2687,7 +2725,7 @@ class RetainedObjectInfo; * default isolate is implicitly created and entered. The embedder * can create additional isolates and use them in parallel in multiple * threads. An isolate can be entered by at most one thread at any - * given time. The Locker/Unlocker API can be used to synchronize. + * given time. The Locker/Unlocker API must be used to synchronize. */ class V8EXPORT Isolate { public: @@ -2818,6 +2856,17 @@ class V8EXPORT StartupDataDecompressor { // NOLINT */ typedef bool (*EntropySource)(unsigned char* buffer, size_t length); + +/** + * Interface for iterating though all external resources in the heap. + */ +class V8EXPORT ExternalResourceVisitor { // NOLINT + public: + virtual ~ExternalResourceVisitor() {} + virtual void VisitExternalString(Handle<String> string) {} +}; + + /** * Container class for static utility functions. */ @@ -3006,12 +3055,25 @@ class V8EXPORT V8 { AllocationAction action); /** - * This function removes callback which was installed by - * AddMemoryAllocationCallback function. + * Removes callback that was installed by AddMemoryAllocationCallback. */ static void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback); /** + * Adds a callback to notify the host application when a script finished + * running. If a script re-enters the runtime during executing, the + * CallCompletedCallback is only invoked when the outer-most script + * execution ends. Executing scripts inside the callback do not trigger + * further callbacks. + */ + static void AddCallCompletedCallback(CallCompletedCallback callback); + + /** + * Removes callback that was installed by AddCallCompletedCallback. + */ + static void RemoveCallCompletedCallback(CallCompletedCallback callback); + + /** * Allows the host application to group objects together. If one * object in the group is alive, all objects in the group are alive. * After each garbage collection, object groups are removed. It is @@ -3161,14 +3223,25 @@ class V8EXPORT V8 { static void GetHeapStatistics(HeapStatistics* heap_statistics); /** + * Iterates through all external resources referenced from current isolate + * heap. This method is not expected to be used except for debugging purposes + * and may be quite slow. + */ + static void VisitExternalResources(ExternalResourceVisitor* visitor); + + /** * Optional notification that the embedder is idle. * V8 uses the notification to reduce memory footprint. * This call can be used repeatedly if the embedder remains idle. * Returns true if the embedder should stop calling IdleNotification * until real work has been done. This indicates that V8 has done * as much cleanup as it will be able to do. + * + * The hint argument specifies the amount of work to be done in the function + * on scale from 1 to 1000. There is no guarantee that the actual work will + * match the hint. */ - static bool IdleNotification(); + static bool IdleNotification(int hint = 1000); /** * Optional notification that the system is running low on memory. @@ -3466,6 +3539,12 @@ class V8EXPORT Context { void AllowCodeGenerationFromStrings(bool allow); /** + * Returns true if code generation from strings is allowed for the context. + * For more details see AllowCodeGenerationFromStrings(bool) documentation. + */ + bool IsCodeGenerationFromStringsAllowed(); + + /** * Stack-allocated class which sets the execution context for all * operations executed within a local scope. */ @@ -3494,13 +3573,15 @@ class V8EXPORT Context { * accessing handles or holding onto object pointers obtained * from V8 handles while in the particular V8 isolate. It is up * to the user of V8 to ensure (perhaps with locking) that this - * constraint is not violated. + * constraint is not violated. In addition to any other synchronization + * mechanism that may be used, the v8::Locker and v8::Unlocker classes + * must be used to signal thead switches to V8. * * v8::Locker is a scoped lock object. While it's * active (i.e. between its construction and destruction) the current thread is - * allowed to use the locked isolate. V8 guarantees that an isolate can be locked - * by at most one thread at any time. In other words, the scope of a v8::Locker is - * a critical section. + * allowed to use the locked isolate. V8 guarantees that an isolate can be + * locked by at most one thread at any time. In other words, the scope of a + * v8::Locker is a critical section. * * Sample usage: * \code @@ -3602,8 +3683,8 @@ class V8EXPORT Locker { static void StopPreemption(); /** - * Returns whether or not the locker for a given isolate, or default isolate if NULL is given, - * is locked by the current thread. + * Returns whether or not the locker for a given isolate, or default isolate + * if NULL is given, is locked by the current thread. */ static bool IsLocked(Isolate* isolate = NULL); @@ -3677,8 +3758,8 @@ class V8EXPORT ActivityControl { // NOLINT namespace internal { -static const int kApiPointerSize = sizeof(void*); // NOLINT -static const int kApiIntSize = sizeof(int); // NOLINT +const int kApiPointerSize = sizeof(void*); // NOLINT +const int kApiIntSize = sizeof(int); // NOLINT // Tag information for HeapObject. const int kHeapObjectTag = 1; @@ -3769,7 +3850,7 @@ class Internals { static const int kFullStringRepresentationMask = 0x07; static const int kExternalTwoByteRepresentationTag = 0x02; - static const int kJSObjectType = 0xa3; + static const int kJSObjectType = 0xa7; static const int kFirstNonstringType = 0x80; static const int kForeignType = 0x85; diff --git a/deps/v8/include/v8stdint.h b/deps/v8/include/v8stdint.h index 50b4f29a64..7c12e1f490 100644 --- a/deps/v8/include/v8stdint.h +++ b/deps/v8/include/v8stdint.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -30,6 +30,7 @@ #ifndef V8STDINT_H_ #define V8STDINT_H_ +#include <stddef.h> #include <stdio.h> #if defined(_WIN32) && !defined(__MINGW32__) diff --git a/deps/v8/preparser/preparser-process.cc b/deps/v8/preparser/preparser-process.cc index e67851cbd4..b0aeb81e2a 100644 --- a/deps/v8/preparser/preparser-process.cc +++ b/deps/v8/preparser/preparser-process.cc @@ -267,34 +267,22 @@ void CheckException(v8::PreParserData* data, ExceptionExpectation ParseExpectation(int argc, const char* argv[]) { + // Parse ["throws" [<exn-type> [<start> [<end>]]]]. ExceptionExpectation expects; - - // Parse exception expectations from (the remainder of) the command line. int arg_index = 0; - // Skip any flags. - while (argc > arg_index && IsFlag(argv[arg_index])) arg_index++; + while (argc > arg_index && strncmp("throws", argv[arg_index], 7)) { + arg_index++; + } if (argc > arg_index) { - if (strncmp("throws", argv[arg_index], 7)) { - // First argument after filename, if present, must be the verbatim - // "throws", marking that the preparsing should fail with an exception. - fail(NULL, "ERROR: Extra arguments not prefixed by \"throws\".\n"); - } expects.throws = true; - do { - arg_index++; - } while (argc > arg_index && IsFlag(argv[arg_index])); - if (argc > arg_index) { - // Next argument is the exception type identifier. + arg_index++; + if (argc > arg_index && !IsFlag(argv[arg_index])) { expects.type = argv[arg_index]; - do { - arg_index++; - } while (argc > arg_index && IsFlag(argv[arg_index])); - if (argc > arg_index) { + arg_index++; + if (argc > arg_index && !IsFlag(argv[arg_index])) { expects.beg_pos = atoi(argv[arg_index]); // NOLINT - do { - arg_index++; - } while (argc > arg_index && IsFlag(argv[arg_index])); - if (argc > arg_index) { + arg_index++; + if (argc > arg_index && !IsFlag(argv[arg_index])) { expects.end_pos = atoi(argv[arg_index]); // NOLINT } } @@ -308,7 +296,8 @@ int main(int argc, const char* argv[]) { // Parse command line. // Format: preparser (<scriptfile> | -e "<source>") // ["throws" [<exn-type> [<start> [<end>]]]] - // Any flags (except an initial -s) are ignored. + // Any flags (except an initial -e) are ignored. + // Flags must not separate "throws" and its arguments. // Check for mandatory filename argument. int arg_index = 1; diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 52607f15c5..42de36bc86 100644..100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -84,6 +84,7 @@ SOURCES = { hydrogen.cc hydrogen-instructions.cc ic.cc + incremental-marking.cc inspector.cc interpreter-irregexp.cc isolate.cc @@ -133,6 +134,7 @@ SOURCES = { v8utils.cc variables.cc version.cc + store-buffer.cc zone.cc extensions/gc-extension.cc extensions/externalize-string-extension.cc @@ -170,6 +172,9 @@ SOURCES = { mips/frames-mips.cc mips/full-codegen-mips.cc mips/ic-mips.cc + mips/lithium-codegen-mips.cc + mips/lithium-gap-resolver-mips.cc + mips/lithium-mips.cc mips/macro-assembler-mips.cc mips/regexp-macro-assembler-mips.cc mips/stub-cache-mips.cc @@ -319,7 +324,7 @@ debug-debugger.js EXPERIMENTAL_LIBRARY_FILES = ''' proxy.js -weakmap.js +collection.js '''.split() diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc index 951209d964..8048738b28 100644 --- a/deps/v8/src/accessors.cc +++ b/deps/v8/src/accessors.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -26,15 +26,16 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" - #include "accessors.h" -#include "ast.h" + +#include "contexts.h" #include "deoptimizer.h" #include "execution.h" #include "factory.h" +#include "frames-inl.h" +#include "isolate.h" #include "list-inl.h" -#include "safepoint-table.h" -#include "scopeinfo.h" +#include "property-details.h" namespace v8 { namespace internal { @@ -486,16 +487,6 @@ MaybeObject* Accessors::FunctionSetPrototype(JSObject* object, NONE); } - if (function->has_initial_map()) { - // If the function has allocated the initial map - // replace it with a copy containing the new prototype. - Object* new_map; - { MaybeObject* maybe_new_map = - function->initial_map()->CopyDropTransitions(); - if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; - } - function->set_initial_map(Map::cast(new_map)); - } Object* prototype; { MaybeObject* maybe_prototype = function->SetPrototype(value); if (!maybe_prototype->ToObject(&prototype)) return maybe_prototype; @@ -527,7 +518,9 @@ MaybeObject* Accessors::FunctionGetLength(Object* object, void*) { // correctly yet. Compile it now and return the right length. HandleScope scope; Handle<JSFunction> handle(function); - if (!CompileLazy(handle, KEEP_EXCEPTION)) return Failure::Exception(); + if (!JSFunction::CompileLazy(handle, KEEP_EXCEPTION)) { + return Failure::Exception(); + } return Smi::FromInt(handle->shared()->length()); } else { return Smi::FromInt(function->shared()->length()); @@ -572,11 +565,12 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction( Handle<JSFunction> inlined_function, int inlined_frame_index) { Factory* factory = Isolate::Current()->factory(); - int args_count = inlined_function->shared()->formal_parameter_count(); - ScopedVector<SlotRef> args_slots(args_count); - SlotRef::ComputeSlotMappingForArguments(frame, - inlined_frame_index, - &args_slots); + Vector<SlotRef> args_slots = + SlotRef::ComputeSlotMappingForArguments( + frame, + inlined_frame_index, + inlined_function->shared()->formal_parameter_count()); + int args_count = args_slots.length(); Handle<JSObject> arguments = factory->NewArgumentsObject(inlined_function, args_count); Handle<FixedArray> array = factory->NewFixedArray(args_count); @@ -585,6 +579,7 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction( array->set(i, *value); } arguments->set_elements(*array); + args_slots.Dispose(); // Return the freshly allocated arguments object. return *arguments; @@ -619,8 +614,9 @@ MaybeObject* Accessors::FunctionGetArguments(Object* object, void*) { if (!frame->is_optimized()) { // If there is an arguments variable in the stack, we return that. - Handle<SerializedScopeInfo> info(function->shared()->scope_info()); - int index = info->StackSlotIndex(isolate->heap()->arguments_symbol()); + Handle<ScopeInfo> scope_info(function->shared()->scope_info()); + int index = scope_info->StackSlotIndex( + isolate->heap()->arguments_symbol()); if (index >= 0) { Handle<Object> arguments(frame->GetExpression(index), isolate); if (!arguments->IsArgumentsMarker()) return *arguments; @@ -672,7 +668,7 @@ static MaybeObject* CheckNonStrictCallerOrThrow( Isolate* isolate, JSFunction* caller) { DisableAssertNoAllocation enable_allocation; - if (caller->shared()->strict_mode()) { + if (!caller->shared()->is_classic_mode()) { return isolate->Throw( *isolate->factory()->NewTypeError("strict_caller", HandleVector<Object>(NULL, 0))); @@ -759,7 +755,12 @@ MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) { caller = potential_caller; potential_caller = it.next(); } - + // If caller is bound, return null. This is compatible with JSC, and + // allows us to make bound functions use the strict function map + // and its associated throwing caller and arguments. + if (caller->shared()->bound()) { + return isolate->heap()->null_value(); + } return CheckNonStrictCallerOrThrow(isolate, caller); } diff --git a/deps/v8/src/accessors.h b/deps/v8/src/accessors.h index 385536d22e..36b9a9984a 100644 --- a/deps/v8/src/accessors.h +++ b/deps/v8/src/accessors.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,7 @@ #define V8_ACCESSORS_H_ #include "allocation.h" +#include "v8globals.h" namespace v8 { namespace internal { diff --git a/deps/v8/src/allocation.cc b/deps/v8/src/allocation.cc index 119b087c19..6c7a08cec8 100644 --- a/deps/v8/src/allocation.cc +++ b/deps/v8/src/allocation.cc @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,10 +25,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "../include/v8stdint.h" -#include "globals.h" -#include "checks.h" #include "allocation.h" + +#include <stdlib.h> // For free, malloc. +#include <string.h> // For memcpy. +#include "checks.h" #include "utils.h" namespace v8 { diff --git a/deps/v8/src/allocation.h b/deps/v8/src/allocation.h index 75aba35d8c..69e72bdbad 100644 --- a/deps/v8/src/allocation.h +++ b/deps/v8/src/allocation.h @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,7 +28,6 @@ #ifndef V8_ALLOCATION_H_ #define V8_ALLOCATION_H_ -#include "checks.h" #include "globals.h" namespace v8 { @@ -81,7 +80,7 @@ class AllStatic { template <typename T> -static T* NewArray(int size) { +T* NewArray(int size) { T* result = new T[size]; if (result == NULL) Malloced::FatalProcessOutOfMemory(); return result; @@ -89,7 +88,7 @@ static T* NewArray(int size) { template <typename T> -static void DeleteArray(T* array) { +void DeleteArray(T* array) { delete[] array; } diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 39c0d02b61..2f8f1d15da 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,34 +25,36 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" - #include "api.h" -#include "arguments.h" +#include <math.h> // For isnan. +#include <string.h> // For memcpy, strlen. +#include "../include/v8-debug.h" +#include "../include/v8-profiler.h" +#include "../include/v8-testing.h" #include "bootstrapper.h" #include "compiler.h" +#include "conversions-inl.h" +#include "counters.h" #include "debug.h" #include "deoptimizer.h" #include "execution.h" -#include "flags.h" #include "global-handles.h" #include "heap-profiler.h" #include "messages.h" -#include "natives.h" #include "parser.h" #include "platform.h" #include "profile-generator-inl.h" +#include "property-details.h" +#include "property.h" #include "runtime-profiler.h" #include "scanner-character-streams.h" -#include "serialize.h" #include "snapshot.h" +#include "unicode-inl.h" #include "v8threads.h" #include "version.h" #include "vm-state-inl.h" -#include "../include/v8-profiler.h" -#include "../include/v8-testing.h" #define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr)) @@ -78,7 +80,7 @@ namespace v8 { bool has_pending_exception = false -#define EXCEPTION_BAILOUT_CHECK(isolate, value) \ +#define EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, do_callback) \ do { \ i::HandleScopeImplementer* handle_scope_implementer = \ (isolate)->handle_scope_implementer(); \ @@ -91,11 +93,22 @@ namespace v8 { } \ bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); \ (isolate)->OptionalRescheduleException(call_depth_is_zero); \ + do_callback \ return value; \ } \ + do_callback \ } while (false) +#define EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, value) \ + EXCEPTION_BAILOUT_CHECK_GENERIC( \ + isolate, value, i::V8::FireCallCompletedCallback(isolate);) + + +#define EXCEPTION_BAILOUT_CHECK(isolate, value) \ + EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, ;) + + #define API_ENTRY_CHECK(isolate, msg) \ do { \ if (v8::Locker::IsActive()) { \ @@ -185,7 +198,10 @@ void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) { int end_marker; heap_stats.end_marker = &end_marker; i::Isolate* isolate = i::Isolate::Current(); - isolate->heap()->RecordStats(&heap_stats, take_snapshot); + // BUG(1718): + // Don't use the take_snapshot since we don't support HeapIterator here + // without doing a special GC. + isolate->heap()->RecordStats(&heap_stats, false); i::V8::SetFatalError(); FatalErrorCallback callback = GetFatalErrorHandler(); { @@ -483,7 +499,7 @@ RegisteredExtension* RegisteredExtension::first_extension_ = NULL; RegisteredExtension::RegisteredExtension(Extension* extension) - : extension_(extension), state_(UNVISITED) { } + : extension_(extension) { } void RegisteredExtension::Register(RegisteredExtension* that) { @@ -501,9 +517,12 @@ void RegisterExtension(Extension* that) { Extension::Extension(const char* name, const char* source, int dep_count, - const char** deps) + const char** deps, + int source_length) : name_(name), - source_(source), + source_length_(source_length >= 0 ? + source_length : (source ? strlen(source) : 0)), + source_(source, source_length_), dep_count_(dep_count), deps_(deps), auto_enable_(false) { } @@ -724,6 +743,7 @@ void Context::Exit() { i::Context* last_context = isolate->handle_scope_implementer()->RestoreContext(); isolate->set_context(last_context); + isolate->set_context_exit_happened(true); } @@ -1407,7 +1427,7 @@ void ObjectTemplate::SetInternalFieldCount(int value) { ScriptData* ScriptData::PreCompile(const char* input, int length) { i::Utf8ToUC16CharacterStream stream( reinterpret_cast<const unsigned char*>(input), length); - return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_scoping); } @@ -1416,10 +1436,10 @@ ScriptData* ScriptData::PreCompile(v8::Handle<String> source) { if (str->IsExternalTwoByteString()) { i::ExternalTwoByteStringUC16CharacterStream stream( i::Handle<i::ExternalTwoByteString>::cast(str), 0, str->length()); - return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_scoping); } else { i::GenericStringUC16CharacterStream stream(str, 0, str->length()); - return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping); + return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_scoping); } } @@ -1562,7 +1582,7 @@ Local<Value> Script::Run() { isolate->context()->global_proxy(), isolate); i::Handle<i::Object> result = i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>()); raw_result = *result; } i::Handle<i::Object> result(raw_result, isolate); @@ -1788,7 +1808,7 @@ v8::Handle<v8::StackTrace> Message::GetStackTrace() const { static i::Handle<i::Object> CallV8HeapFunction(const char* name, i::Handle<i::Object> recv, int argc, - i::Object** argv[], + i::Handle<i::Object> argv[], bool* has_pending_exception) { i::Isolate* isolate = i::Isolate::Current(); i::Handle<i::String> fmt_str = isolate->factory()->LookupAsciiSymbol(name); @@ -1805,10 +1825,10 @@ static i::Handle<i::Object> CallV8HeapFunction(const char* name, static i::Handle<i::Object> CallV8HeapFunction(const char* name, i::Handle<i::Object> data, bool* has_pending_exception) { - i::Object** argv[1] = { data.location() }; + i::Handle<i::Object> argv[] = { data }; return CallV8HeapFunction(name, i::Isolate::Current()->js_builtins_object(), - 1, + ARRAY_SIZE(argv), argv, has_pending_exception); } @@ -2148,6 +2168,11 @@ bool Value::IsInt32() const { if (obj->IsSmi()) return true; if (obj->IsNumber()) { double value = obj->Number(); + static const i::DoubleRepresentation minus_zero(-0.0); + i::DoubleRepresentation rep(value); + if (rep.bits == minus_zero.bits) { + return false; + } return i::FastI2D(i::FastD2I(value)) == value; } return false; @@ -2160,6 +2185,11 @@ bool Value::IsUint32() const { if (obj->IsSmi()) return i::Smi::cast(*obj)->value() >= 0; if (obj->IsNumber()) { double value = obj->Number(); + static const i::DoubleRepresentation minus_zero(-0.0); + i::DoubleRepresentation rep(value); + if (rep.bits == minus_zero.bits) { + return false; + } return i::FastUI2D(i::FastD2UI(value)) == value; } return false; @@ -2628,10 +2658,11 @@ bool Value::Equals(Handle<Value> that) const { if (obj->IsJSObject() && other->IsJSObject()) { return *obj == *other; } - i::Object** args[1] = { other.location() }; + i::Handle<i::Object> args[] = { other }; EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> result = - CallV8HeapFunction("EQUALS", obj, 1, args, &has_pending_exception); + CallV8HeapFunction("EQUALS", obj, ARRAY_SIZE(args), args, + &has_pending_exception); EXCEPTION_BAILOUT_CHECK(isolate, false); return *result == i::Smi::FromInt(i::EQUAL); } @@ -2721,7 +2752,7 @@ bool v8::Object::Set(uint32_t index, v8::Handle<Value> value) { i::Handle<i::JSObject> self = Utils::OpenHandle(this); i::Handle<i::Object> value_obj = Utils::OpenHandle(*value); EXCEPTION_PREAMBLE(isolate); - i::Handle<i::Object> obj = i::SetElement( + i::Handle<i::Object> obj = i::JSObject::SetElement( self, index, value_obj, @@ -2794,7 +2825,7 @@ Local<Value> v8::Object::Get(uint32_t index) { ENTER_V8(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); EXCEPTION_PREAMBLE(isolate); - i::Handle<i::Object> result = i::GetElement(self, index); + i::Handle<i::Object> result = i::Object::GetElement(self, index); has_pending_exception = result.is_null(); EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); return Utils::ToLocal(result); @@ -2827,7 +2858,7 @@ Local<Value> v8::Object::GetPrototype() { return Local<v8::Value>()); ENTER_V8(isolate); i::Handle<i::Object> self = Utils::OpenHandle(this); - i::Handle<i::Object> result = i::GetPrototype(self); + i::Handle<i::Object> result(self->GetPrototype()); return Utils::ToLocal(result); } @@ -2874,8 +2905,10 @@ Local<Array> v8::Object::GetPropertyNames() { ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); + bool threw = false; i::Handle<i::FixedArray> value = - i::GetKeysInFixedArrayFor(self, i::INCLUDE_PROTOS); + i::GetKeysInFixedArrayFor(self, i::INCLUDE_PROTOS, &threw); + if (threw) return Local<v8::Array>(); // Because we use caching to speed up enumeration it is important // to never change the result of the basic enumeration function so // we clone the result. @@ -2893,8 +2926,10 @@ Local<Array> v8::Object::GetOwnPropertyNames() { ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); + bool threw = false; i::Handle<i::FixedArray> value = - i::GetKeysInFixedArrayFor(self, i::LOCAL_ONLY); + i::GetKeysInFixedArrayFor(self, i::LOCAL_ONLY, &threw); + if (threw) return Local<v8::Array>(); // Because we use caching to speed up enumeration it is important // to never change the result of the basic enumeration function so // we clone the result. @@ -2977,7 +3012,7 @@ bool v8::Object::Delete(v8::Handle<String> key) { i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - return i::DeleteProperty(self, key_obj)->IsTrue(); + return i::JSObject::DeleteProperty(self, key_obj)->IsTrue(); } @@ -2998,7 +3033,7 @@ bool v8::Object::Delete(uint32_t index) { ENTER_V8(isolate); HandleScope scope; i::Handle<i::JSObject> self = Utils::OpenHandle(this); - return i::DeleteElement(self, index)->IsTrue(); + return i::JSObject::DeleteElement(self, index)->IsTrue(); } @@ -3093,7 +3128,10 @@ static Local<Value> GetPropertyByLookup(i::Isolate* isolate, // If the property being looked up is a callback, it can throw // an exception. EXCEPTION_PREAMBLE(isolate); - i::Handle<i::Object> result = i::GetProperty(receiver, name, lookup); + PropertyAttributes ignored; + i::Handle<i::Object> result = + i::Object::GetProperty(receiver, receiver, lookup, name, + &ignored); has_pending_exception = result.is_null(); EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); @@ -3110,7 +3148,7 @@ Local<Value> v8::Object::GetRealNamedPropertyInPrototypeChain( ENTER_V8(isolate); i::Handle<i::JSObject> self_obj = Utils::OpenHandle(this); i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - i::LookupResult lookup; + i::LookupResult lookup(isolate); self_obj->LookupRealNamedPropertyInPrototypes(*key_obj, &lookup); return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup); } @@ -3123,7 +3161,7 @@ Local<Value> v8::Object::GetRealNamedProperty(Handle<String> key) { ENTER_V8(isolate); i::Handle<i::JSObject> self_obj = Utils::OpenHandle(this); i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - i::LookupResult lookup; + i::LookupResult lookup(isolate); self_obj->LookupRealNamedProperty(*key_obj, &lookup); return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup); } @@ -3200,7 +3238,7 @@ int v8::Object::GetIdentityHash() { ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); - return i::GetIdentityHash(self); + return i::JSObject::GetIdentityHash(self); } @@ -3211,21 +3249,11 @@ bool v8::Object::SetHiddenValue(v8::Handle<v8::String> key, ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); - i::Handle<i::Object> hidden_props(i::GetHiddenProperties( - self, - i::JSObject::ALLOW_CREATION)); - i::Handle<i::Object> key_obj = Utils::OpenHandle(*key); + i::Handle<i::String> key_obj = Utils::OpenHandle(*key); i::Handle<i::Object> value_obj = Utils::OpenHandle(*value); - EXCEPTION_PREAMBLE(isolate); - i::Handle<i::Object> obj = i::SetProperty( - hidden_props, - key_obj, - value_obj, - static_cast<PropertyAttributes>(None), - i::kNonStrictMode); - has_pending_exception = obj.is_null(); - EXCEPTION_BAILOUT_CHECK(isolate, false); - return true; + i::Handle<i::Object> result = + i::JSObject::SetHiddenProperty(self, key_obj, value_obj); + return *result == *self; } @@ -3235,20 +3263,9 @@ v8::Local<v8::Value> v8::Object::GetHiddenValue(v8::Handle<v8::String> key) { return Local<v8::Value>()); ENTER_V8(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); - i::Handle<i::Object> hidden_props(i::GetHiddenProperties( - self, - i::JSObject::OMIT_CREATION)); - if (hidden_props->IsUndefined()) { - return v8::Local<v8::Value>(); - } i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - EXCEPTION_PREAMBLE(isolate); - i::Handle<i::Object> result = i::GetProperty(hidden_props, key_obj); - has_pending_exception = result.is_null(); - EXCEPTION_BAILOUT_CHECK(isolate, v8::Local<v8::Value>()); - if (result->IsUndefined()) { - return v8::Local<v8::Value>(); - } + i::Handle<i::Object> result(self->GetHiddenProperty(*key_obj)); + if (result->IsUndefined()) return v8::Local<v8::Value>(); return Utils::ToLocal(result); } @@ -3259,15 +3276,9 @@ bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) { ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle<i::JSObject> self = Utils::OpenHandle(this); - i::Handle<i::Object> hidden_props(i::GetHiddenProperties( - self, - i::JSObject::OMIT_CREATION)); - if (hidden_props->IsUndefined()) { - return true; - } - i::Handle<i::JSObject> js_obj(i::JSObject::cast(*hidden_props)); i::Handle<i::String> key_obj = Utils::OpenHandle(*key); - return i::DeleteProperty(js_obj, key_obj)->IsTrue(); + self->DeleteHiddenProperty(*key_obj); + return true; } @@ -3317,22 +3328,12 @@ void PrepareExternalArrayElements(i::Handle<i::JSObject> object, i::Handle<i::ExternalArray> array = isolate->factory()->NewExternalArray(length, array_type, data); - // If the object already has external elements, create a new, unique - // map if the element type is now changing, because assumptions about - // generated code based on the receiver's map will be invalid. - i::Handle<i::HeapObject> elements(object->elements()); - bool cant_reuse_map = - elements->map()->IsUndefined() || - !elements->map()->has_external_array_elements() || - elements->map() != isolate->heap()->MapForExternalArrayType(array_type); - if (cant_reuse_map) { - i::Handle<i::Map> external_array_map = - isolate->factory()->GetElementsTransitionMap( - i::Handle<i::Map>(object->map()), - GetElementsKindFromExternalArrayType(array_type), - object->HasFastProperties()); - object->set_map(*external_array_map); - } + i::Handle<i::Map> external_array_map = + isolate->factory()->GetElementsTransitionMap( + object, + GetElementsKindFromExternalArrayType(array_type)); + + object->set_map(*external_array_map); object->set_elements(*array); } @@ -3491,7 +3492,8 @@ bool v8::Object::IsCallable() { } -Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, +Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, + int argc, v8::Handle<v8::Value> argv[]) { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ON_BAILOUT(isolate, "v8::Object::CallAsFunction()", @@ -3502,7 +3504,7 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, i::Handle<i::JSObject> obj = Utils::OpenHandle(this); i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv); STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); - i::Object*** args = reinterpret_cast<i::Object***>(argv); + i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv); i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>(); if (obj->IsJSFunction()) { fun = i::Handle<i::JSFunction>::cast(obj); @@ -3517,7 +3519,7 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> returned = i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>()); return Utils::ToLocal(scope.CloseAndEscape(returned)); } @@ -3532,13 +3534,13 @@ Local<v8::Value> Object::CallAsConstructor(int argc, i::HandleScope scope(isolate); i::Handle<i::JSObject> obj = Utils::OpenHandle(this); STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); - i::Object*** args = reinterpret_cast<i::Object***>(argv); + i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv); if (obj->IsJSFunction()) { i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(obj); EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> returned = i::Execution::New(fun, argc, args, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>()); return Utils::ToLocal(scope.CloseAndEscape( i::Handle<i::JSObject>::cast(returned))); } @@ -3551,7 +3553,7 @@ Local<v8::Value> Object::CallAsConstructor(int argc, EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> returned = i::Execution::Call(fun, obj, argc, args, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>()); ASSERT(!delegate->IsUndefined()); return Utils::ToLocal(scope.CloseAndEscape(returned)); } @@ -3574,11 +3576,11 @@ Local<v8::Object> Function::NewInstance(int argc, HandleScope scope; i::Handle<i::JSFunction> function = Utils::OpenHandle(this); STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); - i::Object*** args = reinterpret_cast<i::Object***>(argv); + i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv); EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> returned = i::Execution::New(function, argc, args, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>()); return scope.Close(Utils::ToLocal(i::Handle<i::JSObject>::cast(returned))); } @@ -3595,11 +3597,11 @@ Local<v8::Value> Function::Call(v8::Handle<v8::Object> recv, int argc, i::Handle<i::JSFunction> fun = Utils::OpenHandle(this); i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv); STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); - i::Object*** args = reinterpret_cast<i::Object***>(argv); + i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv); EXCEPTION_PREAMBLE(isolate); i::Handle<i::Object> returned = i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); - EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>()); + EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Object>()); raw_result = *returned; } i::Handle<i::Object> result(raw_result); @@ -3622,6 +3624,12 @@ Handle<Value> Function::GetName() const { } +Handle<Value> Function::GetInferredName() const { + i::Handle<i::JSFunction> func = Utils::OpenHandle(this); + return Utils::ToLocal(i::Handle<i::Object>(func->shared()->inferred_name())); +} + + ScriptOrigin Function::GetScriptOrigin() const { i::Handle<i::JSFunction> func = Utils::OpenHandle(this); if (func->shared()->script()->IsScript()) { @@ -3649,6 +3657,23 @@ int Function::GetScriptLineNumber() const { } +int Function::GetScriptColumnNumber() const { + i::Handle<i::JSFunction> func = Utils::OpenHandle(this); + if (func->shared()->script()->IsScript()) { + i::Handle<i::Script> script(i::Script::cast(func->shared()->script())); + return i::GetScriptColumnNumber(script, func->shared()->start_position()); + } + return kLineOffsetNotFound; +} + +Handle<Value> Function::GetScriptId() const { + i::Handle<i::JSFunction> func = Utils::OpenHandle(this); + if (!func->shared()->script()->IsScript()) + return v8::Undefined(); + i::Handle<i::Script> script(i::Script::cast(func->shared()->script())); + return Utils::ToLocal(i::Handle<i::Object>(script->id())); +} + int String::Length() const { i::Handle<i::String> str = Utils::OpenHandle(this); if (IsDeadCheck(str->GetIsolate(), "v8::String::Length()")) return 0; @@ -3671,13 +3696,30 @@ int String::WriteUtf8(char* buffer, if (IsDeadCheck(isolate, "v8::String::WriteUtf8()")) return 0; LOG_API(isolate, "String::WriteUtf8"); ENTER_V8(isolate); - i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); i::Handle<i::String> str = Utils::OpenHandle(this); + if (str->IsAsciiRepresentation()) { + int len; + if (capacity == -1) { + capacity = str->length() + 1; + len = str->length(); + } else { + len = i::Min(capacity, str->length()); + } + i::String::WriteToFlat(*str, buffer, 0, len); + if (nchars_ref != NULL) *nchars_ref = len; + if (!(options & NO_NULL_TERMINATION) && capacity > len) { + buffer[len] = '\0'; + return len + 1; + } + return len; + } + + i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); isolate->string_tracker()->RecordWrite(str); if (options & HINT_MANY_WRITES_EXPECTED) { // Flatten the string for efficiency. This applies whether we are // using StringInputBuffer or Get(i) to access the characters. - str->TryFlatten(); + FlattenString(str); } write_input_buffer.Reset(0, *str); int len = str->length(); @@ -3806,10 +3848,11 @@ bool v8::String::IsExternalAscii() const { void v8::String::VerifyExternalStringResource( v8::String::ExternalStringResource* value) const { i::Handle<i::String> str = Utils::OpenHandle(this); - v8::String::ExternalStringResource* expected; + const v8::String::ExternalStringResource* expected; if (i::StringShape(*str).IsExternalTwoByte()) { - void* resource = i::Handle<i::ExternalTwoByteString>::cast(str)->resource(); - expected = reinterpret_cast<ExternalStringResource*>(resource); + const void* resource = + i::Handle<i::ExternalTwoByteString>::cast(str)->resource(); + expected = reinterpret_cast<const ExternalStringResource*>(resource); } else { expected = NULL; } @@ -3817,7 +3860,7 @@ void v8::String::VerifyExternalStringResource( } -v8::String::ExternalAsciiStringResource* +const v8::String::ExternalAsciiStringResource* v8::String::GetExternalAsciiStringResource() const { i::Handle<i::String> str = Utils::OpenHandle(this); if (IsDeadCheck(str->GetIsolate(), @@ -3825,8 +3868,9 @@ v8::String::ExternalAsciiStringResource* return NULL; } if (i::StringShape(*str).IsExternalAscii()) { - void* resource = i::Handle<i::ExternalAsciiString>::cast(str)->resource(); - return reinterpret_cast<ExternalAsciiStringResource*>(resource); + const void* resource = + i::Handle<i::ExternalAsciiString>::cast(str)->resource(); + return reinterpret_cast<const ExternalAsciiStringResource*>(resource); } else { return NULL; } @@ -3996,6 +4040,15 @@ HeapStatistics::HeapStatistics(): total_heap_size_(0), void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) { + if (!i::Isolate::Current()->IsInitialized()) { + // Isolate is unitialized thus heap is not configured yet. + heap_statistics->set_total_heap_size(0); + heap_statistics->set_total_heap_size_executable(0); + heap_statistics->set_used_heap_size(0); + heap_statistics->set_heap_size_limit(0); + return; + } + i::Heap* heap = i::Isolate::Current()->heap(); heap_statistics->set_total_heap_size(heap->CommittedMemory()); heap_statistics->set_total_heap_size_executable( @@ -4005,18 +4058,26 @@ void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) { } -bool v8::V8::IdleNotification() { +void v8::V8::VisitExternalResources(ExternalResourceVisitor* visitor) { + i::Isolate* isolate = i::Isolate::Current(); + IsDeadCheck(isolate, "v8::V8::VisitExternalResources"); + isolate->heap()->VisitExternalResources(visitor); +} + + +bool v8::V8::IdleNotification(int hint) { // Returning true tells the caller that it need not // continue to call IdleNotification. - if (!i::Isolate::Current()->IsInitialized()) return true; - return i::V8::IdleNotification(); + i::Isolate* isolate = i::Isolate::Current(); + if (isolate == NULL || !isolate->IsInitialized()) return true; + return i::V8::IdleNotification(hint); } void v8::V8::LowMemoryNotification() { i::Isolate* isolate = i::Isolate::Current(); - if (!isolate->IsInitialized()) return; - isolate->heap()->CollectAllGarbage(true); + if (isolate == NULL || !isolate->IsInitialized()) return; + isolate->heap()->CollectAllAvailableGarbage("low memory notification"); } @@ -4110,8 +4171,9 @@ Persistent<Context> v8::Context::New( } // Leave V8. - if (env.is_null()) + if (env.is_null()) { return Persistent<Context>(); + } return Persistent<Context>(Utils::ToLocal(env)); } @@ -4251,6 +4313,20 @@ void Context::AllowCodeGenerationFromStrings(bool allow) { } +bool Context::IsCodeGenerationFromStringsAllowed() { + i::Isolate* isolate = i::Isolate::Current(); + if (IsDeadCheck(isolate, + "v8::Context::IsCodeGenerationFromStringsAllowed()")) { + return false; + } + ENTER_V8(isolate); + i::Object** ctx = reinterpret_cast<i::Object**>(this); + i::Handle<i::Context> context = + i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx)); + return !context->allow_code_gen_from_strings()->IsFalse(); +} + + void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) { i::GlobalHandles::SetWrapperClassId(global_handle, class_id); } @@ -4299,7 +4375,7 @@ static Local<External> ExternalNewImpl(void* data) { } static void* ExternalValueImpl(i::Handle<i::Object> obj) { - return reinterpret_cast<void*>(i::Foreign::cast(*obj)->address()); + return reinterpret_cast<void*>(i::Foreign::cast(*obj)->foreign_address()); } @@ -4325,7 +4401,7 @@ void* v8::Object::SlowGetPointerFromInternalField(int index) { if (value->IsSmi()) { return i::Internals::GetExternalPointerFromSmi(value); } else if (value->IsForeign()) { - return reinterpret_cast<void*>(i::Foreign::cast(value)->address()); + return reinterpret_cast<void*>(i::Foreign::cast(value)->foreign_address()); } else { return NULL; } @@ -4535,15 +4611,13 @@ bool v8::String::MakeExternal( bool v8::String::CanMakeExternal() { + if (!internal::FLAG_clever_optimizations) return false; i::Handle<i::String> obj = Utils::OpenHandle(this); i::Isolate* isolate = obj->GetIsolate(); if (IsDeadCheck(isolate, "v8::String::CanMakeExternal()")) return false; - if (isolate->string_tracker()->IsFreshUnusedString(obj)) { - return false; - } + if (isolate->string_tracker()->IsFreshUnusedString(obj)) return false; int size = obj->Size(); // Byte size of the original string. - if (size < i::ExternalString::kSize) - return false; + if (size < i::ExternalString::kShortSize) return false; i::StringShape shape(*obj); return !shape.IsExternal(); } @@ -4877,7 +4951,7 @@ void V8::RemoveMessageListeners(MessageCallback that) { NeanderObject listener(i::JSObject::cast(listeners.get(i))); i::Handle<i::Foreign> callback_obj(i::Foreign::cast(listener.get(0))); - if (callback_obj->address() == FUNCTION_ADDR(that)) { + if (callback_obj->foreign_address() == FUNCTION_ADDR(that)) { listeners.set(i, isolate->heap()->undefined_value()); } } @@ -5023,6 +5097,21 @@ void V8::RemoveMemoryAllocationCallback(MemoryAllocationCallback callback) { } +void V8::AddCallCompletedCallback(CallCompletedCallback callback) { + if (callback == NULL) return; + i::Isolate* isolate = i::Isolate::Current(); + if (IsDeadCheck(isolate, "v8::V8::AddLeaveScriptCallback()")) return; + i::V8::AddCallCompletedCallback(callback); +} + + +void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) { + i::Isolate* isolate = i::Isolate::Current(); + if (IsDeadCheck(isolate, "v8::V8::RemoveLeaveScriptCallback()")) return; + i::V8::RemoveCallCompletedCallback(callback); +} + + void V8::PauseProfiler() { i::Isolate* isolate = i::Isolate::Current(); isolate->logger()->PauseProfiler(); @@ -5487,8 +5576,14 @@ bool Debug::EnableAgent(const char* name, int port, bool wait_for_connection) { wait_for_connection); } + +void Debug::DisableAgent() { + return i::Isolate::Current()->debugger()->StopAgent(); +} + + void Debug::ProcessDebugMessages() { - i::Execution::ProcessDebugMesssages(true); + i::Execution::ProcessDebugMessages(true); } Local<Context> Debug::GetDebugContext() { @@ -5993,9 +6088,7 @@ static void SetFlagsFromString(const char* flags) { void Testing::PrepareStressRun(int run) { static const char* kLazyOptimizations = - "--prepare-always-opt --nolimit-inlining " - "--noalways-opt --noopt-eagerly"; - static const char* kEagerOptimizations = "--opt-eagerly"; + "--prepare-always-opt --nolimit-inlining --noalways-opt"; static const char* kForcedOptimizations = "--always-opt"; // If deoptimization stressed turn on frequent deoptimization. If no value @@ -6012,15 +6105,12 @@ void Testing::PrepareStressRun(int run) { if (run == GetStressRuns() - 1) { SetFlagsFromString(kForcedOptimizations); } else { - SetFlagsFromString(kEagerOptimizations); SetFlagsFromString(kLazyOptimizations); } #else if (run == GetStressRuns() - 1) { SetFlagsFromString(kForcedOptimizations); - } else if (run == GetStressRuns() - 2) { - SetFlagsFromString(kEagerOptimizations); - } else { + } else if (run != GetStressRuns() - 2) { SetFlagsFromString(kLazyOptimizations); } #endif diff --git a/deps/v8/src/api.h b/deps/v8/src/api.h index 07723cb32c..89cf0c864c 100644 --- a/deps/v8/src/api.h +++ b/deps/v8/src/api.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,10 +28,14 @@ #ifndef V8_API_H_ #define V8_API_H_ -#include "apiutils.h" -#include "factory.h" +#include "v8.h" #include "../include/v8-testing.h" +#include "apiutils.h" +#include "contexts.h" +#include "factory.h" +#include "isolate.h" +#include "list-inl.h" namespace v8 { @@ -112,15 +116,16 @@ void NeanderObject::set(int offset, v8::internal::Object* value) { } -template <typename T> static inline T ToCData(v8::internal::Object* obj) { +template <typename T> inline T ToCData(v8::internal::Object* obj) { STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address)); return reinterpret_cast<T>( - reinterpret_cast<intptr_t>(v8::internal::Foreign::cast(obj)->address())); + reinterpret_cast<intptr_t>( + v8::internal::Foreign::cast(obj)->foreign_address())); } template <typename T> -static inline v8::internal::Handle<v8::internal::Object> FromCData(T obj) { +inline v8::internal::Handle<v8::internal::Object> FromCData(T obj) { STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address)); return FACTORY->NewForeign( reinterpret_cast<v8::internal::Address>(reinterpret_cast<intptr_t>(obj))); @@ -136,10 +141,6 @@ class ApiFunction { }; -enum ExtensionTraversalState { - UNVISITED, VISITED, INSTALLED -}; - class RegisteredExtension { public: @@ -148,14 +149,11 @@ class RegisteredExtension { Extension* extension() { return extension_; } RegisteredExtension* next() { return next_; } RegisteredExtension* next_auto() { return next_auto_; } - ExtensionTraversalState state() { return state_; } - void set_state(ExtensionTraversalState value) { state_ = value; } static RegisteredExtension* first_extension() { return first_extension_; } private: Extension* extension_; RegisteredExtension* next_; RegisteredExtension* next_auto_; - ExtensionTraversalState state_; static RegisteredExtension* first_extension_; }; @@ -242,7 +240,7 @@ class Utils { template <class T> -static inline T* ToApi(v8::internal::Handle<v8::internal::Object> obj) { +inline T* ToApi(v8::internal::Handle<v8::internal::Object> obj) { return reinterpret_cast<T*>(obj.location()); } @@ -483,7 +481,7 @@ class HandleScopeImplementer { }; -static const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page +const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page void HandleScopeImplementer::SaveContext(Context* context) { diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h index 5ad7b5abae..dd8ffcd77c 100644 --- a/deps/v8/src/arm/assembler-arm-inl.h +++ b/deps/v8/src/arm/assembler-arm-inl.h @@ -38,6 +38,7 @@ #define V8_ARM_ASSEMBLER_ARM_INL_H_ #include "arm/assembler-arm.h" + #include "cpu.h" #include "debug.h" @@ -71,7 +72,9 @@ Address RelocInfo::target_address() { Address RelocInfo::target_address_address() { - ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); + ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY + || rmode_ == EMBEDDED_OBJECT + || rmode_ == EXTERNAL_REFERENCE); return reinterpret_cast<Address>(Assembler::target_address_address_at(pc_)); } @@ -81,9 +84,14 @@ int RelocInfo::target_address_size() { } -void RelocInfo::set_target_address(Address target) { +void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); Assembler::set_target_address_at(pc_, target); + if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -105,9 +113,15 @@ Object** RelocInfo::target_object_address() { } -void RelocInfo::set_target_object(Object* target) { +void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target)); + if (mode == UPDATE_WRITE_BARRIER && + host() != NULL && + target->IsHeapObject()) { + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), &Memory::Object_at(pc_), HeapObject::cast(target)); + } } @@ -134,10 +148,17 @@ JSGlobalPropertyCell* RelocInfo::target_cell() { } -void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell) { +void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, + WriteBarrierMode mode) { ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; Memory::Address_at(pc_) = address; + if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { + // TODO(1550) We are passing NULL as a slot because cell can never be on + // evacuation candidate. + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), NULL, cell); + } } @@ -154,6 +175,11 @@ void RelocInfo::set_call_address(Address target) { ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target; + if (host() != NULL) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -202,13 +228,13 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() { void RelocInfo::Visit(ObjectVisitor* visitor) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - visitor->VisitPointer(target_object_address()); + visitor->VisitEmbeddedPointer(this); } else if (RelocInfo::IsCodeTarget(mode)) { visitor->VisitCodeTarget(this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { visitor->VisitGlobalPropertyCell(this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - visitor->VisitExternalReference(target_reference_address()); + visitor->VisitExternalReference(this); #ifdef ENABLE_DEBUGGER_SUPPORT // TODO(isolates): Get a cached isolate below. } else if (((RelocInfo::IsJSReturn(mode) && @@ -228,13 +254,13 @@ template<typename StaticVisitor> void RelocInfo::Visit(Heap* heap) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitPointer(heap, target_object_address()); + StaticVisitor::VisitEmbeddedPointer(heap, this); } else if (RelocInfo::IsCodeTarget(mode)) { StaticVisitor::VisitCodeTarget(heap, this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { StaticVisitor::VisitGlobalPropertyCell(heap, this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - StaticVisitor::VisitExternalReference(target_reference_address()); + StaticVisitor::VisitExternalReference(this); #ifdef ENABLE_DEBUGGER_SUPPORT } else if (heap->isolate()->debug()->has_break_points() && ((RelocInfo::IsJSReturn(mode) && diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index 0ec36921ab..25922361a2 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -78,7 +78,9 @@ static uint64_t CpuFeaturesImpliedByCompiler() { void CpuFeatures::Probe() { - ASSERT(!initialized_); + unsigned standard_features = (OS::CpuFeaturesImpliedByPlatform() | + CpuFeaturesImpliedByCompiler()); + ASSERT(supported_ == 0 || supported_ == standard_features); #ifdef DEBUG initialized_ = true; #endif @@ -86,8 +88,7 @@ void CpuFeatures::Probe() { // Get the features implied by the OS and the compiler settings. This is the // minimal set of features which is also alowed for generated code in the // snapshot. - supported_ |= OS::CpuFeaturesImpliedByPlatform(); - supported_ |= CpuFeaturesImpliedByCompiler(); + supported_ |= standard_features; if (Serializer::enabled()) { // No probing for features if we might serialize (generate snapshot). @@ -316,7 +317,7 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) own_buffer_ = false; } - // Setup buffer pointers. + // Set up buffer pointers. ASSERT(buffer_ != NULL); pc_ = buffer_; reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); @@ -348,7 +349,7 @@ void Assembler::GetCode(CodeDesc* desc) { CheckConstPool(true, false); ASSERT(num_pending_reloc_info_ == 0); - // Setup code descriptor. + // Set up code descriptor. desc->buffer = buffer_; desc->buffer_size = buffer_size_; desc->instr_size = pc_offset(); @@ -2445,7 +2446,7 @@ void Assembler::GrowBuffer() { } CHECK_GT(desc.buffer_size, 0); // no overflow - // Setup new buffer. + // Set up new buffer. desc.buffer = NewArray<byte>(desc.buffer_size); desc.instr_size = pc_offset(); @@ -2505,7 +2506,8 @@ void Assembler::dd(uint32_t data) { void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { - RelocInfo rinfo(pc_, rmode, data); // we do not try to reuse pool constants + // We do not try to reuse pool constants. + RelocInfo rinfo(pc_, rmode, data, NULL); if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::DEBUG_BREAK_SLOT) { // Adjust code for new modes. ASSERT(RelocInfo::IsDebugBreakSlot(rmode) @@ -2537,7 +2539,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { } ASSERT(buffer_space() >= kMaxRelocSize); // too late to grow buffer here if (rmode == RelocInfo::CODE_TARGET_WITH_ID) { - RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId()); + RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId(), NULL); ClearRecordedAstId(); reloc_info_writer.Write(&reloc_info_with_ast_id); } else { diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index eeadaca8a8..11e39df682 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -300,11 +300,13 @@ const DwVfpRegister d13 = { 13 }; const DwVfpRegister d14 = { 14 }; const DwVfpRegister d15 = { 15 }; -// Aliases for double registers. -const DwVfpRegister kFirstCalleeSavedDoubleReg = d8; -const DwVfpRegister kLastCalleeSavedDoubleReg = d15; -const DwVfpRegister kDoubleRegZero = d14; -const DwVfpRegister kScratchDoubleReg = d15; +// Aliases for double registers. Defined using #define instead of +// "static const DwVfpRegister&" because Clang complains otherwise when a +// compilation unit that includes this header doesn't use the variables. +#define kFirstCalleeSavedDoubleReg d8 +#define kLastCalleeSavedDoubleReg d15 +#define kDoubleRegZero d14 +#define kScratchDoubleReg d15 // Coprocessor register @@ -1207,6 +1209,10 @@ class Assembler : public AssemblerBase { PositionsRecorder* positions_recorder() { return &positions_recorder_; } // Read/patch instructions + Instr instr_at(int pos) { return *reinterpret_cast<Instr*>(buffer_ + pos); } + void instr_at_put(int pos, Instr instr) { + *reinterpret_cast<Instr*>(buffer_ + pos) = instr; + } static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); } static void instr_at_put(byte* pc, Instr instr) { *reinterpret_cast<Instr*>(pc) = instr; @@ -1261,12 +1267,6 @@ class Assembler : public AssemblerBase { int buffer_space() const { return reloc_info_writer.pos() - pc_; } - // Read/patch instructions - Instr instr_at(int pos) { return *reinterpret_cast<Instr*>(buffer_ + pos); } - void instr_at_put(int pos, Instr instr) { - *reinterpret_cast<Instr*>(buffer_ + pos) = instr; - } - // Decode branch instruction at pos and return branch target pos int target_at(int pos); diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index ae8cb56f9f..50b6bce30b 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -72,6 +72,22 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, } +// Load the built-in InternalArray function from the current context. +static void GenerateLoadInternalArrayFunction(MacroAssembler* masm, + Register result) { + // Load the global context. + + __ ldr(result, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ ldr(result, + FieldMemOperand(result, GlobalObject::kGlobalContextOffset)); + // Load the InternalArray function from the global context. + __ ldr(result, + MemOperand(result, + Context::SlotOffset( + Context::INTERNAL_ARRAY_FUNCTION_INDEX))); +} + + // Load the built-in Array function from the current context. static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { // Load the global context. @@ -86,12 +102,6 @@ static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { } -// This constant has the same value as JSArray::kPreallocatedArrayElements and -// if JSArray::kPreallocatedArrayElements is changed handling of loop unfolding -// below should be reconsidered. -static const int kLoopUnfoldLimit = 4; - - // Allocate an empty JSArray. The allocated array is put into the result // register. An elements backing store is allocated with size initial_capacity // and filled with the hole values. @@ -101,16 +111,17 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Register scratch1, Register scratch2, Register scratch3, - int initial_capacity, Label* gc_required) { - ASSERT(initial_capacity > 0); - // Load the initial map from the array function. - __ ldr(scratch1, FieldMemOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + const int initial_capacity = JSArray::kPreallocatedArrayElements; + STATIC_ASSERT(initial_capacity >= 0); + __ LoadInitialArrayMap(array_function, scratch2, scratch1); // Allocate the JSArray object together with space for a fixed array with the // requested elements. - int size = JSArray::kSize + FixedArray::SizeFor(initial_capacity); + int size = JSArray::kSize; + if (initial_capacity > 0) { + size += FixedArray::SizeFor(initial_capacity); + } __ AllocateInNewSpace(size, result, scratch2, @@ -130,6 +141,11 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, __ mov(scratch3, Operand(0, RelocInfo::NONE)); __ str(scratch3, FieldMemOperand(result, JSArray::kLengthOffset)); + if (initial_capacity == 0) { + __ str(scratch1, FieldMemOperand(result, JSArray::kElementsOffset)); + return; + } + // Calculate the location of the elements array and set elements array member // of the JSArray. // result: JSObject @@ -138,7 +154,6 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, __ str(scratch1, FieldMemOperand(result, JSArray::kElementsOffset)); // Clear the heap tag on the elements array. - STATIC_ASSERT(kSmiTag == 0); __ sub(scratch1, scratch1, Operand(kHeapObjectTag)); // Initialize the FixedArray and fill it with holes. FixedArray length is @@ -147,18 +162,29 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // scratch1: elements array (untagged) // scratch2: start of next object __ LoadRoot(scratch3, Heap::kFixedArrayMapRootIndex); - ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset); + STATIC_ASSERT(0 * kPointerSize == FixedArray::kMapOffset); __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); __ mov(scratch3, Operand(Smi::FromInt(initial_capacity))); - ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + STATIC_ASSERT(1 * kPointerSize == FixedArray::kLengthOffset); __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); - // Fill the FixedArray with the hole value. - ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); - ASSERT(initial_capacity <= kLoopUnfoldLimit); + // Fill the FixedArray with the hole value. Inline the code if short. + STATIC_ASSERT(2 * kPointerSize == FixedArray::kHeaderSize); __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex); - for (int i = 0; i < initial_capacity; i++) { + static const int kLoopUnfoldLimit = 4; + if (initial_capacity <= kLoopUnfoldLimit) { + for (int i = 0; i < initial_capacity; i++) { + __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); + } + } else { + Label loop, entry; + __ add(scratch2, scratch1, Operand(initial_capacity * kPointerSize)); + __ b(&entry); + __ bind(&loop); __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); + __ bind(&entry); + __ cmp(scratch1, scratch2); + __ b(lt, &loop); } } @@ -173,7 +199,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // register elements_array_storage is scratched. static void AllocateJSArray(MacroAssembler* masm, Register array_function, // Array function. - Register array_size, // As a smi. + Register array_size, // As a smi, cannot be 0. Register result, Register elements_array_storage, Register elements_array_end, @@ -181,32 +207,16 @@ static void AllocateJSArray(MacroAssembler* masm, Register scratch2, bool fill_with_hole, Label* gc_required) { - Label not_empty, allocated; - // Load the initial map from the array function. - __ ldr(elements_array_storage, - FieldMemOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); - - // Check whether an empty sized array is requested. - __ tst(array_size, array_size); - __ b(ne, ¬_empty); - - // If an empty array is requested allocate a small elements array anyway. This - // keeps the code below free of special casing for the empty array. - int size = JSArray::kSize + - FixedArray::SizeFor(JSArray::kPreallocatedArrayElements); - __ AllocateInNewSpace(size, - result, - elements_array_end, - scratch1, - gc_required, - TAG_OBJECT); - __ jmp(&allocated); + __ LoadInitialArrayMap(array_function, scratch2, elements_array_storage); + + if (FLAG_debug_code) { // Assert that array size is not zero. + __ tst(array_size, array_size); + __ Assert(ne, "array size is unexpectedly 0"); + } // Allocate the JSArray object together with space for a FixedArray with the // requested number of elements. - __ bind(¬_empty); STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); __ mov(elements_array_end, Operand((JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize)); @@ -226,7 +236,6 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array_storage: initial map // array_size: size of array (smi) - __ bind(&allocated); __ str(elements_array_storage, FieldMemOperand(result, JSObject::kMapOffset)); __ LoadRoot(elements_array_storage, Heap::kEmptyFixedArrayRootIndex); __ str(elements_array_storage, @@ -256,14 +265,6 @@ static void AllocateJSArray(MacroAssembler* masm, ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset); __ str(scratch1, MemOperand(elements_array_storage, kPointerSize, PostIndex)); STATIC_ASSERT(kSmiTag == 0); - __ tst(array_size, array_size); - // Length of the FixedArray is the number of pre-allocated elements if - // the actual JSArray has length 0 and the size of the JSArray for non-empty - // JSArrays. The length of a FixedArray is stored as a smi. - __ mov(array_size, - Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)), - LeaveCC, - eq); ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); __ str(array_size, MemOperand(elements_array_storage, kPointerSize, PostIndex)); @@ -311,23 +312,24 @@ static void AllocateJSArray(MacroAssembler* masm, static void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code) { Counters* counters = masm->isolate()->counters(); - Label argc_one_or_more, argc_two_or_more; + Label argc_one_or_more, argc_two_or_more, not_empty_array, empty_array, + has_non_smi_element; // Check for array construction with zero arguments or one. __ cmp(r0, Operand(0, RelocInfo::NONE)); __ b(ne, &argc_one_or_more); // Handle construction of an empty array. + __ bind(&empty_array); AllocateEmptyJSArray(masm, r1, r2, r3, r4, r5, - JSArray::kPreallocatedArrayElements, call_generic_code); __ IncrementCounter(counters->array_function_native(), 1, r3, r4); - // Setup return value, remove receiver from stack and return. + // Set up return value, remove receiver from stack and return. __ mov(r0, r2); __ add(sp, sp, Operand(kPointerSize)); __ Jump(lr); @@ -339,6 +341,13 @@ static void ArrayNativeCode(MacroAssembler* masm, __ b(ne, &argc_two_or_more); STATIC_ASSERT(kSmiTag == 0); __ ldr(r2, MemOperand(sp)); // Get the argument from the stack. + __ tst(r2, r2); + __ b(ne, ¬_empty_array); + __ Drop(1); // Adjust stack. + __ mov(r0, Operand(0)); // Treat this as a call with argc of zero. + __ b(&empty_array); + + __ bind(¬_empty_array); __ and_(r3, r2, Operand(kIntptrSignBit | kSmiTagMask), SetCC); __ b(ne, call_generic_code); @@ -363,7 +372,7 @@ static void ArrayNativeCode(MacroAssembler* masm, true, call_generic_code); __ IncrementCounter(counters->array_function_native(), 1, r2, r4); - // Setup return value, remove receiver and argument from stack and return. + // Set up return value, remove receiver and argument from stack and return. __ mov(r0, r3); __ add(sp, sp, Operand(2 * kPointerSize)); __ Jump(lr); @@ -398,13 +407,18 @@ static void ArrayNativeCode(MacroAssembler* masm, // r5: elements_array_end (untagged) // sp[0]: last argument Label loop, entry; + __ mov(r7, sp); __ jmp(&entry); __ bind(&loop); - __ ldr(r2, MemOperand(sp, kPointerSize, PostIndex)); + __ ldr(r2, MemOperand(r7, kPointerSize, PostIndex)); + if (FLAG_smi_only_arrays) { + __ JumpIfNotSmi(r2, &has_non_smi_element); + } __ str(r2, MemOperand(r5, -kPointerSize, PreIndex)); __ bind(&entry); __ cmp(r4, r5); __ b(lt, &loop); + __ mov(sp, r7); // Remove caller arguments and receiver from the stack, setup return value and // return. @@ -414,6 +428,44 @@ static void ArrayNativeCode(MacroAssembler* masm, __ add(sp, sp, Operand(kPointerSize)); __ mov(r0, r3); __ Jump(lr); + + __ bind(&has_non_smi_element); + __ UndoAllocationInNewSpace(r3, r4); + __ b(call_generic_code); +} + + +void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- lr : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + Label generic_array_code, one_or_more_arguments, two_or_more_arguments; + + // Get the InternalArray function. + GenerateLoadInternalArrayFunction(masm, r1); + + if (FLAG_debug_code) { + // Initial map for the builtin InternalArray functions should be maps. + __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); + __ tst(r2, Operand(kSmiTagMask)); + __ Assert(ne, "Unexpected initial map for InternalArray function"); + __ CompareObjectType(r2, r3, r4, MAP_TYPE); + __ Assert(eq, "Unexpected initial map for InternalArray function"); + } + + // Run the native code for the InternalArray function called as a normal + // function. + ArrayNativeCode(masm, &generic_array_code); + + // Jump to the generic array code if the specialized code cannot handle the + // construction. + __ bind(&generic_array_code); + + Handle<Code> array_code = + masm->isolate()->builtins()->InternalArrayCodeGeneric(); + __ Jump(array_code, RelocInfo::CODE_TARGET); } @@ -582,10 +634,11 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ bind(&convert_argument); __ push(function); // Preserve the function. __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4); - __ EnterInternalFrame(); - __ push(r0); - __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r0); + __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); + } __ pop(function); __ mov(argument, r0); __ b(&argument_is_string); @@ -601,15 +654,18 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // create a string wrapper. __ bind(&gc_required); __ IncrementCounter(counters->string_ctor_gc_required(), 1, r3, r4); - __ EnterInternalFrame(); - __ push(argument); - __ CallRuntime(Runtime::kNewStringWrapper, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(argument); + __ CallRuntime(Runtime::kNewStringWrapper, 1); + } __ Ret(); } -void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { +static void Generate_JSConstructStubHelper(MacroAssembler* masm, + bool is_api_function, + bool count_constructions) { // ----------- S t a t e ------------- // -- r0 : number of arguments // -- r1 : constructor function @@ -617,354 +673,319 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // -- sp[...]: constructor arguments // ----------------------------------- - Label non_function_call; - // Check that the function is not a smi. - __ JumpIfSmi(r1, &non_function_call); - // Check that the function is a JSFunction. - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &non_function_call); - - // Jump to the function-specific construct stub. - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kConstructStubOffset)); - __ add(pc, r2, Operand(Code::kHeaderSize - kHeapObjectTag)); - - // r0: number of arguments - // r1: called object - __ bind(&non_function_call); - // Set expected number of arguments to zero (not changing r0). - __ mov(r2, Operand(0, RelocInfo::NONE)); - __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); - __ SetCallKind(r5, CALL_AS_METHOD); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); -} - - -static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool count_constructions) { // Should never count constructions for api objects. ASSERT(!is_api_function || !count_constructions); Isolate* isolate = masm->isolate(); // Enter a construct frame. - __ EnterConstructFrame(); - - // Preserve the two incoming parameters on the stack. - __ mov(r0, Operand(r0, LSL, kSmiTagSize)); - __ push(r0); // Smi-tagged arguments count. - __ push(r1); // Constructor function. - - // Try to allocate the object without transitioning into C code. If any of the - // preconditions is not met, the code bails out to the runtime call. - Label rt_call, allocated; - if (FLAG_inline_new) { - Label undo_allocation; + { + FrameScope scope(masm, StackFrame::CONSTRUCT); + + // Preserve the two incoming parameters on the stack. + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); + __ push(r0); // Smi-tagged arguments count. + __ push(r1); // Constructor function. + + // Try to allocate the object without transitioning into C code. If any of + // the preconditions is not met, the code bails out to the runtime call. + Label rt_call, allocated; + if (FLAG_inline_new) { + Label undo_allocation; #ifdef ENABLE_DEBUGGER_SUPPORT - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(isolate); - __ mov(r2, Operand(debug_step_in_fp)); - __ ldr(r2, MemOperand(r2)); - __ tst(r2, r2); - __ b(ne, &rt_call); + ExternalReference debug_step_in_fp = + ExternalReference::debug_step_in_fp_address(isolate); + __ mov(r2, Operand(debug_step_in_fp)); + __ ldr(r2, MemOperand(r2)); + __ tst(r2, r2); + __ b(ne, &rt_call); #endif - // Load the initial map and verify that it is in fact a map. - // r1: constructor function - __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); - __ JumpIfSmi(r2, &rt_call); - __ CompareObjectType(r2, r3, r4, MAP_TYPE); - __ b(ne, &rt_call); - - // Check that the constructor is not constructing a JSFunction (see comments - // in Runtime_NewObject in runtime.cc). In which case the initial map's - // instance type would be JS_FUNCTION_TYPE. - // r1: constructor function - // r2: initial map - __ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE); - __ b(eq, &rt_call); - - if (count_constructions) { - Label allocate; - // Decrease generous allocation count. - __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - MemOperand constructor_count = - FieldMemOperand(r3, SharedFunctionInfo::kConstructionCountOffset); - __ ldrb(r4, constructor_count); - __ sub(r4, r4, Operand(1), SetCC); - __ strb(r4, constructor_count); - __ b(ne, &allocate); - - __ Push(r1, r2); - - __ push(r1); // constructor - // The call will replace the stub, so the countdown is only done once. - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - - __ pop(r2); - __ pop(r1); - - __ bind(&allocate); - } + // Load the initial map and verify that it is in fact a map. + // r1: constructor function + __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); + __ JumpIfSmi(r2, &rt_call); + __ CompareObjectType(r2, r3, r4, MAP_TYPE); + __ b(ne, &rt_call); + + // Check that the constructor is not constructing a JSFunction (see + // comments in Runtime_NewObject in runtime.cc). In which case the + // initial map's instance type would be JS_FUNCTION_TYPE. + // r1: constructor function + // r2: initial map + __ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE); + __ b(eq, &rt_call); - // Now allocate the JSObject on the heap. - // r1: constructor function - // r2: initial map - __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); - __ AllocateInNewSpace(r3, r4, r5, r6, &rt_call, SIZE_IN_WORDS); + if (count_constructions) { + Label allocate; + // Decrease generous allocation count. + __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + MemOperand constructor_count = + FieldMemOperand(r3, SharedFunctionInfo::kConstructionCountOffset); + __ ldrb(r4, constructor_count); + __ sub(r4, r4, Operand(1), SetCC); + __ strb(r4, constructor_count); + __ b(ne, &allocate); + + __ Push(r1, r2); + + __ push(r1); // constructor + // The call will replace the stub, so the countdown is only done once. + __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); + + __ pop(r2); + __ pop(r1); + + __ bind(&allocate); + } - // Allocated the JSObject, now initialize the fields. Map is set to initial - // map and properties and elements are set to empty fixed array. - // r1: constructor function - // r2: initial map - // r3: object size - // r4: JSObject (not tagged) - __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex); - __ mov(r5, r4); - ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); - __ str(r2, MemOperand(r5, kPointerSize, PostIndex)); - ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - - // Fill all the in-object properties with the appropriate filler. - // r1: constructor function - // r2: initial map - // r3: object size (in words) - // r4: JSObject (not tagged) - // r5: First in-object property of JSObject (not tagged) - __ add(r6, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. - ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize); - { Label loop, entry; + // Now allocate the JSObject on the heap. + // r1: constructor function + // r2: initial map + __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); + __ AllocateInNewSpace(r3, r4, r5, r6, &rt_call, SIZE_IN_WORDS); + + // Allocated the JSObject, now initialize the fields. Map is set to + // initial map and properties and elements are set to empty fixed array. + // r1: constructor function + // r2: initial map + // r3: object size + // r4: JSObject (not tagged) + __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex); + __ mov(r5, r4); + ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); + __ str(r2, MemOperand(r5, kPointerSize, PostIndex)); + ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset); + __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); + ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset); + __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); + + // Fill all the in-object properties with the appropriate filler. + // r1: constructor function + // r2: initial map + // r3: object size (in words) + // r4: JSObject (not tagged) + // r5: First in-object property of JSObject (not tagged) + __ add(r6, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. + ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize); + __ LoadRoot(r7, Heap::kUndefinedValueRootIndex); if (count_constructions) { + __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset)); + __ Ubfx(r0, r0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte, + kBitsPerByte); + __ add(r0, r5, Operand(r0, LSL, kPointerSizeLog2)); + // r0: offset of first field after pre-allocated fields + if (FLAG_debug_code) { + __ cmp(r0, r6); + __ Assert(le, "Unexpected number of pre-allocated property fields."); + } + __ InitializeFieldsWithFiller(r5, r0, r7); // To allow for truncation. __ LoadRoot(r7, Heap::kOnePointerFillerMapRootIndex); - } else { - __ LoadRoot(r7, Heap::kUndefinedValueRootIndex); } - __ b(&entry); - __ bind(&loop); - __ str(r7, MemOperand(r5, kPointerSize, PostIndex)); - __ bind(&entry); - __ cmp(r5, r6); - __ b(lt, &loop); - } + __ InitializeFieldsWithFiller(r5, r6, r7); + + // Add the object tag to make the JSObject real, so that we can continue + // and jump into the continuation code at any time from now on. Any + // failures need to undo the allocation, so that the heap is in a + // consistent state and verifiable. + __ add(r4, r4, Operand(kHeapObjectTag)); + + // Check if a non-empty properties array is needed. Continue with + // allocated object if not fall through to runtime call if it is. + // r1: constructor function + // r4: JSObject + // r5: start of next object (not tagged) + __ ldrb(r3, FieldMemOperand(r2, Map::kUnusedPropertyFieldsOffset)); + // The field instance sizes contains both pre-allocated property fields + // and in-object properties. + __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset)); + __ Ubfx(r6, r0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte, + kBitsPerByte); + __ add(r3, r3, Operand(r6)); + __ Ubfx(r6, r0, Map::kInObjectPropertiesByte * kBitsPerByte, + kBitsPerByte); + __ sub(r3, r3, Operand(r6), SetCC); + + // Done if no extra properties are to be allocated. + __ b(eq, &allocated); + __ Assert(pl, "Property allocation count failed."); + + // Scale the number of elements by pointer size and add the header for + // FixedArrays to the start of the next object calculation from above. + // r1: constructor + // r3: number of elements in properties array + // r4: JSObject + // r5: start of next object + __ add(r0, r3, Operand(FixedArray::kHeaderSize / kPointerSize)); + __ AllocateInNewSpace( + r0, + r5, + r6, + r2, + &undo_allocation, + static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS)); + + // Initialize the FixedArray. + // r1: constructor + // r3: number of elements in properties array + // r4: JSObject + // r5: FixedArray (not tagged) + __ LoadRoot(r6, Heap::kFixedArrayMapRootIndex); + __ mov(r2, r5); + ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); + __ str(r6, MemOperand(r2, kPointerSize, PostIndex)); + ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + __ mov(r0, Operand(r3, LSL, kSmiTagSize)); + __ str(r0, MemOperand(r2, kPointerSize, PostIndex)); + + // Initialize the fields to undefined. + // r1: constructor function + // r2: First element of FixedArray (not tagged) + // r3: number of elements in properties array + // r4: JSObject + // r5: FixedArray (not tagged) + __ add(r6, r2, Operand(r3, LSL, kPointerSizeLog2)); // End of object. + ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); + { Label loop, entry; + if (count_constructions) { + __ LoadRoot(r7, Heap::kUndefinedValueRootIndex); + } else if (FLAG_debug_code) { + __ LoadRoot(r8, Heap::kUndefinedValueRootIndex); + __ cmp(r7, r8); + __ Assert(eq, "Undefined value not loaded."); + } + __ b(&entry); + __ bind(&loop); + __ str(r7, MemOperand(r2, kPointerSize, PostIndex)); + __ bind(&entry); + __ cmp(r2, r6); + __ b(lt, &loop); + } - // Add the object tag to make the JSObject real, so that we can continue and - // jump into the continuation code at any time from now on. Any failures - // need to undo the allocation, so that the heap is in a consistent state - // and verifiable. - __ add(r4, r4, Operand(kHeapObjectTag)); + // Store the initialized FixedArray into the properties field of + // the JSObject + // r1: constructor function + // r4: JSObject + // r5: FixedArray (not tagged) + __ add(r5, r5, Operand(kHeapObjectTag)); // Add the heap tag. + __ str(r5, FieldMemOperand(r4, JSObject::kPropertiesOffset)); + + // Continue with JSObject being successfully allocated + // r1: constructor function + // r4: JSObject + __ jmp(&allocated); + + // Undo the setting of the new top so that the heap is verifiable. For + // example, the map's unused properties potentially do not match the + // allocated objects unused properties. + // r4: JSObject (previous new top) + __ bind(&undo_allocation); + __ UndoAllocationInNewSpace(r4, r5); + } - // Check if a non-empty properties array is needed. Continue with allocated - // object if not fall through to runtime call if it is. + // Allocate the new receiver object using the runtime call. // r1: constructor function + __ bind(&rt_call); + __ push(r1); // argument for Runtime_NewObject + __ CallRuntime(Runtime::kNewObject, 1); + __ mov(r4, r0); + + // Receiver for constructor call allocated. // r4: JSObject - // r5: start of next object (not tagged) - __ ldrb(r3, FieldMemOperand(r2, Map::kUnusedPropertyFieldsOffset)); - // The field instance sizes contains both pre-allocated property fields and - // in-object properties. - __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset)); - __ Ubfx(r6, r0, Map::kPreAllocatedPropertyFieldsByte * 8, 8); - __ add(r3, r3, Operand(r6)); - __ Ubfx(r6, r0, Map::kInObjectPropertiesByte * 8, 8); - __ sub(r3, r3, Operand(r6), SetCC); - - // Done if no extra properties are to be allocated. - __ b(eq, &allocated); - __ Assert(pl, "Property allocation count failed."); - - // Scale the number of elements by pointer size and add the header for - // FixedArrays to the start of the next object calculation from above. - // r1: constructor - // r3: number of elements in properties array - // r4: JSObject - // r5: start of next object - __ add(r0, r3, Operand(FixedArray::kHeaderSize / kPointerSize)); - __ AllocateInNewSpace( - r0, - r5, - r6, - r2, - &undo_allocation, - static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS)); - - // Initialize the FixedArray. - // r1: constructor - // r3: number of elements in properties array - // r4: JSObject - // r5: FixedArray (not tagged) - __ LoadRoot(r6, Heap::kFixedArrayMapRootIndex); - __ mov(r2, r5); - ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); - __ str(r6, MemOperand(r2, kPointerSize, PostIndex)); - ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); - __ mov(r0, Operand(r3, LSL, kSmiTagSize)); - __ str(r0, MemOperand(r2, kPointerSize, PostIndex)); - - // Initialize the fields to undefined. + __ bind(&allocated); + __ push(r4); + __ push(r4); + + // Reload the number of arguments and the constructor from the stack. + // sp[0]: receiver + // sp[1]: receiver + // sp[2]: constructor function + // sp[3]: number of arguments (smi-tagged) + __ ldr(r1, MemOperand(sp, 2 * kPointerSize)); + __ ldr(r3, MemOperand(sp, 3 * kPointerSize)); + + // Set up pointer to last argument. + __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); + + // Set up number of arguments for function call below + __ mov(r0, Operand(r3, LSR, kSmiTagSize)); + + // Copy arguments and receiver to the expression stack. + // r0: number of arguments // r1: constructor function - // r2: First element of FixedArray (not tagged) - // r3: number of elements in properties array - // r4: JSObject - // r5: FixedArray (not tagged) - __ add(r6, r2, Operand(r3, LSL, kPointerSizeLog2)); // End of object. - ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); - { Label loop, entry; - if (count_constructions) { - __ LoadRoot(r7, Heap::kUndefinedValueRootIndex); - } else if (FLAG_debug_code) { - __ LoadRoot(r8, Heap::kUndefinedValueRootIndex); - __ cmp(r7, r8); - __ Assert(eq, "Undefined value not loaded."); - } - __ b(&entry); - __ bind(&loop); - __ str(r7, MemOperand(r2, kPointerSize, PostIndex)); - __ bind(&entry); - __ cmp(r2, r6); - __ b(lt, &loop); - } + // r2: address of last argument (caller sp) + // r3: number of arguments (smi-tagged) + // sp[0]: receiver + // sp[1]: receiver + // sp[2]: constructor function + // sp[3]: number of arguments (smi-tagged) + Label loop, entry; + __ b(&entry); + __ bind(&loop); + __ ldr(ip, MemOperand(r2, r3, LSL, kPointerSizeLog2 - 1)); + __ push(ip); + __ bind(&entry); + __ sub(r3, r3, Operand(2), SetCC); + __ b(ge, &loop); - // Store the initialized FixedArray into the properties field of - // the JSObject + // Call the function. + // r0: number of arguments // r1: constructor function - // r4: JSObject - // r5: FixedArray (not tagged) - __ add(r5, r5, Operand(kHeapObjectTag)); // Add the heap tag. - __ str(r5, FieldMemOperand(r4, JSObject::kPropertiesOffset)); + if (is_api_function) { + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + Handle<Code> code = + masm->isolate()->builtins()->HandleApiCallConstruct(); + ParameterCount expected(0); + __ InvokeCode(code, expected, expected, + RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD); + } else { + ParameterCount actual(r0); + __ InvokeFunction(r1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - // Continue with JSObject being successfully allocated - // r1: constructor function - // r4: JSObject - __ jmp(&allocated); - - // Undo the setting of the new top so that the heap is verifiable. For - // example, the map's unused properties potentially do not match the - // allocated objects unused properties. - // r4: JSObject (previous new top) - __ bind(&undo_allocation); - __ UndoAllocationInNewSpace(r4, r5); + // Restore context from the frame. + // r0: result + // sp[0]: receiver + // sp[1]: constructor function + // sp[2]: number of arguments (smi-tagged) + __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, exit; + + // If the result is a smi, it is *not* an object in the ECMA sense. + // r0: result + // sp[0]: receiver (newly allocated object) + // sp[1]: constructor function + // sp[2]: number of arguments (smi-tagged) + __ JumpIfSmi(r0, &use_receiver); + + // If the type of the result (stored in its map) is less than + // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. + __ CompareObjectType(r0, r3, r3, FIRST_SPEC_OBJECT_TYPE); + __ b(ge, &exit); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ ldr(r0, MemOperand(sp)); + + // Remove receiver from the stack, remove caller arguments, and + // return. + __ bind(&exit); + // r0: result + // sp[0]: receiver (newly allocated object) + // sp[1]: constructor function + // sp[2]: number of arguments (smi-tagged) + __ ldr(r1, MemOperand(sp, 2 * kPointerSize)); + + // Leave construct frame. } - // Allocate the new receiver object using the runtime call. - // r1: constructor function - __ bind(&rt_call); - __ push(r1); // argument for Runtime_NewObject - __ CallRuntime(Runtime::kNewObject, 1); - __ mov(r4, r0); - - // Receiver for constructor call allocated. - // r4: JSObject - __ bind(&allocated); - __ push(r4); - - // Push the function and the allocated receiver from the stack. - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ ldr(r1, MemOperand(sp, kPointerSize)); - __ push(r1); // Constructor function. - __ push(r4); // Receiver. - - // Reload the number of arguments from the stack. - // r1: constructor function - // sp[0]: receiver - // sp[1]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - __ ldr(r3, MemOperand(sp, 4 * kPointerSize)); - - // Setup pointer to last argument. - __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); - - // Setup number of arguments for function call below - __ mov(r0, Operand(r3, LSR, kSmiTagSize)); - - // Copy arguments and receiver to the expression stack. - // r0: number of arguments - // r2: address of last argument (caller sp) - // r1: constructor function - // r3: number of arguments (smi-tagged) - // sp[0]: receiver - // sp[1]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - Label loop, entry; - __ b(&entry); - __ bind(&loop); - __ ldr(ip, MemOperand(r2, r3, LSL, kPointerSizeLog2 - 1)); - __ push(ip); - __ bind(&entry); - __ sub(r3, r3, Operand(2), SetCC); - __ b(ge, &loop); - - // Call the function. - // r0: number of arguments - // r1: constructor function - if (is_api_function) { - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - Handle<Code> code = - masm->isolate()->builtins()->HandleApiCallConstruct(); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, - RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD); - } else { - ParameterCount actual(r0); - __ InvokeFunction(r1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - } - - // Pop the function from the stack. - // sp[0]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - __ pop(); - - // Restore context from the frame. - // r0: result - // sp[0]: receiver - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; - - // If the result is a smi, it is *not* an object in the ECMA sense. - // r0: result - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ JumpIfSmi(r0, &use_receiver); - - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - __ CompareObjectType(r0, r3, r3, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &exit); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ ldr(r0, MemOperand(sp)); - - // Remove receiver from the stack, remove caller arguments, and - // return. - __ bind(&exit); - // r0: result - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ ldr(r1, MemOperand(sp, 2 * kPointerSize)); - __ LeaveConstructFrame(); __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); __ add(sp, sp, Operand(kPointerSize)); __ IncrementCounter(isolate->counters()->constructed_objects(), 1, r1, r2); @@ -997,60 +1018,62 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // r4: argv // r5-r7, cp may be clobbered - // Clear the context before we push it when entering the JS frame. + // Clear the context before we push it when entering the internal frame. __ mov(cp, Operand(0, RelocInfo::NONE)); // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Set up the context from the function argument. - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + // Set up the context from the function argument. + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - __ InitializeRootRegister(); + __ InitializeRootRegister(); - // Push the function and the receiver onto the stack. - __ push(r1); - __ push(r2); + // Push the function and the receiver onto the stack. + __ push(r1); + __ push(r2); - // Copy arguments to the stack in a loop. - // r1: function - // r3: argc - // r4: argv, i.e. points to first arg - Label loop, entry; - __ add(r2, r4, Operand(r3, LSL, kPointerSizeLog2)); - // r2 points past last arg. - __ b(&entry); - __ bind(&loop); - __ ldr(r0, MemOperand(r4, kPointerSize, PostIndex)); // read next parameter - __ ldr(r0, MemOperand(r0)); // dereference handle - __ push(r0); // push parameter - __ bind(&entry); - __ cmp(r4, r2); - __ b(ne, &loop); - - // Initialize all JavaScript callee-saved registers, since they will be seen - // by the garbage collector as part of handlers. - __ LoadRoot(r4, Heap::kUndefinedValueRootIndex); - __ mov(r5, Operand(r4)); - __ mov(r6, Operand(r4)); - __ mov(r7, Operand(r4)); - if (kR9Available == 1) { - __ mov(r9, Operand(r4)); - } + // Copy arguments to the stack in a loop. + // r1: function + // r3: argc + // r4: argv, i.e. points to first arg + Label loop, entry; + __ add(r2, r4, Operand(r3, LSL, kPointerSizeLog2)); + // r2 points past last arg. + __ b(&entry); + __ bind(&loop); + __ ldr(r0, MemOperand(r4, kPointerSize, PostIndex)); // read next parameter + __ ldr(r0, MemOperand(r0)); // dereference handle + __ push(r0); // push parameter + __ bind(&entry); + __ cmp(r4, r2); + __ b(ne, &loop); - // Invoke the code and pass argc as r0. - __ mov(r0, Operand(r3)); - if (is_construct) { - __ Call(masm->isolate()->builtins()->JSConstructCall()); - } else { - ParameterCount actual(r0); - __ InvokeFunction(r1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - } + // Initialize all JavaScript callee-saved registers, since they will be seen + // by the garbage collector as part of handlers. + __ LoadRoot(r4, Heap::kUndefinedValueRootIndex); + __ mov(r5, Operand(r4)); + __ mov(r6, Operand(r4)); + __ mov(r7, Operand(r4)); + if (kR9Available == 1) { + __ mov(r9, Operand(r4)); + } - // Exit the JS frame and remove the parameters (except function), and return. - // Respect ABI stack constraint. - __ LeaveInternalFrame(); + // Invoke the code and pass argc as r0. + __ mov(r0, Operand(r3)); + if (is_construct) { + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); + __ CallStub(&stub); + } else { + ParameterCount actual(r0); + __ InvokeFunction(r1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } + // Exit the JS frame and remove the parameters (except function), and + // return. + // Respect ABI stack constraint. + } __ Jump(lr); // r0: result @@ -1069,26 +1092,27 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { void Builtins::Generate_LazyCompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve the function. - __ push(r1); - // Push call kind information. - __ push(r5); + // Preserve the function. + __ push(r1); + // Push call kind information. + __ push(r5); - // Push the function on the stack as the argument to the runtime function. - __ push(r1); - __ CallRuntime(Runtime::kLazyCompile, 1); - // Calculate the entry point. - __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); + // Push the function on the stack as the argument to the runtime function. + __ push(r1); + __ CallRuntime(Runtime::kLazyCompile, 1); + // Calculate the entry point. + __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); - // Restore call kind information. - __ pop(r5); - // Restore saved function. - __ pop(r1); + // Restore call kind information. + __ pop(r5); + // Restore saved function. + __ pop(r1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ Jump(r2); @@ -1097,26 +1121,27 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) { void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve the function. - __ push(r1); - // Push call kind information. - __ push(r5); + // Preserve the function. + __ push(r1); + // Push call kind information. + __ push(r5); - // Push the function on the stack as the argument to the runtime function. - __ push(r1); - __ CallRuntime(Runtime::kLazyRecompile, 1); - // Calculate the entry point. - __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); + // Push the function on the stack as the argument to the runtime function. + __ push(r1); + __ CallRuntime(Runtime::kLazyRecompile, 1); + // Calculate the entry point. + __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); - // Restore call kind information. - __ pop(r5); - // Restore saved function. - __ pop(r1); + // Restore call kind information. + __ pop(r5); + // Restore saved function. + __ pop(r1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ Jump(r2); @@ -1125,12 +1150,13 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, Deoptimizer::BailoutType type) { - __ EnterInternalFrame(); - // Pass the function and deoptimization type to the runtime system. - __ mov(r0, Operand(Smi::FromInt(static_cast<int>(type)))); - __ push(r0); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Pass the function and deoptimization type to the runtime system. + __ mov(r0, Operand(Smi::FromInt(static_cast<int>(type)))); + __ push(r0); + __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + } // Get the full codegen state from the stack and untag it -> r6. __ ldr(r6, MemOperand(sp, 0 * kPointerSize)); @@ -1170,9 +1196,10 @@ void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { // the registers without worrying about which of them contain // pointers. This seems a bit fragile. __ stm(db_w, sp, kJSCallerSaved | kCalleeSaved | lr.bit() | fp.bit()); - __ EnterInternalFrame(); - __ CallRuntime(Runtime::kNotifyOSR, 0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kNotifyOSR, 0); + } __ ldm(ia_w, sp, kJSCallerSaved | kCalleeSaved | lr.bit() | fp.bit()); __ Ret(); } @@ -1188,10 +1215,11 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Lookup the function in the JavaScript frame and push it as an // argument to the on-stack replacement function. __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ EnterInternalFrame(); - __ push(r0); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r0); + __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + } // If the result was -1 it means that we couldn't optimize the // function. Just return and continue in the unoptimized version. @@ -1216,7 +1244,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 1. Make sure we have at least one argument. // r0: actual number of arguments { Label done; - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); __ b(ne, &done); __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); __ push(r2); @@ -1273,17 +1301,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ b(ge, &shift_arguments); __ bind(&convert_to_object); - __ EnterInternalFrame(); // In order to preserve argument count. - __ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged. - __ push(r0); - __ push(r2); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(r2, r0); + { + // Enter an internal frame in order to preserve argument count. + FrameScope scope(masm, StackFrame::INTERNAL); + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged. + __ push(r0); + + __ push(r2); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ mov(r2, r0); + + __ pop(r0); + __ mov(r0, Operand(r0, ASR, kSmiTagSize)); + + // Exit the internal frame. + } - __ pop(r0); - __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - __ LeaveInternalFrame(); // Restore the function to r1, and the flag to r4. __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); __ mov(r4, Operand(0, RelocInfo::NONE)); @@ -1403,156 +1437,157 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { const int kRecvOffset = 3 * kPointerSize; const int kFunctionOffset = 4 * kPointerSize; - __ EnterInternalFrame(); + { + FrameScope frame_scope(masm, StackFrame::INTERNAL); - __ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function - __ push(r0); - __ ldr(r0, MemOperand(fp, kArgsOffset)); // get the args array - __ push(r0); - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); - - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - Label okay; - __ LoadRoot(r2, Heap::kRealStackLimitRootIndex); - // Make r2 the space we have left. The stack might already be overflowed - // here which will cause r2 to become negative. - __ sub(r2, sp, r2); - // Check if the arguments will overflow the stack. - __ cmp(r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); - __ b(gt, &okay); // Signed comparison. - - // Out of stack space. - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ push(r1); - __ push(r0); - __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); - // End of stack check. + __ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function + __ push(r0); + __ ldr(r0, MemOperand(fp, kArgsOffset)); // get the args array + __ push(r0); + __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); + + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + Label okay; + __ LoadRoot(r2, Heap::kRealStackLimitRootIndex); + // Make r2 the space we have left. The stack might already be overflowed + // here which will cause r2 to become negative. + __ sub(r2, sp, r2); + // Check if the arguments will overflow the stack. + __ cmp(r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ b(gt, &okay); // Signed comparison. + + // Out of stack space. + __ ldr(r1, MemOperand(fp, kFunctionOffset)); + __ push(r1); + __ push(r0); + __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); + // End of stack check. - // Push current limit and index. - __ bind(&okay); - __ push(r0); // limit - __ mov(r1, Operand(0, RelocInfo::NONE)); // initial index - __ push(r1); + // Push current limit and index. + __ bind(&okay); + __ push(r0); // limit + __ mov(r1, Operand(0, RelocInfo::NONE)); // initial index + __ push(r1); - // Get the receiver. - __ ldr(r0, MemOperand(fp, kRecvOffset)); + // Get the receiver. + __ ldr(r0, MemOperand(fp, kRecvOffset)); - // Check that the function is a JS function (otherwise it must be a proxy). - Label push_receiver; - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &push_receiver); - - // Change context eagerly to get the right global object if necessary. - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - // Load the shared function info while the function is still in r1. - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - - // Compute the receiver. - // Do not transform the receiver for strict mode functions. - Label call_to_object, use_global_receiver; - __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset)); - __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ b(ne, &push_receiver); - - // Do not transform the receiver for strict mode functions. - __ tst(r2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ b(ne, &push_receiver); - - // Compute the receiver in non-strict mode. - __ JumpIfSmi(r0, &call_to_object); - __ LoadRoot(r1, Heap::kNullValueRootIndex); - __ cmp(r0, r1); - __ b(eq, &use_global_receiver); - __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); - __ cmp(r0, r1); - __ b(eq, &use_global_receiver); - - // Check if the receiver is already a JavaScript object. - // r0: receiver - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &push_receiver); - - // Convert the receiver to a regular object. - // r0: receiver - __ bind(&call_to_object); - __ push(r0); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ b(&push_receiver); - - // Use the current global receiver object as the receiver. - __ bind(&use_global_receiver); - const int kGlobalOffset = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; - __ ldr(r0, FieldMemOperand(cp, kGlobalOffset)); - __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalContextOffset)); - __ ldr(r0, FieldMemOperand(r0, kGlobalOffset)); - __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset)); - - // Push the receiver. - // r0: receiver - __ bind(&push_receiver); - __ push(r0); + // Check that the function is a JS function (otherwise it must be a proxy). + Label push_receiver; + __ ldr(r1, MemOperand(fp, kFunctionOffset)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); + __ b(ne, &push_receiver); - // Copy all arguments from the array to the stack. - Label entry, loop; - __ ldr(r0, MemOperand(fp, kIndexOffset)); - __ b(&entry); + // Change context eagerly to get the right global object if necessary. + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + // Load the shared function info while the function is still in r1. + __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - // Load the current argument from the arguments array and push it to the - // stack. - // r0: current argument index - __ bind(&loop); - __ ldr(r1, MemOperand(fp, kArgsOffset)); - __ push(r1); - __ push(r0); + // Compute the receiver. + // Do not transform the receiver for strict mode functions. + Label call_to_object, use_global_receiver; + __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset)); + __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + + kSmiTagSize))); + __ b(ne, &push_receiver); - // Call the runtime to access the property in the arguments array. - __ CallRuntime(Runtime::kGetProperty, 2); - __ push(r0); + // Do not transform the receiver for strict mode functions. + __ tst(r2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); + __ b(ne, &push_receiver); - // Use inline caching to access the arguments. - __ ldr(r0, MemOperand(fp, kIndexOffset)); - __ add(r0, r0, Operand(1 << kSmiTagSize)); - __ str(r0, MemOperand(fp, kIndexOffset)); + // Compute the receiver in non-strict mode. + __ JumpIfSmi(r0, &call_to_object); + __ LoadRoot(r1, Heap::kNullValueRootIndex); + __ cmp(r0, r1); + __ b(eq, &use_global_receiver); + __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); + __ cmp(r0, r1); + __ b(eq, &use_global_receiver); - // Test if the copy loop has finished copying all the elements from the - // arguments object. - __ bind(&entry); - __ ldr(r1, MemOperand(fp, kLimitOffset)); - __ cmp(r0, r1); - __ b(ne, &loop); - - // Invoke the function. - Label call_proxy; - ParameterCount actual(r0); - __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &call_proxy); - __ InvokeFunction(r1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Check if the receiver is already a JavaScript object. + // r0: receiver + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE); + __ b(ge, &push_receiver); - // Tear down the internal frame and remove function, receiver and args. - __ LeaveInternalFrame(); - __ add(sp, sp, Operand(3 * kPointerSize)); - __ Jump(lr); + // Convert the receiver to a regular object. + // r0: receiver + __ bind(&call_to_object); + __ push(r0); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ b(&push_receiver); - // Invoke the function proxy. - __ bind(&call_proxy); - __ push(r1); // add function proxy as last argument - __ add(r0, r0, Operand(1)); - __ mov(r2, Operand(0, RelocInfo::NONE)); - __ SetCallKind(r5, CALL_AS_METHOD); - __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY); - __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); + // Use the current global receiver object as the receiver. + __ bind(&use_global_receiver); + const int kGlobalOffset = + Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + __ ldr(r0, FieldMemOperand(cp, kGlobalOffset)); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalContextOffset)); + __ ldr(r0, FieldMemOperand(r0, kGlobalOffset)); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset)); + + // Push the receiver. + // r0: receiver + __ bind(&push_receiver); + __ push(r0); + + // Copy all arguments from the array to the stack. + Label entry, loop; + __ ldr(r0, MemOperand(fp, kIndexOffset)); + __ b(&entry); + + // Load the current argument from the arguments array and push it to the + // stack. + // r0: current argument index + __ bind(&loop); + __ ldr(r1, MemOperand(fp, kArgsOffset)); + __ push(r1); + __ push(r0); + + // Call the runtime to access the property in the arguments array. + __ CallRuntime(Runtime::kGetProperty, 2); + __ push(r0); + + // Use inline caching to access the arguments. + __ ldr(r0, MemOperand(fp, kIndexOffset)); + __ add(r0, r0, Operand(1 << kSmiTagSize)); + __ str(r0, MemOperand(fp, kIndexOffset)); + + // Test if the copy loop has finished copying all the elements from the + // arguments object. + __ bind(&entry); + __ ldr(r1, MemOperand(fp, kLimitOffset)); + __ cmp(r0, r1); + __ b(ne, &loop); + + // Invoke the function. + Label call_proxy; + ParameterCount actual(r0); + __ mov(r0, Operand(r0, ASR, kSmiTagSize)); + __ ldr(r1, MemOperand(fp, kFunctionOffset)); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); + __ b(ne, &call_proxy); + __ InvokeFunction(r1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + + frame_scope.GenerateLeaveFrame(); + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Jump(lr); - __ LeaveInternalFrame(); + // Invoke the function proxy. + __ bind(&call_proxy); + __ push(r1); // add function proxy as last argument + __ add(r0, r0, Operand(1)); + __ mov(r2, Operand(0, RelocInfo::NONE)); + __ SetCallKind(r5, CALL_AS_METHOD); + __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY); + __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); + + // Tear down the internal frame and remove function, receiver and args. + } __ add(sp, sp, Operand(3 * kPointerSize)); __ Jump(lr); } @@ -1672,6 +1707,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&invoke); __ Call(r3); + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); // Exit frame and return. LeaveArgumentsAdaptorFrame(masm); __ Jump(lr); diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index 36450c945f..c65f5bdf84 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -98,9 +98,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { &gc, TAG_OBJECT); - int map_index = strict_mode_ == kStrictMode - ? Context::STRICT_MODE_FUNCTION_MAP_INDEX - : Context::FUNCTION_MAP_INDEX; + int map_index = (language_mode_ == CLASSIC_MODE) + ? Context::FUNCTION_MAP_INDEX + : Context::STRICT_MODE_FUNCTION_MAP_INDEX; // Compute the function map in the current global context and set that // as the map of the allocated object. @@ -122,7 +122,6 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset)); __ str(r4, FieldMemOperand(r0, JSFunction::kNextFunctionLinkOffset)); - // Initialize the code pointer in the function to be the one // found in the shared function info object. __ ldr(r3, FieldMemOperand(r3, SharedFunctionInfo::kCodeOffset)); @@ -156,21 +155,19 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { // Load the function from the stack. __ ldr(r3, MemOperand(sp, 0)); - // Setup the object header. - __ LoadRoot(r2, Heap::kFunctionContextMapRootIndex); - __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + // Set up the object header. + __ LoadRoot(r1, Heap::kFunctionContextMapRootIndex); __ mov(r2, Operand(Smi::FromInt(length))); __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset)); + __ str(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - // Setup the fixed slots. + // Set up the fixed slots, copy the global object from the previous context. + __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); __ mov(r1, Operand(Smi::FromInt(0))); __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX))); __ str(cp, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX))); __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX))); - - // Copy the global object from the previous context. - __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ str(r2, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX))); // Initialize the rest of the slots to undefined. __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); @@ -189,6 +186,119 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastNewBlockContextStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [sp]: function. + // [sp + kPointerSize]: serialized scope info + + // Try to allocate the context in new space. + Label gc; + int length = slots_ + Context::MIN_CONTEXT_SLOTS; + __ AllocateInNewSpace(FixedArray::SizeFor(length), + r0, r1, r2, &gc, TAG_OBJECT); + + // Load the function from the stack. + __ ldr(r3, MemOperand(sp, 0)); + + // Load the serialized scope info from the stack. + __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); + + // Set up the object header. + __ LoadRoot(r2, Heap::kBlockContextMapRootIndex); + __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + __ mov(r2, Operand(Smi::FromInt(length))); + __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset)); + + // If this block context is nested in the global context we get a smi + // sentinel instead of a function. The block context should get the + // canonical empty function of the global context as its closure which + // we still have to look up. + Label after_sentinel; + __ JumpIfNotSmi(r3, &after_sentinel); + if (FLAG_debug_code) { + const char* message = "Expected 0 as a Smi sentinel"; + __ cmp(r3, Operand::Zero()); + __ Assert(eq, message); + } + __ ldr(r3, GlobalObjectOperand()); + __ ldr(r3, FieldMemOperand(r3, GlobalObject::kGlobalContextOffset)); + __ ldr(r3, ContextOperand(r3, Context::CLOSURE_INDEX)); + __ bind(&after_sentinel); + + // Set up the fixed slots, copy the global object from the previous context. + __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ str(r3, ContextOperand(r0, Context::CLOSURE_INDEX)); + __ str(cp, ContextOperand(r0, Context::PREVIOUS_INDEX)); + __ str(r1, ContextOperand(r0, Context::EXTENSION_INDEX)); + __ str(r2, ContextOperand(r0, Context::GLOBAL_INDEX)); + + // Initialize the rest of the slots to the hole value. + __ LoadRoot(r1, Heap::kTheHoleValueRootIndex); + for (int i = 0; i < slots_; i++) { + __ str(r1, ContextOperand(r0, i + Context::MIN_CONTEXT_SLOTS)); + } + + // Remove the on-stack argument and return. + __ mov(cp, r0); + __ add(sp, sp, Operand(2 * kPointerSize)); + __ Ret(); + + // Need to collect. Call into runtime system. + __ bind(&gc); + __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1); +} + + +static void GenerateFastCloneShallowArrayCommon( + MacroAssembler* masm, + int length, + FastCloneShallowArrayStub::Mode mode, + Label* fail) { + // Registers on entry: + // + // r3: boilerplate literal array. + ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS); + + // All sizes here are multiples of kPointerSize. + int elements_size = 0; + if (length > 0) { + elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + ? FixedDoubleArray::SizeFor(length) + : FixedArray::SizeFor(length); + } + int size = JSArray::kSize + elements_size; + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size, + r0, + r1, + r2, + fail, + TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length == 0)) { + __ ldr(r1, FieldMemOperand(r3, i)); + __ str(r1, FieldMemOperand(r0, i)); + } + } + + if (length > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset)); + __ add(r2, r0, Operand(JSArray::kSize)); + __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset)); + + // Copy the elements array. + ASSERT((elements_size % kPointerSize) == 0); + __ CopyFields(r2, r3, r1.bit(), elements_size / kPointerSize); + } +} + void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // Stack layout on entry: // @@ -196,10 +306,6 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // [sp + kPointerSize]: literal index. // [sp + (2 * kPointerSize)]: literals array. - // All sizes here are multiples of kPointerSize. - int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; - int size = JSArray::kSize + elements_size; - // Load boilerplate object into r3 and check if we need to create a // boilerplate. Label slow_case; @@ -207,64 +313,109 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize)); - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ cmp(r3, ip); + __ CompareRoot(r3, Heap::kUndefinedValueRootIndex); __ b(eq, &slow_case); + FastCloneShallowArrayStub::Mode mode = mode_; + if (mode == CLONE_ANY_ELEMENTS) { + Label double_elements, check_fast_elements; + __ ldr(r0, FieldMemOperand(r3, JSArray::kElementsOffset)); + __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); + __ CompareRoot(r0, Heap::kFixedCOWArrayMapRootIndex); + __ b(ne, &check_fast_elements); + GenerateFastCloneShallowArrayCommon(masm, 0, + COPY_ON_WRITE_ELEMENTS, &slow_case); + // Return and remove the on-stack parameters. + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); + + __ bind(&check_fast_elements); + __ CompareRoot(r0, Heap::kFixedArrayMapRootIndex); + __ b(ne, &double_elements); + GenerateFastCloneShallowArrayCommon(masm, length_, + CLONE_ELEMENTS, &slow_case); + // Return and remove the on-stack parameters. + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); + + __ bind(&double_elements); + mode = CLONE_DOUBLE_ELEMENTS; + // Fall through to generate the code to handle double elements. + } + if (FLAG_debug_code) { const char* message; Heap::RootListIndex expected_map_index; - if (mode_ == CLONE_ELEMENTS) { + if (mode == CLONE_ELEMENTS) { message = "Expected (writable) fixed array"; expected_map_index = Heap::kFixedArrayMapRootIndex; + } else if (mode == CLONE_DOUBLE_ELEMENTS) { + message = "Expected (writable) fixed double array"; + expected_map_index = Heap::kFixedDoubleArrayMapRootIndex; } else { - ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS); + ASSERT(mode == COPY_ON_WRITE_ELEMENTS); message = "Expected copy-on-write fixed array"; expected_map_index = Heap::kFixedCOWArrayMapRootIndex; } __ push(r3); __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset)); __ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ LoadRoot(ip, expected_map_index); - __ cmp(r3, ip); + __ CompareRoot(r3, expected_map_index); __ Assert(eq, message); __ pop(r3); } - // Allocate both the JS array and the elements array in one big - // allocation. This avoids multiple limit checks. - __ AllocateInNewSpace(size, - r0, - r1, - r2, - &slow_case, - TAG_OBJECT); + GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case); - // Copy the JS array part. - for (int i = 0; i < JSArray::kSize; i += kPointerSize) { - if ((i != JSArray::kElementsOffset) || (length_ == 0)) { - __ ldr(r1, FieldMemOperand(r3, i)); - __ str(r1, FieldMemOperand(r0, i)); - } - } + // Return and remove the on-stack parameters. + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); - if (length_ > 0) { - // Get hold of the elements array of the boilerplate and setup the - // elements pointer in the resulting object. - __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset)); - __ add(r2, r0, Operand(JSArray::kSize)); - __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset)); + __ bind(&slow_case); + __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); +} - // Copy the elements array. - __ CopyFields(r2, r3, r1.bit(), elements_size / kPointerSize); + +void FastCloneShallowObjectStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [sp]: object literal flags. + // [sp + kPointerSize]: constant properties. + // [sp + (2 * kPointerSize)]: literal index. + // [sp + (3 * kPointerSize)]: literals array. + + // Load boilerplate object into r3 and check if we need to create a + // boilerplate. + Label slow_case; + __ ldr(r3, MemOperand(sp, 3 * kPointerSize)); + __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); + __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ CompareRoot(r3, Heap::kUndefinedValueRootIndex); + __ b(eq, &slow_case); + + // Check that the boilerplate contains only fast properties and we can + // statically determine the instance size. + int size = JSObject::kHeaderSize + length_ * kPointerSize; + __ ldr(r0, FieldMemOperand(r3, HeapObject::kMapOffset)); + __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceSizeOffset)); + __ cmp(r0, Operand(size >> kPointerSizeLog2)); + __ b(ne, &slow_case); + + // Allocate the JS object and copy header together with all in-object + // properties from the boilerplate. + __ AllocateInNewSpace(size, r0, r1, r2, &slow_case, TAG_OBJECT); + for (int i = 0; i < size; i += kPointerSize) { + __ ldr(r1, FieldMemOperand(r3, i)); + __ str(r1, FieldMemOperand(r0, i)); } // Return and remove the on-stack parameters. - __ add(sp, sp, Operand(3 * kPointerSize)); + __ add(sp, sp, Operand(4 * kPointerSize)); __ Ret(); __ bind(&slow_case); - __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); + __ TailCallRuntime(Runtime::kCreateObjectLiteralShallow, 4, 1); } @@ -432,7 +583,9 @@ void FloatingPointHelper::LoadNumber(MacroAssembler* masm, Label is_smi, done; - __ JumpIfSmi(object, &is_smi); + // Smi-check + __ UntagAndJumpIfSmi(scratch1, object, &is_smi); + // Heap number check __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number); // Handle loading a double from a heap number. @@ -454,7 +607,6 @@ void FloatingPointHelper::LoadNumber(MacroAssembler* masm, if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); // Convert smi to double using VFP instructions. - __ SmiUntag(scratch1, object); __ vmov(dst.high(), scratch1); __ vcvt_f64_s32(dst, dst.high()); if (destination == kCoreRegisters) { @@ -489,11 +641,10 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm, Heap::kHeapNumberMapRootIndex, "HeapNumberMap register clobbered."); } - Label is_smi; Label done; Label not_in_int32_range; - __ JumpIfSmi(object, &is_smi); + __ UntagAndJumpIfSmi(dst, object, &done); __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset)); __ cmp(scratch1, heap_number_map); __ b(ne, not_number); @@ -513,10 +664,6 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm, scratch1, scratch2, scratch3); - __ jmp(&done); - - __ bind(&is_smi); - __ SmiUntag(dst, object); __ bind(&done); } @@ -559,7 +706,7 @@ void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm, // Get the absolute value of the object (as an unsigned integer). __ rsb(int_scratch, int_scratch, Operand::Zero(), SetCC, mi); - // Get mantisssa[51:20]. + // Get mantissa[51:20]. // Get the position of the first set bit. __ CountLeadingZeros(dst1, int_scratch, scratch2); @@ -689,10 +836,7 @@ void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm, Label done; - // Untag the object into the destination register. - __ SmiUntag(dst, object); - // Just return if the object is a smi. - __ JumpIfSmi(object, &done); + __ UntagAndJumpIfSmi(dst, object, &done); if (FLAG_debug_code) { __ AbortIfNotRootValue(heap_number_map, @@ -793,7 +937,7 @@ void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm, // non zero bits left. So we need the (30 - exponent) last bits of the // 31 higher bits of the mantissa to be null. // Because bits [21:0] are null, we can check instead that the - // (32 - exponent) last bits of the 32 higher bits of the mantisssa are null. + // (32 - exponent) last bits of the 32 higher bits of the mantissa are null. // Get the 32 higher bits of the mantissa in dst. __ Ubfx(dst, @@ -838,9 +982,11 @@ void FloatingPointHelper::CallCCodeForDoubleOperation( __ vmov(d0, r0, r1); __ vmov(d1, r2, r3); } - // Call C routine that may not cause GC or other trouble. - __ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()), - 0, 2); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction( + ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2); + } // Store answer in the overwritable heap number. Double returned in // registers r0 and r1 or in d0. if (masm->use_eabi_hardfloat()) { @@ -857,6 +1003,29 @@ void FloatingPointHelper::CallCCodeForDoubleOperation( } +bool WriteInt32ToHeapNumberStub::IsPregenerated() { + // These variants are compiled ahead of time. See next method. + if (the_int_.is(r1) && the_heap_number_.is(r0) && scratch_.is(r2)) { + return true; + } + if (the_int_.is(r2) && the_heap_number_.is(r0) && scratch_.is(r3)) { + return true; + } + // Other register combinations are generated as and when they are needed, + // so it is unsafe to call them from stubs (we can't generate a stub while + // we are generating a stub). + return false; +} + + +void WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime() { + WriteInt32ToHeapNumberStub stub1(r1, r0, r2); + WriteInt32ToHeapNumberStub stub2(r2, r0, r3); + stub1.GetCode()->set_is_pregenerated(true); + stub2.GetCode()->set_is_pregenerated(true); +} + + // See comment for class. void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) { Label max_negative_int; @@ -1197,6 +1366,8 @@ static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, __ vmov(d0, r0, r1); __ vmov(d1, r2, r3); } + + AllowExternalCallThatCantCauseGC scope(masm); __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), 0, 2); __ pop(pc); // Return. @@ -1214,7 +1385,7 @@ static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm, // If either operand is a JS object or an oddball value, then they are // not equal since their pointers are different. // There is no test for undetectability in strict equality. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); + STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); Label first_non_object; // Get the type of the first operand into r2 and compare it with // FIRST_SPEC_OBJECT_TYPE. @@ -1606,6 +1777,8 @@ void CompareStub::Generate(MacroAssembler* masm) { // The stub expects its argument in the tos_ register and returns its result in // it, too: zero for false, and a non-zero value for true. void ToBooleanStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. // This stub uses VFP3 instructions. CpuFeatures::Scope scope(VFP3); @@ -1713,6 +1886,41 @@ void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) { } +void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + __ stm(db_w, sp, kCallerSaved | lr.bit()); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(VFP3); + __ sub(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters)); + for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) { + DwVfpRegister reg = DwVfpRegister::from_code(i); + __ vstr(reg, MemOperand(sp, i * kDoubleSize)); + } + } + const int argument_count = 1; + const int fp_argument_count = 0; + const Register scratch = r1; + + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(argument_count, fp_argument_count, scratch); + __ mov(r0, Operand(ExternalReference::isolate_address())); + __ CallCFunction( + ExternalReference::store_buffer_overflow_function(masm->isolate()), + argument_count); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(VFP3); + for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) { + DwVfpRegister reg = DwVfpRegister::from_code(i); + __ vldr(reg, MemOperand(sp, i * kDoubleSize)); + } + __ add(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters)); + } + __ ldm(ia_w, sp, kCallerSaved | pc.bit()); // Also pop pc to get Ret(0). +} + + void UnaryOpStub::PrintName(StringStream* stream) { const char* op_name = Token::Name(op_); const char* overwrite_name = NULL; // Make g++ happy. @@ -1866,12 +2074,13 @@ void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm, __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(r0); - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ mov(r1, Operand(r0)); - __ pop(r0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r0); + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(r1, Operand(r0)); + __ pop(r0); + } __ bind(&heapnumber_allocated); __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); @@ -1912,13 +2121,14 @@ void UnaryOpStub::GenerateHeapNumberCodeBitNot( __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(r0); // Push the heap number, not the untagged int32. - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ mov(r2, r0); // Move the new heap number into r2. - // Get the heap number into r0, now that the new heap number is in r2. - __ pop(r0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r0); // Push the heap number, not the untagged int32. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(r2, r0); // Move the new heap number into r2. + // Get the heap number into r0, now that the new heap number is in r2. + __ pop(r0); + } // Convert the heap number in r0 to an untagged integer in r1. // This can't go slow-case because it's the same number we already @@ -2028,6 +2238,10 @@ void BinaryOpStub::GenerateTypeTransitionWithSavedArgs( void BinaryOpStub::Generate(MacroAssembler* masm) { + // Explicitly allow generation of nested stubs. It is safe here because + // generation code does not use any raw pointers. + AllowStubCallsScope allow_stub_calls(masm, true); + switch (operands_type_) { case BinaryOpIC::UNINITIALIZED: GenerateTypeTransition(masm); @@ -2110,7 +2324,7 @@ void BinaryOpStub::GenerateSmiSmiOperation(MacroAssembler* masm) { __ cmp(ip, Operand(scratch2)); __ b(ne, ¬_smi_result); // Go slow on zero result to handle -0. - __ tst(scratch1, Operand(scratch1)); + __ cmp(scratch1, Operand(0)); __ mov(right, Operand(scratch1), LeaveCC, ne); __ Ret(ne); // We need -0 if we were multiplying a negative number with 0 to get 0. @@ -3082,10 +3296,12 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Check if cache matches: Double value is stored in uint32_t[2] array. __ ldm(ia, cache_entry, r4.bit() | r5.bit() | r6.bit()); __ cmp(r2, r4); - __ b(ne, &calculate); - __ cmp(r3, r5); + __ cmp(r3, r5, eq); __ b(ne, &calculate); // Cache hit. Load result, cleanup and return. + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter( + counters->transcendental_cache_hit(), 1, scratch0, scratch1); if (tagged) { // Pop input value from stack and load result into r0. __ pop(); @@ -3098,6 +3314,9 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { } // if (CpuFeatures::IsSupported(VFP3)) __ bind(&calculate); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter( + counters->transcendental_cache_miss(), 1, scratch0, scratch1); if (tagged) { __ bind(&invalid_cache); ExternalReference runtime_function = @@ -3133,10 +3352,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex); __ AllocateHeapNumber(r0, scratch0, scratch1, r5, &skip_cache); __ vstr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset)); - __ EnterInternalFrame(); - __ push(r0); - __ CallRuntime(RuntimeFunction(), 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r0); + __ CallRuntime(RuntimeFunction(), 1); + } __ vldr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset)); __ Ret(); @@ -3149,14 +3369,15 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // We return the value in d2 without adding it to the cache, but // we cause a scavenging GC so that future allocations will succeed. - __ EnterInternalFrame(); - - // Allocate an aligned object larger than a HeapNumber. - ASSERT(4 * kPointerSize >= HeapNumber::kSize); - __ mov(scratch0, Operand(4 * kPointerSize)); - __ push(scratch0); - __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Allocate an aligned object larger than a HeapNumber. + ASSERT(4 * kPointerSize >= HeapNumber::kSize); + __ mov(scratch0, Operand(4 * kPointerSize)); + __ push(scratch0); + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); + } __ Ret(); } } @@ -3173,6 +3394,7 @@ void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm, } else { __ vmov(r0, r1, d2); } + AllowExternalCallThatCantCauseGC scope(masm); switch (type_) { case TranscendentalCache::SIN: __ CallCFunction(ExternalReference::math_sin_double_function(isolate), @@ -3182,6 +3404,10 @@ void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm, __ CallCFunction(ExternalReference::math_cos_double_function(isolate), 0, 1); break; + case TranscendentalCache::TAN: + __ CallCFunction(ExternalReference::math_tan_double_function(isolate), + 0, 1); + break; case TranscendentalCache::LOG: __ CallCFunction(ExternalReference::math_log_double_function(isolate), 0, 1); @@ -3199,6 +3425,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { // Add more cases when necessary. case TranscendentalCache::SIN: return Runtime::kMath_sin; case TranscendentalCache::COS: return Runtime::kMath_cos; + case TranscendentalCache::TAN: return Runtime::kMath_tan; case TranscendentalCache::LOG: return Runtime::kMath_log; default: UNIMPLEMENTED(); @@ -3213,104 +3440,201 @@ void StackCheckStub::Generate(MacroAssembler* masm) { void MathPowStub::Generate(MacroAssembler* masm) { - Label call_runtime; - - if (CpuFeatures::IsSupported(VFP3)) { - CpuFeatures::Scope scope(VFP3); - - Label base_not_smi; - Label exponent_not_smi; - Label convert_exponent; - - const Register base = r0; - const Register exponent = r1; - const Register heapnumbermap = r5; - const Register heapnumber = r6; - const DoubleRegister double_base = d0; - const DoubleRegister double_exponent = d1; - const DoubleRegister double_result = d2; - const SwVfpRegister single_scratch = s0; - const Register scratch = r9; - const Register scratch2 = r7; - - __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex); + CpuFeatures::Scope vfp3_scope(VFP3); + const Register base = r1; + const Register exponent = r2; + const Register heapnumbermap = r5; + const Register heapnumber = r0; + const DoubleRegister double_base = d1; + const DoubleRegister double_exponent = d2; + const DoubleRegister double_result = d3; + const DoubleRegister double_scratch = d0; + const SwVfpRegister single_scratch = s0; + const Register scratch = r9; + const Register scratch2 = r7; + + Label call_runtime, done, int_exponent; + if (exponent_type_ == ON_STACK) { + Label base_is_smi, unpack_exponent; + // The exponent and base are supplied as arguments on the stack. + // This can only happen if the stub is called from non-optimized code. + // Load input parameters from stack to double registers. __ ldr(base, MemOperand(sp, 1 * kPointerSize)); __ ldr(exponent, MemOperand(sp, 0 * kPointerSize)); - // Convert base to double value and store it in d0. - __ JumpIfNotSmi(base, &base_not_smi); - // Base is a Smi. Untag and convert it. - __ SmiUntag(base); - __ vmov(single_scratch, base); - __ vcvt_f64_s32(double_base, single_scratch); - __ b(&convert_exponent); + __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex); - __ bind(&base_not_smi); + __ UntagAndJumpIfSmi(scratch, base, &base_is_smi); __ ldr(scratch, FieldMemOperand(base, JSObject::kMapOffset)); __ cmp(scratch, heapnumbermap); __ b(ne, &call_runtime); - // Base is a heapnumber. Load it into double register. + __ vldr(double_base, FieldMemOperand(base, HeapNumber::kValueOffset)); + __ jmp(&unpack_exponent); - __ bind(&convert_exponent); - __ JumpIfNotSmi(exponent, &exponent_not_smi); - __ SmiUntag(exponent); - - // The base is in a double register and the exponent is - // an untagged smi. Allocate a heap number and call a - // C function for integer exponents. The register containing - // the heap number is callee-saved. - __ AllocateHeapNumber(heapnumber, - scratch, - scratch2, - heapnumbermap, - &call_runtime); - __ push(lr); - __ PrepareCallCFunction(1, 1, scratch); - __ SetCallCDoubleArguments(double_base, exponent); - __ CallCFunction( - ExternalReference::power_double_int_function(masm->isolate()), - 1, 1); - __ pop(lr); - __ GetCFunctionDoubleResult(double_result); - __ vstr(double_result, - FieldMemOperand(heapnumber, HeapNumber::kValueOffset)); - __ mov(r0, heapnumber); - __ Ret(2 * kPointerSize); + __ bind(&base_is_smi); + __ vmov(single_scratch, scratch); + __ vcvt_f64_s32(double_base, single_scratch); + __ bind(&unpack_exponent); + + __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent); - __ bind(&exponent_not_smi); __ ldr(scratch, FieldMemOperand(exponent, JSObject::kMapOffset)); __ cmp(scratch, heapnumbermap); __ b(ne, &call_runtime); - // Exponent is a heapnumber. Load it into double register. __ vldr(double_exponent, FieldMemOperand(exponent, HeapNumber::kValueOffset)); + } else if (exponent_type_ == TAGGED) { + // Base is already in double_base. + __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent); + + __ vldr(double_exponent, + FieldMemOperand(exponent, HeapNumber::kValueOffset)); + } + + if (exponent_type_ != INTEGER) { + Label int_exponent_convert; + // Detect integer exponents stored as double. + __ vcvt_u32_f64(single_scratch, double_exponent); + // We do not check for NaN or Infinity here because comparing numbers on + // ARM correctly distinguishes NaNs. We end up calling the built-in. + __ vcvt_f64_u32(double_scratch, single_scratch); + __ VFPCompareAndSetFlags(double_scratch, double_exponent); + __ b(eq, &int_exponent_convert); + + if (exponent_type_ == ON_STACK) { + // Detect square root case. Crankshaft detects constant +/-0.5 at + // compile time and uses DoMathPowHalf instead. We then skip this check + // for non-constant cases of +/-0.5 as these hardly occur. + Label not_plus_half; + + // Test for 0.5. + __ vmov(double_scratch, 0.5); + __ VFPCompareAndSetFlags(double_exponent, double_scratch); + __ b(ne, ¬_plus_half); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13). + __ vmov(double_scratch, -V8_INFINITY); + __ VFPCompareAndSetFlags(double_base, double_scratch); + __ vneg(double_result, double_scratch, eq); + __ b(eq, &done); + + // Add +0 to convert -0 to +0. + __ vadd(double_scratch, double_base, kDoubleRegZero); + __ vsqrt(double_result, double_scratch); + __ jmp(&done); + + __ bind(¬_plus_half); + __ vmov(double_scratch, -0.5); + __ VFPCompareAndSetFlags(double_exponent, double_scratch); + __ b(ne, &call_runtime); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13). + __ vmov(double_scratch, -V8_INFINITY); + __ VFPCompareAndSetFlags(double_base, double_scratch); + __ vmov(double_result, kDoubleRegZero, eq); + __ b(eq, &done); + + // Add +0 to convert -0 to +0. + __ vadd(double_scratch, double_base, kDoubleRegZero); + __ vmov(double_result, 1); + __ vsqrt(double_scratch, double_scratch); + __ vdiv(double_result, double_result, double_scratch); + __ jmp(&done); + } - // The base and the exponent are in double registers. - // Allocate a heap number and call a C function for - // double exponents. The register containing - // the heap number is callee-saved. - __ AllocateHeapNumber(heapnumber, - scratch, - scratch2, - heapnumbermap, - &call_runtime); __ push(lr); - __ PrepareCallCFunction(0, 2, scratch); - __ SetCallCDoubleArguments(double_base, double_exponent); - __ CallCFunction( - ExternalReference::power_double_double_function(masm->isolate()), - 0, 2); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(0, 2, scratch); + __ SetCallCDoubleArguments(double_base, double_exponent); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), + 0, 2); + } __ pop(lr); __ GetCFunctionDoubleResult(double_result); + __ jmp(&done); + + __ bind(&int_exponent_convert); + __ vcvt_u32_f64(single_scratch, double_exponent); + __ vmov(scratch, single_scratch); + } + + // Calculate power with integer exponent. + __ bind(&int_exponent); + + // Get two copies of exponent in the registers scratch and exponent. + if (exponent_type_ == INTEGER) { + __ mov(scratch, exponent); + } else { + // Exponent has previously been stored into scratch as untagged integer. + __ mov(exponent, scratch); + } + __ vmov(double_scratch, double_base); // Back up base. + __ vmov(double_result, 1.0); + + // Get absolute value of exponent. + __ cmp(scratch, Operand(0)); + __ mov(scratch2, Operand(0), LeaveCC, mi); + __ sub(scratch, scratch2, scratch, LeaveCC, mi); + + Label while_true; + __ bind(&while_true); + __ mov(scratch, Operand(scratch, ASR, 1), SetCC); + __ vmul(double_result, double_result, double_scratch, cs); + __ vmul(double_scratch, double_scratch, double_scratch, ne); + __ b(ne, &while_true); + + __ cmp(exponent, Operand(0)); + __ b(ge, &done); + __ vmov(double_scratch, 1.0); + __ vdiv(double_result, double_scratch, double_result); + // Test whether result is zero. Bail out to check for subnormal result. + // Due to subnormals, x^-y == (1/x)^y does not hold in all cases. + __ VFPCompareAndSetFlags(double_result, 0.0); + __ b(ne, &done); + // double_exponent may not containe the exponent value if the input was a + // smi. We set it with exponent value before bailing out. + __ vmov(single_scratch, exponent); + __ vcvt_f64_s32(double_exponent, single_scratch); + + // Returning or bailing out. + Counters* counters = masm->isolate()->counters(); + if (exponent_type_ == ON_STACK) { + // The arguments are still on the stack. + __ bind(&call_runtime); + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + + // The stub is called from non-optimized code, which expects the result + // as heap number in exponent. + __ bind(&done); + __ AllocateHeapNumber( + heapnumber, scratch, scratch2, heapnumbermap, &call_runtime); __ vstr(double_result, FieldMemOperand(heapnumber, HeapNumber::kValueOffset)); - __ mov(r0, heapnumber); - __ Ret(2 * kPointerSize); - } + ASSERT(heapnumber.is(r0)); + __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2); + __ Ret(2); + } else { + __ push(lr); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(0, 2, scratch); + __ SetCallCDoubleArguments(double_base, double_exponent); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), + 0, 2); + } + __ pop(lr); + __ GetCFunctionDoubleResult(double_result); - __ bind(&call_runtime); - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + __ bind(&done); + __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2); + __ Ret(); + } } @@ -3319,6 +3643,37 @@ bool CEntryStub::NeedsImmovableCode() { } +bool CEntryStub::IsPregenerated() { + return (!save_doubles_ || ISOLATE->fp_stubs_generated()) && + result_size_ == 1; +} + + +void CodeStub::GenerateStubsAheadOfTime() { + CEntryStub::GenerateAheadOfTime(); + WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(); + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(); + RecordWriteStub::GenerateFixedRegStubsAheadOfTime(); +} + + +void CodeStub::GenerateFPStubs() { + CEntryStub save_doubles(1, kSaveFPRegs); + Handle<Code> code = save_doubles.GetCode(); + code->set_is_pregenerated(true); + StoreBufferOverflowStub stub(kSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + code->GetIsolate()->set_fp_stubs_generated(true); +} + + +void CEntryStub::GenerateAheadOfTime() { + CEntryStub stub(1, kDontSaveFPRegs); + Handle<Code> code = stub.GetCode(); + code->set_is_pregenerated(true); +} + + void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { __ Throw(r0); } @@ -3430,8 +3785,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ b(eq, throw_out_of_memory_exception); // Retrieve the pending exception and clear the variable. - __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate))); - __ ldr(r3, MemOperand(ip)); + __ mov(r3, Operand(isolate->factory()->the_hole_value())); __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ ldr(r0, MemOperand(ip)); @@ -3469,9 +3823,10 @@ void CEntryStub::Generate(MacroAssembler* masm) { __ sub(r6, r6, Operand(kPointerSize)); // Enter the exit frame that transitions from JavaScript to C++. + FrameScope scope(masm, StackFrame::MANUAL); __ EnterExitFrame(save_doubles_); - // Setup argc and the builtin function in callee-saved registers. + // Set up argc and the builtin function in callee-saved registers. __ mov(r4, Operand(r0)); __ mov(r5, Operand(r1)); @@ -3527,7 +3882,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // r3: argc // [sp+0]: argv - Label invoke, exit; + Label invoke, handler_entry, exit; // Called from C, so do not pop argc and args on exit (preserve sp) // No need to save register-passed args @@ -3548,7 +3903,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // r2: receiver // r3: argc - // Setup argv in r4. + // Set up argv in r4. int offset_to_argv = (kNumCalleeSaved + 1) * kPointerSize; if (CpuFeatures::IsSupported(VFP3)) { offset_to_argv += kNumDoubleCalleeSaved * kDoubleSize; @@ -3571,7 +3926,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ ldr(r5, MemOperand(r5)); __ Push(r8, r7, r6, r5); - // Setup frame pointer for the frame to be pushed. + // Set up frame pointer for the frame to be pushed. __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); // If this is the outermost JS call, set js_entry_sp value. @@ -3590,31 +3945,33 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ bind(&cont); __ push(ip); - // Call a faked try-block that does the invoke. - __ bl(&invoke); - - // Caught exception: Store result (exception) in the pending - // exception field in the JSEnv and return a failure sentinel. - // Coming in here the fp will be invalid because the PushTryHandler below - // sets it to 0 to signal the existence of the JSEntry frame. + // Jump to a faked try block that does the invoke, with a faked catch + // block that sets the pending exception. + __ jmp(&invoke); + __ bind(&handler_entry); + handler_offset_ = handler_entry.pos(); + // Caught exception: Store result (exception) in the pending exception + // field in the JSEnv and return a failure sentinel. Coming in here the + // fp will be invalid because the PushTryHandler below sets it to 0 to + // signal the existence of the JSEntry frame. __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ str(r0, MemOperand(ip)); __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); __ b(&exit); - // Invoke: Link this frame into the handler chain. + // Invoke: Link this frame into the handler chain. There's only one + // handler block in this code object, so its index is 0. __ bind(&invoke); // Must preserve r0-r4, r5-r7 are available. - __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); + __ PushTryHandler(StackHandler::JS_ENTRY, 0); // If an exception not caught by another handler occurs, this handler // returns control to the code after the bl(&invoke) above, which // restores all kCalleeSaved registers (including cp and fp) to their // saved values before returning a failure to C. // Clear any pending exceptions. - __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate))); - __ ldr(r5, MemOperand(ip)); + __ mov(r5, Operand(isolate->factory()->the_hole_value())); __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ str(r5, MemOperand(ip)); @@ -3708,7 +4065,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { const Register inline_site = r9; const Register scratch = r2; - const int32_t kDeltaToLoadBoolResult = 3 * kPointerSize; + const int32_t kDeltaToLoadBoolResult = 4 * kPointerSize; Label slow, loop, is_instance, is_not_instance, not_js_object; @@ -3725,11 +4082,9 @@ void InstanceofStub::Generate(MacroAssembler* masm) { // real lookup and update the call site cache. if (!HasCallSiteInlineCheck()) { Label miss; - __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex); - __ cmp(function, ip); + __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex); __ b(ne, &miss); - __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex); - __ cmp(map, ip); + __ CompareRoot(map, Heap::kInstanceofCacheMapRootIndex); __ b(ne, &miss); __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex); __ Ret(HasArgsInRegisters() ? 0 : 2); @@ -3738,7 +4093,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } // Get the prototype of the function. - __ TryGetFunctionPrototype(function, prototype, scratch, &slow); + __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(prototype, &slow); @@ -3759,7 +4114,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ sub(inline_site, lr, scratch); // Get the map location in scratch and patch it. __ GetRelocatedValueLocation(inline_site, scratch); - __ str(map, MemOperand(scratch)); + __ ldr(scratch, MemOperand(scratch)); + __ str(map, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); } // Register mapping: r3 is object map and r4 is function prototype. @@ -3851,10 +4207,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); } else { - __ EnterInternalFrame(); - __ Push(r0, r1); - __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(r0, r1); + __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); + } __ cmp(r0, Operand::Zero()); __ LoadRoot(r0, Heap::kTrueValueRootIndex, eq); __ LoadRoot(r0, Heap::kFalseValueRootIndex, ne); @@ -4027,7 +4384,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ str(r3, FieldMemOperand(r0, i)); } - // Setup the callee in-object property. + // Set up the callee in-object property. STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); __ ldr(r3, MemOperand(sp, 2 * kPointerSize)); const int kCalleeOffset = JSObject::kHeaderSize + @@ -4040,7 +4397,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Heap::kArgumentsLengthIndex * kPointerSize; __ str(r2, FieldMemOperand(r0, kLengthOffset)); - // Setup the elements pointer in the allocated arguments object. + // Set up the elements pointer in the allocated arguments object. // If we allocated a parameter map, r4 will point there, otherwise // it will point to the backing store. __ add(r4, r0, Operand(Heap::kArgumentsObjectSize)); @@ -4135,7 +4492,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ Ret(); // Do the runtime call to allocate the arguments object. - // r2 = argument count (taggged) + // r2 = argument count (tagged) __ bind(&runtime); __ str(r2, MemOperand(sp, 0 * kPointerSize)); // Patch argument count. __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1); @@ -4208,7 +4565,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Get the parameters pointer from the stack. __ ldr(r2, MemOperand(sp, 1 * kPointerSize)); - // Setup the elements pointer in the allocated arguments object and + // Set up the elements pointer in the allocated arguments object and // initialize the header in the elements fixed array. __ add(r4, r0, Operand(Heap::kArgumentsObjectSizeStrict)); __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset)); @@ -4220,7 +4577,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Copy the fixed array slots. Label loop; - // Setup r4 to point to the first array slot. + // Set up r4 to point to the first array slot. __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ bind(&loop); // Pre-decrement r2 with kPointerSize on each iteration. @@ -4250,10 +4607,6 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { #ifdef V8_INTERPRETED_REGEXP __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); #else // V8_INTERPRETED_REGEXP - if (!FLAG_regexp_entry_native) { - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); - return; - } // Stack frame on entry. // sp[0]: last_match_info (expected JSArray) @@ -4285,7 +4638,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { ExternalReference::address_of_regexp_stack_memory_size(isolate); __ mov(r0, Operand(address_of_regexp_stack_memory_size)); __ ldr(r0, MemOperand(r0, 0)); - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); __ b(eq, &runtime); // Check that the first argument is a JSRegExp object. @@ -4356,8 +4709,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ ldr(last_match_info_elements, FieldMemOperand(r0, JSArray::kElementsOffset)); __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset)); - __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); - __ cmp(r0, ip); + __ CompareRoot(r0, Heap::kFixedArrayMapRootIndex); __ b(ne, &runtime); // Check that the last match info has space for the capture registers and the // additional information. @@ -4375,25 +4727,39 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { Label seq_string; __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset)); __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); - // First check for flat string. - __ and_(r1, r0, Operand(kIsNotStringMask | kStringRepresentationMask), SetCC); + // First check for flat string. None of the following string type tests will + // succeed if subject is not a string or a short external string. + __ and_(r1, + r0, + Operand(kIsNotStringMask | + kStringRepresentationMask | + kShortExternalStringMask), + SetCC); STATIC_ASSERT((kStringTag | kSeqStringTag) == 0); __ b(eq, &seq_string); // subject: Subject string // regexp_data: RegExp data (FixedArray) + // r1: whether subject is a string and if yes, its string representation // Check for flat cons string or sliced string. // A flat cons string is a cons string where the second part is the empty // string. In that case the subject string is just the first part of the cons // string. Also in this case the first part of the cons string is known to be // a sequential string or an external string. // In the case of a sliced string its offset has to be taken into account. - Label cons_string, check_encoding; + Label cons_string, external_string, check_encoding; STATIC_ASSERT(kConsStringTag < kExternalStringTag); STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); + STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); + STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); __ cmp(r1, Operand(kExternalStringTag)); __ b(lt, &cons_string); - __ b(eq, &runtime); + __ b(eq, &external_string); + + // Catch non-string subject or short external string. + STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0); + __ tst(r1, Operand(kIsNotStringMask | kShortExternalStringMask)); + __ b(ne, &runtime); // String is sliced. __ ldr(r9, FieldMemOperand(subject, SlicedString::kOffsetOffset)); @@ -4404,8 +4770,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // String is a cons string, check whether it is flat. __ bind(&cons_string); __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset)); - __ LoadRoot(r1, Heap::kEmptyStringRootIndex); - __ cmp(r0, r1); + __ CompareRoot(r0, Heap::kEmptyStringRootIndex); __ b(ne, &runtime); __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset)); // Is first part of cons or parent of slice a flat string? @@ -4414,7 +4779,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); STATIC_ASSERT(kSeqStringTag == 0); __ tst(r0, Operand(kStringRepresentationMask)); - __ b(ne, &runtime); + __ b(ne, &external_string); + __ bind(&seq_string); // subject: Subject string // regexp_data: RegExp data (FixedArray) @@ -4480,8 +4846,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // For arguments 4 and 3 get string length, calculate start of string data and // calculate the shift of the index (0 for ASCII and 1 for two byte). - STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); - __ add(r8, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ add(r8, subject, Operand(SeqString::kHeaderSize - kHeapObjectTag)); __ eor(r3, r3, Operand(1)); // Load the length from the original subject string from the previous stack // frame. Therefore we have to use fp, which points exactly to two pointer @@ -4532,8 +4897,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // stack overflow (on the backtrack stack) was detected in RegExp code but // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. - __ mov(r1, Operand(ExternalReference::the_hole_value_location(isolate))); - __ ldr(r1, MemOperand(r1, 0)); + __ mov(r1, Operand(isolate->factory()->the_hole_value())); __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ ldr(r0, MemOperand(r2, 0)); @@ -4575,16 +4939,25 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ str(r2, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastCaptureCountOffset)); // Store last subject and last input. - __ mov(r3, last_match_info_elements); // Moved up to reduce latency. __ str(subject, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastSubjectOffset)); - __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7); + __ mov(r2, subject); + __ RecordWriteField(last_match_info_elements, + RegExpImpl::kLastSubjectOffset, + r2, + r7, + kLRHasNotBeenSaved, + kDontSaveFPRegs); __ str(subject, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastInputOffset)); - __ mov(r3, last_match_info_elements); - __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7); + __ RecordWriteField(last_match_info_elements, + RegExpImpl::kLastInputOffset, + subject, + r7, + kLRHasNotBeenSaved, + kDontSaveFPRegs); // Get the static offsets vector filled by the native regexp code. ExternalReference address_of_static_offsets_vector = @@ -4615,6 +4988,26 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ add(sp, sp, Operand(4 * kPointerSize)); __ Ret(); + // External string. Short external strings have already been ruled out. + // r0: scratch + __ bind(&external_string); + __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset)); + __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ tst(r0, Operand(kIsIndirectStringMask)); + __ Assert(eq, "external string expected, but not found"); + } + __ ldr(subject, + FieldMemOperand(subject, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ sub(subject, + subject, + Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + __ jmp(&seq_string); + // Do the runtime call to execute the regexp. __ bind(&runtime); __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); @@ -4670,11 +5063,11 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // Set input, index and length fields from arguments. __ ldr(r1, MemOperand(sp, kPointerSize * 0)); + __ ldr(r2, MemOperand(sp, kPointerSize * 1)); + __ ldr(r6, MemOperand(sp, kPointerSize * 2)); __ str(r1, FieldMemOperand(r0, JSRegExpResult::kInputOffset)); - __ ldr(r1, MemOperand(sp, kPointerSize * 1)); - __ str(r1, FieldMemOperand(r0, JSRegExpResult::kIndexOffset)); - __ ldr(r1, MemOperand(sp, kPointerSize * 2)); - __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset)); + __ str(r2, FieldMemOperand(r0, JSRegExpResult::kIndexOffset)); + __ str(r6, FieldMemOperand(r0, JSArray::kLengthOffset)); // Fill out the elements FixedArray. // r0: JSArray, tagged. @@ -4696,9 +5089,9 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // r3: Start of elements in FixedArray. // r5: Number of elements to fill. Label loop; - __ tst(r5, Operand(r5)); + __ cmp(r5, Operand(0)); __ bind(&loop); - __ b(le, &done); // Jump if r1 is negative or zero. + __ b(le, &done); // Jump if r5 is negative or zero. __ sub(r5, r5, Operand(1), SetCC); __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2)); __ jmp(&loop); @@ -4712,7 +5105,48 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { } +static void GenerateRecordCallTarget(MacroAssembler* masm) { + // Cache the called function in a global property cell. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // r1 : the function to call + // r2 : cache cell for call target + Label done; + + ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()), + masm->isolate()->heap()->undefined_value()); + ASSERT_EQ(*TypeFeedbackCells::UninitializedSentinel(masm->isolate()), + masm->isolate()->heap()->the_hole_value()); + + // Load the cache state into r3. + __ ldr(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset)); + + // A monomorphic cache hit or an already megamorphic state: invoke the + // function without changing the state. + __ cmp(r3, r1); + __ b(eq, &done); + __ CompareRoot(r3, Heap::kUndefinedValueRootIndex); + __ b(eq, &done); + + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ CompareRoot(r3, Heap::kTheHoleValueRootIndex); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex, ne); + __ str(ip, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset), ne); + + // An uninitialized cache is patched with the function. + __ str(r1, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset), eq); + // No need for a write barrier here - cells are rescanned. + + __ bind(&done); +} + + void CallFunctionStub::Generate(MacroAssembler* masm) { + // r1 : the function to call + // r2 : cache cell for call target Label slow, non_function; // The receiver might implicitly be the global object. This is @@ -4727,16 +5161,12 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ CompareRoot(r4, Heap::kTheHoleValueRootIndex); __ b(ne, &call); // Patch the receiver on the stack with the global receiver object. - __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); - __ str(r1, MemOperand(sp, argc_ * kPointerSize)); + __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset)); + __ str(r2, MemOperand(sp, argc_ * kPointerSize)); __ bind(&call); } - // Get the function to call from the stack. - // function, receiver [, arguments] - __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize)); - // Check that the function is really a JavaScript function. // r1: pushed function (to be verified) __ JumpIfSmi(r1, &non_function); @@ -4774,7 +5204,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE)); __ mov(r2, Operand(0, RelocInfo::NONE)); __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY); - __ SetCallKind(r5, CALL_AS_FUNCTION); + __ SetCallKind(r5, CALL_AS_METHOD); { Handle<Code> adaptor = masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); @@ -4785,7 +5215,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // of the original receiver from the call site). __ bind(&non_function); __ str(r1, MemOperand(sp, argc_ * kPointerSize)); - __ mov(r0, Operand(argc_)); // Setup the number of arguments. + __ mov(r0, Operand(argc_)); // Set up the number of arguments. __ mov(r2, Operand(0, RelocInfo::NONE)); __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); __ SetCallKind(r5, CALL_AS_METHOD); @@ -4794,6 +5224,48 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { } +void CallConstructStub::Generate(MacroAssembler* masm) { + // r0 : number of arguments + // r1 : the function to call + // r2 : cache cell for call target + Label slow, non_function_call; + + // Check that the function is not a smi. + __ JumpIfSmi(r1, &non_function_call); + // Check that the function is a JSFunction. + __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE); + __ b(ne, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + } + + // Jump to the function-specific construct stub. + __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kConstructStubOffset)); + __ add(pc, r2, Operand(Code::kHeaderSize - kHeapObjectTag)); + + // r0: number of arguments + // r1: called object + // r3: object type + Label do_call; + __ bind(&slow); + __ cmp(r3, Operand(JS_FUNCTION_PROXY_TYPE)); + __ b(ne, &non_function_call); + __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); + __ jmp(&do_call); + + __ bind(&non_function_call); + __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); + __ bind(&do_call); + // Set expected number of arguments to zero (not changing r0). + __ mov(r2, Operand(0, RelocInfo::NONE)); + __ SetCallKind(r5, CALL_AS_METHOD); + __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); +} + + // Unfortunately you have to run without snapshots to see most of these // names in the profile since most compare stubs end up in the snapshot. void CompareStub::PrintName(StringStream* stream) { @@ -4855,100 +5327,41 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // If the index is non-smi trigger the non-smi case. __ JumpIfNotSmi(index_, &index_not_smi_); - - // Put smi-tagged index into scratch register. - __ mov(scratch_, index_); __ bind(&got_smi_index_); // Check for index out of range. __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset)); - __ cmp(ip, Operand(scratch_)); + __ cmp(ip, Operand(index_)); __ b(ls, index_out_of_range_); - // We need special handling for non-flat strings. - STATIC_ASSERT(kSeqStringTag == 0); - __ tst(result_, Operand(kStringRepresentationMask)); - __ b(eq, &flat_string); + __ mov(index_, Operand(index_, ASR, kSmiTagSize)); - // Handle non-flat strings. - __ and_(result_, result_, Operand(kStringRepresentationMask)); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - __ cmp(result_, Operand(kExternalStringTag)); - __ b(gt, &sliced_string); - __ b(eq, &call_runtime_); - - // ConsString. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - Label assure_seq_string; - __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset)); - __ LoadRoot(ip, Heap::kEmptyStringRootIndex); - __ cmp(result_, Operand(ip)); - __ b(ne, &call_runtime_); - // Get the first of the two strings and load its instance type. - __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset)); - __ jmp(&assure_seq_string); - - // SlicedString, unpack and add offset. - __ bind(&sliced_string); - __ ldr(result_, FieldMemOperand(object_, SlicedString::kOffsetOffset)); - __ add(scratch_, scratch_, result_); - __ ldr(object_, FieldMemOperand(object_, SlicedString::kParentOffset)); + StringCharLoadGenerator::Generate(masm, + object_, + index_, + result_, + &call_runtime_); - // Assure that we are dealing with a sequential string. Go to runtime if not. - __ bind(&assure_seq_string); - __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); - __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); - // Check that parent is not an external string. Go to runtime otherwise. - STATIC_ASSERT(kSeqStringTag == 0); - __ tst(result_, Operand(kStringRepresentationMask)); - __ b(ne, &call_runtime_); - - // Check for 1-byte or 2-byte string. - __ bind(&flat_string); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ tst(result_, Operand(kStringEncodingMask)); - __ b(ne, &ascii_string); - - // 2-byte string. - // Load the 2-byte character code into the result register. We can - // add without shifting since the smi tag size is the log2 of the - // number of bytes in a two-byte character. - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0); - __ add(scratch_, object_, Operand(scratch_)); - __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize)); - __ jmp(&got_char_code); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize)); - __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize)); - - __ bind(&got_char_code); __ mov(result_, Operand(result_, LSL, kSmiTagSize)); __ bind(&exit_); } void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharCodeAt slow case"); // Index is not a smi. __ bind(&index_not_smi_); // If index is a heap number, try converting it to an integer. __ CheckMap(index_, - scratch_, + result_, Heap::kHeapNumberMapRootIndex, index_not_number_, DONT_DO_SMI_CHECK); call_helper.BeforeCall(masm); - __ Push(object_, index_); + __ push(object_); __ push(index_); // Consumed by runtime conversion function. if (index_flags_ == STRING_INDEX_IS_NUMBER) { __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); @@ -4959,15 +5372,14 @@ void StringCharCodeAtGenerator::GenerateSlow( } // Save the conversion result before the pop instructions below // have a chance to overwrite it. - __ Move(scratch_, r0); - __ pop(index_); + __ Move(index_, r0); __ pop(object_); // Reload the instance type. __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); call_helper.AfterCall(masm); // If index is still not a smi, it must be out of range. - __ JumpIfNotSmi(scratch_, index_out_of_range_); + __ JumpIfNotSmi(index_, index_out_of_range_); // Otherwise, return to the fast path. __ jmp(&got_smi_index_); @@ -4976,6 +5388,7 @@ void StringCharCodeAtGenerator::GenerateSlow( // is too complex (e.g., when the string needs to be flattened). __ bind(&call_runtime_); call_helper.BeforeCall(masm); + __ mov(index_, Operand(index_, LSL, kSmiTagSize)); __ Push(object_, index_); __ CallRuntime(Runtime::kStringCharCodeAt, 2); __ Move(result_, r0); @@ -5004,15 +5417,15 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { STATIC_ASSERT(kSmiTag == 0); __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize)); __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize)); - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ cmp(result_, Operand(ip)); + __ CompareRoot(result_, Heap::kUndefinedValueRootIndex); __ b(eq, &slow_case_); __ bind(&exit_); } void StringCharFromCodeGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharFromCode slow case"); __ bind(&slow_case_); @@ -5037,7 +5450,8 @@ void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) { void StringCharAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { char_code_at_generator_.GenerateSlow(masm, call_helper); char_from_code_generator_.GenerateSlow(masm, call_helper); } @@ -5321,11 +5735,11 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, __ cmp(undefined, candidate); __ b(eq, not_found); - // Must be null (deleted entry). + // Must be the hole (deleted entry). if (FLAG_debug_code) { - __ LoadRoot(ip, Heap::kNullValueRootIndex); + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ cmp(ip, candidate); - __ Assert(eq, "oddball in symbol table is not undefined or null"); + __ Assert(eq, "oddball in symbol table is not undefined or the hole"); } __ jmp(&next_probe[i]); @@ -5421,37 +5835,24 @@ void SubStringStub::Generate(MacroAssembler* masm) { static const int kFromOffset = 1 * kPointerSize; static const int kStringOffset = 2 * kPointerSize; - // Check bounds and smi-ness. - Register to = r6; - Register from = r7; - - __ Ldrd(to, from, MemOperand(sp, kToOffset)); + __ Ldrd(r2, r3, MemOperand(sp, kToOffset)); STATIC_ASSERT(kFromOffset == kToOffset + 4); STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); // I.e., arithmetic shift right by one un-smi-tags. - __ mov(r2, Operand(to, ASR, 1), SetCC); - __ mov(r3, Operand(from, ASR, 1), SetCC, cc); + __ mov(r2, Operand(r2, ASR, 1), SetCC); + __ mov(r3, Operand(r3, ASR, 1), SetCC, cc); // If either to or from had the smi tag bit set, then carry is set now. __ b(cs, &runtime); // Either "from" or "to" is not a smi. - __ b(mi, &runtime); // From is negative. - - // Both to and from are smis. - __ sub(r2, r2, Operand(r3), SetCC); + // We want to bailout to runtime here if From is negative. In that case, the + // next instruction is not executed and we fall through to bailing out to + // runtime. pl is the opposite of mi. + // Both r2 and r3 are untagged integers. + __ sub(r2, r2, Operand(r3), SetCC, pl); __ b(mi, &runtime); // Fail if from > to. - // Special handling of sub-strings of length 1 and 2. One character strings - // are handled in the runtime system (looked up in the single character - // cache). Two character strings are looked for in the symbol cache in - // generated code. - __ cmp(r2, Operand(2)); - __ b(lt, &runtime); - // r2: result string length - // r3: from index (untagged smi) - // r6 (a.k.a. to): to (smi) - // r7 (a.k.a. from): from offset (smi) - // Make sure first argument is a sequential (or flat) string. + // Make sure first argument is a string. __ ldr(r0, MemOperand(sp, kStringOffset)); STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(r0, &runtime); @@ -5466,67 +5867,15 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ cmp(r2, Operand(r4, ASR, 1)); __ b(eq, &return_r0); - Label create_slice; - if (FLAG_string_slices) { - __ cmp(r2, Operand(SlicedString::kMinLength)); - __ b(ge, &create_slice); - } - - // r0: original string - // r1: instance type - // r2: result string length - // r3: from index (untagged smi) - // r6 (a.k.a. to): to (smi) - // r7 (a.k.a. from): from offset (smi) - Label seq_string; - __ and_(r4, r1, Operand(kStringRepresentationMask)); - STATIC_ASSERT(kSeqStringTag < kConsStringTag); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kConsStringTag < kSlicedStringTag); - __ cmp(r4, Operand(kConsStringTag)); - __ b(gt, &runtime); // Slices and external strings go to runtime. - __ b(lt, &seq_string); // Sequential strings are handled directly. - - // Cons string. Try to recurse (once) on the first substring. - // (This adds a little more generality than necessary to handle flattened - // cons strings, but not much). - __ ldr(r0, FieldMemOperand(r0, ConsString::kFirstOffset)); - __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset)); - __ tst(r1, Operand(kStringRepresentationMask)); - STATIC_ASSERT(kSeqStringTag == 0); - __ b(ne, &runtime); // Cons, slices and external strings go to runtime. - - // Definitly a sequential string. - __ bind(&seq_string); - - // r0: original string - // r1: instance type - // r2: result string length - // r3: from index (untagged smi) - // r6 (a.k.a. to): to (smi) - // r7 (a.k.a. from): from offset (smi) - __ ldr(r4, FieldMemOperand(r0, String::kLengthOffset)); - __ cmp(r4, Operand(to)); - __ b(lt, &runtime); // Fail if to > length. - to = no_reg; - - // r0: original string or left hand side of the original cons string. - // r1: instance type - // r2: result string length - // r3: from index (untagged smi) - // r7 (a.k.a. from): from offset (smi) - // Check for flat ASCII string. - Label non_ascii_flat; - __ tst(r1, Operand(kStringEncodingMask)); - STATIC_ASSERT(kTwoByteStringTag == 0); - __ b(eq, &non_ascii_flat); - Label result_longer_than_two; + // Check for special case of two character ASCII string, in which case + // we do a lookup in the symbol table first. __ cmp(r2, Operand(2)); __ b(gt, &result_longer_than_two); + __ b(lt, &runtime); + + __ JumpIfInstanceTypeIsNotSequentialAscii(r1, r1, &runtime); - // Sub string of length 2 requested. // Get the two characters forming the sub string. __ add(r0, r0, Operand(r3)); __ ldrb(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize)); @@ -5536,7 +5885,6 @@ void SubStringStub::Generate(MacroAssembler* masm) { Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string); - Counters* counters = masm->isolate()->counters(); __ jmp(&return_r0); // r2: result string length. @@ -5547,18 +5895,114 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ jmp(&return_r0); __ bind(&result_longer_than_two); + // Deal with different string types: update the index if necessary + // and put the underlying string into r5. + // r0: original string + // r1: instance type + // r2: length + // r3: from index (untagged) + Label underlying_unpacked, sliced_string, seq_or_external_string; + // If the string is not indirect, it can only be sequential or external. + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ tst(r1, Operand(kIsIndirectStringMask)); + __ b(eq, &seq_or_external_string); + + __ tst(r1, Operand(kSlicedNotConsMask)); + __ b(ne, &sliced_string); + // Cons string. Check whether it is flat, then fetch first part. + __ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset)); + __ CompareRoot(r5, Heap::kEmptyStringRootIndex); + __ b(ne, &runtime); + __ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset)); + // Update instance type. + __ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset)); + __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked); - // Locate 'from' character of string. - __ add(r5, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ add(r5, r5, Operand(from, ASR, 1)); + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ ldr(r4, FieldMemOperand(r0, SlicedString::kOffsetOffset)); + __ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); + __ add(r3, r3, Operand(r4, ASR, 1)); // Add offset to index. + // Update instance type. + __ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset)); + __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked); - // Allocate the result. - __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime); + __ bind(&seq_or_external_string); + // Sequential or external string. Just move string to the expected register. + __ mov(r5, r0); - // r0: result string - // r2: result string length - // r5: first character of substring to copy - // r7 (a.k.a. from): from offset (smi) + __ bind(&underlying_unpacked); + + if (FLAG_string_slices) { + Label copy_routine; + // r5: underlying subject string + // r1: instance type of underlying subject string + // r2: length + // r3: adjusted start index (untagged) + __ cmp(r2, Operand(SlicedString::kMinLength)); + // Short slice. Copy instead of slicing. + __ b(lt, ©_routine); + // Allocate new sliced string. At this point we do not reload the instance + // type including the string encoding because we simply rely on the info + // provided by the original string. It does not matter if the original + // string's encoding is wrong because we always have to recheck encoding of + // the newly created string's parent anyways due to externalized strings. + Label two_byte_slice, set_slice_header; + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ tst(r1, Operand(kStringEncodingMask)); + __ b(eq, &two_byte_slice); + __ AllocateAsciiSlicedString(r0, r2, r6, r7, &runtime); + __ jmp(&set_slice_header); + __ bind(&two_byte_slice); + __ AllocateTwoByteSlicedString(r0, r2, r6, r7, &runtime); + __ bind(&set_slice_header); + __ mov(r3, Operand(r3, LSL, 1)); + __ str(r3, FieldMemOperand(r0, SlicedString::kOffsetOffset)); + __ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); + __ jmp(&return_r0); + + __ bind(©_routine); + } + + // r5: underlying subject string + // r1: instance type of underlying subject string + // r2: length + // r3: adjusted start index (untagged) + Label two_byte_sequential, sequential_string, allocate_result; + STATIC_ASSERT(kExternalStringTag != 0); + STATIC_ASSERT(kSeqStringTag == 0); + __ tst(r1, Operand(kExternalStringTag)); + __ b(eq, &sequential_string); + + // Handle external string. + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ tst(r1, Operand(kShortExternalStringTag)); + __ b(ne, &runtime); + __ ldr(r5, FieldMemOperand(r5, ExternalString::kResourceDataOffset)); + // r5 already points to the first character of underlying string. + __ jmp(&allocate_result); + + __ bind(&sequential_string); + // Locate first character of underlying subject string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + + __ bind(&allocate_result); + // Sequential acii string. Allocate the result. + STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0); + __ tst(r1, Operand(kStringEncodingMask)); + __ b(eq, &two_byte_sequential); + + // Allocate and copy the resulting ASCII string. + __ AllocateAsciiString(r0, r2, r4, r6, r7, &runtime); + + // Locate first character of substring to copy. + __ add(r5, r5, r3); // Locate first character of result. __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); @@ -5571,30 +6015,16 @@ void SubStringStub::Generate(MacroAssembler* masm) { COPY_ASCII | DEST_ALWAYS_ALIGNED); __ jmp(&return_r0); - __ bind(&non_ascii_flat); - // r0: original string - // r2: result string length - // r7 (a.k.a. from): from offset (smi) - // Check for flat two byte string. + // Allocate and copy the resulting two-byte string. + __ bind(&two_byte_sequential); + __ AllocateTwoByteString(r0, r2, r4, r6, r7, &runtime); - // Locate 'from' character of string. - __ add(r5, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // As "from" is a smi it is 2 times the value which matches the size of a two - // byte character. + // Locate first character of substring to copy. STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); - __ add(r5, r5, Operand(from)); - - // Allocate the result. - __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime); - - // r0: result string - // r2: result string length - // r5: first character of substring to copy + __ add(r5, r5, Operand(r3, LSL, 1)); // Locate first character of result. __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - from = no_reg; - // r0: result string. // r1: first character of result. // r2: result length. @@ -5602,72 +6032,9 @@ void SubStringStub::Generate(MacroAssembler* masm) { STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong( masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED); - __ jmp(&return_r0); - - if (FLAG_string_slices) { - __ bind(&create_slice); - // r0: original string - // r1: instance type - // r2: length - // r3: from index (untagged smi) - // r6 (a.k.a. to): to (smi) - // r7 (a.k.a. from): from offset (smi) - Label allocate_slice, sliced_string, seq_string; - STATIC_ASSERT(kSeqStringTag == 0); - __ tst(r1, Operand(kStringRepresentationMask)); - __ b(eq, &seq_string); - STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); - STATIC_ASSERT(kIsIndirectStringMask != 0); - __ tst(r1, Operand(kIsIndirectStringMask)); - // External string. Jump to runtime. - __ b(eq, &runtime); - - __ tst(r1, Operand(kSlicedNotConsMask)); - __ b(ne, &sliced_string); - // Cons string. Check whether it is flat, then fetch first part. - __ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset)); - __ LoadRoot(r9, Heap::kEmptyStringRootIndex); - __ cmp(r5, r9); - __ b(ne, &runtime); - __ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset)); - __ jmp(&allocate_slice); - - __ bind(&sliced_string); - // Sliced string. Fetch parent and correct start index by offset. - __ ldr(r5, FieldMemOperand(r0, SlicedString::kOffsetOffset)); - __ add(r7, r7, r5); - __ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); - __ jmp(&allocate_slice); - - __ bind(&seq_string); - // Sequential string. Just move string to the right register. - __ mov(r5, r0); - - __ bind(&allocate_slice); - // r1: instance type of original string - // r2: length - // r5: underlying subject string - // r7 (a.k.a. from): from offset (smi) - // Allocate new sliced string. At this point we do not reload the instance - // type including the string encoding because we simply rely on the info - // provided by the original string. It does not matter if the original - // string's encoding is wrong because we always have to recheck encoding of - // the newly created string's parent anyways due to externalized strings. - Label two_byte_slice, set_slice_header; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ tst(r1, Operand(kStringEncodingMask)); - __ b(eq, &two_byte_slice); - __ AllocateAsciiSlicedString(r0, r2, r3, r4, &runtime); - __ jmp(&set_slice_header); - __ bind(&two_byte_slice); - __ AllocateTwoByteSlicedString(r0, r2, r3, r4, &runtime); - __ bind(&set_slice_header); - __ str(r7, FieldMemOperand(r0, SlicedString::kOffsetOffset)); - __ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); - } __ bind(&return_r0); + Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->sub_string_native(), 1, r3, r4); __ add(sp, sp, Operand(3 * kPointerSize)); __ Ret(); @@ -5700,7 +6067,7 @@ void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm, Label compare_chars; __ bind(&check_zero_length); STATIC_ASSERT(kSmiTag == 0); - __ tst(length, Operand(length)); + __ cmp(length, Operand(0)); __ b(ne, &compare_chars); __ mov(r0, Operand(Smi::FromInt(EQUAL))); __ Ret(); @@ -5733,7 +6100,7 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, __ mov(scratch1, scratch2, LeaveCC, gt); Register min_length = scratch1; STATIC_ASSERT(kSmiTag == 0); - __ tst(min_length, Operand(min_length)); + __ cmp(min_length, Operand(0)); __ b(eq, &compare_lengths); // Compare loop. @@ -5824,7 +6191,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) { - Label string_add_runtime, call_builtin; + Label call_runtime, call_builtin; Builtins::JavaScript builtin_id = Builtins::ADD; Counters* counters = masm->isolate()->counters(); @@ -5839,7 +6206,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Make sure that both arguments are strings if not known in advance. if (flags_ == NO_STRING_ADD_FLAGS) { - __ JumpIfEitherSmi(r0, r1, &string_add_runtime); + __ JumpIfEitherSmi(r0, r1, &call_runtime); // Load instance types. __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); @@ -5849,7 +6216,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // If either is not a string, go to runtime. __ tst(r4, Operand(kIsNotStringMask)); __ tst(r5, Operand(kIsNotStringMask), eq); - __ b(ne, &string_add_runtime); + __ b(ne, &call_runtime); } else { // Here at least one of the arguments is definitely a string. // We convert the one that is not known to be a string. @@ -5918,7 +6285,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset)); } __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7, - &string_add_runtime); + &call_runtime); // Get the two characters forming the sub string. __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize)); @@ -5940,7 +6307,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // halfword store instruction (which assumes that processor is // in a little endian mode) __ mov(r6, Operand(2)); - __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime); + __ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime); __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize)); __ IncrementCounter(counters->string_add_native(), 1, r2, r3); __ add(sp, sp, Operand(2 * kPointerSize)); @@ -5948,14 +6315,14 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ bind(&longer_than_two); // Check if resulting string will be flat. - __ cmp(r6, Operand(String::kMinNonFlatLength)); + __ cmp(r6, Operand(ConsString::kMinLength)); __ b(lt, &string_add_flat_result); // Handle exceptionally long strings in the runtime system. STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0); ASSERT(IsPowerOf2(String::kMaxLength + 1)); // kMaxLength + 1 is representable as shifted literal, kMaxLength is not. __ cmp(r6, Operand(String::kMaxLength + 1)); - __ b(hs, &string_add_runtime); + __ b(hs, &call_runtime); // If result is not supposed to be flat, allocate a cons string object. // If both strings are ASCII the result is an ASCII cons string. @@ -5973,7 +6340,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Allocate an ASCII cons string. __ bind(&ascii_data); - __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime); + __ AllocateAsciiConsString(r7, r6, r4, r5, &call_runtime); __ bind(&allocated); // Fill the fields of the cons string. __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset)); @@ -5998,11 +6365,13 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ b(eq, &ascii_data); // Allocate a two byte cons string. - __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime); + __ AllocateTwoByteConsString(r7, r6, r4, r5, &call_runtime); __ jmp(&allocated); - // Handle creating a flat result. First check that both strings are - // sequential and that they have the same encoding. + // We cannot encounter sliced strings or cons strings here since: + STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength); + // Handle creating a flat result from either external or sequential strings. + // Locate the first characters' locations. // r0: first string // r1: second string // r2: length of first string @@ -6010,6 +6379,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS) // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS) // r6: sum of lengths. + Label first_prepared, second_prepared; __ bind(&string_add_flat_result); if (flags_ != NO_STRING_ADD_FLAGS) { __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); @@ -6017,97 +6387,88 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset)); } - // Check that both strings are sequential. + + // Check whether both strings have same encoding + __ eor(r7, r4, Operand(r5)); + __ tst(r7, Operand(kStringEncodingMask)); + __ b(ne, &call_runtime); + STATIC_ASSERT(kSeqStringTag == 0); __ tst(r4, Operand(kStringRepresentationMask)); - __ tst(r5, Operand(kStringRepresentationMask), eq); - __ b(ne, &string_add_runtime); - // Now check if both strings have the same encoding (ASCII/Two-byte). - // r0: first string. - // r1: second string. + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ add(r7, + r0, + Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag), + LeaveCC, + eq); + __ b(eq, &first_prepared); + // External string: rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ tst(r4, Operand(kShortExternalStringMask)); + __ b(ne, &call_runtime); + __ ldr(r7, FieldMemOperand(r0, ExternalString::kResourceDataOffset)); + __ bind(&first_prepared); + + STATIC_ASSERT(kSeqStringTag == 0); + __ tst(r5, Operand(kStringRepresentationMask)); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ add(r1, + r1, + Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag), + LeaveCC, + eq); + __ b(eq, &second_prepared); + // External string: rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ tst(r5, Operand(kShortExternalStringMask)); + __ b(ne, &call_runtime); + __ ldr(r1, FieldMemOperand(r1, ExternalString::kResourceDataOffset)); + __ bind(&second_prepared); + + Label non_ascii_string_add_flat_result; + // r7: first character of first string + // r1: first character of second string // r2: length of first string. // r3: length of second string. - // r6: sum of lengths.. - Label non_ascii_string_add_flat_result; - ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test. - __ eor(r7, r4, Operand(r5)); - __ tst(r7, Operand(kStringEncodingMask)); - __ b(ne, &string_add_runtime); - // And see if it's ASCII or two-byte. - __ tst(r4, Operand(kStringEncodingMask)); + // r6: sum of lengths. + // Both strings have the same encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ tst(r5, Operand(kStringEncodingMask)); __ b(eq, &non_ascii_string_add_flat_result); - // Both strings are sequential ASCII strings. We also know that they are - // short (since the sum of the lengths is less than kMinNonFlatLength). - // r6: length of resulting flat string - __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime); - // Locate first character of result. - __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument. - __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // r0: first character of first string. - // r1: second string. + __ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime); + __ add(r6, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // r0: result string. + // r7: first character of first string. + // r1: first character of second string. // r2: length of first string. // r3: length of second string. // r6: first character of result. - // r7: result string. - StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true); - - // Load second argument and locate first character. - __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // r1: first character of second string. - // r3: length of second string. + StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, true); // r6: next character of result. - // r7: result string. StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true); - __ mov(r0, Operand(r7)); __ IncrementCounter(counters->string_add_native(), 1, r2, r3); __ add(sp, sp, Operand(2 * kPointerSize)); __ Ret(); __ bind(&non_ascii_string_add_flat_result); - // Both strings are sequential two byte strings. - // r0: first string. - // r1: second string. - // r2: length of first string. - // r3: length of second string. - // r6: sum of length of strings. - __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime); - // r0: first string. - // r1: second string. - // r2: length of first string. - // r3: length of second string. - // r7: result string. - - // Locate first character of result. - __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument. - __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - - // r0: first character of first string. - // r1: second string. + __ AllocateTwoByteString(r0, r6, r4, r5, r9, &call_runtime); + __ add(r6, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // r0: result string. + // r7: first character of first string. + // r1: first character of second string. // r2: length of first string. // r3: length of second string. // r6: first character of result. - // r7: result string. - StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false); - - // Locate first character of second argument. - __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - - // r1: first character of second string. - // r3: length of second string. - // r6: next character of result (after copy of first string). - // r7: result string. + StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, false); + // r6: next character of result. StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false); - - __ mov(r0, Operand(r7)); __ IncrementCounter(counters->string_add_native(), 1, r2, r3); __ add(sp, sp, Operand(2 * kPointerSize)); __ Ret(); // Just jump to runtime to add the two strings. - __ bind(&string_add_runtime); + __ bind(&call_runtime); __ TailCallRuntime(Runtime::kStringAdd, 2, 1); if (call_builtin.is_linked()) { @@ -6359,25 +6720,47 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { } +void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) { + Label miss; + __ and_(r2, r1, Operand(r0)); + __ JumpIfSmi(r2, &miss); + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ cmp(r2, Operand(known_map_)); + __ b(ne, &miss); + __ cmp(r3, Operand(known_map_)); + __ b(ne, &miss); + + __ sub(r0, r0, Operand(r1)); + __ Ret(); + + __ bind(&miss); + GenerateMiss(masm); +} + + + void ICCompareStub::GenerateMiss(MacroAssembler* masm) { - __ Push(r1, r0); - __ push(lr); + { + // Call the runtime system in a fresh internal frame. + ExternalReference miss = + ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); + + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(r1, r0); + __ push(lr); + __ Push(r1, r0); + __ mov(ip, Operand(Smi::FromInt(op_))); + __ push(ip); + __ CallExternalReference(miss, 3); + // Compute the entry point of the rewritten stub. + __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); + // Restore registers. + __ pop(lr); + __ pop(r0); + __ pop(r1); + } - // Call the runtime system in a fresh internal frame. - ExternalReference miss = - ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); - __ EnterInternalFrame(); - __ Push(r1, r0); - __ mov(ip, Operand(Smi::FromInt(op_))); - __ push(ip); - __ CallExternalReference(miss, 3); - __ LeaveInternalFrame(); - // Compute the entry point of the rewritten stub. - __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); - // Restore registers. - __ pop(lr); - __ pop(r0); - __ pop(r1); __ Jump(r2); } @@ -6410,14 +6793,13 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm, } -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - String* name, - Register scratch0) { +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle<String> name, + Register scratch0) { // If names of slots in range from 1 to kProbes - 1 for the hash value are // not equal to the name and kProbes-th slot is not used (its name is the // undefined value), it guarantees the hash table doesn't contain the @@ -6475,14 +6857,12 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( __ ldr(r0, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); __ mov(r1, Operand(Handle<String>(name))); StringDictionaryLookupStub stub(NEGATIVE_LOOKUP); - MaybeObject* result = masm->TryCallStub(&stub); - if (result->IsFailure()) return result; - __ tst(r0, Operand(r0)); + __ CallStub(&stub); + __ cmp(r0, Operand(0)); __ ldm(ia_w, sp, spill_mask); __ b(eq, done); __ b(ne, miss); - return result; } @@ -6497,6 +6877,11 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, Register name, Register scratch1, Register scratch2) { + ASSERT(!elements.is(scratch1)); + ASSERT(!elements.is(scratch2)); + ASSERT(!name.is(scratch1)); + ASSERT(!name.is(scratch2)); + // Assert that name contains a string. if (FLAG_debug_code) __ AbortIfNotString(name); @@ -6540,11 +6925,17 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, ~(scratch1.bit() | scratch2.bit()); __ stm(db_w, sp, spill_mask); - __ Move(r0, elements); - __ Move(r1, name); + if (name.is(r0)) { + ASSERT(!elements.is(r1)); + __ Move(r1, name); + __ Move(r0, elements); + } else { + __ Move(r0, elements); + __ Move(r1, name); + } StringDictionaryLookupStub stub(POSITIVE_LOOKUP); __ CallStub(&stub); - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); __ mov(scratch2, Operand(r2)); __ ldm(ia_w, sp, spill_mask); @@ -6554,6 +6945,8 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. // Registers: // result: StringDictionary to probe // r1: key @@ -6643,6 +7036,333 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { } +struct AheadOfTimeWriteBarrierStubList { + Register object, value, address; + RememberedSetAction action; +}; + + +struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { + // Used in RegExpExecStub. + { r6, r4, r7, EMIT_REMEMBERED_SET }, + { r6, r2, r7, EMIT_REMEMBERED_SET }, + // Used in CompileArrayPushCall. + // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore. + // Also used in KeyedStoreIC::GenerateGeneric. + { r3, r4, r5, EMIT_REMEMBERED_SET }, + // Used in CompileStoreGlobal. + { r4, r1, r2, OMIT_REMEMBERED_SET }, + // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField. + { r1, r2, r3, EMIT_REMEMBERED_SET }, + { r3, r2, r1, EMIT_REMEMBERED_SET }, + // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField. + { r2, r1, r3, EMIT_REMEMBERED_SET }, + { r3, r1, r2, EMIT_REMEMBERED_SET }, + // KeyedStoreStubCompiler::GenerateStoreFastElement. + { r4, r2, r3, EMIT_REMEMBERED_SET }, + // ElementsTransitionGenerator::GenerateSmiOnlyToObject + // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // and ElementsTransitionGenerator::GenerateDoubleToObject + { r2, r3, r9, EMIT_REMEMBERED_SET }, + // ElementsTransitionGenerator::GenerateDoubleToObject + { r6, r2, r0, EMIT_REMEMBERED_SET }, + { r2, r6, r9, EMIT_REMEMBERED_SET }, + // StoreArrayLiteralElementStub::Generate + { r5, r0, r6, EMIT_REMEMBERED_SET }, + // Null termination. + { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET} +}; + + +bool RecordWriteStub::IsPregenerated() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + if (object_.is(entry->object) && + value_.is(entry->value) && + address_.is(entry->address) && + remembered_set_action_ == entry->action && + save_fp_regs_mode_ == kDontSaveFPRegs) { + return true; + } + } + return false; +} + + +bool StoreBufferOverflowStub::IsPregenerated() { + return save_doubles_ == kDontSaveFPRegs || ISOLATE->fp_stubs_generated(); +} + + +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() { + StoreBufferOverflowStub stub1(kDontSaveFPRegs); + stub1.GetCode()->set_is_pregenerated(true); +} + + +void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + RecordWriteStub stub(entry->object, + entry->value, + entry->address, + entry->action, + kDontSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + } +} + + +// Takes the input in 3 registers: address_ value_ and object_. A pointer to +// the value has just been written into the object, now this stub makes sure +// we keep the GC informed. The word in the object where the value has been +// written is in the address register. +void RecordWriteStub::Generate(MacroAssembler* masm) { + Label skip_to_incremental_noncompacting; + Label skip_to_incremental_compacting; + + // The first two instructions are generated with labels so as to get the + // offset fixed up correctly by the bind(Label*) call. We patch it back and + // forth between a compare instructions (a nop in this position) and the + // real branch when we start and stop incremental heap marking. + // See RecordWriteStub::Patch for details. + __ b(&skip_to_incremental_noncompacting); + __ b(&skip_to_incremental_compacting); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } + __ Ret(); + + __ bind(&skip_to_incremental_noncompacting); + GenerateIncremental(masm, INCREMENTAL); + + __ bind(&skip_to_incremental_compacting); + GenerateIncremental(masm, INCREMENTAL_COMPACTION); + + // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. + // Will be checked in IncrementalMarking::ActivateGeneratedStub. + ASSERT(Assembler::GetBranchOffset(masm->instr_at(0)) < (1 << 12)); + ASSERT(Assembler::GetBranchOffset(masm->instr_at(4)) < (1 << 12)); + PatchBranchIntoNop(masm, 0); + PatchBranchIntoNop(masm, Assembler::kInstrSize); +} + + +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { + regs_.Save(masm); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + Label dont_need_remembered_set; + + __ ldr(regs_.scratch0(), MemOperand(regs_.address(), 0)); + __ JumpIfNotInNewSpace(regs_.scratch0(), // Value. + regs_.scratch0(), + &dont_need_remembered_set); + + __ CheckPageFlag(regs_.object(), + regs_.scratch0(), + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + ne, + &dont_need_remembered_set); + + // First notify the incremental marker if necessary, then update the + // remembered set. + CheckNeedsToInformIncrementalMarker( + masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + + __ bind(&dont_need_remembered_set); + } + + CheckNeedsToInformIncrementalMarker( + masm, kReturnOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ Ret(); +} + + +void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) { + regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_); + int argument_count = 3; + __ PrepareCallCFunction(argument_count, regs_.scratch0()); + Register address = + r0.is(regs_.address()) ? regs_.scratch0() : regs_.address(); + ASSERT(!address.is(regs_.object())); + ASSERT(!address.is(r0)); + __ Move(address, regs_.address()); + __ Move(r0, regs_.object()); + if (mode == INCREMENTAL_COMPACTION) { + __ Move(r1, address); + } else { + ASSERT(mode == INCREMENTAL); + __ ldr(r1, MemOperand(address, 0)); + } + __ mov(r2, Operand(ExternalReference::isolate_address())); + + AllowExternalCallThatCantCauseGC scope(masm); + if (mode == INCREMENTAL_COMPACTION) { + __ CallCFunction( + ExternalReference::incremental_evacuation_record_write_function( + masm->isolate()), + argument_count); + } else { + ASSERT(mode == INCREMENTAL); + __ CallCFunction( + ExternalReference::incremental_marking_record_write_function( + masm->isolate()), + argument_count); + } + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_); +} + + +void RecordWriteStub::CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode) { + Label on_black; + Label need_incremental; + Label need_incremental_pop_scratch; + + // Let's look at the color of the object: If it is not black we don't have + // to inform the incremental marker. + __ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ Ret(); + } + + __ bind(&on_black); + + // Get the value from the slot. + __ ldr(regs_.scratch0(), MemOperand(regs_.address(), 0)); + + if (mode == INCREMENTAL_COMPACTION) { + Label ensure_not_white; + + __ CheckPageFlag(regs_.scratch0(), // Contains value. + regs_.scratch1(), // Scratch. + MemoryChunk::kEvacuationCandidateMask, + eq, + &ensure_not_white); + + __ CheckPageFlag(regs_.object(), + regs_.scratch1(), // Scratch. + MemoryChunk::kSkipEvacuationSlotsRecordingMask, + eq, + &need_incremental); + + __ bind(&ensure_not_white); + } + + // We need extra registers for this, so we push the object and the address + // register temporarily. + __ Push(regs_.object(), regs_.address()); + __ EnsureNotWhite(regs_.scratch0(), // The value. + regs_.scratch1(), // Scratch. + regs_.object(), // Scratch. + regs_.address(), // Scratch. + &need_incremental_pop_scratch); + __ Pop(regs_.object(), regs_.address()); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ Ret(); + } + + __ bind(&need_incremental_pop_scratch); + __ Pop(regs_.object(), regs_.address()); + + __ bind(&need_incremental); + + // Fall through when we need to inform the incremental marker. +} + + +void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : element value to store + // -- r1 : array literal + // -- r2 : map of array literal + // -- r3 : element index as smi + // -- r4 : array literal index in function as smi + // ----------------------------------- + + Label element_done; + Label double_elements; + Label smi_element; + Label slow_elements; + Label fast_elements; + + __ CheckFastElements(r2, r5, &double_elements); + // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + __ JumpIfSmi(r0, &smi_element); + __ CheckFastSmiOnlyElements(r2, r5, &fast_elements); + + // Store into the array literal requires a elements transition. Call into + // the runtime. + __ bind(&slow_elements); + // call. + __ Push(r1, r3, r0); + __ ldr(r5, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ ldr(r5, FieldMemOperand(r5, JSFunction::kLiteralsOffset)); + __ Push(r5, r4); + __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); + + // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + __ bind(&fast_elements); + __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset)); + __ add(r6, r5, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ add(r6, r6, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ str(r0, MemOperand(r6, 0)); + // Update the write barrier for the array store. + __ RecordWrite(r5, r6, r0, kLRHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ Ret(); + + // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or + // FAST_ELEMENTS, and value is Smi. + __ bind(&smi_element); + __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset)); + __ add(r6, r5, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(r0, FieldMemOperand(r6, FixedArray::kHeaderSize)); + __ Ret(); + + // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + __ bind(&double_elements); + __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset)); + __ StoreNumberToDoubleElements(r0, r3, r1, r5, r6, r7, r9, r2, + &slow_elements); + __ Ret(); +} + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/arm/code-stubs-arm.h b/deps/v8/src/arm/code-stubs-arm.h index cdea03ee8a..38ed476cc1 100644 --- a/deps/v8/src/arm/code-stubs-arm.h +++ b/deps/v8/src/arm/code-stubs-arm.h @@ -58,6 +58,25 @@ class TranscendentalCacheStub: public CodeStub { }; +class StoreBufferOverflowStub: public CodeStub { + public: + explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) + : save_doubles_(save_fp) { } + + void Generate(MacroAssembler* masm); + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + private: + SaveFPRegsMode save_doubles_; + + Major MajorKey() { return StoreBufferOverflow; } + int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } +}; + + class UnaryOpStub: public CodeStub { public: UnaryOpStub(Token::Value op, @@ -117,7 +136,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_unary_op_type(operand_type_); } }; @@ -216,7 +235,7 @@ class BinaryOpStub: public CodeStub { return BinaryOpIC::ToState(operands_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } @@ -387,6 +406,9 @@ class WriteInt32ToHeapNumberStub : public CodeStub { the_heap_number_(the_heap_number), scratch_(scratch) { } + bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + private: Register the_int_; Register the_heap_number_; @@ -435,6 +457,218 @@ class NumberToStringStub: public CodeStub { }; +class RecordWriteStub: public CodeStub { + public: + RecordWriteStub(Register object, + Register value, + Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode) + : object_(object), + value_(value), + address_(address), + remembered_set_action_(remembered_set_action), + save_fp_regs_mode_(fp_mode), + regs_(object, // An input reg. + address, // An input reg. + value) { // One scratch reg. + } + + enum Mode { + STORE_BUFFER_ONLY, + INCREMENTAL, + INCREMENTAL_COMPACTION + }; + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + static void PatchBranchIntoNop(MacroAssembler* masm, int pos) { + masm->instr_at_put(pos, (masm->instr_at(pos) & ~B27) | (B24 | B20)); + ASSERT(Assembler::IsTstImmediate(masm->instr_at(pos))); + } + + static void PatchNopIntoBranch(MacroAssembler* masm, int pos) { + masm->instr_at_put(pos, (masm->instr_at(pos) & ~(B24 | B20)) | B27); + ASSERT(Assembler::IsBranch(masm->instr_at(pos))); + } + + static Mode GetMode(Code* stub) { + Instr first_instruction = Assembler::instr_at(stub->instruction_start()); + Instr second_instruction = Assembler::instr_at(stub->instruction_start() + + Assembler::kInstrSize); + + if (Assembler::IsBranch(first_instruction)) { + return INCREMENTAL; + } + + ASSERT(Assembler::IsTstImmediate(first_instruction)); + + if (Assembler::IsBranch(second_instruction)) { + return INCREMENTAL_COMPACTION; + } + + ASSERT(Assembler::IsTstImmediate(second_instruction)); + + return STORE_BUFFER_ONLY; + } + + static void Patch(Code* stub, Mode mode) { + MacroAssembler masm(NULL, + stub->instruction_start(), + stub->instruction_size()); + switch (mode) { + case STORE_BUFFER_ONLY: + ASSERT(GetMode(stub) == INCREMENTAL || + GetMode(stub) == INCREMENTAL_COMPACTION); + PatchBranchIntoNop(&masm, 0); + PatchBranchIntoNop(&masm, Assembler::kInstrSize); + break; + case INCREMENTAL: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + PatchNopIntoBranch(&masm, 0); + break; + case INCREMENTAL_COMPACTION: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + PatchNopIntoBranch(&masm, Assembler::kInstrSize); + break; + } + ASSERT(GetMode(stub) == mode); + CPU::FlushICache(stub->instruction_start(), 2 * Assembler::kInstrSize); + } + + private: + // This is a helper class for freeing up 3 scratch registers. The input is + // two registers that must be preserved and one scratch register provided by + // the caller. + class RegisterAllocation { + public: + RegisterAllocation(Register object, + Register address, + Register scratch0) + : object_(object), + address_(address), + scratch0_(scratch0) { + ASSERT(!AreAliased(scratch0, object, address, no_reg)); + scratch1_ = GetRegThatIsNotOneOf(object_, address_, scratch0_); + } + + void Save(MacroAssembler* masm) { + ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); + // We don't have to save scratch0_ because it was given to us as + // a scratch register. + masm->push(scratch1_); + } + + void Restore(MacroAssembler* masm) { + masm->pop(scratch1_); + } + + // If we have to call into C then we need to save and restore all caller- + // saved registers that were not already preserved. The scratch registers + // will be restored by other means so we don't bother pushing them here. + void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { + masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(VFP3); + masm->sub(sp, + sp, + Operand(kDoubleSize * (DwVfpRegister::kNumRegisters - 1))); + // Save all VFP registers except d0. + for (int i = DwVfpRegister::kNumRegisters - 1; i > 0; i--) { + DwVfpRegister reg = DwVfpRegister::from_code(i); + masm->vstr(reg, MemOperand(sp, (i - 1) * kDoubleSize)); + } + } + } + + inline void RestoreCallerSaveRegisters(MacroAssembler*masm, + SaveFPRegsMode mode) { + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(VFP3); + // Restore all VFP registers except d0. + for (int i = DwVfpRegister::kNumRegisters - 1; i > 0; i--) { + DwVfpRegister reg = DwVfpRegister::from_code(i); + masm->vldr(reg, MemOperand(sp, (i - 1) * kDoubleSize)); + } + masm->add(sp, + sp, + Operand(kDoubleSize * (DwVfpRegister::kNumRegisters - 1))); + } + masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); + } + + inline Register object() { return object_; } + inline Register address() { return address_; } + inline Register scratch0() { return scratch0_; } + inline Register scratch1() { return scratch1_; } + + private: + Register object_; + Register address_; + Register scratch0_; + Register scratch1_; + + Register GetRegThatIsNotOneOf(Register r1, + Register r2, + Register r3) { + for (int i = 0; i < Register::kNumAllocatableRegisters; i++) { + Register candidate = Register::FromAllocationIndex(i); + if (candidate.is(r1)) continue; + if (candidate.is(r2)) continue; + if (candidate.is(r3)) continue; + return candidate; + } + UNREACHABLE(); + return no_reg; + } + friend class RecordWriteStub; + }; + + enum OnNoNeedToInformIncrementalMarker { + kReturnOnNoNeedToInformIncrementalMarker, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker + }; + + void Generate(MacroAssembler* masm); + void GenerateIncremental(MacroAssembler* masm, Mode mode); + void CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode); + void InformIncrementalMarker(MacroAssembler* masm, Mode mode); + + Major MajorKey() { return RecordWrite; } + + int MinorKey() { + return ObjectBits::encode(object_.code()) | + ValueBits::encode(value_.code()) | + AddressBits::encode(address_.code()) | + RememberedSetActionBits::encode(remembered_set_action_) | + SaveFPRegsModeBits::encode(save_fp_regs_mode_); + } + + void Activate(Code* code) { + code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); + } + + class ObjectBits: public BitField<int, 0, 4> {}; + class ValueBits: public BitField<int, 4, 4> {}; + class AddressBits: public BitField<int, 8, 4> {}; + class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {}; + class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {}; + + Register object_; + Register value_; + Register address_; + RememberedSetAction remembered_set_action_; + SaveFPRegsMode save_fp_regs_mode_; + Label slow_; + RegisterAllocation regs_; +}; + + // Enter C code from generated RegExp code in a way that allows // the C code to fix the return address in case of a GC. // Currently only needed on ARM. @@ -622,14 +856,13 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - String* name, - Register scratch0); + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle<String> name, + Register scratch0); static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss, @@ -639,6 +872,8 @@ class StringDictionaryLookupStub: public CodeStub { Register r0, Register r1); + virtual bool SometimesSetsUpAFrame() { return false; } + private: static const int kInlinedProbes = 4; static const int kTotalProbes = 20; @@ -651,7 +886,7 @@ class StringDictionaryLookupStub: public CodeStub { StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; - Major MajorKey() { return StringDictionaryNegativeLookup; } + Major MajorKey() { return StringDictionaryLookup; } int MinorKey() { return LookupModeBits::encode(mode_); diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index bf748a9b6a..ce35b97c18 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -30,22 +30,367 @@ #if defined(V8_TARGET_ARCH_ARM) #include "codegen.h" +#include "macro-assembler.h" namespace v8 { namespace internal { +#define __ ACCESS_MASM(masm) + // ------------------------------------------------------------------------- // Platform-specific RuntimeCallHelper functions. void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterInternalFrame(); + masm->EnterFrame(StackFrame::INTERNAL); + ASSERT(!masm->has_frame()); + masm->set_has_frame(true); } void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveInternalFrame(); + masm->LeaveFrame(StackFrame::INTERNAL); + ASSERT(masm->has_frame()); + masm->set_has_frame(false); +} + + +// ------------------------------------------------------------------------- +// Code generators + +void ElementsTransitionGenerator::GenerateSmiOnlyToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : value + // -- r1 : key + // -- r2 : receiver + // -- lr : return address + // -- r3 : target map, scratch for subsequent call + // -- r4 : scratch (elements) + // ----------------------------------- + // Set transitioned map. + __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ RecordWriteField(r2, + HeapObject::kMapOffset, + r3, + r9, + kLRHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); +} + + +void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- r0 : value + // -- r1 : key + // -- r2 : receiver + // -- lr : return address + // -- r3 : target map, scratch for subsequent call + // -- r4 : scratch (elements) + // ----------------------------------- + Label loop, entry, convert_hole, gc_required; + bool vfp3_supported = CpuFeatures::IsSupported(VFP3); + __ push(lr); + + __ ldr(r4, FieldMemOperand(r2, JSObject::kElementsOffset)); + __ ldr(r5, FieldMemOperand(r4, FixedArray::kLengthOffset)); + // r4: source FixedArray + // r5: number of elements (smi-tagged) + + // Allocate new FixedDoubleArray. + __ mov(lr, Operand(FixedDoubleArray::kHeaderSize)); + __ add(lr, lr, Operand(r5, LSL, 2)); + __ AllocateInNewSpace(lr, r6, r7, r9, &gc_required, NO_ALLOCATION_FLAGS); + // r6: destination FixedDoubleArray, not tagged as heap object + // Set destination FixedDoubleArray's length and map. + __ LoadRoot(r9, Heap::kFixedDoubleArrayMapRootIndex); + __ str(r5, MemOperand(r6, FixedDoubleArray::kLengthOffset)); + __ str(r9, MemOperand(r6, HeapObject::kMapOffset)); + // Update receiver's map. + + __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ RecordWriteField(r2, + HeapObject::kMapOffset, + r3, + r9, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created FixedDoubleArray. + __ add(r3, r6, Operand(kHeapObjectTag)); + __ str(r3, FieldMemOperand(r2, JSObject::kElementsOffset)); + __ RecordWriteField(r2, + JSObject::kElementsOffset, + r3, + r9, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + + // Prepare for conversion loop. + __ add(r3, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ add(r7, r6, Operand(FixedDoubleArray::kHeaderSize)); + __ add(r6, r7, Operand(r5, LSL, 2)); + __ mov(r4, Operand(kHoleNanLower32)); + __ mov(r5, Operand(kHoleNanUpper32)); + // r3: begin of source FixedArray element fields, not tagged + // r4: kHoleNanLower32 + // r5: kHoleNanUpper32 + // r6: end of destination FixedDoubleArray, not tagged + // r7: begin of FixedDoubleArray element fields, not tagged + if (!vfp3_supported) __ Push(r1, r0); + + __ b(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ pop(lr); + __ b(fail); + + // Convert and copy elements. + __ bind(&loop); + __ ldr(r9, MemOperand(r3, 4, PostIndex)); + // r9: current element + __ UntagAndJumpIfNotSmi(r9, r9, &convert_hole); + + // Normal smi, convert to double and store. + if (vfp3_supported) { + CpuFeatures::Scope scope(VFP3); + __ vmov(s0, r9); + __ vcvt_f64_s32(d0, s0); + __ vstr(d0, r7, 0); + __ add(r7, r7, Operand(8)); + } else { + FloatingPointHelper::ConvertIntToDouble(masm, + r9, + FloatingPointHelper::kCoreRegisters, + d0, + r0, + r1, + lr, + s0); + __ Strd(r0, r1, MemOperand(r7, 8, PostIndex)); + } + __ b(&entry); + + // Hole found, store the-hole NaN. + __ bind(&convert_hole); + if (FLAG_debug_code) { + // Restore a "smi-untagged" heap object. + __ SmiTag(r9); + __ orr(r9, r9, Operand(1)); + __ CompareRoot(r9, Heap::kTheHoleValueRootIndex); + __ Assert(eq, "object found in smi-only array"); + } + __ Strd(r4, r5, MemOperand(r7, 8, PostIndex)); + + __ bind(&entry); + __ cmp(r7, r6); + __ b(lt, &loop); + + if (!vfp3_supported) __ Pop(r1, r0); + __ pop(lr); +} + + +void ElementsTransitionGenerator::GenerateDoubleToObject( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- r0 : value + // -- r1 : key + // -- r2 : receiver + // -- lr : return address + // -- r3 : target map, scratch for subsequent call + // -- r4 : scratch (elements) + // ----------------------------------- + Label entry, loop, convert_hole, gc_required; + + __ push(lr); + __ ldr(r4, FieldMemOperand(r2, JSObject::kElementsOffset)); + __ Push(r3, r2, r1, r0); + __ ldr(r5, FieldMemOperand(r4, FixedArray::kLengthOffset)); + // r4: source FixedDoubleArray + // r5: number of elements (smi-tagged) + + // Allocate new FixedArray. + __ mov(r0, Operand(FixedDoubleArray::kHeaderSize)); + __ add(r0, r0, Operand(r5, LSL, 1)); + __ AllocateInNewSpace(r0, r6, r7, r9, &gc_required, NO_ALLOCATION_FLAGS); + // r6: destination FixedArray, not tagged as heap object + // Set destination FixedDoubleArray's length and map. + __ LoadRoot(r9, Heap::kFixedArrayMapRootIndex); + __ str(r5, MemOperand(r6, FixedDoubleArray::kLengthOffset)); + __ str(r9, MemOperand(r6, HeapObject::kMapOffset)); + + // Prepare for conversion loop. + __ add(r4, r4, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag + 4)); + __ add(r3, r6, Operand(FixedArray::kHeaderSize)); + __ add(r6, r6, Operand(kHeapObjectTag)); + __ add(r5, r3, Operand(r5, LSL, 1)); + __ LoadRoot(r7, Heap::kTheHoleValueRootIndex); + __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex); + // Using offsetted addresses in r4 to fully take advantage of post-indexing. + // r3: begin of destination FixedArray element fields, not tagged + // r4: begin of source FixedDoubleArray element fields, not tagged, +4 + // r5: end of destination FixedArray, not tagged + // r6: destination FixedArray + // r7: the-hole pointer + // r9: heap number map + __ b(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ Pop(r3, r2, r1, r0); + __ pop(lr); + __ b(fail); + + __ bind(&loop); + __ ldr(r1, MemOperand(r4, 8, PostIndex)); + // lr: current element's upper 32 bit + // r4: address of next element's upper 32 bit + __ cmp(r1, Operand(kHoleNanUpper32)); + __ b(eq, &convert_hole); + + // Non-hole double, copy value into a heap number. + __ AllocateHeapNumber(r2, r0, lr, r9, &gc_required); + // r2: new heap number + __ ldr(r0, MemOperand(r4, 12, NegOffset)); + __ Strd(r0, r1, FieldMemOperand(r2, HeapNumber::kValueOffset)); + __ mov(r0, r3); + __ str(r2, MemOperand(r3, 4, PostIndex)); + __ RecordWrite(r6, + r0, + r2, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ b(&entry); + + // Replace the-hole NaN with the-hole pointer. + __ bind(&convert_hole); + __ str(r7, MemOperand(r3, 4, PostIndex)); + + __ bind(&entry); + __ cmp(r3, r5); + __ b(lt, &loop); + + __ Pop(r3, r2, r1, r0); + // Update receiver's map. + __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ RecordWriteField(r2, + HeapObject::kMapOffset, + r3, + r9, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created and filled FixedArray. + __ str(r6, FieldMemOperand(r2, JSObject::kElementsOffset)); + __ RecordWriteField(r2, + JSObject::kElementsOffset, + r6, + r9, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ pop(lr); +} + + +void StringCharLoadGenerator::Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime) { + // Fetch the instance type of the receiver into result register. + __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); + __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for indirect strings. + Label check_sequential; + __ tst(result, Operand(kIsIndirectStringMask)); + __ b(eq, &check_sequential); + + // Dispatch on the indirect string shape: slice or cons. + Label cons_string; + __ tst(result, Operand(kSlicedNotConsMask)); + __ b(eq, &cons_string); + + // Handle slices. + Label indirect_string_loaded; + __ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset)); + __ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset)); + __ add(index, index, Operand(result, ASR, kSmiTagSize)); + __ jmp(&indirect_string_loaded); + + // Handle cons strings. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ bind(&cons_string); + __ ldr(result, FieldMemOperand(string, ConsString::kSecondOffset)); + __ CompareRoot(result, Heap::kEmptyStringRootIndex); + __ b(ne, call_runtime); + // Get the first of the two strings and load its instance type. + __ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset)); + + __ bind(&indirect_string_loaded); + __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); + __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); + + // Distinguish sequential and external strings. Only these two string + // representations can reach here (slices and flat cons strings have been + // reduced to the underlying sequential or external string). + Label external_string, check_encoding; + __ bind(&check_sequential); + STATIC_ASSERT(kSeqStringTag == 0); + __ tst(result, Operand(kStringRepresentationMask)); + __ b(ne, &external_string); + + // Prepare sequential strings + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ add(string, + string, + Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + __ jmp(&check_encoding); + + // Handle external strings. + __ bind(&external_string); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ tst(result, Operand(kIsIndirectStringMask)); + __ Assert(eq, "external string expected, but not found"); + } + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ tst(result, Operand(kShortExternalStringMask)); + __ b(ne, call_runtime); + __ ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset)); + + Label ascii, done; + __ bind(&check_encoding); + STATIC_ASSERT(kTwoByteStringTag == 0); + __ tst(result, Operand(kStringEncodingMask)); + __ b(ne, &ascii); + // Two-byte string. + __ ldrh(result, MemOperand(string, index, LSL, 1)); + __ jmp(&done); + __ bind(&ascii); + // Ascii string. + __ ldrb(result, MemOperand(string, index)); + __ bind(&done); } +#undef __ } } // namespace v8::internal diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index d27982abac..c340e6b108 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -29,7 +29,6 @@ #define V8_ARM_CODEGEN_ARM_H_ #include "ast.h" -#include "code-stubs-arm.h" #include "ic-inl.h" namespace v8 { @@ -69,21 +68,26 @@ class CodeGenerator: public AstVisitor { int pos, bool right_here = false); - // Constants related to patching of inlined load/store. - static int GetInlinedKeyedLoadInstructionsAfterPatch() { - return FLAG_debug_code ? 32 : 13; - } - static const int kInlinedKeyedStoreInstructionsAfterPatch = 8; - static int GetInlinedNamedStoreInstructionsAfterPatch() { - ASSERT(Isolate::Current()->inlined_write_barrier_size() != -1); - return Isolate::Current()->inlined_write_barrier_size() + 4; - } - private: DISALLOW_COPY_AND_ASSIGN(CodeGenerator); }; +class StringCharLoadGenerator : public AllStatic { + public: + // Generates the code for handling different string types and loading the + // indexed character into |result|. We expect |index| as untagged input and + // |result| as untagged output. + static void Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime); + + private: + DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); +}; + } } // namespace v8::internal #endif // V8_ARM_CODEGEN_ARM_H_ diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 823c6ff7e1..e767001e1d 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -87,22 +87,21 @@ namespace v8 { namespace internal { // Constant pool marker. -static const int kConstantPoolMarkerMask = 0xffe00000; -static const int kConstantPoolMarker = 0x0c000000; -static const int kConstantPoolLengthMask = 0x001ffff; +const int kConstantPoolMarkerMask = 0xffe00000; +const int kConstantPoolMarker = 0x0c000000; +const int kConstantPoolLengthMask = 0x001ffff; // Number of registers in normal ARM mode. -static const int kNumRegisters = 16; +const int kNumRegisters = 16; // VFP support. -static const int kNumVFPSingleRegisters = 32; -static const int kNumVFPDoubleRegisters = 16; -static const int kNumVFPRegisters = - kNumVFPSingleRegisters + kNumVFPDoubleRegisters; +const int kNumVFPSingleRegisters = 32; +const int kNumVFPDoubleRegisters = 16; +const int kNumVFPRegisters = kNumVFPSingleRegisters + kNumVFPDoubleRegisters; // PC is register 15. -static const int kPCRegister = 15; -static const int kNoRegister = -1; +const int kPCRegister = 15; +const int kNoRegister = -1; // ----------------------------------------------------------------------------- // Conditions. @@ -371,9 +370,9 @@ enum SoftwareInterruptCodes { // stop kStopCode = 1 << 23 }; -static const uint32_t kStopCodeMask = kStopCode - 1; -static const uint32_t kMaxStopCode = kStopCode - 1; -static const int32_t kDefaultStopCode = -1; +const uint32_t kStopCodeMask = kStopCode - 1; +const uint32_t kMaxStopCode = kStopCode - 1; +const int32_t kDefaultStopCode = -1; // Type of VFP register. Determines register encoding. @@ -391,17 +390,17 @@ enum VFPConversionMode { // This mask does not include the "inexact" or "input denormal" cumulative // exceptions flags, because we usually don't want to check for it. -static const uint32_t kVFPExceptionMask = 0xf; -static const uint32_t kVFPInvalidOpExceptionBit = 1 << 0; -static const uint32_t kVFPOverflowExceptionBit = 1 << 2; -static const uint32_t kVFPUnderflowExceptionBit = 1 << 3; -static const uint32_t kVFPInexactExceptionBit = 1 << 4; -static const uint32_t kVFPFlushToZeroMask = 1 << 24; +const uint32_t kVFPExceptionMask = 0xf; +const uint32_t kVFPInvalidOpExceptionBit = 1 << 0; +const uint32_t kVFPOverflowExceptionBit = 1 << 2; +const uint32_t kVFPUnderflowExceptionBit = 1 << 3; +const uint32_t kVFPInexactExceptionBit = 1 << 4; +const uint32_t kVFPFlushToZeroMask = 1 << 24; -static const uint32_t kVFPNConditionFlagBit = 1 << 31; -static const uint32_t kVFPZConditionFlagBit = 1 << 30; -static const uint32_t kVFPCConditionFlagBit = 1 << 29; -static const uint32_t kVFPVConditionFlagBit = 1 << 28; +const uint32_t kVFPNConditionFlagBit = 1 << 31; +const uint32_t kVFPZConditionFlagBit = 1 << 30; +const uint32_t kVFPCConditionFlagBit = 1 << 29; +const uint32_t kVFPVConditionFlagBit = 1 << 28; // VFP rounding modes. See ARM DDI 0406B Page A2-29. @@ -418,7 +417,7 @@ enum VFPRoundingMode { kRoundToZero = RZ }; -static const uint32_t kVFPRoundingModeMask = 3 << 22; +const uint32_t kVFPRoundingModeMask = 3 << 22; enum CheckForInexactConversion { kCheckForInexactConversion, @@ -574,13 +573,13 @@ class Instruction { // The naming of these accessor corresponds to figure A3-1. // // Two kind of accessors are declared: - // - <Name>Field() will return the raw field, ie the field's bits at their + // - <Name>Field() will return the raw field, i.e. the field's bits at their // original place in the instruction encoding. - // eg. if instr is the 'addgt r0, r1, r2' instruction, encoded as 0xC0810002 - // ConditionField(instr) will return 0xC0000000. + // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as + // 0xC0810002 ConditionField(instr) will return 0xC0000000. // - <Name>Value() will return the field value, shifted back to bit 0. - // eg. if instr is the 'addgt r0, r1, r2' instruction, encoded as 0xC0810002 - // ConditionField(instr) will return 0xC. + // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as + // 0xC0810002 ConditionField(instr) will return 0xC. // Generally applicable fields diff --git a/deps/v8/src/arm/cpu-arm.cc b/deps/v8/src/arm/cpu-arm.cc index 51cfeb6c87..7b08ed8c2f 100644 --- a/deps/v8/src/arm/cpu-arm.cc +++ b/deps/v8/src/arm/cpu-arm.cc @@ -41,7 +41,7 @@ namespace v8 { namespace internal { -void CPU::Setup() { +void CPU::SetUp() { CpuFeatures::Probe(); } diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index 07a22722c8..96139a2597 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -132,55 +132,57 @@ void BreakLocationIterator::ClearDebugBreakAtSlot() { static void Generate_DebugBreakCallHelper(MacroAssembler* masm, RegList object_regs, RegList non_object_regs) { - __ EnterInternalFrame(); - - // Store the registers containing live values on the expression stack to - // make sure that these are correctly updated during GC. Non object values - // are stored as a smi causing it to be untouched by GC. - ASSERT((object_regs & ~kJSCallerSaved) == 0); - ASSERT((non_object_regs & ~kJSCallerSaved) == 0); - ASSERT((object_regs & non_object_regs) == 0); - if ((object_regs | non_object_regs) != 0) { - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if ((non_object_regs & (1 << r)) != 0) { - if (FLAG_debug_code) { - __ tst(reg, Operand(0xc0000000)); - __ Assert(eq, "Unable to encode value as smi"); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Store the registers containing live values on the expression stack to + // make sure that these are correctly updated during GC. Non object values + // are stored as a smi causing it to be untouched by GC. + ASSERT((object_regs & ~kJSCallerSaved) == 0); + ASSERT((non_object_regs & ~kJSCallerSaved) == 0); + ASSERT((object_regs & non_object_regs) == 0); + if ((object_regs | non_object_regs) != 0) { + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if ((non_object_regs & (1 << r)) != 0) { + if (FLAG_debug_code) { + __ tst(reg, Operand(0xc0000000)); + __ Assert(eq, "Unable to encode value as smi"); + } + __ mov(reg, Operand(reg, LSL, kSmiTagSize)); } - __ mov(reg, Operand(reg, LSL, kSmiTagSize)); } + __ stm(db_w, sp, object_regs | non_object_regs); } - __ stm(db_w, sp, object_regs | non_object_regs); - } #ifdef DEBUG - __ RecordComment("// Calling from debug break to runtime - come in - over"); + __ RecordComment("// Calling from debug break to runtime - come in - over"); #endif - __ mov(r0, Operand(0, RelocInfo::NONE)); // no arguments - __ mov(r1, Operand(ExternalReference::debug_break(masm->isolate()))); - - CEntryStub ceb(1); - __ CallStub(&ceb); - - // Restore the register values from the expression stack. - if ((object_regs | non_object_regs) != 0) { - __ ldm(ia_w, sp, object_regs | non_object_regs); - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if ((non_object_regs & (1 << r)) != 0) { - __ mov(reg, Operand(reg, LSR, kSmiTagSize)); - } - if (FLAG_debug_code && - (((object_regs |non_object_regs) & (1 << r)) == 0)) { - __ mov(reg, Operand(kDebugZapValue)); + __ mov(r0, Operand(0, RelocInfo::NONE)); // no arguments + __ mov(r1, Operand(ExternalReference::debug_break(masm->isolate()))); + + CEntryStub ceb(1); + __ CallStub(&ceb); + + // Restore the register values from the expression stack. + if ((object_regs | non_object_regs) != 0) { + __ ldm(ia_w, sp, object_regs | non_object_regs); + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if ((non_object_regs & (1 << r)) != 0) { + __ mov(reg, Operand(reg, LSR, kSmiTagSize)); + } + if (FLAG_debug_code && + (((object_regs |non_object_regs) & (1 << r)) == 0)) { + __ mov(reg, Operand(kDebugZapValue)); + } } } - } - __ LeaveInternalFrame(); + // Leave the internal frame. + } // Now that the break point has been handled, resume normal execution by // jumping to the target address intended by the caller and that was @@ -249,14 +251,6 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { - // Calling convention for construct call (from builtins-arm.cc) - // -- r0 : number of arguments (not smi) - // -- r1 : constructor function - Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit()); -} - - void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that r0 is TOS which // is an object - this is not generally the case so this should be used with @@ -265,11 +259,43 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { +void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-arm.cc). // ----------- S t a t e ------------- - // No registers used on entry. + // -- r1 : function // ----------------------------------- - Generate_DebugBreakCallHelper(masm, 0, 0); + Generate_DebugBreakCallHelper(masm, r1.bit(), 0); +} + + +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-arm.cc). + // ----------- S t a t e ------------- + // -- r1 : function + // -- r2 : cache cell for call target + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit(), 0); +} + + +void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { + // Calling convention for CallConstructStub (from code-stubs-arm.cc) + // ----------- S t a t e ------------- + // -- r0 : number of arguments (not smi) + // -- r1 : constructor function + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit()); +} + + +void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { + // Calling convention for CallConstructStub (from code-stubs-arm.cc) + // ----------- S t a t e ------------- + // -- r0 : number of arguments (not smi) + // -- r1 : constructor function + // -- r2 : cache cell for call target + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit(), r0.bit()); } diff --git a/deps/v8/src/arm/deoptimizer-arm.cc b/deps/v8/src/arm/deoptimizer-arm.cc index d4f251f760..76d89541c5 100644 --- a/deps/v8/src/arm/deoptimizer-arm.cc +++ b/deps/v8/src/arm/deoptimizer-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -79,18 +79,24 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { ASSERT(prev_call_address == NULL || call_address >= prev_call_address + patch_size()); ASSERT(call_address + patch_size() <= code->instruction_end()); - #ifdef DEBUG prev_call_address = call_address; #endif } + Isolate* isolate = code->GetIsolate(); + // Add the deoptimizing code to the list. DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); - DeoptimizerData* data = code->GetIsolate()->deoptimizer_data(); + DeoptimizerData* data = isolate->deoptimizer_data(); node->set_next(data->deoptimizing_code_list_); data->deoptimizing_code_list_ = node; + // We might be in the middle of incremental marking with compaction. + // Tell collector to treat this code object in a special way and + // ignore all slots that might have been recorded on it. + isolate->heap()->mark_compact_collector()->InvalidateCode(code); + // Set the code for the function to non-optimized version. function->ReplaceCode(function->shared()->code()); @@ -102,7 +108,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { } -void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, +void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { const int kInstrSize = Assembler::kInstrSize; @@ -137,10 +144,14 @@ void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, reinterpret_cast<uint32_t>(check_code->entry())); Memory::uint32_at(stack_check_address_pointer) = reinterpret_cast<uint32_t>(replacement_code->entry()); + + unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, pc_after - 2 * kInstrSize, replacement_code); } -void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, +void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { const int kInstrSize = Assembler::kInstrSize; @@ -161,6 +172,9 @@ void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, reinterpret_cast<uint32_t>(replacement_code->entry())); Memory::uint32_at(stack_check_address_pointer) = reinterpret_cast<uint32_t>(check_code->entry()); + + check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, pc_after - 2 * kInstrSize, check_code); } @@ -197,12 +211,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() { ASSERT(Translation::BEGIN == opcode); USE(opcode); int count = iterator.Next(); + iterator.Skip(1); // Drop JS frame count. ASSERT(count == 1); USE(count); opcode = static_cast<Translation::Opcode>(iterator.Next()); USE(opcode); - ASSERT(Translation::FRAME == opcode); + ASSERT(Translation::JS_FRAME == opcode); unsigned node_id = iterator.Next(); USE(node_id); ASSERT(node_id == ast_id); @@ -238,9 +253,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_ = new FrameDescription*[1]; output_[0] = new(output_frame_size) FrameDescription( output_frame_size, function_); -#ifdef DEBUG - output_[0]->SetKind(Code::OPTIMIZED_FUNCTION); -#endif + output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); // Clear the incoming parameters in the optimized frame to avoid // confusing the garbage collector. @@ -305,7 +318,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_[0] = input_; output_[0]->SetPc(reinterpret_cast<uint32_t>(from_)); } else { - // Setup the frame pointer and the context pointer. + // Set up the frame pointer and the context pointer. output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code())); output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code())); @@ -328,15 +341,115 @@ void Deoptimizer::DoComputeOsrOutputFrame() { } +void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; + unsigned input_frame_size = input_->GetFrameSize(); + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, function); + output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR); + + // Arguments adaptor can not be topmost or bottommost. + ASSERT(frame_index > 0 && frame_index < output_count_ - 1); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address of the frame is computed from the previous + // frame's top and this frame's size. + uint32_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + unsigned input_offset = input_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + input_offset -= (parameter_count * kPointerSize); + + // Read caller's PC from the previous frame. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t callers_pc = output_[frame_index - 1]->GetPc(); + output_frame->SetFrameSlot(output_offset, callers_pc); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n", + top_address + output_offset, output_offset, callers_pc); + } + + // Read caller's FP from the previous frame, and set this frame's FP. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t value = output_[frame_index - 1]->GetFp(); + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + output_frame->SetFp(fp_value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n", + fp_value, output_offset, value); + } + + // A marker value is used in place of the context. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t context = reinterpret_cast<intptr_t>( + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); + output_frame->SetFrameSlot(output_offset, context); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n", + top_address + output_offset, output_offset, context); + } + + // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(function); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* adaptor_trampoline = + builtins->builtin(Builtins::kArgumentsAdaptorTrampoline); + uint32_t pc = reinterpret_cast<uint32_t>( + adaptor_trampoline->instruction_start() + + isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value()); + output_frame->SetPc(pc); +} + + // This code is very similar to ia32 code, but relies on register names (fp, sp) // and how the frame is laid out. -void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, - int frame_index) { +void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, + int frame_index) { // Read the ast node id, function, and frame height for this output frame. - Translation::Opcode opcode = - static_cast<Translation::Opcode>(iterator->Next()); - USE(opcode); - ASSERT(Translation::FRAME == opcode); int node_id = iterator->Next(); JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); unsigned height = iterator->Next(); @@ -356,9 +469,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, // Allocate and store the output frame description. FrameDescription* output_frame = new(output_frame_size) FrameDescription(output_frame_size, function); -#ifdef DEBUG - output_frame->SetKind(Code::FUNCTION); -#endif + output_frame->SetFrameType(StackFrame::JAVA_SCRIPT); bool is_bottommost = (0 == frame_index); bool is_topmost = (output_count_ - 1 == frame_index); @@ -600,7 +711,10 @@ void Deoptimizer::EntryGenerator::Generate() { __ mov(r5, Operand(ExternalReference::isolate_address())); __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate. // Call Deoptimizer::New(). - __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + } // Preserve "deoptimizer" object in register r0 and get the input // frame descriptor pointer to r1 (deoptimizer->input_); @@ -654,8 +768,11 @@ void Deoptimizer::EntryGenerator::Generate() { // r0: deoptimizer object; r1: scratch. __ PrepareCallCFunction(1, r1); // Call Deoptimizer::ComputeOutputFrames(). - __ CallCFunction( - ExternalReference::compute_output_frames_function(isolate), 1); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction( + ExternalReference::compute_output_frames_function(isolate), 1); + } __ pop(r0); // Restore deoptimizer object (class Deoptimizer). // Replace the current (input) frame with the output frames. @@ -671,7 +788,6 @@ void Deoptimizer::EntryGenerator::Generate() { __ ldr(r3, MemOperand(r2, FrameDescription::frame_size_offset())); __ bind(&inner_push_loop); __ sub(r3, r3, Operand(sizeof(uint32_t))); - // __ add(r6, r2, Operand(r3, LSL, 1)); __ add(r6, r2, Operand(r3)); __ ldr(r7, MemOperand(r6, FrameDescription::frame_content_offset())); __ push(r7); diff --git a/deps/v8/src/arm/disasm-arm.cc b/deps/v8/src/arm/disasm-arm.cc index 603b3cfd9c..96a7d3ce6b 100644 --- a/deps/v8/src/arm/disasm-arm.cc +++ b/deps/v8/src/arm/disasm-arm.cc @@ -473,7 +473,7 @@ int Decoder::FormatOption(Instruction* instr, const char* format) { return 1; } case 'i': { // 'i: immediate value from adjacent bits. - // Expects tokens in the form imm%02d@%02d, ie. imm05@07, imm10@16 + // Expects tokens in the form imm%02d@%02d, i.e. imm05@07, imm10@16 int width = (format[3] - '0') * 10 + (format[4] - '0'); int lsb = (format[6] - '0') * 10 + (format[7] - '0'); @@ -662,6 +662,15 @@ void Decoder::Format(Instruction* instr, const char* format) { } +// The disassembler may end up decoding data inlined in the code. We do not want +// it to crash if the data does not ressemble any known instruction. +#define VERIFY(condition) \ +if(!(condition)) { \ + Unknown(instr); \ + return; \ +} + + // For currently unimplemented decodings the disassembler calls Unknown(instr) // which will just print "unknown" of the instruction bits. void Decoder::Unknown(Instruction* instr) { @@ -947,13 +956,13 @@ void Decoder::DecodeType2(Instruction* instr) { void Decoder::DecodeType3(Instruction* instr) { switch (instr->PUField()) { case da_x: { - ASSERT(!instr->HasW()); + VERIFY(!instr->HasW()); Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); break; } case ia_x: { if (instr->HasW()) { - ASSERT(instr->Bits(5, 4) == 0x1); + VERIFY(instr->Bits(5, 4) == 0x1); if (instr->Bit(22) == 0x1) { Format(instr, "usat 'rd, #'imm05@16, 'rm'shift_sat"); } else { @@ -1074,8 +1083,8 @@ int Decoder::DecodeType7(Instruction* instr) { // vmsr // Dd = vsqrt(Dm) void Decoder::DecodeTypeVFP(Instruction* instr) { - ASSERT((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); - ASSERT(instr->Bits(11, 9) == 0x5); + VERIFY((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); + VERIFY(instr->Bits(11, 9) == 0x5); if (instr->Bit(4) == 0) { if (instr->Opc1Value() == 0x7) { @@ -1166,7 +1175,7 @@ void Decoder::DecodeTypeVFP(Instruction* instr) { void Decoder::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( Instruction* instr) { - ASSERT((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && + VERIFY((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)); bool to_arm_register = (instr->VLValue() == 0x1); @@ -1180,8 +1189,8 @@ void Decoder::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( void Decoder::DecodeVCMP(Instruction* instr) { - ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); - ASSERT(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && + VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); + VERIFY(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)); // Comparison. @@ -1203,8 +1212,8 @@ void Decoder::DecodeVCMP(Instruction* instr) { void Decoder::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { - ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); - ASSERT((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); + VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); + VERIFY((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); bool double_to_single = (instr->SzValue() == 1); @@ -1217,8 +1226,8 @@ void Decoder::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { void Decoder::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { - ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); - ASSERT(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || + VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); + VERIFY(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); bool to_integer = (instr->Bit(18) == 1); @@ -1265,7 +1274,7 @@ void Decoder::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { // Ddst = MEM(Rbase + 4*offset). // MEM(Rbase + 4*offset) = Dsrc. void Decoder::DecodeType6CoprocessorIns(Instruction* instr) { - ASSERT(instr->TypeValue() == 6); + VERIFY(instr->TypeValue() == 6); if (instr->CoprocessorValue() == 0xA) { switch (instr->OpcodeValue()) { @@ -1347,6 +1356,7 @@ void Decoder::DecodeType6CoprocessorIns(Instruction* instr) { } } +#undef VERIFIY bool Decoder::IsConstantPoolAt(byte* instr_ptr) { int instruction_bits = *(reinterpret_cast<int*>(instr_ptr)); diff --git a/deps/v8/src/arm/frames-arm.h b/deps/v8/src/arm/frames-arm.h index 26bbd82d00..a10acd0687 100644 --- a/deps/v8/src/arm/frames-arm.h +++ b/deps/v8/src/arm/frames-arm.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -35,22 +35,22 @@ namespace internal { // The ARM ABI does not specify the usage of register r9, which may be reserved // as the static base or thread register on some platforms, in which case we // leave it alone. Adjust the value of kR9Available accordingly: -static const int kR9Available = 1; // 1 if available to us, 0 if reserved +const int kR9Available = 1; // 1 if available to us, 0 if reserved // Register list in load/store instructions // Note that the bit values must match those used in actual instruction encoding -static const int kNumRegs = 16; +const int kNumRegs = 16; // Caller-saved/arguments registers -static const RegList kJSCallerSaved = +const RegList kJSCallerSaved = 1 << 0 | // r0 a1 1 << 1 | // r1 a2 1 << 2 | // r2 a3 1 << 3; // r3 a4 -static const int kNumJSCallerSaved = 4; +const int kNumJSCallerSaved = 4; typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; @@ -60,7 +60,7 @@ int JSCallerSavedCode(int n); // Callee-saved registers preserved when switching from C to JavaScript -static const RegList kCalleeSaved = +const RegList kCalleeSaved = 1 << 4 | // r4 v1 1 << 5 | // r5 v2 1 << 6 | // r6 v3 @@ -70,36 +70,45 @@ static const RegList kCalleeSaved = 1 << 10 | // r10 v7 1 << 11; // r11 v8 (fp in JavaScript code) -static const int kNumCalleeSaved = 7 + kR9Available; +// When calling into C++ (only for C++ calls that can't cause a GC). +// The call code will take care of lr, fp, etc. +const RegList kCallerSaved = + 1 << 0 | // r0 + 1 << 1 | // r1 + 1 << 2 | // r2 + 1 << 3 | // r3 + 1 << 9; // r9 + + +const int kNumCalleeSaved = 7 + kR9Available; // Double registers d8 to d15 are callee-saved. -static const int kNumDoubleCalleeSaved = 8; +const int kNumDoubleCalleeSaved = 8; // Number of registers for which space is reserved in safepoints. Must be a // multiple of 8. // TODO(regis): Only 8 registers may actually be sufficient. Revisit. -static const int kNumSafepointRegisters = 16; +const int kNumSafepointRegisters = 16; // Define the list of registers actually saved at safepoints. // Note that the number of saved registers may be smaller than the reserved // space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. -static const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; -static const int kNumSafepointSavedRegisters = - kNumJSCallerSaved + kNumCalleeSaved; +const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; +const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved; // ---------------------------------------------------- class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kStateOffset = 1 * kPointerSize; - static const int kContextOffset = 2 * kPointerSize; - static const int kFPOffset = 3 * kPointerSize; - static const int kPCOffset = 4 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kCodeOffset = 1 * kPointerSize; + static const int kStateOffset = 2 * kPointerSize; + static const int kContextOffset = 3 * kPointerSize; + static const int kFPOffset = 4 * kPointerSize; - static const int kSize = kPCOffset + kPointerSize; + static const int kSize = kFPOffset + kPointerSize; }; @@ -127,6 +136,9 @@ class ExitFrameConstants : public AllStatic { class StandardFrameConstants : public AllStatic { public: + // Fixed part of the frame consists of return address, caller fp, + // context and function. + static const int kFixedFrameSize = 4 * kPointerSize; static const int kExpressionsOffset = -3 * kPointerSize; static const int kMarkerOffset = -2 * kPointerSize; static const int kContextOffset = -1 * kPointerSize; @@ -152,6 +164,8 @@ class JavaScriptFrameConstants : public AllStatic { class ArgumentsAdaptorFrameConstants : public AllStatic { public: static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset; + static const int kFrameSize = + StandardFrameConstants::kFixedFrameSize + kPointerSize; }; diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 50ed8b1da7..2adddef111 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -39,6 +39,7 @@ #include "stub-cache.h" #include "arm/code-stubs-arm.h" +#include "arm/macro-assembler-arm.h" namespace v8 { namespace internal { @@ -46,11 +47,6 @@ namespace internal { #define __ ACCESS_MASM(masm_) -static unsigned GetPropertyId(Property* property) { - return property->id(); -} - - // A patch site is a location in the code which it is possible to patch. This // class has a number of methods to emit the code which is patchable and the // method EmitPatchInfo to record a marker back to the patchable code. This @@ -119,7 +115,7 @@ class JumpPatchSite BASE_EMBEDDED { // function. // // The live registers are: -// o r1: the JS function object being called (ie, ourselves) +// o r1: the JS function object being called (i.e., ourselves) // o cp: our context // o fp: our caller's frame pointer // o sp: stack pointer @@ -131,6 +127,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ASSERT(info_ == NULL); info_ = info; scope_ = info->scope(); + handler_table_ = + isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); @@ -141,11 +139,32 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif + // We can optionally optimize based on counters rather than statistical + // sampling. + if (info->ShouldSelfOptimize()) { + if (FLAG_trace_opt) { + PrintF("[adding self-optimization header to %s]\n", + *info->function()->debug_name()->ToCString()); + } + MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell( + Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt)); + JSGlobalPropertyCell* cell; + if (maybe_cell->To(&cell)) { + __ mov(r2, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ ldr(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset)); + __ sub(r3, r3, Operand(Smi::FromInt(1)), SetCC); + __ str(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset)); + Handle<Code> compile_stub( + isolate()->builtins()->builtin(Builtins::kLazyRecompile)); + __ Jump(compile_stub, RelocInfo::CODE_TARGET, eq); + } + } + // Strict mode functions and builtins need to replace the receiver // with undefined when called as functions (without an explicit // receiver object). r5 is zero for method calls and non-zero for // function calls. - if (info->is_strict_mode() || info->is_native()) { + if (!info->is_classic_mode() || info->is_native()) { Label ok; __ cmp(r5, Operand(0)); __ b(eq, &ok); @@ -155,6 +174,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { __ bind(&ok); } + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + int locals_count = info->scope()->num_stack_slots(); __ Push(lr, fp, cp, r1); @@ -200,13 +224,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Load parameter from stack. __ ldr(r0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ mov(r1, Operand(Context::SlotOffset(var->index()))); - __ str(r0, MemOperand(cp, r1)); - // Update the write barrier. This clobbers all involved - // registers, so we have to use two more registers to avoid - // clobbering cp. - __ mov(r2, Operand(cp)); - __ RecordWrite(r2, Operand(r1), r3, r0); + MemOperand target = ContextOperand(cp, var->index()); + __ str(r0, target); + + // Update the write barrier. + __ RecordWriteContextSlot( + cp, target.offset(), r0, r3, kLRHasBeenSaved, kDontSaveFPRegs); } } } @@ -234,7 +257,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // The stub will rewrite receiever and parameter count if the previous // stack frame was an arguments adapter frame. ArgumentsAccessStub::Type type; - if (is_strict_mode()) { + if (!is_classic_mode()) { type = ArgumentsAccessStub::NEW_STRICT; } else if (function()->has_duplicate_parameters()) { type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; @@ -263,8 +286,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - int ignored = 0; - EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); + VariableProxy* proxy = scope()->function(); + ASSERT(proxy->var()->mode() == CONST || + proxy->var()->mode() == CONST_HARMONY); + ASSERT(proxy->var()->location() != Variable::UNALLOCATED); + EmitDeclaration(proxy, proxy->var()->mode(), NULL); } VisitDeclarations(scope()->declarations()); } @@ -391,7 +417,7 @@ void FullCodeGenerator::TestContext::Plug(Variable* var) const { ASSERT(var->IsStackAllocated() || var->IsContextSlot()); // For simplicity we always test the accumulator register. codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -414,7 +440,7 @@ void FullCodeGenerator::StackValueContext::Plug( void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -449,7 +475,7 @@ void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -508,7 +534,7 @@ void FullCodeGenerator::TestContext::DropAndPlug(int count, // For simplicity we always test the accumulator register. __ Drop(count); __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -575,7 +601,7 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const { void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -665,17 +691,20 @@ void FullCodeGenerator::SetVar(Variable* var, ASSERT(!scratch1.is(src)); MemOperand location = VarOperand(var, scratch0); __ str(src, location); + // Emit the write barrier code if the location is in the heap. if (var->IsContextSlot()) { - __ RecordWrite(scratch0, - Operand(Context::SlotOffset(var->index())), - scratch1, - src); + __ RecordWriteContextSlot(scratch0, + location.offset(), + src, + scratch1, + kLRHasBeenSaved, + kDontSaveFPRegs); } } -void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, +void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false) { @@ -686,13 +715,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, Label skip; if (should_normalize) __ b(&skip); - - ForwardBailoutStack* current = forward_bailout_stack_; - while (current != NULL) { - PrepareForBailout(current->expr(), state); - current = current->parent(); - } - + PrepareForBailout(expr, TOS_REG); if (should_normalize) { __ LoadRoot(ip, Heap::kTrueValueRootIndex); __ cmp(r0, ip); @@ -703,16 +726,17 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function, - int* global_count) { + VariableMode mode, + FunctionLiteral* function) { // If it was not possible to allocate the variable at compile time, we // need to "declare" it at runtime to make sure it actually exists in the // local context. Variable* variable = proxy->var(); + bool binding_needs_init = (function == NULL) && + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (variable->location()) { case Variable::UNALLOCATED: - ++(*global_count); + ++global_count_; break; case Variable::PARAMETER: @@ -721,7 +745,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ str(result_register(), StackOperand(variable)); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ str(ip, StackOperand(variable)); @@ -746,10 +770,16 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ str(result_register(), ContextOperand(cp, variable->index())); int offset = Context::SlotOffset(variable->index()); // We know that we have written a function, which is not a smi. - __ mov(r1, Operand(cp)); - __ RecordWrite(r1, Operand(offset), r2, result_register()); + __ RecordWriteContextSlot(cp, + offset, + result_register(), + r2, + kLRHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ str(ip, ContextOperand(cp, variable->index())); @@ -761,11 +791,13 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, case Variable::LOOKUP: { Comment cmnt(masm_, "[ Declaration"); __ mov(r2, Operand(variable->name())); - // Declaration nodes are always introduced in one of three modes. - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; + // Declaration nodes are always introduced in one of four modes. + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); + PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY) + ? READ_ONLY : NONE; __ mov(r1, Operand(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -775,7 +807,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ Push(cp, r2, r1); // Push initial value for function declaration. VisitForStackValue(function); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); __ Push(cp, r2, r1, r0); } else { @@ -789,9 +821,6 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } - - void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. // The context is the first argument. @@ -917,11 +946,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&done_convert); __ push(r0); + // Check for proxies. + Label call_runtime; + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CompareObjectType(r0, r1, r1, LAST_JS_PROXY_TYPE); + __ b(le, &call_runtime); + // Check cache validity in generated code. This is a fast case for // the JSObject::IsSimpleEnum cache validity checks. If we cannot // guarantee cache validity, call the runtime system to check cache // validity or get the property names in a fixed array. - Label next, call_runtime; + Label next; // Preload a couple of values used in the loop. Register empty_fixed_array_value = r6; __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); @@ -991,7 +1026,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); - // Setup the four remaining stack slots. + // Set up the four remaining stack slots. __ push(r0); // Map. __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); __ mov(r0, Operand(Smi::FromInt(0))); @@ -1000,9 +1035,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ jmp(&loop); // We got a fixed array in register r0. Iterate through that. + Label non_proxy; __ bind(&fixed_array); - __ mov(r1, Operand(Smi::FromInt(0))); // Map (0) - force slow check. - __ Push(r1, r0); + __ mov(r1, Operand(Smi::FromInt(1))); // Smi indicates slow check + __ ldr(r2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CompareObjectType(r2, r3, r3, LAST_JS_PROXY_TYPE); + __ b(gt, &non_proxy); + __ mov(r1, Operand(Smi::FromInt(0))); // Zero indicates proxy + __ bind(&non_proxy); + __ Push(r1, r0); // Smi and array __ ldr(r1, FieldMemOperand(r0, FixedArray::kLengthOffset)); __ mov(r0, Operand(Smi::FromInt(0))); __ Push(r1, r0); // Fixed array length (as smi) and initial index. @@ -1019,18 +1061,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize)); - // Get the expected map from the stack or a zero map in the + // Get the expected map from the stack or a smi in the // permanent slow case into register r2. __ ldr(r2, MemOperand(sp, 3 * kPointerSize)); // Check if the expected map still matches that of the enumerable. - // If not, we have to filter the key. + // If not, we may have to filter the key. Label update_each; __ ldr(r1, MemOperand(sp, 4 * kPointerSize)); __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); __ cmp(r4, Operand(r2)); __ b(eq, &update_each); + // For proxies, no filtering is done. + // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. + __ cmp(r2, Operand(Smi::FromInt(0))); + __ b(eq, &update_each); + // Convert the entry to a string or (smi) 0 if it isn't a property // any more. If the property has been removed while iterating, we // just skip it. @@ -1085,7 +1132,7 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, !pretenure && scope()->is_function_scope() && info->num_literals() == 0) { - FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(info->language_mode()); __ mov(r0, Operand(info)); __ push(r0); __ CallStub(&stub); @@ -1116,7 +1163,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); __ tst(temp, temp); @@ -1129,7 +1176,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, } // If no outer scope calls eval, we do not need to check more // context extensions. - if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; + if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; s = s->outer_scope(); } @@ -1173,7 +1220,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); __ tst(temp, temp); @@ -1205,15 +1252,24 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (var->mode() == Variable::DYNAMIC_GLOBAL) { + if (var->mode() == DYNAMIC_GLOBAL) { EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + } else if (var->mode() == DYNAMIC_LOCAL) { Variable* local = var->local_if_not_shadowed(); __ ldr(r0, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == Variable::CONST) { + if (local->mode() == CONST || + local->mode() == CONST_HARMONY || + local->mode() == LET) { __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); - __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + if (local->mode() == CONST) { + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + } else { // LET || CONST_HARMONY + __ b(ne, done); + __ mov(r0, Operand(var->name())); + __ push(r0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } } __ jmp(done); } @@ -1246,24 +1302,64 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { Comment cmnt(masm_, var->IsContextSlot() ? "Context variable" : "Stack variable"); - if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { - context()->Plug(var); - } else { - // Let and const need a read barrier. - GetVar(r0, var); - __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); - if (var->mode() == Variable::LET) { - Label done; - __ b(ne, &done); - __ mov(r0, Operand(var->name())); - __ push(r0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - __ bind(&done); + if (var->binding_needs_init()) { + // var->scope() may be NULL when the proxy is located in eval code and + // refers to a potential outside binding. Currently those bindings are + // always looked up dynamically, i.e. in that case + // var->location() == LOOKUP. + // always holds. + ASSERT(var->scope() != NULL); + + // Check if the binding really needs an initialization check. The check + // can be skipped in the following situation: we have a LET or CONST + // binding in harmony mode, both the Variable and the VariableProxy have + // the same declaration scope (i.e. they are both in global code, in the + // same function or in the same eval code) and the VariableProxy is in + // the source physically located after the initializer of the variable. + // + // We cannot skip any initialization checks for CONST in non-harmony + // mode because const variables may be declared but never initialized: + // if (false) { const x; }; var y = x; + // + // The condition on the declaration scopes is a conservative check for + // nested functions that access a binding and are called before the + // binding is initialized: + // function() { f(); let x = 1; function f() { x = 2; } } + // + bool skip_init_check; + if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { + skip_init_check = false; } else { - __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + // Check that we always have valid source position. + ASSERT(var->initializer_position() != RelocInfo::kNoPosition); + ASSERT(proxy->position() != RelocInfo::kNoPosition); + skip_init_check = var->mode() != CONST && + var->initializer_position() < proxy->position(); + } + + if (!skip_init_check) { + // Let and const need a read barrier. + GetVar(r0, var); + __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); + if (var->mode() == LET || var->mode() == CONST_HARMONY) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + Label done; + __ b(ne, &done); + __ mov(r0, Operand(var->name())); + __ push(r0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&done); + } else { + // Uninitalized const bindings outside of harmony mode are unholed. + ASSERT(var->mode() == CONST); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + } + context()->Plug(r0); + break; } - context()->Plug(r0); } + context()->Plug(var); break; } @@ -1337,10 +1433,11 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Comment cmnt(masm_, "[ ObjectLiteral"); + Handle<FixedArray> constant_properties = expr->constant_properties(); __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); - __ mov(r1, Operand(expr->constant_properties())); + __ mov(r1, Operand(constant_properties)); int flags = expr->fast_elements() ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags; @@ -1349,10 +1446,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { : ObjectLiteral::kNoFlags; __ mov(r0, Operand(Smi::FromInt(flags))); __ Push(r3, r2, r1, r0); + int properties_count = constant_properties->length() / 2; if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); + } else { + FastCloneShallowObjectStub stub(properties_count); + __ CallStub(&stub); } // If result_saved is true the result is on top of the stack. If @@ -1386,9 +1488,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(r2, Operand(key->handle())); __ ldr(r1, MemOperand(sp)); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, key->id()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1447,13 +1549,20 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); int length = subexprs->length(); + Handle<FixedArray> constant_elements = expr->constant_elements(); + ASSERT_EQ(2, constant_elements->length()); + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); + bool has_fast_elements = constant_elements_kind == FAST_ELEMENTS; + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(constant_elements->get(1))); __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); - __ mov(r1, Operand(expr->constant_elements())); + __ mov(r1, Operand(constant_elements)); __ Push(r3, r2, r1); - if (expr->constant_elements()->map() == + if (has_fast_elements && constant_elements_values->map() == isolate()->heap()->fixed_cow_array_map()) { FastCloneShallowArrayStub stub( FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); @@ -1465,8 +1574,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::CLONE_ELEMENTS, length); + ASSERT(constant_elements_kind == FAST_ELEMENTS || + constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + FLAG_smi_only_arrays); + FastCloneShallowArrayStub::Mode mode = has_fast_elements + ? FastCloneShallowArrayStub::CLONE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); __ CallStub(&stub); } @@ -1489,15 +1603,23 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - // Store the subexpression value in the array's elements. - __ ldr(r1, MemOperand(sp)); // Copy of array literal. - __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); - int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ str(result_register(), FieldMemOperand(r1, offset)); - - // Update the write barrier for the array store with r0 as the scratch - // register. - __ RecordWrite(r1, Operand(offset), r2, result_register()); + if (constant_elements_kind == FAST_ELEMENTS) { + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ ldr(r6, MemOperand(sp)); // Copy of array literal. + __ ldr(r1, FieldMemOperand(r6, JSObject::kElementsOffset)); + __ str(result_register(), FieldMemOperand(r1, offset)); + // Update the write barrier for the array store. + __ RecordWriteField(r1, offset, result_register(), r2, + kLRHasBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, INLINE_SMI_CHECK); + } else { + __ ldr(r1, MemOperand(sp)); // Copy of array literal. + __ ldr(r2, FieldMemOperand(r1, JSObject::kMapOffset)); + __ mov(r3, Operand(Smi::FromInt(i))); + __ mov(r4, Operand(Smi::FromInt(expr->literal_index()))); + StoreArrayLiteralElementStub stub; + __ CallStub(&stub); + } PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } @@ -1629,7 +1751,7 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { __ mov(r2, Operand(key->handle())); // Call load IC. It has arguments receiver and property name r0 and r2. Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1637,7 +1759,7 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); // Call keyed load IC. It has arguments key and receiver in r0 and r1. Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1715,7 +1837,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ mov(ip, Operand(scratch1, ASR, 31)); __ cmp(ip, Operand(scratch2)); __ b(ne, &stub_call); - __ tst(scratch1, Operand(scratch1)); + __ cmp(scratch1, Operand(0)); __ mov(right, Operand(scratch1), LeaveCC, ne); __ b(ne, &done); __ add(scratch2, right, Operand(left), SetCC); @@ -1785,9 +1907,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(r1, r0); __ pop(r0); // Restore value. __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1798,9 +1920,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(r1, r0); __ pop(r2); __ pop(r0); // Restore value. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1816,9 +1938,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // Global var, const, or let. __ mov(r2, Operand(var->name())); __ ldr(r1, GlobalObjectOperand()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { @@ -1844,12 +1966,12 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { __ push(r0); // Value. __ mov(r1, Operand(var->name())); - __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ mov(r0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, r1, r0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { @@ -1869,12 +1991,14 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // RecordWrite may destroy all its register arguments. __ mov(r3, result_register()); int offset = Context::SlotOffset(var->index()); - __ RecordWrite(r1, Operand(offset), r2, r3); + __ RecordWriteContextSlot( + r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs); } } - } else if (var->mode() != Variable::CONST) { - // Assignment to var or initializing assignment to let. + } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { + // Assignment to var or initializing assignment to let/const + // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, r1); if (FLAG_debug_code && op == Token::INIT_LET) { @@ -1887,13 +2011,15 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ str(r0, location); if (var->IsContextSlot()) { __ mov(r3, r0); - __ RecordWrite(r1, Operand(Context::SlotOffset(var->index())), r2, r3); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot( + r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs); } } else { ASSERT(var->IsLookupSlot()); __ push(r0); // Value. __ mov(r1, Operand(var->name())); - __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ mov(r0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, r1, r0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } @@ -1930,9 +2056,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ pop(r1); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1976,9 +2102,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(r2); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -2083,6 +2209,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, flags); + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2091,8 +2218,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } -void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, - int arg_count) { +void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); @@ -2101,22 +2227,20 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, } __ push(r1); - // Push the receiver of the enclosing function and do runtime call. + // Push the receiver of the enclosing function. int receiver_offset = 2 + info_->scope()->num_parameters(); __ ldr(r1, MemOperand(fp, receiver_offset * kPointerSize)); __ push(r1); - // Push the strict mode flag. In harmony mode every eval call - // is a strict mode eval call. - StrictModeFlag strict_mode = strict_mode_flag(); - if (FLAG_harmony_block_scoping) { - strict_mode = kStrictMode; - } - __ mov(r1, Operand(Smi::FromInt(strict_mode))); + // Push the language mode. + __ mov(r1, Operand(Smi::FromInt(language_mode()))); __ push(r1); - __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP - ? Runtime::kResolvePossiblyDirectEvalNoLookup - : Runtime::kResolvePossiblyDirectEval, 4); + // Push the start position of the scope the calls resides in. + __ mov(r1, Operand(Smi::FromInt(scope()->start_position()))); + __ push(r1); + + // Do the runtime call. + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); } @@ -2150,28 +2274,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly - // in generated code. If we succeed, there is no need to perform a - // context lookup in the runtime system. - Label done; - Variable* var = proxy->var(); - if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { - Label slow; - EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); - // Push the function and resolve eval. - __ push(r0); - EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); - __ jmp(&done); - __ bind(&slow); - } - // Push a copy of the function (found below the arguments) and // resolve eval. __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(r1); - EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - __ bind(&done); + EmitResolvePossiblyDirectEval(arg_count); // The runtime call returns a pair of values in r0 (function) and // r1 (receiver). Touch up the stack with the right values. @@ -2182,6 +2289,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2288,14 +2396,28 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ mov(r0, Operand(arg_count)); __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); - Handle<Code> construct_builtin = - isolate()->builtins()->JSConstructCall(); - __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); + // Record call targets in unoptimized code, but not in the snapshot. + CallFunctionFlags flags; + if (!Serializer::enabled()) { + flags = RECORD_CALL_TARGET; + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->id(), cell); + __ mov(r2, Operand(cell)); + } else { + flags = NO_CALL_FUNCTION_FLAGS; + } + + CallConstructStub stub(flags); + __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); context()->Plug(r0); } -void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2307,7 +2429,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ tst(r0, Operand(kSmiTagMask)); Split(eq, if_true, if_false, fall_through); @@ -2315,7 +2437,8 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2327,7 +2450,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ tst(r0, Operand(kSmiTagMask | 0x80000000)); Split(eq, if_true, if_false, fall_through); @@ -2335,7 +2458,8 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2360,14 +2484,15 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ cmp(r1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); __ b(lt, if_false); __ cmp(r1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(le, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2381,14 +2506,15 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { __ JumpIfSmi(r0, if_false); __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ge, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2404,7 +2530,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset)); __ tst(r1, Operand(1 << Map::kIsUndetectable)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ne, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2412,8 +2538,8 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - ZoneList<Expression*>* args) { - + CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2492,12 +2618,13 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ strb(r2, FieldMemOperand(r1, Map::kBitField2Offset)); __ jmp(if_true); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2511,14 +2638,15 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { __ JumpIfSmi(r0, if_false); __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2532,14 +2660,15 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { __ JumpIfSmi(r0, if_false); __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2553,7 +2682,7 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { __ JumpIfSmi(r0, if_false); __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2561,8 +2690,8 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { -void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label materialize_true, materialize_false; Label* if_true = NULL; @@ -2585,14 +2714,15 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { __ bind(&check_frame_marker); __ ldr(r1, MemOperand(r2, StandardFrameConstants::kMarkerOffset)); __ cmp(r1, Operand(Smi::FromInt(StackFrame::CONSTRUCT))); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -2608,14 +2738,15 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { __ pop(r1); __ cmp(r0, r1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitArguments(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); // ArgumentsAccessStub expects the key in edx and the formal @@ -2629,9 +2760,8 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label exit; // Get the number of formal parameters. __ mov(r0, Operand(Smi::FromInt(info_->scope()->num_parameters()))); @@ -2651,7 +2781,8 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); Label done, null, function, non_function_constructor; @@ -2662,20 +2793,24 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { // Check that the object is a JS object but take special care of JS // functions to make sure they have 'Function' as their class. + // Assume that there are only two callable types, and one of them is at + // either end of the type range for JS object types. Saves extra comparisons. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ CompareObjectType(r0, r0, r1, FIRST_SPEC_OBJECT_TYPE); // Map is now in r0. __ b(lt, &null); - - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - __ cmp(r1, Operand(FIRST_CALLABLE_SPEC_OBJECT_TYPE)); - __ b(ge, &function); - - // Check if the constructor in the map is a function. + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + __ b(eq, &function); + + __ cmp(r1, Operand(LAST_SPEC_OBJECT_TYPE)); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + __ b(eq, &function); + // Assume that there is no larger type. + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); + + // Check if the constructor in the map is a JS function. __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset)); __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE); __ b(ne, &non_function_constructor); @@ -2707,7 +2842,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitLog(CallRuntime* expr) { // Conditionally generate a log call. // Args: // 0 (literal string): The type of logging (corresponds to the flags). @@ -2715,6 +2850,7 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { // 1 (string): Format string. Access the string at argument index 2 // with '%2s' (see Logger::LogRuntime for all the formats). // 2 (array): Arguments to the format string. + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 3); if (CodeGenerator::ShouldGenerateLog(args->at(0))) { VisitForStackValue(args->at(1)); @@ -2728,9 +2864,8 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label slow_allocate_heapnumber; Label heapnumber_allocated; @@ -2750,7 +2885,8 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). if (CpuFeatures::IsSupported(VFP3)) { __ PrepareCallCFunction(1, r0); - __ mov(r0, Operand(ExternalReference::isolate_address())); + __ ldr(r0, ContextOperand(context_register(), Context::GLOBAL_INDEX)); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalContextOffset)); __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); CpuFeatures::Scope scope(VFP3); @@ -2770,8 +2906,9 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { __ mov(r0, r4); } else { __ PrepareCallCFunction(2, r0); + __ ldr(r1, ContextOperand(context_register(), Context::GLOBAL_INDEX)); __ mov(r0, Operand(r4)); - __ mov(r1, Operand(ExternalReference::isolate_address())); + __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset)); __ CallCFunction( ExternalReference::fill_heap_number_with_random_function(isolate()), 2); } @@ -2780,9 +2917,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2792,9 +2930,10 @@ void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpExecStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 4); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2805,9 +2944,9 @@ void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); - VisitForAccumulatorValue(args->at(0)); // Load the object. Label done; @@ -2823,20 +2962,25 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { // Load the arguments on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); - MathPowStub stub; - __ CallStub(&stub); + if (CpuFeatures::IsSupported(VFP3)) { + MathPowStub stub(MathPowStub::ON_STACK); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kMath_pow, 2); + } context()->Plug(r0); } -void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); - VisitForStackValue(args->at(0)); // Load the object. VisitForAccumulatorValue(args->at(1)); // Load the value. __ pop(r1); // r0 = value. r1 = object. @@ -2853,16 +2997,18 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset)); // Update the write barrier. Save the value as it will be // overwritten by the write barrier code and is needed afterward. - __ RecordWrite(r1, Operand(JSValue::kValueOffset - kHeapObjectTag), r2, r3); + __ mov(r2, r0); + __ RecordWriteField( + r1, JSValue::kValueOffset, r2, r3, kLRHasBeenSaved, kDontSaveFPRegs); __ bind(&done); context()->Plug(r0); } -void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 1); - // Load the argument on the stack and call the stub. VisitForStackValue(args->at(0)); @@ -2872,9 +3018,9 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); - VisitForAccumulatorValue(args->at(0)); Label done; @@ -2890,15 +3036,14 @@ void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); - VisitForStackValue(args->at(0)); VisitForAccumulatorValue(args->at(1)); Register object = r1; Register index = r0; - Register scratch = r2; Register result = r3; __ pop(object); @@ -2908,7 +3053,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Label done; StringCharCodeAtGenerator generator(object, index, - scratch, result, &need_conversion, &need_conversion, @@ -2937,16 +3081,15 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); - VisitForStackValue(args->at(0)); VisitForAccumulatorValue(args->at(1)); Register object = r1; Register index = r0; - Register scratch1 = r2; - Register scratch2 = r3; + Register scratch = r3; Register result = r0; __ pop(object); @@ -2956,8 +3099,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Label done; StringCharAtGenerator generator(object, index, - scratch1, - scratch2, + scratch, result, &need_conversion, &need_conversion, @@ -2986,9 +3128,9 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2998,9 +3140,9 @@ void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3010,10 +3152,11 @@ void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -3021,10 +3164,23 @@ void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); + ASSERT(args->length() == 1); + VisitForStackValue(args->at(0)); + __ CallStub(&stub); + context()->Plug(r0); +} + + +void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -3032,10 +3188,11 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::LOG, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -3043,8 +3200,9 @@ void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { // Load the argument on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallRuntime(Runtime::kMath_sqrt, 1); @@ -3052,7 +3210,8 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() >= 2); int arg_count = args->length() - 2; // 2 ~ receiver and function. @@ -3061,18 +3220,31 @@ void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { } VisitForAccumulatorValue(args->last()); // Function. + // Check for proxy. + Label proxy, done; + __ CompareObjectType(r0, r1, r1, JS_FUNCTION_PROXY_TYPE); + __ b(eq, &proxy); + // InvokeFunction requires the function in r1. Move it in there. __ mov(r1, result_register()); ParameterCount count(arg_count); __ InvokeFunction(r1, count, CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ jmp(&done); + + __ bind(&proxy); + __ push(r0); + __ CallRuntime(Runtime::kCall, args->length()); + __ bind(&done); + context()->Plug(r0); } -void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { RegExpConstructResultStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3082,7 +3254,8 @@ void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSwapElements(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3141,16 +3314,31 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ str(scratch1, MemOperand(index2, 0)); __ str(scratch2, MemOperand(index1, 0)); - Label new_space; - __ InNewSpace(elements, scratch1, eq, &new_space); + Label no_remembered_set; + __ CheckPageFlag(elements, + scratch1, + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + ne, + &no_remembered_set); // Possible optimization: do a check that both values are Smis // (or them and test against Smi mask.) - __ mov(scratch1, elements); - __ RecordWriteHelper(elements, index1, scratch2); - __ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements. + // We are swapping two objects in an array and the incremental marker never + // pauses in the middle of scanning a single object. Therefore the + // incremental marker is not disturbed, so we don't need to call the + // RecordWrite stub that notifies the incremental marker. + __ RememberedSetHelper(elements, + index1, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + __ RememberedSetHelper(elements, + index2, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); - __ bind(&new_space); + __ bind(&no_remembered_set); // We are done. Drop elements from the stack, and return undefined. __ Drop(3); __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); @@ -3164,9 +3352,9 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); - ASSERT_NE(NULL, args->at(0)->AsLiteral()); int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); @@ -3215,7 +3403,8 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); Register right = r0; @@ -3255,7 +3444,8 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); VisitForAccumulatorValue(args->at(0)); Label materialize_true, materialize_false; @@ -3267,14 +3457,15 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset)); __ tst(r0, Operand(String::kContainsCachedArrayIndexMask)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3289,12 +3480,12 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { Label bailout, done, one_char_separator, long_separator, non_trivial_array, not_size_one_array, loop, empty_separator_loop, one_char_separator_loop, one_char_separator_loop_entry, long_separator_loop; - + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(1)); VisitForAccumulatorValue(args->at(0)); @@ -3457,7 +3648,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // One-character separator case __ bind(&one_char_separator); - // Replace separator with its ascii character value. + // Replace separator with its ASCII character value. __ ldrb(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize)); // Jump into the loop after the code that copies the separator, so the first // element is not preceded by a separator @@ -3468,7 +3659,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // result_pos: the position to which we are currently copying characters. // element: Current array element. // elements_end: Array end. - // separator: Single separator ascii char (in lower byte). + // separator: Single separator ASCII char (in lower byte). // Copy the separator character to the result. __ strb(separator, MemOperand(result_pos, 1, PostIndex)); @@ -3571,7 +3762,9 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { if (property != NULL) { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); - __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ mov(r1, Operand(Smi::FromInt(strict_mode_flag))); __ push(r1); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(r0); @@ -3579,7 +3772,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode // but "delete this" is allowed. - ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); + ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); if (var->IsUnallocated()) { __ ldr(r2, GlobalObjectOperand()); __ mov(r1, Operand(var->name())); @@ -3622,18 +3815,35 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { // Unary NOT has no side effects so it's only necessary to visit the // subexpression. Match the optimizing compiler by not branching. VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); } else { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - - // Notice that the labels are swapped. - context()->PrepareTest(&materialize_true, &materialize_false, - &if_false, &if_true, &fall_through); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitForControl(expr->expression(), if_true, if_false, fall_through); - context()->Plug(if_false, if_true); // Labels swapped. + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + __ bind(&materialize_true); + PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); + __ LoadRoot(r0, Heap::kTrueValueRootIndex); + if (context()->IsStackValue()) __ push(r0); + __ jmp(&done); + __ bind(&materialize_false); + PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); + __ LoadRoot(r0, Heap::kFalseValueRootIndex); + if (context()->IsStackValue()) __ push(r0); + __ bind(&done); } break; } @@ -3826,9 +4036,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); __ pop(r1); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3843,9 +4053,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(r1); // Key. __ pop(r2); // Receiver. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3892,20 +4102,25 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { context()->Plug(r0); } else { // This expression cannot throw a reference error at the top level. - VisitInCurrentContext(expr); + VisitInDuplicateContext(expr); } } void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through) { + Expression* sub_expr, + Handle<String> check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + { AccumulatorValueContext context(this); - VisitForTypeofValue(expr); + VisitForTypeofValue(sub_expr); } - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); if (check->Equals(isolate()->heap()->number_symbol())) { __ JumpIfSmi(r0, if_true); @@ -3942,9 +4157,11 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else if (check->Equals(isolate()->heap()->function_symbol())) { __ JumpIfSmi(r0, if_false); - __ CompareObjectType(r0, r1, r0, FIRST_CALLABLE_SPEC_OBJECT_TYPE); - Split(ge, if_true, if_false, fall_through); - + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ CompareObjectType(r0, r0, r1, JS_FUNCTION_TYPE); + __ b(eq, if_true); + __ cmp(r1, Operand(JS_FUNCTION_PROXY_TYPE)); + Split(eq, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(r0, if_false); if (!FLAG_harmony_typeof) { @@ -3963,18 +4180,7 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else { if (if_false != fall_through) __ jmp(if_false); } -} - - -void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through) { - VisitForAccumulatorValue(expr); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ CompareRoot(r0, Heap::kUndefinedValueRootIndex); - Split(eq, if_true, if_false, fall_through); + context()->Plug(if_true, if_false); } @@ -3982,9 +4188,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); SetSourcePosition(expr->position()); + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -3992,20 +4201,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { - context()->Plug(if_true, if_false); - return; - } - Token::Value op = expr->op(); VisitForStackValue(expr->left()); switch (op) { case Token::IN: VisitForStackValue(expr->right()); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); __ LoadRoot(ip, Heap::kTrueValueRootIndex); __ cmp(r0, ip); Split(eq, if_true, if_false, fall_through); @@ -4015,7 +4217,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForStackValue(expr->right()); InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); // The stub returns 0 for true. __ tst(r0, r0); Split(eq, if_true, if_false, fall_through); @@ -4029,33 +4231,25 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: case Token::EQ: cond = eq; - __ pop(r1); break; case Token::LT: cond = lt; - __ pop(r1); break; case Token::GT: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cond = lt; - __ mov(r1, result_register()); - __ pop(r0); + cond = gt; break; case Token::LTE: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cond = ge; - __ mov(r1, result_register()); - __ pop(r0); + cond = le; break; case Token::GTE: cond = ge; - __ pop(r1); break; case Token::IN: case Token::INSTANCEOF: default: UNREACHABLE(); } + __ pop(r1); bool inline_smi_code = ShouldInlineSmiCase(op); JumpPatchSite patch_site(masm_); @@ -4073,7 +4267,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Handle<Code> ic = CompareIC::GetUninitialized(op); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ cmp(r0, Operand(0)); Split(cond, if_true, if_false, fall_through); } @@ -4085,8 +4279,9 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } -void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { - Comment cmnt(masm_, "[ CompareToNull"); +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4094,15 +4289,21 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - VisitForAccumulatorValue(expr->expression()); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - __ LoadRoot(r1, Heap::kNullValueRootIndex); + VisitForAccumulatorValue(sub_expr); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + Heap::RootListIndex nil_value = nil == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; + __ LoadRoot(r1, nil_value); __ cmp(r0, r1); - if (expr->is_strict()) { + if (expr->op() == Token::EQ_STRICT) { Split(eq, if_true, if_false, fall_through); } else { + Heap::RootListIndex other_nil_value = nil == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; __ b(eq, if_true); - __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); + __ LoadRoot(r1, other_nil_value); __ cmp(r0, r1); __ b(eq, if_true); __ JumpIfSmi(r0, if_false); diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 2e49cae928..14daadaea9 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -208,7 +208,8 @@ static void GenerateDictionaryStore(MacroAssembler* masm, // Update the write barrier. Make sure not to clobber the value. __ mov(scratch1, value); - __ RecordWrite(elements, scratch2, scratch1); + __ RecordWrite( + elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs); } @@ -381,10 +382,10 @@ Object* CallIC_Miss(Arguments args); // The generated code does not accept smi keys. // The generated code falls through if both probes miss. -static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, + int argc, + Code::Kind kind, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- r1 : receiver // -- r2 : name @@ -394,7 +395,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, - extra_ic_state, + extra_state, NORMAL, argc); Isolate::Current()->stub_cache()->GenerateProbe( @@ -463,7 +464,7 @@ static void GenerateFunctionTailCall(MacroAssembler* masm, } -static void GenerateCallNormal(MacroAssembler* masm, int argc) { +void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -485,10 +486,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { } -static void GenerateCallMiss(MacroAssembler* masm, - int argc, - IC::UtilityId id, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -504,21 +505,22 @@ static void GenerateCallMiss(MacroAssembler* masm, // Get the receiver of the function from the stack. __ ldr(r3, MemOperand(sp, argc * kPointerSize)); - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push the receiver and the name of the function. - __ Push(r3, r2); + // Push the receiver and the name of the function. + __ Push(r3, r2); - // Call the entry. - __ mov(r0, Operand(2)); - __ mov(r1, Operand(ExternalReference(IC_Utility(id), isolate))); + // Call the entry. + __ mov(r0, Operand(2)); + __ mov(r1, Operand(ExternalReference(IC_Utility(id), isolate))); - CEntryStub stub(1); - __ CallStub(&stub); + CEntryStub stub(1); + __ CallStub(&stub); - // Move result to r1 and leave the internal frame. - __ mov(r1, Operand(r0)); - __ LeaveInternalFrame(); + // Move result to r1 and leave the internal frame. + __ mov(r1, Operand(r0)); + } // Check if the receiver is a global object of some sort. // This can happen only for regular CallIC but not KeyedCallIC. @@ -539,7 +541,7 @@ static void GenerateCallMiss(MacroAssembler* masm, } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state) + CallKind call_kind = CallICBase::Contextual::decode(extra_state) ? CALL_AS_FUNCTION : CALL_AS_METHOD; ParameterCount actual(argc); @@ -551,18 +553,6 @@ static void GenerateCallMiss(MacroAssembler* masm, } -void CallIC::GenerateMiss(MacroAssembler* masm, - int argc, - Code::ExtraICState extra_ic_state) { - // ----------- S t a t e ------------- - // -- r2 : name - // -- lr : return address - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state); -} - - void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc, Code::ExtraICState extra_ic_state) { @@ -578,27 +568,6 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, } -void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- r2 : name - // -- lr : return address - // ----------------------------------- - - GenerateCallNormal(masm, argc); - GenerateMiss(masm, argc, Code::kNoExtraICState); -} - - -void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- r2 : name - // -- lr : return address - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState); -} - - void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // -- r2 : name @@ -650,12 +619,13 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // This branch is taken when calling KeyedCallIC_Miss is neither required // nor beneficial. __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, r0, r3); - __ EnterInternalFrame(); - __ push(r2); // save the key - __ Push(r1, r2); // pass the receiver and the key - __ CallRuntime(Runtime::kKeyedGetProperty, 2); - __ pop(r2); // restore the key - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r2); // save the key + __ Push(r1, r2); // pass the receiver and the key + __ CallRuntime(Runtime::kKeyedGetProperty, 2); + __ pop(r2); // restore the key + } __ mov(r1, r0); __ jmp(&do_call); @@ -715,7 +685,7 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ JumpIfSmi(r2, &miss); __ IsObjectJSStringType(r2, r0, &miss); - GenerateCallNormal(masm, argc); + CallICBase::GenerateNormal(masm, argc); __ bind(&miss); GenerateMiss(masm, argc); } @@ -908,7 +878,8 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { GenerateMappedArgumentsLookup(masm, r2, r1, r3, r4, r5, ¬in, &slow); __ str(r0, mapped_location); __ add(r6, r3, r5); - __ RecordWrite(r3, r6, r9); + __ mov(r9, r0); + __ RecordWrite(r3, r6, r9, kLRHasNotBeenSaved, kDontSaveFPRegs); __ Ret(); __ bind(¬in); // The unmapped lookup expects that the parameter map is in r3. @@ -916,7 +887,8 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { GenerateUnmappedArgumentsLookup(masm, r1, r3, r4, &slow); __ str(r0, unmapped_location); __ add(r6, r3, r4); - __ RecordWrite(r3, r6, r9); + __ mov(r9, r0); + __ RecordWrite(r3, r6, r9, kLRHasNotBeenSaved, kDontSaveFPRegs); __ Ret(); __ bind(&slow); GenerateMiss(masm, false); @@ -1059,15 +1031,34 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ mov(r3, Operand(r2, ASR, KeyedLookupCache::kMapHashShift)); __ ldr(r4, FieldMemOperand(r0, String::kHashFieldOffset)); __ eor(r3, r3, Operand(r4, ASR, String::kHashShift)); - __ And(r3, r3, Operand(KeyedLookupCache::kCapacityMask)); + int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask; + __ And(r3, r3, Operand(mask)); // Load the key (consisting of map and symbol) from the cache and // check for match. + Label load_in_object_property; + static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; + Label hit_on_nth_entry[kEntriesPerBucket]; ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys(isolate); + __ mov(r4, Operand(cache_keys)); __ add(r4, r4, Operand(r3, LSL, kPointerSizeLog2 + 1)); - __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex)); // Move r4 to symbol. + + for (int i = 0; i < kEntriesPerBucket - 1; i++) { + Label try_next_entry; + // Load map and move r4 to next entry. + __ ldr(r5, MemOperand(r4, kPointerSize * 2, PostIndex)); + __ cmp(r2, r5); + __ b(ne, &try_next_entry); + __ ldr(r5, MemOperand(r4, -kPointerSize)); // Load symbol + __ cmp(r0, r5); + __ b(eq, &hit_on_nth_entry[i]); + __ bind(&try_next_entry); + } + + // Last entry: Load map and move r4 to symbol. + __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex)); __ cmp(r2, r5); __ b(ne, &slow); __ ldr(r5, MemOperand(r4)); @@ -1081,13 +1072,25 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // r3 : lookup cache index ExternalReference cache_field_offsets = ExternalReference::keyed_lookup_cache_field_offsets(isolate); - __ mov(r4, Operand(cache_field_offsets)); - __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2)); - __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset)); - __ sub(r5, r5, r6, SetCC); - __ b(ge, &property_array_property); + + // Hit on nth entry. + for (int i = kEntriesPerBucket - 1; i >= 0; i--) { + __ bind(&hit_on_nth_entry[i]); + __ mov(r4, Operand(cache_field_offsets)); + if (i != 0) { + __ add(r3, r3, Operand(i)); + } + __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2)); + __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset)); + __ sub(r5, r5, r6, SetCC); + __ b(ge, &property_array_property); + if (i != 0) { + __ jmp(&load_in_object_property); + } + } // Load in-object property. + __ bind(&load_in_object_property); __ ldrb(r6, FieldMemOperand(r2, Map::kInstanceSizeOffset)); __ add(r6, r6, r5); // Index from start of object. __ sub(r1, r1, Operand(kHeapObjectTag)); // Remove the heap tag. @@ -1137,14 +1140,12 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { Register receiver = r1; Register index = r0; - Register scratch1 = r2; - Register scratch2 = r3; + Register scratch = r3; Register result = r0; StringCharAtGenerator char_at_generator(receiver, index, - scratch1, - scratch2, + scratch, result, &miss, // When not a string. &miss, // When not a number. @@ -1239,6 +1240,47 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- r2 : receiver + // -- r3 : target map + // -- lr : return address + // ----------------------------------- + // Must return the modified receiver in r0. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + __ mov(r0, r2); + __ Ret(); + __ bind(&fail); + } + + __ push(r2); + __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1); +} + + +void KeyedStoreIC::GenerateTransitionElementsDoubleToObject( + MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- r2 : receiver + // -- r3 : target map + // -- lr : return address + // ----------------------------------- + // Must return the modified receiver in r0. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); + __ mov(r0, r2); + __ Ret(); + __ bind(&fail); + } + + __ push(r2); + __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1); +} + + void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, StrictModeFlag strict_mode) { // ---------- S t a t e -------------- @@ -1267,13 +1309,19 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // -- r2 : receiver // -- lr : return address // ----------------------------------- - Label slow, fast, array, extra; + Label slow, array, extra, check_if_double_array; + Label fast_object_with_map_check, fast_object_without_map_check; + Label fast_double_with_map_check, fast_double_without_map_check; + Label transition_smi_elements, finish_object_store, non_double_value; + Label transition_double_elements; // Register usage. Register value = r0; Register key = r1; Register receiver = r2; - Register elements = r3; // Elements array of the receiver. + Register receiver_map = r3; + Register elements_map = r6; + Register elements = r7; // Elements array of the receiver. // r4 and r5 are used as general scratch registers. // Check that the key is a smi. @@ -1281,35 +1329,26 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // Check that the object isn't a smi. __ JumpIfSmi(receiver, &slow); // Get the map of the object. - __ ldr(r4, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); // Check that the receiver does not require access checks. We need // to do this because this generic stub does not perform map checks. - __ ldrb(ip, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ ldrb(ip, FieldMemOperand(receiver_map, Map::kBitFieldOffset)); __ tst(ip, Operand(1 << Map::kIsAccessCheckNeeded)); __ b(ne, &slow); // Check if the object is a JS array or not. - __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); + __ ldrb(r4, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset)); __ cmp(r4, Operand(JS_ARRAY_TYPE)); __ b(eq, &array); // Check that the object is some kind of JSObject. - __ cmp(r4, Operand(FIRST_JS_RECEIVER_TYPE)); + __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE)); __ b(lt, &slow); - __ cmp(r4, Operand(JS_PROXY_TYPE)); - __ b(eq, &slow); - __ cmp(r4, Operand(JS_FUNCTION_PROXY_TYPE)); - __ b(eq, &slow); // Object case: Check key against length in the elements array. __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - // Check that the object is in fast mode and writable. - __ ldr(r4, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); - __ cmp(r4, ip); - __ b(ne, &slow); // Check array bounds. Both the key and the length of FixedArray are smis. __ ldr(ip, FieldMemOperand(elements, FixedArray::kLengthOffset)); __ cmp(key, Operand(ip)); - __ b(lo, &fast); + __ b(lo, &fast_object_with_map_check); // Slow case, handle jump to runtime. __ bind(&slow); @@ -1330,21 +1369,31 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ ldr(ip, FieldMemOperand(elements, FixedArray::kLengthOffset)); __ cmp(key, Operand(ip)); __ b(hs, &slow); + __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); + __ cmp(elements_map, + Operand(masm->isolate()->factory()->fixed_array_map())); + __ b(ne, &check_if_double_array); // Calculate key + 1 as smi. STATIC_ASSERT(kSmiTag == 0); __ add(r4, key, Operand(Smi::FromInt(1))); __ str(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); - __ b(&fast); + __ b(&fast_object_without_map_check); + + __ bind(&check_if_double_array); + __ cmp(elements_map, + Operand(masm->isolate()->factory()->fixed_double_array_map())); + __ b(ne, &slow); + // Add 1 to key, and go to common element store code for doubles. + STATIC_ASSERT(kSmiTag == 0); + __ add(r4, key, Operand(Smi::FromInt(1))); + __ str(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ jmp(&fast_double_without_map_check); // Array case: Get the length and the elements array from the JS // array. Check that the array is in fast mode (and writable); if it // is the length is always a smi. __ bind(&array); __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ ldr(r4, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); - __ cmp(r4, ip); - __ b(ne, &slow); // Check the key against the length in the array. __ ldr(ip, FieldMemOperand(receiver, JSArray::kLengthOffset)); @@ -1352,19 +1401,104 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ b(hs, &extra); // Fall through to fast case. - __ bind(&fast); - // Fast case, store the value to the elements backing store. - __ add(r5, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - __ add(r5, r5, Operand(key, LSL, kPointerSizeLog2 - kSmiTagSize)); - __ str(value, MemOperand(r5)); - // Skip write barrier if the written value is a smi. - __ tst(value, Operand(kSmiTagMask)); - __ Ret(eq); + __ bind(&fast_object_with_map_check); + Register scratch_value = r4; + Register address = r5; + __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); + __ cmp(elements_map, + Operand(masm->isolate()->factory()->fixed_array_map())); + __ b(ne, &fast_double_with_map_check); + __ bind(&fast_object_without_map_check); + // Smi stores don't require further checks. + Label non_smi_value; + __ JumpIfNotSmi(value, &non_smi_value); + // It's irrelevant whether array is smi-only or not when writing a smi. + __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ add(address, address, Operand(key, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(value, MemOperand(address)); + __ Ret(); + + __ bind(&non_smi_value); + // Escape to elements kind transition case. + __ CheckFastObjectElements(receiver_map, scratch_value, + &transition_smi_elements); + // Fast elements array, store the value to the elements backing store. + __ bind(&finish_object_store); + __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ add(address, address, Operand(key, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(value, MemOperand(address)); // Update write barrier for the elements array address. - __ sub(r4, r5, Operand(elements)); - __ RecordWrite(elements, Operand(r4), r5, r6); + __ mov(scratch_value, value); // Preserve the value which is returned. + __ RecordWrite(elements, + address, + scratch_value, + kLRHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ Ret(); + __ bind(&fast_double_with_map_check); + // Check for fast double array case. If this fails, call through to the + // runtime. + __ cmp(elements_map, + Operand(masm->isolate()->factory()->fixed_double_array_map())); + __ b(ne, &slow); + __ bind(&fast_double_without_map_check); + __ StoreNumberToDoubleElements(value, + key, + receiver, + elements, + r3, + r4, + r5, + r6, + &transition_double_elements); __ Ret(); + + __ bind(&transition_smi_elements); + // Transition the array appropriately depending on the value type. + __ ldr(r4, FieldMemOperand(value, HeapObject::kMapOffset)); + __ CompareRoot(r4, Heap::kHeapNumberMapRootIndex); + __ b(ne, &non_double_value); + + // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // FAST_DOUBLE_ELEMENTS and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_DOUBLE_ELEMENTS, + receiver_map, + r4, + &slow); + ASSERT(receiver_map.is(r3)); // Transition code expects map in r3 + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&fast_double_without_map_check); + + __ bind(&non_double_value); + // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + receiver_map, + r4, + &slow); + ASSERT(receiver_map.is(r3)); // Transition code expects map in r3 + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); + + __ bind(&transition_double_elements); + // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a + // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and + // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, + FAST_ELEMENTS, + receiver_map, + r4, + &slow); + ASSERT(receiver_map.is(r3)); // Transition code expects map in r3 + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); + __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); } @@ -1414,11 +1548,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { // -- lr : return address // ----------------------------------- // - // This accepts as a receiver anything JSObject::SetElementsLength accepts - // (currently anything except for external and pixel arrays which means - // anything with elements of FixedArray type.), but currently is restricted - // to JSArray. - // Value must be a number, but only smis are accepted as the most common case. + // This accepts as a receiver anything JSArray::SetElementsLength accepts + // (currently anything except for external arrays which means anything with + // elements of FixedArray type). Value must be a number, but only smis are + // accepted as the most common case. Label miss; @@ -1440,6 +1573,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { __ CompareObjectType(scratch, scratch, scratch, FIXED_ARRAY_TYPE); __ b(ne, &miss); + // Check that the array has fast properties, otherwise the length + // property might have been redefined. + __ ldr(scratch, FieldMemOperand(receiver, JSArray::kPropertiesOffset)); + __ ldr(scratch, FieldMemOperand(scratch, FixedArray::kMapOffset)); + __ CompareRoot(scratch, Heap::kHashTableMapRootIndex); + __ b(eq, &miss); + // Check that value is a smi. __ JumpIfNotSmi(value, &miss); @@ -1510,11 +1650,9 @@ Condition CompareIC::ComputeCondition(Token::Value op) { case Token::LT: return lt; case Token::GT: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return lt; + return gt; case Token::LTE: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return ge; + return le; case Token::GTE: return ge; default: @@ -1534,6 +1672,9 @@ void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { rewritten = stub.GetCode(); } else { ICCompareStub stub(op_, state); + if (state == KNOWN_OBJECTS) { + stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map())); + } rewritten = stub.GetCode(); } set_target(*rewritten); diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index 30ccd05bee..1111c67faf 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -212,10 +212,11 @@ void LCmpIDAndBranch::PrintDataTo(StringStream* stream) { } -void LIsNullAndBranch::PrintDataTo(StringStream* stream) { +void LIsNilAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if "); InputAt(0)->PrintTo(stream); - stream->Add(is_strict() ? " === null" : " == null"); + stream->Add(kind() == kStrictEquality ? " === " : " == "); + stream->Add(nil() == kNullValue ? "null" : "undefined"); stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); } @@ -227,6 +228,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { } +void LIsStringAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_string("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LIsSmiAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_smi("); InputAt(0)->PrintTo(stream); @@ -241,6 +249,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { } +void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if string_compare("); + InputAt(0)->PrintTo(stream); + InputAt(1)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if has_instance_type("); InputAt(0)->PrintTo(stream); @@ -390,6 +406,12 @@ void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) { } +void LTransitionElementsKind::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add(" %p -> %p", *original_map(), *transitioned_map()); +} + + LChunk::LChunk(CompilationInfo* info, HGraph* graph) : spill_slot_count_(0), info_(info), @@ -559,11 +581,6 @@ void LChunkBuilder::Abort(const char* format, ...) { } -LRegister* LChunkBuilder::ToOperand(Register reg) { - return LRegister::Create(Register::ToAllocationIndex(reg)); -} - - LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { return new LUnallocated(LUnallocated::FIXED_REGISTER, Register::ToAllocationIndex(reg)); @@ -654,7 +671,7 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { HInstruction* instr = HInstruction::cast(value); VisitInstruction(instr); } - allocator_->RecordUse(value, operand); + operand->set_virtual_register(value->id()); return operand; } @@ -662,19 +679,13 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { template<int I, int T> LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result) { - allocator_->RecordDefinition(current_instruction_, result); + result->set_virtual_register(current_instruction_->id()); instr->set_result(result); return instr; } template<int I, int T> -LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr) { - return Define(instr, new LUnallocated(LUnallocated::NONE)); -} - - -template<int I, int T> LInstruction* LChunkBuilder::DefineAsRegister( LTemplateInstruction<1, I, T>* instr) { return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); @@ -711,7 +722,9 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - instr->set_environment(CreateEnvironment(hydrogen_env)); + int argument_index_accumulator = 0; + instr->set_environment(CreateEnvironment(hydrogen_env, + &argument_index_accumulator)); return instr; } @@ -741,7 +754,7 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, instr->MarkAsCall(); instr = AssignPointerMap(instr); - if (hinstr->HasSideEffects()) { + if (hinstr->HasObservableSideEffects()) { ASSERT(hinstr->next()->IsSimulate()); HSimulate* sim = HSimulate::cast(hinstr->next()); instr = SetInstructionPendingDeoptimizationEnvironment( @@ -753,7 +766,8 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, // Thus we still need to attach environment to this call even if // call sequence can not deoptimize eagerly. bool needs_environment = - (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || !hinstr->HasSideEffects(); + (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || + !hinstr->HasObservableSideEffects(); if (needs_environment && !instr->HasEnvironment()) { instr = AssignEnvironment(instr); } @@ -777,21 +791,22 @@ LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { LUnallocated* LChunkBuilder::TempRegister() { LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER); - allocator_->RecordTemporary(operand); + operand->set_virtual_register(allocator_->GetVirtualRegister()); + if (!allocator_->AllocationOk()) Abort("Not enough virtual registers."); return operand; } LOperand* LChunkBuilder::FixedTemp(Register reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } LOperand* LChunkBuilder::FixedTemp(DoubleRegister reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } @@ -811,28 +826,6 @@ LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { } -LInstruction* LChunkBuilder::DoBit(Token::Value op, - HBitwiseBinaryOperation* instr) { - if (instr->representation().IsInteger32()) { - ASSERT(instr->left()->representation().IsInteger32()); - ASSERT(instr->right()->representation().IsInteger32()); - - LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); - LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); - return DefineAsRegister(new LBitI(op, left, right)); - } else { - ASSERT(instr->representation().IsTagged()); - ASSERT(instr->left()->representation().IsTagged()); - ASSERT(instr->right()->representation().IsTagged()); - - LOperand* left = UseFixed(instr->left(), r1); - LOperand* right = UseFixed(instr->right(), r0); - LArithmeticT* result = new LArithmeticT(op, left, right); - return MarkAsCall(DefineFixed(result, r0), instr); - } -} - - LInstruction* LChunkBuilder::DoShift(Token::Value op, HBitwiseBinaryOperation* instr) { if (instr->representation().IsTagged()) { @@ -994,20 +987,24 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { +LEnvironment* LChunkBuilder::CreateEnvironment( + HEnvironment* hydrogen_env, + int* argument_index_accumulator) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); + LEnvironment* outer = + CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber); + ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); int value_count = hydrogen_env->length(); LEnvironment* result = new LEnvironment(hydrogen_env->closure(), + hydrogen_env->is_arguments_adaptor(), ast_id, hydrogen_env->parameter_count(), argument_count_, value_count, outer); - int argument_index = 0; + int argument_index = *argument_index_accumulator; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1023,6 +1020,10 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { result->AddValue(op, value->representation()); } + if (!hydrogen_env->is_arguments_adaptor()) { + *argument_index_accumulator = argument_index; + } + return result; } @@ -1033,14 +1034,23 @@ LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { - HValue* v = instr->value(); - if (v->EmitAtUses()) { - HBasicBlock* successor = HConstant::cast(v)->ToBoolean() + HValue* value = instr->value(); + if (value->EmitAtUses()) { + HBasicBlock* successor = HConstant::cast(value)->ToBoolean() ? instr->FirstSuccessor() : instr->SecondSuccessor(); return new LGoto(successor->block_id()); } - return AssignEnvironment(new LBranch(UseRegister(v))); + + LBranch* result = new LBranch(UseRegister(value)); + // Tagged values that are not known smis or booleans require a + // deoptimization environment. + Representation rep = value->representation(); + HType type = value->type(); + if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean()) { + return AssignEnvironment(result); + } + return result; } @@ -1148,6 +1158,11 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { LOperand* input = UseFixedDouble(instr->value(), d2); LUnaryMathOperation* result = new LUnaryMathOperation(input, NULL); return MarkAsCall(DefineFixedDouble(result, d2), instr); + } else if (op == kMathPowHalf) { + LOperand* input = UseFixedDouble(instr->value(), d2); + LOperand* temp = FixedTemp(d3); + LUnaryMathOperation* result = new LUnaryMathOperation(input, temp); + return DefineFixedDouble(result, d2); } else { LOperand* input = UseRegisterAtStart(instr->value()); LOperand* temp = (op == kMathFloor) ? TempRegister() : NULL; @@ -1161,8 +1176,6 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { return DefineAsRegister(result); case kMathRound: return AssignEnvironment(DefineAsRegister(result)); - case kMathPowHalf: - return DefineAsRegister(result); default: UNREACHABLE(); return NULL; @@ -1206,8 +1219,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) { LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { + LOperand* function = UseFixed(instr->function(), r1); argument_count_ -= instr->argument_count(); - return MarkAsCall(DefineFixed(new LCallFunction, r0), instr); + return MarkAsCall(DefineFixed(new LCallFunction(function), r0), instr); } @@ -1232,8 +1246,24 @@ LInstruction* LChunkBuilder::DoShl(HShl* instr) { } -LInstruction* LChunkBuilder::DoBitAnd(HBitAnd* instr) { - return DoBit(Token::BIT_AND, instr); +LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + + LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); + LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); + return DefineAsRegister(new LBitI(left, right)); + } else { + ASSERT(instr->representation().IsTagged()); + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + + LOperand* left = UseFixed(instr->left(), r1); + LOperand* right = UseFixed(instr->right(), r0); + LArithmeticT* result = new LArithmeticT(instr->op(), left, right); + return MarkAsCall(DefineFixed(result, r0), instr); + } } @@ -1244,16 +1274,6 @@ LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) { } -LInstruction* LChunkBuilder::DoBitOr(HBitOr* instr) { - return DoBit(Token::BIT_OR, instr); -} - - -LInstruction* LChunkBuilder::DoBitXor(HBitXor* instr) { - return DoBit(Token::BIT_XOR, instr); -} - - LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { if (instr->representation().IsDouble()) { return DoArithmeticD(Token::DIV, instr); @@ -1329,7 +1349,12 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) { } else { left = UseRegisterAtStart(instr->LeastConstantOperand()); } - return AssignEnvironment(DefineAsRegister(new LMulI(left, right, temp))); + LMulI* mul = new LMulI(left, right, temp); + if (instr->CheckFlag(HValue::kCanOverflow) || + instr->CheckFlag(HValue::kBailoutOnMinusZero)) { + AssignEnvironment(mul); + } + return DefineAsRegister(mul); } else if (instr->representation().IsDouble()) { return DoArithmeticD(Token::MUL, instr); @@ -1390,7 +1415,7 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { LOperand* left = UseFixedDouble(instr->left(), d1); LOperand* right = exponent_type.IsDouble() ? UseFixedDouble(instr->right(), d2) : - UseFixed(instr->right(), r0); + UseFixed(instr->right(), r2); LPower* result = new LPower(left, right); return MarkAsCall(DefineFixedDouble(result, d3), instr, @@ -1398,13 +1423,20 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseFixed(instr->global_object(), r0); + LRandom* result = new LRandom(global_object); + return MarkAsCall(DefineFixedDouble(result, d7), instr); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { - Token::Value op = instr->token(); ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); - bool reversed = (op == Token::GT || op == Token::LTE); - LOperand* left = UseFixed(instr->left(), reversed ? r0 : r1); - LOperand* right = UseFixed(instr->right(), reversed ? r1 : r0); + LOperand* left = UseFixed(instr->left(), r1); + LOperand* right = UseFixed(instr->right(), r0); LCmpT* result = new LCmpT(left, right); return MarkAsCall(DefineFixed(result, r0), instr); } @@ -1416,8 +1448,8 @@ LInstruction* LChunkBuilder::DoCompareIDAndBranch( if (r.IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); + LOperand* left = UseRegisterOrConstantAtStart(instr->left()); + LOperand* right = UseRegisterOrConstantAtStart(instr->right()); return new LCmpIDAndBranch(left, right); } else { ASSERT(r.IsDouble()); @@ -1444,9 +1476,9 @@ LInstruction* LChunkBuilder::DoCompareConstantEqAndBranch( } -LInstruction* LChunkBuilder::DoIsNullAndBranch(HIsNullAndBranch* instr) { +LInstruction* LChunkBuilder::DoIsNilAndBranch(HIsNilAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LIsNullAndBranch(UseRegisterAtStart(instr->value())); + return new LIsNilAndBranch(UseRegisterAtStart(instr->value())); } @@ -1457,6 +1489,13 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { } +LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* temp = TempRegister(); + return new LIsStringAndBranch(UseRegisterAtStart(instr->value()), temp); +} + + LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); return new LIsSmiAndBranch(Use(instr->value())); @@ -1471,6 +1510,17 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch( } +LInstruction* LChunkBuilder::DoStringCompareAndBranch( + HStringCompareAndBranch* instr) { + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + LOperand* left = UseFixed(instr->left(), r1); + LOperand* right = UseFixed(instr->right(), r0); + LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right); + return MarkAsCall(result, instr); +} + + LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch( HHasInstanceTypeAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); @@ -1498,7 +1548,7 @@ LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( LInstruction* LChunkBuilder::DoClassOfTestAndBranch( HClassOfTestAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LClassOfTestAndBranch(UseTempRegister(instr->value()), + return new LClassOfTestAndBranch(UseRegister(instr->value()), TempRegister()); } @@ -1525,7 +1575,7 @@ LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LOperand* object = UseRegister(instr->value()); LValueOf* result = new LValueOf(object, TempRegister()); - return AssignEnvironment(DefineAsRegister(result)); + return DefineAsRegister(result); } @@ -1571,11 +1621,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { return AssignEnvironment(DefineAsRegister(res)); } else { ASSERT(to.IsInteger32()); - LOperand* value = UseRegister(instr->value()); + LOperand* value = UseRegisterAtStart(instr->value()); bool needs_check = !instr->value()->type().IsSmi(); LInstruction* res = NULL; if (!needs_check) { - res = DefineSameAsFirst(new LSmiUntag(value, needs_check)); + res = DefineAsRegister(new LSmiUntag(value, needs_check)); } else { LOperand* temp1 = TempRegister(); LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister() @@ -1611,12 +1661,12 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { } else if (from.IsInteger32()) { if (to.IsTagged()) { HValue* val = instr->value(); - LOperand* value = UseRegister(val); + LOperand* value = UseRegisterAtStart(val); if (val->HasRange() && val->range()->IsInSmiRange()) { - return DefineSameAsFirst(new LSmiTag(value)); + return DefineAsRegister(new LSmiTag(value)); } else { LNumberTagI* result = new LNumberTagI(value); - return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); } } else { ASSERT(to.IsDouble()); @@ -1734,7 +1784,7 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { LLoadGlobalCell* result = new LLoadGlobalCell; - return instr->check_hole_value() + return instr->RequiresHoleCheck() ? AssignEnvironment(DefineAsRegister(result)) : DefineAsRegister(result); } @@ -1748,14 +1798,12 @@ LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) { LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) { - if (instr->check_hole_value()) { - LOperand* temp = TempRegister(); - LOperand* value = UseRegister(instr->value()); - return AssignEnvironment(new LStoreGlobalCell(value, temp)); - } else { - LOperand* value = UseRegisterAtStart(instr->value()); - return new LStoreGlobalCell(value, NULL); - } + LOperand* value = UseRegister(instr->value()); + // Use a temp to check the value in the cell in the case where we perform + // a hole check. + return instr->RequiresHoleCheck() + ? AssignEnvironment(new LStoreGlobalCell(value, TempRegister())) + : new LStoreGlobalCell(value, NULL); } @@ -1770,7 +1818,8 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) { LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { LOperand* context = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LLoadContextSlot(context)); + LInstruction* result = DefineAsRegister(new LLoadContextSlot(context)); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } @@ -1784,7 +1833,8 @@ LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { context = UseRegister(instr->context()); value = UseRegister(instr->value()); } - return new LStoreContextSlot(context, value); + LInstruction* result = new LStoreContextSlot(context, value); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } @@ -1843,7 +1893,8 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastElement( LOperand* obj = UseRegisterAtStart(instr->object()); LOperand* key = UseRegisterAtStart(instr->key()); LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); - return AssignEnvironment(DefineAsRegister(result)); + if (instr->RequiresHoleCheck()) AssignEnvironment(result); + return DefineAsRegister(result); } @@ -1862,12 +1913,11 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ElementsKind elements_kind = instr->elements_kind(); - Representation representation(instr->representation()); ASSERT( - (representation.IsInteger32() && + (instr->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); @@ -1907,8 +1957,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( LOperand* key = needs_write_barrier ? UseTempRegister(instr->key()) : UseRegisterOrConstantAtStart(instr->key()); - - return AssignEnvironment(new LStoreKeyedFastElement(obj, key, val)); + return new LStoreKeyedFastElement(obj, key, val); } @@ -1928,13 +1977,12 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { - Representation representation(instr->value()->representation()); ElementsKind elements_kind = instr->elements_kind(); ASSERT( - (representation.IsInteger32() && + (instr->value()->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->value()->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); @@ -1968,6 +2016,26 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { } +LInstruction* LChunkBuilder::DoTransitionElementsKind( + HTransitionElementsKind* instr) { + if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + LOperand* object = UseRegister(instr->object()); + LOperand* new_map_reg = TempRegister(); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, NULL); + return DefineSameAsFirst(result); + } else { + LOperand* object = UseFixed(instr->object(), r0); + LOperand* fixed_object_reg = FixedTemp(r2); + LOperand* new_map_reg = FixedTemp(r3); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, fixed_object_reg); + return MarkAsCall(DefineFixed(result, r0), instr); + } +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool needs_write_barrier = instr->NeedsWriteBarrier(); @@ -2025,8 +2093,14 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { } -LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteral, r0), instr); +LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralFast, r0), instr); +} + + +LInstruction* LChunkBuilder::DoObjectLiteralGeneric( + HObjectLiteralGeneric* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, r0), instr); } @@ -2164,6 +2238,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { HEnvironment* outer = current_block_->last_environment(); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner = outer->CopyForInlining(instr->closure(), + instr->arguments_count(), instr->function(), undefined, instr->call_kind()); @@ -2174,7 +2249,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { - HEnvironment* outer = current_block_->last_environment()->outer(); + HEnvironment* outer = current_block_->last_environment()-> + DiscardInlined(false); current_block_->UpdateEnvironment(outer); return NULL; } diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h index 8c18760fd1..45043593bd 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -107,10 +107,12 @@ class LCodeGen; V(Integer32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ - V(IsNullAndBranch) \ + V(IsNilAndBranch) \ V(IsObjectAndBranch) \ + V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ + V(StringCompareAndBranch) \ V(JSArrayLength) \ V(Label) \ V(LazyBailout) \ @@ -132,12 +134,14 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteral) \ + V(ObjectLiteralFast) \ + V(ObjectLiteralGeneric) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(ShiftI) \ @@ -162,6 +166,7 @@ class LCodeGen; V(ThisFunction) \ V(Throw) \ V(ToFastProperties) \ + V(TransitionElementsKind) \ V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ @@ -627,16 +632,17 @@ class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> { }; -class LIsNullAndBranch: public LControlInstruction<1, 0> { +class LIsNilAndBranch: public LControlInstruction<1, 0> { public: - explicit LIsNullAndBranch(LOperand* value) { + explicit LIsNilAndBranch(LOperand* value) { inputs_[0] = value; } - DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch, "is-null-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsNullAndBranch) + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch) - bool is_strict() const { return hydrogen()->is_strict(); } + EqualityKind kind() const { return hydrogen()->kind(); } + NilValue nil() const { return hydrogen()->nil(); } virtual void PrintDataTo(StringStream* stream); }; @@ -656,6 +662,20 @@ class LIsObjectAndBranch: public LControlInstruction<1, 1> { }; +class LIsStringAndBranch: public LControlInstruction<1, 1> { + public: + LIsStringAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + class LIsSmiAndBranch: public LControlInstruction<1, 0> { public: explicit LIsSmiAndBranch(LOperand* value) { @@ -684,6 +704,23 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> { }; +class LStringCompareAndBranch: public LControlInstruction<2, 0> { + public: + LStringCompareAndBranch(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, + "string-compare-and-branch") + DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) + + Token::Value op() const { return hydrogen()->token(); } + + virtual void PrintDataTo(StringStream* stream); +}; + + class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> { public: explicit LHasInstanceTypeAndBranch(LOperand* value) { @@ -794,18 +831,15 @@ class LBoundsCheck: public LTemplateInstruction<0, 2, 0> { class LBitI: public LTemplateInstruction<1, 2, 0> { public: - LBitI(Token::Value op, LOperand* left, LOperand* right) - : op_(op) { + LBitI(LOperand* left, LOperand* right) { inputs_[0] = left; inputs_[1] = right; } - Token::Value op() const { return op_; } + Token::Value op() const { return hydrogen()->op(); } DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") - - private: - Token::Value op_; + DECLARE_HYDROGEN_ACCESSOR(Bitwise) }; @@ -993,6 +1027,17 @@ class LPower: public LTemplateInstruction<1, 2, 0> { }; +class LRandom: public LTemplateInstruction<1, 1, 0> { + public: + explicit LRandom(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD: public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) @@ -1209,6 +1254,8 @@ class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> { DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) + + LOperand* value() { return inputs_[0]; } }; @@ -1226,7 +1273,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> { LOperand* global_object() { return InputAt(0); } Handle<Object> name() const { return hydrogen()->name(); } LOperand* value() { return InputAt(1); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1259,7 +1306,6 @@ class LStoreContextSlot: public LTemplateInstruction<0, 2, 0> { LOperand* context() { return InputAt(0); } LOperand* value() { return InputAt(1); } int slot_index() { return hydrogen()->slot_index(); } - int needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } virtual void PrintDataTo(StringStream* stream); }; @@ -1276,7 +1322,9 @@ class LPushArgument: public LTemplateInstruction<0, 1, 0> { class LThisFunction: public LTemplateInstruction<1, 0, 0> { + public: DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function") + DECLARE_HYDROGEN_ACCESSOR(ThisFunction) }; @@ -1379,12 +1427,17 @@ class LCallNamed: public LTemplateInstruction<1, 0, 0> { }; -class LCallFunction: public LTemplateInstruction<1, 0, 0> { +class LCallFunction: public LTemplateInstruction<1, 1, 0> { public: + explicit LCallFunction(LOperand* function) { + inputs_[0] = function; + } + DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") DECLARE_HYDROGEN_ACCESSOR(CallFunction) - int arity() const { return hydrogen()->argument_count() - 2; } + LOperand* function() { return inputs_[0]; } + int arity() const { return hydrogen()->argument_count() - 1; } }; @@ -1560,7 +1613,6 @@ class LStoreNamedField: public LTemplateInstruction<0, 2, 0> { Handle<Object> name() const { return hydrogen()->name(); } bool is_in_object() { return hydrogen()->is_in_object(); } int offset() { return hydrogen()->offset(); } - bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } Handle<Map> transition() const { return hydrogen()->transition(); } }; @@ -1580,7 +1632,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> { LOperand* object() { return inputs_[0]; } LOperand* value() { return inputs_[1]; } Handle<Object> name() const { return hydrogen()->name(); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1642,7 +1694,7 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> { LOperand* object() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { @@ -1668,6 +1720,30 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { }; +class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> { + public: + LTransitionElementsKind(LOperand* object, + LOperand* new_map_temp, + LOperand* temp_reg) { + inputs_[0] = object; + temps_[0] = new_map_temp; + temps_[1] = temp_reg; + } + + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, + "transition-elements-kind") + DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* new_map_reg() { return temps_[0]; } + LOperand* temp_reg() { return temps_[1]; } + Handle<Map> original_map() { return hydrogen()->original_map(); } + Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); } +}; + + class LStringAdd: public LTemplateInstruction<1, 2, 0> { public: LStringAdd(LOperand* left, LOperand* right) { @@ -1731,6 +1807,8 @@ class LCheckFunction: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return InputAt(0); } + DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function") DECLARE_HYDROGEN_ACCESSOR(CheckFunction) }; @@ -1838,10 +1916,17 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { }; -class LObjectLiteral: public LTemplateInstruction<1, 0, 0> { +class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) +}; + + +class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) }; @@ -2076,7 +2161,6 @@ class LChunkBuilder BASE_EMBEDDED { void Abort(const char* format, ...); // Methods for getting operands for Use / Define / Temp. - LRegister* ToOperand(Register reg); LUnallocated* ToUnallocated(Register reg); LUnallocated* ToUnallocated(DoubleRegister reg); @@ -2127,8 +2211,6 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result); template<int I, int T> - LInstruction* Define(LTemplateInstruction<1, I, T>* instr); - template<int I, int T> LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr); template<int I, int T> LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr, @@ -2159,12 +2241,12 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, + int* argument_index_accumulator); void VisitInstruction(HInstruction* current); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); - LInstruction* DoBit(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoArithmeticD(Token::Value op, HArithmeticBinaryOperation* instr); diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index 4a201ab987..64ca1a37bf 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -67,6 +67,14 @@ bool LCodeGen::GenerateCode() { status_ = GENERATING; CpuFeatures::Scope scope1(VFP3); CpuFeatures::Scope scope2(ARMv7); + + CodeStub::GenerateFPStubs(); + + // Open a frame scope to indicate that there is a frame on the stack. The + // NONE indicates that the scope shouldn't actually generate code to set up + // the frame (that is done in GeneratePrologue). + FrameScope frame_scope(masm_, StackFrame::NONE); + return GeneratePrologue() && GenerateBody() && GenerateDeferredCode() && @@ -135,7 +143,7 @@ bool LCodeGen::GeneratePrologue() { // with undefined when called as functions (without an explicit // receiver object). r5 is zero for method calls and non-zero for // function calls. - if (info_->is_strict_mode() || info_->is_native()) { + if (!info_->is_classic_mode() || info_->is_native()) { Label ok; __ cmp(r5, Operand(0)); __ b(eq, &ok); @@ -190,13 +198,11 @@ bool LCodeGen::GeneratePrologue() { // Load parameter from stack. __ ldr(r0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ mov(r1, Operand(Context::SlotOffset(var->index()))); - __ str(r0, MemOperand(cp, r1)); - // Update the write barrier. This clobbers all involved - // registers, so we have to use two more registers to avoid - // clobbering cp. - __ mov(r2, Operand(cp)); - __ RecordWrite(r2, Operand(r1), r3, r0); + MemOperand target = ContextOperand(cp, var->index()); + __ str(r0, target); + // Update the write barrier. This clobbers r3 and r0. + __ RecordWriteContextSlot( + cp, target.offset(), r0, r3, kLRHasBeenSaved, kSaveFPRegs); } } Comment(";;; End allocate local context"); @@ -238,6 +244,9 @@ bool LCodeGen::GenerateDeferredCode() { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; __ bind(code->entry()); + Comment(";;; Deferred code @%d: %s.", + code->instruction_index(), + code->instr()->Mnemonic()); code->Generate(); __ jmp(code->exit()); } @@ -253,7 +262,7 @@ bool LCodeGen::GenerateDeferredCode() { bool LCodeGen::GenerateDeoptJumpTable() { // Check that the jump table is accessible from everywhere in the function - // code, ie that offsets to the table can be encoded in the 24bit signed + // code, i.e. that offsets to the table can be encoded in the 24bit signed // immediate of a branch instruction. // To simplify we consider the code size from the first instruction to the // end of the jump table. We also don't consider the pc load delta. @@ -312,7 +321,22 @@ Register LCodeGen::EmitLoadRegister(LOperand* op, Register scratch) { if (op->IsRegister()) { return ToRegister(op->index()); } else if (op->IsConstantOperand()) { - __ mov(scratch, ToOperand(op)); + LConstantOperand* const_op = LConstantOperand::cast(op); + Handle<Object> literal = chunk_->LookupLiteral(const_op); + Representation r = chunk_->LookupLiteralRepresentation(const_op); + if (r.IsInteger32()) { + ASSERT(literal->IsNumber()); + __ mov(scratch, Operand(static_cast<int32_t>(literal->Number()))); + } else if (r.IsDouble()) { + Abort("EmitLoadRegister: Unsupported double immediate."); + } else { + ASSERT(r.IsTagged()); + if (literal->IsSmi()) { + __ mov(scratch, Operand(literal)); + } else { + __ LoadHeapObject(scratch, Handle<HeapObject>::cast(literal)); + } + } return scratch; } else if (op->IsStackSlot() || op->IsArgument()) { __ ldr(scratch, ToMemOperand(op)); @@ -361,6 +385,18 @@ DoubleRegister LCodeGen::EmitLoadDoubleRegister(LOperand* op, } +Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const { + Handle<Object> literal = chunk_->LookupLiteral(op); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + return literal; +} + + +bool LCodeGen::IsInteger32(LConstantOperand* op) const { + return chunk_->LookupLiteralRepresentation(op).IsInteger32(); +} + + int LCodeGen::ToInteger32(LConstantOperand* op) const { Handle<Object> value = chunk_->LookupLiteral(op); ASSERT(chunk_->LookupLiteralRepresentation(op).IsInteger32()); @@ -370,6 +406,12 @@ int LCodeGen::ToInteger32(LConstantOperand* op) const { } +double LCodeGen::ToDouble(LConstantOperand* op) const { + Handle<Object> value = chunk_->LookupLiteral(op); + return value->Number(); +} + + Operand LCodeGen::ToOperand(LOperand* op) { if (op->IsConstantOperand()) { LConstantOperand* const_op = LConstantOperand::cast(op); @@ -437,7 +479,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - translation->BeginFrame(environment->ast_id(), closure_id, height); + if (environment->is_arguments_adaptor()) { + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + } else { + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); // spilled_registers_ and spilled_double_registers_ are either @@ -570,10 +616,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, // |>------------ translation_size ------------<| int frame_count = 0; + int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; + if (!e->is_arguments_adaptor()) { + ++jsframe_count; + } } - Translation translation(&translations_, frame_count); + Translation translation(&translations_, frame_count, jsframe_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); int pc_offset = masm()->pc_offset(); @@ -623,7 +673,6 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) { int length = deoptimizations_.length(); if (length == 0) return; - ASSERT(FLAG_deopt); Handle<DeoptimizationInputData> data = factory()->NewDeoptimizationInputData(length, TENURED); @@ -699,7 +748,7 @@ void LCodeGen::RecordSafepoint( Safepoint::DeoptMode deopt_mode) { ASSERT(expected_safepoint_kind_ == kind); - const ZoneList<LOperand*>* operands = pointers->operands(); + const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands(); Safepoint safepoint = safepoints_.DefineSafepoint(masm(), kind, arguments, deopt_mode); for (int i = 0; i < operands->length(); i++) { @@ -986,6 +1035,7 @@ void LCodeGen::DoDivI(LDivI* instr) { virtual void Generate() { codegen()->DoDeferredBinaryOpStub(instr_, Token::DIV); } + virtual LInstruction* instr() { return instr_; } private: LDivI* instr_; }; @@ -1321,8 +1371,13 @@ void LCodeGen::DoConstantD(LConstantD* instr) { void LCodeGen::DoConstantT(LConstantT* instr) { - ASSERT(instr->result()->IsRegister()); - __ mov(ToRegister(instr->result()), Operand(instr->value())); + Handle<Object> value = instr->value(); + if (value->IsSmi()) { + __ mov(ToRegister(instr->result()), Operand(value)); + } else { + __ LoadHeapObject(ToRegister(instr->result()), + Handle<HeapObject>::cast(value)); + } } @@ -1649,30 +1704,44 @@ Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { } -void LCodeGen::EmitCmpI(LOperand* left, LOperand* right) { - __ cmp(ToRegister(left), ToRegister(right)); -} - - void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { LOperand* left = instr->InputAt(0); LOperand* right = instr->InputAt(1); int false_block = chunk_->LookupDestination(instr->false_block_id()); int true_block = chunk_->LookupDestination(instr->true_block_id()); - - if (instr->is_double()) { - // Compare left and right as doubles and load the - // resulting flags into the normal status register. - __ VFPCompareAndSetFlags(ToDoubleRegister(left), ToDoubleRegister(right)); - // If a NaN is involved, i.e. the result is unordered (V set), - // jump to false block label. - __ b(vs, chunk_->GetAssemblyLabel(false_block)); + Condition cond = TokenToCondition(instr->op(), false); + + if (left->IsConstantOperand() && right->IsConstantOperand()) { + // We can statically evaluate the comparison. + double left_val = ToDouble(LConstantOperand::cast(left)); + double right_val = ToDouble(LConstantOperand::cast(right)); + int next_block = + EvalComparison(instr->op(), left_val, right_val) ? true_block + : false_block; + EmitGoto(next_block); } else { - EmitCmpI(left, right); + if (instr->is_double()) { + // Compare left and right operands as doubles and load the + // resulting flags into the normal status register. + __ VFPCompareAndSetFlags(ToDoubleRegister(left), ToDoubleRegister(right)); + // If a NaN is involved, i.e. the result is unordered (V set), + // jump to false block label. + __ b(vs, chunk_->GetAssemblyLabel(false_block)); + } else { + if (right->IsConstantOperand()) { + __ cmp(ToRegister(left), + Operand(ToInteger32(LConstantOperand::cast(right)))); + } else if (left->IsConstantOperand()) { + __ cmp(ToRegister(right), + Operand(ToInteger32(LConstantOperand::cast(left)))); + // We transposed the operands. Reverse the condition. + cond = ReverseCondition(cond); + } else { + __ cmp(ToRegister(left), ToRegister(right)); + } + } + EmitBranch(true_block, false_block, cond); } - - Condition cc = TokenToCondition(instr->op(), instr->is_double()); - EmitBranch(true_block, false_block, cc); } @@ -1697,25 +1766,35 @@ void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { } -void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) { +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { Register scratch = scratch0(); Register reg = ToRegister(instr->InputAt(0)); + int false_block = chunk_->LookupDestination(instr->false_block_id()); - // TODO(fsc): If the expression is known to be a smi, then it's - // definitely not null. Jump to the false block. + // If the expression is known to be untagged or a smi, then it's definitely + // not null, and it can't be a an undetectable object. + if (instr->hydrogen()->representation().IsSpecialization() || + instr->hydrogen()->type().IsSmi()) { + EmitGoto(false_block); + return; + } int true_block = chunk_->LookupDestination(instr->true_block_id()); - int false_block = chunk_->LookupDestination(instr->false_block_id()); - - __ LoadRoot(ip, Heap::kNullValueRootIndex); + Heap::RootListIndex nil_value = instr->nil() == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; + __ LoadRoot(ip, nil_value); __ cmp(reg, ip); - if (instr->is_strict()) { + if (instr->kind() == kStrictEquality) { EmitBranch(true_block, false_block, eq); } else { + Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); __ b(eq, true_label); - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ LoadRoot(ip, other_nil_value); __ cmp(reg, ip); __ b(eq, true_label); __ JumpIfSmi(reg, false_label); @@ -1772,6 +1851,31 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { } +Condition LCodeGen::EmitIsString(Register input, + Register temp1, + Label* is_not_string) { + __ JumpIfSmi(input, is_not_string); + __ CompareObjectType(input, temp1, temp1, FIRST_NONSTRING_TYPE); + + return lt; +} + + +void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp1 = ToRegister(instr->TempAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = + EmitIsString(reg, temp1, false_label); + + EmitBranch(true_block, false_block, true_cond); +} + + void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1797,6 +1901,41 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { } +static Condition ComputeCompareCondition(Token::Value op) { + switch (op) { + case Token::EQ_STRICT: + case Token::EQ: + return eq; + case Token::LT: + return lt; + case Token::GT: + return gt; + case Token::LTE: + return le; + case Token::GTE: + return ge; + default: + UNREACHABLE(); + return kNoCondition; + } +} + + +void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) { + Token::Value op = instr->op(); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Handle<Code> ic = CompareIC::GetUninitialized(op); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + __ cmp(r0, Operand(0)); // This instruction also signals no smi code inlined. + + Condition condition = ComputeCompareCondition(op); + + EmitBranch(true_block, false_block, condition); +} + + static InstanceType TestType(HHasInstanceTypeAndBranch* instr) { InstanceType from = instr->from(); InstanceType to = instr->to(); @@ -1862,7 +2001,7 @@ void LCodeGen::DoHasCachedArrayIndexAndBranch( // Branches to a label or falls through with the answer in flags. Trashes -// the temp registers, but not the input. Only input and temp2 may alias. +// the temp registers, but not the input. void LCodeGen::EmitClassOfTest(Label* is_true, Label* is_false, Handle<String>class_name, @@ -1870,30 +2009,40 @@ void LCodeGen::EmitClassOfTest(Label* is_true, Register temp, Register temp2) { ASSERT(!input.is(temp)); - ASSERT(!temp.is(temp2)); // But input and temp2 may be the same register. + ASSERT(!input.is(temp2)); + ASSERT(!temp.is(temp2)); + __ JumpIfSmi(input, is_false); - __ CompareObjectType(input, temp, temp2, FIRST_SPEC_OBJECT_TYPE); - __ b(lt, is_false); - // Map is now in temp. - // Functions have class 'Function'. - __ CompareInstanceType(temp, temp2, FIRST_CALLABLE_SPEC_OBJECT_TYPE); if (class_name->IsEqualTo(CStrVector("Function"))) { - __ b(ge, is_true); + // Assuming the following assertions, we can use the same compares to test + // for both being a function type and being in the object type range. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CompareObjectType(input, temp, temp2, FIRST_SPEC_OBJECT_TYPE); + __ b(lt, is_false); + __ b(eq, is_true); + __ cmp(temp2, Operand(LAST_SPEC_OBJECT_TYPE)); + __ b(eq, is_true); } else { - __ b(ge, is_false); + // Faster code path to avoid two compares: subtract lower bound from the + // actual type and do a signed compare with the width of the type range. + __ ldr(temp, FieldMemOperand(input, HeapObject::kMapOffset)); + __ ldrb(temp2, FieldMemOperand(temp, Map::kInstanceTypeOffset)); + __ sub(temp2, temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ cmp(temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE - + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ b(gt, is_false); } + // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range. // Check if the constructor in the map is a function. __ ldr(temp, FieldMemOperand(temp, Map::kConstructorOffset)); - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - // Objects with a non-function constructor have class 'Object'. __ CompareObjectType(temp, temp2, temp2, JS_FUNCTION_TYPE); if (class_name->IsEqualTo(CStrVector("Object"))) { @@ -1970,9 +2119,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { virtual void Generate() { codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } - + virtual LInstruction* instr() { return instr_; } Label* map_check() { return &map_check_; } - private: LInstanceOfKnownGlobal* instr_; Label map_check_; @@ -2002,7 +2150,10 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { // We use Factory::the_hole_value() on purpose instead of loading from the // root array to force relocation to be able to later patch with // the cached map. - __ mov(ip, Operand(factory()->the_hole_value())); + Handle<JSGlobalPropertyCell> cell = + factory()->NewJSGlobalPropertyCell(factory()->the_hole_value()); + __ mov(ip, Operand(Handle<Object>(cell))); + __ ldr(ip, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset)); __ cmp(map, Operand(ip)); __ b(ne, &cache_miss); // We use Factory::the_hole_value() on purpose instead of loading from the @@ -2057,7 +2208,7 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, // offset to the location of the map check. Register temp = ToRegister(instr->TempAt(0)); ASSERT(temp.is(r4)); - __ mov(InstanceofStub::right(), Operand(instr->function())); + __ LoadHeapObject(InstanceofStub::right(), instr->function()); static const int kAdditionalDelta = 4; int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta; Label before_push_delta; @@ -2078,26 +2229,6 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, } -static Condition ComputeCompareCondition(Token::Value op) { - switch (op) { - case Token::EQ_STRICT: - case Token::EQ: - return eq; - case Token::LT: - return lt; - case Token::GT: - return gt; - case Token::LTE: - return le; - case Token::GTE: - return ge; - default: - UNREACHABLE(); - return kNoCondition; - } -} - - void LCodeGen::DoCmpT(LCmpT* instr) { Token::Value op = instr->op(); @@ -2106,9 +2237,6 @@ void LCodeGen::DoCmpT(LCmpT* instr) { __ cmp(r0, Operand(0)); // This instruction also signals no smi code inlined. Condition condition = ComputeCompareCondition(op); - if (op == Token::GT || op == Token::LTE) { - condition = ReverseCondition(condition); - } __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex, condition); @@ -2137,7 +2265,7 @@ void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { Register result = ToRegister(instr->result()); __ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell()))); __ ldr(result, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset)); - if (instr->hydrogen()->check_hole_value()) { + if (instr->hydrogen()->RequiresHoleCheck()) { __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ cmp(result, ip); DeoptimizeIf(eq, instr->environment()); @@ -2158,27 +2286,27 @@ void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) { void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { - Register value = ToRegister(instr->InputAt(0)); - Register scratch = scratch0(); + Register value = ToRegister(instr->value()); + Register cell = scratch0(); // Load the cell. - __ mov(scratch, Operand(Handle<Object>(instr->hydrogen()->cell()))); + __ mov(cell, Operand(instr->hydrogen()->cell())); // If the cell we are storing to contains the hole it could have // been deleted from the property dictionary. In that case, we need // to update the property details in the property dictionary to mark // it as no longer deleted. - if (instr->hydrogen()->check_hole_value()) { - Register scratch2 = ToRegister(instr->TempAt(0)); - __ ldr(scratch2, - FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(scratch2, ip); + if (instr->hydrogen()->RequiresHoleCheck()) { + // We use a temp to check the payload (CompareRoot might clobber ip). + Register payload = ToRegister(instr->TempAt(0)); + __ ldr(payload, FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset)); + __ CompareRoot(payload, Heap::kTheHoleValueRootIndex); DeoptimizeIf(eq, instr->environment()); } // Store the value. - __ str(value, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); + __ str(value, FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset)); + // Cells are always rescanned, so no write barrier here. } @@ -2187,7 +2315,7 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) { ASSERT(ToRegister(instr->value()).is(r0)); __ mov(r2, Operand(instr->name())); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr); @@ -2198,17 +2326,53 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { Register context = ToRegister(instr->context()); Register result = ToRegister(instr->result()); __ ldr(result, ContextOperand(context, instr->slot_index())); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(result, ip); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(eq, instr->environment()); + } else { + __ mov(result, Operand(factory()->undefined_value()), LeaveCC, eq); + } + } } void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { Register context = ToRegister(instr->context()); Register value = ToRegister(instr->value()); - __ str(value, ContextOperand(context, instr->slot_index())); - if (instr->needs_write_barrier()) { - int offset = Context::SlotOffset(instr->slot_index()); - __ RecordWrite(context, Operand(offset), value, scratch0()); + Register scratch = scratch0(); + MemOperand target = ContextOperand(context, instr->slot_index()); + + Label skip_assignment; + + if (instr->hydrogen()->RequiresHoleCheck()) { + __ ldr(scratch, target); + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(scratch, ip); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(eq, instr->environment()); + } else { + __ b(ne, &skip_assignment); + } } + + __ str(value, target); + if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; + __ RecordWriteContextSlot(context, + target.offset(), + value, + scratch, + kLRHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); + } + + __ bind(&skip_assignment); } @@ -2228,9 +2392,9 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, Register object, Handle<Map> type, Handle<String> name) { - LookupResult lookup; + LookupResult lookup(isolate()); type->LookupInDescriptors(NULL, *name, &lookup); - ASSERT(lookup.IsProperty() && + ASSERT(lookup.IsFound() && (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION)); if (lookup.type() == FIELD) { int index = lookup.GetLocalFieldIndexFromMap(*type); @@ -2246,7 +2410,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, } } else { Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type)); - LoadHeapObject(result, Handle<HeapObject>::cast(function)); + __ LoadHeapObject(result, function); } } @@ -2457,13 +2621,9 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); } - if (instr->hydrogen()->RequiresHoleCheck()) { - // TODO(danno): If no hole check is required, there is no need to allocate - // elements into a temporary register, instead scratch can be used. - __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); - __ cmp(scratch, Operand(kHoleNanUpper32)); - DeoptimizeIf(eq, instr->environment()); - } + __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); + __ cmp(scratch, Operand(kHoleNanUpper32)); + DeoptimizeIf(eq, instr->environment()); __ vldr(result, elements, 0); } @@ -2534,6 +2694,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -2674,7 +2835,7 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { this, pointers, Safepoint::kLazyDeopt); // The number of arguments is stored in receiver which is r0, as expected // by InvokeFunction. - v8::internal::ParameterCount actual(receiver); + ParameterCount actual(receiver); __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator, CALL_AS_METHOD); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2694,7 +2855,7 @@ void LCodeGen::DoPushArgument(LPushArgument* instr) { void LCodeGen::DoThisFunction(LThisFunction* instr) { Register result = ToRegister(instr->result()); - __ ldr(result, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ LoadHeapObject(result, instr->hydrogen()->closure()); } @@ -2729,31 +2890,41 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, int arity, LInstruction* instr, CallKind call_kind) { - // Change context if needed. - bool change_context = - (info()->closure()->context() != function->context()) || - scope()->contains_with() || - (scope()->num_heap_slots() > 0); - if (change_context) { - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - } - - // Set r0 to arguments count if adaption is not needed. Assumes that r0 - // is available to write to at this point. - if (!function->NeedsArgumentsAdaption()) { - __ mov(r0, Operand(arity)); - } + bool can_invoke_directly = !function->NeedsArgumentsAdaption() || + function->shared()->formal_parameter_count() == arity; LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); - // Invoke function. - __ SetCallKind(r5, call_kind); - __ ldr(ip, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); - __ Call(ip); + if (can_invoke_directly) { + __ LoadHeapObject(r1, function); + // Change context if needed. + bool change_context = + (info()->closure()->context() != function->context()) || + scope()->contains_with() || + (scope()->num_heap_slots() > 0); + if (change_context) { + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + } - // Setup deoptimization. - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); + // Set r0 to arguments count if adaption is not needed. Assumes that r0 + // is available to write to at this point. + if (!function->NeedsArgumentsAdaption()) { + __ mov(r0, Operand(arity)); + } + + // Invoke function. + __ SetCallKind(r5, call_kind); + __ ldr(ip, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); + __ Call(ip); + + // Set up deoptimization. + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); + } else { + SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); + ParameterCount count(arity); + __ InvokeFunction(function, count, CALL_FUNCTION, generator, call_kind); + } // Restore context. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2762,7 +2933,6 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) { ASSERT(ToRegister(instr->result()).is(r0)); - __ mov(r1, Operand(instr->function())); CallKnownFunction(instr->function(), instr->arity(), instr, @@ -2860,6 +3030,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { virtual void Generate() { codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_); } + virtual LInstruction* instr() { return instr_; } private: LUnaryMathOperation* instr_; }; @@ -2990,68 +3161,77 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); DoubleRegister result = ToDoubleRegister(instr->result()); + DoubleRegister temp = ToDoubleRegister(instr->TempAt(0)); + + // Note that according to ECMA-262 15.8.2.13: + // Math.pow(-Infinity, 0.5) == Infinity + // Math.sqrt(-Infinity) == NaN + Label done; + __ vmov(temp, -V8_INFINITY); + __ VFPCompareAndSetFlags(input, temp); + __ vneg(result, temp, eq); + __ b(&done, eq); + // Add +0 to convert -0 to +0. __ vadd(result, input, kDoubleRegZero); __ vsqrt(result, result); + __ bind(&done); } void LCodeGen::DoPower(LPower* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); - Register scratch = scratch0(); - DoubleRegister result_reg = ToDoubleRegister(instr->result()); Representation exponent_type = instr->hydrogen()->right()->representation(); - if (exponent_type.IsDouble()) { - // Prepare arguments and call C function. - __ PrepareCallCFunction(0, 2, scratch); - __ SetCallCDoubleArguments(ToDoubleRegister(left), - ToDoubleRegister(right)); - __ CallCFunction( - ExternalReference::power_double_double_function(isolate()), 0, 2); + // Having marked this as a call, we can use any registers. + // Just make sure that the input/output registers are the expected ones. + ASSERT(!instr->InputAt(1)->IsDoubleRegister() || + ToDoubleRegister(instr->InputAt(1)).is(d2)); + ASSERT(!instr->InputAt(1)->IsRegister() || + ToRegister(instr->InputAt(1)).is(r2)); + ASSERT(ToDoubleRegister(instr->InputAt(0)).is(d1)); + ASSERT(ToDoubleRegister(instr->result()).is(d3)); + + if (exponent_type.IsTagged()) { + Label no_deopt; + __ JumpIfSmi(r2, &no_deopt); + __ ldr(r7, FieldMemOperand(r2, HeapObject::kMapOffset)); + __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); + __ cmp(r7, Operand(ip)); + DeoptimizeIf(ne, instr->environment()); + __ bind(&no_deopt); + MathPowStub stub(MathPowStub::TAGGED); + __ CallStub(&stub); } else if (exponent_type.IsInteger32()) { - ASSERT(ToRegister(right).is(r0)); - // Prepare arguments and call C function. - __ PrepareCallCFunction(1, 1, scratch); - __ SetCallCDoubleArguments(ToDoubleRegister(left), ToRegister(right)); - __ CallCFunction( - ExternalReference::power_double_int_function(isolate()), 1, 1); + MathPowStub stub(MathPowStub::INTEGER); + __ CallStub(&stub); } else { - ASSERT(exponent_type.IsTagged()); - ASSERT(instr->hydrogen()->left()->representation().IsDouble()); + ASSERT(exponent_type.IsDouble()); + MathPowStub stub(MathPowStub::DOUBLE); + __ CallStub(&stub); + } +} - Register right_reg = ToRegister(right); - // Check for smi on the right hand side. - Label non_smi, call; - __ JumpIfNotSmi(right_reg, &non_smi); +void LCodeGen::DoRandom(LRandom* instr) { + // Having marked this instruction as a call we can use any + // registers. + ASSERT(ToDoubleRegister(instr->result()).is(d7)); + ASSERT(ToRegister(instr->InputAt(0)).is(r0)); - // Untag smi and convert it to a double. - __ SmiUntag(right_reg); - SwVfpRegister single_scratch = double_scratch0().low(); - __ vmov(single_scratch, right_reg); - __ vcvt_f64_s32(result_reg, single_scratch); - __ jmp(&call); + __ PrepareCallCFunction(1, scratch0()); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); - // Heap number map check. - __ bind(&non_smi); - __ ldr(scratch, FieldMemOperand(right_reg, HeapObject::kMapOffset)); - __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); - __ cmp(scratch, Operand(ip)); - DeoptimizeIf(ne, instr->environment()); - int32_t value_offset = HeapNumber::kValueOffset - kHeapObjectTag; - __ add(scratch, right_reg, Operand(value_offset)); - __ vldr(result_reg, scratch, 0); - - // Prepare arguments and call C function. - __ bind(&call); - __ PrepareCallCFunction(0, 2, scratch); - __ SetCallCDoubleArguments(ToDoubleRegister(left), result_reg); - __ CallCFunction( - ExternalReference::power_double_double_function(isolate()), 0, 2); - } - // Store the result in the result register. - __ GetCFunctionDoubleResult(result_reg); + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + // Create this constant using mov/orr to avoid PC relative load. + __ mov(r1, Operand(0x41000000)); + __ orr(r1, r1, Operand(0x300000)); + // Move 0x41300000xxxxxxxx (x = random bits) to VFP. + __ vmov(d7, r0, r1); + // Move 0x4130000000000000 to VFP. + __ mov(r0, Operand(0, RelocInfo::NONE)); + __ vmov(d8, r0, r1); + // Subtract and store the result in the heap number. + __ vsub(d7, d7, d8); } @@ -3063,6 +3243,14 @@ void LCodeGen::DoMathLog(LUnaryMathOperation* instr) { } +void LCodeGen::DoMathTan(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(d2)); + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + void LCodeGen::DoMathCos(LUnaryMathOperation* instr) { ASSERT(ToDoubleRegister(instr->result()).is(d2)); TranscendentalCacheStub stub(TranscendentalCache::COS, @@ -3102,6 +3290,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { case kMathSin: DoMathSin(instr); break; + case kMathTan: + DoMathTan(instr); + break; case kMathLog: DoMathLog(instr); break; @@ -3151,12 +3342,12 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { void LCodeGen::DoCallFunction(LCallFunction* instr) { + ASSERT(ToRegister(instr->function()).is(r1)); ASSERT(ToRegister(instr->result()).is(r0)); int arity = instr->arity(); - CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT); + CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); - __ Drop(1); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); } @@ -3176,7 +3367,6 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) { ASSERT(ToRegister(instr->result()).is(r0)); - __ mov(r1, Operand(instr->target())); CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION); } @@ -3185,9 +3375,9 @@ void LCodeGen::DoCallNew(LCallNew* instr) { ASSERT(ToRegister(instr->InputAt(0)).is(r1)); ASSERT(ToRegister(instr->result()).is(r0)); - Handle<Code> builtin = isolate()->builtins()->JSConstructCall(); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ mov(r0, Operand(instr->arity())); - CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr); + CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr); } @@ -3210,19 +3400,36 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { } // Do the store. + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; if (instr->is_in_object()) { __ str(value, FieldMemOperand(object, offset)); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { // Update the write barrier for the object for in-object properties. - __ RecordWrite(object, Operand(offset), value, scratch); + __ RecordWriteField(object, + offset, + value, + scratch, + kLRHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } else { __ ldr(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset)); __ str(value, FieldMemOperand(scratch, offset)); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { // Update the write barrier for the properties array. // object is used as a scratch register. - __ RecordWrite(scratch, Operand(offset), value, object); + __ RecordWriteField(scratch, + offset, + value, + object, + kLRHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } } @@ -3234,7 +3441,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { // Name is always in r2. __ mov(r2, Operand(instr->name())); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); @@ -3266,9 +3473,18 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { } if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; // Compute address of modified element and store it into key register. - __ add(key, scratch, Operand(FixedArray::kHeaderSize)); - __ RecordWrite(elements, key, value); + __ add(key, scratch, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ RecordWrite(elements, + key, + value, + kLRHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } @@ -3369,6 +3585,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3383,13 +3600,55 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(r1)); ASSERT(ToRegister(instr->value()).is(r0)); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() : isolate()->builtins()->KeyedStoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); } +void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { + Register object_reg = ToRegister(instr->object()); + Register new_map_reg = ToRegister(instr->new_map_reg()); + Register scratch = scratch0(); + + Handle<Map> from_map = instr->original_map(); + Handle<Map> to_map = instr->transitioned_map(); + ElementsKind from_kind = from_map->elements_kind(); + ElementsKind to_kind = to_map->elements_kind(); + + Label not_applicable; + __ ldr(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset)); + __ cmp(scratch, Operand(from_map)); + __ b(ne, ¬_applicable); + __ mov(new_map_reg, Operand(to_map)); + if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + __ str(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset)); + // Write barrier. + __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, + scratch, kLRHasBeenSaved, kDontSaveFPRegs); + } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && + to_kind == FAST_DOUBLE_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(r2)); + ASSERT(new_map_reg.is(r3)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), + RelocInfo::CODE_TARGET, instr); + } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(r2)); + ASSERT(new_map_reg.is(r3)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(), + RelocInfo::CODE_TARGET, instr); + } else { + UNREACHABLE(); + } + __ bind(¬_applicable); +} + + void LCodeGen::DoStringAdd(LStringAdd* instr) { __ push(ToRegister(instr->left())); __ push(ToRegister(instr->right())); @@ -3404,87 +3663,19 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharCodeAt* instr_; }; - Register string = ToRegister(instr->string()); - Register index = ToRegister(instr->index()); - Register result = ToRegister(instr->result()); - DeferredStringCharCodeAt* deferred = new DeferredStringCharCodeAt(this, instr); - // Fetch the instance type of the receiver into result register. - __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); - __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); - - // We need special handling for indirect strings. - Label check_sequential; - __ tst(result, Operand(kIsIndirectStringMask)); - __ b(eq, &check_sequential); - - // Dispatch on the indirect string shape: slice or cons. - Label cons_string; - __ tst(result, Operand(kSlicedNotConsMask)); - __ b(eq, &cons_string); - - // Handle slices. - Label indirect_string_loaded; - __ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset)); - __ add(index, index, Operand(result, ASR, kSmiTagSize)); - __ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset)); - __ jmp(&indirect_string_loaded); - - // Handle conses. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - __ bind(&cons_string); - __ ldr(result, FieldMemOperand(string, ConsString::kSecondOffset)); - __ LoadRoot(ip, Heap::kEmptyStringRootIndex); - __ cmp(result, ip); - __ b(ne, deferred->entry()); - // Get the first of the two strings and load its instance type. - __ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset)); - - __ bind(&indirect_string_loaded); - __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); - __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); - - // Check whether the string is sequential. The only non-sequential - // shapes we support have just been unwrapped above. - __ bind(&check_sequential); - STATIC_ASSERT(kSeqStringTag == 0); - __ tst(result, Operand(kStringRepresentationMask)); - __ b(ne, deferred->entry()); - - // Dispatch on the encoding: ASCII or two-byte. - Label ascii_string; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ tst(result, Operand(kStringEncodingMask)); - __ b(ne, &ascii_string); - - // Two-byte string. - // Load the two-byte character code into the result register. - Label done; - __ add(result, - string, - Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - __ ldrh(result, MemOperand(result, index, LSL, 1)); - __ jmp(&done); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ add(result, - string, - Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ ldrb(result, MemOperand(result, index)); - - __ bind(&done); + StringCharLoadGenerator::Generate(masm(), + ToRegister(instr->string()), + ToRegister(instr->index()), + ToRegister(instr->result()), + deferred->entry()); __ bind(deferred->exit()); } @@ -3527,6 +3718,7 @@ void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharFromCode* instr_; }; @@ -3598,16 +3790,16 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) { DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); } + virtual LInstruction* instr() { return instr_; } private: LNumberTagI* instr_; }; - LOperand* input = instr->InputAt(0); - ASSERT(input->IsRegister() && input->Equals(instr->result())); - Register reg = ToRegister(input); + Register src = ToRegister(instr->InputAt(0)); + Register dst = ToRegister(instr->result()); DeferredNumberTagI* deferred = new DeferredNumberTagI(this, instr); - __ SmiTag(reg, SetCC); + __ SmiTag(dst, src, SetCC); __ b(vs, deferred->entry()); __ bind(deferred->exit()); } @@ -3615,7 +3807,8 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) { void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) { Label slow; - Register reg = ToRegister(instr->InputAt(0)); + Register src = ToRegister(instr->InputAt(0)); + Register dst = ToRegister(instr->result()); DoubleRegister dbl_scratch = double_scratch0(); SwVfpRegister flt_scratch = dbl_scratch.low(); @@ -3626,14 +3819,16 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) { // disagree. Try to allocate a heap number in new space and store // the value in there. If that fails, call the runtime system. Label done; - __ SmiUntag(reg); - __ eor(reg, reg, Operand(0x80000000)); - __ vmov(flt_scratch, reg); + if (dst.is(src)) { + __ SmiUntag(src, dst); + __ eor(src, src, Operand(0x80000000)); + } + __ vmov(flt_scratch, src); __ vcvt_f64_s32(dbl_scratch, flt_scratch); if (FLAG_inline_new) { __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex); __ AllocateHeapNumber(r5, r3, r4, r6, &slow); - if (!reg.is(r5)) __ mov(reg, r5); + __ Move(dst, r5); __ b(&done); } @@ -3644,16 +3839,16 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) { // register is stored, as this register is in the pointer map, but contains an // integer value. __ mov(ip, Operand(0)); - __ StoreToSafepointRegisterSlot(ip, reg); + __ StoreToSafepointRegisterSlot(ip, dst); CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); - if (!reg.is(r0)) __ mov(reg, r0); + __ Move(dst, r0); // Done. Put the value in dbl_scratch into the value of the allocated heap // number. __ bind(&done); - __ sub(ip, reg, Operand(kHeapObjectTag)); + __ sub(ip, dst, Operand(kHeapObjectTag)); __ vstr(dbl_scratch, ip, HeapNumber::kValueOffset); - __ StoreToSafepointRegisterSlot(reg, reg); + __ StoreToSafepointRegisterSlot(dst, dst); } @@ -3663,6 +3858,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } + virtual LInstruction* instr() { return instr_; } private: LNumberTagD* instr_; }; @@ -3700,23 +3896,21 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { void LCodeGen::DoSmiTag(LSmiTag* instr) { - LOperand* input = instr->InputAt(0); - ASSERT(input->IsRegister() && input->Equals(instr->result())); ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)); - __ SmiTag(ToRegister(input)); + __ SmiTag(ToRegister(instr->result()), ToRegister(instr->InputAt(0))); } void LCodeGen::DoSmiUntag(LSmiUntag* instr) { - LOperand* input = instr->InputAt(0); - ASSERT(input->IsRegister() && input->Equals(instr->result())); + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); if (instr->needs_check()) { STATIC_ASSERT(kHeapObjectTag == 1); // If the input is a HeapObject, SmiUntag will set the carry flag. - __ SmiUntag(ToRegister(input), SetCC); + __ SmiUntag(result, input, SetCC); DeoptimizeIf(cs, instr->environment()); } else { - __ SmiUntag(ToRegister(input)); + __ SmiUntag(result, input); } } @@ -3724,6 +3918,7 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagD(Register input_reg, DoubleRegister result_reg, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env) { Register scratch = scratch0(); SwVfpRegister flt_scratch = double_scratch0().low(); @@ -3732,7 +3927,7 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, Label load_smi, heap_number, done; // Smi check. - __ JumpIfSmi(input_reg, &load_smi); + __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi); // Heap number map check. __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset)); @@ -3759,28 +3954,25 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, // Heap number to double register conversion. __ sub(ip, input_reg, Operand(kHeapObjectTag)); __ vldr(result_reg, ip, HeapNumber::kValueOffset); + if (deoptimize_on_minus_zero) { + __ vmov(ip, result_reg.low()); + __ cmp(ip, Operand(0)); + __ b(ne, &done); + __ vmov(ip, result_reg.high()); + __ cmp(ip, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(eq, env); + } __ jmp(&done); // Smi to double register conversion __ bind(&load_smi); - __ SmiUntag(input_reg); // Untag smi before converting to float. - __ vmov(flt_scratch, input_reg); + // scratch: untagged value of input_reg + __ vmov(flt_scratch, scratch); __ vcvt_f64_s32(result_reg, flt_scratch); - __ SmiTag(input_reg); // Retag smi. __ bind(&done); } -class DeferredTaggedToI: public LDeferredCode { - public: - DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } - private: - LTaggedToI* instr_; -}; - - void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { Register input_reg = ToRegister(instr->InputAt(0)); Register scratch1 = scratch0(); @@ -3863,6 +4055,16 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { void LCodeGen::DoTaggedToI(LTaggedToI* instr) { + class DeferredTaggedToI: public LDeferredCode { + public: + DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LTaggedToI* instr_; + }; + LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); ASSERT(input->Equals(instr->result())); @@ -3892,6 +4094,7 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { EmitNumberUntagD(input_reg, result_reg, instr->hydrogen()->deoptimize_on_undefined(), + instr->hydrogen()->deoptimize_on_minus_zero(), instr->environment()); } @@ -3989,21 +4192,42 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckFunction(LCheckFunction* instr) { - ASSERT(instr->InputAt(0)->IsRegister()); - Register reg = ToRegister(instr->InputAt(0)); - __ cmp(reg, Operand(instr->hydrogen()->target())); + Register reg = ToRegister(instr->value()); + Handle<JSFunction> target = instr->hydrogen()->target(); + if (isolate()->heap()->InNewSpace(*target)) { + Register reg = ToRegister(instr->value()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(target); + __ mov(ip, Operand(Handle<Object>(cell))); + __ ldr(ip, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset)); + __ cmp(reg, ip); + } else { + __ cmp(reg, Operand(target)); + } DeoptimizeIf(ne, instr->environment()); } +void LCodeGen::DoCheckMapCommon(Register reg, + Register scratch, + Handle<Map> map, + CompareMapMode mode, + LEnvironment* env) { + Label success; + __ CompareMap(reg, scratch, map, &success, mode); + DeoptimizeIf(ne, env); + __ bind(&success); +} + + void LCodeGen::DoCheckMap(LCheckMap* instr) { Register scratch = scratch0(); LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); Register reg = ToRegister(input); - __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset)); - __ cmp(scratch, Operand(instr->hydrogen()->map())); - DeoptimizeIf(ne, instr->environment()); + Handle<Map> map = instr->hydrogen()->map(); + DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(), + instr->environment()); } @@ -4030,7 +4254,7 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { Label is_smi, done, heap_number; // Both smi and heap number cases are handled. - __ JumpIfSmi(input_reg, &is_smi); + __ UntagAndJumpIfSmi(result_reg, input_reg, &is_smi); // Check for heap number __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset)); @@ -4053,26 +4277,12 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { // smi __ bind(&is_smi); - __ SmiUntag(result_reg, input_reg); __ ClampUint8(result_reg, result_reg); __ bind(&done); } -void LCodeGen::LoadHeapObject(Register result, - Handle<HeapObject> object) { - if (heap()->InNewSpace(*object)) { - Handle<JSGlobalPropertyCell> cell = - factory()->NewJSGlobalPropertyCell(object); - __ mov(result, Operand(cell)); - __ ldr(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset)); - } else { - __ mov(result, Operand(object)); - } -} - - void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Register temp1 = ToRegister(instr->TempAt(0)); Register temp2 = ToRegister(instr->TempAt(1)); @@ -4081,31 +4291,53 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Handle<JSObject> current_prototype = instr->prototype(); // Load prototype object. - LoadHeapObject(temp1, current_prototype); + __ LoadHeapObject(temp1, current_prototype); // Check prototype maps up to the holder. while (!current_prototype.is_identical_to(holder)) { - __ ldr(temp2, FieldMemOperand(temp1, HeapObject::kMapOffset)); - __ cmp(temp2, Operand(Handle<Map>(current_prototype->map()))); - DeoptimizeIf(ne, instr->environment()); + DoCheckMapCommon(temp1, temp2, + Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); current_prototype = Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype())); // Load next prototype object. - LoadHeapObject(temp1, current_prototype); + __ LoadHeapObject(temp1, current_prototype); } // Check the holder map. - __ ldr(temp2, FieldMemOperand(temp1, HeapObject::kMapOffset)); - __ cmp(temp2, Operand(Handle<Map>(current_prototype->map()))); + DoCheckMapCommon(temp1, temp2, + Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); DeoptimizeIf(ne, instr->environment()); } void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { + Heap* heap = isolate()->heap(); + ElementsKind boilerplate_elements_kind = + instr->hydrogen()->boilerplate_elements_kind(); + + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to FAST_ELEMENTS. + if (boilerplate_elements_kind != FAST_ELEMENTS) { + __ LoadHeapObject(r1, instr->hydrogen()->boilerplate_object()); + // Load map into r2. + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); + // Load the map's "bit field 2". + __ ldrb(r2, FieldMemOperand(r2, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ ubfx(r2, r2, Map::kElementsKindShift, Map::kElementsKindBitCount); + __ cmp(r2, Operand(boilerplate_elements_kind)); + DeoptimizeIf(ne, instr->environment()); + } + __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); __ mov(r2, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); - __ mov(r1, Operand(instr->hydrogen()->constant_elements())); + // Boilerplate already exists, constant elements are never accessed. + // Pass an empty fixed array. + __ mov(r1, Operand(Handle<FixedArray>(heap->empty_fixed_array()))); __ Push(r3, r2, r1); // Pick the right runtime function or stub to call. @@ -4122,26 +4354,106 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr); } else { FastCloneShallowArrayStub::Mode mode = - FastCloneShallowArrayStub::CLONE_ELEMENTS; + boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS + ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ELEMENTS; FastCloneShallowArrayStub stub(mode, length); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } -void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { +void LCodeGen::EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset) { + ASSERT(!source.is(r2)); + ASSERT(!result.is(r2)); + + // Increase the offset so that subsequent objects end up right after + // this one. + int current_offset = *offset; + int size = object->map()->instance_size(); + *offset += size; + + // Copy object header. + ASSERT(object->properties()->length() == 0); + ASSERT(object->elements()->length() == 0 || + object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); + int inobject_properties = object->map()->inobject_properties(); + int header_size = size - inobject_properties * kPointerSize; + for (int i = 0; i < header_size; i += kPointerSize) { + __ ldr(r2, FieldMemOperand(source, i)); + __ str(r2, FieldMemOperand(result, current_offset + i)); + } + + // Copy in-object properties. + for (int i = 0; i < inobject_properties; i++) { + int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + __ add(r2, result, Operand(*offset)); + __ str(r2, FieldMemOperand(result, total_offset)); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(r2, Handle<HeapObject>::cast(value)); + __ str(r2, FieldMemOperand(result, total_offset)); + } else { + __ mov(r2, Operand(value)); + __ str(r2, FieldMemOperand(result, total_offset)); + } + } +} + + +void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { + int size = instr->hydrogen()->total_size(); + + // Allocate all objects that are part of the literal in one big + // allocation. This avoids multiple limit checks. + Label allocated, runtime_allocate; + __ AllocateInNewSpace(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ mov(r0, Operand(Smi::FromInt(size))); + __ push(r0); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + + __ bind(&allocated); + int offset = 0; + __ LoadHeapObject(r1, instr->hydrogen()->boilerplate()); + EmitDeepCopy(instr->hydrogen()->boilerplate(), r0, r1, &offset); + ASSERT_EQ(size, offset); +} + + +void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { + Handle<FixedArray> constant_properties = + instr->hydrogen()->constant_properties(); + __ ldr(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ ldr(r4, FieldMemOperand(r4, JSFunction::kLiteralsOffset)); __ mov(r3, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); - __ mov(r2, Operand(instr->hydrogen()->constant_properties())); - __ mov(r1, Operand(Smi::FromInt(instr->hydrogen()->fast_elements() ? 1 : 0))); + __ mov(r2, Operand(constant_properties)); + int flags = instr->hydrogen()->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + __ mov(r1, Operand(Smi::FromInt(flags))); __ Push(r4, r3, r2, r1); // Pick the right runtime function to call. + int properties_count = constant_properties->length() / 2; if (instr->hydrogen()->depth() > 1) { CallRuntime(Runtime::kCreateObjectLiteral, 4, instr); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr); + } else { + FastCloneShallowObjectStub stub(properties_count); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } @@ -4214,8 +4526,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { Handle<SharedFunctionInfo> shared_info = instr->shared_info(); bool pretenure = instr->hydrogen()->pretenure(); if (!pretenure && shared_info->num_literals() == 0) { - FastNewClosureStub stub( - shared_info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(shared_info->language_mode()); __ mov(r1, Operand(shared_info)); __ push(r1); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); @@ -4248,8 +4559,9 @@ void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { false_label, input, instr->type_literal()); - - EmitBranch(true_block, false_block, final_branch_condition); + if (final_branch_condition != kNoCondition) { + EmitBranch(true_block, false_block, final_branch_condition); + } } @@ -4295,10 +4607,12 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = ne; } else if (type_name->Equals(heap()->function_symbol())) { + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ JumpIfSmi(input, false_label); - __ CompareObjectType(input, input, scratch, - FIRST_CALLABLE_SPEC_OBJECT_TYPE); - final_branch_condition = ge; + __ CompareObjectType(input, scratch, input, JS_FUNCTION_TYPE); + __ b(eq, true_label); + __ cmp(input, Operand(JS_FUNCTION_PROXY_TYPE)); + final_branch_condition = eq; } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); @@ -4317,9 +4631,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = eq; } else { - final_branch_condition = ne; __ b(false_label); - // A dead branch instruction will be generated after this point. } return final_branch_condition; @@ -4430,6 +4742,7 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStackCheck* instr_; }; diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h index 0e34c9f854..00823e1638 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.h +++ b/deps/v8/src/arm/lithium-codegen-arm.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -87,11 +87,15 @@ class LCodeGen BASE_EMBEDDED { SwVfpRegister flt_scratch, DoubleRegister dbl_scratch); int ToInteger32(LConstantOperand* op) const; + double ToDouble(LConstantOperand* op) const; Operand ToOperand(LOperand* op); MemOperand ToMemOperand(LOperand* op) const; // Returns a MemOperand pointing to the high word of a DoubleStackSlot. MemOperand ToHighMemOperand(LOperand* op) const; + bool IsInteger32(LConstantOperand* op) const; + Handle<Object> ToHandle(LConstantOperand* op) const; + // Try to generate code for the entire chunk, but it may fail if the // chunk contains constructs we cannot handle. Returns true if the // code generation attempt succeeded. @@ -115,6 +119,9 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); + void DoCheckMapCommon(Register reg, Register scratch, Handle<Map> map, + CompareMapMode mode, LEnvironment* env); + // Parallel move support. void DoParallelMove(LParallelMove* move); void DoGap(LGap* instr); @@ -140,8 +147,8 @@ class LCodeGen BASE_EMBEDDED { bool is_done() const { return status_ == DONE; } bool is_aborted() const { return status_ == ABORTED; } - int strict_mode_flag() const { - return info()->is_strict_mode() ? kStrictMode : kNonStrictMode; + StrictModeFlag strict_mode_flag() const { + return info()->is_classic_mode() ? kNonStrictMode : kStrictMode; } LChunk* chunk() const { return chunk_; } @@ -207,7 +214,7 @@ class LCodeGen BASE_EMBEDDED { LInstruction* instr); // Generate a direct call to a known function. Expects the function - // to be in edi. + // to be in r1. void CallKnownFunction(Handle<JSFunction> function, int arity, LInstruction* instr, @@ -241,6 +248,7 @@ class LCodeGen BASE_EMBEDDED { void DoMathSqrt(LUnaryMathOperation* instr); void DoMathPowHalf(LUnaryMathOperation* instr); void DoMathLog(LUnaryMathOperation* instr); + void DoMathTan(LUnaryMathOperation* instr); void DoMathCos(LUnaryMathOperation* instr); void DoMathSin(LUnaryMathOperation* instr); @@ -262,17 +270,19 @@ class LCodeGen BASE_EMBEDDED { static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); void EmitBranch(int left_block, int right_block, Condition cc); - void EmitCmpI(LOperand* left, LOperand* right); void EmitNumberUntagD(Register input, DoubleRegister result, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env); // Emits optimized code for typeof x == "y". Modifies input register. // Returns the condition on which a final split to // true and false label should be made, to optimize fallthrough. - Condition EmitTypeofIs(Label* true_label, Label* false_label, - Register input, Handle<String> type_name); + Condition EmitTypeofIs(Label* true_label, + Label* false_label, + Register input, + Handle<String> type_name); // Emits optimized code for %_IsObject(x). Preserves input register. // Returns the condition on which a final split to @@ -282,6 +292,13 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsString(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsString(Register input, + Register temp1, + Label* is_not_string); + // Emits optimized code for %_IsConstructCall(). // Caller should branch on equal condition. void EmitIsConstructCall(Register temp1, Register temp2); @@ -291,6 +308,13 @@ class LCodeGen BASE_EMBEDDED { Handle<Map> type, Handle<String> name); + // Emits optimized code to deep-copy the contents of statically known + // object graphs (e.g. object literal boilerplate). + void EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset); + struct JumpTableEntry { explicit inline JumpTableEntry(Address entry) : label(), @@ -378,16 +402,20 @@ class LCodeGen BASE_EMBEDDED { class LDeferredCode: public ZoneObject { public: explicit LDeferredCode(LCodeGen* codegen) - : codegen_(codegen), external_exit_(NULL) { + : codegen_(codegen), + external_exit_(NULL), + instruction_index_(codegen->current_instruction_) { codegen->AddDeferredCode(this); } virtual ~LDeferredCode() { } virtual void Generate() = 0; + virtual LInstruction* instr() = 0; - void SetExit(Label *exit) { external_exit_ = exit; } + void SetExit(Label* exit) { external_exit_ = exit; } Label* entry() { return &entry_; } Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; } + int instruction_index() const { return instruction_index_; } protected: LCodeGen* codegen() const { return codegen_; } @@ -398,6 +426,7 @@ class LDeferredCode: public ZoneObject { Label entry_; Label exit_; Label* external_exit_; + int instruction_index_; }; } } // namespace v8::internal diff --git a/deps/v8/src/arm/lithium-gap-resolver-arm.cc b/deps/v8/src/arm/lithium-gap-resolver-arm.cc index 1cfdc791cc..cefca476ad 100644 --- a/deps/v8/src/arm/lithium-gap-resolver-arm.cc +++ b/deps/v8/src/arm/lithium-gap-resolver-arm.cc @@ -245,13 +245,24 @@ void LGapResolver::EmitMove(int index) { } } else if (source->IsConstantOperand()) { - Operand source_operand = cgen_->ToOperand(source); + LConstantOperand* constant_source = LConstantOperand::cast(source); if (destination->IsRegister()) { - __ mov(cgen_->ToRegister(destination), source_operand); + Register dst = cgen_->ToRegister(destination); + if (cgen_->IsInteger32(constant_source)) { + __ mov(dst, Operand(cgen_->ToInteger32(constant_source))); + } else { + __ LoadObject(dst, cgen_->ToHandle(constant_source)); + } } else { ASSERT(destination->IsStackSlot()); ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone. - __ mov(kSavedValueRegister, source_operand); + if (cgen_->IsInteger32(constant_source)) { + __ mov(kSavedValueRegister, + Operand(cgen_->ToInteger32(constant_source))); + } else { + __ LoadObject(kSavedValueRegister, + cgen_->ToHandle(constant_source)); + } __ str(kSavedValueRegister, cgen_->ToMemOperand(destination)); } diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 7a1f802450..2f0e5fa459 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -42,7 +42,8 @@ namespace internal { MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) : Assembler(arg_isolate, buffer, size), generating_stub_(false), - allow_stub_calls_(true) { + allow_stub_calls_(true), + has_frame_(false) { if (isolate() != NULL) { code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), isolate()); @@ -406,29 +407,16 @@ void MacroAssembler::StoreRoot(Register source, } -void MacroAssembler::RecordWriteHelper(Register object, - Register address, - Register scratch) { - if (emit_debug_code()) { - // Check that the object is not in new space. - Label not_in_new_space; - InNewSpace(object, scratch, ne, ¬_in_new_space); - Abort("new-space object passed to RecordWriteHelper"); - bind(¬_in_new_space); +void MacroAssembler::LoadHeapObject(Register result, + Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + mov(result, Operand(cell)); + ldr(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset)); + } else { + mov(result, Operand(object)); } - - // Calculate page address. - Bfc(object, 0, kPageSizeBits); - - // Calculate region number. - Ubfx(address, address, Page::kRegionSizeLog2, - kPageSizeBits - Page::kRegionSizeLog2); - - // Mark region dirty. - ldr(scratch, MemOperand(object, Page::kDirtyFlagOffset)); - mov(ip, Operand(1)); - orr(scratch, scratch, Operand(ip, LSL, address)); - str(scratch, MemOperand(object, Page::kDirtyFlagOffset)); } @@ -443,38 +431,52 @@ void MacroAssembler::InNewSpace(Register object, } -// Will clobber 4 registers: object, offset, scratch, ip. The -// register 'object' contains a heap object pointer. The heap object -// tag is shifted away. -void MacroAssembler::RecordWrite(Register object, - Operand offset, - Register scratch0, - Register scratch1) { - // The compiled code assumes that record write doesn't change the - // context register, so we check that none of the clobbered - // registers are cp. - ASSERT(!object.is(cp) && !scratch0.is(cp) && !scratch1.is(cp)); - +void MacroAssembler::RecordWriteField( + Register object, + int offset, + Register value, + Register dst, + LinkRegisterStatus lr_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis. Label done; - // First, test that the object is not in the new space. We cannot set - // region marks for new space pages. - InNewSpace(object, scratch0, eq, &done); + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done); + } - // Add offset into the object. - add(scratch0, object, offset); + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + ASSERT(IsAligned(offset, kPointerSize)); - // Record the actual write. - RecordWriteHelper(object, scratch0, scratch1); + add(dst, object, Operand(offset - kHeapObjectTag)); + if (emit_debug_code()) { + Label ok; + tst(dst, Operand((1 << kPointerSizeLog2) - 1)); + b(eq, &ok); + stop("Unaligned cell in write barrier"); + bind(&ok); + } + + RecordWrite(object, + dst, + value, + lr_status, + save_fp, + remembered_set_action, + OMIT_SMI_CHECK); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered input registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - mov(object, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch0, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch1, Operand(BitCast<int32_t>(kZapValue))); + mov(value, Operand(BitCast<int32_t>(kZapValue + 4))); + mov(dst, Operand(BitCast<int32_t>(kZapValue + 8))); } } @@ -484,29 +486,100 @@ void MacroAssembler::RecordWrite(Register object, // tag is shifted away. void MacroAssembler::RecordWrite(Register object, Register address, - Register scratch) { + Register value, + LinkRegisterStatus lr_status, + SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { // The compiled code assumes that record write doesn't change the // context register, so we check that none of the clobbered // registers are cp. - ASSERT(!object.is(cp) && !address.is(cp) && !scratch.is(cp)); + ASSERT(!address.is(cp) && !value.is(cp)); + + if (emit_debug_code()) { + ldr(ip, MemOperand(address)); + cmp(ip, value); + Check(eq, "Wrong address or value passed to RecordWrite"); + } Label done; - // First, test that the object is not in the new space. We cannot set - // region marks for new space pages. - InNewSpace(object, scratch, eq, &done); + if (smi_check == INLINE_SMI_CHECK) { + ASSERT_EQ(0, kSmiTag); + tst(value, Operand(kSmiTagMask)); + b(eq, &done); + } + + CheckPageFlag(value, + value, // Used as scratch. + MemoryChunk::kPointersToHereAreInterestingMask, + eq, + &done); + CheckPageFlag(object, + value, // Used as scratch. + MemoryChunk::kPointersFromHereAreInterestingMask, + eq, + &done); // Record the actual write. - RecordWriteHelper(object, address, scratch); + if (lr_status == kLRHasNotBeenSaved) { + push(lr); + } + RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); + CallStub(&stub); + if (lr_status == kLRHasNotBeenSaved) { + pop(lr); + } bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - mov(object, Operand(BitCast<int32_t>(kZapValue))); - mov(address, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch, Operand(BitCast<int32_t>(kZapValue))); + mov(address, Operand(BitCast<int32_t>(kZapValue + 12))); + mov(value, Operand(BitCast<int32_t>(kZapValue + 16))); + } +} + + +void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. + Register address, + Register scratch, + SaveFPRegsMode fp_mode, + RememberedSetFinalAction and_then) { + Label done; + if (emit_debug_code()) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok); + stop("Remembered set pointer is in new space"); + bind(&ok); + } + // Load store buffer top. + ExternalReference store_buffer = + ExternalReference::store_buffer_top(isolate()); + mov(ip, Operand(store_buffer)); + ldr(scratch, MemOperand(ip)); + // Store pointer to buffer and increment buffer top. + str(address, MemOperand(scratch, kPointerSize, PostIndex)); + // Write back new top of buffer. + str(scratch, MemOperand(ip)); + // Call stub on end of buffer. + // Check for end of buffer. + tst(scratch, Operand(StoreBuffer::kStoreBufferOverflowBit)); + if (and_then == kFallThroughAtEnd) { + b(eq, &done); + } else { + ASSERT(and_then == kReturnAtEnd); + Ret(eq); + } + push(lr); + StoreBufferOverflowStub store_buffer_overflow = + StoreBufferOverflowStub(fp_mode); + CallStub(&store_buffer_overflow); + pop(lr); + bind(&done); + if (and_then == kReturnAtEnd) { + Ret(); } } @@ -744,12 +817,12 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) { - // Setup the frame structure on the stack. + // Set up the frame structure on the stack. ASSERT_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement); ASSERT_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset); ASSERT_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset); Push(lr, fp); - mov(fp, Operand(sp)); // Setup new frame pointer. + mov(fp, Operand(sp)); // Set up new frame pointer. // Reserve room for saved entry sp and code object. sub(sp, sp, Operand(2 * kPointerSize)); if (emit_debug_code()) { @@ -884,10 +957,12 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, Handle<Code> code_constant, Register code_reg, Label* done, + bool* definitely_mismatches, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { bool definitely_matches = false; + *definitely_mismatches = false; Label regular_invoke; // Check whether the expected and actual arguments count match. If not, @@ -918,6 +993,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, // arguments. definitely_matches = true; } else { + *definitely_mismatches = true; mov(r2, Operand(expected.immediate())); } } @@ -945,7 +1021,9 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, SetCallKind(r5, call_kind); Call(adaptor); call_wrapper.AfterCall(); - b(done); + if (!*definitely_mismatches) { + b(done); + } } else { SetCallKind(r5, call_kind); Jump(adaptor, RelocInfo::CODE_TARGET); @@ -961,24 +1039,30 @@ void MacroAssembler::InvokeCode(Register code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { - Label done; + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); - InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag, + Label done; + bool definitely_mismatches = false; + InvokePrologue(expected, actual, Handle<Code>::null(), code, + &done, &definitely_mismatches, flag, call_wrapper, call_kind); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - SetCallKind(r5, call_kind); - Call(code); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(r5, call_kind); - Jump(code); - } + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + SetCallKind(r5, call_kind); + Call(code); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(r5, call_kind); + Jump(code); + } - // Continue here if InvokePrologue does handle the invocation due to - // mismatched parameter counts. - bind(&done); + // Continue here if InvokePrologue does handle the invocation due to + // mismatched parameter counts. + bind(&done); + } } @@ -988,21 +1072,27 @@ void MacroAssembler::InvokeCode(Handle<Code> code, RelocInfo::Mode rmode, InvokeFlag flag, CallKind call_kind) { - Label done; + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); - InvokePrologue(expected, actual, code, no_reg, &done, flag, + Label done; + bool definitely_mismatches = false; + InvokePrologue(expected, actual, code, no_reg, + &done, &definitely_mismatches, flag, NullCallWrapper(), call_kind); - if (flag == CALL_FUNCTION) { - SetCallKind(r5, call_kind); - Call(code, rmode); - } else { - SetCallKind(r5, call_kind); - Jump(code, rmode); - } + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + SetCallKind(r5, call_kind); + Call(code, rmode); + } else { + SetCallKind(r5, call_kind); + Jump(code, rmode); + } - // Continue here if InvokePrologue does handle the invocation due to - // mismatched parameter counts. - bind(&done); + // Continue here if InvokePrologue does handle the invocation due to + // mismatched parameter counts. + bind(&done); + } } @@ -1011,6 +1101,9 @@ void MacroAssembler::InvokeFunction(Register fun, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + // Contract with called JS functions requires that function is passed in r1. ASSERT(fun.is(r1)); @@ -1031,28 +1124,24 @@ void MacroAssembler::InvokeFunction(Register fun, } -void MacroAssembler::InvokeFunction(JSFunction* function, +void MacroAssembler::InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper, CallKind call_kind) { - ASSERT(function->is_compiled()); + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); // Get the function and setup the context. - mov(r1, Operand(Handle<JSFunction>(function))); + LoadHeapObject(r1, function); ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - // Invoke the cached code. - Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); - if (V8::UseCrankshaft()) { - // TODO(kasperl): For now, we always call indirectly through the - // code field in the function to allow recompilation to take effect - // without changing any of the call sites. - ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); - InvokeCode(r3, expected, actual, flag, NullCallWrapper(), call_kind); - } else { - InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag, call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); + InvokeCode(r3, expected, actual, flag, call_wrapper, call_kind); } @@ -1090,56 +1179,49 @@ void MacroAssembler::IsObjectJSStringType(Register object, #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::DebugBreak() { - ASSERT(allow_stub_calls()); mov(r0, Operand(0, RelocInfo::NONE)); mov(r1, Operand(ExternalReference(Runtime::kDebugBreak, isolate()))); CEntryStub ces(1); + ASSERT(AllowThisStubCall(&ces)); Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); } #endif -void MacroAssembler::PushTryHandler(CodeLocation try_location, - HandlerType type) { +void MacroAssembler::PushTryHandler(StackHandler::Kind kind, + int handler_index) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - - // The pc (return address) is passed in register lr. - if (try_location == IN_JAVASCRIPT) { - if (type == TRY_CATCH_HANDLER) { - mov(r3, Operand(StackHandler::TRY_CATCH)); - } else { - mov(r3, Operand(StackHandler::TRY_FINALLY)); - } - stm(db_w, sp, r3.bit() | cp.bit() | fp.bit() | lr.bit()); - // Save the current handler as the next handler. - mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - ldr(r1, MemOperand(r3)); - push(r1); - // Link this handler as the new current one. - str(sp, MemOperand(r3)); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // For the JSEntry handler, we must preserve r0-r4, r5-r7 are available. + // We will build up the handler from the bottom by pushing on the stack. + // Set up the code object (r5) and the state (r6) for pushing. + unsigned state = + StackHandler::IndexField::encode(handler_index) | + StackHandler::KindField::encode(kind); + mov(r5, Operand(CodeObject())); + mov(r6, Operand(state)); + + // Push the frame pointer, context, state, and code object. + if (kind == StackHandler::JS_ENTRY) { + mov(r7, Operand(Smi::FromInt(0))); // Indicates no context. + mov(ip, Operand(0, RelocInfo::NONE)); // NULL frame pointer. + stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | ip.bit()); } else { - // Must preserve r0-r4, r5-r7 are available. - ASSERT(try_location == IN_JS_ENTRY); - // The frame pointer does not point to a JS frame so we save NULL - // for fp. We expect the code throwing an exception to check fp - // before dereferencing it to restore the context. - mov(r5, Operand(StackHandler::ENTRY)); // State. - mov(r6, Operand(Smi::FromInt(0))); // Indicates no context. - mov(r7, Operand(0, RelocInfo::NONE)); // NULL frame pointer. - stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | lr.bit()); - // Save the current handler as the next handler. - mov(r7, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - ldr(r6, MemOperand(r7)); - push(r6); - // Link this handler as the new current one. - str(sp, MemOperand(r7)); + stm(db_w, sp, r5.bit() | r6.bit() | cp.bit() | fp.bit()); } + + // Link the current handler as the next handler. + mov(r6, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); + ldr(r5, MemOperand(r6)); + push(r5); + // Set this new handler as the current one. + str(sp, MemOperand(r6)); } @@ -1152,42 +1234,50 @@ void MacroAssembler::PopTryHandler() { } +void MacroAssembler::JumpToHandlerEntry() { + // Compute the handler entry address and jump to it. The handler table is + // a fixed array of (smi-tagged) code offsets. + // r0 = exception, r1 = code object, r2 = state. + ldr(r3, FieldMemOperand(r1, Code::kHandlerTableOffset)); // Handler table. + add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + mov(r2, Operand(r2, LSR, StackHandler::kKindWidth)); // Handler index. + ldr(r2, MemOperand(r3, r2, LSL, kPointerSizeLog2)); // Smi-tagged offset. + add(r1, r1, Operand(Code::kHeaderSize - kHeapObjectTag)); // Code start. + add(pc, r1, Operand(r2, ASR, kSmiTagSize)); // Jump. +} + + void MacroAssembler::Throw(Register value) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // r0 is expected to hold the exception. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // The exception is expected in r0. if (!value.is(r0)) { mov(r0, value); } - - // Drop the sp to the top of the handler. + // Drop the stack pointer to the top of the top handler. mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); ldr(sp, MemOperand(r3)); - // Restore the next handler. pop(r2); str(r2, MemOperand(r3)); - // Restore context and frame pointer, discard state (r3). - ldm(ia_w, sp, r3.bit() | cp.bit() | fp.bit()); + // Get the code object (r1) and state (r2). Restore the context and frame + // pointer. + ldm(ia_w, sp, r1.bit() | r2.bit() | cp.bit() | fp.bit()); // If the handler is a JS frame, restore the context to the frame. - // (r3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any - // of them. - cmp(r3, Operand(StackHandler::ENTRY)); + // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp + // or cp. + tst(cp, cp); str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); -#ifdef DEBUG - if (emit_debug_code()) { - mov(lr, Operand(pc)); - } -#endif - pop(pc); + JumpToHandlerEntry(); } @@ -1196,41 +1286,16 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // r0 is expected to hold the exception. - if (!value.is(r0)) { - mov(r0, value); - } - - // Drop sp to the top stack handler. - mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - ldr(sp, MemOperand(r3)); - - // Unwind the handlers until the ENTRY handler is found. - Label loop, done; - bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - ldr(r2, MemOperand(sp, kStateOffset)); - cmp(r2, Operand(StackHandler::ENTRY)); - b(eq, &done); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - ldr(sp, MemOperand(sp, kNextOffset)); - jmp(&loop); - bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - pop(r2); - str(r2, MemOperand(r3)); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + // The exception is expected in r0. if (type == OUT_OF_MEMORY) { // Set external caught exception to false. - ExternalReference external_caught( - Isolate::kExternalCaughtExceptionAddress, isolate()); + ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress, + isolate()); mov(r0, Operand(false, RelocInfo::NONE)); mov(r2, Operand(external_caught)); str(r0, MemOperand(r2)); @@ -1241,22 +1306,34 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate()))); str(r0, MemOperand(r2)); + } else if (!value.is(r0)) { + mov(r0, value); } - // Stack layout at this point. See also StackHandlerConstants. - // sp -> state (ENTRY) - // cp - // fp - // lr + // Drop the stack pointer to the top of the top stack handler. + mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); + ldr(sp, MemOperand(r3)); - // Restore context and frame pointer, discard state (r2). - ldm(ia_w, sp, r2.bit() | cp.bit() | fp.bit()); -#ifdef DEBUG - if (emit_debug_code()) { - mov(lr, Operand(pc)); - } -#endif - pop(pc); + // Unwind the handlers until the ENTRY handler is found. + Label fetch_next, check_kind; + jmp(&check_kind); + bind(&fetch_next); + ldr(sp, MemOperand(sp, StackHandlerConstants::kNextOffset)); + + bind(&check_kind); + STATIC_ASSERT(StackHandler::JS_ENTRY == 0); + ldr(r2, MemOperand(sp, StackHandlerConstants::kStateOffset)); + tst(r2, Operand(StackHandler::KindField::kMask)); + b(ne, &fetch_next); + + // Set the top handler address to next handler past the top ENTRY handler. + pop(r2); + str(r2, MemOperand(r3)); + // Get the code object (r1) and state (r2). Clear the context and frame + // pointer (0 was saved in the handler). + ldm(ia_w, sp, r1.bit() | r2.bit() | cp.bit() | fp.bit()); + + JumpToHandlerEntry(); } @@ -1358,8 +1435,9 @@ void MacroAssembler::GetNumberHash(Register t0, Register scratch) { // hash = hash ^ (hash >> 4); eor(t0, t0, Operand(t0, LSR, 4)); // hash = hash * 2057; - mov(scratch, Operand(2057)); - mul(t0, t0, scratch); + mov(scratch, Operand(t0, LSL, 11)); + add(t0, t0, Operand(t0, LSL, 3)); + add(t0, t0, scratch); // hash = hash ^ (hash >> 16); eor(t0, t0, Operand(t0, LSR, 16)); } @@ -1548,6 +1626,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, ASSERT(!result.is(scratch1)); ASSERT(!result.is(scratch2)); ASSERT(!scratch1.is(scratch2)); + ASSERT(!object_size.is(ip)); ASSERT(!result.is(ip)); ASSERT(!scratch1.is(ip)); ASSERT(!scratch2.is(ip)); @@ -1805,25 +1884,170 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + b(hi, fail); +} + + +void MacroAssembler::CheckFastObjectElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + b(ls, fail); cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); b(hi, fail); } +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + b(hi, fail); +} + + +void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, + Register key_reg, + Register receiver_reg, + Register elements_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Label* fail) { + Label smi_value, maybe_nan, have_double_value, is_nan, done; + Register mantissa_reg = scratch2; + Register exponent_reg = scratch3; + + // Handle smi values specially. + JumpIfSmi(value_reg, &smi_value); + + // Ensure that the object is a heap number + CheckMap(value_reg, + scratch1, + isolate()->factory()->heap_number_map(), + fail, + DONT_DO_SMI_CHECK); + + // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000 + // in the exponent. + mov(scratch1, Operand(kNaNOrInfinityLowerBoundUpper32)); + ldr(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset)); + cmp(exponent_reg, scratch1); + b(ge, &maybe_nan); + + ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + + bind(&have_double_value); + add(scratch1, elements_reg, + Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); + str(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize)); + uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); + str(exponent_reg, FieldMemOperand(scratch1, offset)); + jmp(&done); + + bind(&maybe_nan); + // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise + // it's an Infinity, and the non-NaN code path applies. + b(gt, &is_nan); + ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + cmp(mantissa_reg, Operand(0)); + b(eq, &have_double_value); + bind(&is_nan); + // Load canonical NaN for storing into the double array. + uint64_t nan_int64 = BitCast<uint64_t>( + FixedDoubleArray::canonical_not_the_hole_nan_as_double()); + mov(mantissa_reg, Operand(static_cast<uint32_t>(nan_int64))); + mov(exponent_reg, Operand(static_cast<uint32_t>(nan_int64 >> 32))); + jmp(&have_double_value); + + bind(&smi_value); + add(scratch1, elements_reg, + Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + add(scratch1, scratch1, + Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); + // scratch1 is now effective address of the double element + + FloatingPointHelper::Destination destination; + if (CpuFeatures::IsSupported(VFP3)) { + destination = FloatingPointHelper::kVFPRegisters; + } else { + destination = FloatingPointHelper::kCoreRegisters; + } + + Register untagged_value = receiver_reg; + SmiUntag(untagged_value, value_reg); + FloatingPointHelper::ConvertIntToDouble(this, + untagged_value, + destination, + d0, + mantissa_reg, + exponent_reg, + scratch4, + s2); + if (destination == FloatingPointHelper::kVFPRegisters) { + CpuFeatures::Scope scope(VFP3); + vstr(d0, scratch1, 0); + } else { + str(mantissa_reg, MemOperand(scratch1, 0)); + str(exponent_reg, MemOperand(scratch1, Register::kSizeInBytes)); + } + bind(&done); +} + + +void MacroAssembler::CompareMap(Register obj, + Register scratch, + Handle<Map> map, + Label* early_success, + CompareMapMode mode) { + ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); + cmp(scratch, Operand(map)); + if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { + Map* transitioned_fast_element_map( + map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); + ASSERT(transitioned_fast_element_map == NULL || + map->elements_kind() != FAST_ELEMENTS); + if (transitioned_fast_element_map != NULL) { + b(eq, early_success); + cmp(scratch, Operand(Handle<Map>(transitioned_fast_element_map))); + } + + Map* transitioned_double_map( + map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); + ASSERT(transitioned_double_map == NULL || + map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); + if (transitioned_double_map != NULL) { + b(eq, early_success); + cmp(scratch, Operand(Handle<Map>(transitioned_double_map))); + } + } +} + + void MacroAssembler::CheckMap(Register obj, Register scratch, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type) { + SmiCheckType smi_check_type, + CompareMapMode mode) { if (smi_check_type == DO_SMI_CHECK) { JumpIfSmi(obj, fail); } - ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - mov(ip, Operand(map)); - cmp(scratch, ip); + + Label success; + CompareMap(obj, scratch, map, &success, mode); b(ne, fail); + bind(&success); } @@ -1862,7 +2086,8 @@ void MacroAssembler::DispatchMap(Register obj, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. JumpIfSmi(function, miss); @@ -1870,6 +2095,16 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE); b(ne, miss); + if (miss_on_bound_function) { + ldr(scratch, + FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); + ldr(scratch, + FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); + tst(scratch, + Operand(Smi::FromInt(1 << SharedFunctionInfo::kBoundFunction))); + b(ne, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); @@ -1907,47 +2142,24 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, void MacroAssembler::CallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. Call(stub->GetCode(), RelocInfo::CODE_TARGET, kNoASTId, cond); } -MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Handle<Code> code(Code::cast(result)); - Call(code, RelocInfo::CODE_TARGET, kNoASTId, cond); - return result; -} - - void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe()); Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond); } -MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Jump(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond); - return result; -} - - static int AddressOffset(ExternalReference ref0, ExternalReference ref1) { return ref0.address() - ref1.address(); } -MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( - ExternalReference function, int stack_space) { +void MacroAssembler::CallApiFunctionAndReturn(ExternalReference function, + int stack_space) { ExternalReference next_address = ExternalReference::handle_scope_next_address(); const int kNextOffset = 0; @@ -2010,14 +2222,10 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( mov(pc, lr); bind(&promote_scheduled_exception); - MaybeObject* result - = TryTailCallExternalReference( - ExternalReference(Runtime::kPromoteScheduledException, isolate()), - 0, - 1); - if (result->IsFailure()) { - return result; - } + TailCallExternalReference( + ExternalReference(Runtime::kPromoteScheduledException, isolate()), + 0, + 1); // HandleScope limit has changed. Delete allocated extensions. bind(&delete_allocated_handles); @@ -2029,8 +2237,12 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( ExternalReference::delete_handle_scope_extensions(isolate()), 1); mov(r0, r4); jmp(&leave_exit_frame); +} - return result; + +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { + if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false; + return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(); } @@ -2178,7 +2390,7 @@ void MacroAssembler::ConvertToInt32(Register source, b(gt, not_int32); // We know the exponent is smaller than 30 (biased). If it is less than - // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie + // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, i.e. // it rounds to zero. const uint32_t zero_exponent = HeapNumber::kExponentBias + 0; sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC); @@ -2429,8 +2641,7 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { const Runtime::Function* function = Runtime::FunctionForId(id); mov(r0, Operand(function->nargs)); mov(r1, Operand(ExternalReference(function, isolate()))); - CEntryStub stub(1); - stub.SaveDoubles(); + CEntryStub stub(1, kSaveFPRegs); CallStub(&stub); } @@ -2457,17 +2668,6 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } -MaybeObject* MacroAssembler::TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size) { - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - mov(r0, Operand(num_arguments)); - return TryJumpToExternalReference(ext); -} - - void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -2488,21 +2688,12 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) { } -MaybeObject* MacroAssembler::TryJumpToExternalReference( - const ExternalReference& builtin) { -#if defined(__thumb__) - // Thumb mode builtin. - ASSERT((reinterpret_cast<intptr_t>(builtin.address()) & 1) == 1); -#endif - mov(r1, Operand(builtin)); - CEntryStub stub(1); - return TryTailCallStub(&stub); -} - - void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, const CallWrapper& call_wrapper) { + // You can't call a builtin without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + GetBuiltinEntry(r2, id); if (flag == CALL_FUNCTION) { call_wrapper.BeforeCall(CallSize(r2)); @@ -2634,14 +2825,20 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif - // Disable stub call restrictions to always allow calls to abort. - AllowStubCallsScope allow_scope(this, true); mov(r0, Operand(p0)); push(r0); mov(r0, Operand(Smi::FromInt(p1 - p0))); push(r0); - CallRuntime(Runtime::kAbort, 2); + // Disable stub call restrictions to always allow calls to abort. + if (!has_frame_) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + CallRuntime(Runtime::kAbort, 2); + } else { + CallRuntime(Runtime::kAbort, 2); + } // will not return here if (is_const_pool_blocked()) { // If the calling code cares about the exact number of @@ -2673,6 +2870,47 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match) { + // Load the global or builtins object from the current context. + ldr(scratch, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); + + // Check that the function's map is the same as the expected cached map. + int expected_index = + Context::GetContextMapIndexFromElementsKind(expected_kind); + ldr(ip, MemOperand(scratch, Context::SlotOffset(expected_index))); + cmp(map_in_out, ip); + b(ne, no_map_match); + + // Use the transitioned cached map. + int trans_index = + Context::GetContextMapIndexFromElementsKind(transitioned_kind); + ldr(map_in_out, MemOperand(scratch, Context::SlotOffset(trans_index))); +} + + +void MacroAssembler::LoadInitialArrayMap( + Register function_in, Register scratch, Register map_out) { + ASSERT(!function_in.is(map_out)); + Label done; + ldr(map_out, FieldMemOperand(function_in, + JSFunction::kPrototypeOrInitialMapOffset)); + if (!FLAG_smi_only_arrays) { + LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + map_out, + scratch, + &done); + } + bind(&done); +} + + void MacroAssembler::LoadGlobalFunction(int index, Register function) { // Load the global or builtins object from the current context. ldr(function, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); @@ -2733,6 +2971,22 @@ void MacroAssembler::JumpIfNotBothSmi(Register reg1, } +void MacroAssembler::UntagAndJumpIfSmi( + Register dst, Register src, Label* smi_case) { + STATIC_ASSERT(kSmiTag == 0); + mov(dst, Operand(src, ASR, kSmiTagSize), SetCC); + b(cc, smi_case); // Shifter carry is not set for a smi. +} + + +void MacroAssembler::UntagAndJumpIfNotSmi( + Register dst, Register src, Label* non_smi_case) { + STATIC_ASSERT(kSmiTag == 0); + mov(dst, Operand(src, ASR, kSmiTagSize), SetCC); + b(cs, non_smi_case); // Shifter carry is set for a non-smi. +} + + void MacroAssembler::JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi) { @@ -2942,6 +3196,19 @@ void MacroAssembler::CopyBytes(Register src, } +void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler) { + Label loop, entry; + b(&entry); + bind(&loop); + str(filler, MemOperand(start_offset, kPointerSize, PostIndex)); + bind(&entry); + cmp(start_offset, end_offset); + b(lt, &loop); +} + + void MacroAssembler::CountLeadingZeros(Register zeros, // Answer. Register source, // Input. Register scratch) { @@ -2953,8 +3220,10 @@ void MacroAssembler::CountLeadingZeros(Register zeros, // Answer. #ifdef CAN_USE_ARMV5_INSTRUCTIONS clz(zeros, source); // This instruction is only supported after ARM5. #else - mov(zeros, Operand(0, RelocInfo::NONE)); + // Order of the next two lines is important: zeros register + // can be the same as source register. Move(scratch, source); + mov(zeros, Operand(0, RelocInfo::NONE)); // Top 16. tst(scratch, Operand(0xffff0000)); add(zeros, zeros, Operand(16), LeaveCC, eq); @@ -3101,23 +3370,15 @@ void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg, void MacroAssembler::CallCFunction(ExternalReference function, int num_reg_arguments, int num_double_arguments) { - CallCFunctionHelper(no_reg, - function, - ip, - num_reg_arguments, - num_double_arguments); + mov(ip, Operand(function)); + CallCFunctionHelper(ip, num_reg_arguments, num_double_arguments); } void MacroAssembler::CallCFunction(Register function, - Register scratch, - int num_reg_arguments, - int num_double_arguments) { - CallCFunctionHelper(function, - ExternalReference::the_hole_value_location(isolate()), - scratch, - num_reg_arguments, - num_double_arguments); + int num_reg_arguments, + int num_double_arguments) { + CallCFunctionHelper(function, num_reg_arguments, num_double_arguments); } @@ -3128,17 +3389,15 @@ void MacroAssembler::CallCFunction(ExternalReference function, void MacroAssembler::CallCFunction(Register function, - Register scratch, int num_arguments) { - CallCFunction(function, scratch, num_arguments, 0); + CallCFunction(function, num_arguments, 0); } void MacroAssembler::CallCFunctionHelper(Register function, - ExternalReference function_reference, - Register scratch, int num_reg_arguments, int num_double_arguments) { + ASSERT(has_frame()); // Make sure that the stack is aligned before calling a C function unless // running in the simulator. The simulator has its own alignment check which // provides more information. @@ -3162,10 +3421,6 @@ void MacroAssembler::CallCFunctionHelper(Register function, // Just call directly. The function called cannot cause a GC, or // allow preemption, so the return address in the link register // stays correct. - if (function.is(no_reg)) { - mov(scratch, Operand(function_reference)); - function = scratch; - } Call(function); int stack_passed_arguments = CalculateStackPassedWords( num_reg_arguments, num_double_arguments); @@ -3197,6 +3452,185 @@ void MacroAssembler::GetRelocatedValueLocation(Register ldr_location, } +void MacroAssembler::CheckPageFlag( + Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met) { + and_(scratch, object, Operand(~Page::kPageAlignmentMask)); + ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); + tst(scratch, Operand(mask)); + b(cc, condition_met); +} + + +void MacroAssembler::JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black) { + HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern. + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); +} + + +void MacroAssembler::HasColor(Register object, + Register bitmap_scratch, + Register mask_scratch, + Label* has_color, + int first_bit, + int second_bit) { + ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, no_reg)); + + GetMarkBits(object, bitmap_scratch, mask_scratch); + + Label other_color, word_boundary; + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + tst(ip, Operand(mask_scratch)); + b(first_bit == 1 ? eq : ne, &other_color); + // Shift left 1 by adding. + add(mask_scratch, mask_scratch, Operand(mask_scratch), SetCC); + b(eq, &word_boundary); + tst(ip, Operand(mask_scratch)); + b(second_bit == 1 ? ne : eq, has_color); + jmp(&other_color); + + bind(&word_boundary); + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize)); + tst(ip, Operand(1)); + b(second_bit == 1 ? ne : eq, has_color); + bind(&other_color); +} + + +// Detect some, but not all, common pointer-free objects. This is used by the +// incremental write barrier which doesn't care about oddballs (they are always +// marked black immediately so this code is not hit). +void MacroAssembler::JumpIfDataObject(Register value, + Register scratch, + Label* not_data_object) { + Label is_data_object; + ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset)); + CompareRoot(scratch, Heap::kHeapNumberMapRootIndex); + b(eq, &is_data_object); + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); + tst(scratch, Operand(kIsIndirectStringMask | kIsNotStringMask)); + b(ne, not_data_object); + bind(&is_data_object); +} + + +void MacroAssembler::GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg) { + ASSERT(!AreAliased(addr_reg, bitmap_reg, mask_reg, no_reg)); + and_(bitmap_reg, addr_reg, Operand(~Page::kPageAlignmentMask)); + Ubfx(mask_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); + const int kLowBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; + Ubfx(ip, addr_reg, kLowBits, kPageSizeBits - kLowBits); + add(bitmap_reg, bitmap_reg, Operand(ip, LSL, kPointerSizeLog2)); + mov(ip, Operand(1)); + mov(mask_reg, Operand(ip, LSL, mask_reg)); +} + + +void MacroAssembler::EnsureNotWhite( + Register value, + Register bitmap_scratch, + Register mask_scratch, + Register load_scratch, + Label* value_is_white_and_not_data) { + ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, ip)); + GetMarkBits(value, bitmap_scratch, mask_scratch); + + // If the value is black or grey we don't need to do anything. + ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); + ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + Label done; + + // Since both black and grey have a 1 in the first position and white does + // not have a 1 there we only need to check one bit. + ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + tst(mask_scratch, load_scratch); + b(ne, &done); + + if (emit_debug_code()) { + // Check for impossible bit pattern. + Label ok; + // LSL may overflow, making the check conservative. + tst(load_scratch, Operand(mask_scratch, LSL, 1)); + b(eq, &ok); + stop("Impossible marking bit pattern"); + bind(&ok); + } + + // Value is white. We check whether it is data that doesn't need scanning. + // Currently only checks for HeapNumber and non-cons strings. + Register map = load_scratch; // Holds map while checking type. + Register length = load_scratch; // Holds length of object after testing type. + Label is_data_object; + + // Check for heap-number + ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); + CompareRoot(map, Heap::kHeapNumberMapRootIndex); + mov(length, Operand(HeapNumber::kSize), LeaveCC, eq); + b(eq, &is_data_object); + + // Check for strings. + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + Register instance_type = load_scratch; + ldrb(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); + tst(instance_type, Operand(kIsIndirectStringMask | kIsNotStringMask)); + b(ne, value_is_white_and_not_data); + // It's a non-indirect (non-cons and non-slice) string. + // If it's external, the length is just ExternalString::kSize. + // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). + // External strings are the only ones with the kExternalStringTag bit + // set. + ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); + ASSERT_EQ(0, kConsStringTag & kExternalStringTag); + tst(instance_type, Operand(kExternalStringTag)); + mov(length, Operand(ExternalString::kSize), LeaveCC, ne); + b(ne, &is_data_object); + + // Sequential string, either ASCII or UC16. + // For ASCII (char-size of 1) we shift the smi tag away to get the length. + // For UC16 (char-size of 2) we just leave the smi tag in place, thereby + // getting the length multiplied by 2. + ASSERT(kAsciiStringTag == 4 && kStringEncodingMask == 4); + ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + ldr(ip, FieldMemOperand(value, String::kLengthOffset)); + tst(instance_type, Operand(kStringEncodingMask)); + mov(ip, Operand(ip, LSR, 1), LeaveCC, ne); + add(length, ip, Operand(SeqString::kHeaderSize + kObjectAlignmentMask)); + and_(length, length, Operand(~kObjectAlignmentMask)); + + bind(&is_data_object); + // Value is a data object, and it is white. Mark it black. Since we know + // that the object is white we can make it black by flipping one bit. + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + orr(ip, ip, Operand(mask_scratch)); + str(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + + and_(bitmap_scratch, bitmap_scratch, Operand(~Page::kPageAlignmentMask)); + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + add(ip, ip, Operand(length)); + str(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + + bind(&done); +} + + void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) { Usat(output_reg, 8, Operand(input_reg)); } @@ -3246,6 +3680,17 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +bool AreAliased(Register r1, Register r2, Register r3, Register r4) { + if (r1.is(r2)) return true; + if (r1.is(r3)) return true; + if (r1.is(r4)) return true; + if (r2.is(r3)) return true; + if (r2.is(r4)) return true; + if (r3.is(r4)) return true; + return false; +} + + CodePatcher::CodePatcher(byte* address, int instructions) : address_(address), instructions_(instructions), diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 0546e6a15e..45cca9042a 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,7 @@ #define V8_ARM_MACRO_ASSEMBLER_ARM_H_ #include "assembler.h" +#include "frames.h" #include "v8globals.h" namespace v8 { @@ -38,12 +39,12 @@ namespace internal { // Static helper functions // Generate a MemOperand for loading a field from an object. -static inline MemOperand FieldMemOperand(Register object, int offset) { +inline MemOperand FieldMemOperand(Register object, int offset) { return MemOperand(object, offset - kHeapObjectTag); } -static inline Operand SmiUntagOperand(Register object) { +inline Operand SmiUntagOperand(Register object) { return Operand(object, ASR, kSmiTagSize); } @@ -79,6 +80,14 @@ enum ObjectToDoubleFlags { }; +enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; +enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved }; + + +bool AreAliased(Register r1, Register r2, Register r3, Register r4); + + // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: @@ -157,40 +166,136 @@ class MacroAssembler: public Assembler { Heap::RootListIndex index, Condition cond = al); + void LoadHeapObject(Register dst, Handle<HeapObject> object); - // Check if object is in new space. - // scratch can be object itself, but it will be clobbered. - void InNewSpace(Register object, - Register scratch, - Condition cond, // eq for new space, ne otherwise - Label* branch); + void LoadObject(Register result, Handle<Object> object) { + if (object->IsHeapObject()) { + LoadHeapObject(result, Handle<HeapObject>::cast(object)); + } else { + Move(result, object); + } + } + // --------------------------------------------------------------------------- + // GC Support + + void IncrementalMarkingRecordWriteHelper(Register object, + Register value, + Register address); + + enum RememberedSetFinalAction { + kReturnAtEnd, + kFallThroughAtEnd + }; + + // Record in the remembered set the fact that we have a pointer to new space + // at the address pointed to by the addr register. Only works if addr is not + // in new space. + void RememberedSetHelper(Register object, // Used for debug code. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then); + + void CheckPageFlag(Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met); + + // Check if object is in new space. Jumps if the object is not in new space. + // The register scratch can be object itself, but scratch will be clobbered. + void JumpIfNotInNewSpace(Register object, + Register scratch, + Label* branch) { + InNewSpace(object, scratch, ne, branch); + } - // For the page containing |object| mark the region covering [address] - // dirty. The object address must be in the first 8K of an allocated page. - void RecordWriteHelper(Register object, - Register address, - Register scratch); + // Check if object is in new space. Jumps if the object is in new space. + // The register scratch can be object itself, but it will be clobbered. + void JumpIfInNewSpace(Register object, + Register scratch, + Label* branch) { + InNewSpace(object, scratch, eq, branch); + } + + // Check if an object has a given incremental marking color. + void HasColor(Register object, + Register scratch0, + Register scratch1, + Label* has_color, + int first_bit, + int second_bit); - // For the page containing |object| mark the region covering - // [object+offset] dirty. The object address must be in the first 8K - // of an allocated page. The 'scratch' registers are used in the - // implementation and all 3 registers are clobbered by the - // operation, as well as the ip register. RecordWrite updates the - // write barrier even when storing smis. - void RecordWrite(Register object, - Operand offset, + void JumpIfBlack(Register object, Register scratch0, - Register scratch1); + Register scratch1, + Label* on_black); + + // Checks the color of an object. If the object is already grey or black + // then we just fall through, since it is already live. If it is white and + // we can determine that it doesn't need to be scanned, then we just mark it + // black and fall through. For the rest we jump to the label so the + // incremental marker can fix its assumptions. + void EnsureNotWhite(Register object, + Register scratch1, + Register scratch2, + Register scratch3, + Label* object_is_white_and_not_data); + + // Detects conservatively whether an object is data-only, i.e. it does need to + // be scanned by the garbage collector. + void JumpIfDataObject(Register value, + Register scratch, + Label* not_data_object); + + // Notify the garbage collector that we wrote a pointer into an object. + // |object| is the object being stored into, |value| is the object being + // stored. value and scratch registers are clobbered by the operation. + // The offset is the offset from the start of the object, not the offset from + // the tagged HeapObject pointer. For use with FieldOperand(reg, off). + void RecordWriteField( + Register object, + int offset, + Register value, + Register scratch, + LinkRegisterStatus lr_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // As above, but the offset has the tag presubtracted. For use with + // MemOperand(reg, off). + inline void RecordWriteContextSlot( + Register context, + int offset, + Register value, + Register scratch, + LinkRegisterStatus lr_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK) { + RecordWriteField(context, + offset + kHeapObjectTag, + value, + scratch, + lr_status, + save_fp, + remembered_set_action, + smi_check); + } - // For the page containing |object| mark the region covering - // [address] dirty. The object address must be in the first 8K of an - // allocated page. All 3 registers are clobbered by the operation, - // as well as the ip register. RecordWrite updates the write barrier - // even when storing smis. - void RecordWrite(Register object, - Register address, - Register scratch); + // For a given |object| notify the garbage collector that the slot |address| + // has been written. |value| is the object being stored. The value and + // address registers are clobbered by the operation. + void RecordWrite( + Register object, + Register address, + Register value, + LinkRegisterStatus lr_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); // Push a handle. void Push(Handle<Object> handle); @@ -225,8 +330,11 @@ class MacroAssembler: public Assembler { } // Push four registers. Pushes leftmost register first (to highest address). - void Push(Register src1, Register src2, - Register src3, Register src4, Condition cond = al) { + void Push(Register src1, + Register src2, + Register src3, + Register src4, + Condition cond = al) { ASSERT(!src1.is(src2)); ASSERT(!src2.is(src3)); ASSERT(!src1.is(src3)); @@ -265,6 +373,57 @@ class MacroAssembler: public Assembler { } } + // Pop three registers. Pops rightmost register first (from lower address). + void Pop(Register src1, Register src2, Register src3, Condition cond = al) { + ASSERT(!src1.is(src2)); + ASSERT(!src2.is(src3)); + ASSERT(!src1.is(src3)); + if (src1.code() > src2.code()) { + if (src2.code() > src3.code()) { + ldm(ia_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); + } else { + ldr(src3, MemOperand(sp, 4, PostIndex), cond); + ldm(ia_w, sp, src1.bit() | src2.bit(), cond); + } + } else { + Pop(src2, src3, cond); + str(src1, MemOperand(sp, 4, PostIndex), cond); + } + } + + // Pop four registers. Pops rightmost register first (from lower address). + void Pop(Register src1, + Register src2, + Register src3, + Register src4, + Condition cond = al) { + ASSERT(!src1.is(src2)); + ASSERT(!src2.is(src3)); + ASSERT(!src1.is(src3)); + ASSERT(!src1.is(src4)); + ASSERT(!src2.is(src4)); + ASSERT(!src3.is(src4)); + if (src1.code() > src2.code()) { + if (src2.code() > src3.code()) { + if (src3.code() > src4.code()) { + ldm(ia_w, + sp, + src1.bit() | src2.bit() | src3.bit() | src4.bit(), + cond); + } else { + ldr(src4, MemOperand(sp, 4, PostIndex), cond); + ldm(ia_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); + } + } else { + Pop(src3, src4, cond); + ldm(ia_w, sp, src1.bit() | src2.bit(), cond); + } + } else { + Pop(src2, src3, src4, cond); + ldr(src1, MemOperand(sp, 4, PostIndex), cond); + } + } + // Push and pop the registers that can hold pointers, as defined by the // RegList constant kSafepointSavedRegisters. void PushSafepointRegisters(); @@ -318,16 +477,6 @@ class MacroAssembler: public Assembler { const double imm, const Condition cond = al); - - // --------------------------------------------------------------------------- - // Activation frames - - void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } - void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); } - - void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } - void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter exit frame. // stack_space - extra stack space, used for alignment before call to C. void EnterExitFrame(bool save_doubles, int stack_space = 0); @@ -342,6 +491,22 @@ class MacroAssembler: public Assembler { void LoadContext(Register dst, int context_chain_length); + // Conditionally load the cached Array transitioned map of type + // transitioned_kind from the global context if the map in register + // map_in_out is the cached Array map in the global context of + // expected_kind. + void LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match); + + // Load the initial map for new Arrays from a JSFunction. + void LoadInitialArrayMap(Register function_in, + Register scratch, + Register map_out); + void LoadGlobalFunction(int index, Register function); // Load the initial map from the global function. The registers @@ -351,15 +516,15 @@ class MacroAssembler: public Assembler { Register scratch); void InitializeRootRegister() { - ExternalReference roots_address = - ExternalReference::roots_address(isolate()); - mov(kRootRegister, Operand(roots_address)); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(kRootRegister, Operand(roots_array_start)); } // --------------------------------------------------------------------------- // JavaScript invokes - // Setup call kind marking in ecx. The method takes ecx as an + // Set up call kind marking in ecx. The method takes ecx as an // explicit first parameter to make the code more readable at the // call sites. void SetCallKind(Register dst, CallKind kind); @@ -387,9 +552,10 @@ class MacroAssembler: public Assembler { const CallWrapper& call_wrapper, CallKind call_kind); - void InvokeFunction(JSFunction* function, + void InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper, CallKind call_kind); void IsObjectJSObjectType(Register heap_object, @@ -416,9 +582,7 @@ class MacroAssembler: public Assembler { // Exception handling // Push a new try handler and link into try handler chain. - // The return address must be passed in register lr. - // On exit, r0 contains TOS (code slot). - void PushTryHandler(CodeLocation try_location, HandlerType type); + void PushTryHandler(StackHandler::Kind kind, int handler_index); // Unlink the stack handler on top of the stack from the try handler chain. // Must preserve the result register. @@ -457,7 +621,7 @@ class MacroAssembler: public Assembler { } // Check if the given instruction is a 'type' marker. - // ie. check if is is a mov r<type>, r<type> (referenced as nop(type)) + // i.e. check if is is a mov r<type>, r<type> (referenced as nop(type)) // These instructions are generated to mark special location in the code, // like some special IC code. static inline bool IsMarkedCode(Instr instr, int type) { @@ -576,6 +740,13 @@ class MacroAssembler: public Assembler { Register length, Register scratch); + // Initialize fields with filler values. Fields starting at |start_offset| + // not including end_offset are overwritten with the value in |filler|. At + // the end the loop, |start_offset| takes the value of |end_offset|. + void InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler); + // --------------------------------------------------------------------------- // Support functions. @@ -587,7 +758,8 @@ class MacroAssembler: public Assembler { void TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Compare object type for heap object. heap_object contains a non-Smi // whose object type should be compared with the given type. This both @@ -615,15 +787,52 @@ class MacroAssembler: public Assembler { Register scratch, Label* fail); - // Check if the map of an object is equal to a specified map (either - // given directly or as an index into the root list) and branch to - // label if not. Skip the smi check if not required (object is known - // to be a heap object) + // Check if a map for a JSObject indicates that the object can have both smi + // and HeapObject elements. Jump to the specified label if it does not. + void CheckFastObjectElements(Register map, + Register scratch, + Label* fail); + + // Check if a map for a JSObject indicates that the object has fast smi only + // elements. Jump to the specified label if it does not. + void CheckFastSmiOnlyElements(Register map, + Register scratch, + Label* fail); + + // Check to see if maybe_number can be stored as a double in + // FastDoubleElements. If it can, store it at the index specified by key in + // the FastDoubleElements array elements. Otherwise jump to fail, in which + // case scratch2, scratch3 and scratch4 are unmodified. + void StoreNumberToDoubleElements(Register value_reg, + Register key_reg, + Register receiver_reg, + Register elements_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Label* fail); + + // Compare an object's map with the specified map and its transitioned + // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. Condition flags are + // set with result of map compare. If multiple map compares are required, the + // compare sequences branches to early_success. + void CompareMap(Register obj, + Register scratch, + Handle<Map> map, + Label* early_success, + CompareMapMode mode = REQUIRE_EXACT_MAP); + + // Check if the map of an object is equal to a specified map and branch to + // label if not. Skip the smi check if not required (object is known to be a + // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match + // against maps that are ElementsKind transition maps of the specified map. void CheckMap(Register obj, Register scratch, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type); + SmiCheckType smi_check_type, + CompareMapMode mode = REQUIRE_EXACT_MAP); void CheckMap(Register obj, @@ -715,7 +924,7 @@ class MacroAssembler: public Assembler { // Truncates a double using a specific rounding mode. // Clears the z flag (ne condition) if an overflow occurs. // If exact_conversion is true, the z flag is also cleared if the conversion - // was inexact, ie. if the double value could not be converted exactly + // was inexact, i.e. if the double value could not be converted exactly // to a 32bit integer. void EmitVFPTruncate(VFPRoundingMode rounding_mode, SwVfpRegister result, @@ -761,20 +970,9 @@ class MacroAssembler: public Assembler { // Call a code stub. void CallStub(CodeStub* stub, Condition cond = al); - // Call a code stub and return the code object called. Try to generate - // the code if necessary. Do not perform a GC but instead return a retry - // after GC failure. - MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub, Condition cond = al); - // Call a code stub. void TailCallStub(CodeStub* stub, Condition cond = al); - // Tail call a code stub (jump) and return the code object called. Try to - // generate the code if necessary. Do not perform a GC but instead return - // a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallStub(CodeStub* stub, - Condition cond = al); - // Call a runtime routine. void CallRuntime(const Runtime::Function* f, int num_arguments); void CallRuntimeSaveDoubles(Runtime::FunctionId id); @@ -793,12 +991,6 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); - // Tail call of a runtime routine (jump). Try to generate the code if - // necessary. Do not perform a GC but instead return a retry after GC - // failure. - MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size); - // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, @@ -837,28 +1029,25 @@ class MacroAssembler: public Assembler { // return address (unless this is somehow accounted for by the called // function). void CallCFunction(ExternalReference function, int num_arguments); - void CallCFunction(Register function, Register scratch, int num_arguments); + void CallCFunction(Register function, int num_arguments); void CallCFunction(ExternalReference function, int num_reg_arguments, int num_double_arguments); - void CallCFunction(Register function, Register scratch, + void CallCFunction(Register function, int num_reg_arguments, int num_double_arguments); void GetCFunctionDoubleResult(const DoubleRegister dst); - // Calls an API function. Allocates HandleScope, extracts returned value - // from handle and propagates exceptions. Restores context. - // stack_space - space to be unwound on exit (includes the call js - // arguments space and the additional space allocated for the fast call). - MaybeObject* TryCallApiFunctionAndReturn(ExternalReference function, - int stack_space); + // Calls an API function. Allocates HandleScope, extracts returned value + // from handle and propagates exceptions. Restores context. stack_space + // - space to be unwound on exit (includes the call JS arguments space and + // the additional space allocated for the fast call). + void CallApiFunctionAndReturn(ExternalReference function, int stack_space); // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& builtin); - MaybeObject* TryJumpToExternalReference(const ExternalReference& ext); - // Invoke specified builtin JavaScript function. Adds an entry to // the unresolved list if the name does not resolve. void InvokeBuiltin(Builtins::JavaScript id, @@ -909,6 +1098,9 @@ class MacroAssembler: public Assembler { bool generating_stub() { return generating_stub_; } void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } bool allow_stub_calls() { return allow_stub_calls_; } + void set_has_frame(bool value) { has_frame_ = value; } + bool has_frame() { return has_frame_; } + inline bool AllowThisStubCall(CodeStub* stub); // EABI variant for double arguments in use. bool use_eabi_hardfloat() { @@ -967,6 +1159,14 @@ class MacroAssembler: public Assembler { mov(dst, Operand(src, ASR, kSmiTagSize), s); } + // Untag the source value into destination and jump if source is a smi. + // Souce and destination can be the same register. + void UntagAndJumpIfSmi(Register dst, Register src, Label* smi_case); + + // Untag the source value into destination and jump if source is not a smi. + // Souce and destination can be the same register. + void UntagAndJumpIfNotSmi(Register dst, Register src, Label* non_smi_case); + // Jump the register contains a smi. inline void JumpIfSmi(Register value, Label* smi_label) { tst(value, Operand(kSmiTagMask)); @@ -1055,10 +1255,12 @@ class MacroAssembler: public Assembler { void LoadInstanceDescriptors(Register map, Register descriptors); + // Activation support. + void EnterFrame(StackFrame::Type type); + void LeaveFrame(StackFrame::Type type); + private: void CallCFunctionHelper(Register function, - ExternalReference function_reference, - Register scratch, int num_reg_arguments, int num_double_arguments); @@ -1070,20 +1272,34 @@ class MacroAssembler: public Assembler { Handle<Code> code_constant, Register code_reg, Label* done, + bool* definitely_mismatches, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind); - // Activation support. - void EnterFrame(StackFrame::Type type); - void LeaveFrame(StackFrame::Type type); - void InitializeNewString(Register string, Register length, Heap::RootListIndex map_index, Register scratch1, Register scratch2); + // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. + void InNewSpace(Register object, + Register scratch, + Condition cond, // eq for new space, ne otherwise. + Label* branch); + + // Helper for finding the mark bits for an address. Afterwards, the + // bitmap register points at the word with the mark bits and the mask + // the position of the first bit. Leaves addr_reg unchanged. + inline void GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg); + + // Helper for throwing exceptions. Compute a handler address and jump to + // it. See the implementation for register usage. + void JumpToHandlerEntry(); + // Compute memory operands for safepoint stack slots. static int SafepointRegisterStackIndex(int reg_code); MemOperand SafepointRegisterSlot(Register reg); @@ -1091,6 +1307,7 @@ class MacroAssembler: public Assembler { bool generating_stub_; bool allow_stub_calls_; + bool has_frame_; // This handle will be patched with the code object on installation. Handle<Object> code_object_; @@ -1136,12 +1353,12 @@ class CodePatcher { // ----------------------------------------------------------------------------- // Static helper functions. -static MemOperand ContextOperand(Register context, int index) { +inline MemOperand ContextOperand(Register context, int index) { return MemOperand(context, Context::SlotOffset(index)); } -static inline MemOperand GlobalObjectOperand() { +inline MemOperand GlobalObjectOperand() { return ContextOperand(cp, Context::GLOBAL_INDEX); } diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.cc b/deps/v8/src/arm/regexp-macro-assembler-arm.cc index cd76edbf15..880c372538 100644 --- a/deps/v8/src/arm/regexp-macro-assembler-arm.cc +++ b/deps/v8/src/arm/regexp-macro-assembler-arm.cc @@ -371,9 +371,12 @@ void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase( // Isolate. __ mov(r3, Operand(ExternalReference::isolate_address())); - ExternalReference function = - ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); - __ CallCFunction(function, argument_count); + { + AllowExternalCallThatCantCauseGC scope(masm_); + ExternalReference function = + ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); + __ CallCFunction(function, argument_count); + } // Check if function returned non-zero for success or zero for failure. __ cmp(r0, Operand(0, RelocInfo::NONE)); @@ -568,7 +571,7 @@ bool RegExpMacroAssemblerARM::CheckSpecialCharacterClass(uc16 type, ExternalReference map = ExternalReference::re_word_character_map(); __ mov(r0, Operand(map)); __ ldrb(r0, MemOperand(r0, current_character())); - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); BranchOrBacktrack(eq, on_no_match); return true; } @@ -582,7 +585,7 @@ bool RegExpMacroAssemblerARM::CheckSpecialCharacterClass(uc16 type, ExternalReference map = ExternalReference::re_word_character_map(); __ mov(r0, Operand(map)); __ ldrb(r0, MemOperand(r0, current_character())); - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); BranchOrBacktrack(ne, on_no_match); if (mode_ != ASCII) { __ bind(&done); @@ -611,6 +614,12 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) { // Entry code: __ bind(&entry_label_); + + // Tell the system that we have a stack frame. Because the type is MANUAL, no + // is generated. + FrameScope scope(masm_, StackFrame::MANUAL); + + // Actually emit code to start a new stack frame. // Push arguments // Save callee-save registers. // Start new stack frame. @@ -672,7 +681,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) { // Determine whether the start index is zero, that is at the start of the // string, and store that value in a local variable. - __ tst(r1, Operand(r1)); + __ cmp(r1, Operand(0)); __ mov(r1, Operand(1), LeaveCC, eq); __ mov(r1, Operand(0, RelocInfo::NONE), LeaveCC, ne); __ str(r1, MemOperand(frame_pointer(), kAtStart)); @@ -1102,6 +1111,11 @@ int RegExpMacroAssemblerARM::CheckStackGuardState(Address* return_address, frame_entry<const String*>(re_frame, kInputString) = *subject; frame_entry<const byte*>(re_frame, kInputStart) = new_address; frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length; + } else if (frame_entry<const String*>(re_frame, kInputString) != *subject) { + // Subject string might have been a ConsString that underwent + // short-circuiting during GC. That will not change start_address but + // will change pointer inside the subject handle. + frame_entry<const String*>(re_frame, kInputString) = *subject; } return 0; diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index 6af535553f..1ae172c008 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -53,7 +53,7 @@ namespace internal { // code. class ArmDebugger { public: - explicit ArmDebugger(Simulator* sim); + explicit ArmDebugger(Simulator* sim) : sim_(sim) { } ~ArmDebugger(); void Stop(Instruction* instr); @@ -84,11 +84,6 @@ class ArmDebugger { }; -ArmDebugger::ArmDebugger(Simulator* sim) { - sim_ = sim; -} - - ArmDebugger::~ArmDebugger() { } @@ -296,6 +291,13 @@ void ArmDebugger::Debug() { if (line == NULL) { break; } else { + char* last_input = sim_->last_debugger_input(); + if (strcmp(line, "\n") == 0 && last_input != NULL) { + line = last_input; + } else { + // Ownership is transferred to sim_; + sim_->set_last_debugger_input(line); + } // Use sscanf to parse the individual parts of the command line. At the // moment no command expects more than two parameters. int argc = SScanF(line, @@ -611,7 +613,6 @@ void ArmDebugger::Debug() { PrintF("Unknown command: %s\n", cmd); } } - DeleteArray(line); } // Add all the breakpoints back to stop execution and enter the debugger @@ -645,6 +646,12 @@ static bool AllOnOnePage(uintptr_t start, int size) { } +void Simulator::set_last_debugger_input(char* input) { + DeleteArray(last_debugger_input_); + last_debugger_input_ = input; +} + + void Simulator::FlushICache(v8::internal::HashMap* i_cache, void* start_addr, size_t size) { @@ -734,7 +741,7 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { isolate_->set_simulator_i_cache(i_cache_); } Initialize(isolate); - // Setup simulator support first. Some of this information is needed to + // Set up simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack stack_ = reinterpret_cast<char*>(malloc(stack_size)); @@ -743,7 +750,7 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { break_pc_ = NULL; break_instr_ = 0; - // Setup architecture state. + // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < num_registers; i++) { registers_[i] = 0; @@ -781,6 +788,8 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { registers_[pc] = bad_lr; registers_[lr] = bad_lr; InitializeCoverage(); + + last_debugger_input_ = NULL; } @@ -1268,9 +1277,9 @@ void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) { // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit() const { - // Leave a safety margin of 256 bytes to prevent overrunning the stack when + // Leave a safety margin of 512 bytes to prevent overrunning the stack when // pushing values. - return reinterpret_cast<uintptr_t>(stack_) + 256; + return reinterpret_cast<uintptr_t>(stack_) + 512; } @@ -1618,6 +1627,8 @@ void Simulator::HandleRList(Instruction* instr, bool load) { ProcessPUW(instr, num_regs, kPointerSize, &start_address, &end_address); intptr_t* address = reinterpret_cast<intptr_t*>(start_address); + // Catch null pointers a little earlier. + ASSERT(start_address > 8191 || start_address < 0); int reg = 0; while (rlist != 0) { if ((rlist & 1) != 0) { @@ -3313,7 +3324,7 @@ void Simulator::Execute() { int32_t Simulator::Call(byte* entry, int argument_count, ...) { va_list parameters; va_start(parameters, argument_count); - // Setup arguments + // Set up arguments // First four arguments passed in registers. ASSERT(argument_count >= 4); @@ -3356,7 +3367,7 @@ int32_t Simulator::Call(byte* entry, int argument_count, ...) { int32_t r10_val = get_register(r10); int32_t r11_val = get_register(r11); - // Setup the callee-saved registers with a known value. To be able to check + // Set up the callee-saved registers with a known value. To be able to check // that they are preserved properly across JS execution. int32_t callee_saved_value = icount_; set_register(r4, callee_saved_value); diff --git a/deps/v8/src/arm/simulator-arm.h b/deps/v8/src/arm/simulator-arm.h index 391ef69f5e..585f1e0176 100644 --- a/deps/v8/src/arm/simulator-arm.h +++ b/deps/v8/src/arm/simulator-arm.h @@ -194,6 +194,10 @@ class Simulator { // Pop an address from the JS stack. uintptr_t PopAddress(); + // Debugger input. + void set_last_debugger_input(char* input); + char* last_debugger_input() { return last_debugger_input_; } + // ICache checking. static void FlushICache(v8::internal::HashMap* i_cache, void* start, size_t size); @@ -360,6 +364,9 @@ class Simulator { bool pc_modified_; int icount_; + // Debugger input. + char* last_debugger_input_; + // Icache simulation v8::internal::HashMap* i_cache_; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index f8565924b1..2f2c5a838d 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -45,6 +45,7 @@ static void ProbeTable(Isolate* isolate, StubCache::Table table, Register name, Register offset, + int offset_shift_bits, Register scratch, Register scratch2) { ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); @@ -63,23 +64,34 @@ static void ProbeTable(Isolate* isolate, // Check that the key in the entry matches the name. __ mov(offsets_base_addr, Operand(key_offset)); - __ ldr(ip, MemOperand(offsets_base_addr, offset, LSL, 1)); + __ ldr(ip, MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits)); __ cmp(name, ip); __ b(ne, &miss); // Get the code entry from the cache. __ add(offsets_base_addr, offsets_base_addr, Operand(value_off_addr - key_off_addr)); - __ ldr(scratch2, MemOperand(offsets_base_addr, offset, LSL, 1)); + __ ldr(scratch2, + MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits)); // Check that the flags match what we're looking for. __ ldr(scratch2, FieldMemOperand(scratch2, Code::kFlagsOffset)); - __ bic(scratch2, scratch2, Operand(Code::kFlagsNotUsedInLookup)); - __ cmp(scratch2, Operand(flags)); + // It's a nice optimization if this constant is encodable in the bic insn. + + uint32_t mask = Code::kFlagsNotUsedInLookup; + ASSERT(__ ImmediateFitsAddrMode1Instruction(mask)); + __ bic(scratch2, scratch2, Operand(mask)); + // Using cmn and the negative instead of cmp means we can use movw. + if (flags < 0) { + __ cmn(scratch2, Operand(-flags)); + } else { + __ cmp(scratch2, Operand(flags)); + } __ b(ne, &miss); // Re-load code entry from cache. - __ ldr(offset, MemOperand(offsets_base_addr, offset, LSL, 1)); + __ ldr(offset, + MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits)); // Jump to the first instruction in the code stub. __ add(offset, offset, Operand(Code::kHeaderSize - kHeapObjectTag)); @@ -95,13 +107,12 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( - MacroAssembler* masm, - Label* miss_label, - Register receiver, - String* name, - Register scratch0, - Register scratch1) { +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle<String> name, + Register scratch0, + Register scratch1) { ASSERT(name->IsSymbol()); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); @@ -138,20 +149,15 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup( - masm, - miss_label, - &done, - receiver, - properties, - name, - scratch1); - if (result->IsFailure()) return result; - + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + receiver, + properties, + name, + scratch1); __ bind(&done); __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); - - return result; } @@ -195,23 +201,41 @@ void StubCache::GenerateProbe(MacroAssembler* masm, __ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset)); __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset)); __ add(scratch, scratch, Operand(ip)); - __ eor(scratch, scratch, Operand(flags)); - __ and_(scratch, - scratch, - Operand((kPrimaryTableSize - 1) << kHeapObjectTagSize)); + uint32_t mask = (kPrimaryTableSize - 1) << kHeapObjectTagSize; + // Mask down the eor argument to the minimum to keep the immediate + // ARM-encodable. + __ eor(scratch, scratch, Operand(flags & mask)); + // Prefer and_ to ubfx here because ubfx takes 2 cycles. + __ and_(scratch, scratch, Operand(mask)); + __ mov(scratch, Operand(scratch, LSR, 1)); // Probe the primary table. - ProbeTable(isolate, masm, flags, kPrimary, name, scratch, extra, extra2); + ProbeTable(isolate, + masm, + flags, + kPrimary, + name, + scratch, + 1, + extra, + extra2); // Primary miss: Compute hash for secondary probe. - __ sub(scratch, scratch, Operand(name)); - __ add(scratch, scratch, Operand(flags)); - __ and_(scratch, - scratch, - Operand((kSecondaryTableSize - 1) << kHeapObjectTagSize)); + __ sub(scratch, scratch, Operand(name, LSR, 1)); + uint32_t mask2 = (kSecondaryTableSize - 1) << (kHeapObjectTagSize - 1); + __ add(scratch, scratch, Operand((flags >> 1) & mask2)); + __ and_(scratch, scratch, Operand(mask2)); // Probe the secondary table. - ProbeTable(isolate, masm, flags, kSecondary, name, scratch, extra, extra2); + ProbeTable(isolate, + masm, + flags, + kSecondary, + name, + scratch, + 1, + extra, + extra2); // Cache miss: Fall-through and let caller handle the miss by // entering the runtime system. @@ -238,7 +262,10 @@ void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, int index, Register prototype, Label* miss) { + MacroAssembler* masm, + int index, + Register prototype, + Label* miss) { Isolate* isolate = masm->isolate(); // Check we're still in the same context. __ ldr(prototype, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); @@ -246,8 +273,8 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( __ cmp(prototype, ip); __ b(ne, miss); // Get the global function with the given index. - JSFunction* function = - JSFunction::cast(isolate->global_context()->get(index)); + Handle<JSFunction> function( + JSFunction::cast(isolate->global_context()->get(index))); // Load its initial map. The global functions all have initial maps. __ Move(prototype, Handle<Map>(function->initial_map())); // Load the prototype from the initial map. @@ -259,8 +286,10 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle<JSObject> holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -367,9 +396,9 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // may be clobbered. Upon branch to miss_label, the receiver and name // registers have their original values. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, int index, - Map* transition, + Handle<Map> transition, Register receiver_reg, Register name_reg, Register scratch, @@ -377,13 +406,9 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // r0 : value Label exit; - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver_reg, miss_label); - - // Check that the map of the receiver hasn't changed. - __ ldr(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); - __ cmp(scratch, Operand(Handle<Map>(object->map()))); - __ b(ne, miss_label); + // Check that the map of the object hasn't changed. + __ CheckMap(receiver_reg, scratch, Handle<Map>(object->map()), miss_label, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -395,11 +420,11 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); // Perform map transition for the receiver if necessary. - if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { + if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. __ push(receiver_reg); - __ mov(r2, Operand(Handle<Map>(transition))); + __ mov(r2, Operand(transition)); __ Push(r2, r0); __ TailCallExternalReference( ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), @@ -409,10 +434,10 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, return; } - if (transition != NULL) { + if (!transition.is_null()) { // Update the map of the object; no write barrier updating is // needed because the map is never in new space. - __ mov(ip, Operand(Handle<Map>(transition))); + __ mov(ip, Operand(transition)); __ str(ip, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); } @@ -431,7 +456,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the now unused name_reg as a scratch register. - __ RecordWrite(receiver_reg, Operand(offset), name_reg, scratch); + __ mov(name_reg, r0); + __ RecordWriteField(receiver_reg, + offset, + name_reg, + scratch, + kLRHasNotBeenSaved, + kDontSaveFPRegs); } else { // Write to the properties array. int offset = index * kPointerSize + FixedArray::kHeaderSize; @@ -444,7 +475,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Ok to clobber receiver_reg and name_reg, since we return. - __ RecordWrite(scratch, Operand(offset), name_reg, receiver_reg); + __ mov(name_reg, r0); + __ RecordWriteField(scratch, + offset, + name_reg, + receiver_reg, + kLRHasNotBeenSaved, + kDontSaveFPRegs); } // Return the value (register r0). @@ -455,20 +492,15 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); - Code* code = NULL; - if (kind == Code::LOAD_IC) { - code = masm->isolate()->builtins()->builtin(Builtins::kLoadIC_Miss); - } else { - code = masm->isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Miss); - } - - Handle<Code> ic(code); - __ Jump(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = (kind == Code::LOAD_IC) + ? masm->isolate()->builtins()->LoadIC_Miss() + : masm->isolate()->builtins()->KeyedLoadIC_Miss(); + __ Jump(code, RelocInfo::CODE_TARGET); } static void GenerateCallFunction(MacroAssembler* masm, - Object* object, + Handle<Object> object, const ParameterCount& arguments, Label* miss, Code::ExtraICState extra_ic_state) { @@ -501,12 +533,12 @@ static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, Register name, - JSObject* holder_obj) { + Handle<JSObject> holder_obj) { __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!masm->isolate()->heap()->InNewSpace(interceptor)); + Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); + ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); Register scratch = name; - __ mov(scratch, Operand(Handle<Object>(interceptor))); + __ mov(scratch, Operand(interceptor)); __ push(scratch); __ push(receiver); __ push(holder); @@ -515,11 +547,12 @@ static void PushInterceptorArguments(MacroAssembler* masm, } -static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { +static void CompileCallLoadPropertyWithInterceptor( + MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + Handle<JSObject> holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); ExternalReference ref = @@ -532,6 +565,7 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, __ CallStub(&stub); } + static const int kFastApiCallArguments = 3; // Reserves space for the extra arguments to FastHandleApiCall in the @@ -553,44 +587,42 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm) { } -static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, +static void GenerateFastApiDirectCall(MacroAssembler* masm, const CallOptimization& optimization, int argc) { // ----------- S t a t e ------------- // -- sp[0] : holder (set by CheckPrototypes) - // -- sp[4] : callee js function + // -- sp[4] : callee JS function // -- sp[8] : call data - // -- sp[12] : last js argument + // -- sp[12] : last JS argument // -- ... - // -- sp[(argc + 3) * 4] : first js argument + // -- sp[(argc + 3) * 4] : first JS argument // -- sp[(argc + 4) * 4] : receiver // ----------------------------------- // Get the function and setup the context. - JSFunction* function = optimization.constant_function(); - __ mov(r5, Operand(Handle<JSFunction>(function))); + Handle<JSFunction> function = optimization.constant_function(); + __ LoadHeapObject(r5, function); __ ldr(cp, FieldMemOperand(r5, JSFunction::kContextOffset)); // Pass the additional arguments FastHandleApiCall expects. - Object* call_data = optimization.api_call_info()->data(); - Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); - if (masm->isolate()->heap()->InNewSpace(call_data)) { - __ Move(r0, api_call_info_handle); + Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); + Handle<Object> call_data(api_call_info->data()); + if (masm->isolate()->heap()->InNewSpace(*call_data)) { + __ Move(r0, api_call_info); __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kDataOffset)); } else { - __ Move(r6, Handle<Object>(call_data)); + __ Move(r6, call_data); } - // Store js function and call data. + // Store JS function and call data. __ stm(ib, sp, r5.bit() | r6.bit()); // r2 points to call data as expected by Arguments // (refer to layout above). __ add(r2, sp, Operand(2 * kPointerSize)); - Object* callback = optimization.api_call_info()->callback(); - Address api_function_address = v8::ToCData<Address>(callback); - ApiFunction fun(api_function_address); - const int kApiStackSpace = 4; + + FrameScope frame_scope(masm, StackFrame::MANUAL); __ EnterExitFrame(false, kApiStackSpace); // r0 = v8::Arguments& @@ -608,17 +640,18 @@ static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, __ mov(ip, Operand(0)); __ str(ip, MemOperand(r0, 3 * kPointerSize)); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. const int kStackUnwindSpace = argc + kFastApiCallArguments + 1; + Address function_address = v8::ToCData<Address>(api_call_info->callback()); + ApiFunction fun(function_address); ExternalReference ref = ExternalReference(&fun, ExternalReference::DIRECT_API_CALL, masm->isolate()); - return masm->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace); + AllowExternalCallThatCantCauseGC scope(masm); + + __ CallApiFunctionAndReturn(ref, kStackUnwindSpace); } + class CallInterceptorCompiler BASE_EMBEDDED { public: CallInterceptorCompiler(StubCompiler* stub_compiler, @@ -630,86 +663,63 @@ class CallInterceptorCompiler BASE_EMBEDDED { name_(name), extra_ic_state_(extra_ic_state) {} - MaybeObject* Compile(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - LookupResult* lookup, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Label* miss) { + void Compile(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Label* miss) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); - CallOptimization optimization(lookup); - if (optimization.is_constant_call()) { - return CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, + holder, lookup, name, optimization, miss); } else { - CompileRegular(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - name, - holder, - miss); - return masm->isolate()->heap()->undefined_value(); + CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, + name, holder, miss); } } private: - MaybeObject* CompileCacheable(MacroAssembler* masm, - JSObject* object, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - JSObject* interceptor_holder, - LookupResult* lookup, - String* name, - const CallOptimization& optimization, - Label* miss_label) { + void CompileCacheable(MacroAssembler* masm, + Handle<JSObject> object, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<JSObject> interceptor_holder, + LookupResult* lookup, + Handle<String> name, + const CallOptimization& optimization, + Label* miss_label) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); - Counters* counters = masm->isolate()->counters(); - int depth1 = kInvalidProtoDepth; int depth2 = kInvalidProtoDepth; bool can_do_fast_api_call = false; if (optimization.is_simple_api_call() && - !lookup->holder()->IsGlobalObject()) { - depth1 = - optimization.GetPrototypeDepthOfExpectedType(object, - interceptor_holder); - if (depth1 == kInvalidProtoDepth) { - depth2 = - optimization.GetPrototypeDepthOfExpectedType(interceptor_holder, - lookup->holder()); - } - can_do_fast_api_call = (depth1 != kInvalidProtoDepth) || - (depth2 != kInvalidProtoDepth); + !lookup->holder()->IsGlobalObject()) { + depth1 = optimization.GetPrototypeDepthOfExpectedType( + object, interceptor_holder); + if (depth1 == kInvalidProtoDepth) { + depth2 = optimization.GetPrototypeDepthOfExpectedType( + interceptor_holder, Handle<JSObject>(lookup->holder())); + } + can_do_fast_api_call = + depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth; } __ IncrementCounter(counters->call_const_interceptor(), 1, - scratch1, scratch2); + scratch1, scratch2); if (can_do_fast_api_call) { __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1, @@ -722,9 +732,9 @@ class CallInterceptorCompiler BASE_EMBEDDED { Label miss_cleanup; Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; Register holder = - stub_compiler_->CheckPrototypes(object, receiver, - interceptor_holder, scratch1, - scratch2, scratch3, name, depth1, miss); + stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, depth1, miss); // Invoke an interceptor and if it provides a value, // branch to |regular_invoke|. @@ -737,10 +747,11 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Check that the maps from interceptor's holder to constant function's // holder haven't changed and thus we can use cached constant function. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { stub_compiler_->CheckPrototypes(interceptor_holder, receiver, - lookup->holder(), scratch1, - scratch2, scratch3, name, depth2, miss); + Handle<JSObject>(lookup->holder()), + scratch1, scratch2, scratch3, + name, depth2, miss); } else { // CheckPrototypes has a side effect of fetching a 'holder' // for API (object which is instanceof for the signature). It's @@ -751,16 +762,13 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - MaybeObject* result = GenerateFastApiDirectCall(masm, - optimization, - arguments_.immediate()); - if (result->IsFailure()) return result; + GenerateFastApiDirectCall(masm, optimization, arguments_.immediate()); } else { CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(optimization.constant_function(), arguments_, - JUMP_FUNCTION, call_kind); + JUMP_FUNCTION, NullCallWrapper(), call_kind); } // Deferred code for fast API call case---clean preallocated space. @@ -775,64 +783,53 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm); } - - return masm->isolate()->heap()->undefined_value(); } void CompileRegular(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, Register receiver, Register scratch1, Register scratch2, Register scratch3, - String* name, - JSObject* interceptor_holder, + Handle<String> name, + Handle<JSObject> interceptor_holder, Label* miss_label) { Register holder = stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, name, - miss_label); + scratch1, scratch2, scratch3, + name, miss_label); // Call a runtime function to load the interceptor property. - __ EnterInternalFrame(); + FrameScope scope(masm, StackFrame::INTERNAL); // Save the name_ register across the call. __ push(name_); - - PushInterceptorArguments(masm, - receiver, - holder, - name_, - interceptor_holder); - + PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder); __ CallExternalReference( ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall), masm->isolate()), 5); - // Restore the name_ register. __ pop(name_); - __ LeaveInternalFrame(); + // Leave the internal frame. } void LoadWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, - JSObject* holder_obj, + Handle<JSObject> holder_obj, Register scratch, Label* interceptor_succeeded) { - __ EnterInternalFrame(); - __ Push(holder, name_); - - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); - - __ pop(name_); // Restore the name. - __ pop(receiver); // Restore the holder. - __ LeaveInternalFrame(); - + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(holder, name_); + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. + } // If interceptor returns no-result sentinel, call the constant function. __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); __ cmp(r0, scratch); @@ -849,52 +846,42 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( - MacroAssembler* masm, - GlobalObject* global, - String* name, - Register scratch, - Label* miss) { - Object* probe; - { MaybeObject* maybe_probe = global->EnsurePropertyCell(name); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle<GlobalObject> global, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSGlobalPropertyCell> cell = + GlobalObject::EnsurePropertyCell(global, name); ASSERT(cell->value()->IsTheHole()); - __ mov(scratch, Operand(Handle<Object>(cell))); + __ mov(scratch, Operand(cell)); __ ldr(scratch, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ cmp(scratch, ip); __ b(ne, miss); - return cell; } + // Calls GenerateCheckPropertyCell for each global object in the prototype chain // from object to (but not including) holder. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( - MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - Register scratch, - Label* miss) { - JSObject* current = object; - while (current != holder) { +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { if (current->IsGlobalObject()) { - // Returns a cell or a failure. - MaybeObject* result = GenerateCheckPropertyCell( - masm, - GlobalObject::cast(current), - name, - scratch, - miss); - if (result->IsFailure()) return result; + GenerateCheckPropertyCell(masm, + Handle<GlobalObject>::cast(current), + name, + scratch, + miss); } - ASSERT(current->IsJSObject()); - current = JSObject::cast(current->GetPrototype()); + current = Handle<JSObject>(JSObject::cast(current->GetPrototype())); } - return NULL; } @@ -1008,13 +995,13 @@ static void GenerateUInt2Double(MacroAssembler* masm, #define __ ACCESS_MASM(masm()) -Register StubCompiler::CheckPrototypes(JSObject* object, +Register StubCompiler::CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, int save_at_depth, Label* miss) { // Make sure there's no overlap between holder and object registers. @@ -1032,83 +1019,51 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Check the maps in the prototype chain. // Traverse the prototype chain from the object and do map checks. - JSObject* current = object; - while (current != holder) { - depth++; + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { + ++depth; // Only global objects and objects that do not require access // checks are allowed in stubs. ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - ASSERT(current->GetPrototype()->IsJSObject()); - JSObject* prototype = JSObject::cast(current->GetPrototype()); + Handle<JSObject> prototype(JSObject::cast(current->GetPrototype())); if (!current->HasFastProperties() && !current->IsJSGlobalObject() && !current->IsJSGlobalProxy()) { if (!name->IsSymbol()) { - MaybeObject* maybe_lookup_result = heap()->LookupSymbol(name); - Object* lookup_result = NULL; // Initialization to please compiler. - if (!maybe_lookup_result->ToObject(&lookup_result)) { - set_failure(Failure::cast(maybe_lookup_result)); - return reg; - } - name = String::cast(lookup_result); + name = factory()->LookupSymbol(name); } - ASSERT(current->property_dictionary()->FindEntry(name) == + ASSERT(current->property_dictionary()->FindEntry(*name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); - if (negative_lookup->IsFailure()) { - set_failure(Failure::cast(negative_lookup)); - return reg; - } + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // from now the object is in holder_reg + reg = holder_reg; // From now on the object will be in holder_reg. __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); - } else if (heap()->InNewSpace(prototype)) { - // Get the map of the current object. - __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - __ cmp(scratch1, Operand(Handle<Map>(current->map()))); - - // Branch on the result of the map check. - __ b(ne, miss); + } else { + Handle<Map> current_map(current->map()); + __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK, + ALLOW_ELEMENT_TRANSITION_MAPS); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - // Restore scratch register to be the map of the object. In the - // new space case below, we load the prototype from the map in - // the scratch register. - __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); + __ CheckAccessGlobalProxy(reg, scratch2, miss); } + reg = holder_reg; // From now on the object will be in holder_reg. - reg = holder_reg; // from now the object is in holder_reg - // The prototype is in new space; we cannot store a reference - // to it in the code. Load it from the map. - __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); - } else { - // Check the map of the current object. - __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - __ cmp(scratch1, Operand(Handle<Map>(current->map()))); - // Branch on the result of the map check. - __ b(ne, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); + if (heap()->InNewSpace(*prototype)) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ mov(reg, Operand(prototype)); } - // The prototype is in old space; load it directly. - reg = holder_reg; // from now the object is in holder_reg - __ mov(reg, Operand(Handle<JSObject>(prototype))); } if (save_at_depth == depth) { @@ -1119,143 +1074,130 @@ Register StubCompiler::CheckPrototypes(JSObject* object, current = prototype; } - // Check the holder map. - __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - __ cmp(scratch1, Operand(Handle<Map>(current->map()))); - __ b(ne, miss); - // Log the check depth. LOG(masm()->isolate(), IntEvent("check-maps-depth", depth + 1)); + // Check the holder map. + __ CheckMap(reg, scratch1, Handle<Map>(current->map()), miss, + DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); + // Perform security check for access to the global object. ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); if (holder->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); - }; - - // If we've skipped any global objects, it's not enough to verify - // that their maps haven't changed. We also need to check that the - // property cell for the property is still empty. - MaybeObject* result = GenerateCheckPropertyCells(masm(), - object, - holder, - name, - scratch1, - miss); - if (result->IsFailure()) set_failure(Failure::cast(result)); + } + + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); // Return the register containing the holder. return reg; } -void StubCompiler::GenerateLoadField(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadField(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, int index, - String* name, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, - name, miss); + Register reg = CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); GenerateFastPropertyLoad(masm(), r0, reg, holder, index); __ Ret(); } -void StubCompiler::GenerateLoadConstant(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, - Object* value, - String* name, + Handle<JSFunction> value, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, name, - miss); + CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); // Return the constant value. - __ mov(r0, Operand(Handle<Object>(value))); + __ LoadHeapObject(r0, value); __ Ret(); } -MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss) { +void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, + Handle<JSObject> holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, - name, miss); + Register reg = CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); // Build AccessorInfo::args_ list on the stack and push property name below // the exit frame to make GC aware of them and store pointers to them. __ push(receiver); __ mov(scratch2, sp); // scratch2 = AccessorInfo::args_ - Handle<AccessorInfo> callback_handle(callback); - if (heap()->InNewSpace(callback_handle->data())) { - __ Move(scratch3, callback_handle); + if (heap()->InNewSpace(callback->data())) { + __ Move(scratch3, callback); __ ldr(scratch3, FieldMemOperand(scratch3, AccessorInfo::kDataOffset)); } else { - __ Move(scratch3, Handle<Object>(callback_handle->data())); + __ Move(scratch3, Handle<Object>(callback->data())); } __ Push(reg, scratch3, name_reg); __ mov(r0, sp); // r0 = Handle<String> - Address getter_address = v8::ToCData<Address>(callback->getter()); - ApiFunction fun(getter_address); - const int kApiStackSpace = 1; + FrameScope frame_scope(masm(), StackFrame::MANUAL); __ EnterExitFrame(false, kApiStackSpace); + // Create AccessorInfo instance on the stack above the exit frame with - // scratch2 (internal::Object **args_) as the data. + // scratch2 (internal::Object** args_) as the data. __ str(scratch2, MemOperand(sp, 1 * kPointerSize)); __ add(r1, sp, Operand(1 * kPointerSize)); // r1 = AccessorInfo& - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. const int kStackUnwindSpace = 4; + Address getter_address = v8::ToCData<Address>(callback->getter()); + ApiFunction fun(getter_address); ExternalReference ref = ExternalReference(&fun, ExternalReference::DIRECT_GETTER_CALL, masm()->isolate()); - return masm()->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace); + __ CallApiFunctionAndReturn(ref, kStackUnwindSpace); } -void StubCompiler::GenerateLoadInterceptor(JSObject* object, - JSObject* interceptor_holder, +void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> interceptor_holder, LookupResult* lookup, Register receiver, Register name_reg, Register scratch1, Register scratch2, Register scratch3, - String* name, + Handle<String> name, Label* miss) { ASSERT(interceptor_holder->HasNamedInterceptor()); ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -1267,13 +1209,13 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // and CALLBACKS, so inline only them, other cases may be added // later. bool compile_followup_inline = false; - if (lookup->IsProperty() && lookup->IsCacheable()) { + if (lookup->IsFound() && lookup->IsCacheable()) { if (lookup->type() == FIELD) { compile_followup_inline = true; } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsAccessorInfo() && - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { - compile_followup_inline = true; + lookup->GetCallbackObject()->IsAccessorInfo()) { + compile_followup_inline = + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL; } } @@ -1288,48 +1230,45 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // Save necessary data before invoking an interceptor. // Requires a frame to make GC aware of pushed pointers. - __ EnterInternalFrame(); - - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - // CALLBACKS case needs a receiver to be passed into C++ callback. - __ Push(receiver, holder_reg, name_reg); - } else { - __ Push(holder_reg, name_reg); - } - - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method.) - CompileCallLoadPropertyWithInterceptor(masm(), - receiver, - holder_reg, - name_reg, - interceptor_holder); - - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex); - __ cmp(r0, scratch1); - __ b(eq, &interceptor_failed); - __ LeaveInternalFrame(); - __ Ret(); + { + FrameScope frame_scope(masm(), StackFrame::INTERNAL); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + // CALLBACKS case needs a receiver to be passed into C++ callback. + __ Push(receiver, holder_reg, name_reg); + } else { + __ Push(holder_reg, name_reg); + } + // Invoke an interceptor. Note: map checks from receiver to + // interceptor's holder has been compiled before (see a caller + // of this method.) + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + interceptor_holder); + // Check if interceptor provided a value for property. If it's + // the case, return immediately. + Label interceptor_failed; + __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex); + __ cmp(r0, scratch1); + __ b(eq, &interceptor_failed); + frame_scope.GenerateLeaveFrame(); + __ Ret(); - __ bind(&interceptor_failed); - __ pop(name_reg); - __ pop(holder_reg); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - __ pop(receiver); + __ bind(&interceptor_failed); + __ pop(name_reg); + __ pop(holder_reg); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + __ pop(receiver); + } + // Leave the internal frame. } - - __ LeaveInternalFrame(); - // Check that the maps from interceptor's holder to lookup's holder // haven't changed. And load lookup's holder into |holder| register. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { holder_reg = CheckPrototypes(interceptor_holder, holder_reg, - lookup->holder(), + Handle<JSObject>(lookup->holder()), scratch1, scratch2, scratch3, @@ -1341,21 +1280,21 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), r0, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle<JSObject>(lookup->holder()), + lookup->GetFieldIndex()); __ Ret(); } else { // We found CALLBACKS property in prototype chain of interceptor's // holder. ASSERT(lookup->type() == CALLBACKS); - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); - ASSERT(callback != NULL); + Handle<AccessorInfo> callback( + AccessorInfo::cast(lookup->GetCallbackObject())); ASSERT(callback->getter() != NULL); // Tail call to runtime. // Important invariant in CALLBACKS case: the code above must be // structured to never clobber |receiver| register. - __ Move(scratch2, Handle<AccessorInfo>(callback)); + __ Move(scratch2, callback); // holder_reg is either receiver or scratch1. if (!receiver.is(holder_reg)) { ASSERT(scratch1.is(holder_reg)); @@ -1392,17 +1331,17 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ cmp(r2, Operand(Handle<String>(name))); + __ cmp(r2, Operand(name)); __ b(ne, miss); } } -void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, +void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, Label* miss) { ASSERT(holder->IsGlobalObject()); @@ -1415,7 +1354,7 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(r0, miss); } @@ -1424,15 +1363,16 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, } -void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, - Label* miss) { +void CallStubCompiler::GenerateLoadFunctionFromCell( + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Label* miss) { // Get the value from the cell. - __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ mov(r3, Operand(cell)); __ ldr(r1, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset)); // Check that the cell contains the same function. - if (heap()->InNewSpace(function)) { + if (heap()->InNewSpace(*function)) { // We can't embed a pointer to a function in new space so we have // to verify that the shared function info is unchanged. This has // the nice side effect that multiple closures based on the same @@ -1446,30 +1386,26 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, __ Move(r3, Handle<SharedFunctionInfo>(function->shared())); __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ cmp(r4, r3); - __ b(ne, miss); } else { - __ cmp(r1, Operand(Handle<JSFunction>(function))); - __ b(ne, miss); + __ cmp(r1, Operand(function)); } + __ b(ne, miss); } -MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = +void CallStubCompiler::GenerateMissBranch() { + Handle<Code> code = isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), kind_, - extra_ic_state_); - Object* obj; - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - __ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET); - return obj; + extra_state_); + __ Jump(code, RelocInfo::CODE_TARGET); } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, +Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -1489,23 +1425,23 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, Register reg = CheckPrototypes(object, r0, holder, r1, r3, r4, name, &miss); GenerateFastPropertyLoad(masm(), r1, reg, holder, index); - GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_); + GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); } -MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPushCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -1515,14 +1451,12 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss; - GenerateNameCheck(name, &miss); Register receiver = r1; - // Get the receiver from the stack const int argc = arguments().immediate(); __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); @@ -1531,8 +1465,8 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ JumpIfSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), receiver, - holder, r3, r0, r4, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, r3, r0, r4, + name, &miss); if (argc == 0) { // Nothing to do, just return the length. @@ -1542,21 +1476,21 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } else { Label call_builtin; - Register elements = r3; - Register end_elements = r5; + if (argc == 1) { // Otherwise fall through to call the builtin. + Label attempt_to_grow_elements; - // Get the elements array of the object. - __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); + Register elements = r6; + Register end_elements = r5; + // Get the elements array of the object. + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); - // Check that the elements are in fast mode and writable. - __ CheckMap(elements, - r0, - Heap::kFixedArrayMapRootIndex, - &call_builtin, - DONT_DO_SMI_CHECK); + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, + r0, + Heap::kFixedArrayMapRootIndex, + &call_builtin, + DONT_DO_SMI_CHECK); - if (argc == 1) { // Otherwise fall through to call the builtin. - Label exit, with_write_barrier, attempt_to_grow_elements; // Get the array's length into r0 and calculate new length. __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); @@ -1564,18 +1498,22 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, STATIC_ASSERT(kSmiTag == 0); __ add(r0, r0, Operand(Smi::FromInt(argc))); - // Get the element's length. + // Get the elements' length. __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); // Check if we could survive without allocation. __ cmp(r0, r4); __ b(gt, &attempt_to_grow_elements); + // Check if value is a smi. + Label with_write_barrier; + __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize)); + __ JumpIfNotSmi(r4, &with_write_barrier); + // Save new length. __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); - // Push the element. - __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize)); + // Store the value. // We may need a register containing the address end_elements below, // so write back the value in end_elements. __ add(end_elements, elements, @@ -1585,14 +1523,51 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex)); // Check for a smi. - __ JumpIfNotSmi(r4, &with_write_barrier); - __ bind(&exit); __ Drop(argc + 1); __ Ret(); __ bind(&with_write_barrier); - __ InNewSpace(elements, r4, eq, &exit); - __ RecordWriteHelper(elements, end_elements, r4); + + __ ldr(r3, FieldMemOperand(receiver, HeapObject::kMapOffset)); + + if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) { + Label fast_object, not_fast_object; + __ CheckFastObjectElements(r3, r7, ¬_fast_object); + __ jmp(&fast_object); + // In case of fast smi-only, convert to fast object, otherwise bail out. + __ bind(¬_fast_object); + __ CheckFastSmiOnlyElements(r3, r7, &call_builtin); + // edx: receiver + // r3: map + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + r3, + r7, + &call_builtin); + __ mov(r2, receiver); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + __ bind(&fast_object); + } else { + __ CheckFastObjectElements(r3, r3, &call_builtin); + } + + // Save new length. + __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Store the value. + // We may need a register containing the address end_elements below, + // so write back the value in end_elements. + __ add(end_elements, elements, + Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex)); + + __ RecordWrite(elements, + end_elements, + r4, + kLRHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); __ Drop(argc + 1); __ Ret(); @@ -1604,6 +1579,15 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ b(&call_builtin); } + __ ldr(r2, MemOperand(sp, (argc - 1) * kPointerSize)); + // Growing elements that are SMI-only requires special handling in case + // the new element is non-Smi. For now, delegate to the builtin. + Label no_fast_elements_check; + __ JumpIfSmi(r2, &no_fast_elements_check); + __ ldr(r7, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ CheckFastObjectElements(r7, r7, &call_builtin); + __ bind(&no_fast_elements_check); + Isolate* isolate = masm()->isolate(); ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address(isolate); @@ -1616,26 +1600,25 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); __ add(end_elements, end_elements, Operand(kEndElementsOffset)); __ mov(r7, Operand(new_space_allocation_top)); - __ ldr(r6, MemOperand(r7)); - __ cmp(end_elements, r6); + __ ldr(r3, MemOperand(r7)); + __ cmp(end_elements, r3); __ b(ne, &call_builtin); __ mov(r9, Operand(new_space_allocation_limit)); __ ldr(r9, MemOperand(r9)); - __ add(r6, r6, Operand(kAllocationDelta * kPointerSize)); - __ cmp(r6, r9); + __ add(r3, r3, Operand(kAllocationDelta * kPointerSize)); + __ cmp(r3, r9); __ b(hi, &call_builtin); // We fit and could grow elements. // Update new_space_allocation_top. - __ str(r6, MemOperand(r7)); + __ str(r3, MemOperand(r7)); // Push the argument. - __ ldr(r6, MemOperand(sp, (argc - 1) * kPointerSize)); - __ str(r6, MemOperand(end_elements)); + __ str(r2, MemOperand(end_elements)); // Fill the rest with holes. - __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); + __ LoadRoot(r3, Heap::kTheHoleValueRootIndex); for (int i = 1; i < kAllocationDelta; i++) { - __ str(r6, MemOperand(end_elements, i * kPointerSize)); + __ str(r3, MemOperand(end_elements, i * kPointerSize)); } // Update elements' and array's sizes. @@ -1656,19 +1639,19 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPopCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -1678,25 +1661,22 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss, return_undefined, call_builtin; - Register receiver = r1; Register elements = r3; - GenerateNameCheck(name, &miss); // Get the receiver from the stack const int argc = arguments().immediate(); __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); - // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), - receiver, holder, elements, r4, r0, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, elements, + r4, r0, name, &miss); // Get the elements array of the object. __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); @@ -1745,20 +1725,19 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : function name // -- lr : return address @@ -1768,21 +1747,19 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); - Label miss; Label name_miss; Label index_out_of_range; Label* index_out_of_range_label = &index_out_of_range; if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. @@ -1790,13 +1767,12 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Context::STRING_FUNCTION_INDEX, r0, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, - r1, r3, r4, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + r0, holder, r1, r3, r4, name, &miss); Register receiver = r1; Register index = r4; - Register scratch = r3; Register result = r0; __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); if (argc > 0) { @@ -1805,20 +1781,19 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharCodeAtGenerator char_code_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_code_at_generator.GenerateFast(masm()); + StringCharCodeAtGenerator generator(receiver, + index, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_code_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1829,22 +1804,21 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ bind(&miss); // Restore function name in r2. - __ Move(r2, Handle<String>(name)); + __ Move(r2, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : function name // -- lr : return address @@ -1854,21 +1828,18 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); - Label miss; Label name_miss; Label index_out_of_range; Label* index_out_of_range_label = &index_out_of_range; - if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. @@ -1876,14 +1847,13 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( Context::STRING_FUNCTION_INDEX, r0, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, - r1, r3, r4, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + r0, holder, r1, r3, r4, name, &miss); Register receiver = r0; Register index = r4; - Register scratch1 = r1; - Register scratch2 = r3; + Register scratch = r3; Register result = r0; __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); if (argc > 0) { @@ -1892,21 +1862,20 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharAtGenerator char_at_generator(receiver, - index, - scratch1, - scratch2, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_at_generator.GenerateFast(masm()); + StringCharAtGenerator generator(receiver, + index, + scratch, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1917,22 +1886,21 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ bind(&miss); // Restore function name in r2. - __ Move(r2, Handle<String>(name)); + __ Move(r2, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : function name // -- lr : return address @@ -1945,22 +1913,23 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(r1, &miss); - CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -1976,34 +1945,35 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // Convert the smi code to uint16. __ and_(code, code, Operand(Smi::FromInt(0xffff))); - StringCharFromCodeGenerator char_from_code_generator(code, r0); - char_from_code_generator.GenerateFast(masm()); + StringCharFromCodeGenerator generator(code, r0); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_from_code_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathFloorCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : function name // -- lr : return address @@ -2013,31 +1983,28 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, // ----------------------------------- if (!CpuFeatures::IsSupported(VFP3)) { - return heap()->undefined_value(); + return Handle<Code>::null(); } CpuFeatures::Scope scope_vfp3(VFP3); - const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss, slow; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); - STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(r1, &miss); - - CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2069,7 +2036,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ vmrs(r3); // Set custom FPCSR: // - Set rounding mode to "Round towards Minus Infinity" - // (ie bits [23:22] = 0b10). + // (i.e. bits [23:22] = 0b10). // - Clear vfp cumulative exception flags (bits [3:0]). // - Make sure Flush-to-zero mode control bit is unset (bit 22). __ bic(r9, r3, @@ -2135,23 +2102,24 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ bind(&slow); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathAbsCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : function name // -- lr : return address @@ -2161,25 +2129,22 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // ----------------------------------- const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; GenerateNameCheck(name, &miss); - - if (cell == NULL) { + if (cell.is_null()) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); - STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(r1, &miss); - - CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2236,39 +2201,38 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileFastApiCall( +Handle<Code> CallStubCompiler::CompileFastApiCall( const CallOptimization& optimization, - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { Counters* counters = isolate()->counters(); ASSERT(optimization.is_simple_api_call()); // Bail out if object is a global object as we don't want to // repatch it to global receiver. - if (object->IsGlobalObject()) return heap()->undefined_value(); - if (cell != NULL) return heap()->undefined_value(); - if (!object->IsJSObject()) return heap()->undefined_value(); + if (object->IsGlobalObject()) return Handle<Code>::null(); + if (!cell.is_null()) return Handle<Code>::null(); + if (!object->IsJSObject()) return Handle<Code>::null(); int depth = optimization.GetPrototypeDepthOfExpectedType( - JSObject::cast(object), holder); - if (depth == kInvalidProtoDepth) return heap()->undefined_value(); + Handle<JSObject>::cast(object), holder); + if (depth == kInvalidProtoDepth) return Handle<Code>::null(); Label miss, miss_before_stack_reserved; - GenerateNameCheck(name, &miss_before_stack_reserved); // Get the receiver from the stack. @@ -2284,44 +2248,40 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( ReserveSpaceForFastApiCall(masm(), r0); // Check that the maps haven't changed and find a Holder as a side effect. - CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, + CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, name, depth, &miss); - MaybeObject* result = GenerateFastApiDirectCall(masm(), optimization, argc); - if (result->IsFailure()) return result; + GenerateFastApiDirectCall(masm(), optimization, argc); __ bind(&miss); FreeSpaceForFastApiCall(masm()); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, - JSObject* holder, - JSFunction* function, - String* name, +Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function, + Handle<String> name, CheckType check) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, NULL, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, + Handle<JSGlobalPropertyCell>::null(), + function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the receiver from the stack @@ -2336,16 +2296,14 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Make sure that it's okay not to patch the on stack receiver // unless we're doing a receiver map check. ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); - - SharedFunctionInfo* function_info = function->shared(); switch (check) { case RECEIVER_MAP_CHECK: __ IncrementCounter(masm()->isolate()->counters()->call_const(), 1, r0, r3); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, + name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -2356,28 +2314,25 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, break; case STRING_CHECK: - if (!function->IsBuiltin() && !function_info->strict_mode()) { - // Calling non-strict non-builtins with a value as the receiver - // requires boxing. - __ jmp(&miss); - } else { + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { // Check that the object is a two-byte string or a symbol. __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE); __ b(ge, &miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::STRING_FUNCTION_INDEX, r0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, - r1, r4, name, &miss); - } - break; - - case NUMBER_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + r0, holder, r3, r1, r4, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case NUMBER_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a smi or a heap number. __ JumpIfSmi(r1, &fast); @@ -2387,18 +2342,18 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::NUMBER_FUNCTION_INDEX, r0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, - r1, r4, name, &miss); - } - break; - } - - case BOOLEAN_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + r0, holder, r3, r1, r4, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case BOOLEAN_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a boolean. __ LoadRoot(ip, Heap::kTrueValueRootIndex); @@ -2411,112 +2366,92 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::BOOLEAN_FUNCTION_INDEX, r0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, - r1, r4, name, &miss); + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + r0, holder, r3, r1, r4, name, &miss); + } else { + // Calling non-strict non-builtins with a value as the receiver + // requires boxing. + __ jmp(&miss); } break; - } - - default: - UNREACHABLE(); } - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, call_kind); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address // ----------------------------------- - Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // Get the receiver from the stack. __ ldr(r1, MemOperand(sp, argc * kPointerSize)); - CallInterceptorCompiler compiler(this, arguments(), r2, extra_ic_state_); - MaybeObject* result = compiler.Compile(masm(), - object, - holder, - name, - &lookup, - r1, - r3, - r4, - r0, - &miss); - if (result->IsFailure()) { - return result; - } + CallInterceptorCompiler compiler(this, arguments(), r2, extra_state_); + compiler.Compile(masm(), object, holder, name, &lookup, r1, r3, r4, r0, + &miss); // Move returned value, the function to call, to r1. __ mov(r1, r0); // Restore receiver. __ ldr(r0, MemOperand(sp, argc * kPointerSize)); - GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_); + GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(INTERCEPTOR, name); } -MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileCallGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address // ----------------------------------- - if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, cell, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, cell, function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - GenerateGlobalReceiverCheck(object, holder, name, &miss); - GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy if @@ -2526,45 +2461,37 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, __ str(r3, MemOperand(sp, argc * kPointerSize)); } - // Setup the context (function already in r1). + // Set up the context (function already in r1). __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); // Jump to the cached code (tail call). Counters* counters = masm()->isolate()->counters(); __ IncrementCounter(counters->call_global_inline(), 1, r3, r4); - ASSERT(function->is_compiled()); - Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - if (V8::UseCrankshaft()) { - // TODO(kasperl): For now, we always call indirectly through the - // code field in the function to allow recompilation to take effect - // without changing any of the call sites. - __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); - __ InvokeCode(r3, expected, arguments(), JUMP_FUNCTION, - NullCallWrapper(), call_kind); - } else { - __ InvokeCode(code, expected, arguments(), RelocInfo::CODE_TARGET, - JUMP_FUNCTION, call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); + __ InvokeCode(r3, expected, arguments(), JUMP_FUNCTION, + NullCallWrapper(), call_kind); // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1, r1, r3); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); } -MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -2573,24 +2500,20 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, // ----------------------------------- Label miss; - GenerateStoreField(masm(), - object, - index, - transition, - r1, r2, r3, - &miss); + GenerateStoreField(masm(), object, index, transition, r1, r2, r3, &miss); __ bind(&miss); Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, - AccessorInfo* callback, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreCallback( + Handle<JSObject> object, + Handle<AccessorInfo> callback, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -2599,13 +2522,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(r1, &miss); - // Check that the map of the object hasn't changed. - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ cmp(r3, Operand(Handle<Map>(object->map()))); - __ b(ne, &miss); + __ CheckMap(r1, r3, Handle<Map>(object->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -2617,7 +2536,7 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); __ push(r1); // receiver - __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback info + __ mov(ip, Operand(callback)); // callback info __ Push(ip, r2, r0); // Do tail-call to the runtime system. @@ -2636,8 +2555,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, } -MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreInterceptor( + Handle<JSObject> receiver, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -2646,13 +2566,9 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(r1, &miss); - // Check that the map of the object hasn't changed. - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - __ cmp(r3, Operand(Handle<Map>(receiver->map()))); - __ b(ne, &miss); + __ CheckMap(r1, r3, Handle<Map>(receiver->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (receiver->IsJSGlobalProxy()) { @@ -2684,9 +2600,10 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, } -MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, - JSGlobalPropertyCell* cell, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreGlobal( + Handle<GlobalObject> object, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -2704,7 +2621,7 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, // cell could have been deleted and reintroducing the global needs // to update the property details in the property dictionary of the // global object. We bail out to the runtime system to do that. - __ mov(r4, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ mov(r4, Operand(cell)); __ LoadRoot(r5, Heap::kTheHoleValueRootIndex); __ ldr(r6, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset)); __ cmp(r5, r6); @@ -2712,6 +2629,7 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, // Store the value in the cell. __ str(r0, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset)); + // Cells are always rescanned, so no write barrier here. Counters* counters = masm()->isolate()->counters(); __ IncrementCounter(counters->named_store_global_inline(), 1, r4, r3); @@ -2728,9 +2646,9 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last) { +Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> last) { // ----------- S t a t e ------------- // -- r0 : receiver // -- lr : return address @@ -2746,15 +2664,8 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - r1, - &miss); - if (cell->IsFailure()) { - miss.Unuse(); - return cell; - } + GenerateCheckPropertyCell( + masm(), Handle<GlobalObject>::cast(last), name, r1, &miss); } // Return undefined if maps of the full prototype chain are still the @@ -2766,14 +2677,14 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NONEXISTENT, heap()->empty_string()); + return GetCode(NONEXISTENT, factory()->empty_string()); } -MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, - JSObject* holder, +Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name @@ -2790,24 +2701,19 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> LoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name // -- lr : return address // ----------------------------------- Label miss; - - MaybeObject* result = GenerateLoadCallback(object, holder, r0, r2, r3, r1, r4, - callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(object, holder, r0, r2, r3, r1, r4, callback, name, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2816,10 +2722,10 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, } -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name @@ -2836,9 +2742,9 @@ MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name @@ -2846,17 +2752,9 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, // ----------------------------------- Label miss; - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(object, - holder, - &lookup, - r0, - r2, - r3, - r1, - r4, - name, + GenerateLoadInterceptor(object, holder, &lookup, r0, r2, r3, r1, r4, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2866,11 +2764,12 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete) { +Handle<Code> LoadStubCompiler::CompileLoadGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name, + bool is_dont_delete) { // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name @@ -2881,7 +2780,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(r0, &miss); } @@ -2889,7 +2788,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, CheckPrototypes(object, r0, holder, r3, r4, r1, name, &miss); // Get the value from the cell. - __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ mov(r3, Operand(cell)); __ ldr(r4, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset)); // Check for deleted property if property can actually be deleted. @@ -2913,9 +2812,9 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int index) { // ----------- S t a t e ------------- // -- lr : return address @@ -2925,7 +2824,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, Label miss; // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); GenerateLoadField(receiver, holder, r1, r2, r3, r4, index, name, &miss); @@ -2936,11 +2835,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -2949,16 +2848,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( Label miss; // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); - MaybeObject* result = GenerateLoadCallback(receiver, holder, r1, r0, r2, r3, - r4, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(receiver, holder, r1, r0, r2, r3, r4, callback, name, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2966,10 +2860,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( } -MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -2978,7 +2873,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, Label miss; // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); GenerateLoadConstant(receiver, holder, r1, r2, r3, r4, value, name, &miss); @@ -2990,9 +2885,10 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor( + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -3001,20 +2897,12 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, Label miss; // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(receiver, - holder, - &lookup, - r1, - r0, - r2, - r3, - r4, - name, + GenerateLoadInterceptor(receiver, holder, &lookup, r1, r0, r2, r3, r4, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -3023,7 +2911,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -3032,7 +2921,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { Label miss; // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); GenerateLoadArrayLength(masm(), r1, r2, &miss); @@ -3043,7 +2932,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -3055,7 +2945,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ IncrementCounter(counters->keyed_load_string_length(), 1, r2, r3); // Check the key is the cached one. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); GenerateLoadStringLength(masm(), r1, r2, r3, &miss, true); @@ -3068,7 +2958,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype( + Handle<String> name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -3080,7 +2971,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { __ IncrementCounter(counters->keyed_load_function_prototype(), 1, r2, r3); // Check the name hasn't changed. - __ cmp(r0, Operand(Handle<String>(name))); + __ cmp(r0, Operand(name)); __ b(ne, &miss); GenerateLoadFunctionPrototype(masm(), r1, r2, r3, &miss); @@ -3092,33 +2983,29 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key // -- r1 : receiver // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); - MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(r1, - r2, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode(); + + __ DispatchMap(r1, r2, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_ics) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key @@ -3130,11 +3017,9 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( int receiver_count = receiver_maps->length(); __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - Handle<Code> code(handler_ics->at(current)); - __ mov(ip, Operand(map)); + __ mov(ip, Operand(receiver_maps->at(current))); __ cmp(r2, ip); - __ Jump(code, RelocInfo::CODE_TARGET, eq); + __ Jump(handler_ics->at(current), RelocInfo::CODE_TARGET, eq); } __ bind(&miss); @@ -3142,14 +3027,14 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( __ Jump(miss_ic, RelocInfo::CODE_TARGET, al); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : name @@ -3162,17 +3047,12 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ IncrementCounter(counters->keyed_store_field(), 1, r3, r4); // Check that the name has not changed. - __ cmp(r1, Operand(Handle<String>(name))); + __ cmp(r1, Operand(name)); __ b(ne, &miss); // r3 is used as scratch register. r1 and r2 keep their values if a jump to // the miss label is generated. - GenerateStoreField(masm(), - object, - index, - transition, - r2, r1, r3, - &miss); + GenerateStoreField(masm(), object, index, transition, r2, r1, r3, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_store_field(), 1, r3, r4); @@ -3180,11 +3060,12 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { +Handle<Code> KeyedStoreStubCompiler::CompileStoreElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : key @@ -3192,29 +3073,25 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- lr : return address // -- r3 : scratch // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - MaybeObject* maybe_stub = - KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(r2, - r3, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = + KeyedStoreElementStub(is_js_array, elements_kind).GetCode(); + + __ DispatchMap(r2, r3, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_stubs, + MapHandleList* transitioned_maps) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : key @@ -3227,12 +3104,18 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( int receiver_count = receiver_maps->length(); __ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset)); - for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - Handle<Code> code(handler_ics->at(current)); - __ mov(ip, Operand(map)); + for (int i = 0; i < receiver_count; ++i) { + __ mov(ip, Operand(receiver_maps->at(i))); __ cmp(r3, ip); - __ Jump(code, RelocInfo::CODE_TARGET, eq); + if (transitioned_maps->at(i).is_null()) { + __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, eq); + } else { + Label next_map; + __ b(ne, &next_map); + __ mov(r3, Operand(transitioned_maps->at(i))); + __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, al); + __ bind(&next_map); + } } __ bind(&miss); @@ -3240,11 +3123,12 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( __ Jump(miss_ic, RelocInfo::CODE_TARGET, al); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { +Handle<Code> ConstructStubCompiler::CompileConstructStub( + Handle<JSFunction> function) { // ----------- S t a t e ------------- // -- r0 : argc // -- r1 : constructor @@ -3290,12 +3174,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // r2: initial map // r7: undefined __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); - __ AllocateInNewSpace(r3, - r4, - r5, - r6, - &generic_stub_call, - SIZE_IN_WORDS); + __ AllocateInNewSpace(r3, r4, r5, r6, &generic_stub_call, SIZE_IN_WORDS); // Allocated the JSObject, now initialize the fields. Map is set to initial // map and properties and elements are set to empty fixed array. @@ -3327,7 +3206,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // r7: undefined // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. - SharedFunctionInfo* shared = function->shared(); + Handle<SharedFunctionInfo> shared(function->shared()); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { Label not_passed, next; @@ -3454,6 +3333,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3540,6 +3420,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3784,9 +3665,9 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1); __ bind(&miss_force_generic); - Code* stub = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET); + Handle<Code> stub = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(stub, RelocInfo::CODE_TARGET); } @@ -3880,6 +3761,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3943,6 +3825,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -4082,6 +3965,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -4157,9 +4041,9 @@ void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) { __ Ret(); __ bind(&miss_force_generic); - Code* stub = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET); + Handle<Code> stub = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(stub, RelocInfo::CODE_TARGET); } @@ -4234,8 +4118,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : key @@ -4244,7 +4130,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, // -- r3 : scratch // -- r4 : scratch (elements) // ----------------------------------- - Label miss_force_generic; + Label miss_force_generic, transition_elements_kind; Register value_reg = r0; Register key_reg = r1; @@ -4277,15 +4163,33 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ cmp(key_reg, scratch); __ b(hs, &miss_force_generic); - __ add(scratch, - elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); - __ str(value_reg, - MemOperand(scratch, key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); - __ RecordWrite(scratch, - Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize), - receiver_reg , elements_reg); - + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + __ JumpIfNotSmi(value_reg, &transition_elements_kind); + __ add(scratch, + elements_reg, + Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + __ add(scratch, + scratch, + Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(value_reg, MemOperand(scratch)); + } else { + ASSERT(elements_kind == FAST_ELEMENTS); + __ add(scratch, + elements_reg, + Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + __ add(scratch, + scratch, + Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ str(value_reg, MemOperand(scratch)); + __ mov(receiver_reg, value_reg); + __ RecordWrite(elements_reg, // Object. + scratch, // Address. + receiver_reg, // Value. + kLRHasNotBeenSaved, + kDontSaveFPRegs); + } // value_reg (r0) is preserved. // Done. __ Ret(); @@ -4294,6 +4198,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ Jump(ic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ Jump(ic_miss, RelocInfo::CODE_TARGET); } @@ -4309,15 +4217,15 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // -- r4 : scratch // -- r5 : scratch // ----------------------------------- - Label miss_force_generic, smi_value, is_nan, maybe_nan, have_double_value; + Label miss_force_generic, transition_elements_kind; Register value_reg = r0; Register key_reg = r1; Register receiver_reg = r2; - Register scratch = r3; - Register elements_reg = r4; - Register mantissa_reg = r5; - Register exponent_reg = r6; + Register elements_reg = r3; + Register scratch1 = r4; + Register scratch2 = r5; + Register scratch3 = r6; Register scratch4 = r7; // This stub is meant to be tail-jumped to, the receiver must already @@ -4329,90 +4237,25 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // Check that the key is within bounds. if (is_js_array) { - __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset)); + __ ldr(scratch1, FieldMemOperand(receiver_reg, JSArray::kLengthOffset)); } else { - __ ldr(scratch, + __ ldr(scratch1, FieldMemOperand(elements_reg, FixedArray::kLengthOffset)); } // Compare smis, unsigned compare catches both negative and out-of-bound // indexes. - __ cmp(key_reg, scratch); + __ cmp(key_reg, scratch1); __ b(hs, &miss_force_generic); - // Handle smi values specially. - __ JumpIfSmi(value_reg, &smi_value); - - // Ensure that the object is a heap number - __ CheckMap(value_reg, - scratch, - masm->isolate()->factory()->heap_number_map(), - &miss_force_generic, - DONT_DO_SMI_CHECK); - - // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000 - // in the exponent. - __ mov(scratch, Operand(kNaNOrInfinityLowerBoundUpper32)); - __ ldr(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset)); - __ cmp(exponent_reg, scratch); - __ b(ge, &maybe_nan); - - __ ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); - - __ bind(&have_double_value); - __ add(scratch, elements_reg, - Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); - __ str(mantissa_reg, FieldMemOperand(scratch, FixedDoubleArray::kHeaderSize)); - uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); - __ str(exponent_reg, FieldMemOperand(scratch, offset)); - __ Ret(); - - __ bind(&maybe_nan); - // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise - // it's an Infinity, and the non-NaN code path applies. - __ b(gt, &is_nan); - __ ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); - __ cmp(mantissa_reg, Operand(0)); - __ b(eq, &have_double_value); - __ bind(&is_nan); - // Load canonical NaN for storing into the double array. - uint64_t nan_int64 = BitCast<uint64_t>( - FixedDoubleArray::canonical_not_the_hole_nan_as_double()); - __ mov(mantissa_reg, Operand(static_cast<uint32_t>(nan_int64))); - __ mov(exponent_reg, Operand(static_cast<uint32_t>(nan_int64 >> 32))); - __ jmp(&have_double_value); - - __ bind(&smi_value); - __ add(scratch, elements_reg, - Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); - __ add(scratch, scratch, - Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); - // scratch is now effective address of the double element - - FloatingPointHelper::Destination destination; - if (CpuFeatures::IsSupported(VFP3)) { - destination = FloatingPointHelper::kVFPRegisters; - } else { - destination = FloatingPointHelper::kCoreRegisters; - } - - Register untagged_value = receiver_reg; - __ SmiUntag(untagged_value, value_reg); - FloatingPointHelper::ConvertIntToDouble( - masm, - untagged_value, - destination, - d0, - mantissa_reg, - exponent_reg, - scratch4, - s2); - if (destination == FloatingPointHelper::kVFPRegisters) { - CpuFeatures::Scope scope(VFP3); - __ vstr(d0, scratch, 0); - } else { - __ str(mantissa_reg, MemOperand(scratch, 0)); - __ str(exponent_reg, MemOperand(scratch, Register::kSizeInBytes)); - } + __ StoreNumberToDoubleElements(value_reg, + key_reg, + receiver_reg, + elements_reg, + scratch1, + scratch2, + scratch3, + scratch4, + &transition_elements_kind); __ Ret(); // Handle store cache miss, replacing the ic with the generic stub. @@ -4420,6 +4263,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ Jump(ic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ Jump(ic_miss, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js index 98fe3ac7b1..16e37c5a7b 100644 --- a/deps/v8/src/array.js +++ b/deps/v8/src/array.js @@ -201,17 +201,14 @@ function ConvertToString(x) { function ConvertToLocaleString(e) { - if (e == null) { + if (IS_NULL_OR_UNDEFINED(e)) { return ''; } else { - // e_obj's toLocaleString might be overwritten, check if it is a function. - // Call ToString if toLocaleString is not a function. - // See issue 877615. + // According to ES5, section 15.4.4.3, the toLocaleString conversion + // must throw a TypeError if ToObject(e).toLocaleString isn't + // callable. var e_obj = ToObject(e); - if (IS_SPEC_FUNCTION(e_obj.toLocaleString)) - return ToString(e_obj.toLocaleString()); - else - return ToString(e); + return %ToString(e_obj.toLocaleString()); } } @@ -331,8 +328,9 @@ function SimpleSlice(array, start_i, del_count, len, deleted_elements) { // would be the appropriate test. We follow KJS in consulting the // prototype. var current = array[index]; - if (!IS_UNDEFINED(current) || index in array) + if (!IS_UNDEFINED(current) || index in array) { deleted_elements[i] = current; + } } } @@ -381,18 +379,31 @@ function SimpleMove(array, start_i, del_count, len, num_additional_args) { function ArrayToString() { - if (!IS_ARRAY(this)) { - throw new $TypeError('Array.prototype.toString is not generic'); + var array; + var func; + if (IS_ARRAY(this)) { + func = this.join; + if (func === ArrayJoin) { + return Join(this, this.length, ',', ConvertToString); + } + array = this; + } else { + array = ToObject(this); + func = array.join; } - return Join(this, this.length, ',', ConvertToString); + if (!IS_SPEC_FUNCTION(func)) { + return %_CallFunction(array, ObjectToString); + } + return %_CallFunction(array, func); } function ArrayToLocaleString() { - if (!IS_ARRAY(this)) { - throw new $TypeError('Array.prototype.toString is not generic'); - } - return Join(this, this.length, ',', ConvertToLocaleString); + var array = ToObject(this); + var arrayLen = array.length; + var len = TO_UINT32(arrayLen); + if (len === 0) return ""; + return Join(array, len, ',', ConvertToLocaleString); } @@ -485,12 +496,12 @@ function SparseReverse(array, len) { if (j_complement <= i) { high = j; - while (keys[--high_counter] == j); + while (keys[--high_counter] == j) { } low = j_complement; } if (j_complement >= i) { low = i; - while (keys[++low_counter] == i); + while (keys[++low_counter] == i) { } high = len - i - 1; } @@ -566,10 +577,11 @@ function ArrayShift() { var first = this[0]; - if (IS_ARRAY(this)) + if (IS_ARRAY(this)) { SmartMove(this, 0, 1, len, 0); - else + } else { SimpleMove(this, 0, 1, len, 0); + } this.length = len - 1; @@ -586,10 +598,11 @@ function ArrayUnshift(arg1) { // length == 1 var len = TO_UINT32(this.length); var num_arguments = %_ArgumentsLength(); - if (IS_ARRAY(this)) + if (IS_ARRAY(this)) { SmartMove(this, 0, 0, len, num_arguments); - else + } else { SimpleMove(this, 0, 0, len, num_arguments); + } for (var i = 0; i < num_arguments; i++) { this[i] = %_Arguments(i); @@ -993,25 +1006,32 @@ function ArrayFilter(f, receiver) { ["Array.prototype.filter"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = ToUint32(array.length); + if (!IS_SPEC_FUNCTION(f)) { throw MakeTypeError('called_non_callable', [ f ]); } if (IS_NULL_OR_UNDEFINED(receiver)) { receiver = %GetDefaultReceiver(f) || receiver; + } else if (!IS_SPEC_OBJECT(receiver)) { + receiver = ToObject(receiver); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = ToUint32(this.length); - var result = []; - var result_length = 0; + + var result = new $Array(); + var accumulator = new InternalArray(); + var accumulator_length = 0; for (var i = 0; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - if (%_CallFunction(receiver, current, i, this, f)) { - result[result_length++] = current; + var current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { + if (%_CallFunction(receiver, current, i, array, f)) { + accumulator[accumulator_length++] = current; } } } + %MoveArrayContents(accumulator, result); return result; } @@ -1022,19 +1042,24 @@ function ArrayForEach(f, receiver) { ["Array.prototype.forEach"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = TO_UINT32(array.length); + if (!IS_SPEC_FUNCTION(f)) { throw MakeTypeError('called_non_callable', [ f ]); } if (IS_NULL_OR_UNDEFINED(receiver)) { receiver = %GetDefaultReceiver(f) || receiver; + } else if (!IS_SPEC_OBJECT(receiver)) { + receiver = ToObject(receiver); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = TO_UINT32(this.length); + for (var i = 0; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - %_CallFunction(receiver, current, i, this, f); + var current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { + %_CallFunction(receiver, current, i, array, f); } } } @@ -1048,19 +1073,24 @@ function ArraySome(f, receiver) { ["Array.prototype.some"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = TO_UINT32(array.length); + if (!IS_SPEC_FUNCTION(f)) { throw MakeTypeError('called_non_callable', [ f ]); } if (IS_NULL_OR_UNDEFINED(receiver)) { receiver = %GetDefaultReceiver(f) || receiver; + } else if (!IS_SPEC_OBJECT(receiver)) { + receiver = ToObject(receiver); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = TO_UINT32(this.length); + for (var i = 0; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - if (%_CallFunction(receiver, current, i, this, f)) return true; + var current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { + if (%_CallFunction(receiver, current, i, array, f)) return true; } } return false; @@ -1073,19 +1103,24 @@ function ArrayEvery(f, receiver) { ["Array.prototype.every"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = TO_UINT32(array.length); + if (!IS_SPEC_FUNCTION(f)) { throw MakeTypeError('called_non_callable', [ f ]); } if (IS_NULL_OR_UNDEFINED(receiver)) { receiver = %GetDefaultReceiver(f) || receiver; + } else if (!IS_SPEC_OBJECT(receiver)) { + receiver = ToObject(receiver); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = TO_UINT32(this.length); + for (var i = 0; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - if (!%_CallFunction(receiver, current, i, this, f)) return false; + var current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { + if (!%_CallFunction(receiver, current, i, array, f)) return false; } } return true; @@ -1097,21 +1132,26 @@ function ArrayMap(f, receiver) { ["Array.prototype.map"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = TO_UINT32(array.length); + if (!IS_SPEC_FUNCTION(f)) { throw MakeTypeError('called_non_callable', [ f ]); } if (IS_NULL_OR_UNDEFINED(receiver)) { receiver = %GetDefaultReceiver(f) || receiver; + } else if (!IS_SPEC_OBJECT(receiver)) { + receiver = ToObject(receiver); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = TO_UINT32(this.length); + var result = new $Array(); var accumulator = new InternalArray(length); for (var i = 0; i < length; i++) { - var current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { - accumulator[i] = %_CallFunction(receiver, current, i, this, f); + var current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { + accumulator[i] = %_CallFunction(receiver, current, i, array, f); } } %MoveArrayContents(accumulator, result); @@ -1245,19 +1285,20 @@ function ArrayReduce(callback, current) { ["Array.prototype.reduce"]); } + // Pull out the length so that modifications to the length in the + // loop will not affect the looping and side effects are visible. + var array = ToObject(this); + var length = ToUint32(array.length); + if (!IS_SPEC_FUNCTION(callback)) { throw MakeTypeError('called_non_callable', [callback]); } - // Pull out the length so that modifications to the length in the - // loop will not affect the looping. - var length = ToUint32(this.length); var i = 0; - find_initial: if (%_ArgumentsLength() < 2) { for (; i < length; i++) { - current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { + current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { i++; break find_initial; } @@ -1267,9 +1308,9 @@ function ArrayReduce(callback, current) { var receiver = %GetDefaultReceiver(callback); for (; i < length; i++) { - var element = this[i]; - if (!IS_UNDEFINED(element) || i in this) { - current = %_CallFunction(receiver, current, element, i, this, callback); + var element = array[i]; + if (!IS_UNDEFINED(element) || i in array) { + current = %_CallFunction(receiver, current, element, i, array, callback); } } return current; @@ -1281,15 +1322,20 @@ function ArrayReduceRight(callback, current) { ["Array.prototype.reduceRight"]); } + // Pull out the length so that side effects are visible before the + // callback function is checked. + var array = ToObject(this); + var length = ToUint32(array.length); + if (!IS_SPEC_FUNCTION(callback)) { throw MakeTypeError('called_non_callable', [callback]); } - var i = ToUint32(this.length) - 1; + var i = length - 1; find_initial: if (%_ArgumentsLength() < 2) { for (; i >= 0; i--) { - current = this[i]; - if (!IS_UNDEFINED(current) || i in this) { + current = array[i]; + if (!IS_UNDEFINED(current) || i in array) { i--; break find_initial; } @@ -1299,9 +1345,9 @@ function ArrayReduceRight(callback, current) { var receiver = %GetDefaultReceiver(callback); for (; i >= 0; i--) { - var element = this[i]; - if (!IS_UNDEFINED(element) || i in this) { - current = %_CallFunction(receiver, current, element, i, this, callback); + var element = array[i]; + if (!IS_UNDEFINED(element) || i in array) { + current = %_CallFunction(receiver, current, element, i, array, callback); } } return current; @@ -1342,7 +1388,7 @@ function SetUpArray() { // set their names. // Manipulate the length of some of the functions to meet // expectations set by ECMA-262 or Mozilla. - InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array( + InstallFunctions($Array.prototype, DONT_ENUM, $Array( "toString", getFunction("toString", ArrayToString), "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString), "join", getFunction("join", ArrayJoin), diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index ad5f350816..0bec57752f 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 Sun Microsystems Inc. +// Copyright (c) 1994-2006 Sun Microsystems Inc. // All Rights Reserved. // // Redistribution and use in source and binary forms, with or without @@ -30,23 +30,42 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. -#include "v8.h" +#include "assembler.h" -#include "arguments.h" +#include <math.h> // For cos, log, pow, sin, tan, etc. +#include "api.h" +#include "builtins.h" +#include "counters.h" +#include "cpu.h" +#include "debug.h" #include "deoptimizer.h" #include "execution.h" -#include "ic-inl.h" -#include "factory.h" +#include "ic.h" +#include "isolate.h" +#include "jsregexp.h" +#include "platform.h" +#include "regexp-macro-assembler.h" +#include "regexp-stack.h" #include "runtime.h" -#include "runtime-profiler.h" #include "serialize.h" +#include "store-buffer-inl.h" #include "stub-cache.h" -#include "regexp-stack.h" -#include "ast.h" -#include "regexp-macro-assembler.h" -#include "platform.h" +#include "token.h" + +#if V8_TARGET_ARCH_IA32 +#include "ia32/assembler-ia32-inl.h" +#elif V8_TARGET_ARCH_X64 +#include "x64/assembler-x64-inl.h" +#elif V8_TARGET_ARCH_ARM +#include "arm/assembler-arm-inl.h" +#elif V8_TARGET_ARCH_MIPS +#include "mips/assembler-mips-inl.h" +#else +#error "Unknown architecture." +#endif + // Include native regexp-macro-assembler. #ifndef V8_INTERPRETED_REGEXP #if V8_TARGET_ARCH_IA32 @@ -516,6 +535,7 @@ void RelocIterator::next() { RelocIterator::RelocIterator(Code* code, int mode_mask) { + rinfo_.host_ = code; rinfo_.pc_ = code->instruction_start(); rinfo_.data_ = 0; // Relocation info is read backwards. @@ -736,9 +756,38 @@ ExternalReference::ExternalReference(const SCTableReference& table_ref) : address_(table_ref.address()) {} +ExternalReference ExternalReference:: + incremental_marking_record_write_function(Isolate* isolate) { + return ExternalReference(Redirect( + isolate, + FUNCTION_ADDR(IncrementalMarking::RecordWriteFromCode))); +} + + +ExternalReference ExternalReference:: + incremental_evacuation_record_write_function(Isolate* isolate) { + return ExternalReference(Redirect( + isolate, + FUNCTION_ADDR(IncrementalMarking::RecordWriteForEvacuationFromCode))); +} + + +ExternalReference ExternalReference:: + store_buffer_overflow_function(Isolate* isolate) { + return ExternalReference(Redirect( + isolate, + FUNCTION_ADDR(StoreBuffer::StoreBufferOverflow))); +} + + +ExternalReference ExternalReference::flush_icache_function(Isolate* isolate) { + return ExternalReference(Redirect(isolate, FUNCTION_ADDR(CPU::FlushICache))); +} + + ExternalReference ExternalReference::perform_gc_function(Isolate* isolate) { - return ExternalReference(Redirect(isolate, - FUNCTION_ADDR(Runtime::PerformGC))); + return + ExternalReference(Redirect(isolate, FUNCTION_ADDR(Runtime::PerformGC))); } @@ -785,11 +834,6 @@ ExternalReference ExternalReference::compute_output_frames_function( } -ExternalReference ExternalReference::global_contexts_list(Isolate* isolate) { - return ExternalReference(isolate->heap()->global_contexts_list_address()); -} - - ExternalReference ExternalReference::keyed_lookup_cache_keys(Isolate* isolate) { return ExternalReference(isolate->keyed_lookup_cache()->keys_address()); } @@ -802,19 +846,8 @@ ExternalReference ExternalReference::keyed_lookup_cache_field_offsets( } -ExternalReference ExternalReference::the_hole_value_location(Isolate* isolate) { - return ExternalReference(isolate->factory()->the_hole_value().location()); -} - - -ExternalReference ExternalReference::arguments_marker_location( - Isolate* isolate) { - return ExternalReference(isolate->factory()->arguments_marker().location()); -} - - -ExternalReference ExternalReference::roots_address(Isolate* isolate) { - return ExternalReference(isolate->heap()->roots_address()); +ExternalReference ExternalReference::roots_array_start(Isolate* isolate) { + return ExternalReference(isolate->heap()->roots_array_start()); } @@ -840,9 +873,14 @@ ExternalReference ExternalReference::new_space_start(Isolate* isolate) { } +ExternalReference ExternalReference::store_buffer_top(Isolate* isolate) { + return ExternalReference(isolate->heap()->store_buffer()->TopAddress()); +} + + ExternalReference ExternalReference::new_space_mask(Isolate* isolate) { - Address mask = reinterpret_cast<Address>(isolate->heap()->NewSpaceMask()); - return ExternalReference(mask); + return ExternalReference(reinterpret_cast<Address>( + isolate->heap()->NewSpaceMask())); } @@ -1025,6 +1063,11 @@ static double math_cos_double(double x) { } +static double math_tan_double(double x) { + return tan(x); +} + + static double math_log_double(double x) { return log(x); } @@ -1046,6 +1089,14 @@ ExternalReference ExternalReference::math_cos_double_function( } +ExternalReference ExternalReference::math_tan_double_function( + Isolate* isolate) { + return ExternalReference(Redirect(isolate, + FUNCTION_ADDR(math_tan_double), + BUILTIN_FP_CALL)); +} + + ExternalReference ExternalReference::math_log_double_function( Isolate* isolate) { return ExternalReference(Redirect(isolate, @@ -1074,17 +1125,9 @@ double power_double_int(double x, int y) { double power_double_double(double x, double y) { - int y_int = static_cast<int>(y); - if (y == y_int) { - return power_double_int(x, y_int); // Returns 1.0 for exponent 0. - } - if (!isinf(x)) { - if (y == 0.5) return sqrt(x + 0.0); // -0 must be converted to +0. - if (y == -0.5) return 1.0 / sqrt(x + 0.0); - } - if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) { - return OS::nan_value(); - } + // The checks for special cases can be dropped in ia32 because it has already + // been done in generated code before bailing out here. + if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) return OS::nan_value(); return pow(x, y); } @@ -1111,6 +1154,23 @@ static int native_compare_doubles(double y, double x) { } +bool EvalComparison(Token::Value op, double op1, double op2) { + ASSERT(Token::IsCompareOp(op)); + switch (op) { + case Token::EQ: + case Token::EQ_STRICT: return (op1 == op2); + case Token::NE: return (op1 != op2); + case Token::LT: return (op1 < op2); + case Token::GT: return (op1 > op2); + case Token::LTE: return (op1 <= op2); + case Token::GTE: return (op1 >= op2); + default: + UNREACHABLE(); + return false; + } +} + + ExternalReference ExternalReference::double_fp_operation( Token::Value operation, Isolate* isolate) { typedef double BinaryFPOperation(double x, double y); diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index d58034df0d..e7c92b451c 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -30,19 +30,27 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. #ifndef V8_ASSEMBLER_H_ #define V8_ASSEMBLER_H_ +#include "v8.h" + #include "allocation.h" +#include "builtins.h" #include "gdb-jit.h" +#include "isolate.h" #include "runtime.h" #include "token.h" namespace v8 { + +class ApiFunction; + namespace internal { +struct StatsCounter; const unsigned kNoASTId = -1; // ----------------------------------------------------------------------------- // Platform independent assembler base class. @@ -143,6 +151,9 @@ class Label BASE_EMBEDDED { }; +enum SaveFPRegsMode { kDontSaveFPRegs, kSaveFPRegs }; + + // ----------------------------------------------------------------------------- // Relocation information @@ -216,8 +227,9 @@ class RelocInfo BASE_EMBEDDED { RelocInfo() {} - RelocInfo(byte* pc, Mode rmode, intptr_t data) - : pc_(pc), rmode_(rmode), data_(data) { + + RelocInfo(byte* pc, Mode rmode, intptr_t data, Code* host) + : pc_(pc), rmode_(rmode), data_(data), host_(host) { } static inline bool IsConstructCall(Mode mode) { @@ -226,6 +238,9 @@ class RelocInfo BASE_EMBEDDED { static inline bool IsCodeTarget(Mode mode) { return mode <= LAST_CODE_ENUM; } + static inline bool IsEmbeddedObject(Mode mode) { + return mode == EMBEDDED_OBJECT; + } // Is the relocation mode affected by GC? static inline bool IsGCRelocMode(Mode mode) { return mode <= LAST_GCED_ENUM; @@ -258,12 +273,13 @@ class RelocInfo BASE_EMBEDDED { void set_pc(byte* pc) { pc_ = pc; } Mode rmode() const { return rmode_; } intptr_t data() const { return data_; } + Code* host() const { return host_; } // Apply a relocation by delta bytes INLINE(void apply(intptr_t delta)); // Is the pointer this relocation info refers to coded like a plain pointer - // or is it strange in some way (eg relative or patched into a series of + // or is it strange in some way (e.g. relative or patched into a series of // instructions). bool IsCodedSpecially(); @@ -271,14 +287,17 @@ class RelocInfo BASE_EMBEDDED { // this relocation applies to; // can only be called if IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY INLINE(Address target_address()); - INLINE(void set_target_address(Address target)); + INLINE(void set_target_address(Address target, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER)); INLINE(Object* target_object()); INLINE(Handle<Object> target_object_handle(Assembler* origin)); INLINE(Object** target_object_address()); - INLINE(void set_target_object(Object* target)); + INLINE(void set_target_object(Object* target, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER)); INLINE(JSGlobalPropertyCell* target_cell()); INLINE(Handle<JSGlobalPropertyCell> target_cell_handle()); - INLINE(void set_target_cell(JSGlobalPropertyCell* cell)); + INLINE(void set_target_cell(JSGlobalPropertyCell* cell, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER)); // Read the address of the word containing the target_address in an @@ -353,13 +372,14 @@ class RelocInfo BASE_EMBEDDED { byte* pc_; Mode rmode_; intptr_t data_; + Code* host_; #ifdef V8_TARGET_ARCH_MIPS // Code and Embedded Object pointers in mips are stored split // across two consecutive 32-bit instructions. Heap management // routines expect to access these pointers indirectly. The following // location provides a place for these pointers to exist natually // when accessed via the Iterator. - Object *reconstructed_obj_ptr_; + Object* reconstructed_obj_ptr_; // External-reference pointers are also split across instruction-pairs // in mips, but are accessed via indirect pointers. This location // provides a place for that pointer to exist naturally. Its address @@ -561,6 +581,13 @@ class ExternalReference BASE_EMBEDDED { // pattern. This means that they have to be added to the // ExternalReferenceTable in serialize.cc manually. + static ExternalReference incremental_marking_record_write_function( + Isolate* isolate); + static ExternalReference incremental_evacuation_record_write_function( + Isolate* isolate); + static ExternalReference store_buffer_overflow_function( + Isolate* isolate); + static ExternalReference flush_icache_function(Isolate* isolate); static ExternalReference perform_gc_function(Isolate* isolate); static ExternalReference fill_heap_number_with_random_function( Isolate* isolate); @@ -571,20 +598,13 @@ class ExternalReference BASE_EMBEDDED { // Deoptimization support. static ExternalReference new_deoptimizer_function(Isolate* isolate); static ExternalReference compute_output_frames_function(Isolate* isolate); - static ExternalReference global_contexts_list(Isolate* isolate); // Static data in the keyed lookup cache. static ExternalReference keyed_lookup_cache_keys(Isolate* isolate); static ExternalReference keyed_lookup_cache_field_offsets(Isolate* isolate); - // Static variable Factory::the_hole_value.location() - static ExternalReference the_hole_value_location(Isolate* isolate); - - // Static variable Factory::arguments_marker.location() - static ExternalReference arguments_marker_location(Isolate* isolate); - - // Static variable Heap::roots_address() - static ExternalReference roots_address(Isolate* isolate); + // Static variable Heap::roots_array_start() + static ExternalReference roots_array_start(Isolate* isolate); // Static variable StackGuard::address_of_jslimit() static ExternalReference address_of_stack_limit(Isolate* isolate); @@ -606,6 +626,10 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference new_space_start(Isolate* isolate); static ExternalReference new_space_mask(Isolate* isolate); static ExternalReference heap_always_allocate_scope_depth(Isolate* isolate); + static ExternalReference new_space_mark_bits(Isolate* isolate); + + // Write barrier. + static ExternalReference store_buffer_top(Isolate* isolate); // Used for fast allocation in generated code. static ExternalReference new_space_allocation_top_address(Isolate* isolate); @@ -635,6 +659,7 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference math_sin_double_function(Isolate* isolate); static ExternalReference math_cos_double_function(Isolate* isolate); + static ExternalReference math_tan_double_function(Isolate* isolate); static ExternalReference math_log_double_function(Isolate* isolate); Address address() const {return reinterpret_cast<Address>(address_);} @@ -799,33 +824,33 @@ class PreservePositionScope BASE_EMBEDDED { // ----------------------------------------------------------------------------- // Utility functions -static inline bool is_intn(int x, int n) { +inline bool is_intn(int x, int n) { return -(1 << (n-1)) <= x && x < (1 << (n-1)); } -static inline bool is_int8(int x) { return is_intn(x, 8); } -static inline bool is_int16(int x) { return is_intn(x, 16); } -static inline bool is_int18(int x) { return is_intn(x, 18); } -static inline bool is_int24(int x) { return is_intn(x, 24); } +inline bool is_int8(int x) { return is_intn(x, 8); } +inline bool is_int16(int x) { return is_intn(x, 16); } +inline bool is_int18(int x) { return is_intn(x, 18); } +inline bool is_int24(int x) { return is_intn(x, 24); } -static inline bool is_uintn(int x, int n) { +inline bool is_uintn(int x, int n) { return (x & -(1 << n)) == 0; } -static inline bool is_uint2(int x) { return is_uintn(x, 2); } -static inline bool is_uint3(int x) { return is_uintn(x, 3); } -static inline bool is_uint4(int x) { return is_uintn(x, 4); } -static inline bool is_uint5(int x) { return is_uintn(x, 5); } -static inline bool is_uint6(int x) { return is_uintn(x, 6); } -static inline bool is_uint8(int x) { return is_uintn(x, 8); } -static inline bool is_uint10(int x) { return is_uintn(x, 10); } -static inline bool is_uint12(int x) { return is_uintn(x, 12); } -static inline bool is_uint16(int x) { return is_uintn(x, 16); } -static inline bool is_uint24(int x) { return is_uintn(x, 24); } -static inline bool is_uint26(int x) { return is_uintn(x, 26); } -static inline bool is_uint28(int x) { return is_uintn(x, 28); } - -static inline int NumberOfBitsSet(uint32_t x) { +inline bool is_uint2(int x) { return is_uintn(x, 2); } +inline bool is_uint3(int x) { return is_uintn(x, 3); } +inline bool is_uint4(int x) { return is_uintn(x, 4); } +inline bool is_uint5(int x) { return is_uintn(x, 5); } +inline bool is_uint6(int x) { return is_uintn(x, 6); } +inline bool is_uint8(int x) { return is_uintn(x, 8); } +inline bool is_uint10(int x) { return is_uintn(x, 10); } +inline bool is_uint12(int x) { return is_uintn(x, 12); } +inline bool is_uint16(int x) { return is_uintn(x, 16); } +inline bool is_uint24(int x) { return is_uintn(x, 24); } +inline bool is_uint26(int x) { return is_uintn(x, 26); } +inline bool is_uint28(int x) { return is_uintn(x, 28); } + +inline int NumberOfBitsSet(uint32_t x) { unsigned int num_bits_set; for (num_bits_set = 0; x; x >>= 1) { num_bits_set += x & 1; @@ -833,6 +858,8 @@ static inline int NumberOfBitsSet(uint32_t x) { return num_bits_set; } +bool EvalComparison(Token::Value op, double op1, double op2); + // Computes pow(x, y) with the special cases in the spec for Math.pow. double power_double_int(double x, int y); double power_double_double(double x, double y); diff --git a/deps/v8/src/ast-inl.h b/deps/v8/src/ast-inl.h deleted file mode 100644 index 731ad2ff3f..0000000000 --- a/deps/v8/src/ast-inl.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_AST_INL_H_ -#define V8_AST_INL_H_ - -#include "v8.h" - -#include "ast.h" -#include "scopes.h" - -namespace v8 { -namespace internal { - - -SwitchStatement::SwitchStatement(Isolate* isolate, - ZoneStringList* labels) - : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS), - tag_(NULL), cases_(NULL) { -} - - -Block::Block(Isolate* isolate, - ZoneStringList* labels, - int capacity, - bool is_initializer_block) - : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY), - statements_(capacity), - is_initializer_block_(is_initializer_block), - block_scope_(NULL) { -} - - -BreakableStatement::BreakableStatement(Isolate* isolate, - ZoneStringList* labels, - Type type) - : labels_(labels), - type_(type), - entry_id_(GetNextId(isolate)), - exit_id_(GetNextId(isolate)) { - ASSERT(labels == NULL || labels->length() > 0); -} - - -IterationStatement::IterationStatement(Isolate* isolate, ZoneStringList* labels) - : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS), - body_(NULL), - continue_target_(), - osr_entry_id_(GetNextId(isolate)) { -} - - -DoWhileStatement::DoWhileStatement(Isolate* isolate, ZoneStringList* labels) - : IterationStatement(isolate, labels), - cond_(NULL), - condition_position_(-1), - continue_id_(GetNextId(isolate)), - back_edge_id_(GetNextId(isolate)) { -} - - -WhileStatement::WhileStatement(Isolate* isolate, ZoneStringList* labels) - : IterationStatement(isolate, labels), - cond_(NULL), - may_have_function_literal_(true), - body_id_(GetNextId(isolate)) { -} - - -ForStatement::ForStatement(Isolate* isolate, ZoneStringList* labels) - : IterationStatement(isolate, labels), - init_(NULL), - cond_(NULL), - next_(NULL), - may_have_function_literal_(true), - loop_variable_(NULL), - continue_id_(GetNextId(isolate)), - body_id_(GetNextId(isolate)) { -} - - -ForInStatement::ForInStatement(Isolate* isolate, ZoneStringList* labels) - : IterationStatement(isolate, labels), - each_(NULL), - enumerable_(NULL), - assignment_id_(GetNextId(isolate)) { -} - - -bool FunctionLiteral::strict_mode() const { - return scope()->is_strict_mode(); -} - - -} } // namespace v8::internal - -#endif // V8_AST_INL_H_ diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index 418cc432b6..980dba6371 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,10 +25,15 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" - #include "ast.h" + +#include <math.h> // For isfinite. +#include "builtins.h" +#include "conversions.h" +#include "hashmap.h" #include "parser.h" +#include "property-details.h" +#include "property.h" #include "scopes.h" #include "string-stream.h" #include "type-info.h" @@ -48,16 +53,19 @@ AST_NODE_LIST(DECL_ACCEPT) // ---------------------------------------------------------------------------- // Implementation of other node functionality. -Assignment* ExpressionStatement::StatementAsSimpleAssignment() { - return (expression()->AsAssignment() != NULL && - !expression()->AsAssignment()->is_compound()) - ? expression()->AsAssignment() - : NULL; + +bool Expression::IsSmiLiteral() { + return AsLiteral() != NULL && AsLiteral()->handle()->IsSmi(); +} + + +bool Expression::IsStringLiteral() { + return AsLiteral() != NULL && AsLiteral()->handle()->IsString(); } -CountOperation* ExpressionStatement::StatementAsCountOperation() { - return expression()->AsCountOperation(); +bool Expression::IsNullLiteral() { + return AsLiteral() != NULL && AsLiteral()->handle()->IsNull(); } @@ -66,8 +74,8 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var) name_(var->name()), var_(NULL), // Will be set by the call to BindTo. is_this_(var->is_this()), - inside_with_(false), is_trivial_(false), + is_lvalue_(false), position_(RelocInfo::kNoPosition) { BindTo(var); } @@ -76,14 +84,13 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var) VariableProxy::VariableProxy(Isolate* isolate, Handle<String> name, bool is_this, - bool inside_with, int position) : Expression(isolate), name_(name), var_(NULL), is_this_(is_this), - inside_with_(inside_with), is_trivial_(false), + is_lvalue_(false), position_(position) { // Names must be canonicalized for fast equality checks. ASSERT(name->IsSymbol()); @@ -119,18 +126,7 @@ Assignment::Assignment(Isolate* isolate, assignment_id_(GetNextId(isolate)), block_start_(false), block_end_(false), - is_monomorphic_(false) { - ASSERT(Token::IsAssignmentOp(op)); - if (is_compound()) { - binary_operation_ = - new(isolate->zone()) BinaryOperation(isolate, - binary_op(), - target, - value, - pos + 1); - compound_load_id_ = GetNextId(isolate); - } -} + is_monomorphic_(false) { } Token::Value Assignment::binary_op() const { @@ -157,6 +153,21 @@ bool FunctionLiteral::AllowsLazyCompilation() { } +int FunctionLiteral::start_position() const { + return scope()->start_position(); +} + + +int FunctionLiteral::end_position() const { + return scope()->end_position(); +} + + +LanguageMode FunctionLiteral::language_mode() const { + return scope()->language_mode(); +} + + ObjectLiteral::Property::Property(Literal* key, Expression* value) { emit_store_ = true; key_ = key; @@ -175,9 +186,7 @@ ObjectLiteral::Property::Property(Literal* key, Expression* value) { ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) { - Isolate* isolate = Isolate::Current(); emit_store_ = true; - key_ = new(isolate->zone()) Literal(isolate, value->name()); value_ = value; kind_ = is_getter ? GETTER : SETTER; } @@ -327,284 +336,89 @@ bool BinaryOperation::ResultOverwriteAllowed() { } -bool CompareOperation::IsLiteralCompareTypeof(Expression** expr, - Handle<String>* check) { - if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return false; - - UnaryOperation* left_unary = left_->AsUnaryOperation(); - UnaryOperation* right_unary = right_->AsUnaryOperation(); - Literal* left_literal = left_->AsLiteral(); - Literal* right_literal = right_->AsLiteral(); - - // Check for the pattern: typeof <expression> == <string literal>. - if (left_unary != NULL && left_unary->op() == Token::TYPEOF && - right_literal != NULL && right_literal->handle()->IsString()) { - *expr = left_unary->expression(); - *check = Handle<String>::cast(right_literal->handle()); - return true; - } - - // Check for the pattern: <string literal> == typeof <expression>. - if (right_unary != NULL && right_unary->op() == Token::TYPEOF && - left_literal != NULL && left_literal->handle()->IsString()) { - *expr = right_unary->expression(); - *check = Handle<String>::cast(left_literal->handle()); - return true; - } - - return false; +static bool IsTypeof(Expression* expr) { + UnaryOperation* maybe_unary = expr->AsUnaryOperation(); + return maybe_unary != NULL && maybe_unary->op() == Token::TYPEOF; } -bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) { - if (op_ != Token::EQ_STRICT) return false; - - UnaryOperation* left_unary = left_->AsUnaryOperation(); - UnaryOperation* right_unary = right_->AsUnaryOperation(); - - // Check for the pattern: <expression> === void <literal>. - if (right_unary != NULL && right_unary->op() == Token::VOID && - right_unary->expression()->AsLiteral() != NULL) { - *expr = left_; +// Check for the pattern: typeof <expression> equals <string literal>. +static bool MatchLiteralCompareTypeof(Expression* left, + Token::Value op, + Expression* right, + Expression** expr, + Handle<String>* check) { + if (IsTypeof(left) && right->IsStringLiteral() && Token::IsEqualityOp(op)) { + *expr = left->AsUnaryOperation()->expression(); + *check = Handle<String>::cast(right->AsLiteral()->handle()); return true; } - - // Check for the pattern: void <literal> === <expression>. - if (left_unary != NULL && left_unary->op() == Token::VOID && - left_unary->expression()->AsLiteral() != NULL) { - *expr = right_; - return true; - } - return false; } -// ---------------------------------------------------------------------------- -// Inlining support - -bool Declaration::IsInlineable() const { - return proxy()->var()->IsStackAllocated() && fun() == NULL; -} - - -bool TargetCollector::IsInlineable() const { - UNREACHABLE(); - return false; -} - - -bool ForInStatement::IsInlineable() const { - return false; -} - - -bool WithStatement::IsInlineable() const { - return false; -} - - -bool SwitchStatement::IsInlineable() const { - return false; -} - - -bool TryStatement::IsInlineable() const { - return false; -} - - -bool TryCatchStatement::IsInlineable() const { - return false; -} - - -bool TryFinallyStatement::IsInlineable() const { - return false; -} - - -bool DebuggerStatement::IsInlineable() const { - return false; -} - - -bool Throw::IsInlineable() const { - return exception()->IsInlineable(); -} - - -bool MaterializedLiteral::IsInlineable() const { - // TODO(1322): Allow materialized literals. - return false; -} - - -bool FunctionLiteral::IsInlineable() const { - // TODO(1322): Allow materialized literals. - return false; -} - - -bool ThisFunction::IsInlineable() const { - return false; -} - - -bool SharedFunctionInfoLiteral::IsInlineable() const { - return false; -} - - -bool ForStatement::IsInlineable() const { - return (init() == NULL || init()->IsInlineable()) - && (cond() == NULL || cond()->IsInlineable()) - && (next() == NULL || next()->IsInlineable()) - && body()->IsInlineable(); -} - - -bool WhileStatement::IsInlineable() const { - return cond()->IsInlineable() - && body()->IsInlineable(); -} - - -bool DoWhileStatement::IsInlineable() const { - return cond()->IsInlineable() - && body()->IsInlineable(); -} - - -bool ContinueStatement::IsInlineable() const { - return true; -} - - -bool BreakStatement::IsInlineable() const { - return true; -} - - -bool EmptyStatement::IsInlineable() const { - return true; -} - - -bool Literal::IsInlineable() const { - return true; -} - - -bool Block::IsInlineable() const { - const int count = statements_.length(); - for (int i = 0; i < count; ++i) { - if (!statements_[i]->IsInlineable()) return false; - } - return true; -} - - -bool ExpressionStatement::IsInlineable() const { - return expression()->IsInlineable(); -} - - -bool IfStatement::IsInlineable() const { - return condition()->IsInlineable() - && then_statement()->IsInlineable() - && else_statement()->IsInlineable(); -} - - -bool ReturnStatement::IsInlineable() const { - return expression()->IsInlineable(); -} - - -bool Conditional::IsInlineable() const { - return condition()->IsInlineable() && then_expression()->IsInlineable() && - else_expression()->IsInlineable(); -} - - -bool VariableProxy::IsInlineable() const { - return var()->IsUnallocated() || var()->IsStackAllocated(); -} - - -bool Assignment::IsInlineable() const { - return target()->IsInlineable() && value()->IsInlineable(); +bool CompareOperation::IsLiteralCompareTypeof(Expression** expr, + Handle<String>* check) { + return MatchLiteralCompareTypeof(left_, op_, right_, expr, check) || + MatchLiteralCompareTypeof(right_, op_, left_, expr, check); } -bool Property::IsInlineable() const { - return obj()->IsInlineable() && key()->IsInlineable(); +static bool IsVoidOfLiteral(Expression* expr) { + UnaryOperation* maybe_unary = expr->AsUnaryOperation(); + return maybe_unary != NULL && + maybe_unary->op() == Token::VOID && + maybe_unary->expression()->AsLiteral() != NULL; } -bool Call::IsInlineable() const { - if (!expression()->IsInlineable()) return false; - const int count = arguments()->length(); - for (int i = 0; i < count; ++i) { - if (!arguments()->at(i)->IsInlineable()) return false; +// Check for the pattern: void <literal> equals <expression> +static bool MatchLiteralCompareUndefined(Expression* left, + Token::Value op, + Expression* right, + Expression** expr) { + if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) { + *expr = right; + return true; } - return true; + return false; } -bool CallNew::IsInlineable() const { - if (!expression()->IsInlineable()) return false; - const int count = arguments()->length(); - for (int i = 0; i < count; ++i) { - if (!arguments()->at(i)->IsInlineable()) return false; - } - return true; +bool CompareOperation::IsLiteralCompareUndefined(Expression** expr) { + return MatchLiteralCompareUndefined(left_, op_, right_, expr) || + MatchLiteralCompareUndefined(right_, op_, left_, expr); } -bool CallRuntime::IsInlineable() const { - // Don't try to inline JS runtime calls because we don't (currently) even - // optimize them. - if (is_jsruntime()) return false; - // Don't inline the %_ArgumentsLength or %_Arguments because their - // implementation will not work. There is no stack frame to get them - // from. - if (function()->intrinsic_type == Runtime::INLINE && - (name()->IsEqualTo(CStrVector("_ArgumentsLength")) || - name()->IsEqualTo(CStrVector("_Arguments")))) { - return false; - } - const int count = arguments()->length(); - for (int i = 0; i < count; ++i) { - if (!arguments()->at(i)->IsInlineable()) return false; +// Check for the pattern: null equals <expression> +static bool MatchLiteralCompareNull(Expression* left, + Token::Value op, + Expression* right, + Expression** expr) { + if (left->IsNullLiteral() && Token::IsEqualityOp(op)) { + *expr = right; + return true; } - return true; -} - - -bool UnaryOperation::IsInlineable() const { - return expression()->IsInlineable(); + return false; } -bool BinaryOperation::IsInlineable() const { - return left()->IsInlineable() && right()->IsInlineable(); +bool CompareOperation::IsLiteralCompareNull(Expression** expr) { + return MatchLiteralCompareNull(left_, op_, right_, expr) || + MatchLiteralCompareNull(right_, op_, left_, expr); } -bool CompareOperation::IsInlineable() const { - return left()->IsInlineable() && right()->IsInlineable(); -} - +// ---------------------------------------------------------------------------- +// Inlining support -bool CompareToNull::IsInlineable() const { - return expression()->IsInlineable(); +bool Declaration::IsInlineable() const { + return proxy()->var()->IsStackAllocated(); } - -bool CountOperation::IsInlineable() const { - return expression()->IsInlineable(); +bool VariableDeclaration::IsInlineable() const { + return Declaration::IsInlineable() && fun() == NULL; } @@ -677,6 +491,10 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) { TypeInfo info = oracle->SwitchType(this); if (info.IsSmi()) { compare_type_ = SMI_ONLY; + } else if (info.IsSymbol()) { + compare_type_ = SYMBOL_ONLY; + } else if (info.IsNonSymbol()) { + compare_type_ = STRING_ONLY; } else if (info.IsNonPrimitive()) { compare_type_ = OBJECT_ONLY; } else { @@ -685,39 +503,33 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) { } -static bool CanCallWithoutIC(Handle<JSFunction> target, int arity) { - SharedFunctionInfo* info = target->shared(); - // If the number of formal parameters of the target function does - // not match the number of arguments we're passing, we don't want to - // deal with it. Otherwise, we can call it directly. - return !target->NeedsArgumentsAdaption() || - info->formal_parameter_count() == arity; -} - - bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) { + // If there is an interceptor, we can't compute the target for a direct call. + if (type->has_named_interceptor()) return false; + if (check_type_ == RECEIVER_MAP_CHECK) { - // For primitive checks the holder is set up to point to the - // corresponding prototype object, i.e. one step of the algorithm - // below has been already performed. - // For non-primitive checks we clear it to allow computing targets - // for polymorphic calls. + // For primitive checks the holder is set up to point to the corresponding + // prototype object, i.e. one step of the algorithm below has been already + // performed. For non-primitive checks we clear it to allow computing + // targets for polymorphic calls. holder_ = Handle<JSObject>::null(); } + LookupResult lookup(type->GetIsolate()); while (true) { - LookupResult lookup; type->LookupInDescriptors(NULL, *name, &lookup); - // If the function wasn't found directly in the map, we start - // looking upwards through the prototype chain. - if (!lookup.IsFound() && type->prototype()->IsJSObject()) { - holder_ = Handle<JSObject>(JSObject::cast(type->prototype())); - type = Handle<Map>(holder()->map()); - } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) { - target_ = Handle<JSFunction>(lookup.GetConstantFunctionFromMap(*type)); - return CanCallWithoutIC(target_, arguments()->length()); - } else { + // For properties we know the target iff we have a constant function. + if (lookup.IsFound() && lookup.IsProperty()) { + if (lookup.type() == CONSTANT_FUNCTION) { + target_ = Handle<JSFunction>(lookup.GetConstantFunctionFromMap(*type)); + return true; + } return false; } + // If we reach the end of the prototype chain, we don't know the target. + if (!type->prototype()->IsJSObject()) return false; + // Go up the prototype chain, recording where we are currently. + holder_ = Handle<JSObject>(JSObject::cast(type->prototype())); + type = Handle<Map>(holder()->map()); } } @@ -726,7 +538,7 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global, LookupResult* lookup) { target_ = Handle<JSFunction>::null(); cell_ = Handle<JSGlobalPropertyCell>::null(); - ASSERT(lookup->IsProperty() && + ASSERT(lookup->IsFound() && lookup->type() == NORMAL && lookup->holder() == *global); cell_ = Handle<JSGlobalPropertyCell>(global->GetPropertyCell(lookup)); @@ -734,8 +546,7 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global, Handle<JSFunction> candidate(JSFunction::cast(cell_->value())); // If the function is in new space we assume it's more likely to // change and thus prefer the general IC code. - if (!HEAP->InNewSpace(*candidate) && - CanCallWithoutIC(candidate, arguments()->length())) { + if (!HEAP->InNewSpace(*candidate)) { target_ = candidate; return true; } @@ -746,37 +557,41 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global, void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle, CallKind call_kind) { + is_monomorphic_ = oracle->CallIsMonomorphic(this); Property* property = expression()->AsProperty(); - ASSERT(property != NULL); - // Specialize for the receiver types seen at runtime. - Literal* key = property->key()->AsLiteral(); - ASSERT(key != NULL && key->handle()->IsString()); - Handle<String> name = Handle<String>::cast(key->handle()); - receiver_types_.Clear(); - oracle->CallReceiverTypes(this, name, call_kind, &receiver_types_); + if (property == NULL) { + // Function call. Specialize for monomorphic calls. + if (is_monomorphic_) target_ = oracle->GetCallTarget(this); + } else { + // Method call. Specialize for the receiver types seen at runtime. + Literal* key = property->key()->AsLiteral(); + ASSERT(key != NULL && key->handle()->IsString()); + Handle<String> name = Handle<String>::cast(key->handle()); + receiver_types_.Clear(); + oracle->CallReceiverTypes(this, name, call_kind, &receiver_types_); #ifdef DEBUG - if (FLAG_enable_slow_asserts) { - int length = receiver_types_.length(); - for (int i = 0; i < length; i++) { - Handle<Map> map = receiver_types_.at(i); - ASSERT(!map.is_null() && *map != NULL); + if (FLAG_enable_slow_asserts) { + int length = receiver_types_.length(); + for (int i = 0; i < length; i++) { + Handle<Map> map = receiver_types_.at(i); + ASSERT(!map.is_null() && *map != NULL); + } } - } #endif - is_monomorphic_ = oracle->CallIsMonomorphic(this); - check_type_ = oracle->GetCallCheckType(this); - if (is_monomorphic_) { - Handle<Map> map; - if (receiver_types_.length() > 0) { - ASSERT(check_type_ == RECEIVER_MAP_CHECK); - map = receiver_types_.at(0); - } else { - ASSERT(check_type_ != RECEIVER_MAP_CHECK); - holder_ = Handle<JSObject>( - oracle->GetPrototypeForPrimitiveCheck(check_type_)); - map = Handle<Map>(holder_->map()); + check_type_ = oracle->GetCallCheckType(this); + if (is_monomorphic_) { + Handle<Map> map; + if (receiver_types_.length() > 0) { + ASSERT(check_type_ == RECEIVER_MAP_CHECK); + map = receiver_types_.at(0); + } else { + ASSERT(check_type_ != RECEIVER_MAP_CHECK); + holder_ = Handle<JSObject>( + oracle->GetPrototypeForPrimitiveCheck(check_type_)); + map = Handle<Map>(holder_->map()); + } + is_monomorphic_ = ComputeTarget(map, name); } - is_monomorphic_ = ComputeTarget(map, name); } } @@ -856,8 +671,6 @@ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE) #undef MAKE_TYPE_CASE -RegExpEmpty RegExpEmpty::kInstance; - static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) { Interval result = Interval::Empty(); @@ -1175,4 +988,169 @@ CaseClause::CaseClause(Isolate* isolate, entry_id_(AstNode::GetNextId(isolate)) { } + +#define INCREASE_NODE_COUNT(NodeType) \ + void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ + increase_node_count(); \ + } + +INCREASE_NODE_COUNT(VariableDeclaration) +INCREASE_NODE_COUNT(ModuleDeclaration) +INCREASE_NODE_COUNT(ModuleLiteral) +INCREASE_NODE_COUNT(ModuleVariable) +INCREASE_NODE_COUNT(ModulePath) +INCREASE_NODE_COUNT(ModuleUrl) +INCREASE_NODE_COUNT(Block) +INCREASE_NODE_COUNT(ExpressionStatement) +INCREASE_NODE_COUNT(EmptyStatement) +INCREASE_NODE_COUNT(IfStatement) +INCREASE_NODE_COUNT(ContinueStatement) +INCREASE_NODE_COUNT(BreakStatement) +INCREASE_NODE_COUNT(ReturnStatement) +INCREASE_NODE_COUNT(Conditional) +INCREASE_NODE_COUNT(Literal) +INCREASE_NODE_COUNT(Assignment) +INCREASE_NODE_COUNT(Throw) +INCREASE_NODE_COUNT(Property) +INCREASE_NODE_COUNT(UnaryOperation) +INCREASE_NODE_COUNT(CountOperation) +INCREASE_NODE_COUNT(BinaryOperation) +INCREASE_NODE_COUNT(CompareOperation) +INCREASE_NODE_COUNT(ThisFunction) + +#undef INCREASE_NODE_COUNT + + +void AstConstructionVisitor::VisitWithStatement(WithStatement* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitSwitchStatement(SwitchStatement* node) { + increase_node_count(); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitDoWhileStatement(DoWhileStatement* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitWhileStatement(WhileStatement* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitForStatement(ForStatement* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitForInStatement(ForInStatement* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitTryCatchStatement(TryCatchStatement* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitTryFinallyStatement( + TryFinallyStatement* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitDebuggerStatement(DebuggerStatement* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitFunctionLiteral(FunctionLiteral* node) { + increase_node_count(); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitSharedFunctionInfoLiteral( + SharedFunctionInfoLiteral* node) { + increase_node_count(); + add_flag(kDontOptimize); + add_flag(kDontInline); +} + + +void AstConstructionVisitor::VisitVariableProxy(VariableProxy* node) { + increase_node_count(); + // In theory, we'd have to add: + // if(node->var()->IsLookupSlot()) { add_flag(kDontInline); } + // However, node->var() is usually not bound yet at VariableProxy creation + // time, and LOOKUP variables only result from constructs that cannot + // be inlined anyway. +} + + +void AstConstructionVisitor::VisitRegExpLiteral(RegExpLiteral* node) { + increase_node_count(); + add_flag(kDontInline); // TODO(1322): Allow materialized literals. +} + + +void AstConstructionVisitor::VisitObjectLiteral(ObjectLiteral* node) { + increase_node_count(); + add_flag(kDontInline); // TODO(1322): Allow materialized literals. +} + + +void AstConstructionVisitor::VisitArrayLiteral(ArrayLiteral* node) { + increase_node_count(); + add_flag(kDontInline); // TODO(1322): Allow materialized literals. +} + + +void AstConstructionVisitor::VisitCall(Call* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitCallNew(CallNew* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); +} + + +void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) { + increase_node_count(); + add_flag(kDontSelfOptimize); + if (node->is_jsruntime()) { + // Don't try to inline JS runtime calls because we don't (currently) even + // optimize them. + add_flag(kDontInline); + } else if (node->function()->intrinsic_type == Runtime::INLINE && + (node->name()->IsEqualTo(CStrVector("_ArgumentsLength")) || + node->name()->IsEqualTo(CStrVector("_Arguments")))) { + // Don't inline the %_ArgumentsLength or %_Arguments because their + // implementation will not work. There is no stack frame to get them + // from. + add_flag(kDontInline); + } +} + } } // namespace v8::internal diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index b56205f9a6..7f812326fa 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,14 +28,20 @@ #ifndef V8_AST_H_ #define V8_AST_H_ -#include "allocation.h" -#include "execution.h" +#include "v8.h" + +#include "assembler.h" #include "factory.h" +#include "isolate.h" #include "jsregexp.h" +#include "list-inl.h" #include "runtime.h" #include "small-pointer-list.h" +#include "smart-array-pointer.h" #include "token.h" +#include "utils.h" #include "variables.h" +#include "zone-inl.h" namespace v8 { namespace internal { @@ -53,6 +59,16 @@ namespace internal { // Nodes of the abstract syntax tree. Only concrete classes are // enumerated here. +#define DECLARATION_NODE_LIST(V) \ + V(VariableDeclaration) \ + V(ModuleDeclaration) \ + +#define MODULE_NODE_LIST(V) \ + V(ModuleLiteral) \ + V(ModuleVariable) \ + V(ModulePath) \ + V(ModuleUrl) + #define STATEMENT_NODE_LIST(V) \ V(Block) \ V(ExpressionStatement) \ @@ -90,21 +106,41 @@ namespace internal { V(CountOperation) \ V(BinaryOperation) \ V(CompareOperation) \ - V(CompareToNull) \ V(ThisFunction) #define AST_NODE_LIST(V) \ - V(Declaration) \ + DECLARATION_NODE_LIST(V) \ + MODULE_NODE_LIST(V) \ STATEMENT_NODE_LIST(V) \ EXPRESSION_NODE_LIST(V) // Forward declarations -class BitVector; -class DefinitionInfo; +class AstConstructionVisitor; +template<class> class AstNodeFactory; +class AstVisitor; +class Declaration; +class Module; +class BreakableStatement; +class Expression; +class IterationStatement; class MaterializedLiteral; +class Statement; class TargetCollector; class TypeFeedbackOracle; +class RegExpAlternative; +class RegExpAssertion; +class RegExpAtom; +class RegExpBackReference; +class RegExpCapture; +class RegExpCharacterClass; +class RegExpCompiler; +class RegExpDisjunction; +class RegExpEmpty; +class RegExpLookahead; +class RegExpQuantifier; +class RegExpText; + #define DEF_FORWARD_DECLARATION(type) class type; AST_NODE_LIST(DEF_FORWARD_DECLARATION) #undef DEF_FORWARD_DECLARATION @@ -119,7 +155,30 @@ typedef ZoneList<Handle<Object> > ZoneObjectList; #define DECLARE_NODE_TYPE(type) \ virtual void Accept(AstVisitor* v); \ virtual AstNode::Type node_type() const { return AstNode::k##type; } \ - virtual type* As##type() { return this; } + + +enum AstPropertiesFlag { + kDontInline, + kDontOptimize, + kDontSelfOptimize, + kDontSoftInline +}; + + +class AstProperties BASE_EMBEDDED { + public: + class Flags : public EnumSet<AstPropertiesFlag, int> {}; + + AstProperties() : node_count_(0) { } + + Flags* flags() { return &flags_; } + int node_count() { return node_count_; } + void add_node_count(int count) { node_count_ += count; } + + private: + Flags flags_; + int node_count_; +}; class AstNode: public ZoneObject { @@ -138,14 +197,11 @@ class AstNode: public ZoneObject { // that emit code (function declarations). static const int kDeclarationsId = 3; - // Override ZoneObject's new to count allocated AST nodes. void* operator new(size_t size, Zone* zone) { - Isolate* isolate = zone->isolate(); - isolate->set_ast_node_count(isolate->ast_node_count() + 1); return zone->New(static_cast<int>(size)); } - AstNode() {} + AstNode() { } virtual ~AstNode() { } @@ -154,10 +210,12 @@ class AstNode: public ZoneObject { // Type testing & conversion functions overridden by concrete subclasses. #define DECLARE_NODE_FUNCTIONS(type) \ - virtual type* As##type() { return NULL; } + bool Is##type() { return node_type() == AstNode::k##type; } \ + type* As##type() { return Is##type() ? reinterpret_cast<type*>(this) : NULL; } AST_NODE_LIST(DECLARE_NODE_FUNCTIONS) #undef DECLARE_NODE_FUNCTIONS + virtual Declaration* AsDeclaration() { return NULL; } virtual Statement* AsStatement() { return NULL; } virtual Expression* AsExpression() { return NULL; } virtual TargetCollector* AsTargetCollector() { return NULL; } @@ -165,19 +223,15 @@ class AstNode: public ZoneObject { virtual IterationStatement* AsIterationStatement() { return NULL; } virtual MaterializedLiteral* AsMaterializedLiteral() { return NULL; } - // True if the node is simple enough for us to inline calls containing it. - virtual bool IsInlineable() const = 0; - - static int Count() { return Isolate::Current()->ast_node_count(); } static void ResetIds() { Isolate::Current()->set_ast_node_id(0); } protected: - static unsigned GetNextId(Isolate* isolate) { + static int GetNextId(Isolate* isolate) { return ReserveIdRange(isolate, 1); } - static unsigned ReserveIdRange(Isolate* isolate, int n) { - unsigned tmp = isolate->ast_node_id(); + static int ReserveIdRange(Isolate* isolate, int n) { + int tmp = isolate->ast_node_id(); isolate->set_ast_node_id(tmp + n); return tmp; } @@ -191,15 +245,17 @@ class AstNode: public ZoneObject { }; +#define DECLARE_NODE_TYPE(type) \ + virtual void Accept(AstVisitor* v); \ + virtual AstNode::Type node_type() const { return AstNode::k##type; } \ + + class Statement: public AstNode { public: Statement() : statement_pos_(RelocInfo::kNoPosition) {} virtual Statement* AsStatement() { return this; } - virtual Assignment* StatementAsSimpleAssignment() { return NULL; } - virtual CountOperation* StatementAsCountOperation() { return NULL; } - bool IsEmpty() { return AsEmptyStatement() != NULL; } void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; } @@ -254,10 +310,6 @@ class Expression: public AstNode { kTest }; - explicit Expression(Isolate* isolate) - : id_(GetNextId(isolate)), - test_id_(GetNextId(isolate)) {} - virtual int position() const { UNREACHABLE(); return 0; @@ -265,7 +317,6 @@ class Expression: public AstNode { virtual Expression* AsExpression() { return this; } - virtual bool IsTrivial() { return false; } virtual bool IsValidLeftHandSide() { return false; } // Helpers for ToBoolean conversion. @@ -277,27 +328,24 @@ class Expression: public AstNode { // names because [] for string objects is handled only by keyed ICs. virtual bool IsPropertyName() { return false; } - // Mark the expression as being compiled as an expression - // statement. This is used to transform postfix increments to - // (faster) prefix increments. - virtual void MarkAsStatement() { /* do nothing */ } - // True iff the result can be safely overwritten (to avoid allocation). // False for operations that can return one of their operands. virtual bool ResultOverwriteAllowed() { return false; } // True iff the expression is a literal represented as a smi. - virtual bool IsSmiLiteral() { return false; } + bool IsSmiLiteral(); + + // True iff the expression is a string literal. + bool IsStringLiteral(); + + // True iff the expression is the null literal. + bool IsNullLiteral(); // Type feedback information for assignments and properties. virtual bool IsMonomorphic() { UNREACHABLE(); return false; } - virtual bool IsArrayLength() { - UNREACHABLE(); - return false; - } virtual SmallMapList* GetReceiverTypes() { UNREACHABLE(); return NULL; @@ -312,9 +360,14 @@ class Expression: public AstNode { unsigned id() const { return id_; } unsigned test_id() const { return test_id_; } + protected: + explicit Expression(Isolate* isolate) + : id_(GetNextId(isolate)), + test_id_(GetNextId(isolate)) {} + private: - unsigned id_; - unsigned test_id_; + int id_; + int test_id_; }; @@ -343,7 +396,14 @@ class BreakableStatement: public Statement { int ExitId() const { return exit_id_; } protected: - BreakableStatement(Isolate* isolate, ZoneStringList* labels, Type type); + BreakableStatement(Isolate* isolate, ZoneStringList* labels, Type type) + : labels_(labels), + type_(type), + entry_id_(GetNextId(isolate)), + exit_id_(GetNextId(isolate)) { + ASSERT(labels == NULL || labels->length() > 0); + } + private: ZoneStringList* labels_; @@ -356,25 +416,8 @@ class BreakableStatement: public Statement { class Block: public BreakableStatement { public: - inline Block(Isolate* isolate, - ZoneStringList* labels, - int capacity, - bool is_initializer_block); - DECLARE_NODE_TYPE(Block) - virtual Assignment* StatementAsSimpleAssignment() { - if (statements_.length() != 1) return NULL; - return statements_[0]->StatementAsSimpleAssignment(); - } - - virtual CountOperation* StatementAsCountOperation() { - if (statements_.length() != 1) return NULL; - return statements_[0]->StatementAsCountOperation(); - } - - virtual bool IsInlineable() const; - void AddStatement(Statement* statement) { statements_.Add(statement); } ZoneList<Statement*>* statements() { return &statements_; } @@ -383,6 +426,19 @@ class Block: public BreakableStatement { Scope* block_scope() const { return block_scope_; } void set_block_scope(Scope* block_scope) { block_scope_ = block_scope; } + protected: + template<class> friend class AstNodeFactory; + + Block(Isolate* isolate, + ZoneStringList* labels, + int capacity, + bool is_initializer_block) + : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY), + statements_(capacity), + is_initializer_block_(is_initializer_block), + block_scope_(NULL) { + } + private: ZoneList<Statement*> statements_; bool is_initializer_block_; @@ -392,36 +448,162 @@ class Block: public BreakableStatement { class Declaration: public AstNode { public: + VariableProxy* proxy() const { return proxy_; } + VariableMode mode() const { return mode_; } + Scope* scope() const { return scope_; } + virtual bool IsInlineable() const; + + virtual Declaration* AsDeclaration() { return this; } + virtual VariableDeclaration* AsVariableDeclaration() { return NULL; } + + protected: Declaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* fun, + VariableMode mode, Scope* scope) : proxy_(proxy), mode_(mode), - fun_(fun), scope_(scope) { - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - // At the moment there are no "const functions"'s in JavaScript... - ASSERT(fun == NULL || mode == Variable::VAR || mode == Variable::LET); + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); } - DECLARE_NODE_TYPE(Declaration) + private: + VariableProxy* proxy_; + VariableMode mode_; + + // Nested scope from which the declaration originated. + Scope* scope_; +}; + + +class VariableDeclaration: public Declaration { + public: + DECLARE_NODE_TYPE(VariableDeclaration) + + virtual VariableDeclaration* AsVariableDeclaration() { return this; } - VariableProxy* proxy() const { return proxy_; } - Variable::Mode mode() const { return mode_; } FunctionLiteral* fun() const { return fun_; } // may be NULL virtual bool IsInlineable() const; - Scope* scope() const { return scope_; } + + protected: + template<class> friend class AstNodeFactory; + + VariableDeclaration(VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* fun, + Scope* scope) + : Declaration(proxy, mode, scope), + fun_(fun) { + // At the moment there are no "const functions"'s in JavaScript... + ASSERT(fun == NULL || mode == VAR || mode == LET); + } private: - VariableProxy* proxy_; - Variable::Mode mode_; FunctionLiteral* fun_; +}; - // Nested scope from which the declaration originated. - Scope* scope_; + +class ModuleDeclaration: public Declaration { + public: + DECLARE_NODE_TYPE(ModuleDeclaration) + + Module* module() const { return module_; } + + protected: + template<class> friend class AstNodeFactory; + + ModuleDeclaration(VariableProxy* proxy, + Module* module, + Scope* scope) + : Declaration(proxy, LET, scope), + module_(module) { + } + + private: + Module* module_; +}; + + +class Module: public AstNode { + // TODO(rossberg): stuff to come... + protected: + Module() {} +}; + + +class ModuleLiteral: public Module { + public: + DECLARE_NODE_TYPE(ModuleLiteral) + + Block* body() const { return body_; } + + protected: + template<class> friend class AstNodeFactory; + + explicit ModuleLiteral(Block* body) + : body_(body) { + } + + private: + Block* body_; +}; + + +class ModuleVariable: public Module { + public: + DECLARE_NODE_TYPE(ModuleVariable) + + Variable* var() const { return var_; } + + protected: + template<class> friend class AstNodeFactory; + + explicit ModuleVariable(Variable* var) + : var_(var) { + } + + private: + Variable* var_; +}; + + +class ModulePath: public Module { + public: + DECLARE_NODE_TYPE(ModulePath) + + Module* module() const { return module_; } + Handle<String> name() const { return name_; } + + protected: + template<class> friend class AstNodeFactory; + + ModulePath(Module* module, Handle<String> name) + : module_(module), + name_(name) { + } + + private: + Module* module_; + Handle<String> name_; +}; + + +class ModuleUrl: public Module { + public: + DECLARE_NODE_TYPE(ModuleUrl) + + Handle<String> url() const { return url_; } + + protected: + template<class> friend class AstNodeFactory; + + explicit ModuleUrl(Handle<String> url) : url_(url) { + } + + private: + Handle<String> url_; }; @@ -441,7 +623,11 @@ class IterationStatement: public BreakableStatement { Label* continue_target() { return &continue_target_; } protected: - inline IterationStatement(Isolate* isolate, ZoneStringList* labels); + IterationStatement(Isolate* isolate, ZoneStringList* labels) + : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS), + body_(NULL), + osr_entry_id_(GetNextId(isolate)) { + } void Initialize(Statement* body) { body_ = body; @@ -456,8 +642,6 @@ class IterationStatement: public BreakableStatement { class DoWhileStatement: public IterationStatement { public: - inline DoWhileStatement(Isolate* isolate, ZoneStringList* labels); - DECLARE_NODE_TYPE(DoWhileStatement) void Initialize(Expression* cond, Statement* body) { @@ -477,7 +661,16 @@ class DoWhileStatement: public IterationStatement { virtual int StackCheckId() const { return back_edge_id_; } int BackEdgeId() const { return back_edge_id_; } - virtual bool IsInlineable() const; + protected: + template<class> friend class AstNodeFactory; + + DoWhileStatement(Isolate* isolate, ZoneStringList* labels) + : IterationStatement(isolate, labels), + cond_(NULL), + condition_position_(-1), + continue_id_(GetNextId(isolate)), + back_edge_id_(GetNextId(isolate)) { + } private: Expression* cond_; @@ -489,8 +682,6 @@ class DoWhileStatement: public IterationStatement { class WhileStatement: public IterationStatement { public: - inline WhileStatement(Isolate* isolate, ZoneStringList* labels); - DECLARE_NODE_TYPE(WhileStatement) void Initialize(Expression* cond, Statement* body) { @@ -505,13 +696,22 @@ class WhileStatement: public IterationStatement { void set_may_have_function_literal(bool value) { may_have_function_literal_ = value; } - virtual bool IsInlineable() const; // Bailout support. virtual int ContinueId() const { return EntryId(); } virtual int StackCheckId() const { return body_id_; } int BodyId() const { return body_id_; } + protected: + template<class> friend class AstNodeFactory; + + WhileStatement(Isolate* isolate, ZoneStringList* labels) + : IterationStatement(isolate, labels), + cond_(NULL), + may_have_function_literal_(true), + body_id_(GetNextId(isolate)) { + } + private: Expression* cond_; // True if there is a function literal subexpression in the condition. @@ -522,8 +722,6 @@ class WhileStatement: public IterationStatement { class ForStatement: public IterationStatement { public: - inline ForStatement(Isolate* isolate, ZoneStringList* labels); - DECLARE_NODE_TYPE(ForStatement) void Initialize(Statement* init, @@ -555,7 +753,20 @@ class ForStatement: public IterationStatement { bool is_fast_smi_loop() { return loop_variable_ != NULL; } Variable* loop_variable() { return loop_variable_; } void set_loop_variable(Variable* var) { loop_variable_ = var; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + ForStatement(Isolate* isolate, ZoneStringList* labels) + : IterationStatement(isolate, labels), + init_(NULL), + cond_(NULL), + next_(NULL), + may_have_function_literal_(true), + loop_variable_(NULL), + continue_id_(GetNextId(isolate)), + body_id_(GetNextId(isolate)) { + } private: Statement* init_; @@ -571,8 +782,6 @@ class ForStatement: public IterationStatement { class ForInStatement: public IterationStatement { public: - inline ForInStatement(Isolate* isolate, ZoneStringList* labels); - DECLARE_NODE_TYPE(ForInStatement) void Initialize(Expression* each, Expression* enumerable, Statement* body) { @@ -583,13 +792,22 @@ class ForInStatement: public IterationStatement { Expression* each() const { return each_; } Expression* enumerable() const { return enumerable_; } - virtual bool IsInlineable() const; // Bailout support. int AssignmentId() const { return assignment_id_; } virtual int ContinueId() const { return EntryId(); } virtual int StackCheckId() const { return EntryId(); } + protected: + template<class> friend class AstNodeFactory; + + ForInStatement(Isolate* isolate, ZoneStringList* labels) + : IterationStatement(isolate, labels), + each_(NULL), + enumerable_(NULL), + assignment_id_(GetNextId(isolate)) { + } + private: Expression* each_; Expression* enumerable_; @@ -599,19 +817,17 @@ class ForInStatement: public IterationStatement { class ExpressionStatement: public Statement { public: - explicit ExpressionStatement(Expression* expression) - : expression_(expression) { } - DECLARE_NODE_TYPE(ExpressionStatement) - virtual bool IsInlineable() const; - - virtual Assignment* StatementAsSimpleAssignment(); - virtual CountOperation* StatementAsCountOperation(); - void set_expression(Expression* e) { expression_ = e; } Expression* expression() const { return expression_; } + protected: + template<class> friend class AstNodeFactory; + + explicit ExpressionStatement(Expression* expression) + : expression_(expression) { } + private: Expression* expression_; }; @@ -619,13 +835,15 @@ class ExpressionStatement: public Statement { class ContinueStatement: public Statement { public: - explicit ContinueStatement(IterationStatement* target) - : target_(target) { } - DECLARE_NODE_TYPE(ContinueStatement) IterationStatement* target() const { return target_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + explicit ContinueStatement(IterationStatement* target) + : target_(target) { } private: IterationStatement* target_; @@ -634,13 +852,15 @@ class ContinueStatement: public Statement { class BreakStatement: public Statement { public: - explicit BreakStatement(BreakableStatement* target) - : target_(target) { } - DECLARE_NODE_TYPE(BreakStatement) BreakableStatement* target() const { return target_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + explicit BreakStatement(BreakableStatement* target) + : target_(target) { } private: BreakableStatement* target_; @@ -649,13 +869,15 @@ class BreakStatement: public Statement { class ReturnStatement: public Statement { public: - explicit ReturnStatement(Expression* expression) - : expression_(expression) { } - DECLARE_NODE_TYPE(ReturnStatement) Expression* expression() const { return expression_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + explicit ReturnStatement(Expression* expression) + : expression_(expression) { } private: Expression* expression_; @@ -664,15 +886,17 @@ class ReturnStatement: public Statement { class WithStatement: public Statement { public: - WithStatement(Expression* expression, Statement* statement) - : expression_(expression), statement_(statement) { } - DECLARE_NODE_TYPE(WithStatement) Expression* expression() const { return expression_; } Statement* statement() const { return statement_; } - virtual bool IsInlineable() const; + protected: + template<class> friend class AstNodeFactory; + + WithStatement(Expression* expression, Statement* statement) + : expression_(expression), + statement_(statement) { } private: Expression* expression_; @@ -704,6 +928,8 @@ class CaseClause: public ZoneObject { // Type feedback information. void RecordTypeFeedback(TypeFeedbackOracle* oracle); bool IsSmiCompare() { return compare_type_ == SMI_ONLY; } + bool IsSymbolCompare() { return compare_type_ == SYMBOL_ONLY; } + bool IsStringCompare() { return compare_type_ == STRING_ONLY; } bool IsObjectCompare() { return compare_type_ == OBJECT_ONLY; } private: @@ -711,7 +937,13 @@ class CaseClause: public ZoneObject { Label body_target_; ZoneList<Statement*>* statements_; int position_; - enum CompareTypeFeedback { NONE, SMI_ONLY, OBJECT_ONLY }; + enum CompareTypeFeedback { + NONE, + SMI_ONLY, + SYMBOL_ONLY, + STRING_ONLY, + OBJECT_ONLY + }; CompareTypeFeedback compare_type_; int compare_id_; int entry_id_; @@ -720,8 +952,6 @@ class CaseClause: public ZoneObject { class SwitchStatement: public BreakableStatement { public: - inline SwitchStatement(Isolate* isolate, ZoneStringList* labels); - DECLARE_NODE_TYPE(SwitchStatement) void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) { @@ -731,7 +961,14 @@ class SwitchStatement: public BreakableStatement { Expression* tag() const { return tag_; } ZoneList<CaseClause*>* cases() const { return cases_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + SwitchStatement(Isolate* isolate, ZoneStringList* labels) + : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS), + tag_(NULL), + cases_(NULL) { } private: Expression* tag_; @@ -746,22 +983,8 @@ class SwitchStatement: public BreakableStatement { // given if-statement has a then- or an else-part containing code. class IfStatement: public Statement { public: - IfStatement(Isolate* isolate, - Expression* condition, - Statement* then_statement, - Statement* else_statement) - : condition_(condition), - then_statement_(then_statement), - else_statement_(else_statement), - if_id_(GetNextId(isolate)), - then_id_(GetNextId(isolate)), - else_id_(GetNextId(isolate)) { - } - DECLARE_NODE_TYPE(IfStatement) - virtual bool IsInlineable() const; - bool HasThenStatement() const { return !then_statement()->IsEmpty(); } bool HasElseStatement() const { return !else_statement()->IsEmpty(); } @@ -773,6 +996,21 @@ class IfStatement: public Statement { int ThenId() const { return then_id_; } int ElseId() const { return else_id_; } + protected: + template<class> friend class AstNodeFactory; + + IfStatement(Isolate* isolate, + Expression* condition, + Statement* then_statement, + Statement* else_statement) + : condition_(condition), + then_statement_(then_statement), + else_statement_(else_statement), + if_id_(GetNextId(isolate)), + then_id_(GetNextId(isolate)), + else_id_(GetNextId(isolate)) { + } + private: Expression* condition_; Statement* then_statement_; @@ -787,7 +1025,7 @@ class IfStatement: public Statement { // stack in the compiler; this should probably be reworked. class TargetCollector: public AstNode { public: - TargetCollector(): targets_(0) { } + TargetCollector() : targets_(0) { } // Adds a jump target to the collector. The collector stores a pointer not // a copy of the target to make binding work, so make sure not to pass in @@ -799,7 +1037,6 @@ class TargetCollector: public AstNode { virtual TargetCollector* AsTargetCollector() { return this; } ZoneList<Label*>* targets() { return &targets_; } - virtual bool IsInlineable() const; private: ZoneList<Label*> targets_; @@ -808,18 +1045,24 @@ class TargetCollector: public AstNode { class TryStatement: public Statement { public: - explicit TryStatement(Block* try_block) - : try_block_(try_block), escaping_targets_(NULL) { } - void set_escaping_targets(ZoneList<Label*>* targets) { escaping_targets_ = targets; } + int index() const { return index_; } Block* try_block() const { return try_block_; } ZoneList<Label*>* escaping_targets() const { return escaping_targets_; } - virtual bool IsInlineable() const; + + protected: + TryStatement(int index, Block* try_block) + : index_(index), + try_block_(try_block), + escaping_targets_(NULL) { } private: + // Unique (per-function) index of this handler. This is not an AST ID. + int index_; + Block* try_block_; ZoneList<Label*>* escaping_targets_; }; @@ -827,23 +1070,26 @@ class TryStatement: public Statement { class TryCatchStatement: public TryStatement { public: - TryCatchStatement(Block* try_block, + DECLARE_NODE_TYPE(TryCatchStatement) + + Scope* scope() { return scope_; } + Variable* variable() { return variable_; } + Block* catch_block() const { return catch_block_; } + + protected: + template<class> friend class AstNodeFactory; + + TryCatchStatement(int index, + Block* try_block, Scope* scope, Variable* variable, Block* catch_block) - : TryStatement(try_block), + : TryStatement(index, try_block), scope_(scope), variable_(variable), catch_block_(catch_block) { } - DECLARE_NODE_TYPE(TryCatchStatement) - - Scope* scope() { return scope_; } - Variable* variable() { return variable_; } - Block* catch_block() const { return catch_block_; } - virtual bool IsInlineable() const; - private: Scope* scope_; Variable* variable_; @@ -853,14 +1099,16 @@ class TryCatchStatement: public TryStatement { class TryFinallyStatement: public TryStatement { public: - TryFinallyStatement(Block* try_block, Block* finally_block) - : TryStatement(try_block), - finally_block_(finally_block) { } - DECLARE_NODE_TYPE(TryFinallyStatement) Block* finally_block() const { return finally_block_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + TryFinallyStatement(int index, Block* try_block, Block* finally_block) + : TryStatement(index, try_block), + finally_block_(finally_block) { } private: Block* finally_block_; @@ -870,7 +1118,11 @@ class TryFinallyStatement: public TryStatement { class DebuggerStatement: public Statement { public: DECLARE_NODE_TYPE(DebuggerStatement) - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + DebuggerStatement() {} }; @@ -878,20 +1130,17 @@ class EmptyStatement: public Statement { public: DECLARE_NODE_TYPE(EmptyStatement) - virtual bool IsInlineable() const; + protected: + template<class> friend class AstNodeFactory; + + EmptyStatement() {} }; class Literal: public Expression { public: - Literal(Isolate* isolate, Handle<Object> handle) - : Expression(isolate), handle_(handle) { } - DECLARE_NODE_TYPE(Literal) - virtual bool IsTrivial() { return true; } - virtual bool IsSmiLiteral() { return handle_->IsSmi(); } - // Check if this literal is identical to the other literal. bool IsIdenticalTo(const Literal* other) const { return handle_.is_identical_to(other->handle_); @@ -928,7 +1177,13 @@ class Literal: public Expression { } Handle<Object> handle() const { return handle_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + Literal(Isolate* isolate, Handle<Object> handle) + : Expression(isolate), + handle_(handle) { } private: Handle<Object> handle_; @@ -938,15 +1193,6 @@ class Literal: public Expression { // Base class for literals that needs space in the corresponding JSFunction. class MaterializedLiteral: public Expression { public: - MaterializedLiteral(Isolate* isolate, - int literal_index, - bool is_simple, - int depth) - : Expression(isolate), - literal_index_(literal_index), - is_simple_(is_simple), - depth_(depth) {} - virtual MaterializedLiteral* AsMaterializedLiteral() { return this; } int literal_index() { return literal_index_; } @@ -956,7 +1202,16 @@ class MaterializedLiteral: public Expression { bool is_simple() const { return is_simple_; } int depth() const { return depth_; } - virtual bool IsInlineable() const; + + protected: + MaterializedLiteral(Isolate* isolate, + int literal_index, + bool is_simple, + int depth) + : Expression(isolate), + literal_index_(literal_index), + is_simple_(is_simple), + depth_(depth) {} private: int literal_index_; @@ -983,7 +1238,6 @@ class ObjectLiteral: public MaterializedLiteral { }; Property(Literal* key, Expression* value); - Property(bool is_getter, FunctionLiteral* value); Literal* key() { return key_; } Expression* value() { return value_; } @@ -994,6 +1248,12 @@ class ObjectLiteral: public MaterializedLiteral { void set_emit_store(bool emit_store); bool emit_store(); + protected: + template<class> friend class AstNodeFactory; + + Property(bool is_getter, FunctionLiteral* value); + void set_key(Literal* key) { key_ = key; } + private: Literal* key_; Expression* value_; @@ -1001,20 +1261,6 @@ class ObjectLiteral: public MaterializedLiteral { bool emit_store_; }; - ObjectLiteral(Isolate* isolate, - Handle<FixedArray> constant_properties, - ZoneList<Property*>* properties, - int literal_index, - bool is_simple, - bool fast_elements, - int depth, - bool has_function) - : MaterializedLiteral(isolate, literal_index, is_simple, depth), - constant_properties_(constant_properties), - properties_(properties), - fast_elements_(fast_elements), - has_function_(has_function) {} - DECLARE_NODE_TYPE(ObjectLiteral) Handle<FixedArray> constant_properties() const { @@ -1037,6 +1283,23 @@ class ObjectLiteral: public MaterializedLiteral { kHasFunction = 1 << 1 }; + protected: + template<class> friend class AstNodeFactory; + + ObjectLiteral(Isolate* isolate, + Handle<FixedArray> constant_properties, + ZoneList<Property*>* properties, + int literal_index, + bool is_simple, + bool fast_elements, + int depth, + bool has_function) + : MaterializedLiteral(isolate, literal_index, is_simple, depth), + constant_properties_(constant_properties), + properties_(properties), + fast_elements_(fast_elements), + has_function_(has_function) {} + private: Handle<FixedArray> constant_properties_; ZoneList<Property*>* properties_; @@ -1048,6 +1311,14 @@ class ObjectLiteral: public MaterializedLiteral { // Node for capturing a regexp literal. class RegExpLiteral: public MaterializedLiteral { public: + DECLARE_NODE_TYPE(RegExpLiteral) + + Handle<String> pattern() const { return pattern_; } + Handle<String> flags() const { return flags_; } + + protected: + template<class> friend class AstNodeFactory; + RegExpLiteral(Isolate* isolate, Handle<String> pattern, Handle<String> flags, @@ -1056,11 +1327,6 @@ class RegExpLiteral: public MaterializedLiteral { pattern_(pattern), flags_(flags) {} - DECLARE_NODE_TYPE(RegExpLiteral) - - Handle<String> pattern() const { return pattern_; } - Handle<String> flags() const { return flags_; } - private: Handle<String> pattern_; Handle<String> flags_; @@ -1070,6 +1336,17 @@ class RegExpLiteral: public MaterializedLiteral { // for minimizing the work when constructing it at runtime. class ArrayLiteral: public MaterializedLiteral { public: + DECLARE_NODE_TYPE(ArrayLiteral) + + Handle<FixedArray> constant_elements() const { return constant_elements_; } + ZoneList<Expression*>* values() const { return values_; } + + // Return an AST id for an element that is used in simulate instructions. + int GetIdForElement(int i) { return first_element_id_ + i; } + + protected: + template<class> friend class AstNodeFactory; + ArrayLiteral(Isolate* isolate, Handle<FixedArray> constant_elements, ZoneList<Expression*>* values, @@ -1081,14 +1358,6 @@ class ArrayLiteral: public MaterializedLiteral { values_(values), first_element_id_(ReserveIdRange(isolate, values->length())) {} - DECLARE_NODE_TYPE(ArrayLiteral) - - Handle<FixedArray> constant_elements() const { return constant_elements_; } - ZoneList<Expression*>* values() const { return values_; } - - // Return an AST id for an element that is used in simulate instructions. - int GetIdForElement(int i) { return first_element_id_ + i; } - private: Handle<FixedArray> constant_elements_; ZoneList<Expression*>* values_; @@ -1098,77 +1367,59 @@ class ArrayLiteral: public MaterializedLiteral { class VariableProxy: public Expression { public: - VariableProxy(Isolate* isolate, Variable* var); - DECLARE_NODE_TYPE(VariableProxy) virtual bool IsValidLeftHandSide() { return var_ == NULL ? true : var_->IsValidLeftHandSide(); } - virtual bool IsTrivial() { - // Reading from a mutable variable is a side effect, but the - // variable for 'this' is immutable. - return is_this_ || is_trivial_; - } - - virtual bool IsInlineable() const; - bool IsVariable(Handle<String> n) { return !is_this() && name().is_identical_to(n); } bool IsArguments() { return var_ != NULL && var_->is_arguments(); } + bool IsLValue() { + return is_lvalue_; + } + Handle<String> name() const { return name_; } Variable* var() const { return var_; } bool is_this() const { return is_this_; } - bool inside_with() const { return inside_with_; } int position() const { return position_; } void MarkAsTrivial() { is_trivial_ = true; } + void MarkAsLValue() { is_lvalue_ = true; } // Bind this proxy to the variable var. void BindTo(Variable* var); protected: - Handle<String> name_; - Variable* var_; // resolved variable, or NULL - bool is_this_; - bool inside_with_; - bool is_trivial_; - int position_; + template<class> friend class AstNodeFactory; + + VariableProxy(Isolate* isolate, Variable* var); VariableProxy(Isolate* isolate, Handle<String> name, bool is_this, - bool inside_with, - int position = RelocInfo::kNoPosition); + int position); - friend class Scope; + Handle<String> name_; + Variable* var_; // resolved variable, or NULL + bool is_this_; + bool is_trivial_; + // True if this variable proxy is being used in an assignment + // or with a increment/decrement operator. + bool is_lvalue_; + int position_; }; class Property: public Expression { public: - Property(Isolate* isolate, - Expression* obj, - Expression* key, - int pos) - : Expression(isolate), - obj_(obj), - key_(key), - pos_(pos), - is_monomorphic_(false), - is_array_length_(false), - is_string_length_(false), - is_string_access_(false), - is_function_prototype_(false) { } - DECLARE_NODE_TYPE(Property) virtual bool IsValidLeftHandSide() { return true; } - virtual bool IsInlineable() const; Expression* obj() const { return obj_; } Expression* key() const { return key_; } @@ -1182,7 +1433,24 @@ class Property: public Expression { void RecordTypeFeedback(TypeFeedbackOracle* oracle); virtual bool IsMonomorphic() { return is_monomorphic_; } virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; } - virtual bool IsArrayLength() { return is_array_length_; } + bool IsArrayLength() { return is_array_length_; } + + protected: + template<class> friend class AstNodeFactory; + + Property(Isolate* isolate, + Expression* obj, + Expression* key, + int pos) + : Expression(isolate), + obj_(obj), + key_(key), + pos_(pos), + is_monomorphic_(false), + is_array_length_(false), + is_string_length_(false), + is_string_access_(false), + is_function_prototype_(false) { } private: Expression* obj_; @@ -1200,23 +1468,8 @@ class Property: public Expression { class Call: public Expression { public: - Call(Isolate* isolate, - Expression* expression, - ZoneList<Expression*>* arguments, - int pos) - : Expression(isolate), - expression_(expression), - arguments_(arguments), - pos_(pos), - is_monomorphic_(false), - check_type_(RECEIVER_MAP_CHECK), - return_id_(GetNextId(isolate)) { - } - DECLARE_NODE_TYPE(Call) - virtual bool IsInlineable() const; - Expression* expression() const { return expression_; } ZoneList<Expression*>* arguments() const { return arguments_; } virtual int position() const { return pos_; } @@ -1241,6 +1494,21 @@ class Call: public Expression { bool return_is_recorded_; #endif + protected: + template<class> friend class AstNodeFactory; + + Call(Isolate* isolate, + Expression* expression, + ZoneList<Expression*>* arguments, + int pos) + : Expression(isolate), + expression_(expression), + arguments_(arguments), + pos_(pos), + is_monomorphic_(false), + check_type_(RECEIVER_MAP_CHECK), + return_id_(GetNextId(isolate)) { } + private: Expression* expression_; ZoneList<Expression*>* arguments_; @@ -1259,6 +1527,15 @@ class Call: public Expression { class CallNew: public Expression { public: + DECLARE_NODE_TYPE(CallNew) + + Expression* expression() const { return expression_; } + ZoneList<Expression*>* arguments() const { return arguments_; } + virtual int position() const { return pos_; } + + protected: + template<class> friend class AstNodeFactory; + CallNew(Isolate* isolate, Expression* expression, ZoneList<Expression*>* arguments, @@ -1268,14 +1545,6 @@ class CallNew: public Expression { arguments_(arguments), pos_(pos) { } - DECLARE_NODE_TYPE(CallNew) - - virtual bool IsInlineable() const; - - Expression* expression() const { return expression_; } - ZoneList<Expression*>* arguments() const { return arguments_; } - virtual int position() const { return pos_; } - private: Expression* expression_; ZoneList<Expression*>* arguments_; @@ -1289,6 +1558,16 @@ class CallNew: public Expression { // implemented in JavaScript (see "v8natives.js"). class CallRuntime: public Expression { public: + DECLARE_NODE_TYPE(CallRuntime) + + Handle<String> name() const { return name_; } + const Runtime::Function* function() const { return function_; } + ZoneList<Expression*>* arguments() const { return arguments_; } + bool is_jsruntime() const { return function_ == NULL; } + + protected: + template<class> friend class AstNodeFactory; + CallRuntime(Isolate* isolate, Handle<String> name, const Runtime::Function* function, @@ -1298,15 +1577,6 @@ class CallRuntime: public Expression { function_(function), arguments_(arguments) { } - DECLARE_NODE_TYPE(CallRuntime) - - virtual bool IsInlineable() const; - - Handle<String> name() const { return name_; } - const Runtime::Function* function() const { return function_; } - ZoneList<Expression*>* arguments() const { return arguments_; } - bool is_jsruntime() const { return function_ == NULL; } - private: Handle<String> name_; const Runtime::Function* function_; @@ -1316,49 +1586,53 @@ class CallRuntime: public Expression { class UnaryOperation: public Expression { public: - UnaryOperation(Isolate* isolate, - Token::Value op, - Expression* expression, - int pos) - : Expression(isolate), op_(op), expression_(expression), pos_(pos) { - ASSERT(Token::IsUnaryOp(op)); - } - DECLARE_NODE_TYPE(UnaryOperation) - virtual bool IsInlineable() const; - virtual bool ResultOverwriteAllowed(); Token::Value op() const { return op_; } Expression* expression() const { return expression_; } virtual int position() const { return pos_; } + int MaterializeTrueId() { return materialize_true_id_; } + int MaterializeFalseId() { return materialize_false_id_; } + + protected: + template<class> friend class AstNodeFactory; + + UnaryOperation(Isolate* isolate, + Token::Value op, + Expression* expression, + int pos) + : Expression(isolate), + op_(op), + expression_(expression), + pos_(pos), + materialize_true_id_(AstNode::kNoNumber), + materialize_false_id_(AstNode::kNoNumber) { + ASSERT(Token::IsUnaryOp(op)); + if (op == Token::NOT) { + materialize_true_id_ = GetNextId(isolate); + materialize_false_id_ = GetNextId(isolate); + } + } + private: Token::Value op_; Expression* expression_; int pos_; + + // For unary not (Token::NOT), the AST ids where true and false will + // actually be materialized, respectively. + int materialize_true_id_; + int materialize_false_id_; }; class BinaryOperation: public Expression { public: - BinaryOperation(Isolate* isolate, - Token::Value op, - Expression* left, - Expression* right, - int pos) - : Expression(isolate), op_(op), left_(left), right_(right), pos_(pos) { - ASSERT(Token::IsBinaryOp(op)); - right_id_ = (op == Token::AND || op == Token::OR) - ? static_cast<int>(GetNextId(isolate)) - : AstNode::kNoNumber; - } - DECLARE_NODE_TYPE(BinaryOperation) - virtual bool IsInlineable() const; - virtual bool ResultOverwriteAllowed(); Token::Value op() const { return op_; } @@ -1369,6 +1643,21 @@ class BinaryOperation: public Expression { // Bailout support. int RightId() const { return right_id_; } + protected: + template<class> friend class AstNodeFactory; + + BinaryOperation(Isolate* isolate, + Token::Value op, + Expression* left, + Expression* right, + int pos) + : Expression(isolate), op_(op), left_(left), right_(right), pos_(pos) { + ASSERT(Token::IsBinaryOp(op)); + right_id_ = (op == Token::AND || op == Token::OR) + ? GetNextId(isolate) + : AstNode::kNoNumber; + } + private: Token::Value op_; Expression* left_; @@ -1382,19 +1671,6 @@ class BinaryOperation: public Expression { class CountOperation: public Expression { public: - CountOperation(Isolate* isolate, - Token::Value op, - bool is_prefix, - Expression* expr, - int pos) - : Expression(isolate), - op_(op), - is_prefix_(is_prefix), - expression_(expr), - pos_(pos), - assignment_id_(GetNextId(isolate)), - count_id_(GetNextId(isolate)) {} - DECLARE_NODE_TYPE(CountOperation) bool is_prefix() const { return is_prefix_; } @@ -1410,8 +1686,6 @@ class CountOperation: public Expression { virtual void MarkAsStatement() { is_prefix_ = true; } - virtual bool IsInlineable() const; - void RecordTypeFeedback(TypeFeedbackOracle* oracle); virtual bool IsMonomorphic() { return is_monomorphic_; } virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; } @@ -1420,6 +1694,22 @@ class CountOperation: public Expression { int AssignmentId() const { return assignment_id_; } int CountId() const { return count_id_; } + protected: + template<class> friend class AstNodeFactory; + + CountOperation(Isolate* isolate, + Token::Value op, + bool is_prefix, + Expression* expr, + int pos) + : Expression(isolate), + op_(op), + is_prefix_(is_prefix), + expression_(expr), + pos_(pos), + assignment_id_(GetNextId(isolate)), + count_id_(GetNextId(isolate)) {} + private: Token::Value op_; bool is_prefix_; @@ -1434,20 +1724,6 @@ class CountOperation: public Expression { class CompareOperation: public Expression { public: - CompareOperation(Isolate* isolate, - Token::Value op, - Expression* left, - Expression* right, - int pos) - : Expression(isolate), - op_(op), - left_(left), - right_(right), - pos_(pos), - compare_type_(NONE) { - ASSERT(Token::IsCompareOp(op)); - } - DECLARE_NODE_TYPE(CompareOperation) Token::Value op() const { return op_; } @@ -1455,8 +1731,6 @@ class CompareOperation: public Expression { Expression* right() const { return right_; } virtual int position() const { return pos_; } - virtual bool IsInlineable() const; - // Type feedback information. void RecordTypeFeedback(TypeFeedbackOracle* oracle); bool IsSmiCompare() { return compare_type_ == SMI_ONLY; } @@ -1465,6 +1739,24 @@ class CompareOperation: public Expression { // Match special cases. bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check); bool IsLiteralCompareUndefined(Expression** expr); + bool IsLiteralCompareNull(Expression** expr); + + protected: + template<class> friend class AstNodeFactory; + + CompareOperation(Isolate* isolate, + Token::Value op, + Expression* left, + Expression* right, + int pos) + : Expression(isolate), + op_(op), + left_(left), + right_(right), + pos_(pos), + compare_type_(NONE) { + ASSERT(Token::IsCompareOp(op)); + } private: Token::Value op_; @@ -1477,27 +1769,23 @@ class CompareOperation: public Expression { }; -class CompareToNull: public Expression { +class Conditional: public Expression { public: - CompareToNull(Isolate* isolate, bool is_strict, Expression* expression) - : Expression(isolate), is_strict_(is_strict), expression_(expression) { } - - DECLARE_NODE_TYPE(CompareToNull) + DECLARE_NODE_TYPE(Conditional) - virtual bool IsInlineable() const; + Expression* condition() const { return condition_; } + Expression* then_expression() const { return then_expression_; } + Expression* else_expression() const { return else_expression_; } - bool is_strict() const { return is_strict_; } - Token::Value op() const { return is_strict_ ? Token::EQ_STRICT : Token::EQ; } - Expression* expression() const { return expression_; } + int then_expression_position() const { return then_expression_position_; } + int else_expression_position() const { return else_expression_position_; } - private: - bool is_strict_; - Expression* expression_; -}; + int ThenId() const { return then_id_; } + int ElseId() const { return else_id_; } + protected: + template<class> friend class AstNodeFactory; -class Conditional: public Expression { - public: Conditional(Isolate* isolate, Expression* condition, Expression* then_expression, @@ -1511,22 +1799,7 @@ class Conditional: public Expression { then_expression_position_(then_expression_position), else_expression_position_(else_expression_position), then_id_(GetNextId(isolate)), - else_id_(GetNextId(isolate)) { - } - - DECLARE_NODE_TYPE(Conditional) - - virtual bool IsInlineable() const; - - Expression* condition() const { return condition_; } - Expression* then_expression() const { return then_expression_; } - Expression* else_expression() const { return else_expression_; } - - int then_expression_position() const { return then_expression_position_; } - int else_expression_position() const { return else_expression_position_; } - - int ThenId() const { return then_id_; } - int ElseId() const { return else_id_; } + else_id_(GetNextId(isolate)) { } private: Expression* condition_; @@ -1541,16 +1814,8 @@ class Conditional: public Expression { class Assignment: public Expression { public: - Assignment(Isolate* isolate, - Token::Value op, - Expression* target, - Expression* value, - int pos); - DECLARE_NODE_TYPE(Assignment) - virtual bool IsInlineable() const; - Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; } Token::Value binary_op() const; @@ -1582,6 +1847,25 @@ class Assignment: public Expression { int CompoundLoadId() const { return compound_load_id_; } int AssignmentId() const { return assignment_id_; } + protected: + template<class> friend class AstNodeFactory; + + Assignment(Isolate* isolate, + Token::Value op, + Expression* target, + Expression* value, + int pos); + + template<class Visitor> + void Init(Isolate* isolate, AstNodeFactory<Visitor>* factory) { + ASSERT(Token::IsAssignmentOp(op_)); + if (is_compound()) { + binary_operation_ = + factory->NewBinaryOperation(binary_op(), target_, value_, pos_ + 1); + compound_load_id_ = GetNextId(isolate); + } + } + private: Token::Value op_; Expression* target_; @@ -1601,14 +1885,16 @@ class Assignment: public Expression { class Throw: public Expression { public: - Throw(Isolate* isolate, Expression* exception, int pos) - : Expression(isolate), exception_(exception), pos_(pos) {} - DECLARE_NODE_TYPE(Throw) Expression* exception() const { return exception_; } virtual int position() const { return pos_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + Throw(Isolate* isolate, Expression* exception, int pos) + : Expression(isolate), exception_(exception), pos_(pos) {} private: Expression* exception_; @@ -1624,39 +1910,6 @@ class FunctionLiteral: public Expression { DECLARATION }; - FunctionLiteral(Isolate* isolate, - Handle<String> name, - Scope* scope, - ZoneList<Statement*>* body, - int materialized_literal_count, - int expected_property_count, - bool has_only_simple_this_property_assignments, - Handle<FixedArray> this_property_assignments, - int num_parameters, - int start_position, - int end_position, - Type type, - bool has_duplicate_parameters) - : Expression(isolate), - name_(name), - scope_(scope), - body_(body), - materialized_literal_count_(materialized_literal_count), - expected_property_count_(expected_property_count), - has_only_simple_this_property_assignments_( - has_only_simple_this_property_assignments), - this_property_assignments_(this_property_assignments), - num_parameters_(num_parameters), - start_position_(start_position), - end_position_(end_position), - function_token_position_(RelocInfo::kNoPosition), - inferred_name_(HEAP->empty_string()), - is_expression_(type != DECLARATION), - is_anonymous_(type == ANONYMOUS_EXPRESSION), - pretenure_(false), - has_duplicate_parameters_(has_duplicate_parameters) { - } - DECLARE_NODE_TYPE(FunctionLiteral) Handle<String> name() const { return name_; } @@ -1664,21 +1917,23 @@ class FunctionLiteral: public Expression { ZoneList<Statement*>* body() const { return body_; } void set_function_token_position(int pos) { function_token_position_ = pos; } int function_token_position() const { return function_token_position_; } - int start_position() const { return start_position_; } - int end_position() const { return end_position_; } - bool is_expression() const { return is_expression_; } - bool is_anonymous() const { return is_anonymous_; } - bool strict_mode() const; + int start_position() const; + int end_position() const; + bool is_expression() const { return IsExpression::decode(bitfield_); } + bool is_anonymous() const { return IsAnonymous::decode(bitfield_); } + bool is_classic_mode() const { return language_mode() == CLASSIC_MODE; } + LanguageMode language_mode() const; int materialized_literal_count() { return materialized_literal_count_; } int expected_property_count() { return expected_property_count_; } + int handler_count() { return handler_count_; } bool has_only_simple_this_property_assignments() { - return has_only_simple_this_property_assignments_; + return HasOnlySimpleThisPropertyAssignments::decode(bitfield_); } Handle<FixedArray> this_property_assignments() { return this_property_assignments_; } - int num_parameters() { return num_parameters_; } + int parameter_count() { return parameter_count_; } bool AllowsLazyCompilation(); @@ -1692,45 +1947,93 @@ class FunctionLiteral: public Expression { inferred_name_ = inferred_name; } - bool pretenure() { return pretenure_; } - void set_pretenure(bool value) { pretenure_ = value; } - virtual bool IsInlineable() const; + bool pretenure() { return Pretenure::decode(bitfield_); } + void set_pretenure() { bitfield_ |= Pretenure::encode(true); } + + bool has_duplicate_parameters() { + return HasDuplicateParameters::decode(bitfield_); + } + + int ast_node_count() { return ast_properties_.node_count(); } + AstProperties::Flags* flags() { return ast_properties_.flags(); } + void set_ast_properties(AstProperties* ast_properties) { + ast_properties_ = *ast_properties; + } + + protected: + template<class> friend class AstNodeFactory; - bool has_duplicate_parameters() { return has_duplicate_parameters_; } + FunctionLiteral(Isolate* isolate, + Handle<String> name, + Scope* scope, + ZoneList<Statement*>* body, + int materialized_literal_count, + int expected_property_count, + int handler_count, + bool has_only_simple_this_property_assignments, + Handle<FixedArray> this_property_assignments, + int parameter_count, + Type type, + bool has_duplicate_parameters) + : Expression(isolate), + name_(name), + scope_(scope), + body_(body), + this_property_assignments_(this_property_assignments), + inferred_name_(isolate->factory()->empty_string()), + materialized_literal_count_(materialized_literal_count), + expected_property_count_(expected_property_count), + handler_count_(handler_count), + parameter_count_(parameter_count), + function_token_position_(RelocInfo::kNoPosition) { + bitfield_ = + HasOnlySimpleThisPropertyAssignments::encode( + has_only_simple_this_property_assignments) | + IsExpression::encode(type != DECLARATION) | + IsAnonymous::encode(type == ANONYMOUS_EXPRESSION) | + Pretenure::encode(false) | + HasDuplicateParameters::encode(has_duplicate_parameters); + } private: Handle<String> name_; Scope* scope_; ZoneList<Statement*>* body_; + Handle<FixedArray> this_property_assignments_; + Handle<String> inferred_name_; + AstProperties ast_properties_; + int materialized_literal_count_; int expected_property_count_; - bool has_only_simple_this_property_assignments_; - Handle<FixedArray> this_property_assignments_; - int num_parameters_; - int start_position_; - int end_position_; + int handler_count_; + int parameter_count_; int function_token_position_; - Handle<String> inferred_name_; - bool is_expression_; - bool is_anonymous_; - bool pretenure_; - bool has_duplicate_parameters_; + + unsigned bitfield_; + class HasOnlySimpleThisPropertyAssignments: public BitField<bool, 0, 1> {}; + class IsExpression: public BitField<bool, 1, 1> {}; + class IsAnonymous: public BitField<bool, 2, 1> {}; + class Pretenure: public BitField<bool, 3, 1> {}; + class HasDuplicateParameters: public BitField<bool, 4, 1> {}; }; class SharedFunctionInfoLiteral: public Expression { public: - SharedFunctionInfoLiteral( - Isolate* isolate, - Handle<SharedFunctionInfo> shared_function_info) - : Expression(isolate), shared_function_info_(shared_function_info) { } - DECLARE_NODE_TYPE(SharedFunctionInfoLiteral) Handle<SharedFunctionInfo> shared_function_info() const { return shared_function_info_; } - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + SharedFunctionInfoLiteral( + Isolate* isolate, + Handle<SharedFunctionInfo> shared_function_info) + : Expression(isolate), + shared_function_info_(shared_function_info) { } private: Handle<SharedFunctionInfo> shared_function_info_; @@ -1739,9 +2042,12 @@ class SharedFunctionInfoLiteral: public Expression { class ThisFunction: public Expression { public: - explicit ThisFunction(Isolate* isolate) : Expression(isolate) {} DECLARE_NODE_TYPE(ThisFunction) - virtual bool IsInlineable() const; + + protected: + template<class> friend class AstNodeFactory; + + explicit ThisFunction(Isolate* isolate): Expression(isolate) {} }; @@ -2096,9 +2402,10 @@ class RegExpEmpty: public RegExpTree { virtual bool IsEmpty(); virtual int min_match() { return 0; } virtual int max_match() { return 0; } - static RegExpEmpty* GetInstance() { return &kInstance; } - private: - static RegExpEmpty kInstance; + static RegExpEmpty* GetInstance() { + static RegExpEmpty* instance = ::new RegExpEmpty(); + return instance; + } }; @@ -2144,6 +2451,371 @@ class AstVisitor BASE_EMBEDDED { }; +// ---------------------------------------------------------------------------- +// Construction time visitor. + +class AstConstructionVisitor BASE_EMBEDDED { + public: + AstConstructionVisitor() { } + + AstProperties* ast_properties() { return &properties_; } + + private: + template<class> friend class AstNodeFactory; + + // Node visitors. +#define DEF_VISIT(type) \ + void Visit##type(type* node); + AST_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT + + void increase_node_count() { properties_.add_node_count(1); } + void add_flag(AstPropertiesFlag flag) { properties_.flags()->Add(flag); } + + AstProperties properties_; +}; + + +class AstNullVisitor BASE_EMBEDDED { + public: + // Node visitors. +#define DEF_VISIT(type) \ + void Visit##type(type* node) {} + AST_NODE_LIST(DEF_VISIT) +#undef DEF_VISIT +}; + + + +// ---------------------------------------------------------------------------- +// AstNode factory + +template<class Visitor> +class AstNodeFactory BASE_EMBEDDED { + public: + explicit AstNodeFactory(Isolate* isolate) + : isolate_(isolate), + zone_(isolate_->zone()) { } + + Visitor* visitor() { return &visitor_; } + +#define VISIT_AND_RETURN(NodeType, node) \ + visitor_.Visit##NodeType((node)); \ + return node; + + VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* fun, + Scope* scope) { + VariableDeclaration* decl = + new(zone_) VariableDeclaration(proxy, mode, fun, scope); + VISIT_AND_RETURN(VariableDeclaration, decl) + } + + ModuleDeclaration* NewModuleDeclaration(VariableProxy* proxy, + Module* module, + Scope* scope) { + ModuleDeclaration* decl = + new(zone_) ModuleDeclaration(proxy, module, scope); + VISIT_AND_RETURN(ModuleDeclaration, decl) + } + + ModuleLiteral* NewModuleLiteral(Block* body) { + ModuleLiteral* module = new(zone_) ModuleLiteral(body); + VISIT_AND_RETURN(ModuleLiteral, module) + } + + ModuleVariable* NewModuleVariable(Variable* var) { + ModuleVariable* module = new(zone_) ModuleVariable(var); + VISIT_AND_RETURN(ModuleLiteral, module) + } + + ModulePath* NewModulePath(Module* origin, Handle<String> name) { + ModulePath* module = new(zone_) ModulePath(origin, name); + VISIT_AND_RETURN(ModuleLiteral, module) + } + + ModuleUrl* NewModuleUrl(Handle<String> url) { + ModuleUrl* module = new(zone_) ModuleUrl(url); + VISIT_AND_RETURN(ModuleLiteral, module) + } + + Block* NewBlock(ZoneStringList* labels, + int capacity, + bool is_initializer_block) { + Block* block = new(zone_) Block( + isolate_, labels, capacity, is_initializer_block); + VISIT_AND_RETURN(Block, block) + } + +#define STATEMENT_WITH_LABELS(NodeType) \ + NodeType* New##NodeType(ZoneStringList* labels) { \ + NodeType* stmt = new(zone_) NodeType(isolate_, labels); \ + VISIT_AND_RETURN(NodeType, stmt); \ + } + STATEMENT_WITH_LABELS(DoWhileStatement) + STATEMENT_WITH_LABELS(WhileStatement) + STATEMENT_WITH_LABELS(ForStatement) + STATEMENT_WITH_LABELS(ForInStatement) + STATEMENT_WITH_LABELS(SwitchStatement) +#undef STATEMENT_WITH_LABELS + + ExpressionStatement* NewExpressionStatement(Expression* expression) { + ExpressionStatement* stmt = new(zone_) ExpressionStatement(expression); + VISIT_AND_RETURN(ExpressionStatement, stmt) + } + + ContinueStatement* NewContinueStatement(IterationStatement* target) { + ContinueStatement* stmt = new(zone_) ContinueStatement(target); + VISIT_AND_RETURN(ContinueStatement, stmt) + } + + BreakStatement* NewBreakStatement(BreakableStatement* target) { + BreakStatement* stmt = new(zone_) BreakStatement(target); + VISIT_AND_RETURN(BreakStatement, stmt) + } + + ReturnStatement* NewReturnStatement(Expression* expression) { + ReturnStatement* stmt = new(zone_) ReturnStatement(expression); + VISIT_AND_RETURN(ReturnStatement, stmt) + } + + WithStatement* NewWithStatement(Expression* expression, + Statement* statement) { + WithStatement* stmt = new(zone_) WithStatement(expression, statement); + VISIT_AND_RETURN(WithStatement, stmt) + } + + IfStatement* NewIfStatement(Expression* condition, + Statement* then_statement, + Statement* else_statement) { + IfStatement* stmt = new(zone_) IfStatement( + isolate_, condition, then_statement, else_statement); + VISIT_AND_RETURN(IfStatement, stmt) + } + + TryCatchStatement* NewTryCatchStatement(int index, + Block* try_block, + Scope* scope, + Variable* variable, + Block* catch_block) { + TryCatchStatement* stmt = new(zone_) TryCatchStatement( + index, try_block, scope, variable, catch_block); + VISIT_AND_RETURN(TryCatchStatement, stmt) + } + + TryFinallyStatement* NewTryFinallyStatement(int index, + Block* try_block, + Block* finally_block) { + TryFinallyStatement* stmt = + new(zone_) TryFinallyStatement(index, try_block, finally_block); + VISIT_AND_RETURN(TryFinallyStatement, stmt) + } + + DebuggerStatement* NewDebuggerStatement() { + DebuggerStatement* stmt = new(zone_) DebuggerStatement(); + VISIT_AND_RETURN(DebuggerStatement, stmt) + } + + EmptyStatement* NewEmptyStatement() { + return new(zone_) EmptyStatement(); + } + + Literal* NewLiteral(Handle<Object> handle) { + Literal* lit = new(zone_) Literal(isolate_, handle); + VISIT_AND_RETURN(Literal, lit) + } + + Literal* NewNumberLiteral(double number) { + return NewLiteral(isolate_->factory()->NewNumber(number, TENURED)); + } + + ObjectLiteral* NewObjectLiteral( + Handle<FixedArray> constant_properties, + ZoneList<ObjectLiteral::Property*>* properties, + int literal_index, + bool is_simple, + bool fast_elements, + int depth, + bool has_function) { + ObjectLiteral* lit = new(zone_) ObjectLiteral( + isolate_, constant_properties, properties, literal_index, + is_simple, fast_elements, depth, has_function); + VISIT_AND_RETURN(ObjectLiteral, lit) + } + + ObjectLiteral::Property* NewObjectLiteralProperty(bool is_getter, + FunctionLiteral* value) { + ObjectLiteral::Property* prop = + new(zone_) ObjectLiteral::Property(is_getter, value); + prop->set_key(NewLiteral(value->name())); + return prop; // Not an AST node, will not be visited. + } + + RegExpLiteral* NewRegExpLiteral(Handle<String> pattern, + Handle<String> flags, + int literal_index) { + RegExpLiteral* lit = + new(zone_) RegExpLiteral(isolate_, pattern, flags, literal_index); + VISIT_AND_RETURN(RegExpLiteral, lit); + } + + ArrayLiteral* NewArrayLiteral(Handle<FixedArray> constant_elements, + ZoneList<Expression*>* values, + int literal_index, + bool is_simple, + int depth) { + ArrayLiteral* lit = new(zone_) ArrayLiteral( + isolate_, constant_elements, values, literal_index, is_simple, depth); + VISIT_AND_RETURN(ArrayLiteral, lit) + } + + VariableProxy* NewVariableProxy(Variable* var) { + VariableProxy* proxy = new(zone_) VariableProxy(isolate_, var); + VISIT_AND_RETURN(VariableProxy, proxy) + } + + VariableProxy* NewVariableProxy(Handle<String> name, + bool is_this, + int position = RelocInfo::kNoPosition) { + VariableProxy* proxy = + new(zone_) VariableProxy(isolate_, name, is_this, position); + VISIT_AND_RETURN(VariableProxy, proxy) + } + + Property* NewProperty(Expression* obj, Expression* key, int pos) { + Property* prop = new(zone_) Property(isolate_, obj, key, pos); + VISIT_AND_RETURN(Property, prop) + } + + Call* NewCall(Expression* expression, + ZoneList<Expression*>* arguments, + int pos) { + Call* call = new(zone_) Call(isolate_, expression, arguments, pos); + VISIT_AND_RETURN(Call, call) + } + + CallNew* NewCallNew(Expression* expression, + ZoneList<Expression*>* arguments, + int pos) { + CallNew* call = new(zone_) CallNew(isolate_, expression, arguments, pos); + VISIT_AND_RETURN(CallNew, call) + } + + CallRuntime* NewCallRuntime(Handle<String> name, + const Runtime::Function* function, + ZoneList<Expression*>* arguments) { + CallRuntime* call = + new(zone_) CallRuntime(isolate_, name, function, arguments); + VISIT_AND_RETURN(CallRuntime, call) + } + + UnaryOperation* NewUnaryOperation(Token::Value op, + Expression* expression, + int pos) { + UnaryOperation* node = + new(zone_) UnaryOperation(isolate_, op, expression, pos); + VISIT_AND_RETURN(UnaryOperation, node) + } + + BinaryOperation* NewBinaryOperation(Token::Value op, + Expression* left, + Expression* right, + int pos) { + BinaryOperation* node = + new(zone_) BinaryOperation(isolate_, op, left, right, pos); + VISIT_AND_RETURN(BinaryOperation, node) + } + + CountOperation* NewCountOperation(Token::Value op, + bool is_prefix, + Expression* expr, + int pos) { + CountOperation* node = + new(zone_) CountOperation(isolate_, op, is_prefix, expr, pos); + VISIT_AND_RETURN(CountOperation, node) + } + + CompareOperation* NewCompareOperation(Token::Value op, + Expression* left, + Expression* right, + int pos) { + CompareOperation* node = + new(zone_) CompareOperation(isolate_, op, left, right, pos); + VISIT_AND_RETURN(CompareOperation, node) + } + + Conditional* NewConditional(Expression* condition, + Expression* then_expression, + Expression* else_expression, + int then_expression_position, + int else_expression_position) { + Conditional* cond = new(zone_) Conditional( + isolate_, condition, then_expression, else_expression, + then_expression_position, else_expression_position); + VISIT_AND_RETURN(Conditional, cond) + } + + Assignment* NewAssignment(Token::Value op, + Expression* target, + Expression* value, + int pos) { + Assignment* assign = + new(zone_) Assignment(isolate_, op, target, value, pos); + assign->Init(isolate_, this); + VISIT_AND_RETURN(Assignment, assign) + } + + Throw* NewThrow(Expression* exception, int pos) { + Throw* t = new(zone_) Throw(isolate_, exception, pos); + VISIT_AND_RETURN(Throw, t) + } + + FunctionLiteral* NewFunctionLiteral( + Handle<String> name, + Scope* scope, + ZoneList<Statement*>* body, + int materialized_literal_count, + int expected_property_count, + int handler_count, + bool has_only_simple_this_property_assignments, + Handle<FixedArray> this_property_assignments, + int parameter_count, + bool has_duplicate_parameters, + FunctionLiteral::Type type, + bool visit_with_visitor) { + FunctionLiteral* lit = new(zone_) FunctionLiteral( + isolate_, name, scope, body, + materialized_literal_count, expected_property_count, handler_count, + has_only_simple_this_property_assignments, this_property_assignments, + parameter_count, type, has_duplicate_parameters); + if (visit_with_visitor) { + visitor_.VisitFunctionLiteral(lit); + } + return lit; + } + + SharedFunctionInfoLiteral* NewSharedFunctionInfoLiteral( + Handle<SharedFunctionInfo> shared_function_info) { + SharedFunctionInfoLiteral* lit = + new(zone_) SharedFunctionInfoLiteral(isolate_, shared_function_info); + VISIT_AND_RETURN(SharedFunctionInfoLiteral, lit) + } + + ThisFunction* NewThisFunction() { + ThisFunction* fun = new(zone_) ThisFunction(isolate_); + VISIT_AND_RETURN(ThisFunction, fun) + } + +#undef VISIT_AND_RETURN + + private: + Isolate* isolate_; + Zone* zone_; + Visitor visitor_; +}; + + } } // namespace v8::internal #endif // V8_AST_H_ diff --git a/deps/v8/src/atomicops_internals_mips_gcc.h b/deps/v8/src/atomicops_internals_mips_gcc.h index 5113de2891..9498fd76e1 100644 --- a/deps/v8/src/atomicops_internals_mips_gcc.h +++ b/deps/v8/src/atomicops_internals_mips_gcc.h @@ -30,7 +30,7 @@ #ifndef V8_ATOMICOPS_INTERNALS_MIPS_GCC_H_ #define V8_ATOMICOPS_INTERNALS_MIPS_GCC_H_ -#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("sync" : : : "memory") +#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") namespace v8 { namespace internal { @@ -48,16 +48,19 @@ namespace internal { inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { - Atomic32 prev; - __asm__ __volatile__("1:\n" - "ll %0, %1\n" // prev = *ptr + Atomic32 prev, tmp; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "ll %0, %5\n" // prev = *ptr "bne %0, %3, 2f\n" // if (prev != old_value) goto 2 - "nop\n" // delay slot nop - "sc %2, %1\n" // *ptr = new_value (with atomic check) + "move %2, %4\n" // tmp = new_value + "sc %2, %1\n" // *ptr = tmp (with atomic check) "beqz %2, 1b\n" // start again on atomic error "nop\n" // delay slot nop "2:\n" - : "=&r" (prev), "=m" (*ptr), "+&r" (new_value) + ".set pop\n" + : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) : "Ir" (old_value), "r" (new_value), "m" (*ptr) : "memory"); return prev; @@ -68,12 +71,15 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { Atomic32 temp, old; - __asm__ __volatile__("1:\n" + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" "ll %1, %2\n" // old = *ptr "move %0, %3\n" // temp = new_value "sc %0, %2\n" // *ptr = temp (with atomic check) "beqz %0, 1b\n" // start again on atomic error "nop\n" // delay slot nop + ".set pop\n" : "=&r" (temp), "=&r" (old), "=m" (*ptr) : "r" (new_value), "m" (*ptr) : "memory"); @@ -87,13 +93,15 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { Atomic32 temp, temp2; - __asm__ __volatile__("1:\n" + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" "ll %0, %2\n" // temp = *ptr - "addu %0, %3\n" // temp = temp + increment - "move %1, %0\n" // temp2 = temp - "sc %0, %2\n" // *ptr = temp (with atomic check) - "beqz %0, 1b\n" // start again on atomic error - "nop\n" // delay slot nop + "addu %1, %0, %3\n" // temp2 = temp + increment + "sc %1, %2\n" // *ptr = temp2 (with atomic check) + "beqz %1, 1b\n" // start again on atomic error + "addu %1, %0, %3\n" // temp2 = temp + increment + ".set pop\n" : "=&r" (temp), "=&r" (temp2), "=m" (*ptr) : "Ir" (increment), "m" (*ptr) : "memory"); @@ -103,6 +111,7 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { + ATOMICOPS_COMPILER_BARRIER(); Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment); ATOMICOPS_COMPILER_BARRIER(); return res; @@ -117,16 +126,19 @@ inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { - Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); ATOMICOPS_COMPILER_BARRIER(); - return x; + Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + ATOMICOPS_COMPILER_BARRIER(); + return res; } inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { ATOMICOPS_COMPILER_BARRIER(); - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); + Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + ATOMICOPS_COMPILER_BARRIER(); + return res; } inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { @@ -134,7 +146,7 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { } inline void MemoryBarrier() { - ATOMICOPS_COMPILER_BARRIER(); + __asm__ __volatile__("sync" : : : "memory"); } inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { diff --git a/deps/v8/src/atomicops_internals_x86_macosx.h b/deps/v8/src/atomicops_internals_x86_macosx.h index 2bac006bdc..bfb02b3851 100644 --- a/deps/v8/src/atomicops_internals_x86_macosx.h +++ b/deps/v8/src/atomicops_internals_x86_macosx.h @@ -35,7 +35,7 @@ namespace v8 { namespace internal { -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; @@ -49,7 +49,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, return prev_value; } -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { Atomic32 old_value; do { @@ -59,12 +59,12 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, return old_value; } -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr)); } -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr)); } @@ -73,7 +73,7 @@ inline void MemoryBarrier() { OSMemoryBarrier(); } -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; @@ -87,7 +87,7 @@ inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, return prev_value; } -inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { return Acquire_CompareAndSwap(ptr, old_value, new_value); @@ -97,12 +97,12 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; } -inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; MemoryBarrier(); } -inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { MemoryBarrier(); *ptr = value; } @@ -111,13 +111,13 @@ inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; } -inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { Atomic32 value = *ptr; MemoryBarrier(); return value; } -inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { MemoryBarrier(); return *ptr; } @@ -126,7 +126,7 @@ inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { // 64-bit implementation on 64-bit platform -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; @@ -140,7 +140,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, return prev_value; } -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value) { Atomic64 old_value; do { @@ -150,17 +150,17 @@ inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, return old_value; } -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr)); } -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr)); } -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; @@ -174,7 +174,7 @@ inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, return prev_value; } -inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { // The lib kern interface does not distinguish between @@ -186,12 +186,12 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { *ptr = value; } -inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { *ptr = value; MemoryBarrier(); } -inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { MemoryBarrier(); *ptr = value; } @@ -200,13 +200,13 @@ inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; } -inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { Atomic64 value = *ptr; MemoryBarrier(); return value; } -inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { MemoryBarrier(); return *ptr; } @@ -264,7 +264,7 @@ inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, old_value, new_value); } -inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { +inline void NoBarrier_Store(volatile AtomicWord* ptr, AtomicWord value) { NoBarrier_Store( reinterpret_cast<volatile AtomicWordCastType*>(ptr), value); } @@ -279,7 +279,7 @@ inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { reinterpret_cast<volatile AtomicWordCastType*>(ptr), value); } -inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { +inline AtomicWord NoBarrier_Load(volatile const AtomicWord* ptr) { return NoBarrier_Load( reinterpret_cast<volatile const AtomicWordCastType*>(ptr)); } diff --git a/deps/v8/src/bignum-dtoa.h b/deps/v8/src/bignum-dtoa.h index ea1acbbfc8..93ec1f7706 100644 --- a/deps/v8/src/bignum-dtoa.h +++ b/deps/v8/src/bignum-dtoa.h @@ -44,7 +44,7 @@ enum BignumDtoaMode { BIGNUM_DTOA_PRECISION }; -// Converts the given double 'v' to ascii. +// Converts the given double 'v' to ASCII. // The result should be interpreted as buffer * 10^(point-length). // The buffer will be null-terminated. // diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index f07e625ec0..31a771fbeb 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,9 +34,11 @@ #include "debug.h" #include "execution.h" #include "global-handles.h" +#include "isolate-inl.h" #include "macro-assembler.h" #include "natives.h" #include "objects-visiting.h" +#include "platform.h" #include "snapshot.h" #include "extensions/externalize-string-extension.h" #include "extensions/gc-extension.h" @@ -74,22 +76,15 @@ Handle<String> Bootstrapper::NativesSourceLookup(int index) { Factory* factory = isolate->factory(); Heap* heap = isolate->heap(); if (heap->natives_source_cache()->get(index)->IsUndefined()) { - if (!Snapshot::IsEnabled() || FLAG_new_snapshot) { - // We can use external strings for the natives. - Vector<const char> source = Natives::GetRawScriptSource(index); - NativesExternalStringResource* resource = - new NativesExternalStringResource(this, - source.start(), - source.length()); - Handle<String> source_code = - factory->NewExternalStringFromAscii(resource); - heap->natives_source_cache()->set(index, *source_code); - } else { - // Old snapshot code can't cope with external strings at all. - Handle<String> source_code = - factory->NewStringFromAscii(Natives::GetRawScriptSource(index)); - heap->natives_source_cache()->set(index, *source_code); - } + // We can use external strings for the natives. + Vector<const char> source = Natives::GetRawScriptSource(index); + NativesExternalStringResource* resource = + new NativesExternalStringResource(this, + source.start(), + source.length()); + Handle<String> source_code = + factory->NewExternalStringFromAscii(resource); + heap->natives_source_cache()->set(index, *source_code); } Handle<Object> cached_source(heap->natives_source_cache()->get(index)); return Handle<String>::cast(cached_source); @@ -209,12 +204,31 @@ class Genesis BASE_EMBEDDED { void InstallBuiltinFunctionIds(); void InstallJSFunctionResultCaches(); void InitializeNormalizedMapCaches(); + + enum ExtensionTraversalState { + UNVISITED, VISITED, INSTALLED + }; + + class ExtensionStates { + public: + ExtensionStates(); + ExtensionTraversalState get_state(RegisteredExtension* extension); + void set_state(RegisteredExtension* extension, + ExtensionTraversalState state); + private: + Allocator allocator_; + HashMap map_; + DISALLOW_COPY_AND_ASSIGN(ExtensionStates); + }; + // Used both for deserialized and from-scratch contexts to add the extensions // provided. static bool InstallExtensions(Handle<Context> global_context, v8::ExtensionConfiguration* extensions); - static bool InstallExtension(const char* name); - static bool InstallExtension(v8::RegisteredExtension* current); + static bool InstallExtension(const char* name, + ExtensionStates* extension_states); + static bool InstallExtension(v8::RegisteredExtension* current, + ExtensionStates* extension_states); static void InstallSpecialObjects(Handle<Context> global_context); bool InstallJSBuiltins(Handle<JSBuiltinsObject> builtins); bool ConfigureApiObject(Handle<JSObject> object, @@ -243,13 +257,13 @@ class Genesis BASE_EMBEDDED { Handle<Map> CreateStrictModeFunctionMap( PrototypePropertyMode prototype_mode, Handle<JSFunction> empty_function, - Handle<FixedArray> arguments_callbacks, - Handle<FixedArray> caller_callbacks); + Handle<AccessorPair> arguments_callbacks, + Handle<AccessorPair> caller_callbacks); Handle<DescriptorArray> ComputeStrictFunctionInstanceDescriptor( PrototypePropertyMode propertyMode, - Handle<FixedArray> arguments, - Handle<FixedArray> caller); + Handle<AccessorPair> arguments, + Handle<AccessorPair> caller); static bool CompileBuiltin(Isolate* isolate, int index); static bool CompileExperimentalBuiltin(Isolate* isolate, int index); @@ -278,7 +292,7 @@ class Genesis BASE_EMBEDDED { void Bootstrapper::Iterate(ObjectVisitor* v) { extensions_cache_.Iterate(v); - v->Synchronize("Extensions"); + v->Synchronize(VisitorSynchronization::kExtensions); } @@ -357,10 +371,13 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target, } else { attributes = DONT_ENUM; } - SetLocalPropertyNoThrow(target, symbol, function, attributes); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + target, symbol, function, attributes)); if (is_ecma_native) { function->shared()->set_instance_class_name(*symbol); } + function->shared()->set_native(true); return function; } @@ -374,26 +391,28 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor( PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); + DescriptorArray::WhitenessWitness witness(*descriptors); + { // Add length. Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength); CallbacksDescriptor d(*factory()->length_symbol(), *foreign, attributes); - descriptors->Set(0, &d); + descriptors->Set(0, &d, witness); } { // Add name. Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionName); CallbacksDescriptor d(*factory()->name_symbol(), *foreign, attributes); - descriptors->Set(1, &d); + descriptors->Set(1, &d, witness); } { // Add arguments. Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionArguments); CallbacksDescriptor d(*factory()->arguments_symbol(), *foreign, attributes); - descriptors->Set(2, &d); + descriptors->Set(2, &d, witness); } { // Add caller. Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionCaller); CallbacksDescriptor d(*factory()->caller_symbol(), *foreign, attributes); - descriptors->Set(3, &d); + descriptors->Set(3, &d, witness); } if (prototypeMode != DONT_ADD_PROTOTYPE) { // Add prototype. @@ -403,9 +422,9 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor( Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionPrototype); CallbacksDescriptor d(*factory()->prototype_symbol(), *foreign, attributes); - descriptors->Set(4, &d); + descriptors->Set(4, &d, witness); } - descriptors->Sort(); + descriptors->Sort(witness); return descriptors; } @@ -478,7 +497,7 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) { // 262 15.3.4. Handle<String> symbol = factory->LookupAsciiSymbol("Empty"); Handle<JSFunction> empty_function = - factory->NewFunctionWithoutPrototype(symbol, kNonStrictMode); + factory->NewFunctionWithoutPrototype(symbol, CLASSIC_MODE); // --- E m p t y --- Handle<Code> code = @@ -514,48 +533,50 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) { Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor( PrototypePropertyMode prototypeMode, - Handle<FixedArray> arguments, - Handle<FixedArray> caller) { + Handle<AccessorPair> arguments, + Handle<AccessorPair> caller) { Handle<DescriptorArray> descriptors = factory()->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5); PropertyAttributes attributes = static_cast<PropertyAttributes>( - DONT_ENUM | DONT_DELETE | READ_ONLY); + DONT_ENUM | DONT_DELETE); + + DescriptorArray::WhitenessWitness witness(*descriptors); { // length Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength); CallbacksDescriptor d(*factory()->length_symbol(), *foreign, attributes); - descriptors->Set(0, &d); + descriptors->Set(0, &d, witness); } { // name Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionName); CallbacksDescriptor d(*factory()->name_symbol(), *foreign, attributes); - descriptors->Set(1, &d); + descriptors->Set(1, &d, witness); } { // arguments CallbacksDescriptor d(*factory()->arguments_symbol(), *arguments, attributes); - descriptors->Set(2, &d); + descriptors->Set(2, &d, witness); } { // caller CallbacksDescriptor d(*factory()->caller_symbol(), *caller, attributes); - descriptors->Set(3, &d); + descriptors->Set(3, &d, witness); } // prototype if (prototypeMode != DONT_ADD_PROTOTYPE) { - if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) { - attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY); + if (prototypeMode != ADD_WRITEABLE_PROTOTYPE) { + attributes = static_cast<PropertyAttributes>(attributes | READ_ONLY); } Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionPrototype); CallbacksDescriptor d(*factory()->prototype_symbol(), *foreign, attributes); - descriptors->Set(4, &d); + descriptors->Set(4, &d, witness); } - descriptors->Sort(); + descriptors->Sort(witness); return descriptors; } @@ -565,7 +586,7 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorFunction() { if (throw_type_error_function.is_null()) { Handle<String> name = factory()->LookupAsciiSymbol("ThrowTypeError"); throw_type_error_function = - factory()->NewFunctionWithoutPrototype(name, kNonStrictMode); + factory()->NewFunctionWithoutPrototype(name, CLASSIC_MODE); Handle<Code> code(isolate()->builtins()->builtin( Builtins::kStrictModePoisonPill)); throw_type_error_function->set_map( @@ -574,7 +595,7 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorFunction() { throw_type_error_function->shared()->set_code(*code); throw_type_error_function->shared()->DontAdaptArguments(); - PreventExtensions(throw_type_error_function); + JSObject::PreventExtensions(throw_type_error_function); } return throw_type_error_function; } @@ -583,8 +604,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorFunction() { Handle<Map> Genesis::CreateStrictModeFunctionMap( PrototypePropertyMode prototype_mode, Handle<JSFunction> empty_function, - Handle<FixedArray> arguments_callbacks, - Handle<FixedArray> caller_callbacks) { + Handle<AccessorPair> arguments_callbacks, + Handle<AccessorPair> caller_callbacks) { Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize); Handle<DescriptorArray> descriptors = ComputeStrictFunctionInstanceDescriptor(prototype_mode, @@ -601,8 +622,8 @@ void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) { // Create the callbacks arrays for ThrowTypeError functions. // The get/set callacks are filled in after the maps are created below. Factory* factory = empty->GetIsolate()->factory(); - Handle<FixedArray> arguments = factory->NewFixedArray(2, TENURED); - Handle<FixedArray> caller = factory->NewFixedArray(2, TENURED); + Handle<AccessorPair> arguments(factory->NewAccessorPair()); + Handle<AccessorPair> caller(factory->NewAccessorPair()); // Allocate map for the strict mode function instances. Handle<Map> strict_mode_function_instance_map = @@ -637,11 +658,11 @@ void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) { Handle<JSFunction> throw_function = GetThrowTypeErrorFunction(); - // Complete the callback fixed arrays. - arguments->set(0, *throw_function); - arguments->set(1, *throw_function); - caller->set(0, *throw_function); - caller->set(1, *throw_function); + // Complete the callbacks. + arguments->set_getter(*throw_function); + arguments->set_setter(*throw_function); + caller->set_getter(*throw_function); + caller->set_setter(*throw_function); } @@ -727,11 +748,10 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals( Handle<JSObject> prototype = Handle<JSObject>( JSObject::cast(js_global_function->instance_prototype())); - SetLocalPropertyNoThrow( - prototype, - factory()->constructor_symbol(), - isolate()->object_function(), - NONE); + CHECK_NOT_EMPTY_HANDLE(isolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + prototype, factory()->constructor_symbol(), + isolate()->object_function(), NONE)); } else { Handle<FunctionTemplateInfo> js_global_constructor( FunctionTemplateInfo::cast(js_global_template->constructor())); @@ -808,7 +828,7 @@ void Genesis::HookUpInnerGlobal(Handle<GlobalObject> inner_global) { factory()->LookupAsciiSymbol("global"), inner_global, attributes); - // Setup the reference from the global object to the builtins object. + // Set up the reference from the global object to the builtins object. JSGlobalObject::cast(*inner_global)->set_builtins(*builtins_global); TransferNamedProperties(inner_global_from_snapshot, inner_global); TransferIndexedProperties(inner_global_from_snapshot, inner_global); @@ -837,8 +857,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, Heap* heap = isolate->heap(); Handle<String> object_name = Handle<String>(heap->Object_symbol()); - SetLocalPropertyNoThrow(inner_global, object_name, - isolate->object_function(), DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + inner_global, object_name, + isolate->object_function(), DONT_ENUM)); Handle<JSObject> global = Handle<JSObject>(global_context()->global()); @@ -865,15 +887,12 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, factory->NewForeign(&Accessors::ArrayLength), static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE)); - // Cache the fast JavaScript array map - global_context()->set_js_array_map(array_function->initial_map()); - global_context()->js_array_map()->set_instance_descriptors( - *array_descriptors); // array_function is used internally. JS code creating array object should // search for the 'Array' property on the global object and use that one // as the constructor. 'Array' property on a global object can be // overwritten by JS code. global_context()->set_array_function(*array_function); + array_function->initial_map()->set_instance_descriptors(*array_descriptors); } { // --- N u m b e r --- @@ -940,6 +959,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, ASSERT_EQ(0, initial_map->inobject_properties()); Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(5); + DescriptorArray::WhitenessWitness witness(*descriptors); PropertyAttributes final = static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); int enum_index = 0; @@ -949,7 +969,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, JSRegExp::kSourceFieldIndex, final, enum_index++); - descriptors->Set(0, &field); + descriptors->Set(0, &field, witness); } { // ECMA-262, section 15.10.7.2. @@ -957,7 +977,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, JSRegExp::kGlobalFieldIndex, final, enum_index++); - descriptors->Set(1, &field); + descriptors->Set(1, &field, witness); } { // ECMA-262, section 15.10.7.3. @@ -965,7 +985,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, JSRegExp::kIgnoreCaseFieldIndex, final, enum_index++); - descriptors->Set(2, &field); + descriptors->Set(2, &field, witness); } { // ECMA-262, section 15.10.7.4. @@ -973,7 +993,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, JSRegExp::kMultilineFieldIndex, final, enum_index++); - descriptors->Set(3, &field); + descriptors->Set(3, &field, witness); } { // ECMA-262, section 15.10.7.5. @@ -983,10 +1003,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, JSRegExp::kLastIndexFieldIndex, writable, enum_index++); - descriptors->Set(4, &field); + descriptors->Set(4, &field, witness); } descriptors->SetNextEnumerationIndex(enum_index); - descriptors->Sort(); + descriptors->Sort(witness); initial_map->set_inobject_properties(5); initial_map->set_pre_allocated_property_fields(5); @@ -995,18 +1015,39 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, initial_map->instance_size() + 5 * kPointerSize); initial_map->set_instance_descriptors(*descriptors); initial_map->set_visitor_id(StaticVisitorBase::GetVisitorId(*initial_map)); + + // RegExp prototype object is itself a RegExp. + Handle<Map> proto_map = factory->CopyMapDropTransitions(initial_map); + proto_map->set_prototype(global_context()->initial_object_prototype()); + Handle<JSObject> proto = factory->NewJSObjectFromMap(proto_map); + proto->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, + heap->empty_string()); + proto->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, + heap->false_value()); + proto->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, + heap->false_value()); + proto->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, + heap->false_value()); + proto->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, + Smi::FromInt(0), + SKIP_WRITE_BARRIER); // It's a Smi. + initial_map->set_prototype(*proto); + factory->SetRegExpIrregexpData(Handle<JSRegExp>::cast(proto), + JSRegExp::IRREGEXP, factory->empty_string(), + JSRegExp::Flags(0), 0); } { // -- J S O N Handle<String> name = factory->NewStringFromAscii(CStrVector("JSON")); - Handle<JSFunction> cons = factory->NewFunction( - name, - factory->the_hole_value()); + Handle<JSFunction> cons = factory->NewFunction(name, + factory->the_hole_value()); cons->SetInstancePrototype(global_context()->initial_object_prototype()); cons->SetInstanceClassName(*name); Handle<JSObject> json_object = factory->NewJSObject(cons, TENURED); ASSERT(json_object->IsJSObject()); - SetLocalPropertyNoThrow(global, name, json_object, DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + global, name, json_object, DONT_ENUM)); global_context()->set_json_object(*json_object); } @@ -1036,21 +1077,23 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, global_context()->set_arguments_boilerplate(*result); // Note: length must be added as the first property and // callee must be added as the second property. - SetLocalPropertyNoThrow(result, factory->length_symbol(), - factory->undefined_value(), - DONT_ENUM); - SetLocalPropertyNoThrow(result, factory->callee_symbol(), - factory->undefined_value(), - DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + result, factory->length_symbol(), + factory->undefined_value(), DONT_ENUM)); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + result, factory->callee_symbol(), + factory->undefined_value(), DONT_ENUM)); #ifdef DEBUG - LookupResult lookup; + LookupResult lookup(isolate); result->LocalLookup(heap->callee_symbol(), &lookup); - ASSERT(lookup.IsProperty() && (lookup.type() == FIELD)); + ASSERT(lookup.IsFound() && (lookup.type() == FIELD)); ASSERT(lookup.GetFieldIndex() == Heap::kArgumentsCalleeIndex); result->LocalLookup(heap->length_symbol(), &lookup); - ASSERT(lookup.IsProperty() && (lookup.type() == FIELD)); + ASSERT(lookup.IsFound() && (lookup.type() == FIELD)); ASSERT(lookup.GetFieldIndex() == Heap::kArgumentsLengthIndex); ASSERT(result->map()->inobject_properties() > Heap::kArgumentsCalleeIndex); @@ -1063,11 +1106,6 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, } { // --- aliased_arguments_boilerplate_ - Handle<Map> old_map(global_context()->arguments_boilerplate()->map()); - Handle<Map> new_map = factory->CopyMapDropTransitions(old_map); - new_map->set_pre_allocated_property_fields(2); - Handle<JSObject> result = factory->NewJSObjectFromMap(new_map); - new_map->set_elements_kind(NON_STRICT_ARGUMENTS_ELEMENTS); // Set up a well-formed parameter map to make assertions happy. Handle<FixedArray> elements = factory->NewFixedArray(2); elements->set_map(heap->non_strict_arguments_elements_map()); @@ -1076,7 +1114,16 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, elements->set(0, *array); array = factory->NewFixedArray(0); elements->set(1, *array); + + Handle<Map> old_map(global_context()->arguments_boilerplate()->map()); + Handle<Map> new_map = factory->CopyMapDropTransitions(old_map); + new_map->set_pre_allocated_property_fields(2); + Handle<JSObject> result = factory->NewJSObjectFromMap(new_map); + // Set elements kind after allocating the object because + // NewJSObjectFromMap assumes a fast elements map. + new_map->set_elements_kind(NON_STRICT_ARGUMENTS_ELEMENTS); result->set_elements(*elements); + ASSERT(result->HasNonStrictArgumentsElements()); global_context()->set_aliased_arguments_boilerplate(*result); } @@ -1085,33 +1132,34 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); // Create the ThrowTypeError functions. - Handle<FixedArray> callee = factory->NewFixedArray(2, TENURED); - Handle<FixedArray> caller = factory->NewFixedArray(2, TENURED); + Handle<AccessorPair> callee = factory->NewAccessorPair(); + Handle<AccessorPair> caller = factory->NewAccessorPair(); Handle<JSFunction> throw_function = GetThrowTypeErrorFunction(); // Install the ThrowTypeError functions. - callee->set(0, *throw_function); - callee->set(1, *throw_function); - caller->set(0, *throw_function); - caller->set(1, *throw_function); + callee->set_getter(*throw_function); + callee->set_setter(*throw_function); + caller->set_getter(*throw_function); + caller->set_setter(*throw_function); // Create the descriptor array for the arguments object. Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(3); + DescriptorArray::WhitenessWitness witness(*descriptors); { // length FieldDescriptor d(*factory->length_symbol(), 0, DONT_ENUM); - descriptors->Set(0, &d); + descriptors->Set(0, &d, witness); } { // callee CallbacksDescriptor d(*factory->callee_symbol(), *callee, attributes); - descriptors->Set(1, &d); + descriptors->Set(1, &d, witness); } { // caller CallbacksDescriptor d(*factory->caller_symbol(), *caller, attributes); - descriptors->Set(2, &d); + descriptors->Set(2, &d, witness); } - descriptors->Sort(); + descriptors->Sort(witness); // Create the map. Allocate one in-object field for length. Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, @@ -1131,14 +1179,15 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, global_context()->set_strict_mode_arguments_boilerplate(*result); // Add length property only for strict mode boilerplate. - SetLocalPropertyNoThrow(result, factory->length_symbol(), - factory->undefined_value(), - DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + result, factory->length_symbol(), + factory->undefined_value(), DONT_ENUM)); #ifdef DEBUG - LookupResult lookup; + LookupResult lookup(isolate); result->LocalLookup(heap->length_symbol(), &lookup); - ASSERT(lookup.IsProperty() && (lookup.type() == FIELD)); + ASSERT(lookup.IsFound() && (lookup.type() == FIELD)); ASSERT(lookup.GetFieldIndex() == Heap::kArgumentsLengthIndex); ASSERT(result->map()->inobject_properties() > Heap::kArgumentsLengthIndex); @@ -1195,6 +1244,14 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, // Initialize the data slot. global_context()->set_data(heap->undefined_value()); + + { + // Initialize the random seed slot. + Handle<ByteArray> zeroed_byte_array( + factory->NewByteArray(kRandomStateSize)); + global_context()->set_random_seed(*zeroed_byte_array); + memset(zeroed_byte_array->GetDataStartAddress(), 0, kRandomStateSize); + } } @@ -1202,12 +1259,26 @@ void Genesis::InitializeExperimentalGlobal() { Handle<JSObject> global = Handle<JSObject>(global_context()->global()); // TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no - // longer need to live behind a flag, so WeakMap gets added to the snapshot. - if (FLAG_harmony_weakmaps) { // -- W e a k M a p - Handle<JSObject> prototype = - factory()->NewJSObject(isolate()->object_function(), TENURED); - InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, - prototype, Builtins::kIllegal, true); + // longer need to live behind a flag, so functions get added to the snapshot. + if (FLAG_harmony_collections) { + { // -- S e t + Handle<JSObject> prototype = + factory()->NewJSObject(isolate()->object_function(), TENURED); + InstallFunction(global, "Set", JS_SET_TYPE, JSSet::kSize, + prototype, Builtins::kIllegal, true); + } + { // -- M a p + Handle<JSObject> prototype = + factory()->NewJSObject(isolate()->object_function(), TENURED); + InstallFunction(global, "Map", JS_MAP_TYPE, JSMap::kSize, + prototype, Builtins::kIllegal, true); + } + { // -- W e a k M a p + Handle<JSObject> prototype = + factory()->NewJSObject(isolate()->object_function(), TENURED); + InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, + prototype, Builtins::kIllegal, true); + } } } @@ -1279,7 +1350,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name, if (cache != NULL) cache->Add(name, function_info); } - // Setup the function context. Conceptually, we should clone the + // Set up the function context. Conceptually, we should clone the // function before overwriting the context but since we're in a // single-threaded environment it is not strictly necessary. ASSERT(top_context->IsGlobalContext()); @@ -1327,6 +1398,8 @@ void Genesis::InstallNativeFunctions() { configure_instance_fun); INSTALL_NATIVE(JSFunction, "GetStackTraceLine", get_stack_trace_line_fun); INSTALL_NATIVE(JSObject, "functionCache", function_cache); + INSTALL_NATIVE(JSFunction, "ToCompletePropertyDescriptor", + to_complete_property_descriptor); } void Genesis::InstallExperimentalNativeFunctions() { @@ -1334,6 +1407,7 @@ void Genesis::InstallExperimentalNativeFunctions() { INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap); INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap); INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap); + INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate); } } @@ -1363,7 +1437,7 @@ bool Genesis::InstallNatives() { builtins->set_global_context(*global_context()); builtins->set_global_receiver(*builtins); - // Setup the 'global' properties of the builtins object. The + // Set up the 'global' properties of the builtins object. The // 'global' property that refers to the global object is the only // way to get from code running in the builtins context to the // global object. @@ -1371,9 +1445,11 @@ bool Genesis::InstallNatives() { static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); Handle<String> global_symbol = factory()->LookupAsciiSymbol("global"); Handle<Object> global_obj(global_context()->global()); - SetLocalPropertyNoThrow(builtins, global_symbol, global_obj, attributes); + CHECK_NOT_EMPTY_HANDLE(isolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + builtins, global_symbol, global_obj, attributes)); - // Setup the reference from the global object to the builtins object. + // Set up the reference from the global object to the builtins object. JSGlobalObject::cast(global_context()->global())->set_builtins(*builtins); // Create a bridge function that has context in the global context. @@ -1536,16 +1612,13 @@ bool Genesis::InstallNatives() { // doesn't inherit from Object.prototype. // To be used only for internal work by builtins. Instances // must not be leaked to user code. - // Only works correctly when called as a constructor. The normal - // Array code uses Array.prototype as prototype when called as - // a function. Handle<JSFunction> array_function = InstallFunction(builtins, "InternalArray", JS_ARRAY_TYPE, JSArray::kSize, isolate()->initial_object_prototype(), - Builtins::kArrayCode, + Builtins::kInternalArrayCode, true); Handle<JSObject> prototype = factory()->NewJSObject(isolate()->object_function(), TENURED); @@ -1555,6 +1628,18 @@ bool Genesis::InstallNatives() { isolate()->builtins()->builtin(Builtins::kArrayConstructCode)); array_function->shared()->DontAdaptArguments(); + // InternalArrays should not use Smi-Only array optimizations. There are too + // many places in the C++ runtime code (e.g. RegEx) that assume that + // elements in InternalArrays can be set to non-Smi values without going + // through a common bottleneck that would make the SMI_ONLY -> FAST_ELEMENT + // transition easy to trap. Moreover, they rarely are smi-only. + MaybeObject* maybe_map = + array_function->initial_map()->CopyDropTransitions(); + Map* new_map; + if (!maybe_map->To<Map>(&new_map)) return false; + new_map->set_elements_kind(FAST_ELEMENTS); + array_function->set_initial_map(new_map); + // Make "length" magic on instances. Handle<DescriptorArray> array_descriptors = factory()->CopyAppendForeignDescriptor( @@ -1565,6 +1650,8 @@ bool Genesis::InstallNatives() { array_function->initial_map()->set_instance_descriptors( *array_descriptors); + + global_context()->set_internal_array_function(*array_function); } if (FLAG_disable_native_files) { @@ -1586,7 +1673,7 @@ bool Genesis::InstallNatives() { InstallNativeFunctions(); // Store the map for the string prototype after the natives has been compiled - // and the String function has been setup. + // and the String function has been set up. Handle<JSFunction> string_function(global_context()->string_function()); ASSERT(JSObject::cast( string_function->initial_map()->prototype())->HasFastProperties()); @@ -1648,15 +1735,15 @@ bool Genesis::InstallNatives() { initial_map->set_prototype(*array_prototype); // Update map with length accessor from Array and add "index" and "input". - Handle<Map> array_map(global_context()->js_array_map()); - Handle<DescriptorArray> array_descriptors( - array_map->instance_descriptors()); - ASSERT_EQ(1, array_descriptors->number_of_descriptors()); - Handle<DescriptorArray> reresult_descriptors = factory()->NewDescriptorArray(3); + DescriptorArray::WhitenessWitness witness(*reresult_descriptors); - reresult_descriptors->CopyFrom(0, *array_descriptors, 0); + JSFunction* array_function = global_context()->array_function(); + Handle<DescriptorArray> array_descriptors( + array_function->initial_map()->instance_descriptors()); + int index = array_descriptors->SearchWithCache(heap()->length_symbol()); + reresult_descriptors->CopyFrom(0, *array_descriptors, index, witness); int enum_index = 0; { @@ -1664,7 +1751,7 @@ bool Genesis::InstallNatives() { JSRegExpResult::kIndexIndex, NONE, enum_index++); - reresult_descriptors->Set(1, &index_field); + reresult_descriptors->Set(1, &index_field, witness); } { @@ -1672,9 +1759,9 @@ bool Genesis::InstallNatives() { JSRegExpResult::kInputIndex, NONE, enum_index++); - reresult_descriptors->Set(2, &input_field); + reresult_descriptors->Set(2, &input_field, witness); } - reresult_descriptors->Sort(); + reresult_descriptors->Sort(witness); initial_map->set_inobject_properties(2); initial_map->set_pre_allocated_property_fields(2); @@ -1701,9 +1788,9 @@ bool Genesis::InstallExperimentalNatives() { "native proxy.js") == 0) { if (!CompileExperimentalBuiltin(isolate(), i)) return false; } - if (FLAG_harmony_weakmaps && + if (FLAG_harmony_collections && strcmp(ExperimentalNatives::GetScriptName(i).start(), - "native weakmap.js") == 0) { + "native collection.js") == 0) { if (!CompileExperimentalBuiltin(isolate(), i)) return false; } } @@ -1821,25 +1908,28 @@ bool Bootstrapper::InstallExtensions(Handle<Context> global_context, void Genesis::InstallSpecialObjects(Handle<Context> global_context) { - Factory* factory = global_context->GetIsolate()->factory(); + Isolate* isolate = global_context->GetIsolate(); + Factory* factory = isolate->factory(); HandleScope scope; - Handle<JSGlobalObject> js_global( - JSGlobalObject::cast(global_context->global())); + Handle<JSGlobalObject> global(JSGlobalObject::cast(global_context->global())); // Expose the natives in global if a name for it is specified. if (FLAG_expose_natives_as != NULL && strlen(FLAG_expose_natives_as) != 0) { - Handle<String> natives_string = - factory->LookupAsciiSymbol(FLAG_expose_natives_as); - SetLocalPropertyNoThrow(js_global, natives_string, - Handle<JSObject>(js_global->builtins()), DONT_ENUM); + Handle<String> natives = factory->LookupAsciiSymbol(FLAG_expose_natives_as); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + global, natives, + Handle<JSObject>(global->builtins()), + DONT_ENUM)); } - Handle<Object> Error = GetProperty(js_global, "Error"); + Handle<Object> Error = GetProperty(global, "Error"); if (Error->IsJSObject()) { Handle<String> name = factory->LookupAsciiSymbol("stackTraceLimit"); - SetLocalPropertyNoThrow(Handle<JSObject>::cast(Error), - name, - Handle<Smi>(Smi::FromInt(FLAG_stack_trace_limit)), - NONE); + Handle<Smi> stack_trace_limit(Smi::FromInt(FLAG_stack_trace_limit)); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + Handle<JSObject>::cast(Error), name, + stack_trace_limit, NONE)); } #ifdef ENABLE_DEBUGGER_SUPPORT @@ -1858,11 +1948,41 @@ void Genesis::InstallSpecialObjects(Handle<Context> global_context) { Handle<String> debug_string = factory->LookupAsciiSymbol(FLAG_expose_debug_as); Handle<Object> global_proxy(debug->debug_context()->global_proxy()); - SetLocalPropertyNoThrow(js_global, debug_string, global_proxy, DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + global, debug_string, global_proxy, DONT_ENUM)); } #endif } +static uint32_t Hash(RegisteredExtension* extension) { + return v8::internal::ComputePointerHash(extension); +} + +static bool MatchRegisteredExtensions(void* key1, void* key2) { + return key1 == key2; +} + +Genesis::ExtensionStates::ExtensionStates() + : allocator_(), + map_(MatchRegisteredExtensions, &allocator_, 8) + {} + +Genesis::ExtensionTraversalState Genesis::ExtensionStates::get_state( + RegisteredExtension* extension) { + i::HashMap::Entry* entry = map_.Lookup(extension, Hash(extension), false); + if (entry == NULL) { + return UNVISITED; + } + return static_cast<ExtensionTraversalState>( + reinterpret_cast<intptr_t>(entry->value)); +} + +void Genesis::ExtensionStates::set_state(RegisteredExtension* extension, + ExtensionTraversalState state) { + map_.Lookup(extension, Hash(extension), true)->value = + reinterpret_cast<void*>(static_cast<intptr_t>(state)); +} bool Genesis::InstallExtensions(Handle<Context> global_context, v8::ExtensionConfiguration* extensions) { @@ -1870,29 +1990,27 @@ bool Genesis::InstallExtensions(Handle<Context> global_context, // effort. (The external API reads 'ignore'-- does that mean // we can break the interface?) - // Clear coloring of extension list - v8::RegisteredExtension* current = v8::RegisteredExtension::first_extension(); - while (current != NULL) { - current->set_state(v8::UNVISITED); - current = current->next(); - } + + ExtensionStates extension_states; // All extensions have state UNVISITED. // Install auto extensions. - current = v8::RegisteredExtension::first_extension(); + v8::RegisteredExtension* current = v8::RegisteredExtension::first_extension(); while (current != NULL) { if (current->extension()->auto_enable()) - InstallExtension(current); + InstallExtension(current, &extension_states); current = current->next(); } - if (FLAG_expose_gc) InstallExtension("v8/gc"); - if (FLAG_expose_externalize_string) InstallExtension("v8/externalize"); + if (FLAG_expose_gc) InstallExtension("v8/gc", &extension_states); + if (FLAG_expose_externalize_string) { + InstallExtension("v8/externalize", &extension_states); + } if (extensions == NULL) return true; // Install required extensions int count = v8::ImplementationUtilities::GetNameCount(extensions); const char** names = v8::ImplementationUtilities::GetNames(extensions); for (int i = 0; i < count; i++) { - if (!InstallExtension(names[i])) + if (!InstallExtension(names[i], &extension_states)) return false; } @@ -1902,7 +2020,8 @@ bool Genesis::InstallExtensions(Handle<Context> global_context, // Installs a named extension. This methods is unoptimized and does // not scale well if we want to support a large number of extensions. -bool Genesis::InstallExtension(const char* name) { +bool Genesis::InstallExtension(const char* name, + ExtensionStates* extension_states) { v8::RegisteredExtension* current = v8::RegisteredExtension::first_extension(); // Loop until we find the relevant extension while (current != NULL) { @@ -1915,42 +2034,52 @@ bool Genesis::InstallExtension(const char* name) { "v8::Context::New()", "Cannot find required extension"); return false; } - return InstallExtension(current); + return InstallExtension(current, extension_states); } -bool Genesis::InstallExtension(v8::RegisteredExtension* current) { +bool Genesis::InstallExtension(v8::RegisteredExtension* current, + ExtensionStates* extension_states) { HandleScope scope; - if (current->state() == v8::INSTALLED) return true; + if (extension_states->get_state(current) == INSTALLED) return true; // The current node has already been visited so there must be a // cycle in the dependency graph; fail. - if (current->state() == v8::VISITED) { + if (extension_states->get_state(current) == VISITED) { v8::Utils::ReportApiFailure( "v8::Context::New()", "Circular extension dependency"); return false; } - ASSERT(current->state() == v8::UNVISITED); - current->set_state(v8::VISITED); + ASSERT(extension_states->get_state(current) == UNVISITED); + extension_states->set_state(current, VISITED); v8::Extension* extension = current->extension(); // Install the extension's dependencies for (int i = 0; i < extension->dependency_count(); i++) { - if (!InstallExtension(extension->dependencies()[i])) return false; + if (!InstallExtension(extension->dependencies()[i], extension_states)) + return false; } Isolate* isolate = Isolate::Current(); - Vector<const char> source = CStrVector(extension->source()); - Handle<String> source_code = isolate->factory()->NewStringFromAscii(source); - bool result = CompileScriptCached(CStrVector(extension->name()), - source_code, - isolate->bootstrapper()->extensions_cache(), - extension, - Handle<Context>(isolate->context()), - false); + Handle<String> source_code = + isolate->factory()->NewExternalStringFromAscii(extension->source()); + bool result = CompileScriptCached( + CStrVector(extension->name()), + source_code, + isolate->bootstrapper()->extensions_cache(), + extension, + Handle<Context>(isolate->context()), + false); ASSERT(isolate->has_pending_exception() != result); if (!result) { + // We print out the name of the extension that fail to install. + // When an error is thrown during bootstrapping we automatically print + // the line number at which this happened to the console in the isolate + // error throwing functionality. + OS::PrintError("Error installing extension '%s'.\n", + current->extension()->name()); isolate->clear_pending_exception(); } - current->set_state(v8::INSTALLED); + extension_states->set_state(current, INSTALLED); + isolate->NotifyExtensionInstalled(); return result; } @@ -1967,7 +2096,9 @@ bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) { builtins->set_javascript_builtin(id, *function); Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(function->shared()); - if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false; + if (!SharedFunctionInfo::EnsureCompiled(shared, CLEAR_EXCEPTION)) { + return false; + } // Set the code object on the function object. function->ReplaceCode(function->shared()->code()); builtins->set_javascript_builtin_code(id, shared->code()); @@ -2035,7 +2166,9 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, Handle<String> key = Handle<String>(descs->GetKey(i)); int index = descs->GetFieldIndex(i); Handle<Object> value = Handle<Object>(from->FastPropertyAt(index)); - SetLocalPropertyNoThrow(to, key, value, details.attributes()); + CHECK_NOT_EMPTY_HANDLE(to->GetIsolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + to, key, value, details.attributes())); break; } case CONSTANT_FUNCTION: { @@ -2043,11 +2176,13 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, Handle<String> key = Handle<String>(descs->GetKey(i)); Handle<JSFunction> fun = Handle<JSFunction>(descs->GetConstantFunction(i)); - SetLocalPropertyNoThrow(to, key, fun, details.attributes()); + CHECK_NOT_EMPTY_HANDLE(to->GetIsolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + to, key, fun, details.attributes())); break; } case CALLBACKS: { - LookupResult result; + LookupResult result(isolate()); to->LocalLookup(descs->GetKey(i), &result); // If the property is already there we skip it if (result.IsProperty()) continue; @@ -2058,7 +2193,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, Handle<Object> callbacks(descs->GetCallbacksObject(i)); PropertyDetails d = PropertyDetails(details.attributes(), CALLBACKS, details.index()); - SetNormalizedProperty(to, key, callbacks, d); + JSObject::SetNormalizedProperty(to, key, callbacks, d); break; } case MAP_TRANSITION: @@ -2085,7 +2220,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, if (properties->IsKey(raw_key)) { ASSERT(raw_key->IsString()); // If the property is already there we skip it. - LookupResult result; + LookupResult result(isolate()); to->LocalLookup(String::cast(raw_key), &result); if (result.IsProperty()) continue; // Set the property. @@ -2095,7 +2230,9 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, value = Handle<Object>(JSGlobalPropertyCell::cast(*value)->value()); } PropertyDetails details = properties->DetailsAt(i); - SetLocalPropertyNoThrow(to, key, value, details.attributes()); + CHECK_NOT_EMPTY_HANDLE(to->GetIsolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + to, key, value, details.attributes())); } } } diff --git a/deps/v8/src/bootstrapper.h b/deps/v8/src/bootstrapper.h index abf61b9fe5..101c2e1b1f 100644 --- a/deps/v8/src/bootstrapper.h +++ b/deps/v8/src/bootstrapper.h @@ -88,7 +88,7 @@ class SourceCodeCache BASE_EMBEDDED { // context. class Bootstrapper { public: - // Requires: Heap::Setup has been called. + // Requires: Heap::SetUp has been called. void Initialize(bool create_heap_objects); void TearDown(); diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index e6a0699f07..1badca7bc5 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -33,6 +33,7 @@ #include "builtins.h" #include "gdb-jit.h" #include "ic-inl.h" +#include "mark-compact.h" #include "vm-state-inl.h" namespace v8 { @@ -183,31 +184,37 @@ BUILTIN(EmptyFunction) { } -BUILTIN(ArrayCodeGeneric) { +static MaybeObject* ArrayCodeGenericCommon(Arguments* args, + Isolate* isolate, + JSFunction* constructor) { Heap* heap = isolate->heap(); isolate->counters()->array_function_runtime()->Increment(); JSArray* array; if (CalledAsConstructor(isolate)) { - array = JSArray::cast(*args.receiver()); + array = JSArray::cast((*args)[0]); + // Initialize elements and length in case later allocations fail so that the + // array object is initialized in a valid state. + array->set_length(Smi::FromInt(0)); + array->set_elements(heap->empty_fixed_array()); + if (!FLAG_smi_only_arrays) { + Context* global_context = isolate->context()->global_context(); + if (array->GetElementsKind() == FAST_SMI_ONLY_ELEMENTS && + !global_context->object_js_array_map()->IsUndefined()) { + array->set_map(Map::cast(global_context->object_js_array_map())); + } + } } else { // Allocate the JS Array - JSFunction* constructor = - isolate->context()->global_context()->array_function(); - Object* obj; - { MaybeObject* maybe_obj = heap->AllocateJSObject(constructor); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - array = JSArray::cast(obj); + MaybeObject* maybe_obj = + heap->AllocateEmptyJSArray(FAST_SMI_ONLY_ELEMENTS); + if (!maybe_obj->To(&array)) return maybe_obj; } - // 'array' now contains the JSArray we should initialize. - ASSERT(array->HasFastElements()); - // Optimize the case where there is one argument and the argument is a // small smi. - if (args.length() == 2) { - Object* obj = args[1]; + if (args->length() == 2) { + Object* obj = (*args)[1]; if (obj->IsSmi()) { int len = Smi::cast(obj)->value(); if (len >= 0 && len < JSObject::kInitialMaxFastElementArray) { @@ -215,7 +222,8 @@ BUILTIN(ArrayCodeGeneric) { { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - array->SetContent(FixedArray::cast(obj)); + MaybeObject* maybe_obj = array->SetContent(FixedArray::cast(obj)); + if (maybe_obj->IsFailure()) return maybe_obj; return array; } } @@ -223,58 +231,82 @@ BUILTIN(ArrayCodeGeneric) { { MaybeObject* maybe_obj = array->Initialize(0); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - return array->SetElementsLength(args[1]); + return array->SetElementsLength((*args)[1]); } // Optimize the case where there are no parameters passed. - if (args.length() == 1) { + if (args->length() == 1) { return array->Initialize(JSArray::kPreallocatedArrayElements); } - // Take the arguments as elements. - int number_of_elements = args.length() - 1; - Smi* len = Smi::FromInt(number_of_elements); - Object* obj; - { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len->value()); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; + // Set length and elements on the array. + int number_of_elements = args->length() - 1; + MaybeObject* maybe_object = + array->EnsureCanContainElements(args, 1, number_of_elements, + ALLOW_CONVERTED_DOUBLE_ELEMENTS); + if (maybe_object->IsFailure()) return maybe_object; + + // Allocate an appropriately typed elements array. + MaybeObject* maybe_elms; + ElementsKind elements_kind = array->GetElementsKind(); + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + maybe_elms = heap->AllocateUninitializedFixedDoubleArray( + number_of_elements); + } else { + maybe_elms = heap->AllocateFixedArrayWithHoles(number_of_elements); } + FixedArrayBase* elms; + if (!maybe_elms->To<FixedArrayBase>(&elms)) return maybe_elms; - AssertNoAllocation no_gc; - FixedArray* elms = FixedArray::cast(obj); - WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); // Fill in the content - for (int index = 0; index < number_of_elements; index++) { - elms->set(index, args[index+1], mode); + switch (array->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: { + FixedArray* smi_elms = FixedArray::cast(elms); + for (int index = 0; index < number_of_elements; index++) { + smi_elms->set(index, (*args)[index+1], SKIP_WRITE_BARRIER); + } + break; + } + case FAST_ELEMENTS: { + AssertNoAllocation no_gc; + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); + FixedArray* object_elms = FixedArray::cast(elms); + for (int index = 0; index < number_of_elements; index++) { + object_elms->set(index, (*args)[index+1], mode); + } + break; + } + case FAST_DOUBLE_ELEMENTS: { + FixedDoubleArray* double_elms = FixedDoubleArray::cast(elms); + for (int index = 0; index < number_of_elements; index++) { + double_elms->set(index, (*args)[index+1]->Number()); + } + break; + } + default: + UNREACHABLE(); + break; } - // Set length and elements on the array. - array->set_elements(FixedArray::cast(obj)); - array->set_length(len); - + array->set_elements(elms); + array->set_length(Smi::FromInt(number_of_elements)); return array; } -MUST_USE_RESULT static MaybeObject* AllocateJSArray(Heap* heap) { - JSFunction* array_function = - heap->isolate()->context()->global_context()->array_function(); - Object* result; - { MaybeObject* maybe_result = heap->AllocateJSObject(array_function); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - return result; +BUILTIN(InternalArrayCodeGeneric) { + return ArrayCodeGenericCommon( + &args, + isolate, + isolate->context()->global_context()->internal_array_function()); } -MUST_USE_RESULT static MaybeObject* AllocateEmptyJSArray(Heap* heap) { - Object* result; - { MaybeObject* maybe_result = AllocateJSArray(heap); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - JSArray* result_array = JSArray::cast(result); - result_array->set_length(Smi::FromInt(0)); - result_array->set_elements(heap->empty_fixed_array()); - return result_array; +BUILTIN(ArrayCodeGeneric) { + return ArrayCodeGenericCommon( + &args, + isolate, + isolate->context()->global_context()->array_function()); } @@ -285,6 +317,7 @@ static void CopyElements(Heap* heap, FixedArray* src, int src_index, int len) { + if (len == 0) return; ASSERT(dst != src); // Use MoveElements instead. ASSERT(dst->map() != HEAP->fixed_cow_array_map()); ASSERT(len > 0); @@ -295,6 +328,7 @@ static void CopyElements(Heap* heap, if (mode == UPDATE_WRITE_BARRIER) { heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len); } + heap->incremental_marking()->RecordWrites(dst); } @@ -305,6 +339,7 @@ static void MoveElements(Heap* heap, FixedArray* src, int src_index, int len) { + if (len == 0) return; ASSERT(dst->map() != HEAP->fixed_cow_array_map()); memmove(dst->data_start() + dst_index, src->data_start() + src_index, @@ -313,6 +348,7 @@ static void MoveElements(Heap* heap, if (mode == UPDATE_WRITE_BARRIER) { heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len); } + heap->incremental_marking()->RecordWrites(dst); } @@ -358,6 +394,14 @@ static FixedArray* LeftTrimFixedArray(Heap* heap, former_start[to_trim] = heap->fixed_array_map(); former_start[to_trim + 1] = Smi::FromInt(len - to_trim); + // Maintain marking consistency for HeapObjectIterator and + // IncrementalMarking. + int size_delta = to_trim * kPointerSize; + if (heap->marking()->TransferMark(elms->address(), + elms->address() + size_delta)) { + MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta); + } + return FixedArray::cast(HeapObject::FromAddress( elms->address() + to_trim * kPointerSize)); } @@ -369,9 +413,6 @@ static bool ArrayPrototypeHasNoElements(Heap* heap, // This method depends on non writability of Object and Array prototype // fields. if (array_proto->elements() != heap->empty_fixed_array()) return false; - // Hidden prototype - array_proto = JSObject::cast(array_proto->GetPrototype()); - ASSERT(array_proto->elements() == heap->empty_fixed_array()); // Object.prototype Object* proto = array_proto->GetPrototype(); if (proto == heap->null_value()) return false; @@ -384,20 +425,43 @@ static bool ArrayPrototypeHasNoElements(Heap* heap, MUST_USE_RESULT static inline MaybeObject* EnsureJSArrayWithWritableFastElements( - Heap* heap, Object* receiver) { + Heap* heap, Object* receiver, Arguments* args, int first_added_arg) { if (!receiver->IsJSArray()) return NULL; JSArray* array = JSArray::cast(receiver); HeapObject* elms = array->elements(); - if (elms->map() == heap->fixed_array_map()) return elms; - if (elms->map() == heap->fixed_cow_array_map()) { - return array->EnsureWritableFastElements(); + Map* map = elms->map(); + if (map == heap->fixed_array_map()) { + if (args == NULL || !array->HasFastSmiOnlyElements()) { + return elms; + } + } else if (map == heap->fixed_cow_array_map()) { + MaybeObject* maybe_writable_result = array->EnsureWritableFastElements(); + if (args == NULL || !array->HasFastSmiOnlyElements() || + maybe_writable_result->IsFailure()) { + return maybe_writable_result; + } + } else { + return NULL; } - return NULL; + + // Need to ensure that the arguments passed in args can be contained in + // the array. + int args_length = args->length(); + if (first_added_arg >= args_length) return array->elements(); + + MaybeObject* maybe_array = array->EnsureCanContainElements( + args, + first_added_arg, + args_length - first_added_arg, + DONT_ALLOW_DOUBLE_ELEMENTS); + if (maybe_array->IsFailure()) return maybe_array; + return array->elements(); } static inline bool IsJSArrayFastElementMovingAllowed(Heap* heap, JSArray* receiver) { + if (!FLAG_clever_optimizations) return false; Context* global_context = heap->isolate()->context()->global_context(); JSObject* array_proto = JSObject::cast(global_context->array_function()->prototype()); @@ -413,20 +477,18 @@ MUST_USE_RESULT static MaybeObject* CallJsBuiltin( HandleScope handleScope(isolate); Handle<Object> js_builtin = - GetProperty(Handle<JSObject>( - isolate->global_context()->builtins()), - name); - ASSERT(js_builtin->IsJSFunction()); - Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin)); - ScopedVector<Object**> argv(args.length() - 1); - int n_args = args.length() - 1; - for (int i = 0; i < n_args; i++) { - argv[i] = args.at<Object>(i + 1).location(); + GetProperty(Handle<JSObject>(isolate->global_context()->builtins()), + name); + Handle<JSFunction> function = Handle<JSFunction>::cast(js_builtin); + int argc = args.length() - 1; + ScopedVector<Handle<Object> > argv(argc); + for (int i = 0; i < argc; ++i) { + argv[i] = args.at<Object>(i + 1); } - bool pending_exception = false; + bool pending_exception; Handle<Object> result = Execution::Call(function, args.receiver(), - n_args, + argc, argv.start(), &pending_exception); if (pending_exception) return Failure::Exception(); @@ -439,7 +501,7 @@ BUILTIN(ArrayPush) { Object* receiver = *args.receiver(); Object* elms_obj; { MaybeObject* maybe_elms_obj = - EnsureJSArrayWithWritableFastElements(heap, receiver); + EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 1); if (maybe_elms_obj == NULL) { return CallJsBuiltin(isolate, "ArrayPush", args); } @@ -469,13 +531,10 @@ BUILTIN(ArrayPush) { FixedArray* new_elms = FixedArray::cast(obj); AssertNoAllocation no_gc; - if (len > 0) { - CopyElements(heap, &no_gc, new_elms, 0, elms, 0, len); - } + CopyElements(heap, &no_gc, new_elms, 0, elms, 0, len); FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; - array->set_elements(elms); } // Add the provided values. @@ -485,6 +544,10 @@ BUILTIN(ArrayPush) { elms->set(index + len, args[index + 1], mode); } + if (elms != array->elements()) { + array->set_elements(elms); + } + // Set the length. array->set_length(Smi::FromInt(new_length)); return Smi::FromInt(new_length); @@ -496,7 +559,7 @@ BUILTIN(ArrayPop) { Object* receiver = *args.receiver(); Object* elms_obj; { MaybeObject* maybe_elms_obj = - EnsureJSArrayWithWritableFastElements(heap, receiver); + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0); if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArrayPop", args); if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj; } @@ -529,7 +592,7 @@ BUILTIN(ArrayShift) { Object* receiver = *args.receiver(); Object* elms_obj; { MaybeObject* maybe_elms_obj = - EnsureJSArrayWithWritableFastElements(heap, receiver); + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0); if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArrayShift", args); if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj; @@ -539,7 +602,7 @@ BUILTIN(ArrayShift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); if (len == 0) return heap->undefined_value(); @@ -551,9 +614,7 @@ BUILTIN(ArrayShift) { } if (!heap->lo_space()->Contains(elms)) { - // As elms still in the same space they used to be, - // there is no need to update region dirty mark. - array->set_elements(LeftTrimFixedArray(heap, elms, 1), SKIP_WRITE_BARRIER); + array->set_elements(LeftTrimFixedArray(heap, elms, 1)); } else { // Shift the elements. AssertNoAllocation no_gc; @@ -573,7 +634,7 @@ BUILTIN(ArrayUnshift) { Object* receiver = *args.receiver(); Object* elms_obj; { MaybeObject* maybe_elms_obj = - EnsureJSArrayWithWritableFastElements(heap, receiver); + EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0); if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArrayUnshift", args); if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj; @@ -583,7 +644,7 @@ BUILTIN(ArrayUnshift) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); int to_add = args.length() - 1; @@ -592,6 +653,11 @@ BUILTIN(ArrayUnshift) { // we should never hit this case. ASSERT(to_add <= (Smi::kMaxValue - len)); + MaybeObject* maybe_object = + array->EnsureCanContainElements(&args, 1, to_add, + DONT_ALLOW_DOUBLE_ELEMENTS); + if (maybe_object->IsFailure()) return maybe_object; + if (new_length > elms->length()) { // New backing storage is needed. int capacity = new_length + (new_length >> 1) + 16; @@ -600,13 +666,9 @@ BUILTIN(ArrayUnshift) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* new_elms = FixedArray::cast(obj); - AssertNoAllocation no_gc; - if (len > 0) { - CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len); - } + CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len); FillWithHoles(heap, new_elms, new_length, capacity); - elms = new_elms; array->set_elements(elms); } else { @@ -634,7 +696,7 @@ BUILTIN(ArraySlice) { int len = -1; if (receiver->IsJSArray()) { JSArray* array = JSArray::cast(receiver); - if (!array->HasFastElements() || + if (!array->HasFastTypeElements() || !IsJSArrayFastElementMovingAllowed(heap, array)) { return CallJsBuiltin(isolate, "ArraySlice", args); } @@ -650,7 +712,7 @@ BUILTIN(ArraySlice) { bool is_arguments_object_with_fast_elements = receiver->IsJSObject() && JSObject::cast(receiver)->map() == arguments_map - && JSObject::cast(receiver)->HasFastElements(); + && JSObject::cast(receiver)->HasFastTypeElements(); if (!is_arguments_object_with_fast_elements) { return CallJsBuiltin(isolate, "ArraySlice", args); } @@ -703,32 +765,22 @@ BUILTIN(ArraySlice) { int final = (relative_end < 0) ? Max(len + relative_end, 0) : Min(relative_end, len); - // Calculate the length of result array. - int result_len = final - k; - if (result_len <= 0) { - return AllocateEmptyJSArray(heap); - } + ElementsKind elements_kind = JSObject::cast(receiver)->GetElementsKind(); - Object* result; - { MaybeObject* maybe_result = AllocateJSArray(heap); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - JSArray* result_array = JSArray::cast(result); + // Calculate the length of result array. + int result_len = Max(final - k, 0); - { MaybeObject* maybe_result = - heap->AllocateUninitializedFixedArray(result_len); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - FixedArray* result_elms = FixedArray::cast(result); + MaybeObject* maybe_array = + heap->AllocateJSArrayAndStorage(elements_kind, + result_len, + result_len); + JSArray* result_array; + if (!maybe_array->To(&result_array)) return maybe_array; AssertNoAllocation no_gc; - CopyElements(heap, &no_gc, result_elms, 0, elms, k, result_len); - - // Set elements. - result_array->set_elements(result_elms); + CopyElements(heap, &no_gc, FixedArray::cast(result_array->elements()), 0, + elms, k, result_len); - // Set the length. - result_array->set_length(Smi::FromInt(result_len)); return result_array; } @@ -738,7 +790,7 @@ BUILTIN(ArraySplice) { Object* receiver = *args.receiver(); Object* elms_obj; { MaybeObject* maybe_elms_obj = - EnsureJSArrayWithWritableFastElements(heap, receiver); + EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 3); if (maybe_elms_obj == NULL) return CallJsBuiltin(isolate, "ArraySplice", args); if (!maybe_elms_obj->ToObject(&elms_obj)) return maybe_elms_obj; @@ -748,7 +800,7 @@ BUILTIN(ArraySplice) { } FixedArray* elms = FixedArray::cast(elms_obj); JSArray* array = JSArray::cast(receiver); - ASSERT(array->HasFastElements()); + ASSERT(array->HasFastTypeElements()); int len = Smi::cast(array->length())->value(); @@ -789,45 +841,28 @@ BUILTIN(ArraySplice) { } JSArray* result_array = NULL; - if (actual_delete_count == 0) { - Object* result; - { MaybeObject* maybe_result = AllocateEmptyJSArray(heap); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - result_array = JSArray::cast(result); - } else { - // Allocate result array. - Object* result; - { MaybeObject* maybe_result = AllocateJSArray(heap); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - result_array = JSArray::cast(result); - - { MaybeObject* maybe_result = - heap->AllocateUninitializedFixedArray(actual_delete_count); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - FixedArray* result_elms = FixedArray::cast(result); + ElementsKind elements_kind = + JSObject::cast(receiver)->GetElementsKind(); + MaybeObject* maybe_array = + heap->AllocateJSArrayAndStorage(elements_kind, + actual_delete_count, + actual_delete_count); + if (!maybe_array->To(&result_array)) return maybe_array; + { AssertNoAllocation no_gc; // Fill newly created array. CopyElements(heap, &no_gc, - result_elms, 0, + FixedArray::cast(result_array->elements()), 0, elms, actual_start, actual_delete_count); - - // Set elements. - result_array->set_elements(result_elms); - - // Set the length. - result_array->set_length(Smi::FromInt(actual_delete_count)); } int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0; - int new_length = len - actual_delete_count + item_count; + bool elms_changed = false; if (item_count < actual_delete_count) { // Shrink the array. const bool trim_array = !heap->lo_space()->Contains(elms) && @@ -836,13 +871,14 @@ BUILTIN(ArraySplice) { if (trim_array) { const int delta = actual_delete_count - item_count; - if (actual_start > 0) { + { AssertNoAllocation no_gc; MoveElements(heap, &no_gc, elms, delta, elms, 0, actual_start); } elms = LeftTrimFixedArray(heap, elms, delta); - array->set_elements(elms, SKIP_WRITE_BARRIER); + + elms_changed = true; } else { AssertNoAllocation no_gc; MoveElements(heap, &no_gc, @@ -867,22 +903,21 @@ BUILTIN(ArraySplice) { } FixedArray* new_elms = FixedArray::cast(obj); - AssertNoAllocation no_gc; - // Copy the part before actual_start as is. - if (actual_start > 0) { + { + AssertNoAllocation no_gc; + // Copy the part before actual_start as is. CopyElements(heap, &no_gc, new_elms, 0, elms, 0, actual_start); - } - const int to_copy = len - actual_delete_count - actual_start; - if (to_copy > 0) { + const int to_copy = len - actual_delete_count - actual_start; CopyElements(heap, &no_gc, new_elms, actual_start + item_count, elms, actual_start + actual_delete_count, to_copy); } + FillWithHoles(heap, new_elms, new_length, capacity); elms = new_elms; - array->set_elements(elms); + elms_changed = true; } else { AssertNoAllocation no_gc; MoveElements(heap, &no_gc, @@ -898,6 +933,10 @@ BUILTIN(ArraySplice) { elms->set(k, args[3 + k - actual_start], mode); } + if (elms_changed) { + array->set_elements(elms); + } + // Set the length. array->set_length(Smi::FromInt(new_length)); @@ -918,9 +957,10 @@ BUILTIN(ArrayConcat) { // and calculating total length. int n_arguments = args.length(); int result_len = 0; + ElementsKind elements_kind = FAST_SMI_ONLY_ELEMENTS; for (int i = 0; i < n_arguments; i++) { Object* arg = args[i]; - if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastElements() + if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastTypeElements() || JSArray::cast(arg)->GetPrototype() != array_proto) { return CallJsBuiltin(isolate, "ArrayConcat", args); } @@ -937,43 +977,34 @@ BUILTIN(ArrayConcat) { if (result_len > FixedArray::kMaxLength) { return CallJsBuiltin(isolate, "ArrayConcat", args); } - } - if (result_len == 0) { - return AllocateEmptyJSArray(heap); + if (!JSArray::cast(arg)->HasFastSmiOnlyElements()) { + elements_kind = FAST_ELEMENTS; + } } // Allocate result. - Object* result; - { MaybeObject* maybe_result = AllocateJSArray(heap); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - JSArray* result_array = JSArray::cast(result); - - { MaybeObject* maybe_result = - heap->AllocateUninitializedFixedArray(result_len); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - FixedArray* result_elms = FixedArray::cast(result); + JSArray* result_array; + MaybeObject* maybe_array = + heap->AllocateJSArrayAndStorage(elements_kind, + result_len, + result_len); + if (!maybe_array->To(&result_array)) return maybe_array; + if (result_len == 0) return result_array; // Copy data. AssertNoAllocation no_gc; int start_pos = 0; + FixedArray* result_elms(FixedArray::cast(result_array->elements())); for (int i = 0; i < n_arguments; i++) { JSArray* array = JSArray::cast(args[i]); int len = Smi::cast(array->length())->value(); - if (len > 0) { - FixedArray* elms = FixedArray::cast(array->elements()); - CopyElements(heap, &no_gc, result_elms, start_pos, elms, 0, len); - start_pos += len; - } + FixedArray* elms = FixedArray::cast(array->elements()); + CopyElements(heap, &no_gc, result_elms, start_pos, elms, 0, len); + start_pos += len; } ASSERT(start_pos == result_len); - // Set the length and elements. - result_array->set_length(Smi::FromInt(result_len)); - result_array->set_elements(result_elms); - return result_array; } @@ -1448,6 +1479,14 @@ static void Generate_KeyedStoreIC_NonStrictArguments(MacroAssembler* masm) { KeyedStoreIC::GenerateNonStrictArguments(masm); } +static void Generate_TransitionElementsSmiToDouble(MacroAssembler* masm) { + KeyedStoreIC::GenerateTransitionElementsSmiToDouble(masm); +} + +static void Generate_TransitionElementsDoubleToObject(MacroAssembler* masm) { + KeyedStoreIC::GenerateTransitionElementsDoubleToObject(masm); +} + #ifdef ENABLE_DEBUGGER_SUPPORT static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) { Debug::GenerateLoadICDebugBreak(masm); @@ -1469,18 +1508,30 @@ static void Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) { } -static void Generate_ConstructCall_DebugBreak(MacroAssembler* masm) { - Debug::GenerateConstructCallDebugBreak(masm); +static void Generate_Return_DebugBreak(MacroAssembler* masm) { + Debug::GenerateReturnDebugBreak(masm); } -static void Generate_Return_DebugBreak(MacroAssembler* masm) { - Debug::GenerateReturnDebugBreak(masm); +static void Generate_CallFunctionStub_DebugBreak(MacroAssembler* masm) { + Debug::GenerateCallFunctionStubDebugBreak(masm); +} + + +static void Generate_CallFunctionStub_Recording_DebugBreak( + MacroAssembler* masm) { + Debug::GenerateCallFunctionStubRecordDebugBreak(masm); +} + + +static void Generate_CallConstructStub_DebugBreak(MacroAssembler* masm) { + Debug::GenerateCallConstructStubDebugBreak(masm); } -static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) { - Debug::GenerateStubNoRegistersDebugBreak(masm); +static void Generate_CallConstructStub_Recording_DebugBreak( + MacroAssembler* masm) { + Debug::GenerateCallConstructStubRecordDebugBreak(masm); } @@ -1596,7 +1647,7 @@ void Builtins::InitBuiltinFunctionTable() { #undef DEF_FUNCTION_PTR_A } -void Builtins::Setup(bool create_heap_objects) { +void Builtins::SetUp(bool create_heap_objects) { ASSERT(!initialized_); Isolate* isolate = Isolate::Current(); Heap* heap = isolate->heap(); @@ -1607,20 +1658,22 @@ void Builtins::Setup(bool create_heap_objects) { const BuiltinDesc* functions = BuiltinFunctionTable::functions(); // For now we generate builtin adaptor code into a stack-allocated - // buffer, before copying it into individual code objects. - byte buffer[4*KB]; + // buffer, before copying it into individual code objects. Be careful + // with alignment, some platforms don't like unaligned code. + union { int force_alignment; byte buffer[4*KB]; } u; // Traverse the list of builtins and generate an adaptor in a // separate code object for each one. for (int i = 0; i < builtin_count; i++) { if (create_heap_objects) { - MacroAssembler masm(isolate, buffer, sizeof buffer); + MacroAssembler masm(isolate, u.buffer, sizeof u.buffer); // Generate the code/adaptor. typedef void (*Generator)(MacroAssembler*, int, BuiltinExtraArguments); Generator g = FUNCTION_CAST<Generator>(functions[i].generator); // We pass all arguments to the generator, but it may not use all of // them. This works because the first arguments are on top of the // stack. + ASSERT(!masm.has_frame()); g(&masm, functions[i].name, functions[i].extra_args); // Move the code into the object heap. CodeDesc desc; diff --git a/deps/v8/src/builtins.h b/deps/v8/src/builtins.h index 31090d3a08..f079139d45 100644 --- a/deps/v8/src/builtins.h +++ b/deps/v8/src/builtins.h @@ -44,6 +44,7 @@ enum BuiltinExtraArguments { \ V(EmptyFunction, NO_EXTRA_ARGUMENTS) \ \ + V(InternalArrayCodeGeneric, NO_EXTRA_ARGUMENTS) \ V(ArrayCodeGeneric, NO_EXTRA_ARGUMENTS) \ \ V(ArrayPush, NO_EXTRA_ARGUMENTS) \ @@ -66,8 +67,6 @@ enum BuiltinExtraArguments { #define BUILTIN_LIST_A(V) \ V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ - V(JSConstructCall, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \ @@ -167,6 +166,10 @@ enum BuiltinExtraArguments { kStrictMode) \ V(KeyedStoreIC_NonStrictArguments, KEYED_STORE_IC, MEGAMORPHIC, \ Code::kNoExtraICState) \ + V(TransitionElementsSmiToDouble, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(TransitionElementsDoubleToObject, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ \ /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ V(FunctionCall, BUILTIN, UNINITIALIZED, \ @@ -174,6 +177,8 @@ enum BuiltinExtraArguments { V(FunctionApply, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ \ + V(InternalArrayCode, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ V(ArrayCode, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ V(ArrayConstructCode, BUILTIN, UNINITIALIZED, \ @@ -188,27 +193,31 @@ enum BuiltinExtraArguments { #ifdef ENABLE_DEBUGGER_SUPPORT // Define list of builtins used by the debugger implemented in assembly. -#define BUILTIN_LIST_DEBUG_A(V) \ - V(Return_DebugBreak, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(ConstructCall_DebugBreak, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(StubNoRegisters_DebugBreak, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(LoadIC_DebugBreak, LOAD_IC, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(Slot_DebugBreak, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(PlainReturn_LiveEdit, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) \ - V(FrameDropper_LiveEdit, BUILTIN, DEBUG_BREAK, \ - Code::kNoExtraICState) +#define BUILTIN_LIST_DEBUG_A(V) \ + V(Return_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(CallFunctionStub_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(CallFunctionStub_Recording_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(CallConstructStub_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(CallConstructStub_Recording_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(LoadIC_DebugBreak, LOAD_IC, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(Slot_DebugBreak, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(PlainReturn_LiveEdit, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) \ + V(FrameDropper_LiveEdit, BUILTIN, DEBUG_BREAK, \ + Code::kNoExtraICState) #else #define BUILTIN_LIST_DEBUG_A(V) #endif @@ -234,7 +243,6 @@ enum BuiltinExtraArguments { V(DELETE, 2) \ V(IN, 1) \ V(INSTANCE_OF, 1) \ - V(GET_KEYS, 0) \ V(FILTER_KEY, 1) \ V(CALL_NON_FUNCTION, 0) \ V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \ @@ -259,7 +267,7 @@ class Builtins { // Generate all builtin code objects. Should be called once during // isolate initialization. - void Setup(bool create_heap_objects); + void SetUp(bool create_heap_objects); void TearDown(); // Garbage collection support. @@ -340,7 +348,6 @@ class Builtins { static void Generate_Adaptor(MacroAssembler* masm, CFunctionId id, BuiltinExtraArguments extra_args); - static void Generate_JSConstructCall(MacroAssembler* masm); static void Generate_JSConstructStubCountdown(MacroAssembler* masm); static void Generate_JSConstructStubGeneric(MacroAssembler* masm); static void Generate_JSConstructStubApi(MacroAssembler* masm); @@ -356,6 +363,7 @@ class Builtins { static void Generate_FunctionCall(MacroAssembler* masm); static void Generate_FunctionApply(MacroAssembler* masm); + static void Generate_InternalArrayCode(MacroAssembler* masm); static void Generate_ArrayCode(MacroAssembler* masm); static void Generate_ArrayConstructCode(MacroAssembler* masm); diff --git a/deps/v8/src/bytecodes-irregexp.h b/deps/v8/src/bytecodes-irregexp.h index 93218ea9f1..b13efb36f8 100644 --- a/deps/v8/src/bytecodes-irregexp.h +++ b/deps/v8/src/bytecodes-irregexp.h @@ -1,4 +1,4 @@ -// Copyright 2008-2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -33,12 +33,12 @@ namespace v8 { namespace internal { -static const int BYTECODE_MASK = 0xff; +const int BYTECODE_MASK = 0xff; // The first argument is packed in with the byte code in one word, but so it // has 24 bits, but it can be positive and negative so only use 23 bits for // positive values. -static const unsigned int MAX_FIRST_ARG = 0x7fffffu; -static const int BYTECODE_SHIFT = 8; +const unsigned int MAX_FIRST_ARG = 0x7fffffu; +const int BYTECODE_SHIFT = 8; #define BYTECODE_ITERATOR(V) \ V(BREAK, 0, 4) /* bc8 */ \ diff --git a/deps/v8/src/cached-powers.cc b/deps/v8/src/cached-powers.cc index 30a67a661b..9241d26582 100644 --- a/deps/v8/src/cached-powers.cc +++ b/deps/v8/src/cached-powers.cc @@ -134,14 +134,12 @@ static const CachedPower kCachedPowers[] = { }; static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); -static const int kCachedPowersOffset = -kCachedPowers[0].decimal_exponent; +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) -const int PowersOfTenCache::kDecimalExponentDistance = - kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent; -const int PowersOfTenCache::kMinDecimalExponent = - kCachedPowers[0].decimal_exponent; -const int PowersOfTenCache::kMaxDecimalExponent = - kCachedPowers[kCachedPowersLength - 1].decimal_exponent; +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( int min_exponent, diff --git a/deps/v8/src/char-predicates-inl.h b/deps/v8/src/char-predicates-inl.h index 0dfc80d0b8..1a89ef3b11 100644 --- a/deps/v8/src/char-predicates-inl.h +++ b/deps/v8/src/char-predicates-inl.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -52,7 +52,7 @@ inline bool IsLineFeed(uc32 c) { } -static inline bool IsInRange(int value, int lower_limit, int higher_limit) { +inline bool IsInRange(int value, int lower_limit, int higher_limit) { ASSERT(lower_limit <= higher_limit); return static_cast<unsigned int>(value - lower_limit) <= static_cast<unsigned int>(higher_limit - lower_limit); diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index 2f359f6cd8..608aa14806 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -51,26 +51,20 @@ extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); #endif -// Used by the CHECK macro -- should not be called directly. -static inline void CheckHelper(const char* file, - int line, - const char* source, - bool condition) { - if (!condition) - V8_Fatal(file, line, "CHECK(%s) failed", source); -} - - // The CHECK macro checks that the given condition is true; if not, it // prints a message to stderr and aborts. -#define CHECK(condition) CheckHelper(__FILE__, __LINE__, #condition, condition) +#define CHECK(condition) do { \ + if (!(condition)) { \ + V8_Fatal(__FILE__, __LINE__, "CHECK(%s) failed", #condition); \ + } \ + } while (0) // Helper function used by the CHECK_EQ function when given int // arguments. Should not be called directly. -static inline void CheckEqualsHelper(const char* file, int line, - const char* expected_source, int expected, - const char* value_source, int value) { +inline void CheckEqualsHelper(const char* file, int line, + const char* expected_source, int expected, + const char* value_source, int value) { if (expected != value) { V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# Expected: %i\n# Found: %i", @@ -81,11 +75,11 @@ static inline void CheckEqualsHelper(const char* file, int line, // Helper function used by the CHECK_EQ function when given int64_t // arguments. Should not be called directly. -static inline void CheckEqualsHelper(const char* file, int line, - const char* expected_source, - int64_t expected, - const char* value_source, - int64_t value) { +inline void CheckEqualsHelper(const char* file, int line, + const char* expected_source, + int64_t expected, + const char* value_source, + int64_t value) { if (expected != value) { // Print int64_t values in hex, as two int32s, // to avoid platform-dependencies. @@ -103,12 +97,12 @@ static inline void CheckEqualsHelper(const char* file, int line, // Helper function used by the CHECK_NE function when given int // arguments. Should not be called directly. -static inline void CheckNonEqualsHelper(const char* file, - int line, - const char* unexpected_source, - int unexpected, - const char* value_source, - int value) { +inline void CheckNonEqualsHelper(const char* file, + int line, + const char* unexpected_source, + int unexpected, + const char* value_source, + int value) { if (unexpected == value) { V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %i", unexpected_source, value_source, value); @@ -118,12 +112,12 @@ static inline void CheckNonEqualsHelper(const char* file, // Helper function used by the CHECK function when given string // arguments. Should not be called directly. -static inline void CheckEqualsHelper(const char* file, - int line, - const char* expected_source, - const char* expected, - const char* value_source, - const char* value) { +inline void CheckEqualsHelper(const char* file, + int line, + const char* expected_source, + const char* expected, + const char* value_source, + const char* value) { if ((expected == NULL && value != NULL) || (expected != NULL && value == NULL) || (expected != NULL && value != NULL && strcmp(expected, value) != 0)) { @@ -134,12 +128,12 @@ static inline void CheckEqualsHelper(const char* file, } -static inline void CheckNonEqualsHelper(const char* file, - int line, - const char* expected_source, - const char* expected, - const char* value_source, - const char* value) { +inline void CheckNonEqualsHelper(const char* file, + int line, + const char* expected_source, + const char* expected, + const char* value_source, + const char* value) { if (expected == value || (expected != NULL && value != NULL && strcmp(expected, value) == 0)) { V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %s", @@ -150,12 +144,12 @@ static inline void CheckNonEqualsHelper(const char* file, // Helper function used by the CHECK function when given pointer // arguments. Should not be called directly. -static inline void CheckEqualsHelper(const char* file, - int line, - const char* expected_source, - const void* expected, - const char* value_source, - const void* value) { +inline void CheckEqualsHelper(const char* file, + int line, + const char* expected_source, + const void* expected, + const char* value_source, + const void* value) { if (expected != value) { V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# Expected: %p\n# Found: %p", @@ -165,12 +159,12 @@ static inline void CheckEqualsHelper(const char* file, } -static inline void CheckNonEqualsHelper(const char* file, - int line, - const char* expected_source, - const void* expected, - const char* value_source, - const void* value) { +inline void CheckNonEqualsHelper(const char* file, + int line, + const char* expected_source, + const void* expected, + const char* value_source, + const void* value) { if (expected == value) { V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p", expected_source, value_source, value); @@ -180,12 +174,12 @@ static inline void CheckNonEqualsHelper(const char* file, // Helper function used by the CHECK function when given floating // point arguments. Should not be called directly. -static inline void CheckEqualsHelper(const char* file, - int line, - const char* expected_source, - double expected, - const char* value_source, - double value) { +inline void CheckEqualsHelper(const char* file, + int line, + const char* expected_source, + double expected, + const char* value_source, + double value) { // Force values to 64 bit memory to truncate 80 bit precision on IA32. volatile double* exp = new double[1]; *exp = expected; @@ -201,12 +195,12 @@ static inline void CheckEqualsHelper(const char* file, } -static inline void CheckNonEqualsHelper(const char* file, - int line, - const char* expected_source, - double expected, - const char* value_source, - double value) { +inline void CheckNonEqualsHelper(const char* file, + int line, + const char* expected_source, + double expected, + const char* value_source, + double value) { // Force values to 64 bit memory to truncate 80 bit precision on IA32. volatile double* exp = new double[1]; *exp = expected; @@ -257,25 +251,22 @@ template <int> class StaticAssertionHelper { }; SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__) -namespace v8 { namespace internal { - -bool EnableSlowAsserts(); +extern bool FLAG_enable_slow_asserts; -} } // namespace v8::internal // The ASSERT macro is equivalent to CHECK except that it only // generates code in debug builds. #ifdef DEBUG -#define ASSERT_RESULT(expr) CHECK(expr) -#define ASSERT(condition) CHECK(condition) -#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2) -#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2) -#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2) -#define ASSERT_LT(v1, v2) CHECK_LT(v1, v2) -#define ASSERT_LE(v1, v2) CHECK_LE(v1, v2) -#define SLOW_ASSERT(condition) if (EnableSlowAsserts()) CHECK(condition) +#define ASSERT_RESULT(expr) CHECK(expr) +#define ASSERT(condition) CHECK(condition) +#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2) +#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2) +#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2) +#define ASSERT_LT(v1, v2) CHECK_LT(v1, v2) +#define ASSERT_LE(v1, v2) CHECK_LE(v1, v2) +#define SLOW_ASSERT(condition) CHECK(!FLAG_enable_slow_asserts || (condition)) #else -#define ASSERT_RESULT(expr) (expr) +#define ASSERT_RESULT(expr) (expr) #define ASSERT(condition) ((void) 0) #define ASSERT_EQ(v1, v2) ((void) 0) #define ASSERT_NE(v1, v2) ((void) 0) diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 724445e849..dd1cc5e47a 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -52,11 +52,12 @@ void CodeStub::GenerateCode(MacroAssembler* masm) { // Update the static counter each time a new code stub is generated. masm->isolate()->counters()->code_stubs()->Increment(); - // Nested stubs are not allowed for leafs. - AllowStubCallsScope allow_scope(masm, AllowsStubCalls()); + // Nested stubs are not allowed for leaves. + AllowStubCallsScope allow_scope(masm, false); // Generate the code for the stub. masm->set_generating_stub(true); + NoCurrentFrameScope scope(masm); Generate(masm); } @@ -100,7 +101,14 @@ Handle<Code> CodeStub::GetCode() { Factory* factory = isolate->factory(); Heap* heap = isolate->heap(); Code* code; - if (!FindCodeInCache(&code)) { + if (UseSpecialCache() + ? FindCodeInSpecialCache(&code) + : FindCodeInCache(&code)) { + ASSERT(IsPregenerated() == code->is_pregenerated()); + return Handle<Code>(code); + } + + { HandleScope scope(isolate); // Generate the new code. @@ -118,61 +126,28 @@ Handle<Code> CodeStub::GetCode() { Handle<Code> new_object = factory->NewCode( desc, flags, masm.CodeObject(), NeedsImmovableCode()); RecordCodeGeneration(*new_object, &masm); - FinishCode(*new_object); - - // Update the dictionary and the root in Heap. - Handle<UnseededNumberDictionary> dict = - factory->DictionaryAtNumberPut( - Handle<UnseededNumberDictionary>(heap->code_stubs()), - GetKey(), - new_object); - heap->public_set_code_stubs(*dict); - + FinishCode(new_object); + + if (UseSpecialCache()) { + AddToSpecialCache(new_object); + } else { + // Update the dictionary and the root in Heap. + Handle<UnseededNumberDictionary> dict = + factory->DictionaryAtNumberPut( + Handle<UnseededNumberDictionary>(heap->code_stubs()), + GetKey(), + new_object); + heap->public_set_code_stubs(*dict); + } code = *new_object; } + Activate(code); ASSERT(!NeedsImmovableCode() || heap->lo_space()->Contains(code)); return Handle<Code>(code, isolate); } -MaybeObject* CodeStub::TryGetCode() { - Code* code; - if (!FindCodeInCache(&code)) { - // Generate the new code. - MacroAssembler masm(Isolate::Current(), NULL, 256); - GenerateCode(&masm); - Heap* heap = masm.isolate()->heap(); - - // Create the code object. - CodeDesc desc; - masm.GetCode(&desc); - - // Try to copy the generated code into a heap object. - Code::Flags flags = Code::ComputeFlags( - static_cast<Code::Kind>(GetCodeKind()), - GetICState()); - Object* new_object; - { MaybeObject* maybe_new_object = - heap->CreateCode(desc, flags, masm.CodeObject()); - if (!maybe_new_object->ToObject(&new_object)) return maybe_new_object; - } - code = Code::cast(new_object); - RecordCodeGeneration(code, &masm); - FinishCode(code); - - // Try to update the code cache but do not fail if unable. - MaybeObject* maybe_new_object = - heap->code_stubs()->AtNumberPut(GetKey(), code); - if (maybe_new_object->ToObject(&new_object)) { - heap->public_set_code_stubs(UnseededNumberDictionary::cast(new_object)); - } - } - - return code; -} - - const char* CodeStub::MajorName(CodeStub::Major major_key, bool allow_unknown_keys) { switch (major_key) { @@ -188,6 +163,37 @@ const char* CodeStub::MajorName(CodeStub::Major major_key, } +void CodeStub::PrintName(StringStream* stream) { + stream->Add("%s", MajorName(MajorKey(), false)); +} + + +void ICCompareStub::AddToSpecialCache(Handle<Code> new_object) { + ASSERT(*known_map_ != NULL); + Isolate* isolate = new_object->GetIsolate(); + Factory* factory = isolate->factory(); + return Map::UpdateCodeCache(known_map_, + factory->compare_ic_symbol(), + new_object); +} + + +bool ICCompareStub::FindCodeInSpecialCache(Code** code_out) { + Isolate* isolate = known_map_->GetIsolate(); + Factory* factory = isolate->factory(); + Code::Flags flags = Code::ComputeFlags( + static_cast<Code::Kind>(GetCodeKind()), + UNINITIALIZED); + Handle<Object> probe( + known_map_->FindInCodeCache(*factory->compare_ic_symbol(), flags)); + if (probe->IsCode()) { + *code_out = Code::cast(*probe); + return true; + } + return false; +} + + int ICCompareStub::MinorKey() { return OpField::encode(op_ - Token::EQ) | StateField::encode(state_); } @@ -213,6 +219,10 @@ void ICCompareStub::Generate(MacroAssembler* masm) { case CompareIC::OBJECTS: GenerateObjects(masm); break; + case CompareIC::KNOWN_OBJECTS: + ASSERT(*known_map_ != NULL); + GenerateKnownObjects(masm); + break; default: UNREACHABLE(); } @@ -242,9 +252,18 @@ void InstanceofStub::PrintName(StringStream* stream) { } +void JSEntryStub::FinishCode(Handle<Code> code) { + Handle<FixedArray> handler_table = + code->GetIsolate()->factory()->NewFixedArray(1, TENURED); + handler_table->set(0, Smi::FromInt(handler_offset_)); + code->set_handler_table(*handler_table); +} + + void KeyedLoadElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastElement(masm); break; case FAST_DOUBLE_ELEMENTS: @@ -274,7 +293,11 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) { void KeyedStoreElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { case FAST_ELEMENTS: - KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_); + case FAST_SMI_ONLY_ELEMENTS: { + KeyedStoreStubCompiler::GenerateStoreFastElement(masm, + is_js_array_, + elements_kind_); + } break; case FAST_DOUBLE_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, @@ -302,24 +325,26 @@ void KeyedStoreElementStub::Generate(MacroAssembler* masm) { void ArgumentsAccessStub::PrintName(StringStream* stream) { - const char* type_name = NULL; // Make g++ happy. + stream->Add("ArgumentsAccessStub_"); switch (type_) { - case READ_ELEMENT: type_name = "ReadElement"; break; - case NEW_NON_STRICT_FAST: type_name = "NewNonStrictFast"; break; - case NEW_NON_STRICT_SLOW: type_name = "NewNonStrictSlow"; break; - case NEW_STRICT: type_name = "NewStrict"; break; + case READ_ELEMENT: stream->Add("ReadElement"); break; + case NEW_NON_STRICT_FAST: stream->Add("NewNonStrictFast"); break; + case NEW_NON_STRICT_SLOW: stream->Add("NewNonStrictSlow"); break; + case NEW_STRICT: stream->Add("NewStrict"); break; } - stream->Add("ArgumentsAccessStub_%s", type_name); } void CallFunctionStub::PrintName(StringStream* stream) { - const char* flags_name = NULL; // Make g++ happy. - switch (flags_) { - case NO_CALL_FUNCTION_FLAGS: flags_name = ""; break; - case RECEIVER_MIGHT_BE_IMPLICIT: flags_name = "_Implicit"; break; - } - stream->Add("CallFunctionStub_Args%d%s", argc_, flags_name); + stream->Add("CallFunctionStub_Args%d", argc_); + if (ReceiverMightBeImplicit()) stream->Add("_Implicit"); + if (RecordCallTarget()) stream->Add("_Recording"); +} + + +void CallConstructStub::PrintName(StringStream* stream) { + stream->Add("CallConstructStub"); + if (RecordCallTarget()) stream->Add("_Recording"); } @@ -402,4 +427,29 @@ bool ToBooleanStub::Types::CanBeUndetectable() const { } +void ElementsTransitionAndStoreStub::Generate(MacroAssembler* masm) { + Label fail; + if (!FLAG_trace_elements_transitions) { + if (to_ == FAST_ELEMENTS) { + if (from_ == FAST_SMI_ONLY_ELEMENTS) { + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + } else if (from_ == FAST_DOUBLE_ELEMENTS) { + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); + } else { + UNREACHABLE(); + } + KeyedStoreStubCompiler::GenerateStoreFastElement(masm, + is_jsarray_, + FAST_ELEMENTS); + } else if (from_ == FAST_SMI_ONLY_ELEMENTS && to_ == FAST_DOUBLE_ELEMENTS) { + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, is_jsarray_); + } else { + UNREACHABLE(); + } + } + masm->bind(&fail); + KeyedStoreIC::GenerateRuntimeSetProperty(masm, strict_mode_); +} + } } // namespace v8::internal diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 64c89b93d1..78ff554fdb 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -30,6 +30,7 @@ #include "allocation.h" #include "globals.h" +#include "codegen.h" namespace v8 { namespace internal { @@ -37,6 +38,7 @@ namespace internal { // List of code stubs used on all platforms. #define CODE_STUB_LIST_ALL_PLATFORMS(V) \ V(CallFunction) \ + V(CallConstruct) \ V(UnaryOp) \ V(BinaryOp) \ V(StringAdd) \ @@ -45,27 +47,22 @@ namespace internal { V(Compare) \ V(CompareIC) \ V(MathPow) \ + V(RecordWrite) \ + V(StoreBufferOverflow) \ + V(RegExpExec) \ V(TranscendentalCache) \ V(Instanceof) \ - /* All stubs above this line only exist in a few versions, which are */ \ - /* generated ahead of time. Therefore compiling a call to one of */ \ - /* them can't cause a new stub to be compiled, so compiling a call to */ \ - /* them is GC safe. The ones below this line exist in many variants */ \ - /* so code compiling a call to one can cause a GC. This means they */ \ - /* can't be called from other stubs, since stub generation code is */ \ - /* not GC safe. */ \ V(ConvertToDouble) \ V(WriteInt32ToHeapNumber) \ V(StackCheck) \ V(FastNewClosure) \ V(FastNewContext) \ + V(FastNewBlockContext) \ V(FastCloneShallowArray) \ - V(RevertToNumber) \ + V(FastCloneShallowObject) \ V(ToBoolean) \ V(ToNumber) \ - V(CounterOp) \ V(ArgumentsAccess) \ - V(RegExpExec) \ V(RegExpConstructResult) \ V(NumberToString) \ V(CEntry) \ @@ -73,7 +70,9 @@ namespace internal { V(KeyedLoadElement) \ V(KeyedStoreElement) \ V(DebuggerStatement) \ - V(StringDictionaryNegativeLookup) + V(StringDictionaryLookup) \ + V(ElementsTransitionAndStore) \ + V(StoreArrayLiteralElement) // List of code stubs only used on ARM platforms. #ifdef V8_TARGET_ARCH_ARM @@ -121,11 +120,6 @@ class CodeStub BASE_EMBEDDED { // Retrieve the code for the stub. Generate the code if needed. Handle<Code> GetCode(); - // Retrieve the code for the stub if already generated. Do not - // generate the code if not already generated and instead return a - // retry after GC Failure object. - MUST_USE_RESULT MaybeObject* TryGetCode(); - static Major MajorKeyFromKey(uint32_t key) { return static_cast<Major>(MajorKeyBits::decode(key)); } @@ -142,14 +136,35 @@ class CodeStub BASE_EMBEDDED { virtual ~CodeStub() {} + bool CompilingCallsToThisStubIsGCSafe() { + bool is_pregenerated = IsPregenerated(); + Code* code = NULL; + CHECK(!is_pregenerated || FindCodeInCache(&code)); + return is_pregenerated; + } + + // See comment above, where Instanceof is defined. + virtual bool IsPregenerated() { return false; } + + static void GenerateStubsAheadOfTime(); + static void GenerateFPStubs(); + + // Some stubs put untagged junk on the stack that cannot be scanned by the + // GC. This means that we must be statically sure that no GC can occur while + // they are running. If that is the case they should override this to return + // true, which will cause an assertion if we try to call something that can + // GC or if we try to put a stack frame on top of the junk, which would not + // result in a traversable stack. + virtual bool SometimesSetsUpAFrame() { return true; } + + // Lookup the code in the (possibly custom) cache. + bool FindCodeInCache(Code** code_out); + protected: static const int kMajorBits = 6; static const int kMinorBits = kBitsPerInt - kSmiTagSize - kMajorBits; private: - // Lookup the code in the (possibly custom) cache. - bool FindCodeInCache(Code** code_out); - // Nonvirtual wrapper around the stub-specific Generate function. Call // this function to set up the macro assembler and generate the code. void GenerateCode(MacroAssembler* masm); @@ -162,7 +177,11 @@ class CodeStub BASE_EMBEDDED { void RecordCodeGeneration(Code* code, MacroAssembler* masm); // Finish the code object after it has been generated. - virtual void FinishCode(Code* code) { } + virtual void FinishCode(Handle<Code> code) { } + + // Activate newly generated stub. Is called after + // registering stub in the stub cache. + virtual void Activate(Code* code) { } // Returns information for computing the number key. virtual Major MajorKey() = 0; @@ -176,11 +195,20 @@ class CodeStub BASE_EMBEDDED { return UNINITIALIZED; } + // Add the code to a specialized cache, specific to an individual + // stub type. Please note, this method must add the code object to a + // roots object, otherwise we will remove the code during GC. + virtual void AddToSpecialCache(Handle<Code> new_object) { } + + // Find code in a specialized cache, work is delegated to the specific stub. + virtual bool FindCodeInSpecialCache(Code** code_out) { return false; } + + // If a stub uses a special cache override this. + virtual bool UseSpecialCache() { return false; } + // Returns a name for logging/debugging purposes. SmartArrayPointer<const char> GetName(); - virtual void PrintName(StringStream* stream) { - stream->Add("%s", MajorName(MajorKey(), false)); - } + virtual void PrintName(StringStream* stream); // Returns whether the code generated for this stub needs to be allocated as // a fixed (non-moveable) code object. @@ -193,9 +221,6 @@ class CodeStub BASE_EMBEDDED { MajorKeyBits::encode(MajorKey()); } - // See comment above, where Instanceof is defined. - bool AllowsStubCalls() { return MajorKey() <= Instanceof; } - class MajorKeyBits: public BitField<uint32_t, 0, kMajorBits> {}; class MinorKeyBits: public BitField<uint32_t, kMajorBits, kMinorBits> {}; @@ -286,16 +311,17 @@ class ToNumberStub: public CodeStub { class FastNewClosureStub : public CodeStub { public: - explicit FastNewClosureStub(StrictModeFlag strict_mode) - : strict_mode_(strict_mode) { } + explicit FastNewClosureStub(LanguageMode language_mode) + : language_mode_(language_mode) { } void Generate(MacroAssembler* masm); private: Major MajorKey() { return FastNewClosure; } - int MinorKey() { return strict_mode_; } + int MinorKey() { return language_mode_ == CLASSIC_MODE + ? kNonStrictMode : kStrictMode; } - StrictModeFlag strict_mode_; + LanguageMode language_mode_; }; @@ -304,7 +330,7 @@ class FastNewContextStub : public CodeStub { static const int kMaximumSlots = 64; explicit FastNewContextStub(int slots) : slots_(slots) { - ASSERT(slots_ > 0 && slots <= kMaximumSlots); + ASSERT(slots_ > 0 && slots_ <= kMaximumSlots); } void Generate(MacroAssembler* masm); @@ -317,6 +343,24 @@ class FastNewContextStub : public CodeStub { }; +class FastNewBlockContextStub : public CodeStub { + public: + static const int kMaximumSlots = 64; + + explicit FastNewBlockContextStub(int slots) : slots_(slots) { + ASSERT(slots_ > 0 && slots_ <= kMaximumSlots); + } + + void Generate(MacroAssembler* masm); + + private: + int slots_; + + Major MajorKey() { return FastNewBlockContext; } + int MinorKey() { return slots_; } +}; + + class FastCloneShallowArrayStub : public CodeStub { public: // Maximum length of copied elements array. @@ -324,14 +368,16 @@ class FastCloneShallowArrayStub : public CodeStub { enum Mode { CLONE_ELEMENTS, - COPY_ON_WRITE_ELEMENTS + CLONE_DOUBLE_ELEMENTS, + COPY_ON_WRITE_ELEMENTS, + CLONE_ANY_ELEMENTS }; FastCloneShallowArrayStub(Mode mode, int length) : mode_(mode), length_((mode == COPY_ON_WRITE_ELEMENTS) ? 0 : length) { - ASSERT(length_ >= 0); - ASSERT(length_ <= kMaximumClonedLength); + ASSERT_GE(length_, 0); + ASSERT_LE(length_, kMaximumClonedLength); } void Generate(MacroAssembler* masm); @@ -342,12 +388,32 @@ class FastCloneShallowArrayStub : public CodeStub { Major MajorKey() { return FastCloneShallowArray; } int MinorKey() { - ASSERT(mode_ == 0 || mode_ == 1); - return (length_ << 1) | mode_; + ASSERT(mode_ == 0 || mode_ == 1 || mode_ == 2 || mode_ == 3); + return length_ * 4 + mode_; } }; +class FastCloneShallowObjectStub : public CodeStub { + public: + // Maximum number of properties in copied object. + static const int kMaximumClonedProperties = 6; + + explicit FastCloneShallowObjectStub(int length) : length_(length) { + ASSERT_GE(length_, 0); + ASSERT_LE(length_, kMaximumClonedProperties); + } + + void Generate(MacroAssembler* masm); + + private: + int length_; + + Major MajorKey() { return FastCloneShallowObject; } + int MinorKey() { return length_; } +}; + + class InstanceofStub: public CodeStub { public: enum Flags { @@ -388,12 +454,17 @@ class InstanceofStub: public CodeStub { class MathPowStub: public CodeStub { public: - MathPowStub() {} + enum ExponentType { INTEGER, DOUBLE, TAGGED, ON_STACK}; + + explicit MathPowStub(ExponentType exponent_type) + : exponent_type_(exponent_type) { } virtual void Generate(MacroAssembler* masm); private: virtual CodeStub::Major MajorKey() { return MathPow; } - virtual int MinorKey() { return 0; } + virtual int MinorKey() { return exponent_type_; } + + ExponentType exponent_type_; }; @@ -406,11 +477,15 @@ class ICCompareStub: public CodeStub { virtual void Generate(MacroAssembler* masm); + void set_known_map(Handle<Map> map) { known_map_ = map; } + private: class OpField: public BitField<int, 0, 3> { }; class StateField: public BitField<int, 3, 5> { }; - virtual void FinishCode(Code* code) { code->set_compare_state(state_); } + virtual void FinishCode(Handle<Code> code) { + code->set_compare_state(state_); + } virtual CodeStub::Major MajorKey() { return CompareIC; } virtual int MinorKey(); @@ -423,12 +498,18 @@ class ICCompareStub: public CodeStub { void GenerateStrings(MacroAssembler* masm); void GenerateObjects(MacroAssembler* masm); void GenerateMiss(MacroAssembler* masm); + void GenerateKnownObjects(MacroAssembler* masm); bool strict() const { return op_ == Token::EQ_STRICT; } Condition GetCondition() const { return CompareIC::ComputeCondition(op_); } + virtual void AddToSpecialCache(Handle<Code> new_object); + virtual bool FindCodeInSpecialCache(Code** code_out); + virtual bool UseSpecialCache() { return state_ == CompareIC::KNOWN_OBJECTS; } + Token::Value op_; CompareIC::State state_; + Handle<Map> known_map_; }; @@ -513,7 +594,7 @@ class CompareStub: public CodeStub { int MinorKey(); virtual int GetCodeKind() { return Code::COMPARE_IC; } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_compare_state(CompareIC::GENERIC); } @@ -531,11 +612,18 @@ class CompareStub: public CodeStub { class CEntryStub : public CodeStub { public: - explicit CEntryStub(int result_size) - : result_size_(result_size), save_doubles_(false) { } + explicit CEntryStub(int result_size, + SaveFPRegsMode save_doubles = kDontSaveFPRegs) + : result_size_(result_size), save_doubles_(save_doubles) { } void Generate(MacroAssembler* masm); - void SaveDoubles() { save_doubles_ = true; } + + // The version of this stub that doesn't save doubles is generated ahead of + // time, so it's OK to call it from other stubs that can't cope with GC during + // their code generation. On machines that always have gp registers (x64) we + // can generate both variants ahead of time. + virtual bool IsPregenerated(); + static void GenerateAheadOfTime(); private: void GenerateCore(MacroAssembler* masm, @@ -550,7 +638,7 @@ class CEntryStub : public CodeStub { // Number of pointers/values returned. const int result_size_; - bool save_doubles_; + SaveFPRegsMode save_doubles_; Major MajorKey() { return CEntry; } int MinorKey(); @@ -571,6 +659,10 @@ class JSEntryStub : public CodeStub { private: Major MajorKey() { return JSEntry; } int MinorKey() { return 0; } + + virtual void FinishCode(Handle<Code> code); + + int handler_offset_; }; @@ -647,6 +739,10 @@ class CallFunctionStub: public CodeStub { void Generate(MacroAssembler* masm); + virtual void FinishCode(Handle<Code> code) { + code->set_has_function_cache(RecordCallTarget()); + } + static int ExtractArgcFromMinorKey(int minor_key) { return ArgcBits::decode(minor_key); } @@ -658,8 +754,8 @@ class CallFunctionStub: public CodeStub { virtual void PrintName(StringStream* stream); // Minor key encoding in 32 bits with Bitfield <Type, shift, size>. - class FlagBits: public BitField<CallFunctionFlags, 0, 1> {}; - class ArgcBits: public BitField<unsigned, 1, 32 - 1> {}; + class FlagBits: public BitField<CallFunctionFlags, 0, 2> {}; + class ArgcBits: public BitField<unsigned, 2, 32 - 2> {}; Major MajorKey() { return CallFunction; } int MinorKey() { @@ -670,6 +766,34 @@ class CallFunctionStub: public CodeStub { bool ReceiverMightBeImplicit() { return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0; } + + bool RecordCallTarget() { + return (flags_ & RECORD_CALL_TARGET) != 0; + } +}; + + +class CallConstructStub: public CodeStub { + public: + explicit CallConstructStub(CallFunctionFlags flags) : flags_(flags) {} + + void Generate(MacroAssembler* masm); + + virtual void FinishCode(Handle<Code> code) { + code->set_has_function_cache(RecordCallTarget()); + } + + private: + CallFunctionFlags flags_; + + virtual void PrintName(StringStream* stream); + + Major MajorKey() { return CallConstruct; } + int MinorKey() { return flags_; } + + bool RecordCallTarget() { + return (flags_ & RECORD_CALL_TARGET) != 0; + } }; @@ -698,7 +822,6 @@ class StringCharCodeAtGenerator { public: StringCharCodeAtGenerator(Register object, Register index, - Register scratch, Register result, Label* receiver_not_string, Label* index_not_number, @@ -706,15 +829,11 @@ class StringCharCodeAtGenerator { StringIndexFlags index_flags) : object_(object), index_(index), - scratch_(scratch), result_(result), receiver_not_string_(receiver_not_string), index_not_number_(index_not_number), index_out_of_range_(index_out_of_range), index_flags_(index_flags) { - ASSERT(!scratch_.is(object_)); - ASSERT(!scratch_.is(index_)); - ASSERT(!scratch_.is(result_)); ASSERT(!result_.is(object_)); ASSERT(!result_.is(index_)); } @@ -732,7 +851,6 @@ class StringCharCodeAtGenerator { private: Register object_; Register index_; - Register scratch_; Register result_; Label* receiver_not_string_; @@ -795,8 +913,7 @@ class StringCharAtGenerator { public: StringCharAtGenerator(Register object, Register index, - Register scratch1, - Register scratch2, + Register scratch, Register result, Label* receiver_not_string, Label* index_not_number, @@ -804,13 +921,12 @@ class StringCharAtGenerator { StringIndexFlags index_flags) : char_code_at_generator_(object, index, - scratch1, - scratch2, + scratch, receiver_not_string, index_not_number, index_out_of_range, index_flags), - char_from_code_generator_(scratch2, result) {} + char_from_code_generator_(scratch, result) {} // Generates the fast case code. On the fallthrough path |result| // register contains the result. @@ -934,11 +1050,13 @@ class ToBooleanStub: public CodeStub { virtual int GetCodeKind() { return Code::TO_BOOLEAN_IC; } virtual void PrintName(StringStream* stream); + virtual bool SometimesSetsUpAFrame() { return false; } + private: Major MajorKey() { return ToBoolean; } int MinorKey() { return (tos_.code() << NUMBER_OF_TYPES) | types_.ToByte(); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_to_boolean_state(types_.ToByte()); } @@ -952,6 +1070,56 @@ class ToBooleanStub: public CodeStub { Types types_; }; + +class ElementsTransitionAndStoreStub : public CodeStub { + public: + ElementsTransitionAndStoreStub(ElementsKind from, + ElementsKind to, + bool is_jsarray, + StrictModeFlag strict_mode) + : from_(from), + to_(to), + is_jsarray_(is_jsarray), + strict_mode_(strict_mode) {} + + private: + class FromBits: public BitField<ElementsKind, 0, 8> {}; + class ToBits: public BitField<ElementsKind, 8, 8> {}; + class IsJSArrayBits: public BitField<bool, 16, 8> {}; + class StrictModeBits: public BitField<StrictModeFlag, 24, 8> {}; + + Major MajorKey() { return ElementsTransitionAndStore; } + int MinorKey() { + return FromBits::encode(from_) | + ToBits::encode(to_) | + IsJSArrayBits::encode(is_jsarray_) | + StrictModeBits::encode(strict_mode_); + } + + void Generate(MacroAssembler* masm); + + ElementsKind from_; + ElementsKind to_; + bool is_jsarray_; + StrictModeFlag strict_mode_; + + DISALLOW_COPY_AND_ASSIGN(ElementsTransitionAndStoreStub); +}; + + +class StoreArrayLiteralElementStub : public CodeStub { + public: + explicit StoreArrayLiteralElementStub() {} + + private: + Major MajorKey() { return StoreArrayLiteralElement; } + int MinorKey() { return 0; } + + void Generate(MacroAssembler* masm); + + DISALLOW_COPY_AND_ASSIGN(StoreArrayLiteralElementStub); +}; + } } // namespace v8::internal #endif // V8_CODE_STUBS_H_ diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index cdc9ba1553..3f651205ff 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -62,18 +62,15 @@ void CodeGenerator::MakeCodePrologue(CompilationInfo* info) { #ifdef DEBUG bool print_source = false; bool print_ast = false; - bool print_json_ast = false; const char* ftype; if (Isolate::Current()->bootstrapper()->IsActive()) { print_source = FLAG_print_builtin_source; print_ast = FLAG_print_builtin_ast; - print_json_ast = FLAG_print_builtin_json_ast; ftype = "builtin"; } else { print_source = FLAG_print_source; print_ast = FLAG_print_ast; - print_json_ast = FLAG_print_json_ast; Vector<const char> filter = CStrVector(FLAG_hydrogen_filter); if (print_source && !filter.is_empty()) { print_source = info->function()->name()->IsEqualTo(filter); @@ -81,9 +78,6 @@ void CodeGenerator::MakeCodePrologue(CompilationInfo* info) { if (print_ast && !filter.is_empty()) { print_ast = info->function()->name()->IsEqualTo(filter); } - if (print_json_ast && !filter.is_empty()) { - print_json_ast = info->function()->name()->IsEqualTo(filter); - } ftype = "user-defined"; } @@ -102,11 +96,6 @@ void CodeGenerator::MakeCodePrologue(CompilationInfo* info) { PrintF("--- AST ---\n%s\n", AstPrinter().PrintProgram(info->function())); } - - if (print_json_ast) { - JsonAstBuilder builder; - PrintF("%s", builder.BuildProgram(info->function())); - } #endif // DEBUG } @@ -218,8 +207,8 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) { int CEntryStub::MinorKey() { + int result = (save_doubles_ == kSaveFPRegs) ? 1 : 0; ASSERT(result_size_ == 1 || result_size_ == 2); - int result = save_doubles_ ? 1 : 0; #ifdef _WIN64 return result | ((result_size_ == 1) ? 0 : 2); #else diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index e551abfb11..5360d3ef3c 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -81,4 +81,19 @@ enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; #error Unsupported target architecture. #endif +namespace v8 { +namespace internal { + +class ElementsTransitionGenerator : public AllStatic { + public: + static void GenerateSmiOnlyToObject(MacroAssembler* masm); + static void GenerateSmiOnlyToDouble(MacroAssembler* masm, Label* fail); + static void GenerateDoubleToObject(MacroAssembler* masm, Label* fail); + + private: + DISALLOW_COPY_AND_ASSIGN(ElementsTransitionGenerator); +}; + +} } // namespace v8::internal + #endif // V8_CODEGEN_H_ diff --git a/deps/v8/src/collection.js b/deps/v8/src/collection.js new file mode 100644 index 0000000000..fcf4d38d94 --- /dev/null +++ b/deps/v8/src/collection.js @@ -0,0 +1,249 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +const $Set = global.Set; +const $Map = global.Map; +const $WeakMap = global.WeakMap; + +//------------------------------------------------------------------- + +// Global sentinel to be used instead of undefined keys, which are not +// supported internally but required for Harmony sets and maps. +var undefined_sentinel = {}; + + +function SetConstructor() { + if (%_IsConstructCall()) { + %SetInitialize(this); + } else { + return new $Set(); + } +} + + +function SetAdd(key) { + if (!IS_SET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Set.prototype.add', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return %SetAdd(this, key); +} + + +function SetHas(key) { + if (!IS_SET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Set.prototype.has', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return %SetHas(this, key); +} + + +function SetDelete(key) { + if (!IS_SET(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Set.prototype.delete', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return %SetDelete(this, key); +} + + +function MapConstructor() { + if (%_IsConstructCall()) { + %MapInitialize(this); + } else { + return new $Map(); + } +} + + +function MapGet(key) { + if (!IS_MAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Map.prototype.get', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return %MapGet(this, key); +} + + +function MapSet(key, value) { + if (!IS_MAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Map.prototype.set', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return %MapSet(this, key, value); +} + + +function MapHas(key) { + if (!IS_MAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Map.prototype.has', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + return !IS_UNDEFINED(%MapGet(this, key)); +} + + +function MapDelete(key) { + if (!IS_MAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['Map.prototype.delete', this]); + } + if (IS_UNDEFINED(key)) { + key = undefined_sentinel; + } + if (!IS_UNDEFINED(%MapGet(this, key))) { + %MapSet(this, key, void 0); + return true; + } else { + return false; + } +} + + +function WeakMapConstructor() { + if (%_IsConstructCall()) { + %WeakMapInitialize(this); + } else { + return new $WeakMap(); + } +} + + +function WeakMapGet(key) { + if (!IS_WEAKMAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakMap.prototype.get', this]); + } + if (!IS_SPEC_OBJECT(key)) { + throw %MakeTypeError('invalid_weakmap_key', [this, key]); + } + return %WeakMapGet(this, key); +} + + +function WeakMapSet(key, value) { + if (!IS_WEAKMAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakMap.prototype.set', this]); + } + if (!IS_SPEC_OBJECT(key)) { + throw %MakeTypeError('invalid_weakmap_key', [this, key]); + } + return %WeakMapSet(this, key, value); +} + + +function WeakMapHas(key) { + if (!IS_WEAKMAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakMap.prototype.has', this]); + } + if (!IS_SPEC_OBJECT(key)) { + throw %MakeTypeError('invalid_weakmap_key', [this, key]); + } + return !IS_UNDEFINED(%WeakMapGet(this, key)); +} + + +function WeakMapDelete(key) { + if (!IS_WEAKMAP(this)) { + throw MakeTypeError('incompatible_method_receiver', + ['WeakMap.prototype.delete', this]); + } + if (!IS_SPEC_OBJECT(key)) { + throw %MakeTypeError('invalid_weakmap_key', [this, key]); + } + if (!IS_UNDEFINED(%WeakMapGet(this, key))) { + %WeakMapSet(this, key, void 0); + return true; + } else { + return false; + } +} + +// ------------------------------------------------------------------- + +(function () { + %CheckIsBootstrapping(); + + // Set up the Set and Map constructor function. + %SetCode($Set, SetConstructor); + %SetCode($Map, MapConstructor); + + // Set up the constructor property on the Set and Map prototype object. + %SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM); + %SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM); + + // Set up the non-enumerable functions on the Set prototype object. + InstallFunctions($Set.prototype, DONT_ENUM, $Array( + "add", SetAdd, + "has", SetHas, + "delete", SetDelete + )); + + // Set up the non-enumerable functions on the Map prototype object. + InstallFunctions($Map.prototype, DONT_ENUM, $Array( + "get", MapGet, + "set", MapSet, + "has", MapHas, + "delete", MapDelete + )); + + // Set up the WeakMap constructor function. + %SetCode($WeakMap, WeakMapConstructor); + + // Set up the constructor property on the WeakMap prototype object. + %SetProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM); + + // Set up the non-enumerable functions on the WeakMap prototype object. + InstallFunctions($WeakMap.prototype, DONT_ENUM, $Array( + "get", WeakMapGet, + "set", WeakMapSet, + "has", WeakMapHas, + "delete", WeakMapDelete + )); +})(); diff --git a/deps/v8/src/compilation-cache.cc b/deps/v8/src/compilation-cache.cc index 28e833a493..82cc2231a3 100644 --- a/deps/v8/src/compilation-cache.cc +++ b/deps/v8/src/compilation-cache.cc @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,6 +27,7 @@ #include "v8.h" +#include "assembler.h" #include "compilation-cache.h" #include "serialize.h" @@ -250,7 +251,8 @@ void CompilationCacheScript::Put(Handle<String> source, Handle<SharedFunctionInfo> CompilationCacheEval::Lookup( Handle<String> source, Handle<Context> context, - StrictModeFlag strict_mode) { + LanguageMode language_mode, + int scope_position) { // Make sure not to leak the table into the surrounding handle // scope. Otherwise, we risk keeping old tables around even after // having cleared the cache. @@ -259,7 +261,8 @@ Handle<SharedFunctionInfo> CompilationCacheEval::Lookup( { HandleScope scope(isolate()); for (generation = 0; generation < generations(); generation++) { Handle<CompilationCacheTable> table = GetTable(generation); - result = table->LookupEval(*source, *context, strict_mode); + result = table->LookupEval( + *source, *context, language_mode, scope_position); if (result->IsSharedFunctionInfo()) { break; } @@ -269,7 +272,7 @@ Handle<SharedFunctionInfo> CompilationCacheEval::Lookup( Handle<SharedFunctionInfo> function_info(SharedFunctionInfo::cast(result), isolate()); if (generation != 0) { - Put(source, context, function_info); + Put(source, context, function_info, scope_position); } isolate()->counters()->compilation_cache_hits()->Increment(); return function_info; @@ -283,27 +286,31 @@ Handle<SharedFunctionInfo> CompilationCacheEval::Lookup( MaybeObject* CompilationCacheEval::TryTablePut( Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + int scope_position) { Handle<CompilationCacheTable> table = GetFirstTable(); - return table->PutEval(*source, *context, *function_info); + return table->PutEval(*source, *context, *function_info, scope_position); } Handle<CompilationCacheTable> CompilationCacheEval::TablePut( Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + int scope_position) { CALL_HEAP_FUNCTION(isolate(), - TryTablePut(source, context, function_info), + TryTablePut( + source, context, function_info, scope_position), CompilationCacheTable); } void CompilationCacheEval::Put(Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + int scope_position) { HandleScope scope(isolate()); - SetFirstTable(TablePut(source, context, function_info)); + SetFirstTable(TablePut(source, context, function_info, scope_position)); } @@ -389,16 +396,20 @@ Handle<SharedFunctionInfo> CompilationCache::LookupEval( Handle<String> source, Handle<Context> context, bool is_global, - StrictModeFlag strict_mode) { + LanguageMode language_mode, + int scope_position) { if (!IsEnabled()) { return Handle<SharedFunctionInfo>::null(); } Handle<SharedFunctionInfo> result; if (is_global) { - result = eval_global_.Lookup(source, context, strict_mode); + result = eval_global_.Lookup( + source, context, language_mode, scope_position); } else { - result = eval_contextual_.Lookup(source, context, strict_mode); + ASSERT(scope_position != RelocInfo::kNoPosition); + result = eval_contextual_.Lookup( + source, context, language_mode, scope_position); } return result; } @@ -427,16 +438,18 @@ void CompilationCache::PutScript(Handle<String> source, void CompilationCache::PutEval(Handle<String> source, Handle<Context> context, bool is_global, - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + int scope_position) { if (!IsEnabled()) { return; } HandleScope scope(isolate()); if (is_global) { - eval_global_.Put(source, context, function_info); + eval_global_.Put(source, context, function_info, scope_position); } else { - eval_contextual_.Put(source, context, function_info); + ASSERT(scope_position != RelocInfo::kNoPosition); + eval_contextual_.Put(source, context, function_info, scope_position); } } diff --git a/deps/v8/src/compilation-cache.h b/deps/v8/src/compilation-cache.h index 4339d22641..31f2909680 100644 --- a/deps/v8/src/compilation-cache.h +++ b/deps/v8/src/compilation-cache.h @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -123,7 +123,19 @@ class CompilationCacheScript : public CompilationSubCache { }; -// Sub-cache for eval scripts. +// Sub-cache for eval scripts. Two caches for eval are used. One for eval calls +// in global contexts and one for eval calls in other contexts. The cache +// considers the following pieces of information when checking for matching +// entries: +// 1. The source string. +// 2. The shared function info of the calling function. +// 3. Whether the source should be compiled as strict code or as non-strict +// code. +// Note: Currently there are clients of CompileEval that always compile +// non-strict code even if the calling function is a strict mode function. +// More specifically these are the CompileString, DebugEvaluate and +// DebugEvaluateGlobal runtime functions. +// 4. The start position of the calling scope. class CompilationCacheEval: public CompilationSubCache { public: CompilationCacheEval(Isolate* isolate, int generations) @@ -131,23 +143,27 @@ class CompilationCacheEval: public CompilationSubCache { Handle<SharedFunctionInfo> Lookup(Handle<String> source, Handle<Context> context, - StrictModeFlag strict_mode); + LanguageMode language_mode, + int scope_position); void Put(Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info); + Handle<SharedFunctionInfo> function_info, + int scope_position); private: MUST_USE_RESULT MaybeObject* TryTablePut( Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info); + Handle<SharedFunctionInfo> function_info, + int scope_position); // Note: Returns a new hash table if operation results in expansion. Handle<CompilationCacheTable> TablePut( Handle<String> source, Handle<Context> context, - Handle<SharedFunctionInfo> function_info); + Handle<SharedFunctionInfo> function_info, + int scope_position); DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval); }; @@ -198,7 +214,8 @@ class CompilationCache { Handle<SharedFunctionInfo> LookupEval(Handle<String> source, Handle<Context> context, bool is_global, - StrictModeFlag strict_mode); + LanguageMode language_mode, + int scope_position); // Returns the regexp data associated with the given regexp if it // is in cache, otherwise an empty handle. @@ -215,7 +232,8 @@ class CompilationCache { void PutEval(Handle<String> source, Handle<Context> context, bool is_global, - Handle<SharedFunctionInfo> function_info); + Handle<SharedFunctionInfo> function_info, + int scope_position); // Associate the (source, flags) pair to the given regexp data. // This may overwrite an existing mapping. diff --git a/deps/v8/src/compiler-intrinsics.h b/deps/v8/src/compiler-intrinsics.h new file mode 100644 index 0000000000..3b9c59ea53 --- /dev/null +++ b/deps/v8/src/compiler-intrinsics.h @@ -0,0 +1,77 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_COMPILER_INTRINSICS_H_ +#define V8_COMPILER_INTRINSICS_H_ + +namespace v8 { +namespace internal { + +class CompilerIntrinsics { + public: + // Returns number of zero bits preceding least significant 1 bit. + // Undefined for zero value. + INLINE(static int CountTrailingZeros(uint32_t value)); + + // Returns number of zero bits following most significant 1 bit. + // Undefined for zero value. + INLINE(static int CountLeadingZeros(uint32_t value)); +}; + +#ifdef __GNUC__ +int CompilerIntrinsics::CountTrailingZeros(uint32_t value) { + return __builtin_ctz(value); +} + +int CompilerIntrinsics::CountLeadingZeros(uint32_t value) { + return __builtin_clz(value); +} + +#elif defined(_MSC_VER) + +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) + +int CompilerIntrinsics::CountTrailingZeros(uint32_t value) { + unsigned long result; //NOLINT + _BitScanForward(&result, static_cast<long>(value)); //NOLINT + return static_cast<int>(result); +} + +int CompilerIntrinsics::CountLeadingZeros(uint32_t value) { + unsigned long result; //NOLINT + _BitScanReverse(&result, static_cast<long>(value)); //NOLINT + return 31 - static_cast<int>(result); +} + +#else +#error Unsupported compiler +#endif + +} } // namespace v8::internal + +#endif // V8_COMPILER_INTRINSICS_H_ diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 5e1c4a9789..aea889f8be 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "gdb-jit.h" #include "hydrogen.h" +#include "isolate-inl.h" #include "lithium.h" #include "liveedit.h" #include "parser.h" @@ -52,13 +53,13 @@ namespace internal { CompilationInfo::CompilationInfo(Handle<Script> script) : isolate_(script->GetIsolate()), - flags_(0), + flags_(LanguageModeField::encode(CLASSIC_MODE)), function_(NULL), scope_(NULL), + global_scope_(NULL), script_(script), extension_(NULL), pre_parse_data_(NULL), - supports_deoptimization_(false), osr_ast_id_(AstNode::kNoNumber) { Initialize(NONOPT); } @@ -66,14 +67,15 @@ CompilationInfo::CompilationInfo(Handle<Script> script) CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info) : isolate_(shared_info->GetIsolate()), - flags_(IsLazy::encode(true)), + flags_(LanguageModeField::encode(CLASSIC_MODE) | + IsLazy::encode(true)), function_(NULL), scope_(NULL), + global_scope_(NULL), shared_info_(shared_info), script_(Handle<Script>(Script::cast(shared_info->script()))), extension_(NULL), pre_parse_data_(NULL), - supports_deoptimization_(false), osr_ast_id_(AstNode::kNoNumber) { Initialize(BASE); } @@ -81,15 +83,16 @@ CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info) CompilationInfo::CompilationInfo(Handle<JSFunction> closure) : isolate_(closure->GetIsolate()), - flags_(IsLazy::encode(true)), + flags_(LanguageModeField::encode(CLASSIC_MODE) | + IsLazy::encode(true)), function_(NULL), scope_(NULL), + global_scope_(NULL), closure_(closure), shared_info_(Handle<SharedFunctionInfo>(closure->shared())), script_(Handle<Script>(Script::cast(shared_info_->script()))), extension_(NULL), pre_parse_data_(NULL), - supports_deoptimization_(false), osr_ast_id_(AstNode::kNoNumber) { Initialize(BASE); } @@ -107,6 +110,18 @@ void CompilationInfo::DisableOptimization() { } +// Primitive functions are unlikely to be picked up by the stack-walking +// profiler, so they trigger their own optimization when they're called +// for the SharedFunctionInfo::kCallsUntilPrimitiveOptimization-th time. +bool CompilationInfo::ShouldSelfOptimize() { + return FLAG_self_optimization && + FLAG_crankshaft && + !Serializer::enabled() && + !function()->flags()->Contains(kDontSelfOptimize) && + (shared_info().is_null() || !shared_info()->optimization_disabled()); +} + + void CompilationInfo::AbortOptimization() { Handle<Code> code(shared_info()->code()); SetCode(code); @@ -167,7 +182,11 @@ static void FinishOptimization(Handle<JSFunction> function, int64_t start) { static bool MakeCrankshaftCode(CompilationInfo* info) { // Test if we can optimize this function when asked to. We can only // do this after the scopes are computed. - if (!info->AllowOptimize()) info->DisableOptimization(); + if (!info->AllowOptimize()) { + info->DisableOptimization(); + } else if (info->IsOptimizable()) { + info->EnableDeoptimizationSupport(); + } // In case we are not optimizing simply return the code from // the full code generator. @@ -187,7 +206,7 @@ static bool MakeCrankshaftCode(CompilationInfo* info) { // Fall back to using the full code generator if it's not possible // to use the Hydrogen-based optimizing compiler. We already have // generated code for this from the shared function object. - if (AlwaysFullCompiler() || !FLAG_use_hydrogen) { + if (AlwaysFullCompiler()) { info->SetCode(code); return true; } @@ -275,7 +294,7 @@ static bool MakeCrankshaftCode(CompilationInfo* info) { } Handle<Context> global_context(info->closure()->context()->global_context()); - TypeFeedbackOracle oracle(code, global_context); + TypeFeedbackOracle oracle(code, global_context, info->isolate()); HGraphBuilder builder(info, &oracle); HPhase phase(HPhase::kTotal); HGraph* graph = builder.CreateGraph(); @@ -284,7 +303,7 @@ static bool MakeCrankshaftCode(CompilationInfo* info) { return false; } - if (graph != NULL && FLAG_build_lithium) { + if (graph != NULL) { Handle<Code> optimized_code = graph->Compile(info); if (!optimized_code.is_null()) { info->SetCode(optimized_code); @@ -308,9 +327,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) { static bool GenerateCode(CompilationInfo* info) { - return V8::UseCrankshaft() ? - MakeCrankshaftCode(info) : - FullCodeGenerator::MakeCode(info); + return info->IsCompilingForDebugging() || !V8::UseCrankshaft() ? + FullCodeGenerator::MakeCode(info) : + MakeCrankshaftCode(info); } @@ -328,8 +347,7 @@ bool Compiler::MakeCodeForLiveEdit(CompilationInfo* info) { // the compilation info is set if compilation succeeded. bool succeeded = MakeCode(info); if (!info->shared_info().is_null()) { - Handle<SerializedScopeInfo> scope_info = - SerializedScopeInfo::Create(info->scope()); + Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope()); info->shared_info()->set_scope_info(*scope_info); } return succeeded; @@ -371,8 +389,14 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { // Only allow non-global compiles for eval. ASSERT(info->is_eval() || info->is_global()); - - if (!ParserApi::Parse(info)) return Handle<SharedFunctionInfo>::null(); + ParsingFlags flags = kNoParsingFlags; + if (info->pre_parse_data() != NULL || + String::cast(script->source())->length() > FLAG_min_preparse_length) { + flags = kAllowLazy; + } + if (!ParserApi::Parse(info, flags)) { + return Handle<SharedFunctionInfo>::null(); + } // Measure how long it takes to do the compilation; only take the // rest of the function into account to avoid overlap with the @@ -386,7 +410,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { FunctionLiteral* lit = info->function(); LiveEditFunctionTracker live_edit_tracker(isolate, lit); if (!MakeCode(info)) { - isolate->StackOverflow(); + if (!isolate->has_pending_exception()) isolate->StackOverflow(); return Handle<SharedFunctionInfo>::null(); } @@ -397,7 +421,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { lit->name(), lit->materialized_literal_count(), info->code(), - SerializedScopeInfo::Create(info->scope())); + ScopeInfo::Create(info->scope())); ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position()); Compiler::SetFunctionInfo(result, lit, true, script); @@ -447,7 +471,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, int line_offset, int column_offset, v8::Extension* extension, - ScriptDataImpl* input_pre_data, + ScriptDataImpl* pre_data, Handle<Object> script_data, NativesFlag natives) { Isolate* isolate = source->GetIsolate(); @@ -478,23 +502,12 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, // for small sources, odds are that there aren't many functions // that would be compiled lazily anyway, so we skip the preparse step // in that case too. - ScriptDataImpl* pre_data = input_pre_data; - bool harmony_block_scoping = natives != NATIVES_CODE && - FLAG_harmony_block_scoping; - if (pre_data == NULL - && source_length >= FLAG_min_preparse_length) { - if (source->IsExternalTwoByteString()) { - ExternalTwoByteStringUC16CharacterStream stream( - Handle<ExternalTwoByteString>::cast(source), 0, source->length()); - pre_data = ParserApi::PartialPreParse(&stream, - extension, - harmony_block_scoping); - } else { - GenericStringUC16CharacterStream stream(source, 0, source->length()); - pre_data = ParserApi::PartialPreParse(&stream, - extension, - harmony_block_scoping); - } + int flags = kNoParsingFlags; + if ((natives == NATIVES_CODE) || FLAG_allow_natives_syntax) { + flags |= kAllowNativesSyntax; + } + if (natives != NATIVES_CODE && FLAG_harmony_scoping) { + flags |= EXTENDED_MODE; } // Create a script object describing the script to be compiled. @@ -520,11 +533,6 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, if (extension == NULL && !result.is_null()) { compilation_cache->PutScript(source, result); } - - // Get rid of the pre-parsing data (if necessary). - if (input_pre_data == NULL && pre_data != NULL) { - delete pre_data; - } } if (result.is_null()) isolate->ReportPendingMessages(); @@ -535,7 +543,8 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, Handle<Context> context, bool is_global, - StrictModeFlag strict_mode) { + LanguageMode language_mode, + int scope_position) { Isolate* isolate = source->GetIsolate(); int source_length = source->length(); isolate->counters()->total_eval_size()->Increment(source_length); @@ -551,7 +560,8 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, result = compilation_cache->LookupEval(source, context, is_global, - strict_mode); + language_mode, + scope_position); if (result.is_null()) { // Create a script object describing the script to be compiled. @@ -559,16 +569,20 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, CompilationInfo info(script); info.MarkAsEval(); if (is_global) info.MarkAsGlobal(); - if (strict_mode == kStrictMode) info.MarkAsStrictMode(); + info.SetLanguageMode(language_mode); info.SetCallingContext(context); result = MakeFunctionInfo(&info); if (!result.is_null()) { - CompilationCache* compilation_cache = isolate->compilation_cache(); - // If caller is strict mode, the result must be strict as well, - // but not the other way around. Consider: + // If caller is strict mode, the result must be in strict mode or + // extended mode as well, but not the other way around. Consider: // eval("'use strict'; ..."); - ASSERT(strict_mode == kNonStrictMode || result->strict_mode()); - compilation_cache->PutEval(source, context, is_global, result); + ASSERT(language_mode != STRICT_MODE || !result->is_classic_mode()); + // If caller is in extended mode, the result must also be in + // extended mode. + ASSERT(language_mode != EXTENDED_MODE || + result->is_extended_mode()); + compilation_cache->PutEval( + source, context, is_global, result, scope_position); } } @@ -591,17 +605,16 @@ bool Compiler::CompileLazy(CompilationInfo* info) { isolate->counters()->total_compile_size()->Increment(compiled_size); // Generate the AST for the lazily compiled function. - if (ParserApi::Parse(info)) { + if (ParserApi::Parse(info, kNoParsingFlags)) { // Measure how long it takes to do the lazy compilation; only take the // rest of the function into account to avoid overlap with the lazy // parsing statistics. HistogramTimerScope timer(isolate->counters()->compile_lazy()); - // After parsing we know function's strict mode. Remember it. - if (info->function()->strict_mode()) { - shared->set_strict_mode(true); - info->MarkAsStrictMode(); - } + // After parsing we know the function's language mode. Remember it. + LanguageMode language_mode = info->function()->language_mode(); + info->SetLanguageMode(language_mode); + shared->set_language_mode(language_mode); // Compile the code. if (!MakeCode(info)) { @@ -620,16 +633,15 @@ bool Compiler::CompileLazy(CompilationInfo* info) { RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared); if (info->IsOptimizing()) { - ASSERT(shared->scope_info() != SerializedScopeInfo::Empty()); + ASSERT(shared->scope_info() != ScopeInfo::Empty()); function->ReplaceCode(*code); } else { // Update the shared function info with the compiled code and the // scope info. Please note, that the order of the shared function // info initialization is important since set_scope_info might // trigger a GC, causing the ASSERT below to be invalid if the code - // was flushed. By settting the code object last we avoid this. - Handle<SerializedScopeInfo> scope_info = - SerializedScopeInfo::Create(info->scope()); + // was flushed. By setting the code object last we avoid this. + Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope()); shared->set_scope_info(*scope_info); shared->set_code(*code); if (!function.is_null()) { @@ -652,6 +664,9 @@ bool Compiler::CompileLazy(CompilationInfo* info) { // Check the function has compiled code. ASSERT(shared->is_compiled()); shared->set_code_age(0); + shared->set_dont_crankshaft(lit->flags()->Contains(kDontOptimize)); + shared->set_dont_inline(lit->flags()->Contains(kDontInline)); + shared->set_ast_node_count(lit->ast_node_count()); if (info->AllowOptimize() && !shared->optimization_disabled()) { // If we're asked to always optimize, we compile the optimized @@ -681,7 +696,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, CompilationInfo info(script); info.SetFunction(literal); info.SetScope(literal->scope()); - if (literal->scope()->is_strict_mode()) info.MarkAsStrictMode(); + info.SetLanguageMode(literal->scope()->language_mode()); LiveEditFunctionTracker live_edit_tracker(info.isolate(), literal); // Determine if the function can be lazily compiled. This is necessary to @@ -692,7 +707,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, bool allow_lazy = literal->AllowsLazyCompilation() && !LiveEditFunctionTracker::IsActive(info.isolate()); - Handle<SerializedScopeInfo> scope_info(SerializedScopeInfo::Empty()); + Handle<ScopeInfo> scope_info(ScopeInfo::Empty()); // Generate code if (FLAG_lazy && allow_lazy) { @@ -701,7 +716,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, } else if ((V8::UseCrankshaft() && MakeCrankshaftCode(&info)) || (!V8::UseCrankshaft() && FullCodeGenerator::MakeCode(&info))) { ASSERT(!info.code().is_null()); - scope_info = SerializedScopeInfo::Create(info.scope()); + scope_info = ScopeInfo::Create(info.scope()); } else { return Handle<SharedFunctionInfo>::null(); } @@ -733,8 +748,8 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info, FunctionLiteral* lit, bool is_toplevel, Handle<Script> script) { - function_info->set_length(lit->num_parameters()); - function_info->set_formal_parameter_count(lit->num_parameters()); + function_info->set_length(lit->parameter_count()); + function_info->set_formal_parameter_count(lit->parameter_count()); function_info->set_script(*script); function_info->set_function_token_position(lit->function_token_position()); function_info->set_start_position(lit->start_position()); @@ -747,9 +762,12 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info, lit->has_only_simple_this_property_assignments(), *lit->this_property_assignments()); function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation()); - function_info->set_strict_mode(lit->strict_mode()); + function_info->set_language_mode(lit->language_mode()); function_info->set_uses_arguments(lit->scope()->arguments() != NULL); function_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); + function_info->set_ast_node_count(lit->ast_node_count()); + function_info->set_dont_crankshaft(lit->flags()->Contains(kDontOptimize)); + function_info->set_dont_inline(lit->flags()->Contains(kDontInline)); } diff --git a/deps/v8/src/compiler.h b/deps/v8/src/compiler.h index 69ab27d9c8..38252871ea 100644 --- a/deps/v8/src/compiler.h +++ b/deps/v8/src/compiler.h @@ -52,10 +52,15 @@ class CompilationInfo BASE_EMBEDDED { bool is_lazy() const { return IsLazy::decode(flags_); } bool is_eval() const { return IsEval::decode(flags_); } bool is_global() const { return IsGlobal::decode(flags_); } - bool is_strict_mode() const { return IsStrictMode::decode(flags_); } + bool is_classic_mode() const { return language_mode() == CLASSIC_MODE; } + bool is_extended_mode() const { return language_mode() == EXTENDED_MODE; } + LanguageMode language_mode() const { + return LanguageModeField::decode(flags_); + } bool is_in_loop() const { return IsInLoop::decode(flags_); } FunctionLiteral* function() const { return function_; } Scope* scope() const { return scope_; } + Scope* global_scope() const { return global_scope_; } Handle<Code> code() const { return code_; } Handle<JSFunction> closure() const { return closure_; } Handle<SharedFunctionInfo> shared_info() const { return shared_info_; } @@ -73,11 +78,11 @@ class CompilationInfo BASE_EMBEDDED { ASSERT(!is_lazy()); flags_ |= IsGlobal::encode(true); } - void MarkAsStrictMode() { - flags_ |= IsStrictMode::encode(true); - } - StrictModeFlag StrictMode() { - return is_strict_mode() ? kStrictMode : kNonStrictMode; + void SetLanguageMode(LanguageMode language_mode) { + ASSERT(this->language_mode() == CLASSIC_MODE || + this->language_mode() == language_mode || + language_mode == EXTENDED_MODE); + flags_ = LanguageModeField::update(flags_, language_mode); } void MarkAsInLoop() { ASSERT(is_lazy()); @@ -97,6 +102,10 @@ class CompilationInfo BASE_EMBEDDED { ASSERT(scope_ == NULL); scope_ = scope; } + void SetGlobalScope(Scope* global_scope) { + ASSERT(global_scope_ == NULL); + global_scope_ = global_scope; + } void SetCode(Handle<Code> code) { code_ = code; } void SetExtension(v8::Extension* extension) { ASSERT(!is_lazy()); @@ -114,6 +123,19 @@ class CompilationInfo BASE_EMBEDDED { ASSERT(IsOptimizing()); osr_ast_id_ = osr_ast_id; } + void MarkCompilingForDebugging(Handle<Code> current_code) { + ASSERT(mode_ != OPTIMIZE); + ASSERT(current_code->kind() == Code::FUNCTION); + flags_ |= IsCompilingForDebugging::encode(true); + if (current_code->is_compiled_optimizable()) { + EnableDeoptimizationSupport(); + } else { + mode_ = CompilationInfo::NONOPT; + } + } + bool IsCompilingForDebugging() { + return IsCompilingForDebugging::decode(flags_); + } bool has_global_object() const { return !closure().is_null() && (closure()->context()->global() != NULL); @@ -133,10 +155,12 @@ class CompilationInfo BASE_EMBEDDED { void DisableOptimization(); // Deoptimization support. - bool HasDeoptimizationSupport() const { return supports_deoptimization_; } + bool HasDeoptimizationSupport() const { + return SupportsDeoptimization::decode(flags_); + } void EnableDeoptimizationSupport() { ASSERT(IsOptimizable()); - supports_deoptimization_ = true; + flags_ |= SupportsDeoptimization::encode(true); } // Determine whether or not we can adaptively optimize. @@ -144,6 +168,9 @@ class CompilationInfo BASE_EMBEDDED { return V8::UseCrankshaft() && !closure_.is_null(); } + // Determines whether or not to insert a self-optimization header. + bool ShouldSelfOptimize(); + // Disable all optimization attempts of this info for the rest of the // current compilation pipeline. void AbortOptimization(); @@ -171,8 +198,9 @@ class CompilationInfo BASE_EMBEDDED { if (script_->type()->value() == Script::TYPE_NATIVE) { MarkAsNative(); } - if (!shared_info_.is_null() && shared_info_->strict_mode()) { - MarkAsStrictMode(); + if (!shared_info_.is_null()) { + ASSERT(language_mode() == CLASSIC_MODE); + SetLanguageMode(shared_info_->language_mode()); } } @@ -192,9 +220,14 @@ class CompilationInfo BASE_EMBEDDED { // Flags that can be set for lazy compilation. class IsInLoop: public BitField<bool, 3, 1> {}; // Strict mode - used in eager compilation. - class IsStrictMode: public BitField<bool, 4, 1> {}; + class LanguageModeField: public BitField<LanguageMode, 4, 2> {}; // Is this a function from our natives. class IsNative: public BitField<bool, 6, 1> {}; + // Is this code being compiled with support for deoptimization.. + class SupportsDeoptimization: public BitField<bool, 7, 1> {}; + // If compiling for debugging produce just full code matching the + // initial mode setting. + class IsCompilingForDebugging: public BitField<bool, 8, 1> {}; unsigned flags_; @@ -205,6 +238,8 @@ class CompilationInfo BASE_EMBEDDED { // The scope of the function literal as a convenience. Set to indicate // that scopes have been analyzed. Scope* scope_; + // The global scope provided as a convenience. + Scope* global_scope_; // The compiled code. Handle<Code> code_; @@ -223,7 +258,6 @@ class CompilationInfo BASE_EMBEDDED { // Compilation mode flag and whether deoptimization is allowed. Mode mode_; - bool supports_deoptimization_; int osr_ast_id_; DISALLOW_COPY_AND_ASSIGN(CompilationInfo); @@ -249,6 +283,9 @@ class Compiler : public AllStatic { static const int kMaxInliningLevels = 3; + // Call count before primitive functions trigger their own optimization. + static const int kCallsUntilPrimitiveOpt = 200; + // All routines return a SharedFunctionInfo. // If an error occurs an exception is raised and the return handle // contains NULL. @@ -267,7 +304,8 @@ class Compiler : public AllStatic { static Handle<SharedFunctionInfo> CompileEval(Handle<String> source, Handle<Context> context, bool is_global, - StrictModeFlag strict_mode); + LanguageMode language_mode, + int scope_position); // Compile from function info (used for lazy compilation). Returns true on // success and false if the compilation resulted in a stack overflow. diff --git a/deps/v8/src/contexts.cc b/deps/v8/src/contexts.cc index 4f93abdff1..76784bd704 100644 --- a/deps/v8/src/contexts.cc +++ b/deps/v8/src/contexts.cc @@ -86,14 +86,14 @@ void Context::set_global_proxy(JSObject* object) { Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, - int* index_, + int* index, PropertyAttributes* attributes, BindingFlags* binding_flags) { Isolate* isolate = GetIsolate(); Handle<Context> context(this, isolate); bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0; - *index_ = -1; + *index = -1; *attributes = ABSENT; *binding_flags = MISSING_BINDING; @@ -110,70 +110,51 @@ Handle<Object> Context::Lookup(Handle<String> name, PrintF("\n"); } - // Check extension/with/global object. - if (!context->IsBlockContext() && context->has_extension()) { - if (context->IsCatchContext()) { - // Catch contexts have the variable name in the extension slot. - if (name->Equals(String::cast(context->extension()))) { - if (FLAG_trace_contexts) { - PrintF("=> found in catch context\n"); - } - *index_ = Context::THROWN_OBJECT_INDEX; - *attributes = NONE; - *binding_flags = MUTABLE_IS_INITIALIZED; - return context; - } + // 1. Check global objects, subjects of with, and extension objects. + if (context->IsGlobalContext() || + context->IsWithContext() || + (context->IsFunctionContext() && context->has_extension())) { + Handle<JSObject> object(JSObject::cast(context->extension()), isolate); + // Context extension objects needs to behave as if they have no + // prototype. So even if we want to follow prototype chains, we need + // to only do a local lookup for context extension objects. + if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || + object->IsJSContextExtensionObject()) { + *attributes = object->GetLocalPropertyAttribute(*name); } else { - ASSERT(context->IsGlobalContext() || - context->IsFunctionContext() || - context->IsWithContext()); - // Global, function, and with contexts may have an object in the - // extension slot. - Handle<JSObject> extension(JSObject::cast(context->extension()), - isolate); - // Context extension objects needs to behave as if they have no - // prototype. So even if we want to follow prototype chains, we - // need to only do a local lookup for context extension objects. - if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || - extension->IsJSContextExtensionObject()) { - *attributes = extension->GetLocalPropertyAttribute(*name); - } else { - *attributes = extension->GetPropertyAttribute(*name); - } - if (*attributes != ABSENT) { - // property found - if (FLAG_trace_contexts) { - PrintF("=> found property in context object %p\n", - reinterpret_cast<void*>(*extension)); - } - return extension; + *attributes = object->GetPropertyAttribute(*name); + } + if (*attributes != ABSENT) { + if (FLAG_trace_contexts) { + PrintF("=> found property in context object %p\n", + reinterpret_cast<void*>(*object)); } + return object; } } - // Check serialized scope information of functions and blocks. Only - // functions can have parameters, and a function name. + // 2. Check the context proper if it has slots. if (context->IsFunctionContext() || context->IsBlockContext()) { - // We may have context-local slots. Check locals in the context. - Handle<SerializedScopeInfo> scope_info; + // Use serialized scope information of functions and blocks to search + // for the context index. + Handle<ScopeInfo> scope_info; if (context->IsFunctionContext()) { - scope_info = Handle<SerializedScopeInfo>( + scope_info = Handle<ScopeInfo>( context->closure()->shared()->scope_info(), isolate); } else { - ASSERT(context->IsBlockContext()); - scope_info = Handle<SerializedScopeInfo>( - SerializedScopeInfo::cast(context->extension()), isolate); + scope_info = Handle<ScopeInfo>( + ScopeInfo::cast(context->extension()), isolate); } - - Variable::Mode mode; - int index = scope_info->ContextSlotIndex(*name, &mode); - ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS); - if (index >= 0) { + VariableMode mode; + InitializationFlag init_flag; + int slot_index = scope_info->ContextSlotIndex(*name, &mode, &init_flag); + ASSERT(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS); + if (slot_index >= 0) { if (FLAG_trace_contexts) { PrintF("=> found local in context slot %d (mode = %d)\n", - index, mode); + slot_index, mode); } - *index_ = index; + *index = slot_index; // Note: Fixed context slots are statically allocated by the compiler. // Statically allocated variables always have a statically known mode, // which is the mode with which they were declared when added to the @@ -181,23 +162,31 @@ Handle<Object> Context::Lookup(Handle<String> name, // declared variables that were introduced through declaration nodes) // must not appear here. switch (mode) { - case Variable::INTERNAL: // Fall through. - case Variable::VAR: + case INTERNAL: // Fall through. + case VAR: *attributes = NONE; *binding_flags = MUTABLE_IS_INITIALIZED; break; - case Variable::LET: + case LET: *attributes = NONE; - *binding_flags = MUTABLE_CHECK_INITIALIZED; + *binding_flags = (init_flag == kNeedsInitialization) + ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; + break; + case CONST: + *attributes = READ_ONLY; + *binding_flags = (init_flag == kNeedsInitialization) + ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; break; - case Variable::CONST: + case CONST_HARMONY: *attributes = READ_ONLY; - *binding_flags = IMMUTABLE_CHECK_INITIALIZED; + *binding_flags = (init_flag == kNeedsInitialization) + ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : + IMMUTABLE_IS_INITIALIZED_HARMONY; break; - case Variable::DYNAMIC: - case Variable::DYNAMIC_GLOBAL: - case Variable::DYNAMIC_LOCAL: - case Variable::TEMPORARY: + case DYNAMIC: + case DYNAMIC_GLOBAL: + case DYNAMIC_LOCAL: + case TEMPORARY: UNREACHABLE(); break; } @@ -206,22 +195,37 @@ Handle<Object> Context::Lookup(Handle<String> name, // Check the slot corresponding to the intermediate context holding // only the function name variable. - if (follow_context_chain) { - int index = scope_info->FunctionContextSlotIndex(*name); - if (index >= 0) { + if (follow_context_chain && context->IsFunctionContext()) { + VariableMode mode; + int function_index = scope_info->FunctionContextSlotIndex(*name, &mode); + if (function_index >= 0) { if (FLAG_trace_contexts) { PrintF("=> found intermediate function in context slot %d\n", - index); + function_index); } - *index_ = index; + *index = function_index; *attributes = READ_ONLY; - *binding_flags = IMMUTABLE_IS_INITIALIZED; + ASSERT(mode == CONST || mode == CONST_HARMONY); + *binding_flags = (mode == CONST) + ? IMMUTABLE_IS_INITIALIZED : IMMUTABLE_IS_INITIALIZED_HARMONY; return context; } } + + } else if (context->IsCatchContext()) { + // Catch contexts have the variable name in the extension slot. + if (name->Equals(String::cast(context->extension()))) { + if (FLAG_trace_contexts) { + PrintF("=> found in catch context\n"); + } + *index = Context::THROWN_OBJECT_INDEX; + *attributes = NONE; + *binding_flags = MUTABLE_IS_INITIALIZED; + return context; + } } - // Proceed with the previous context. + // 3. Prepare to continue with the previous (next outermost) context. if (context->IsGlobalContext()) { follow_context_chain = false; } else { @@ -236,68 +240,6 @@ Handle<Object> Context::Lookup(Handle<String> name, } -bool Context::GlobalIfNotShadowedByEval(Handle<String> name) { - Context* context = this; - - // Check that there is no local with the given name in contexts - // before the global context and check that there are no context - // extension objects (conservative check for with statements). - while (!context->IsGlobalContext()) { - // Check if the context is a catch or with context, or has introduced - // bindings by calling non-strict eval. - if (context->has_extension()) return false; - - // Not a with context so it must be a function context. - ASSERT(context->IsFunctionContext()); - - // Check non-parameter locals. - Handle<SerializedScopeInfo> scope_info( - context->closure()->shared()->scope_info()); - Variable::Mode mode; - int index = scope_info->ContextSlotIndex(*name, &mode); - ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS); - if (index >= 0) return false; - - // Check parameter locals. - int param_index = scope_info->ParameterIndex(*name); - if (param_index >= 0) return false; - - // Check context only holding the function name variable. - index = scope_info->FunctionContextSlotIndex(*name); - if (index >= 0) return false; - context = context->previous(); - } - - // No local or potential with statement found so the variable is - // global unless it is shadowed by an eval-introduced variable. - return true; -} - - -void Context::ComputeEvalScopeInfo(bool* outer_scope_calls_eval, - bool* outer_scope_calls_non_strict_eval) { - // Skip up the context chain checking all the function contexts to see - // whether they call eval. - Context* context = this; - while (!context->IsGlobalContext()) { - if (context->IsFunctionContext()) { - Handle<SerializedScopeInfo> scope_info( - context->closure()->shared()->scope_info()); - if (scope_info->CallsEval()) { - *outer_scope_calls_eval = true; - if (!scope_info->IsStrictMode()) { - // No need to go further since the answers will not change from - // here. - *outer_scope_calls_non_strict_eval = true; - return; - } - } - } - context = context->previous(); - } -} - - void Context::AddOptimizedFunction(JSFunction* function) { ASSERT(IsGlobalContext()); #ifdef DEBUG diff --git a/deps/v8/src/contexts.h b/deps/v8/src/contexts.h index 505f86c8ca..1f88c946de 100644 --- a/deps/v8/src/contexts.h +++ b/deps/v8/src/contexts.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -46,24 +46,43 @@ enum ContextLookupFlags { // ES5 10.2 defines lexical environments with mutable and immutable bindings. // Immutable bindings have two states, initialized and uninitialized, and -// their state is changed by the InitializeImmutableBinding method. +// their state is changed by the InitializeImmutableBinding method. The +// BindingFlags enum represents information if a binding has definitely been +// initialized. A mutable binding does not need to be checked and thus has +// the BindingFlag MUTABLE_IS_INITIALIZED. +// +// There are two possibilities for immutable bindings +// * 'const' declared variables. They are initialized when evaluating the +// corresponding declaration statement. They need to be checked for being +// initialized and thus get the flag IMMUTABLE_CHECK_INITIALIZED. +// * The function name of a named function literal. The binding is immediately +// initialized when entering the function and thus does not need to be +// checked. it gets the BindingFlag IMMUTABLE_IS_INITIALIZED. +// Accessing an uninitialized binding produces the undefined value. // // The harmony proposal for block scoped bindings also introduces the -// uninitialized state for mutable bindings. A 'let' declared variable -// is a mutable binding that is created uninitalized upon activation of its -// lexical environment and it is initialized when evaluating its declaration -// statement. Var declared variables are mutable bindings that are -// immediately initialized upon creation. The BindingFlags enum represents -// information if a binding has definitely been initialized. 'const' declared -// variables are created as uninitialized immutable bindings. - -// In harmony mode accessing an uninitialized binding produces a reference -// error. +// uninitialized state for mutable bindings. +// * A 'let' declared variable. They are initialized when evaluating the +// corresponding declaration statement. They need to be checked for being +// initialized and thus get the flag MUTABLE_CHECK_INITIALIZED. +// * A 'var' declared variable. It is initialized immediately upon creation +// and thus doesn't need to be checked. It gets the flag +// MUTABLE_IS_INITIALIZED. +// * Catch bound variables, function parameters and variables introduced by +// function declarations are initialized immediately and do not need to be +// checked. Thus they get the flag MUTABLE_IS_INITIALIZED. +// Immutable bindings in harmony mode get the _HARMONY flag variants. Accessing +// an uninitialized binding produces a reference error. +// +// In V8 uninitialized bindings are set to the hole value upon creation and set +// to a different value upon initialization. enum BindingFlags { MUTABLE_IS_INITIALIZED, MUTABLE_CHECK_INITIALIZED, IMMUTABLE_IS_INITIALIZED, IMMUTABLE_CHECK_INITIALIZED, + IMMUTABLE_IS_INITIALIZED_HARMONY, + IMMUTABLE_CHECK_INITIALIZED_HARMONY, MISSING_BINDING }; @@ -85,7 +104,11 @@ enum BindingFlags { V(STRING_FUNCTION_INDEX, JSFunction, string_function) \ V(STRING_FUNCTION_PROTOTYPE_MAP_INDEX, Map, string_function_prototype_map) \ V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \ + V(INTERNAL_ARRAY_FUNCTION_INDEX, JSFunction, internal_array_function) \ V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \ + V(SMI_JS_ARRAY_MAP_INDEX, Object, smi_js_array_map) \ + V(DOUBLE_JS_ARRAY_MAP_INDEX, Object, double_js_array_map) \ + V(OBJECT_JS_ARRAY_MAP_INDEX, Object, object_js_array_map) \ V(DATE_FUNCTION_INDEX, JSFunction, date_function) \ V(JSON_OBJECT_INDEX, JSObject, json_object) \ V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \ @@ -109,7 +132,6 @@ enum BindingFlags { V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \ V(STRICT_MODE_FUNCTION_INSTANCE_MAP_INDEX, Map, \ strict_mode_function_instance_map) \ - V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\ V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\ V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \ V(ALIASED_ARGUMENTS_BOILERPLATE_INDEX, JSObject, \ @@ -134,9 +156,13 @@ enum BindingFlags { V(MAP_CACHE_INDEX, Object, map_cache) \ V(CONTEXT_DATA_INDEX, Object, data) \ V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \ + V(TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX, JSFunction, \ + to_complete_property_descriptor) \ V(DERIVED_HAS_TRAP_INDEX, JSFunction, derived_has_trap) \ V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \ - V(DERIVED_SET_TRAP_INDEX, JSFunction, derived_set_trap) + V(DERIVED_SET_TRAP_INDEX, JSFunction, derived_set_trap) \ + V(PROXY_ENUMERATE, JSFunction, proxy_enumerate) \ + V(RANDOM_SEED_INDEX, ByteArray, random_seed) // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -192,7 +218,8 @@ class Context: public FixedArray { PREVIOUS_INDEX, // The extension slot is used for either the global object (in global // contexts), eval extension object (function contexts), subject of with - // (with contexts), or the variable name (catch contexts). + // (with contexts), or the variable name (catch contexts), the serialized + // scope info (block contexts). EXTENSION_INDEX, GLOBAL_INDEX, MIN_CONTEXT_SLOTS, @@ -206,7 +233,6 @@ class Context: public FixedArray { ARGUMENTS_BOILERPLATE_INDEX, ALIASED_ARGUMENTS_BOILERPLATE_INDEX, STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX, - JS_ARRAY_MAP_INDEX, REGEXP_RESULT_MAP_INDEX, FUNCTION_MAP_INDEX, STRICT_MODE_FUNCTION_MAP_INDEX, @@ -220,7 +246,11 @@ class Context: public FixedArray { STRING_FUNCTION_INDEX, STRING_FUNCTION_PROTOTYPE_MAP_INDEX, OBJECT_FUNCTION_INDEX, + INTERNAL_ARRAY_FUNCTION_INDEX, ARRAY_FUNCTION_INDEX, + SMI_JS_ARRAY_MAP_INDEX, + DOUBLE_JS_ARRAY_MAP_INDEX, + OBJECT_JS_ARRAY_MAP_INDEX, DATE_FUNCTION_INDEX, JSON_OBJECT_INDEX, REGEXP_FUNCTION_INDEX, @@ -252,9 +282,12 @@ class Context: public FixedArray { OUT_OF_MEMORY_INDEX, CONTEXT_DATA_INDEX, ALLOW_CODE_GEN_FROM_STRINGS_INDEX, + TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX, DERIVED_HAS_TRAP_INDEX, DERIVED_GET_TRAP_INDEX, DERIVED_SET_TRAP_INDEX, + PROXY_ENUMERATE, + RANDOM_SEED_INDEX, // Properties from here are treated as weak references by the full GC. // Scavenge treats them as strong references. @@ -330,18 +363,24 @@ class Context: public FixedArray { // Mark the global context with out of memory. inline void mark_out_of_memory(); - // The exception holder is the object used as a with object in - // the implementation of a catch block. - bool is_exception_holder(Object* object) { - return IsCatchContext() && extension() == object; - } - // A global context hold a list of all functions which have been optimized. void AddOptimizedFunction(JSFunction* function); void RemoveOptimizedFunction(JSFunction* function); Object* OptimizedFunctionsListHead(); void ClearOptimizedFunctions(); + static int GetContextMapIndexFromElementsKind( + ElementsKind elements_kind) { + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + return Context::DOUBLE_JS_ARRAY_MAP_INDEX; + } else if (elements_kind == FAST_ELEMENTS) { + return Context::OBJECT_JS_ARRAY_MAP_INDEX; + } else { + ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); + return Context::SMI_JS_ARRAY_MAP_INDEX; + } + } + #define GLOBAL_CONTEXT_FIELD_ACCESSORS(index, type, name) \ void set_##name(type* value) { \ ASSERT(IsGlobalContext()); \ @@ -355,46 +394,28 @@ class Context: public FixedArray { #undef GLOBAL_CONTEXT_FIELD_ACCESSORS // Lookup the the slot called name, starting with the current context. - // There are 4 possible outcomes: + // There are three possibilities: // - // 1) index_ >= 0 && result->IsContext(): - // most common case, the result is a Context, and index is the - // context slot index, and the slot exists. - // attributes == READ_ONLY for the function name variable, NONE otherwise. + // 1) result->IsContext(): + // The binding was found in a context. *index is always the + // non-negative slot index. *attributes is NONE for var and let + // declarations, READ_ONLY for const declarations (never ABSENT). // - // 2) index_ >= 0 && result->IsJSObject(): - // the result is the JSObject arguments object, the index is the parameter - // index, i.e., key into the arguments object, and the property exists. - // attributes != ABSENT. + // 2) result->IsJSObject(): + // The binding was found as a named property in a context extension + // object (i.e., was introduced via eval), as a property on the subject + // of with, or as a property of the global object. *index is -1 and + // *attributes is not ABSENT. // - // 3) index_ < 0 && result->IsJSObject(): - // the result is the JSObject extension context or the global object, - // and the name is the property name, and the property exists. - // attributes != ABSENT. - // - // 4) index_ < 0 && result.is_null(): - // there was no context found with the corresponding property. - // attributes == ABSENT. + // 3) result.is_null(): + // There was no binding found, *index is always -1 and *attributes is + // always ABSENT. Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags, - int* index_, + int* index, PropertyAttributes* attributes, BindingFlags* binding_flags); - // Determine if a local variable with the given name exists in a - // context. Do not consider context extension objects. This is - // used for compiling code using eval. If the context surrounding - // the eval call does not have a local variable with this name and - // does not contain a with statement the property is global unless - // it is shadowed by a property in an extension object introduced by - // eval. - bool GlobalIfNotShadowedByEval(Handle<String> name); - - // Determine if any function scope in the context call eval and if - // any of those calls are in non-strict mode. - void ComputeEvalScopeInfo(bool* outer_scope_calls_eval, - bool* outer_scope_calls_non_strict_eval); - // Code generation support. static int SlotOffset(int index) { return kHeaderSize + index * kPointerSize - kHeapObjectTag; diff --git a/deps/v8/src/conversions-inl.h b/deps/v8/src/conversions-inl.h index 41cf0d54c2..b098a1c29c 100644 --- a/deps/v8/src/conversions-inl.h +++ b/deps/v8/src/conversions-inl.h @@ -46,15 +46,15 @@ namespace v8 { namespace internal { -static inline double JunkStringValue() { - return std::numeric_limits<double>::quiet_NaN(); +inline double JunkStringValue() { + return BitCast<double, uint64_t>(kQuietNaNMask); } // The fast double-to-unsigned-int conversion routine does not guarantee // rounding towards zero, or any reasonable value if the argument is larger // than what fits in an unsigned 32-bit integer. -static inline unsigned int FastD2UI(double x) { +inline unsigned int FastD2UI(double x) { // There is no unsigned version of lrint, so there is no fast path // in this function as there is in FastD2I. Using lrint doesn't work // for values of 2^31 and above. @@ -80,7 +80,7 @@ static inline unsigned int FastD2UI(double x) { } -static inline double DoubleToInteger(double x) { +inline double DoubleToInteger(double x) { if (isnan(x)) return 0; if (!isfinite(x) || x == 0) return x; return (x >= 0) ? floor(x) : ceil(x); @@ -103,9 +103,9 @@ int32_t DoubleToInt32(double x) { template <class Iterator, class EndMark> -static bool SubStringEquals(Iterator* current, - EndMark end, - const char* substring) { +bool SubStringEquals(Iterator* current, + EndMark end, + const char* substring) { ASSERT(**current == *substring); for (substring++; *substring != '\0'; substring++) { ++*current; @@ -119,9 +119,9 @@ static bool SubStringEquals(Iterator* current, // Returns true if a nonspace character has been found and false if the // end was been reached before finding a nonspace character. template <class Iterator, class EndMark> -static inline bool AdvanceToNonspace(UnicodeCache* unicode_cache, - Iterator* current, - EndMark end) { +inline bool AdvanceToNonspace(UnicodeCache* unicode_cache, + Iterator* current, + EndMark end) { while (*current != end) { if (!unicode_cache->IsWhiteSpace(**current)) return true; ++*current; @@ -132,11 +132,11 @@ static inline bool AdvanceToNonspace(UnicodeCache* unicode_cache, // Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. template <int radix_log_2, class Iterator, class EndMark> -static double InternalStringToIntDouble(UnicodeCache* unicode_cache, - Iterator current, - EndMark end, - bool negative, - bool allow_trailing_junk) { +double InternalStringToIntDouble(UnicodeCache* unicode_cache, + Iterator current, + EndMark end, + bool negative, + bool allow_trailing_junk) { ASSERT(current != end); // Skip leading 0s. @@ -235,10 +235,10 @@ static double InternalStringToIntDouble(UnicodeCache* unicode_cache, template <class Iterator, class EndMark> -static double InternalStringToInt(UnicodeCache* unicode_cache, - Iterator current, - EndMark end, - int radix) { +double InternalStringToInt(UnicodeCache* unicode_cache, + Iterator current, + EndMark end, + int radix) { const bool allow_trailing_junk = true; const double empty_string_val = JunkStringValue(); @@ -430,11 +430,11 @@ static double InternalStringToInt(UnicodeCache* unicode_cache, // 2. *current - gets the current character in the sequence. // 3. ++current (advances the position). template <class Iterator, class EndMark> -static double InternalStringToDouble(UnicodeCache* unicode_cache, - Iterator current, - EndMark end, - int flags, - double empty_string_val) { +double InternalStringToDouble(UnicodeCache* unicode_cache, + Iterator current, + EndMark end, + int flags, + double empty_string_val) { // To make sure that iterator dereferencing is valid the following // convention is used: // 1. Each '++current' statement is followed by check for equality to 'end'. diff --git a/deps/v8/src/conversions.h b/deps/v8/src/conversions.h index e51ad6501c..70559c9e9d 100644 --- a/deps/v8/src/conversions.h +++ b/deps/v8/src/conversions.h @@ -28,8 +28,6 @@ #ifndef V8_CONVERSIONS_H_ #define V8_CONVERSIONS_H_ -#include <limits> - #include "utils.h" namespace v8 { @@ -47,14 +45,14 @@ class UnicodeCache; const int kMaxSignificantDigits = 772; -static inline bool isDigit(int x, int radix) { +inline bool isDigit(int x, int radix) { return (x >= '0' && x <= '9' && x < '0' + radix) || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); } -static inline double SignedZero(bool negative) { +inline double SignedZero(bool negative) { return negative ? -0.0 : 0.0; } @@ -63,16 +61,16 @@ static inline double SignedZero(bool negative) { // rounding towards zero. // The result is unspecified if x is infinite or NaN, or if the rounded // integer value is outside the range of type int. -static inline int FastD2I(double x) { +inline int FastD2I(double x) { // The static_cast convertion from double to int used to be slow, but // as new benchmarks show, now it is much faster than lrint(). return static_cast<int>(x); } -static inline unsigned int FastD2UI(double x); +inline unsigned int FastD2UI(double x); -static inline double FastI2D(int x) { +inline double FastI2D(int x) { // There is no rounding involved in converting an integer to a // double, so this code should compile to a few instructions without // any FPU pipeline stalls. @@ -80,7 +78,7 @@ static inline double FastI2D(int x) { } -static inline double FastUI2D(unsigned x) { +inline double FastUI2D(unsigned x) { // There is no rounding involved in converting an unsigned integer to a // double, so this code should compile to a few instructions without // any FPU pipeline stalls. @@ -89,15 +87,15 @@ static inline double FastUI2D(unsigned x) { // This function should match the exact semantics of ECMA-262 9.4. -static inline double DoubleToInteger(double x); +inline double DoubleToInteger(double x); // This function should match the exact semantics of ECMA-262 9.5. -static inline int32_t DoubleToInt32(double x); +inline int32_t DoubleToInt32(double x); // This function should match the exact semantics of ECMA-262 9.6. -static inline uint32_t DoubleToUint32(double x) { +inline uint32_t DoubleToUint32(double x) { return static_cast<uint32_t>(DoubleToInt32(x)); } diff --git a/deps/v8/src/cpu-profiler.cc b/deps/v8/src/cpu-profiler.cc index d74c034ac5..3cbac77858 100644 --- a/deps/v8/src/cpu-profiler.cc +++ b/deps/v8/src/cpu-profiler.cc @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -39,13 +39,14 @@ namespace v8 { namespace internal { -static const int kEventsBufferSize = 256*KB; -static const int kTickSamplesBufferChunkSize = 64*KB; +static const int kEventsBufferSize = 256 * KB; +static const int kTickSamplesBufferChunkSize = 64 * KB; static const int kTickSamplesBufferChunksCount = 16; +static const int kProfilerStackSize = 64 * KB; ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator) - : Thread("v8:ProfEvntProc"), + : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), generator_(generator), running_(true), ticks_buffer_(sizeof(TickSampleEventRecord), @@ -493,7 +494,7 @@ void CpuProfiler::StartProcessorIfNotStarted() { NoBarrier_Store(&is_profiling_, true); processor_->Start(); // Enumerate stuff we already have in the heap. - if (isolate->heap()->HasBeenSetup()) { + if (isolate->heap()->HasBeenSetUp()) { if (!FLAG_prof_browser_mode) { bool saved_log_code_flag = FLAG_log_code; FLAG_log_code = true; @@ -562,7 +563,7 @@ void CpuProfiler::StopProcessor() { } -void CpuProfiler::Setup() { +void CpuProfiler::SetUp() { Isolate* isolate = Isolate::Current(); if (isolate->cpu_profiler() == NULL) { isolate->set_cpu_profiler(new CpuProfiler()); diff --git a/deps/v8/src/cpu-profiler.h b/deps/v8/src/cpu-profiler.h index a71c0e0ab4..3f4fec5f45 100644 --- a/deps/v8/src/cpu-profiler.h +++ b/deps/v8/src/cpu-profiler.h @@ -204,7 +204,7 @@ namespace internal { // TODO(isolates): isolatify this class. class CpuProfiler { public: - static void Setup(); + static void SetUp(); static void TearDown(); static void StartProfiling(const char* title); @@ -230,11 +230,11 @@ class CpuProfiler { Code* code, String* name); static void CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code, - SharedFunctionInfo *shared, + SharedFunctionInfo* shared, String* name); static void CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code, - SharedFunctionInfo *shared, + SharedFunctionInfo* shared, String* source, int line); static void CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code, int args_count); diff --git a/deps/v8/src/cpu.h b/deps/v8/src/cpu.h index 2525484a01..247af71aa3 100644 --- a/deps/v8/src/cpu.h +++ b/deps/v8/src/cpu.h @@ -53,7 +53,7 @@ namespace internal { class CPU : public AllStatic { public: // Initializes the cpu architecture support. Called once at VM startup. - static void Setup(); + static void SetUp(); static bool SupportsCrankshaft(); diff --git a/deps/v8/src/d8-debug.cc b/deps/v8/src/d8-debug.cc index adefba7322..de0faa8ae6 100644 --- a/deps/v8/src/d8-debug.cc +++ b/deps/v8/src/d8-debug.cc @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,6 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#ifdef ENABLE_DEBUGGER_SUPPORT #include "d8.h" #include "d8-debug.h" @@ -168,7 +169,7 @@ void RemoteDebugger::Run() { bool ok; // Make sure that socket support is initialized. - ok = i::Socket::Setup(); + ok = i::Socket::SetUp(); if (!ok) { printf("Unable to initialize socket support %d\n", i::Socket::LastError()); return; @@ -309,9 +310,7 @@ void RemoteDebugger::HandleKeyboardCommand(char* command) { Handle<Value> request = Shell::DebugCommandToJSONRequest(String::New(command)); if (try_catch.HasCaught()) { - v8::String::Utf8Value exception(try_catch.Exception()); - const char* exception_string = Shell::ToCString(exception); - printf("%s\n", exception_string); + Shell::ReportException(&try_catch); PrintPrompt(); return; } @@ -367,3 +366,5 @@ void KeyboardThread::Run() { } // namespace v8 + +#endif // ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/d8-posix.cc b/deps/v8/src/d8-posix.cc index 289c3b0ae8..8a278e4e42 100644 --- a/deps/v8/src/d8-posix.cc +++ b/deps/v8/src/d8-posix.cc @@ -366,7 +366,8 @@ static Handle<Value> GetStdout(int child_fd, // We're disabling usage of waitid in Mac OS X because it doens't work for us: // a parent process hangs on waiting while a child process is already a zombie. // See http://code.google.com/p/v8/issues/detail?id=401. -#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) +#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \ + && !defined(__NetBSD__) #if !defined(__FreeBSD__) #define HAS_WAITID 1 #endif diff --git a/deps/v8/src/d8-readline.cc b/deps/v8/src/d8-readline.cc index 71be933109..ed7721c513 100644 --- a/deps/v8/src/d8-readline.cc +++ b/deps/v8/src/d8-readline.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -49,10 +49,14 @@ namespace v8 { class ReadLineEditor: public LineEditor { public: ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { } - virtual i::SmartArrayPointer<char> Prompt(const char* prompt); + virtual Handle<String> Prompt(const char* prompt); virtual bool Open(); virtual bool Close(); virtual void AddHistory(const char* str); + + static const char* kHistoryFileName; + static const int kMaxHistoryEntries; + private: static char** AttemptedCompletion(const char* text, int start, int end); static char* CompletionGenerator(const char* text, int state); @@ -66,25 +70,38 @@ char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"', '\0'}; +const char* ReadLineEditor::kHistoryFileName = ".d8_history"; +const int ReadLineEditor::kMaxHistoryEntries = 1000; + + bool ReadLineEditor::Open() { rl_initialize(); rl_attempted_completion_function = AttemptedCompletion; rl_completer_word_break_characters = kWordBreakCharacters; rl_bind_key('\t', rl_complete); using_history(); - stifle_history(Shell::kMaxHistoryEntries); - return read_history(Shell::kHistoryFileName) == 0; + stifle_history(kMaxHistoryEntries); + return read_history(kHistoryFileName) == 0; } bool ReadLineEditor::Close() { - return write_history(Shell::kHistoryFileName) == 0; + return write_history(kHistoryFileName) == 0; } -i::SmartArrayPointer<char> ReadLineEditor::Prompt(const char* prompt) { - char* result = readline(prompt); - return i::SmartArrayPointer<char>(result); +Handle<String> ReadLineEditor::Prompt(const char* prompt) { + char* result = NULL; + { // Release lock for blocking input. + Unlocker unlock(Isolate::GetCurrent()); + result = readline(prompt); + } + if (result != NULL) { + AddHistory(result); + } else { + return Handle<String>(); + } + return String::New(result); } @@ -118,10 +135,10 @@ char* ReadLineEditor::CompletionGenerator(const char* text, int state) { static unsigned current_index; static Persistent<Array> current_completions; if (state == 0) { - i::SmartArrayPointer<char> full_text(i::StrNDup(rl_line_buffer, rl_point)); HandleScope scope; + Local<String> full_text = String::New(rl_line_buffer, rl_point); Handle<Array> completions = - Shell::GetCompletions(String::New(text), String::New(*full_text)); + Shell::GetCompletions(String::New(text), full_text); current_completions = Persistent<Array>::New(completions); current_index = 0; } diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index 63a7d157a9..ad35af6c22 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -66,11 +66,7 @@ namespace v8 { - -#ifndef V8_SHARED LineEditor *LineEditor::first_ = NULL; -const char* Shell::kHistoryFileName = ".d8_history"; -const int Shell::kMaxHistoryEntries = 1000; LineEditor::LineEditor(Type type, const char* name) @@ -96,36 +92,37 @@ LineEditor* LineEditor::Get() { class DumbLineEditor: public LineEditor { public: DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { } - virtual i::SmartArrayPointer<char> Prompt(const char* prompt); + virtual Handle<String> Prompt(const char* prompt); }; static DumbLineEditor dumb_line_editor; -i::SmartArrayPointer<char> DumbLineEditor::Prompt(const char* prompt) { - static const int kBufferSize = 256; - char buffer[kBufferSize]; +Handle<String> DumbLineEditor::Prompt(const char* prompt) { printf("%s", prompt); - char* str = fgets(buffer, kBufferSize, stdin); - return i::SmartArrayPointer<char>(str ? i::StrDup(str) : str); + return Shell::ReadFromStdin(); } +#ifndef V8_SHARED CounterMap* Shell::counter_map_; i::OS::MemoryMappedFile* Shell::counters_file_ = NULL; CounterCollection Shell::local_counters_; CounterCollection* Shell::counters_ = &local_counters_; i::Mutex* Shell::context_mutex_(i::OS::CreateMutex()); Persistent<Context> Shell::utility_context_; -LineEditor* Shell::console = NULL; #endif // V8_SHARED +LineEditor* Shell::console = NULL; Persistent<Context> Shell::evaluation_context_; ShellOptions Shell::options; const char* Shell::kPrompt = "d8> "; +const int MB = 1024 * 1024; + + #ifndef V8_SHARED bool CounterMap::Match(void* key1, void* key2) { const char* name1 = reinterpret_cast<const char*>(key1); @@ -146,11 +143,11 @@ bool Shell::ExecuteString(Handle<String> source, Handle<Value> name, bool print_result, bool report_exceptions) { -#ifndef V8_SHARED +#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) bool FLAG_debugger = i::FLAG_debugger; #else bool FLAG_debugger = false; -#endif // V8_SHARED +#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT HandleScope handle_scope; TryCatch try_catch; options.script_executed = true; @@ -178,7 +175,8 @@ bool Shell::ExecuteString(Handle<String> source, // If all went well and the result wasn't undefined then print // the returned value. v8::String::Utf8Value str(result); - fwrite(*str, sizeof(**str), str.length(), stdout); + size_t count = fwrite(*str, sizeof(**str), str.length(), stdout); + (void) count; // Silence GCC-4.5.x "unused result" warning. printf("\n"); } return true; @@ -237,7 +235,7 @@ Handle<Value> Shell::Read(const Arguments& args) { } -Handle<Value> Shell::ReadLine(const Arguments& args) { +Handle<String> Shell::ReadFromStdin() { static const int kBufferSize = 256; char buffer[kBufferSize]; Handle<String> accumulator = String::New(""); @@ -246,7 +244,12 @@ Handle<Value> Shell::ReadLine(const Arguments& args) { // Continue reading if the line ends with an escape '\\' or the line has // not been fully read into the buffer yet (does not end with '\n'). // If fgets gets an error, just give up. - if (fgets(buffer, kBufferSize, stdin) == NULL) return Null(); + char* input = NULL; + { // Release lock for blocking input. + Unlocker unlock(Isolate::GetCurrent()); + input = fgets(buffer, kBufferSize, stdin); + } + if (input == NULL) return Handle<String>(); length = static_cast<int>(strlen(buffer)); if (length == 0) { return accumulator; @@ -280,51 +283,161 @@ Handle<Value> Shell::Load(const Arguments& args) { return Undefined(); } +static size_t convertToUint(Local<Value> value_in, TryCatch* try_catch) { + if (value_in->IsUint32()) { + return value_in->Uint32Value(); + } + + Local<Value> number = value_in->ToNumber(); + if (try_catch->HasCaught()) return 0; + + ASSERT(number->IsNumber()); + Local<Int32> int32 = number->ToInt32(); + if (try_catch->HasCaught() || int32.IsEmpty()) return 0; + + int32_t raw_value = int32->Int32Value(); + if (try_catch->HasCaught()) return 0; + + if (raw_value < 0) { + ThrowException(String::New("Array length must not be negative.")); + return 0; + } + + static const int kMaxLength = 0x3fffffff; +#ifndef V8_SHARED + ASSERT(kMaxLength == i::ExternalArray::kMaxLength); +#endif // V8_SHARED + if (raw_value > static_cast<int32_t>(kMaxLength)) { + ThrowException( + String::New("Array length exceeds maximum length.")); + } + return static_cast<size_t>(raw_value); +} + + +const char kArrayBufferReferencePropName[] = "_is_array_buffer_"; +const char kArrayBufferMarkerPropName[] = "_array_buffer_ref_"; + Handle<Value> Shell::CreateExternalArray(const Arguments& args, ExternalArrayType type, size_t element_size) { + TryCatch try_catch; + bool is_array_buffer_construct = element_size == 0; + if (is_array_buffer_construct) { + type = v8::kExternalByteArray; + element_size = 1; + } ASSERT(element_size == 1 || element_size == 2 || element_size == 4 || element_size == 8); - if (args.Length() != 1) { + if (args.Length() == 0) { return ThrowException( - String::New("Array constructor needs one parameter.")); + String::New("Array constructor must have at least one " + "parameter.")); } - static const int kMaxLength = 0x3fffffff; -#ifndef V8_SHARED - ASSERT(kMaxLength == i::ExternalArray::kMaxLength); -#endif // V8_SHARED - size_t length = 0; - if (args[0]->IsUint32()) { - length = args[0]->Uint32Value(); - } else { - Local<Number> number = args[0]->ToNumber(); - if (number.IsEmpty() || !number->IsNumber()) { - return ThrowException(String::New("Array length must be a number.")); + bool first_arg_is_array_buffer = + args[0]->IsObject() && + args[0]->ToObject()->Get( + String::New(kArrayBufferMarkerPropName))->IsTrue(); + // Currently, only the following constructors are supported: + // TypedArray(unsigned long length) + // TypedArray(ArrayBuffer buffer, + // optional unsigned long byteOffset, + // optional unsigned long length) + if (args.Length() > 3) { + return ThrowException( + String::New("Array constructor from ArrayBuffer must " + "have 1-3 parameters.")); + } + + Local<Value> length_value = (args.Length() < 3) + ? (first_arg_is_array_buffer + ? args[0]->ToObject()->Get(String::New("length")) + : args[0]) + : args[2]; + size_t length = convertToUint(length_value, &try_catch); + if (try_catch.HasCaught()) return try_catch.Exception(); + + void* data = NULL; + size_t offset = 0; + + Handle<Object> array = Object::New(); + if (first_arg_is_array_buffer) { + Handle<Object> derived_from = args[0]->ToObject(); + data = derived_from->GetIndexedPropertiesExternalArrayData(); + + size_t array_buffer_length = convertToUint( + derived_from->Get(String::New("length")), + &try_catch); + if (try_catch.HasCaught()) return try_catch.Exception(); + + if (data == NULL && array_buffer_length != 0) { + return ThrowException( + String::New("ArrayBuffer doesn't have data")); } - int32_t raw_length = number->ToInt32()->Int32Value(); - if (raw_length < 0) { - return ThrowException(String::New("Array length must not be negative.")); + + if (args.Length() > 1) { + offset = convertToUint(args[1], &try_catch); + if (try_catch.HasCaught()) return try_catch.Exception(); + + // The given byteOffset must be a multiple of the element size of the + // specific type, otherwise an exception is raised. + if (offset % element_size != 0) { + return ThrowException( + String::New("offset must be multiple of element_size")); + } } - if (raw_length > static_cast<int32_t>(kMaxLength)) { + + if (offset > array_buffer_length) { return ThrowException( - String::New("Array length exceeds maximum length.")); + String::New("byteOffset must be less than ArrayBuffer length.")); } - length = static_cast<size_t>(raw_length); - } - if (length > static_cast<size_t>(kMaxLength)) { - return ThrowException(String::New("Array length exceeds maximum length.")); + + if (args.Length() == 2) { + // If length is not explicitly specified, the length of the ArrayBuffer + // minus the byteOffset must be a multiple of the element size of the + // specific type, or an exception is raised. + length = array_buffer_length - offset; + } + + if (args.Length() != 3) { + if (length % element_size != 0) { + return ThrowException( + String::New("ArrayBuffer length minus the byteOffset must be a " + "multiple of the element size")); + } + length /= element_size; + } + + // If a given byteOffset and length references an area beyond the end of + // the ArrayBuffer an exception is raised. + if (offset + (length * element_size) > array_buffer_length) { + return ThrowException( + String::New("length references an area beyond the end of the " + "ArrayBuffer")); + } + + // Hold a reference to the ArrayBuffer so its buffer doesn't get collected. + array->Set(String::New(kArrayBufferReferencePropName), args[0], ReadOnly); } - void* data = calloc(length, element_size); - if (data == NULL) { - return ThrowException(String::New("Memory allocation failed.")); + + if (is_array_buffer_construct) { + array->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly); } - Handle<Object> array = Object::New(); + Persistent<Object> persistent_array = Persistent<Object>::New(array); persistent_array.MakeWeak(data, ExternalArrayWeakCallback); persistent_array.MarkIndependent(); - array->SetIndexedPropertiesToExternalArrayData(data, type, - static_cast<int>(length)); + if (data == NULL && length != 0) { + data = calloc(length, element_size); + if (data == NULL) { + return ThrowException(String::New("Memory allocation failed.")); + } + } + + array->SetIndexedPropertiesToExternalArrayData( + reinterpret_cast<uint8_t*>(data) + offset, type, + static_cast<int>(length)); array->Set(String::New("length"), Int32::New(static_cast<int32_t>(length)), ReadOnly); array->Set(String::New("BYTES_PER_ELEMENT"), @@ -334,11 +447,22 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) { - free(data); + HandleScope scope; + Handle<String> prop_name = String::New(kArrayBufferReferencePropName); + Handle<Object> converted_object = object->ToObject(); + Local<Value> prop_value = converted_object->Get(prop_name); + if (data != NULL && !prop_value->IsObject()) { + free(data); + } object.Dispose(); } +Handle<Value> Shell::ArrayBuffer(const Arguments& args) { + return CreateExternalArray(args, v8::kExternalByteArray, 0); +} + + Handle<Value> Shell::Int8Array(const Arguments& args) { return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); } @@ -410,6 +534,10 @@ Handle<Value> Shell::Version(const Arguments& args) { void Shell::ReportException(v8::TryCatch* try_catch) { HandleScope handle_scope; +#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) + bool enter_context = !Context::InContext(); + if (enter_context) utility_context_->Enter(); +#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT v8::String::Utf8Value exception(try_catch->Exception()); const char* exception_string = ToCString(exception); Handle<Message> message = try_catch->Message(); @@ -444,6 +572,9 @@ void Shell::ReportException(v8::TryCatch* try_catch) { } } printf("\n"); +#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) + if (enter_context) utility_context_->Exit(); +#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT } @@ -481,6 +612,12 @@ Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) { Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); return val; } + + +void Shell::DispatchDebugMessages() { + v8::Context::Scope scope(Shell::evaluation_context_); + v8::Debug::ProcessDebugMessages(); +} #endif // ENABLE_DEBUGGER_SUPPORT #endif // V8_SHARED @@ -594,6 +731,7 @@ void Shell::InstallUtilityScript() { Context::Scope utility_scope(utility_context_); #ifdef ENABLE_DEBUGGER_SUPPORT + if (i::FLAG_debugger) printf("JavaScript debugger enabled\n"); // Install the debugger object in the utility scope i::Debug* debug = i::Isolate::Current()->debug(); debug->Load(); @@ -668,6 +806,8 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { global_template->Set(String::New("print"), FunctionTemplate::New(Print)); global_template->Set(String::New("write"), FunctionTemplate::New(Write)); global_template->Set(String::New("read"), FunctionTemplate::New(Read)); + global_template->Set(String::New("readbinary"), + FunctionTemplate::New(ReadBinary)); global_template->Set(String::New("readline"), FunctionTemplate::New(ReadLine)); global_template->Set(String::New("load"), FunctionTemplate::New(Load)); @@ -679,6 +819,8 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { FunctionTemplate::New(DisableProfiler)); // Bind the handlers for external arrays. + global_template->Set(String::New("ArrayBuffer"), + FunctionTemplate::New(ArrayBuffer)); global_template->Set(String::New("Int8Array"), FunctionTemplate::New(Int8Array)); global_template->Set(String::New("Uint8Array"), @@ -747,6 +889,7 @@ void Shell::Initialize() { // Start the debugger agent if requested. if (i::FLAG_debugger_agent) { v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true); + v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true); } #endif // ENABLE_DEBUGGER_SUPPORT #endif // V8_SHARED @@ -760,13 +903,8 @@ Persistent<Context> Shell::CreateEvaluationContext() { #endif // V8_SHARED // Initialize the global objects Handle<ObjectTemplate> global_template = CreateGlobalTemplate(); - - v8::TryCatch try_catch; Persistent<Context> context = Context::New(NULL, global_template); - if (context.IsEmpty()) { - v8::Local<v8::Value> st = try_catch.StackTrace(); - ASSERT(!context.IsEmpty()); - } + ASSERT(!context.IsEmpty()); Context::Scope scope(context); #ifndef V8_SHARED @@ -797,22 +935,47 @@ void Shell::Exit(int exit_code) { #ifndef V8_SHARED +struct CounterAndKey { + Counter* counter; + const char* key; +}; + + +int CompareKeys(const void* a, const void* b) { + return strcmp(static_cast<const CounterAndKey*>(a)->key, + static_cast<const CounterAndKey*>(b)->key); +} + + void Shell::OnExit() { if (console != NULL) console->Close(); if (i::FLAG_dump_counters) { - printf("+----------------------------------------+-------------+\n"); - printf("| Name | Value |\n"); - printf("+----------------------------------------+-------------+\n"); + int number_of_counters = 0; for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { - Counter* counter = i.CurrentValue(); + number_of_counters++; + } + CounterAndKey* counters = new CounterAndKey[number_of_counters]; + int j = 0; + for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) { + counters[j].counter = i.CurrentValue(); + counters[j].key = i.CurrentKey(); + } + qsort(counters, number_of_counters, sizeof(counters[0]), CompareKeys); + printf("+--------------------------------------------+-------------+\n"); + printf("| Name | Value |\n"); + printf("+--------------------------------------------+-------------+\n"); + for (j = 0; j < number_of_counters; j++) { + Counter* counter = counters[j].counter; + const char* key = counters[j].key; if (counter->is_histogram()) { - printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count()); - printf("| t:%-36s | %11i |\n", i.CurrentKey(), counter->sample_total()); + printf("| c:%-40s | %11i |\n", key, counter->count()); + printf("| t:%-40s | %11i |\n", key, counter->sample_total()); } else { - printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count()); + printf("| %-42s | %11i |\n", key, counter->count()); } } - printf("+----------------------------------------+-------------+\n"); + printf("+--------------------------------------------+-------------+\n"); + delete [] counters; } if (counters_file_ != NULL) delete counters_file_; @@ -821,7 +984,7 @@ void Shell::OnExit() { static FILE* FOpen(const char* path, const char* mode) { -#if (defined(_WIN32) || defined(_WIN64)) +#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) FILE* result; if (fopen_s(&result, path, mode) == 0) { return result; @@ -863,6 +1026,23 @@ static char* ReadChars(const char* name, int* size_out) { } +Handle<Value> Shell::ReadBinary(const Arguments& args) { + String::Utf8Value filename(args[0]); + int size; + if (*filename == NULL) { + return ThrowException(String::New("Error loading file")); + } + char* chars = ReadChars(*filename, &size); + if (chars == NULL) { + return ThrowException(String::New("Error reading file")); + } + // We skip checking the string for UTF8 characters and use it raw as + // backing store for the external string with 8-bit characters. + BinaryResource* resource = new BinaryResource(chars, size); + return String::NewExternal(resource); +} + + #ifndef V8_SHARED static char* ReadToken(char* data, char token) { char* next = i::OS::StrChr(data, token); @@ -902,31 +1082,15 @@ void Shell::RunShell() { Context::Scope context_scope(evaluation_context_); HandleScope outer_scope; Handle<String> name = String::New("(d8)"); -#ifndef V8_SHARED console = LineEditor::Get(); printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name()); - if (i::FLAG_debugger) { - printf("JavaScript debugger enabled\n"); - } console->Open(); while (true) { - i::SmartArrayPointer<char> input = console->Prompt(Shell::kPrompt); - if (input.is_empty()) break; - console->AddHistory(*input); HandleScope inner_scope; - ExecuteString(String::New(*input), name, true, true); + Handle<String> input = console->Prompt(Shell::kPrompt); + if (input.IsEmpty()) break; + ExecuteString(input, name, true, true); } -#else - printf("V8 version %s [D8 light using shared library]\n", V8::GetVersion()); - static const int kBufferSize = 256; - while (true) { - char buffer[kBufferSize]; - printf("%s", Shell::kPrompt); - if (fgets(buffer, kBufferSize, stdin) == NULL) break; - HandleScope inner_scope; - ExecuteString(String::New(buffer), name, true, true); - } -#endif // V8_SHARED printf("\n"); } @@ -1049,14 +1213,11 @@ Handle<String> SourceGroup::ReadFile(const char* name) { #ifndef V8_SHARED i::Thread::Options SourceGroup::GetThreadOptions() { - i::Thread::Options options; - options.name = "IsolateThread"; // On some systems (OSX 10.6) the stack size default is 0.5Mb or less // which is not enough to parse the big literal expressions used in tests. // The stack size should be at least StackGuard::kLimitSize + some - // OS-specific padding for thread startup code. - options.stack_size = 2 << 20; // 2 Mb seems to be enough - return options; + // OS-specific padding for thread startup code. 2Mbytes seems to be enough. + return i::Thread::Options("IsolateThread", 2 * MB); } @@ -1127,7 +1288,7 @@ bool Shell::SetOptions(int argc, char* argv[]) { options.use_preemption = true; argv[i] = NULL; #endif // V8_SHARED - } else if (strcmp(argv[i], "--no-preemption") == 0) { + } else if (strcmp(argv[i], "--nopreemption") == 0) { #ifdef V8_SHARED printf("D8 with shared library does not support multi-threading\n"); return false; @@ -1258,14 +1419,22 @@ int Shell::RunMain(int argc, char* argv[]) { Locker lock; HandleScope scope; Persistent<Context> context = CreateEvaluationContext(); + if (options.last_run) { + // Keep using the same context in the interactive shell. + evaluation_context_ = context; +#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) + // If the interactive debugger is enabled make sure to activate + // it before running the files passed on the command line. + if (i::FLAG_debugger) { + InstallUtilityScript(); + } +#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT + } { Context::Scope cscope(context); options.isolate_sources[0].Execute(); } - if (options.last_run) { - // Keep using the same context in the interactive shell - evaluation_context_ = context; - } else { + if (!options.last_run) { context.Dispose(); } @@ -1316,6 +1485,14 @@ int Shell::Main(int argc, char* argv[]) { } printf("======== Full Deoptimization =======\n"); Testing::DeoptimizeAll(); +#if !defined(V8_SHARED) + } else if (i::FLAG_stress_runs > 0) { + int stress_runs = i::FLAG_stress_runs; + for (int i = 0; i < stress_runs && result == 0; i++) { + printf("============ Run %d/%d ============\n", i + 1, stress_runs); + result = RunMain(argc, argv); + } +#endif } else { result = RunMain(argc, argv); } @@ -1336,9 +1513,11 @@ int Shell::Main(int argc, char* argv[]) { if (( options.interactive_shell || !options.script_executed ) && !options.test_shell ) { -#ifndef V8_SHARED - InstallUtilityScript(); -#endif // V8_SHARED +#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) + if (!i::FLAG_debugger) { + InstallUtilityScript(); + } +#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT RunShell(); } diff --git a/deps/v8/src/d8.gyp b/deps/v8/src/d8.gyp index 70186cfbde..3b92d03681 100644 --- a/deps/v8/src/d8.gyp +++ b/deps/v8/src/d8.gyp @@ -1,4 +1,4 @@ -# Copyright 2010 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -64,8 +64,8 @@ 'libraries': [ '-lreadline', ], 'sources': [ 'd8-readline.cc' ], }], - [ '(OS=="linux" or OS=="mac" or OS=="freebsd" \ - or OS=="openbsd" or OS=="solaris")', { + ['(OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="netbsd" \ + or OS=="openbsd" or OS=="solaris" or OS=="android")', { 'sources': [ 'd8-posix.cc', ] }], [ 'OS=="win"', { diff --git a/deps/v8/src/d8.h b/deps/v8/src/d8.h index 15d8d5d50f..c872f90958 100644 --- a/deps/v8/src/d8.h +++ b/deps/v8/src/d8.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -116,14 +116,13 @@ class CounterMap { #endif // V8_SHARED -#ifndef V8_SHARED class LineEditor { public: enum Type { DUMB = 0, READLINE = 1 }; LineEditor(Type type, const char* name); virtual ~LineEditor() { } - virtual i::SmartArrayPointer<char> Prompt(const char* prompt) = 0; + virtual Handle<String> Prompt(const char* prompt) = 0; virtual bool Open() { return true; } virtual bool Close() { return true; } virtual void AddHistory(const char* str) { } @@ -136,7 +135,6 @@ class LineEditor { LineEditor* next_; static LineEditor* first_; }; -#endif // V8_SHARED class SourceGroup { @@ -197,6 +195,27 @@ class SourceGroup { }; +class BinaryResource : public v8::String::ExternalAsciiStringResource { + public: + BinaryResource(const char* string, int length) + : data_(string), + length_(length) { } + + ~BinaryResource() { + delete[] data_; + data_ = NULL; + length_ = 0; + } + + virtual const char* data() const { return data_; } + virtual size_t length() const { return length_; } + + private: + const char* data_; + size_t length_; +}; + + class ShellOptions { public: ShellOptions() : @@ -268,12 +287,13 @@ class Shell : public i::AllStatic { size_t buckets); static void AddHistogramSample(void* histogram, int sample); static void MapCounters(const char* name); -#endif // V8_SHARED #ifdef ENABLE_DEBUGGER_SUPPORT static Handle<Object> DebugMessageDetails(Handle<String> message); static Handle<Value> DebugCommandToJSONRequest(Handle<String> command); -#endif + static void DispatchDebugMessages(); +#endif // ENABLE_DEBUGGER_SUPPORT +#endif // V8_SHARED #ifdef WIN32 #undef Yield @@ -287,8 +307,13 @@ class Shell : public i::AllStatic { static Handle<Value> EnableProfiler(const Arguments& args); static Handle<Value> DisableProfiler(const Arguments& args); static Handle<Value> Read(const Arguments& args); - static Handle<Value> ReadLine(const Arguments& args); + static Handle<Value> ReadBinary(const Arguments& args); + static Handle<String> ReadFromStdin(); + static Handle<Value> ReadLine(const Arguments& args) { + return ReadFromStdin(); + } static Handle<Value> Load(const Arguments& args); + static Handle<Value> ArrayBuffer(const Arguments& args); static Handle<Value> Int8Array(const Arguments& args); static Handle<Value> Uint8Array(const Arguments& args); static Handle<Value> Int16Array(const Arguments& args); @@ -334,11 +359,8 @@ class Shell : public i::AllStatic { static Handle<Value> RemoveDirectory(const Arguments& args); static void AddOSMethods(Handle<ObjectTemplate> os_template); -#ifndef V8_SHARED - static const char* kHistoryFileName; - static const int kMaxHistoryEntries; + static LineEditor* console; -#endif // V8_SHARED static const char* kPrompt; static ShellOptions options; diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js index 3009037e78..86b8c8106c 100644 --- a/deps/v8/src/d8.js +++ b/deps/v8/src/d8.js @@ -26,10 +26,11 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. String.prototype.startsWith = function (str) { - if (str.length > this.length) + if (str.length > this.length) { return false; + } return this.substr(0, str.length) == str; -} +}; function log10(num) { return Math.log(num)/Math.log(10); @@ -52,8 +53,9 @@ function GetCompletions(global, last, full) { for (var i = 0; i < parts.length; i++) { var part = parts[i]; var next = current[part]; - if (!next) + if (!next) { return []; + } current = next; } var result = []; @@ -63,8 +65,9 @@ function GetCompletions(global, last, full) { var properties = mirror.properties(); for (var i = 0; i < properties.length; i++) { var name = properties[i].name(); - if (typeof name === 'string' && name.startsWith(last)) + if (typeof name === 'string' && name.startsWith(last)) { result.push(name); + } } current = ToInspectableObject(current.__proto__); } @@ -114,7 +117,7 @@ Debug.State = { displaySourceStartLine: -1, displaySourceEndLine: -1, currentSourceLine: -1 -} +}; var trace_compile = false; // Tracing all compile events? var trace_debug_json = false; // Tracing all debug json packets? var last_cmd_line = ''; @@ -150,7 +153,7 @@ function DebugMessageDetails(message) { } function DebugEventDetails(response) { - details = {text:'', running:false} + details = {text:'', running:false}; // Get the running state. details.running = response.running(); @@ -217,7 +220,7 @@ function DebugEventDetails(response) { case 'afterCompile': if (trace_compile) { - result = 'Source ' + body.script.name + ' compiled:\n' + result = 'Source ' + body.script.name + ' compiled:\n'; var source = body.script.source; if (!(source[source.length - 1] == '\n')) { result += source; @@ -237,7 +240,7 @@ function DebugEventDetails(response) { } return details; -}; +} function SourceInfo(body) { @@ -279,7 +282,7 @@ function SourceUnderline(source_text, position) { // Return the source line text with the underline beneath. return source_text + '\n' + underline; -}; +} // Converts a text command to a JSON request. @@ -289,7 +292,7 @@ function DebugCommandToJSONRequest(cmd_line) { print("sending: '" + result + "'"); } return result; -}; +} function DebugRequest(cmd_line) { @@ -514,7 +517,7 @@ function DebugRequest(cmd_line) { DebugRequest.prototype.JSONRequest = function() { return this.request_; -} +}; function RequestPacket(command) { @@ -536,14 +539,14 @@ RequestPacket.prototype.toJSONProtocol = function() { json += ',"arguments":'; // Encode the arguments part. if (this.arguments.toJSONProtocol) { - json += this.arguments.toJSONProtocol() + json += this.arguments.toJSONProtocol(); } else { json += SimpleObjectToJSON_(this.arguments); } } json += '}'; return json; -} +}; DebugRequest.prototype.createRequest = function(command) { @@ -1310,7 +1313,7 @@ DebugRequest.prototype.lolMakeListRequest = } return request; -} +}; function extractObjId(args) { @@ -1499,7 +1502,7 @@ DebugRequest.prototype.traceCommand_ = function(args) { } else { throw new Error('Invalid trace arguments.'); } -} +}; // Handle the help command. DebugRequest.prototype.helpCommand_ = function(args) { @@ -1608,7 +1611,7 @@ DebugRequest.prototype.helpCommand_ = function(args) { print(''); print('disconnect|exit|quit - disconnects and quits the debugger'); print('help - prints this help information'); -} +}; function formatHandleReference_(value) { @@ -1623,7 +1626,7 @@ function formatHandleReference_(value) { function formatObject_(value, include_properties) { var result = ''; result += formatHandleReference_(value); - result += ', type: object' + result += ', type: object'; result += ', constructor '; var ctor = value.constructorFunctionValue(); result += formatHandleReference_(ctor); @@ -1943,7 +1946,7 @@ function roundNumber(num, length) { // Convert a JSON response to text for display in a text based debugger. function DebugResponseDetails(response) { - details = {text:'', running:false} + details = { text: '', running: false }; try { if (!response.success()) { @@ -2308,7 +2311,7 @@ function DebugResponseDetails(response) { } return details; -}; +} /** @@ -2334,7 +2337,7 @@ function ProtocolPackage(json) { */ ProtocolPackage.prototype.type = function() { return this.packet_.type; -} +}; /** @@ -2343,7 +2346,7 @@ ProtocolPackage.prototype.type = function() { */ ProtocolPackage.prototype.event = function() { return this.packet_.event; -} +}; /** @@ -2352,7 +2355,7 @@ ProtocolPackage.prototype.event = function() { */ ProtocolPackage.prototype.requestSeq = function() { return this.packet_.request_seq; -} +}; /** @@ -2361,27 +2364,27 @@ ProtocolPackage.prototype.requestSeq = function() { */ ProtocolPackage.prototype.running = function() { return this.packet_.running ? true : false; -} +}; ProtocolPackage.prototype.success = function() { return this.packet_.success ? true : false; -} +}; ProtocolPackage.prototype.message = function() { return this.packet_.message; -} +}; ProtocolPackage.prototype.command = function() { return this.packet_.command; -} +}; ProtocolPackage.prototype.body = function() { return this.packet_.body; -} +}; ProtocolPackage.prototype.bodyValue = function(index) { @@ -2390,12 +2393,12 @@ ProtocolPackage.prototype.bodyValue = function(index) { } else { return new ProtocolValue(this.packet_.body, this); } -} +}; ProtocolPackage.prototype.body = function() { return this.packet_.body; -} +}; ProtocolPackage.prototype.lookup = function(handle) { @@ -2405,12 +2408,12 @@ ProtocolPackage.prototype.lookup = function(handle) { } else { return new ProtocolReference(handle); } -} +}; ProtocolPackage.prototype.raw_json = function() { return this.raw_json_; -} +}; function ProtocolValue(value, packet) { @@ -2425,7 +2428,7 @@ function ProtocolValue(value, packet) { */ ProtocolValue.prototype.type = function() { return this.value_.type; -} +}; /** @@ -2434,7 +2437,7 @@ ProtocolValue.prototype.type = function() { */ ProtocolValue.prototype.field = function(name) { return this.value_[name]; -} +}; /** @@ -2444,7 +2447,7 @@ ProtocolValue.prototype.field = function(name) { ProtocolValue.prototype.isPrimitive = function() { return this.isUndefined() || this.isNull() || this.isBoolean() || this.isNumber() || this.isString(); -} +}; /** @@ -2453,7 +2456,7 @@ ProtocolValue.prototype.isPrimitive = function() { */ ProtocolValue.prototype.handle = function() { return this.value_.handle; -} +}; /** @@ -2462,7 +2465,7 @@ ProtocolValue.prototype.handle = function() { */ ProtocolValue.prototype.isUndefined = function() { return this.value_.type == 'undefined'; -} +}; /** @@ -2471,7 +2474,7 @@ ProtocolValue.prototype.isUndefined = function() { */ ProtocolValue.prototype.isNull = function() { return this.value_.type == 'null'; -} +}; /** @@ -2480,7 +2483,7 @@ ProtocolValue.prototype.isNull = function() { */ ProtocolValue.prototype.isBoolean = function() { return this.value_.type == 'boolean'; -} +}; /** @@ -2489,7 +2492,7 @@ ProtocolValue.prototype.isBoolean = function() { */ ProtocolValue.prototype.isNumber = function() { return this.value_.type == 'number'; -} +}; /** @@ -2498,7 +2501,7 @@ ProtocolValue.prototype.isNumber = function() { */ ProtocolValue.prototype.isString = function() { return this.value_.type == 'string'; -} +}; /** @@ -2508,7 +2511,7 @@ ProtocolValue.prototype.isString = function() { ProtocolValue.prototype.isObject = function() { return this.value_.type == 'object' || this.value_.type == 'function' || this.value_.type == 'error' || this.value_.type == 'regexp'; -} +}; /** @@ -2518,7 +2521,7 @@ ProtocolValue.prototype.isObject = function() { ProtocolValue.prototype.constructorFunctionValue = function() { var ctor = this.value_.constructorFunction; return this.packet_.lookup(ctor.ref); -} +}; /** @@ -2528,7 +2531,7 @@ ProtocolValue.prototype.constructorFunctionValue = function() { ProtocolValue.prototype.protoObjectValue = function() { var proto = this.value_.protoObject; return this.packet_.lookup(proto.ref); -} +}; /** @@ -2537,7 +2540,7 @@ ProtocolValue.prototype.protoObjectValue = function() { */ ProtocolValue.prototype.propertyCount = function() { return this.value_.properties ? this.value_.properties.length : 0; -} +}; /** @@ -2547,7 +2550,7 @@ ProtocolValue.prototype.propertyCount = function() { ProtocolValue.prototype.propertyName = function(index) { var property = this.value_.properties[index]; return property.name; -} +}; /** @@ -2562,7 +2565,7 @@ ProtocolValue.prototype.propertyIndex = function(name) { } } return null; -} +}; /** @@ -2572,7 +2575,7 @@ ProtocolValue.prototype.propertyIndex = function(name) { ProtocolValue.prototype.propertyValue = function(index) { var property = this.value_.properties[index]; return this.packet_.lookup(property.ref); -} +}; /** @@ -2581,12 +2584,12 @@ ProtocolValue.prototype.propertyValue = function(index) { */ ProtocolValue.prototype.value = function() { return this.value_.value; -} +}; ProtocolValue.prototype.valueString = function() { return this.value_.text; -} +}; function ProtocolReference(handle) { @@ -2596,7 +2599,7 @@ function ProtocolReference(handle) { ProtocolReference.prototype.handle = function() { return this.handle_; -} +}; function MakeJSONPair_(name, value) { @@ -2667,7 +2670,7 @@ function StringToJSON_(value) { // Convert control character to unicode escape sequence. return '\\u00' + '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) + - '0' // TODO %NumberToRadixString(mapped % 16, 16); + '0'; // TODO %NumberToRadixString(mapped % 16, 16) }) + '"'; } @@ -2738,7 +2741,7 @@ function SimpleObjectToJSON_(object) { if (property_value === null) { property_value_json = 'null'; } else if (typeof property_value.toJSONProtocol == 'function') { - property_value_json = property_value.toJSONProtocol(true) + property_value_json = property_value.toJSONProtocol(true); } else if (property_value.constructor.name == 'Array'){ property_value_json = SimpleArrayToJSON_(property_value); } else { @@ -2789,7 +2792,7 @@ function SimpleArrayToJSON_(array) { } var elem = array[i]; if (elem.toJSONProtocol) { - json += elem.toJSONProtocol(true) + json += elem.toJSONProtocol(true); } else if (typeof(elem) === 'object') { json += SimpleObjectToJSON_(elem); } else if (typeof(elem) === 'boolean') { diff --git a/deps/v8/src/date.js b/deps/v8/src/date.js index ccefce5763..999009e863 100644 --- a/deps/v8/src/date.js +++ b/deps/v8/src/date.js @@ -294,8 +294,8 @@ function TimeInYear(year) { } -var ymd_from_time_cache = [$NaN, $NaN, $NaN]; -var ymd_from_time_cached_time = $NaN; +var ymd_from_time_cache = [1970, 0, 1]; +var ymd_from_time_cached_time = 0; function YearFromTime(t) { if (t !== ymd_from_time_cached_time) { @@ -304,7 +304,7 @@ function YearFromTime(t) { } %DateYMDFromTime(t, ymd_from_time_cache); - ymd_from_time_cached_time = t + ymd_from_time_cached_time = t; } return ymd_from_time_cache[0]; @@ -316,7 +316,7 @@ function MonthFromTime(t) { return $NaN; } %DateYMDFromTime(t, ymd_from_time_cache); - ymd_from_time_cached_time = t + ymd_from_time_cached_time = t; } return ymd_from_time_cache[1]; @@ -329,7 +329,7 @@ function DateFromTime(t) { } %DateYMDFromTime(t, ymd_from_time_cache); - ymd_from_time_cached_time = t + ymd_from_time_cached_time = t; } return ymd_from_time_cache[2]; @@ -351,13 +351,12 @@ function MakeDay(year, month, date) { date = TO_INTEGER_MAP_MINUS_ZERO(date); if (year < kMinYear || year > kMaxYear || - month < kMinMonth || month > kMaxMonth || - date < kMinDate || date > kMaxDate) { + month < kMinMonth || month > kMaxMonth) { return $NaN; } - // Now we rely on year, month and date being SMIs. - return %DateMakeDay(year, month, date); + // Now we rely on year and month being SMIs. + return %DateMakeDay(year, month) + date - 1; } @@ -446,8 +445,9 @@ var Date_cache = { minutes = argc > 4 ? ToNumber(minutes) : 0; seconds = argc > 5 ? ToNumber(seconds) : 0; ms = argc > 6 ? ToNumber(ms) : 0; - year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) - ? 1900 + TO_INTEGER(year) : year; + year = (!NUMBER_IS_NAN(year) && + 0 <= TO_INTEGER(year) && + TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year; var day = MakeDay(year, month, date); var time = MakeTime(hours, minutes, seconds, ms); value = TimeClip(UTC(MakeDate(day, time))); @@ -460,7 +460,8 @@ var Date_cache = { var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; -var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; function TwoDigitString(value) { @@ -476,8 +477,10 @@ function DateString(time) { } -var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; -var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; +var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', + 'Thursday', 'Friday', 'Saturday']; +var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December']; function LongDateString(time) { @@ -557,8 +560,9 @@ function DateUTC(year, month, date, hours, minutes, seconds, ms) { minutes = argc > 4 ? ToNumber(minutes) : 0; seconds = argc > 5 ? ToNumber(seconds) : 0; ms = argc > 6 ? ToNumber(ms) : 0; - year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) - ? 1900 + TO_INTEGER(year) : year; + year = (!NUMBER_IS_NAN(year) && + 0 <= TO_INTEGER(year) && + TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year; var day = MakeDay(year, month, date); var time = MakeTime(hours, minutes, seconds, ms); return %_SetValueOf(this, TimeClip(MakeDate(day, time))); @@ -778,7 +782,10 @@ function DateSetTime(ms) { function DateSetMilliseconds(ms) { var t = LocalTime(DATE_VALUE(this)); ms = ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); + var time = MakeTime(HOUR_FROM_TIME(t), + MIN_FROM_TIME(t), + SEC_FROM_TIME(t), + ms); return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); } @@ -787,7 +794,10 @@ function DateSetMilliseconds(ms) { function DateSetUTCMilliseconds(ms) { var t = DATE_VALUE(this); ms = ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); + var time = MakeTime(HOUR_FROM_TIME(t), + MIN_FROM_TIME(t), + SEC_FROM_TIME(t), + ms); return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); } @@ -978,9 +988,10 @@ function PadInt(n, digits) { } +// ECMA 262 - 15.9.5.43 function DateToISOString() { var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; + if (NUMBER_IS_NAN(t)) throw MakeRangeError("invalid_time_value", []); var year = this.getUTCFullYear(); var year_string; if (year >= 0 && year <= 9999) { @@ -1062,7 +1073,7 @@ function SetUpDate() { // Set up non-enumerable functions of the Date prototype object and // set their names. - InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array( + InstallFunctions($Date.prototype, DONT_ENUM, $Array( "toString", DateToString, "toDateString", DateToDateString, "toTimeString", DateToTimeString, diff --git a/deps/v8/src/debug-agent.cc b/deps/v8/src/debug-agent.cc index 591d0b3e46..c30afa85db 100644 --- a/deps/v8/src/debug-agent.cc +++ b/deps/v8/src/debug-agent.cc @@ -229,8 +229,6 @@ void DebuggerAgentSession::Shutdown() { const char* const DebuggerAgentUtil::kContentLength = "Content-Length"; -const int DebuggerAgentUtil::kContentLengthSize = - StrLength(kContentLength); SmartArrayPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { diff --git a/deps/v8/src/debug-agent.h b/deps/v8/src/debug-agent.h index a07fb0f49c..61151900f0 100644 --- a/deps/v8/src/debug-agent.h +++ b/deps/v8/src/debug-agent.h @@ -115,7 +115,6 @@ class DebuggerAgentSession: public Thread { class DebuggerAgentUtil { public: static const char* const kContentLength; - static const int kContentLengthSize; static SmartArrayPointer<char> ReceiveMessage(const Socket* conn); static bool SendConnectMessage(const Socket* conn, diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js index d254ee5696..120a297007 100644 --- a/deps/v8/src/debug-debugger.js +++ b/deps/v8/src/debug-debugger.js @@ -286,7 +286,7 @@ ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) { copy.condition_ = this.condition_; copy.ignoreCount_ = this.ignoreCount_; return copy; -} +}; ScriptBreakPoint.prototype.number = function() { @@ -335,13 +335,13 @@ ScriptBreakPoint.prototype.actual_locations = function() { locations.push(this.break_points_[i].actual_location); } return locations; -} +}; ScriptBreakPoint.prototype.update_positions = function(line, column) { this.line_ = line; this.column_ = column; -} +}; ScriptBreakPoint.prototype.hit_count = function() { @@ -477,9 +477,10 @@ ScriptBreakPoint.prototype.clear = function () { // break points set in this script. function UpdateScriptBreakPoints(script) { for (var i = 0; i < script_break_points.length; i++) { - if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName && - script_break_points[i].matchesScript(script)) { - script_break_points[i].set(script); + var break_point = script_break_points[i]; + if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName) && + break_point.matchesScript(script)) { + break_point.set(script); } } } @@ -585,7 +586,7 @@ Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { var script = %FunctionGetScript(func); var script_offset = %FunctionGetScriptSourcePosition(func); return script.locationFromLine(opt_line, opt_column, script_offset); -} +}; // Returns the character position in a script based on a line number and an @@ -593,7 +594,7 @@ Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { var location = script.locationFromLine(opt_line, opt_column); return location ? location.position : null; -} +}; Debug.findBreakPoint = function(break_point_number, remove) { @@ -627,7 +628,7 @@ Debug.findBreakPointActualLocations = function(break_point_number) { } } return []; -} +}; Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.'); @@ -677,8 +678,9 @@ Debug.setBreakPointByScriptIdAndPosition = function(script_id, position, { break_point = MakeBreakPoint(position); break_point.setCondition(condition); - if (!enabled) + if (!enabled) { break_point.disable(); + } var scripts = this.scripts(); for (var i = 0; i < scripts.length; i++) { if (script_id == scripts[i].id) { @@ -771,7 +773,7 @@ Debug.findScriptBreakPoint = function(break_point_number, remove) { } } return script_break_point; -} +}; // Sets a breakpoint in a script identified through id or name at the @@ -799,7 +801,7 @@ Debug.setScriptBreakPoint = function(type, script_id_or_name, } return script_break_point.number(); -} +}; Debug.setScriptBreakPointById = function(script_id, @@ -808,7 +810,7 @@ Debug.setScriptBreakPointById = function(script_id, return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, script_id, opt_line, opt_column, opt_condition, opt_groupId); -} +}; Debug.setScriptBreakPointByName = function(script_name, @@ -817,7 +819,7 @@ Debug.setScriptBreakPointByName = function(script_name, return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, script_name, opt_line, opt_column, opt_condition, opt_groupId); -} +}; Debug.setScriptBreakPointByRegExp = function(script_regexp, @@ -826,7 +828,7 @@ Debug.setScriptBreakPointByRegExp = function(script_regexp, return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp, script_regexp, opt_line, opt_column, opt_condition, opt_groupId); -} +}; Debug.enableScriptBreakPoint = function(break_point_number) { @@ -841,13 +843,15 @@ Debug.disableScriptBreakPoint = function(break_point_number) { }; -Debug.changeScriptBreakPointCondition = function(break_point_number, condition) { +Debug.changeScriptBreakPointCondition = function( + break_point_number, condition) { var script_break_point = this.findScriptBreakPoint(break_point_number, false); script_break_point.setCondition(condition); }; -Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) { +Debug.changeScriptBreakPointIgnoreCount = function( + break_point_number, ignoreCount) { if (ignoreCount < 0) { throw new Error('Invalid argument'); } @@ -858,12 +862,12 @@ Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCou Debug.scriptBreakPoints = function() { return script_break_points; -} +}; Debug.clearStepping = function() { %ClearStepping(); -} +}; Debug.setBreakOnException = function() { return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true); @@ -940,7 +944,7 @@ ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { var count = opt_count ? %ToNumber(opt_count) : 1; return %PrepareStep(this.break_id, action, count); -} +}; ExecutionState.prototype.evaluateGlobal = function(source, disable_break, opt_additional_context) { @@ -960,8 +964,9 @@ ExecutionState.prototype.threadCount = function() { ExecutionState.prototype.frame = function(opt_index) { // If no index supplied return the selected frame. if (opt_index == null) opt_index = this.selected_frame; - if (opt_index < 0 || opt_index >= this.frameCount()) + if (opt_index < 0 || opt_index >= this.frameCount()) { throw new Error('Illegal frame index.'); + } return new FrameMirror(this.break_id, opt_index); }; @@ -1088,12 +1093,12 @@ ExceptionEvent.prototype.eventType = function() { ExceptionEvent.prototype.exception = function() { return this.exception_; -} +}; ExceptionEvent.prototype.uncaught = function() { return this.uncaught_; -} +}; ExceptionEvent.prototype.func = function() { @@ -1185,7 +1190,7 @@ CompileEvent.prototype.toJSONProtocol = function() { o.body.script = this.script_; return o.toJSONProtocol(); -} +}; function MakeNewFunctionEvent(func) { @@ -1241,7 +1246,7 @@ ScriptCollectedEvent.prototype.toJSONProtocol = function() { o.body = {}; o.body.script = { id: this.id() }; return o.toJSONProtocol(); -} +}; function MakeScriptObject_(script, include_source) { @@ -1258,18 +1263,18 @@ function MakeScriptObject_(script, include_source) { o.source = script.source(); } return o; -}; +} function DebugCommandProcessor(exec_state, opt_is_running) { this.exec_state_ = exec_state; this.running_ = opt_is_running || false; -}; +} DebugCommandProcessor.prototype.processDebugRequest = function (request) { return this.processDebugJSONRequest(request); -} +}; function ProtocolMessage(request) { @@ -1297,13 +1302,13 @@ ProtocolMessage.prototype.setOption = function(name, value) { this.options_ = {}; } this.options_[name] = value; -} +}; ProtocolMessage.prototype.failed = function(message) { this.success = false; this.message = message; -} +}; ProtocolMessage.prototype.toJSONProtocol = function() { @@ -1351,7 +1356,7 @@ ProtocolMessage.prototype.toJSONProtocol = function() { } json.running = this.running; return JSON.stringify(json); -} +}; DebugCommandProcessor.prototype.createResponse = function(request) { @@ -1359,7 +1364,8 @@ DebugCommandProcessor.prototype.createResponse = function(request) { }; -DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) { +DebugCommandProcessor.prototype.processDebugJSONRequest = function( + json_request) { var request; // Current request. var response; // Generated response. try { @@ -1541,7 +1547,7 @@ DebugCommandProcessor.prototype.continueRequest_ = function(request, response) { } } - // Setup the VM for stepping. + // Set up the VM for stepping. this.exec_state_.prepareStep(action, count); } @@ -1646,7 +1652,7 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ = // Add the break point number to the response. response.body = { type: type, - breakpoint: break_point_number } + breakpoint: break_point_number }; // Add break point information to the response. if (break_point instanceof ScriptBreakPoint) { @@ -1660,7 +1666,8 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ = response.body.type = 'scriptRegExp'; response.body.script_regexp = break_point.script_regexp_object().source; } else { - throw new Error("Internal error: Unexpected breakpoint type: " + break_point.type()); + throw new Error("Internal error: Unexpected breakpoint type: " + + break_point.type()); } response.body.line = break_point.line(); response.body.column = break_point.column(); @@ -1672,7 +1679,8 @@ DebugCommandProcessor.prototype.setBreakPointRequest_ = }; -DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) { +DebugCommandProcessor.prototype.changeBreakPointRequest_ = function( + request, response) { // Check for legal request. if (!request.arguments) { response.failed('Missing arguments'); @@ -1709,10 +1717,11 @@ DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, res if (!IS_UNDEFINED(ignoreCount)) { Debug.changeBreakPointIgnoreCount(break_point, ignoreCount); } -} +}; -DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) { +DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function( + request, response) { // Check for legal request. if (!request.arguments) { response.failed('Missing arguments'); @@ -1743,10 +1752,11 @@ DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, // Add the cleared break point numbers to the response. response.body = { breakpoints: cleared_break_points }; -} +}; -DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) { +DebugCommandProcessor.prototype.clearBreakPointRequest_ = function( + request, response) { // Check for legal request. if (!request.arguments) { response.failed('Missing arguments'); @@ -1766,11 +1776,12 @@ DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, resp Debug.clearBreakPoint(break_point); // Add the cleared break point number to the response. - response.body = { breakpoint: break_point } -} + response.body = { breakpoint: break_point }; +}; -DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, response) { +DebugCommandProcessor.prototype.listBreakpointsRequest_ = function( + request, response) { var array = []; for (var i = 0; i < script_break_points.length; i++) { var break_point = script_break_points[i]; @@ -1785,7 +1796,7 @@ DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, resp condition: break_point.condition(), ignoreCount: break_point.ignoreCount(), actual_locations: break_point.actual_locations() - } + }; if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { description.type = 'scriptId'; @@ -1797,7 +1808,8 @@ DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, resp description.type = 'scriptRegExp'; description.script_regexp = break_point.script_regexp_object().source; } else { - throw new Error("Internal error: Unexpected breakpoint type: " + break_point.type()); + throw new Error("Internal error: Unexpected breakpoint type: " + + break_point.type()); } array.push(description); } @@ -1806,15 +1818,15 @@ DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, resp breakpoints: array, breakOnExceptions: Debug.isBreakOnException(), breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException() - } -} + }; +}; DebugCommandProcessor.prototype.disconnectRequest_ = function(request, response) { Debug.disableAllBreakPoints(); this.continueRequest_(request, response); -} +}; DebugCommandProcessor.prototype.setExceptionBreakRequest_ = @@ -1859,10 +1871,11 @@ DebugCommandProcessor.prototype.setExceptionBreakRequest_ = // Add the cleared break point number to the response. response.body = { 'type': type, 'enabled': enabled }; -} +}; -DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) { +DebugCommandProcessor.prototype.backtraceRequest_ = function( + request, response) { // Get the number of frames. var total_frames = this.exec_state_.frameCount(); @@ -1870,12 +1883,12 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) if (total_frames == 0) { response.body = { totalFrames: total_frames - } + }; return; } // Default frame range to include in backtrace. - var from_index = 0 + var from_index = 0; var to_index = kDefaultBacktraceLength; // Get the range from the arguments. @@ -1888,7 +1901,7 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) } if (request.arguments.bottom) { var tmp_index = total_frames - from_index; - from_index = total_frames - to_index + from_index = total_frames - to_index; to_index = tmp_index; } if (from_index < 0 || to_index < 0) { @@ -1914,7 +1927,7 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) toFrame: to_index, totalFrames: total_frames, frames: frames - } + }; }; @@ -1938,8 +1951,8 @@ DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { - // Get the frame for which the scope or scopes are requested. With no frameNumber - // argument use the currently selected frame. + // Get the frame for which the scope or scopes are requested. + // With no frameNumber argument use the currently selected frame. if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) { frame_index = request.arguments.frameNumber; if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { @@ -1949,7 +1962,7 @@ DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { } else { return this.exec_state_.frame(); } -} +}; DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { @@ -1972,7 +1985,7 @@ DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { toScope: total_scopes, totalScopes: total_scopes, scopes: scopes - } + }; }; @@ -2217,7 +2230,8 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { if (!IS_UNDEFINED(request.arguments.types)) { types = %ToNumber(request.arguments.types); if (isNaN(types) || types < 0) { - return response.failed('Invalid types "' + request.arguments.types + '"'); + return response.failed('Invalid types "' + + request.arguments.types + '"'); } } @@ -2286,7 +2300,7 @@ DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { var details = %GetThreadDetails(this.exec_state_.break_id, i); var thread_info = { current: details[0], id: details[1] - } + }; threads.push(thread_info); } @@ -2294,7 +2308,7 @@ DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { response.body = { totalThreads: total_threads, threads: threads - } + }; }; @@ -2306,7 +2320,7 @@ DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) { DebugCommandProcessor.prototype.versionRequest_ = function(request, response) { response.body = { V8Version: %GetV8Version() - } + }; }; @@ -2322,7 +2336,8 @@ DebugCommandProcessor.prototype.profileRequest_ = function(request, response) { }; -DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response) { +DebugCommandProcessor.prototype.changeLiveRequest_ = function( + request, response) { if (!Debug.LiveEdit) { return response.failed('LiveEdit feature is not supported'); } @@ -2393,7 +2408,7 @@ DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request, response.body.flags.push({ name: name, value: value }); } } -} +}; DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) { @@ -2499,7 +2514,7 @@ DebugCommandProcessor.prototype.lolPrintRequest_ = function(request, response) { // running. DebugCommandProcessor.prototype.isRunning = function() { return this.running_; -} +}; DebugCommandProcessor.prototype.systemBreak = function(cmd, args) { @@ -2515,7 +2530,7 @@ function NumberToHex8Str(n) { n = n >>> 4; } return r; -}; +} /** @@ -2591,7 +2606,7 @@ function ValueToProtocolValue_(value, mirror_serializer) { case 'string': case 'number': json = value; - break + break; default: json = null; diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index 20cd802748..f2c4dda875 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -40,6 +40,7 @@ #include "global-handles.h" #include "ic.h" #include "ic-inl.h" +#include "isolate-inl.h" #include "list.h" #include "messages.h" #include "natives.h" @@ -84,21 +85,9 @@ static void PrintLn(v8::Local<v8::Value> value) { } -static Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind) { - Isolate* isolate = Isolate::Current(); - CALL_HEAP_FUNCTION( - isolate, - isolate->stub_cache()->ComputeCallDebugBreak(argc, kind), - Code); -} - - static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { Isolate* isolate = Isolate::Current(); - CALL_HEAP_FUNCTION( - isolate, - isolate->stub_cache()->ComputeCallDebugPrepareStepIn(argc, kind), - Code); + return isolate->stub_cache()->ComputeCallDebugPrepareStepIn(argc, kind); } @@ -401,15 +390,15 @@ void BreakLocationIterator::PrepareStepIn() { // Step in can only be prepared if currently positioned on an IC call, // construct call or CallFunction stub call. Address target = rinfo()->target_address(); - Handle<Code> code(Code::GetCodeFromTargetAddress(target)); - if (code->is_call_stub() || code->is_keyed_call_stub()) { + Handle<Code> target_code(Code::GetCodeFromTargetAddress(target)); + if (target_code->is_call_stub() || target_code->is_keyed_call_stub()) { // Step in through IC call is handled by the runtime system. Therefore make // sure that the any current IC is cleared and the runtime system is // called. If the executing code has a debug break at the location change // the call in the original code as it is the code there that will be // executed in place of the debug break call. - Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count(), - code->kind()); + Handle<Code> stub = ComputeCallDebugPrepareStepIn( + target_code->arguments_count(), target_code->kind()); if (IsDebugBreak()) { original_rinfo()->set_target_address(stub->entry()); } else { @@ -419,7 +408,7 @@ void BreakLocationIterator::PrepareStepIn() { #ifdef DEBUG // All the following stuff is needed only for assertion checks so the code // is wrapped in ifdef. - Handle<Code> maybe_call_function_stub = code; + Handle<Code> maybe_call_function_stub = target_code; if (IsDebugBreak()) { Address original_target = original_rinfo()->target_address(); maybe_call_function_stub = @@ -436,8 +425,9 @@ void BreakLocationIterator::PrepareStepIn() { // Step in through CallFunction stub should also be prepared by caller of // this function (Debug::PrepareStep) which should flood target function // with breakpoints. - ASSERT(RelocInfo::IsConstructCall(rmode()) || code->is_inline_cache_stub() - || is_call_function_stub); + ASSERT(RelocInfo::IsConstructCall(rmode()) || + target_code->is_inline_cache_stub() || + is_call_function_stub); #endif } } @@ -474,11 +464,11 @@ void BreakLocationIterator::SetDebugBreakAtIC() { RelocInfo::Mode mode = rmode(); if (RelocInfo::IsCodeTarget(mode)) { Address target = rinfo()->target_address(); - Handle<Code> code(Code::GetCodeFromTargetAddress(target)); + Handle<Code> target_code(Code::GetCodeFromTargetAddress(target)); // Patch the code to invoke the builtin debug break function matching the // calling convention used by the call site. - Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode)); + Handle<Code> dbgbrk_code(Debug::FindDebugBreak(target_code, mode)); rinfo()->set_target_address(dbgbrk_code->entry()); } } @@ -686,7 +676,7 @@ void ScriptCache::HandleWeakScript(v8::Persistent<v8::Value> obj, void* data) { } -void Debug::Setup(bool create_heap_objects) { +void Debug::SetUp(bool create_heap_objects) { ThreadInit(); if (create_heap_objects) { // Get code to handle debug break on return. @@ -772,7 +762,7 @@ bool Debug::CompileDebuggerScript(int index) { // Execute the shared function in the debugger context. Handle<Context> context = isolate->global_context(); - bool caught_exception = false; + bool caught_exception; Handle<JSFunction> function = factory->NewFunctionFromSharedFunctionInfo(function_info, context); @@ -831,8 +821,8 @@ bool Debug::Load() { Handle<GlobalObject> global = Handle<GlobalObject>(context->global()); RETURN_IF_EMPTY_HANDLE_VALUE( isolate_, - SetProperty(global, key, Handle<Object>(global->builtins()), - NONE, kNonStrictMode), + JSReceiver::SetProperty(global, key, Handle<Object>(global->builtins()), + NONE, kNonStrictMode), false); // Compile the JavaScript for the debugger in the debugger context. @@ -1103,14 +1093,13 @@ bool Debug::CheckBreakPoint(Handle<Object> break_point_object) { Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id()); // Call HandleBreakPointx. - bool caught_exception = false; - const int argc = 2; - Object** argv[argc] = { - break_id.location(), - reinterpret_cast<Object**>(break_point_object.location()) - }; + bool caught_exception; + Handle<Object> argv[] = { break_id, break_point_object }; Handle<Object> result = Execution::TryCall(check_break_point, - isolate_->js_builtins_object(), argc, argv, &caught_exception); + isolate_->js_builtins_object(), + ARRAY_SIZE(argv), + argv, + &caught_exception); // If exception or non boolean result handle as not triggered if (caught_exception || !result->IsBoolean()) { @@ -1151,7 +1140,7 @@ void Debug::SetBreakPoint(Handle<SharedFunctionInfo> shared, Handle<DebugInfo> debug_info = GetDebugInfo(shared); // Source positions starts with zero. - ASSERT(source_position >= 0); + ASSERT(*source_position >= 0); // Find the break point and change it. BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS); @@ -1218,7 +1207,7 @@ void Debug::ClearAllBreakPoints() { void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) { PrepareForBreakPoints(); - // Make sure the function has setup the debug info. + // Make sure the function has set up the debug info. if (!EnsureDebugInfo(shared)) { // Return if we failed to retrieve the debug info. return; @@ -1543,40 +1532,47 @@ bool Debug::IsBreakStub(Code* code) { // Find the builtin to use for invoking the debug break Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) { + Isolate* isolate = Isolate::Current(); + // Find the builtin debug break function matching the calling convention // used by the call site. if (code->is_inline_cache_stub()) { switch (code->kind()) { case Code::CALL_IC: case Code::KEYED_CALL_IC: - return ComputeCallDebugBreak(code->arguments_count(), code->kind()); + return isolate->stub_cache()->ComputeCallDebugBreak( + code->arguments_count(), code->kind()); case Code::LOAD_IC: - return Isolate::Current()->builtins()->LoadIC_DebugBreak(); + return isolate->builtins()->LoadIC_DebugBreak(); case Code::STORE_IC: - return Isolate::Current()->builtins()->StoreIC_DebugBreak(); + return isolate->builtins()->StoreIC_DebugBreak(); case Code::KEYED_LOAD_IC: - return Isolate::Current()->builtins()->KeyedLoadIC_DebugBreak(); + return isolate->builtins()->KeyedLoadIC_DebugBreak(); case Code::KEYED_STORE_IC: - return Isolate::Current()->builtins()->KeyedStoreIC_DebugBreak(); + return isolate->builtins()->KeyedStoreIC_DebugBreak(); default: UNREACHABLE(); } } if (RelocInfo::IsConstructCall(mode)) { - Handle<Code> result = - Isolate::Current()->builtins()->ConstructCall_DebugBreak(); - return result; + if (code->has_function_cache()) { + return isolate->builtins()->CallConstructStub_Recording_DebugBreak(); + } else { + return isolate->builtins()->CallConstructStub_DebugBreak(); + } } if (code->kind() == Code::STUB) { ASSERT(code->major_key() == CodeStub::CallFunction); - Handle<Code> result = - Isolate::Current()->builtins()->StubNoRegisters_DebugBreak(); - return result; + if (code->has_function_cache()) { + return isolate->builtins()->CallFunctionStub_Recording_DebugBreak(); + } else { + return isolate->builtins()->CallFunctionStub_DebugBreak(); + } } UNREACHABLE(); @@ -1726,11 +1722,283 @@ void Debug::ClearStepNext() { } +// Helper function to compile full code for debugging. This code will +// have debug break slots and deoptimization +// information. Deoptimization information is required in case that an +// optimized version of this function is still activated on the +// stack. It will also make sure that the full code is compiled with +// the same flags as the previous version - that is flags which can +// change the code generated. The current method of mapping from +// already compiled full code without debug break slots to full code +// with debug break slots depends on the generated code is otherwise +// exactly the same. +static bool CompileFullCodeForDebugging(Handle<SharedFunctionInfo> shared, + Handle<Code> current_code) { + ASSERT(!current_code->has_debug_break_slots()); + + CompilationInfo info(shared); + info.MarkCompilingForDebugging(current_code); + ASSERT(!info.shared_info()->is_compiled()); + ASSERT(!info.isolate()->has_pending_exception()); + + // Use compile lazy which will end up compiling the full code in the + // configuration configured above. + bool result = Compiler::CompileLazy(&info); + ASSERT(result != Isolate::Current()->has_pending_exception()); + info.isolate()->clear_pending_exception(); +#if DEBUG + if (result) { + Handle<Code> new_code(shared->code()); + ASSERT(new_code->has_debug_break_slots()); + ASSERT(current_code->is_compiled_optimizable() == + new_code->is_compiled_optimizable()); + ASSERT(current_code->instruction_size() <= new_code->instruction_size()); + } +#endif + return result; +} + + +static void CollectActiveFunctionsFromThread( + Isolate* isolate, + ThreadLocalTop* top, + List<Handle<JSFunction> >* active_functions, + Object* active_code_marker) { + // Find all non-optimized code functions with activation frames + // on the stack. This includes functions which have optimized + // activations (including inlined functions) on the stack as the + // non-optimized code is needed for the lazy deoptimization. + for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { + JavaScriptFrame* frame = it.frame(); + if (frame->is_optimized()) { + List<JSFunction*> functions(Compiler::kMaxInliningLevels + 1); + frame->GetFunctions(&functions); + for (int i = 0; i < functions.length(); i++) { + JSFunction* function = functions[i]; + active_functions->Add(Handle<JSFunction>(function)); + function->shared()->code()->set_gc_metadata(active_code_marker); + } + } else if (frame->function()->IsJSFunction()) { + JSFunction* function = JSFunction::cast(frame->function()); + ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); + active_functions->Add(Handle<JSFunction>(function)); + function->shared()->code()->set_gc_metadata(active_code_marker); + } + } +} + + +static void RedirectActivationsToRecompiledCodeOnThread( + Isolate* isolate, + ThreadLocalTop* top) { + for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { + JavaScriptFrame* frame = it.frame(); + + if (frame->is_optimized() || !frame->function()->IsJSFunction()) continue; + + JSFunction* function = JSFunction::cast(frame->function()); + + ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); + + Handle<Code> frame_code(frame->LookupCode()); + if (frame_code->has_debug_break_slots()) continue; + + Handle<Code> new_code(function->shared()->code()); + if (new_code->kind() != Code::FUNCTION || + !new_code->has_debug_break_slots()) { + continue; + } + + intptr_t delta = frame->pc() - frame_code->instruction_start(); + int debug_break_slot_count = 0; + int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT); + for (RelocIterator it(*new_code, mask); !it.done(); it.next()) { + // Check if the pc in the new code with debug break + // slots is before this slot. + RelocInfo* info = it.rinfo(); + int debug_break_slot_bytes = + debug_break_slot_count * Assembler::kDebugBreakSlotLength; + intptr_t new_delta = + info->pc() - + new_code->instruction_start() - + debug_break_slot_bytes; + if (new_delta > delta) { + break; + } + + // Passed a debug break slot in the full code with debug + // break slots. + debug_break_slot_count++; + } + int debug_break_slot_bytes = + debug_break_slot_count * Assembler::kDebugBreakSlotLength; + if (FLAG_trace_deopt) { + PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " + "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " + "for debugging, " + "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n", + reinterpret_cast<intptr_t>( + frame_code->instruction_start()), + reinterpret_cast<intptr_t>( + frame_code->instruction_start()) + + frame_code->instruction_size(), + frame_code->instruction_size(), + reinterpret_cast<intptr_t>(new_code->instruction_start()), + reinterpret_cast<intptr_t>(new_code->instruction_start()) + + new_code->instruction_size(), + new_code->instruction_size(), + reinterpret_cast<intptr_t>(frame->pc()), + reinterpret_cast<intptr_t>(new_code->instruction_start()) + + delta + debug_break_slot_bytes); + } + + // Patch the return address to return into the code with + // debug break slots. + frame->set_pc( + new_code->instruction_start() + delta + debug_break_slot_bytes); + } +} + + +class ActiveFunctionsCollector : public ThreadVisitor { + public: + explicit ActiveFunctionsCollector(List<Handle<JSFunction> >* active_functions, + Object* active_code_marker) + : active_functions_(active_functions), + active_code_marker_(active_code_marker) { } + + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + CollectActiveFunctionsFromThread(isolate, + top, + active_functions_, + active_code_marker_); + } + + private: + List<Handle<JSFunction> >* active_functions_; + Object* active_code_marker_; +}; + + +class ActiveFunctionsRedirector : public ThreadVisitor { + public: + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + RedirectActivationsToRecompiledCodeOnThread(isolate, top); + } +}; + + void Debug::PrepareForBreakPoints() { // If preparing for the first break point make sure to deoptimize all // functions as debugging does not work with optimized code. if (!has_break_points_) { Deoptimizer::DeoptimizeAll(); + + Handle<Code> lazy_compile = + Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile)); + + // Keep the list of activated functions in a handlified list as it + // is used both in GC and non-GC code. + List<Handle<JSFunction> > active_functions(100); + + { + // We are going to iterate heap to find all functions without + // debug break slots. + isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "preparing for breakpoints"); + + // Ensure no GC in this scope as we are going to use gc_metadata + // field in the Code object to mark active functions. + AssertNoAllocation no_allocation; + + Object* active_code_marker = isolate_->heap()->the_hole_value(); + + CollectActiveFunctionsFromThread(isolate_, + isolate_->thread_local_top(), + &active_functions, + active_code_marker); + ActiveFunctionsCollector active_functions_collector(&active_functions, + active_code_marker); + isolate_->thread_manager()->IterateArchivedThreads( + &active_functions_collector); + + // Scan the heap for all non-optimized functions which have no + // debug break slots and are not active or inlined into an active + // function and mark them for lazy compilation. + HeapIterator iterator; + HeapObject* obj = NULL; + while (((obj = iterator.next()) != NULL)) { + if (obj->IsJSFunction()) { + JSFunction* function = JSFunction::cast(obj); + SharedFunctionInfo* shared = function->shared(); + if (shared->allows_lazy_compilation() && + shared->script()->IsScript() && + function->code()->kind() == Code::FUNCTION && + !function->code()->has_debug_break_slots() && + shared->code()->gc_metadata() != active_code_marker) { + function->set_code(*lazy_compile); + function->shared()->set_code(*lazy_compile); + } + } + } + + // Clear gc_metadata field. + for (int i = 0; i < active_functions.length(); i++) { + Handle<JSFunction> function = active_functions[i]; + function->shared()->code()->set_gc_metadata(Smi::FromInt(0)); + } + } + + // Now recompile all functions with activation frames and and + // patch the return address to run in the new compiled code. + for (int i = 0; i < active_functions.length(); i++) { + Handle<JSFunction> function = active_functions[i]; + + if (function->code()->kind() == Code::FUNCTION && + function->code()->has_debug_break_slots()) { + // Nothing to do. Function code already had debug break slots. + continue; + } + + Handle<SharedFunctionInfo> shared(function->shared()); + // If recompilation is not possible just skip it. + if (shared->is_toplevel() || + !shared->allows_lazy_compilation() || + shared->code()->kind() == Code::BUILTIN) { + continue; + } + + // Make sure that the shared full code is compiled with debug + // break slots. + if (!shared->code()->has_debug_break_slots()) { + // Try to compile the full code with debug break slots. If it + // fails just keep the current code. + Handle<Code> current_code(function->shared()->code()); + ZoneScope zone_scope(isolate_, DELETE_ON_EXIT); + shared->set_code(*lazy_compile); + bool prev_force_debugger_active = + isolate_->debugger()->force_debugger_active(); + isolate_->debugger()->set_force_debugger_active(true); + ASSERT(current_code->kind() == Code::FUNCTION); + CompileFullCodeForDebugging(shared, current_code); + isolate_->debugger()->set_force_debugger_active( + prev_force_debugger_active); + if (!shared->is_compiled()) { + shared->set_code(*current_code); + continue; + } + } + + // Keep function code in sync with shared function info. + function->set_code(shared->code()); + } + + RedirectActivationsToRecompiledCodeOnThread(isolate_, + isolate_->thread_local_top()); + + ActiveFunctionsRedirector active_functions_redirector; + isolate_->thread_manager()->IterateArchivedThreads( + &active_functions_redirector); } } @@ -1744,7 +2012,9 @@ bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) { } // Ensure shared in compiled. Return false if this failed. - if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false; + if (!SharedFunctionInfo::EnsureCompiled(shared, CLEAR_EXCEPTION)) { + return false; + } // Create the debug info object. Handle<DebugInfo> debug_info = FACTORY->NewDebugInfo(shared); @@ -1959,9 +2229,11 @@ void Debug::CreateScriptCache() { // Perform two GCs to get rid of all unreferenced scripts. The first GC gets // rid of all the cached script wrappers and the second gets rid of the - // scripts which are no longer referenced. - heap->CollectAllGarbage(false); - heap->CollectAllGarbage(false); + // scripts which are no longer referenced. The second also sweeps precisely, + // which saves us doing yet another GC to make the heap iterable. + heap->CollectAllGarbage(Heap::kNoGCFlags, "Debug::CreateScriptCache"); + heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "Debug::CreateScriptCache"); ASSERT(script_cache_ == NULL); script_cache_ = new ScriptCache(); @@ -1969,6 +2241,8 @@ void Debug::CreateScriptCache() { // Scan heap for Script objects. int count = 0; HeapIterator iterator; + AssertNoAllocation no_allocation; + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { if (obj->IsScript() && Script::cast(obj)->HasValidSource()) { script_cache_->Add(Handle<Script>(Script::cast(obj))); @@ -2009,7 +2283,8 @@ Handle<FixedArray> Debug::GetLoadedScripts() { // Perform GC to get unreferenced scripts evicted from the cache before // returning the content. - isolate_->heap()->CollectAllGarbage(false); + isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, + "Debug::GetLoadedScripts"); // Get the scripts from the cache. return script_cache_->GetScripts(); @@ -2031,6 +2306,7 @@ Debugger::Debugger(Isolate* isolate) compiling_natives_(false), is_loading_debugger_(false), never_unload_debugger_(false), + force_debugger_active_(false), message_handler_(NULL), debugger_unload_pending_(false), host_dispatch_handler_(NULL), @@ -2055,7 +2331,8 @@ Debugger::~Debugger() { Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name, - int argc, Object*** argv, + int argc, + Handle<Object> argv[], bool* caught_exception) { ASSERT(isolate_->context() == *isolate_->debug()->debug_context()); @@ -2072,7 +2349,9 @@ Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name, Handle<Object> js_object = Execution::TryCall( Handle<JSFunction>::cast(constructor), Handle<JSObject>(isolate_->debug()->debug_context()->global()), - argc, argv, caught_exception); + argc, + argv, + caught_exception); return js_object; } @@ -2081,10 +2360,11 @@ Handle<Object> Debugger::MakeExecutionState(bool* caught_exception) { // Create the execution state object. Handle<Object> break_id = isolate_->factory()->NewNumberFromInt( isolate_->debug()->break_id()); - const int argc = 1; - Object** argv[argc] = { break_id.location() }; + Handle<Object> argv[] = { break_id }; return MakeJSObject(CStrVector("MakeExecutionState"), - argc, argv, caught_exception); + ARRAY_SIZE(argv), + argv, + caught_exception); } @@ -2092,11 +2372,9 @@ Handle<Object> Debugger::MakeBreakEvent(Handle<Object> exec_state, Handle<Object> break_points_hit, bool* caught_exception) { // Create the new break event object. - const int argc = 2; - Object** argv[argc] = { exec_state.location(), - break_points_hit.location() }; + Handle<Object> argv[] = { exec_state, break_points_hit }; return MakeJSObject(CStrVector("MakeBreakEvent"), - argc, + ARRAY_SIZE(argv), argv, caught_exception); } @@ -2108,23 +2386,24 @@ Handle<Object> Debugger::MakeExceptionEvent(Handle<Object> exec_state, bool* caught_exception) { Factory* factory = isolate_->factory(); // Create the new exception event object. - const int argc = 3; - Object** argv[argc] = { exec_state.location(), - exception.location(), - uncaught ? factory->true_value().location() : - factory->false_value().location()}; + Handle<Object> argv[] = { exec_state, + exception, + factory->ToBoolean(uncaught) }; return MakeJSObject(CStrVector("MakeExceptionEvent"), - argc, argv, caught_exception); + ARRAY_SIZE(argv), + argv, + caught_exception); } Handle<Object> Debugger::MakeNewFunctionEvent(Handle<Object> function, bool* caught_exception) { // Create the new function event object. - const int argc = 1; - Object** argv[argc] = { function.location() }; + Handle<Object> argv[] = { function }; return MakeJSObject(CStrVector("MakeNewFunctionEvent"), - argc, argv, caught_exception); + ARRAY_SIZE(argv), + argv, + caught_exception); } @@ -2135,14 +2414,11 @@ Handle<Object> Debugger::MakeCompileEvent(Handle<Script> script, // Create the compile event object. Handle<Object> exec_state = MakeExecutionState(caught_exception); Handle<Object> script_wrapper = GetScriptWrapper(script); - const int argc = 3; - Object** argv[argc] = { exec_state.location(), - script_wrapper.location(), - before ? factory->true_value().location() : - factory->false_value().location() }; - + Handle<Object> argv[] = { exec_state, + script_wrapper, + factory->ToBoolean(before) }; return MakeJSObject(CStrVector("MakeCompileEvent"), - argc, + ARRAY_SIZE(argv), argv, caught_exception); } @@ -2153,11 +2429,10 @@ Handle<Object> Debugger::MakeScriptCollectedEvent(int id, // Create the script collected event object. Handle<Object> exec_state = MakeExecutionState(caught_exception); Handle<Object> id_object = Handle<Smi>(Smi::FromInt(id)); - const int argc = 2; - Object** argv[argc] = { exec_state.location(), id_object.location() }; + Handle<Object> argv[] = { exec_state, id_object }; return MakeJSObject(CStrVector("MakeScriptCollectedEvent"), - argc, + ARRAY_SIZE(argv), argv, caught_exception); } @@ -2307,12 +2582,13 @@ void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSValue> wrapper = GetScriptWrapper(script); // Call UpdateScriptBreakPoints expect no exceptions. - bool caught_exception = false; - const int argc = 1; - Object** argv[argc] = { reinterpret_cast<Object**>(wrapper.location()) }; + bool caught_exception; + Handle<Object> argv[] = { wrapper }; Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points), - Isolate::Current()->js_builtins_object(), argc, argv, - &caught_exception); + Isolate::Current()->js_builtins_object(), + ARRAY_SIZE(argv), + argv, + &caught_exception); if (caught_exception) { return; } @@ -2425,7 +2701,8 @@ void Debugger::CallCEventCallback(v8::DebugEvent event, v8::Debug::ClientData* client_data) { Handle<Foreign> callback_obj(Handle<Foreign>::cast(event_listener_)); v8::Debug::EventCallback2 callback = - FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->address()); + FUNCTION_CAST<v8::Debug::EventCallback2>( + callback_obj->foreign_address()); EventDetailsImpl event_details( event, Handle<JSObject>::cast(exec_state), @@ -2443,13 +2720,16 @@ void Debugger::CallJSEventCallback(v8::DebugEvent event, Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_)); // Invoke the JavaScript debug event listener. - const int argc = 4; - Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(), - exec_state.location(), - Handle<Object>::cast(event_data).location(), - event_listener_data_.location() }; - bool caught_exception = false; - Execution::TryCall(fun, isolate_->global(), argc, argv, &caught_exception); + Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event)), + exec_state, + event_data, + event_listener_data_ }; + bool caught_exception; + Execution::TryCall(fun, + isolate_->global(), + ARRAY_SIZE(argv), + argv, + &caught_exception); // Silently ignore exceptions from debug event listeners. } @@ -2640,7 +2920,7 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, command.Dispose(); // Return from debug event processing if either the VM is put into the - // runnning state (through a continue command) or auto continue is active + // running state (through a continue command) or auto continue is active // and there are no more commands queued. if (running && !HasCommands()) { return; @@ -2795,7 +3075,9 @@ void Debugger::EnqueueDebugCommand(v8::Debug::ClientData* client_data) { bool Debugger::IsDebuggerActive() { ScopedLock with(debugger_access_); - return message_handler_ != NULL || !event_listener_.is_null(); + return message_handler_ != NULL || + !event_listener_.is_null() || + force_debugger_active_; } @@ -2818,12 +3100,11 @@ Handle<Object> Debugger::Call(Handle<JSFunction> fun, return isolate_->factory()->undefined_value(); } - static const int kArgc = 2; - Object** argv[kArgc] = { exec_state.location(), data.location() }; + Handle<Object> argv[] = { exec_state, data }; Handle<Object> result = Execution::Call( fun, Handle<Object>(isolate_->debug()->debug_context_->global_proxy()), - kArgc, + ARRAY_SIZE(argv), argv, pending_exception); return result; @@ -2849,7 +3130,7 @@ bool Debugger::StartAgent(const char* name, int port, v8::Debug::DebugBreak(); } - if (Socket::Setup()) { + if (Socket::SetUp()) { if (agent_ == NULL) { agent_ = new DebuggerAgent(name, port); agent_->Start(); @@ -2891,6 +3172,94 @@ void Debugger::CallMessageDispatchHandler() { } +EnterDebugger::EnterDebugger() + : isolate_(Isolate::Current()), + prev_(isolate_->debug()->debugger_entry()), + it_(isolate_), + has_js_frames_(!it_.done()), + save_(isolate_) { + Debug* debug = isolate_->debug(); + ASSERT(prev_ != NULL || !debug->is_interrupt_pending(PREEMPT)); + ASSERT(prev_ != NULL || !debug->is_interrupt_pending(DEBUGBREAK)); + + // Link recursive debugger entry. + debug->set_debugger_entry(this); + + // Store the previous break id and frame id. + break_id_ = debug->break_id(); + break_frame_id_ = debug->break_frame_id(); + + // Create the new break info. If there is no JavaScript frames there is no + // break frame id. + if (has_js_frames_) { + debug->NewBreak(it_.frame()->id()); + } else { + debug->NewBreak(StackFrame::NO_ID); + } + + // Make sure that debugger is loaded and enter the debugger context. + load_failed_ = !debug->Load(); + if (!load_failed_) { + // NOTE the member variable save which saves the previous context before + // this change. + isolate_->set_context(*debug->debug_context()); + } +} + + +EnterDebugger::~EnterDebugger() { + ASSERT(Isolate::Current() == isolate_); + Debug* debug = isolate_->debug(); + + // Restore to the previous break state. + debug->SetBreak(break_frame_id_, break_id_); + + // Check for leaving the debugger. + if (prev_ == NULL) { + // Clear mirror cache when leaving the debugger. Skip this if there is a + // pending exception as clearing the mirror cache calls back into + // JavaScript. This can happen if the v8::Debug::Call is used in which + // case the exception should end up in the calling code. + if (!isolate_->has_pending_exception()) { + // Try to avoid any pending debug break breaking in the clear mirror + // cache JavaScript code. + if (isolate_->stack_guard()->IsDebugBreak()) { + debug->set_interrupts_pending(DEBUGBREAK); + isolate_->stack_guard()->Continue(DEBUGBREAK); + } + debug->ClearMirrorCache(); + } + + // Request preemption and debug break when leaving the last debugger entry + // if any of these where recorded while debugging. + if (debug->is_interrupt_pending(PREEMPT)) { + // This re-scheduling of preemption is to avoid starvation in some + // debugging scenarios. + debug->clear_interrupt_pending(PREEMPT); + isolate_->stack_guard()->Preempt(); + } + if (debug->is_interrupt_pending(DEBUGBREAK)) { + debug->clear_interrupt_pending(DEBUGBREAK); + isolate_->stack_guard()->DebugBreak(); + } + + // If there are commands in the queue when leaving the debugger request + // that these commands are processed. + if (isolate_->debugger()->HasCommands()) { + isolate_->stack_guard()->DebugCommand(); + } + + // If leaving the debugger with the debugger no longer active unload it. + if (!isolate_->debugger()->IsDebuggerActive()) { + isolate_->debugger()->UnloadDebugger(); + } + } + + // Leaving this debugger entry. + debug->set_debugger_entry(prev_); +} + + MessageImpl MessageImpl::NewEvent(DebugEvent event, bool running, Handle<JSObject> exec_state, diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h index a5083eb4fe..b9384e574d 100644 --- a/deps/v8/src/debug.h +++ b/deps/v8/src/debug.h @@ -224,7 +224,7 @@ class DebugInfoListNode { // DebugInfo. class Debug { public: - void Setup(bool create_heap_objects); + void SetUp(bool create_heap_objects); bool Load(); void Unload(); bool IsLoaded() { return !debug_context_.is_null(); } @@ -402,9 +402,11 @@ class Debug { static void GenerateStoreICDebugBreak(MacroAssembler* masm); static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm); static void GenerateKeyedStoreICDebugBreak(MacroAssembler* masm); - static void GenerateConstructCallDebugBreak(MacroAssembler* masm); static void GenerateReturnDebugBreak(MacroAssembler* masm); - static void GenerateStubNoRegistersDebugBreak(MacroAssembler* masm); + static void GenerateCallFunctionStubDebugBreak(MacroAssembler* masm); + static void GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm); + static void GenerateCallConstructStubDebugBreak(MacroAssembler* masm); + static void GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm); static void GenerateSlotDebugBreak(MacroAssembler* masm); static void GeneratePlainReturnLiveEdit(MacroAssembler* masm); @@ -707,7 +709,8 @@ class Debugger { void DebugRequest(const uint16_t* json_request, int length); Handle<Object> MakeJSObject(Vector<const char> constructor_name, - int argc, Object*** argv, + int argc, + Handle<Object> argv[], bool* caught_exception); Handle<Object> MakeExecutionState(bool* caught_exception); Handle<Object> MakeBreakEvent(Handle<Object> exec_state, @@ -811,11 +814,15 @@ class Debugger { } void set_compiling_natives(bool compiling_natives) { - Debugger::compiling_natives_ = compiling_natives; + compiling_natives_ = compiling_natives; } bool compiling_natives() const { return compiling_natives_; } void set_loading_debugger(bool v) { is_loading_debugger_ = v; } bool is_loading_debugger() const { return is_loading_debugger_; } + void set_force_debugger_active(bool force_debugger_active) { + force_debugger_active_ = force_debugger_active; + } + bool force_debugger_active() const { return force_debugger_active_; } bool IsDebuggerActive(); @@ -841,6 +848,7 @@ class Debugger { bool compiling_natives_; // Are we compiling natives? bool is_loading_debugger_; // Are we loading the debugger? bool never_unload_debugger_; // Can we unload the debugger? + bool force_debugger_active_; // Activate debugger without event listeners. v8::Debug::MessageHandler2 message_handler_; bool debugger_unload_pending_; // Was message handler cleared? v8::Debug::HostDispatchHandler host_dispatch_handler_; @@ -871,91 +879,8 @@ class Debugger { // some reason could not be entered FailedToEnter will return true. class EnterDebugger BASE_EMBEDDED { public: - EnterDebugger() - : isolate_(Isolate::Current()), - prev_(isolate_->debug()->debugger_entry()), - it_(isolate_), - has_js_frames_(!it_.done()), - save_(isolate_) { - Debug* debug = isolate_->debug(); - ASSERT(prev_ != NULL || !debug->is_interrupt_pending(PREEMPT)); - ASSERT(prev_ != NULL || !debug->is_interrupt_pending(DEBUGBREAK)); - - // Link recursive debugger entry. - debug->set_debugger_entry(this); - - // Store the previous break id and frame id. - break_id_ = debug->break_id(); - break_frame_id_ = debug->break_frame_id(); - - // Create the new break info. If there is no JavaScript frames there is no - // break frame id. - if (has_js_frames_) { - debug->NewBreak(it_.frame()->id()); - } else { - debug->NewBreak(StackFrame::NO_ID); - } - - // Make sure that debugger is loaded and enter the debugger context. - load_failed_ = !debug->Load(); - if (!load_failed_) { - // NOTE the member variable save which saves the previous context before - // this change. - isolate_->set_context(*debug->debug_context()); - } - } - - ~EnterDebugger() { - ASSERT(Isolate::Current() == isolate_); - Debug* debug = isolate_->debug(); - - // Restore to the previous break state. - debug->SetBreak(break_frame_id_, break_id_); - - // Check for leaving the debugger. - if (prev_ == NULL) { - // Clear mirror cache when leaving the debugger. Skip this if there is a - // pending exception as clearing the mirror cache calls back into - // JavaScript. This can happen if the v8::Debug::Call is used in which - // case the exception should end up in the calling code. - if (!isolate_->has_pending_exception()) { - // Try to avoid any pending debug break breaking in the clear mirror - // cache JavaScript code. - if (isolate_->stack_guard()->IsDebugBreak()) { - debug->set_interrupts_pending(DEBUGBREAK); - isolate_->stack_guard()->Continue(DEBUGBREAK); - } - debug->ClearMirrorCache(); - } - - // Request preemption and debug break when leaving the last debugger entry - // if any of these where recorded while debugging. - if (debug->is_interrupt_pending(PREEMPT)) { - // This re-scheduling of preemption is to avoid starvation in some - // debugging scenarios. - debug->clear_interrupt_pending(PREEMPT); - isolate_->stack_guard()->Preempt(); - } - if (debug->is_interrupt_pending(DEBUGBREAK)) { - debug->clear_interrupt_pending(DEBUGBREAK); - isolate_->stack_guard()->DebugBreak(); - } - - // If there are commands in the queue when leaving the debugger request - // that these commands are processed. - if (isolate_->debugger()->HasCommands()) { - isolate_->stack_guard()->DebugCommand(); - } - - // If leaving the debugger with the debugger no longer active unload it. - if (!isolate_->debugger()->IsDebuggerActive()) { - isolate_->debugger()->UnloadDebugger(); - } - } - - // Leaving this debugger entry. - debug->set_debugger_entry(prev_); - } + EnterDebugger(); + ~EnterDebugger(); // Check whether the debugger could be entered. inline bool FailedToEnter() { return load_failed_; } diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc index 0ada28b015..682eb535ce 100644 --- a/deps/v8/src/deoptimizer.cc +++ b/deps/v8/src/deoptimizer.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -52,11 +52,13 @@ DeoptimizerData::DeoptimizerData() { DeoptimizerData::~DeoptimizerData() { if (eager_deoptimization_entry_code_ != NULL) { - eager_deoptimization_entry_code_->Free(EXECUTABLE); + Isolate::Current()->memory_allocator()->Free( + eager_deoptimization_entry_code_); eager_deoptimization_entry_code_ = NULL; } if (lazy_deoptimization_entry_code_ != NULL) { - lazy_deoptimization_entry_code_->Free(EXECUTABLE); + Isolate::Current()->memory_allocator()->Free( + lazy_deoptimization_entry_code_); lazy_deoptimization_entry_code_ = NULL; } } @@ -71,6 +73,8 @@ void DeoptimizerData::Iterate(ObjectVisitor* v) { #endif +// We rely on this function not causing a GC. It is called from generated code +// without having a real stack frame in place. Deoptimizer* Deoptimizer::New(JSFunction* function, BailoutType type, unsigned bailout_id, @@ -100,10 +104,27 @@ Deoptimizer* Deoptimizer::Grab(Isolate* isolate) { return result; } + +int Deoptimizer::ConvertJSFrameIndexToFrameIndex(int jsframe_index) { + if (jsframe_index == 0) return 0; + + int frame_index = 0; + while (jsframe_index >= 0) { + FrameDescription* frame = output_[frame_index]; + if (frame->GetFrameType() == StackFrame::JAVA_SCRIPT) { + jsframe_index--; + } + frame_index++; + } + + return frame_index - 1; +} + + #ifdef ENABLE_DEBUGGER_SUPPORT DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame( JavaScriptFrame* frame, - int frame_index, + int jsframe_index, Isolate* isolate) { ASSERT(isolate == Isolate::Current()); ASSERT(frame->is_optimized()); @@ -139,22 +160,40 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame( // Create the GC safe output frame information and register it for GC // handling. - ASSERT_LT(frame_index, deoptimizer->output_count()); + ASSERT_LT(jsframe_index, deoptimizer->jsframe_count()); + + // Convert JS frame index into frame index. + int frame_index = deoptimizer->ConvertJSFrameIndexToFrameIndex(jsframe_index); + + bool has_arguments_adaptor = + frame_index > 0 && + deoptimizer->output_[frame_index - 1]->GetFrameType() == + StackFrame::ARGUMENTS_ADAPTOR; + DeoptimizedFrameInfo* info = - new DeoptimizedFrameInfo(deoptimizer, frame_index); + new DeoptimizedFrameInfo(deoptimizer, frame_index, has_arguments_adaptor); isolate->deoptimizer_data()->deoptimized_frame_info_ = info; // Get the "simulated" top and size for the requested frame. - Address top = - reinterpret_cast<Address>(deoptimizer->output_[frame_index]->GetTop()); - uint32_t size = deoptimizer->output_[frame_index]->GetFrameSize(); + FrameDescription* parameters_frame = + deoptimizer->output_[ + has_arguments_adaptor ? (frame_index - 1) : frame_index]; + + uint32_t parameters_size = (info->parameters_count() + 1) * kPointerSize; + Address parameters_top = reinterpret_cast<Address>( + parameters_frame->GetTop() + (parameters_frame->GetFrameSize() - + parameters_size)); + + uint32_t expressions_size = info->expression_count() * kPointerSize; + Address expressions_top = reinterpret_cast<Address>( + deoptimizer->output_[frame_index]->GetTop()); // Done with the GC-unsafe frame descriptions. This re-enables allocation. deoptimizer->DeleteFrameDescriptions(); // Allocate a heap number for the doubles belonging to this frame. deoptimizer->MaterializeHeapNumbersForDebuggerInspectableFrame( - top, size, info); + parameters_top, parameters_size, expressions_top, expressions_size, info); // Finished using the deoptimizer instance. delete deoptimizer; @@ -260,11 +299,16 @@ void Deoptimizer::VisitAllOptimizedFunctions( AssertNoAllocation no_allocation; // Run through the list of all global contexts and deoptimize. - Object* global = Isolate::Current()->heap()->global_contexts_list(); - while (!global->IsUndefined()) { - VisitAllOptimizedFunctionsForGlobalObject(Context::cast(global)->global(), - visitor); - global = Context::cast(global)->get(Context::NEXT_CONTEXT_LINK); + Object* context = Isolate::Current()->heap()->global_contexts_list(); + while (!context->IsUndefined()) { + // GC can happen when the context is not fully initialized, + // so the global field of the context can be undefined. + Object* global = Context::cast(context)->get(Context::GLOBAL_INDEX); + if (!global->IsUndefined()) { + VisitAllOptimizedFunctionsForGlobalObject(JSObject::cast(global), + visitor); + } + context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); } } @@ -304,7 +348,10 @@ Deoptimizer::Deoptimizer(Isolate* isolate, fp_to_sp_delta_(fp_to_sp_delta), input_(NULL), output_count_(0), + jsframe_count_(0), output_(NULL), + frame_alignment_marker_(isolate->heap()->frame_alignment_marker()), + has_alignment_padding_(0), deferred_heap_numbers_(0) { if (FLAG_trace_deopt && type != OSR) { if (type == DEBUGGER) { @@ -329,6 +376,26 @@ Deoptimizer::Deoptimizer(Isolate* isolate, if (type == EAGER) { ASSERT(from == NULL); optimized_code_ = function_->code(); + if (FLAG_trace_deopt && FLAG_code_comments) { + // Print instruction associated with this bailout. + const char* last_comment = NULL; + int mask = RelocInfo::ModeMask(RelocInfo::COMMENT) + | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + for (RelocIterator it(optimized_code_, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + if (info->rmode() == RelocInfo::COMMENT) { + last_comment = reinterpret_cast<const char*>(info->data()); + } + if (info->rmode() == RelocInfo::RUNTIME_ENTRY) { + unsigned id = Deoptimizer::GetDeoptimizationId( + info->target_address(), Deoptimizer::EAGER); + if (id == bailout_id && last_comment != NULL) { + PrintF(" %s\n", last_comment); + break; + } + } + } + } } else if (type == LAZY) { optimized_code_ = FindDeoptimizingCodeFromAddress(from); ASSERT(optimized_code_ != NULL); @@ -346,9 +413,7 @@ Deoptimizer::Deoptimizer(Isolate* isolate, ASSERT(HEAP->allow_allocation(false)); unsigned size = ComputeInputFrameSize(); input_ = new(size) FrameDescription(size, function); -#ifdef DEBUG - input_->SetKind(Code::OPTIMIZED_FUNCTION); -#endif + input_->SetFrameType(StackFrame::JAVA_SCRIPT); } @@ -372,7 +437,7 @@ void Deoptimizer::DeleteFrameDescriptions() { Address Deoptimizer::GetDeoptimizationEntry(int id, BailoutType type) { ASSERT(id >= 0); if (id >= kNumberOfEntries) return NULL; - LargeObjectChunk* base = NULL; + MemoryChunk* base = NULL; DeoptimizerData* data = Isolate::Current()->deoptimizer_data(); if (type == EAGER) { if (data->eager_deoptimization_entry_code_ == NULL) { @@ -386,12 +451,12 @@ Address Deoptimizer::GetDeoptimizationEntry(int id, BailoutType type) { base = data->lazy_deoptimization_entry_code_; } return - static_cast<Address>(base->GetStartAddress()) + (id * table_entry_size_); + static_cast<Address>(base->body()) + (id * table_entry_size_); } int Deoptimizer::GetDeoptimizationId(Address addr, BailoutType type) { - LargeObjectChunk* base = NULL; + MemoryChunk* base = NULL; DeoptimizerData* data = Isolate::Current()->deoptimizer_data(); if (type == EAGER) { base = data->eager_deoptimization_entry_code_; @@ -399,14 +464,14 @@ int Deoptimizer::GetDeoptimizationId(Address addr, BailoutType type) { base = data->lazy_deoptimization_entry_code_; } if (base == NULL || - addr < base->GetStartAddress() || - addr >= base->GetStartAddress() + + addr < base->body() || + addr >= base->body() + (kNumberOfEntries * table_entry_size_)) { return kNotDeoptimizationEntry; } ASSERT_EQ(0, - static_cast<int>(addr - base->GetStartAddress()) % table_entry_size_); - return static_cast<int>(addr - base->GetStartAddress()) / table_entry_size_; + static_cast<int>(addr - base->body()) % table_entry_size_); + return static_cast<int>(addr - base->body()) / table_entry_size_; } @@ -448,6 +513,8 @@ int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) { } +// We rely on this function not causing a GC. It is called from generated code +// without having a real stack frame in place. void Deoptimizer::DoComputeOutputFrames() { if (bailout_type_ == OSR) { DoComputeOsrOutputFrame(); @@ -482,6 +549,7 @@ void Deoptimizer::DoComputeOutputFrames() { // Read the number of output frames and allocate an array for their // descriptions. int count = iterator.Next(); + iterator.Next(); // Drop JS frames count. ASSERT(output_ == NULL); output_ = new FrameDescription*[count]; for (int i = 0; i < count; ++i) { @@ -491,7 +559,21 @@ void Deoptimizer::DoComputeOutputFrames() { // Translate each output frame. for (int i = 0; i < count; ++i) { - DoComputeFrame(&iterator, i); + // Read the ast node id, function, and frame height for this output frame. + Translation::Opcode opcode = + static_cast<Translation::Opcode>(iterator.Next()); + switch (opcode) { + case Translation::JS_FRAME: + DoComputeJSFrame(&iterator, i); + jsframe_count_++; + break; + case Translation::ARGUMENTS_ADAPTOR_FRAME: + DoComputeArgumentsAdaptorFrame(&iterator, i); + break; + default: + UNREACHABLE(); + break; + } } // Print some helpful diagnostic information. @@ -532,39 +614,52 @@ void Deoptimizer::MaterializeHeapNumbers() { #ifdef ENABLE_DEBUGGER_SUPPORT void Deoptimizer::MaterializeHeapNumbersForDebuggerInspectableFrame( - Address top, uint32_t size, DeoptimizedFrameInfo* info) { + Address parameters_top, + uint32_t parameters_size, + Address expressions_top, + uint32_t expressions_size, + DeoptimizedFrameInfo* info) { ASSERT_EQ(DEBUGGER, bailout_type_); + Address parameters_bottom = parameters_top + parameters_size; + Address expressions_bottom = expressions_top + expressions_size; for (int i = 0; i < deferred_heap_numbers_.length(); i++) { HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i]; // Check of the heap number to materialize actually belong to the frame // being extracted. Address slot = d.slot_address(); - if (top <= slot && slot < top + size) { + if (parameters_top <= slot && slot < parameters_bottom) { Handle<Object> num = isolate_->factory()->NewNumber(d.value()); - // Calculate the index with the botton of the expression stack - // at index 0, and the fixed part (including incoming arguments) - // at negative indexes. - int index = static_cast<int>( - info->expression_count_ - (slot - top) / kPointerSize - 1); + + int index = (info->parameters_count() - 1) - + static_cast<int>(slot - parameters_top) / kPointerSize; + if (FLAG_trace_deopt) { PrintF("Materializing a new heap number %p [%e] in slot %p" - "for stack index %d\n", + "for parameter slot #%d\n", reinterpret_cast<void*>(*num), d.value(), d.slot_address(), index); } - if (index >=0) { - info->SetExpression(index, *num); - } else { - // Calculate parameter index subtracting one for the receiver. - int parameter_index = - index + - static_cast<int>(size) / kPointerSize - - info->expression_count_ - 1; - info->SetParameter(parameter_index, *num); + + info->SetParameter(index, *num); + } else if (expressions_top <= slot && slot < expressions_bottom) { + Handle<Object> num = isolate_->factory()->NewNumber(d.value()); + + int index = info->expression_count() - 1 - + static_cast<int>(slot - expressions_top) / kPointerSize; + + if (FLAG_trace_deopt) { + PrintF("Materializing a new heap number %p [%e] in slot %p" + "for expression slot #%d\n", + reinterpret_cast<void*>(*num), + d.value(), + d.slot_address(), + index); } + + info->SetExpression(index, *num); } } } @@ -589,7 +684,8 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, switch (opcode) { case Translation::BEGIN: - case Translation::FRAME: + case Translation::JS_FRAME: + case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::DUPLICATE: UNREACHABLE(); return; @@ -599,11 +695,13 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, intptr_t input_value = input_->GetRegister(input_reg); if (FLAG_trace_deopt) { PrintF( - " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" V8PRIxPTR " ; %s\n", + " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" V8PRIxPTR " ; %s ", output_[frame_index]->GetTop() + output_offset, output_offset, input_value, converter.NameOfCPURegister(input_reg)); + reinterpret_cast<Object*>(input_value)->ShortPrint(); + PrintF("\n"); } output_[frame_index]->SetFrameSlot(output_offset, input_value); return; @@ -656,15 +754,17 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::STACK_SLOT: { int input_slot_index = iterator->Next(); unsigned input_offset = - input_->GetOffsetFromSlotIndex(this, input_slot_index); + input_->GetOffsetFromSlotIndex(input_slot_index); intptr_t input_value = input_->GetFrameSlot(input_offset); if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": ", output_[frame_index]->GetTop() + output_offset); - PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d]\n", + PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d] ", output_offset, input_value, input_offset); + reinterpret_cast<Object*>(input_value)->ShortPrint(); + PrintF("\n"); } output_[frame_index]->SetFrameSlot(output_offset, input_value); return; @@ -673,7 +773,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::INT32_STACK_SLOT: { int input_slot_index = iterator->Next(); unsigned input_offset = - input_->GetOffsetFromSlotIndex(this, input_slot_index); + input_->GetOffsetFromSlotIndex(input_slot_index); intptr_t value = input_->GetFrameSlot(input_offset); bool is_smi = Smi::IsValid(value); if (FLAG_trace_deopt) { @@ -702,7 +802,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::DOUBLE_STACK_SLOT: { int input_slot_index = iterator->Next(); unsigned input_offset = - input_->GetOffsetFromSlotIndex(this, input_slot_index); + input_->GetOffsetFromSlotIndex(input_slot_index); double value = input_->GetDoubleFrameSlot(input_offset); if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n", @@ -771,7 +871,8 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, switch (opcode) { case Translation::BEGIN: - case Translation::FRAME: + case Translation::JS_FRAME: + case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::DUPLICATE: UNREACHABLE(); // Malformed input. return false; @@ -834,12 +935,14 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, case Translation::STACK_SLOT: { int output_index = iterator->Next(); unsigned output_offset = - output->GetOffsetFromSlotIndex(this, output_index); + output->GetOffsetFromSlotIndex(output_index); if (FLAG_trace_osr) { - PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d]\n", + PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ", output_offset, input_value, *input_offset); + reinterpret_cast<Object*>(input_value)->ShortPrint(); + PrintF("\n"); } output->SetFrameSlot(output_offset, input_value); break; @@ -851,7 +954,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, int output_index = iterator->Next(); unsigned output_offset = - output->GetOffsetFromSlotIndex(this, output_index); + output->GetOffsetFromSlotIndex(output_index); int int32_value = input_object->IsSmi() ? Smi::cast(input_object)->value() : DoubleToInt32(input_object->Number()); @@ -883,7 +986,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, int output_index = iterator->Next(); unsigned output_offset = - output->GetOffsetFromSlotIndex(this, output_index); + output->GetOffsetFromSlotIndex(output_index); double double_value = input_object->Number(); uint64_t int_value = BitCast<uint64_t, double>(double_value); int32_t lower = static_cast<int32_t>(int_value); @@ -939,7 +1042,10 @@ void Deoptimizer::PatchStackCheckCode(Code* unoptimized_code, for (uint32_t i = 0; i < table_length; ++i) { uint32_t pc_offset = Memory::uint32_at(stack_check_cursor + kIntSize); Address pc_after = unoptimized_code->instruction_start() + pc_offset; - PatchStackCheckCodeAt(pc_after, check_code, replacement_code); + PatchStackCheckCodeAt(unoptimized_code, + pc_after, + check_code, + replacement_code); stack_check_cursor += 2 * kIntSize; } } @@ -958,7 +1064,10 @@ void Deoptimizer::RevertStackCheckCode(Code* unoptimized_code, for (uint32_t i = 0; i < table_length; ++i) { uint32_t pc_offset = Memory::uint32_at(stack_check_cursor + kIntSize); Address pc_after = unoptimized_code->instruction_start() + pc_offset; - RevertStackCheckCodeAt(pc_after, check_code, replacement_code); + RevertStackCheckCodeAt(unoptimized_code, + pc_after, + check_code, + replacement_code); stack_check_cursor += 2 * kIntSize; } } @@ -988,8 +1097,8 @@ unsigned Deoptimizer::ComputeInputFrameSize() const { unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const { // The fixed part of the frame consists of the return address, frame // pointer, function, context, and all the incoming arguments. - static const unsigned kFixedSlotSize = 4 * kPointerSize; - return ComputeIncomingArgumentSize(function) + kFixedSlotSize; + return ComputeIncomingArgumentSize(function) + + StandardFrameConstants::kFixedFrameSize; } @@ -1025,7 +1134,7 @@ void Deoptimizer::AddDoubleValue(intptr_t slot_address, } -LargeObjectChunk* Deoptimizer::CreateCode(BailoutType type) { +MemoryChunk* Deoptimizer::CreateCode(BailoutType type) { // We cannot run this if the serializer is enabled because this will // cause us to emit relocation information for the external // references. This is fine because the deoptimizer's code section @@ -1039,12 +1148,15 @@ LargeObjectChunk* Deoptimizer::CreateCode(BailoutType type) { masm.GetCode(&desc); ASSERT(desc.reloc_size == 0); - LargeObjectChunk* chunk = LargeObjectChunk::New(desc.instr_size, EXECUTABLE); + MemoryChunk* chunk = + Isolate::Current()->memory_allocator()->AllocateChunk(desc.instr_size, + EXECUTABLE, + NULL); if (chunk == NULL) { V8::FatalProcessOutOfMemory("Not enough memory for deoptimization table"); } - memcpy(chunk->GetStartAddress(), desc.buffer, desc.instr_size); - CPU::FlushICache(chunk->GetStartAddress(), desc.instr_size); + memcpy(chunk->body(), desc.buffer, desc.instr_size); + CPU::FlushICache(chunk->body(), desc.instr_size); return chunk; } @@ -1106,49 +1218,62 @@ FrameDescription::FrameDescription(uint32_t frame_size, } -unsigned FrameDescription::GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, - int slot_index) { +int FrameDescription::ComputeFixedSize() { + return StandardFrameConstants::kFixedFrameSize + + (ComputeParametersCount() + 1) * kPointerSize; +} + + +unsigned FrameDescription::GetOffsetFromSlotIndex(int slot_index) { if (slot_index >= 0) { // Local or spill slots. Skip the fixed part of the frame // including all arguments. - unsigned base = - GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction()); + unsigned base = GetFrameSize() - ComputeFixedSize(); return base - ((slot_index + 1) * kPointerSize); } else { // Incoming parameter. - unsigned base = GetFrameSize() - - deoptimizer->ComputeIncomingArgumentSize(GetFunction()); + int arg_size = (ComputeParametersCount() + 1) * kPointerSize; + unsigned base = GetFrameSize() - arg_size; return base - ((slot_index + 1) * kPointerSize); } } int FrameDescription::ComputeParametersCount() { - return function_->shared()->formal_parameter_count(); + switch (type_) { + case StackFrame::JAVA_SCRIPT: + return function_->shared()->formal_parameter_count(); + case StackFrame::ARGUMENTS_ADAPTOR: { + // Last slot contains number of incomming arguments as a smi. + // Can't use GetExpression(0) because it would cause infinite recursion. + return reinterpret_cast<Smi*>(*GetFrameSlotPointer(0))->value(); + } + default: + UNREACHABLE(); + return 0; + } } -Object* FrameDescription::GetParameter(Deoptimizer* deoptimizer, int index) { - ASSERT_EQ(Code::FUNCTION, kind_); +Object* FrameDescription::GetParameter(int index) { ASSERT(index >= 0); ASSERT(index < ComputeParametersCount()); // The slot indexes for incoming arguments are negative. - unsigned offset = GetOffsetFromSlotIndex(deoptimizer, - index - ComputeParametersCount()); + unsigned offset = GetOffsetFromSlotIndex(index - ComputeParametersCount()); return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset)); } -unsigned FrameDescription::GetExpressionCount(Deoptimizer* deoptimizer) { - ASSERT_EQ(Code::FUNCTION, kind_); - unsigned size = GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction()); +unsigned FrameDescription::GetExpressionCount() { + ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_); + unsigned size = GetFrameSize() - ComputeFixedSize(); return size / kPointerSize; } -Object* FrameDescription::GetExpression(Deoptimizer* deoptimizer, int index) { - ASSERT_EQ(Code::FUNCTION, kind_); - unsigned offset = GetOffsetFromSlotIndex(deoptimizer, index); +Object* FrameDescription::GetExpression(int index) { + ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_); + unsigned offset = GetOffsetFromSlotIndex(index); return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset)); } @@ -1194,8 +1319,15 @@ Handle<ByteArray> TranslationBuffer::CreateByteArray() { } -void Translation::BeginFrame(int node_id, int literal_id, unsigned height) { - buffer_->Add(FRAME); +void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) { + buffer_->Add(ARGUMENTS_ADAPTOR_FRAME); + buffer_->Add(literal_id); + buffer_->Add(height); +} + + +void Translation::BeginJSFrame(int node_id, int literal_id, unsigned height) { + buffer_->Add(JS_FRAME); buffer_->Add(node_id); buffer_->Add(literal_id); buffer_->Add(height); @@ -1259,7 +1391,6 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { case ARGUMENTS_OBJECT: case DUPLICATE: return 0; - case BEGIN: case REGISTER: case INT32_REGISTER: case DOUBLE_REGISTER: @@ -1268,7 +1399,10 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { case DOUBLE_STACK_SLOT: case LITERAL: return 1; - case FRAME: + case BEGIN: + case ARGUMENTS_ADAPTOR_FRAME: + return 2; + case JS_FRAME: return 3; } UNREACHABLE(); @@ -1282,8 +1416,10 @@ const char* Translation::StringFor(Opcode opcode) { switch (opcode) { case BEGIN: return "BEGIN"; - case FRAME: - return "FRAME"; + case JS_FRAME: + return "JS_FRAME"; + case ARGUMENTS_ADAPTOR_FRAME: + return "ARGUMENTS_ADAPTOR_FRAME"; case REGISTER: return "REGISTER"; case INT32_REGISTER: @@ -1337,7 +1473,8 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, switch (opcode) { case Translation::BEGIN: - case Translation::FRAME: + case Translation::JS_FRAME: + case Translation::ARGUMENTS_ADAPTOR_FRAME: // Peeled off before getting here. break; @@ -1383,9 +1520,27 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, } -void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame, - int inlined_frame_index, - Vector<SlotRef>* args_slots) { +void SlotRef::ComputeSlotsForArguments(Vector<SlotRef>* args_slots, + TranslationIterator* it, + DeoptimizationInputData* data, + JavaScriptFrame* frame) { + // Process the translation commands for the arguments. + + // Skip the translation command for the receiver. + it->Skip(Translation::NumberOfOperandsFor( + static_cast<Translation::Opcode>(it->Next()))); + + // Compute slots for arguments. + for (int i = 0; i < args_slots->length(); ++i) { + (*args_slots)[i] = ComputeSlotForNextArgument(it, data, frame); + } +} + + +Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments( + JavaScriptFrame* frame, + int inlined_jsframe_index, + int formal_parameter_count) { AssertNoAllocation no_gc; int deopt_index = AstNode::kNoNumber; DeoptimizationInputData* data = @@ -1394,51 +1549,78 @@ void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame, data->TranslationIndex(deopt_index)->value()); Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); ASSERT(opcode == Translation::BEGIN); - int frame_count = it.Next(); - USE(frame_count); - ASSERT(frame_count > inlined_frame_index); - int frames_to_skip = inlined_frame_index; + it.Next(); // Drop frame count. + int jsframe_count = it.Next(); + USE(jsframe_count); + ASSERT(jsframe_count > inlined_jsframe_index); + int jsframes_to_skip = inlined_jsframe_index; while (true) { opcode = static_cast<Translation::Opcode>(it.Next()); - // Skip over operands to advance to the next opcode. - it.Skip(Translation::NumberOfOperandsFor(opcode)); - if (opcode == Translation::FRAME) { - if (frames_to_skip == 0) { + if (opcode == Translation::ARGUMENTS_ADAPTOR_FRAME) { + if (jsframes_to_skip == 0) { + ASSERT(Translation::NumberOfOperandsFor(opcode) == 2); + + it.Skip(1); // literal id + int height = it.Next(); + + // We reached the arguments adaptor frame corresponding to the + // inlined function in question. Number of arguments is height - 1. + Vector<SlotRef> args_slots = + Vector<SlotRef>::New(height - 1); // Minus receiver. + ComputeSlotsForArguments(&args_slots, &it, data, frame); + return args_slots; + } + } else if (opcode == Translation::JS_FRAME) { + if (jsframes_to_skip == 0) { + // Skip over operands to advance to the next opcode. + it.Skip(Translation::NumberOfOperandsFor(opcode)); + // We reached the frame corresponding to the inlined function // in question. Process the translation commands for the - // arguments. - // - // Skip the translation command for the receiver. - it.Skip(Translation::NumberOfOperandsFor( - static_cast<Translation::Opcode>(it.Next()))); - // Compute slots for arguments. - for (int i = 0; i < args_slots->length(); ++i) { - (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame); - } - return; + // arguments. Number of arguments is equal to the number of + // format parameter count. + Vector<SlotRef> args_slots = + Vector<SlotRef>::New(formal_parameter_count); + ComputeSlotsForArguments(&args_slots, &it, data, frame); + return args_slots; } - frames_to_skip--; + jsframes_to_skip--; } + + // Skip over operands to advance to the next opcode. + it.Skip(Translation::NumberOfOperandsFor(opcode)); } UNREACHABLE(); + return Vector<SlotRef>(); } #ifdef ENABLE_DEBUGGER_SUPPORT DeoptimizedFrameInfo::DeoptimizedFrameInfo( - Deoptimizer* deoptimizer, int frame_index) { + Deoptimizer* deoptimizer, int frame_index, bool has_arguments_adaptor) { FrameDescription* output_frame = deoptimizer->output_[frame_index]; SetFunction(output_frame->GetFunction()); - expression_count_ = output_frame->GetExpressionCount(deoptimizer); + expression_count_ = output_frame->GetExpressionCount(); + expression_stack_ = new Object*[expression_count_]; + // Get the source position using the unoptimized code. + Address pc = reinterpret_cast<Address>(output_frame->GetPc()); + Code* code = Code::cast(Isolate::Current()->heap()->FindCodeObject(pc)); + source_position_ = code->SourcePosition(pc); + + for (int i = 0; i < expression_count_; i++) { + SetExpression(i, output_frame->GetExpression(i)); + } + + if (has_arguments_adaptor) { + output_frame = deoptimizer->output_[frame_index - 1]; + ASSERT(output_frame->GetFrameType() == StackFrame::ARGUMENTS_ADAPTOR); + } + parameters_count_ = output_frame->ComputeParametersCount(); parameters_ = new Object*[parameters_count_]; for (int i = 0; i < parameters_count_; i++) { - SetParameter(i, output_frame->GetParameter(deoptimizer, i)); - } - expression_stack_ = new Object*[expression_count_]; - for (int i = 0; i < expression_count_; i++) { - SetExpression(i, output_frame->GetExpression(deoptimizer, i)); + SetParameter(i, output_frame->GetParameter(i)); } } @@ -1448,6 +1630,7 @@ DeoptimizedFrameInfo::~DeoptimizedFrameInfo() { delete[] parameters_; } + void DeoptimizedFrameInfo::Iterate(ObjectVisitor* v) { v->VisitPointer(BitCast<Object**>(&function_)); v->VisitPointers(parameters_, parameters_ + parameters_count_); diff --git a/deps/v8/src/deoptimizer.h b/deps/v8/src/deoptimizer.h index 8641261b17..44189d96cd 100644 --- a/deps/v8/src/deoptimizer.h +++ b/deps/v8/src/deoptimizer.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -86,8 +86,8 @@ class DeoptimizerData { #endif private: - LargeObjectChunk* eager_deoptimization_entry_code_; - LargeObjectChunk* lazy_deoptimization_entry_code_; + MemoryChunk* eager_deoptimization_entry_code_; + MemoryChunk* lazy_deoptimization_entry_code_; Deoptimizer* current_; #ifdef ENABLE_DEBUGGER_SUPPORT @@ -119,6 +119,9 @@ class Deoptimizer : public Malloced { int output_count() const { return output_count_; } + // Number of created JS frames. Not all created frames are necessarily JS. + int jsframe_count() const { return jsframe_count_; } + static Deoptimizer* New(JSFunction* function, BailoutType type, unsigned bailout_id, @@ -131,7 +134,7 @@ class Deoptimizer : public Malloced { // The returned object with information on the optimized frame needs to be // freed before another one can be generated. static DeoptimizedFrameInfo* DebuggerInspectableFrame(JavaScriptFrame* frame, - int frame_index, + int jsframe_index, Isolate* isolate); static void DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info, Isolate* isolate); @@ -173,7 +176,8 @@ class Deoptimizer : public Malloced { // Patch stack guard check at instruction before pc_after in // the unoptimized code to unconditionally call replacement_code. - static void PatchStackCheckCodeAt(Address pc_after, + static void PatchStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code); @@ -185,7 +189,8 @@ class Deoptimizer : public Malloced { // Change all patched stack guard checks in the unoptimized code // back to a normal stack guard check. - static void RevertStackCheckCodeAt(Address pc_after, + static void RevertStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code); @@ -194,7 +199,11 @@ class Deoptimizer : public Malloced { void MaterializeHeapNumbers(); #ifdef ENABLE_DEBUGGER_SUPPORT void MaterializeHeapNumbersForDebuggerInspectableFrame( - Address top, uint32_t size, DeoptimizedFrameInfo* info); + Address parameters_top, + uint32_t parameters_size, + Address expressions_top, + uint32_t expressions_size, + DeoptimizedFrameInfo* info); #endif static void ComputeOutputFrames(Deoptimizer* deoptimizer); @@ -211,6 +220,11 @@ class Deoptimizer : public Malloced { return OFFSET_OF(Deoptimizer, output_count_); } static int output_offset() { return OFFSET_OF(Deoptimizer, output_); } + static int frame_alignment_marker_offset() { + return OFFSET_OF(Deoptimizer, frame_alignment_marker_); } + static int has_alignment_padding_offset() { + return OFFSET_OF(Deoptimizer, has_alignment_padding_); + } static int GetDeoptimizedCodeCount(Isolate* isolate); @@ -250,8 +264,14 @@ class Deoptimizer : public Malloced { int count_; }; + int ConvertJSFrameIndexToFrameIndex(int jsframe_index); + private: +#ifdef V8_TARGET_ARCH_MIPS static const int kNumberOfEntries = 4096; +#else + static const int kNumberOfEntries = 16384; +#endif Deoptimizer(Isolate* isolate, JSFunction* function, @@ -264,7 +284,9 @@ class Deoptimizer : public Malloced { void DoComputeOutputFrames(); void DoComputeOsrOutputFrame(); - void DoComputeFrame(TranslationIterator* iterator, int frame_index); + void DoComputeJSFrame(TranslationIterator* iterator, int frame_index); + void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, + int frame_index); void DoTranslateCommand(TranslationIterator* iterator, int frame_index, unsigned output_offset); @@ -285,7 +307,7 @@ class Deoptimizer : public Malloced { void AddDoubleValue(intptr_t slot_address, double value); - static LargeObjectChunk* CreateCode(BailoutType type); + static MemoryChunk* CreateCode(BailoutType type); static void GenerateDeoptimizationEntries( MacroAssembler* masm, int count, BailoutType type); @@ -312,9 +334,15 @@ class Deoptimizer : public Malloced { FrameDescription* input_; // Number of output frames. int output_count_; + // Number of output js frames. + int jsframe_count_; // Array of output frame descriptions. FrameDescription** output_; + // Frames can be dynamically padded on ia32 to align untagged doubles. + Object* frame_alignment_marker_; + intptr_t has_alignment_padding_; + List<HeapNumberMaterializationDescriptor> deferred_heap_numbers_; static const int table_entry_size_; @@ -351,14 +379,27 @@ class FrameDescription { JSFunction* GetFunction() const { return function_; } - unsigned GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, int slot_index); + unsigned GetOffsetFromSlotIndex(int slot_index); intptr_t GetFrameSlot(unsigned offset) { return *GetFrameSlotPointer(offset); } double GetDoubleFrameSlot(unsigned offset) { - return *reinterpret_cast<double*>(GetFrameSlotPointer(offset)); + intptr_t* ptr = GetFrameSlotPointer(offset); +#if V8_TARGET_ARCH_MIPS + // Prevent gcc from using load-double (mips ldc1) on (possibly) + // non-64-bit aligned double. Uses two lwc1 instructions. + union conversion { + double d; + uint32_t u[2]; + } c; + c.u[0] = *reinterpret_cast<uint32_t*>(ptr); + c.u[1] = *(reinterpret_cast<uint32_t*>(ptr) + 1); + return c.d; +#else + return *reinterpret_cast<double*>(ptr); +#endif } void SetFrameSlot(unsigned offset, intptr_t value) { @@ -399,22 +440,20 @@ class FrameDescription { void SetContinuation(intptr_t pc) { continuation_ = pc; } -#ifdef DEBUG - Code::Kind GetKind() const { return kind_; } - void SetKind(Code::Kind kind) { kind_ = kind; } -#endif + StackFrame::Type GetFrameType() const { return type_; } + void SetFrameType(StackFrame::Type type) { type_ = type; } // Get the incoming arguments count. int ComputeParametersCount(); // Get a parameter value for an unoptimized frame. - Object* GetParameter(Deoptimizer* deoptimizer, int index); + Object* GetParameter(int index); // Get the expression stack height for a unoptimized frame. - unsigned GetExpressionCount(Deoptimizer* deoptimizer); + unsigned GetExpressionCount(); // Get the expression stack value for an unoptimized frame. - Object* GetExpression(Deoptimizer* deoptimizer, int index); + Object* GetExpression(int index); static int registers_offset() { return OFFSET_OF(FrameDescription, registers_); @@ -457,6 +496,7 @@ class FrameDescription { intptr_t top_; intptr_t pc_; intptr_t fp_; + StackFrame::Type type_; Smi* state_; #ifdef DEBUG Code::Kind kind_; @@ -475,6 +515,8 @@ class FrameDescription { return reinterpret_cast<intptr_t*>( reinterpret_cast<Address>(this) + frame_content_offset() + offset); } + + int ComputeFixedSize(); }; @@ -517,7 +559,8 @@ class Translation BASE_EMBEDDED { public: enum Opcode { BEGIN, - FRAME, + JS_FRAME, + ARGUMENTS_ADAPTOR_FRAME, REGISTER, INT32_REGISTER, DOUBLE_REGISTER, @@ -532,17 +575,19 @@ class Translation BASE_EMBEDDED { DUPLICATE }; - Translation(TranslationBuffer* buffer, int frame_count) + Translation(TranslationBuffer* buffer, int frame_count, int jsframe_count) : buffer_(buffer), index_(buffer->CurrentIndex()) { buffer_->Add(BEGIN); buffer_->Add(frame_count); + buffer_->Add(jsframe_count); } int index() const { return index_; } // Commands. - void BeginFrame(int node_id, int literal_id, unsigned height); + void BeginJSFrame(int node_id, int literal_id, unsigned height); + void BeginArgumentsAdaptorFrame(int literal_id, unsigned height); void StoreRegister(Register reg); void StoreInt32Register(Register reg); void StoreDoubleRegister(DoubleRegister reg); @@ -632,9 +677,10 @@ class SlotRef BASE_EMBEDDED { } } - static void ComputeSlotMappingForArguments(JavaScriptFrame* frame, - int inlined_frame_index, - Vector<SlotRef>* args_slots); + static Vector<SlotRef> ComputeSlotMappingForArguments( + JavaScriptFrame* frame, + int inlined_frame_index, + int formal_parameter_count); private: Address addr_; @@ -654,6 +700,12 @@ class SlotRef BASE_EMBEDDED { static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator, DeoptimizationInputData* data, JavaScriptFrame* frame); + + static void ComputeSlotsForArguments( + Vector<SlotRef>* args_slots, + TranslationIterator* iterator, + DeoptimizationInputData* data, + JavaScriptFrame* frame); }; @@ -662,9 +714,13 @@ class SlotRef BASE_EMBEDDED { // needs to inspect a frame that is part of an optimized frame. The // internally used FrameDescription objects are not GC safe so for use // by the debugger frame information is copied to an object of this type. +// Represents parameters in unadapted form so their number might mismatch +// formal parameter count. class DeoptimizedFrameInfo : public Malloced { public: - DeoptimizedFrameInfo(Deoptimizer* deoptimizer, int frame_index); + DeoptimizedFrameInfo(Deoptimizer* deoptimizer, + int frame_index, + bool has_arguments_adaptor); virtual ~DeoptimizedFrameInfo(); // GC support. @@ -693,6 +749,10 @@ class DeoptimizedFrameInfo : public Malloced { return expression_stack_[index]; } + int GetSourcePosition() { + return source_position_; + } + private: // Set the frame function. void SetFunction(JSFunction* function) { @@ -716,6 +776,7 @@ class DeoptimizedFrameInfo : public Malloced { int expression_count_; Object** parameters_; Object** expression_stack_; + int source_position_; friend class Deoptimizer; }; diff --git a/deps/v8/src/disassembler.cc b/deps/v8/src/disassembler.cc index 1e67b4cb66..e3b40ab93f 100644 --- a/deps/v8/src/disassembler.cc +++ b/deps/v8/src/disassembler.cc @@ -200,7 +200,7 @@ static int DecodeIt(FILE* f, // Print all the reloc info for this instruction which are not comments. for (int i = 0; i < pcs.length(); i++) { // Put together the reloc info - RelocInfo relocinfo(pcs[i], rmodes[i], datas[i]); + RelocInfo relocinfo(pcs[i], rmodes[i], datas[i], NULL); // Indent the printing of the reloc info. if (i == 0) { diff --git a/deps/v8/src/double.h b/deps/v8/src/double.h index 65eded9989..16a3245e9a 100644 --- a/deps/v8/src/double.h +++ b/deps/v8/src/double.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,8 +34,8 @@ namespace v8 { namespace internal { // We assume that doubles and uint64_t have the same endianness. -static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } -static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } +inline uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } +inline double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } // Helper functions for doubles. class Double { diff --git a/deps/v8/src/dtoa.h b/deps/v8/src/dtoa.h index b3e79afa48..948a079191 100644 --- a/deps/v8/src/dtoa.h +++ b/deps/v8/src/dtoa.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -47,9 +47,9 @@ enum DtoaMode { // The maximal length of digits a double can have in base 10. // Note that DoubleToAscii null-terminates its input. So the given buffer should // be at least kBase10MaximalLength + 1 characters long. -static const int kBase10MaximalLength = 17; +const int kBase10MaximalLength = 17; -// Converts the given double 'v' to ascii. +// Converts the given double 'v' to ASCII. // The result should be interpreted as buffer * 10^(point-length). // // The output depends on the given mode: diff --git a/deps/v8/src/elements.cc b/deps/v8/src/elements.cc index 0454644617..e54ec62691 100644 --- a/deps/v8/src/elements.cc +++ b/deps/v8/src/elements.cc @@ -31,6 +31,30 @@ #include "elements.h" #include "utils.h" + +// Each concrete ElementsAccessor can handle exactly one ElementsKind, +// several abstract ElementsAccessor classes are used to allow sharing +// common code. +// +// Inheritance hierarchy: +// - ElementsAccessorBase (abstract) +// - FastElementsAccessor (abstract) +// - FastObjectElementsAccessor +// - FastDoubleElementsAccessor +// - ExternalElementsAccessor (abstract) +// - ExternalByteElementsAccessor +// - ExternalUnsignedByteElementsAccessor +// - ExternalShortElementsAccessor +// - ExternalUnsignedShortElementsAccessor +// - ExternalIntElementsAccessor +// - ExternalUnsignedIntElementsAccessor +// - ExternalFloatElementsAccessor +// - ExternalDoubleElementsAccessor +// - PixelElementsAccessor +// - DictionaryElementsAccessor +// - NonStrictArgumentsElementsAccessor + + namespace v8 { namespace internal { @@ -38,7 +62,7 @@ namespace internal { ElementsAccessor** ElementsAccessor::elements_accessors_; -bool HasKey(FixedArray* array, Object* key) { +static bool HasKey(FixedArray* array, Object* key) { int len0 = array->length(); for (int i = 0; i < len0; i++) { Object* element = array->get(i); @@ -52,6 +76,14 @@ bool HasKey(FixedArray* array, Object* key) { } +static Failure* ThrowArrayLengthRangeError(Heap* heap) { + HandleScope scope(heap->isolate()); + return heap->isolate()->Throw( + *heap->isolate()->factory()->NewRangeError("invalid_array_length", + HandleVector<Object>(NULL, 0))); +} + + // Base class for element handler implementations. Contains the // the common logic for objects with different ElementsKinds. // Subclasses must specialize method for which the element @@ -91,6 +123,33 @@ class ElementsAccessorBase : public ElementsAccessor { return backing_store->GetHeap()->the_hole_value(); } + virtual MaybeObject* SetLength(JSObject* obj, + Object* length) { + ASSERT(obj->IsJSArray()); + return ElementsAccessorSubclass::SetLength( + BackingStoreClass::cast(obj->elements()), obj, length); + } + + static MaybeObject* SetLength(BackingStoreClass* backing_store, + JSObject* obj, + Object* length); + + virtual MaybeObject* SetCapacityAndLength(JSArray* array, + int capacity, + int length) { + return ElementsAccessorSubclass::SetFastElementsCapacityAndLength( + array, + capacity, + length); + } + + static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, + int capacity, + int length) { + UNIMPLEMENTED(); + return obj; + } + virtual MaybeObject* Delete(JSObject* obj, uint32_t key, JSReceiver::DeleteMode mode) = 0; @@ -222,12 +281,76 @@ class ElementsAccessorBase : public ElementsAccessor { }; +// Super class for all fast element arrays. +template<typename FastElementsAccessorSubclass, + typename BackingStore, + int ElementSize> class FastElementsAccessor - : public ElementsAccessorBase<FastElementsAccessor, FixedArray> { + : public ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore> { + protected: + friend class ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore>; + + // Adjusts the length of the fast backing store or returns the new length or + // undefined in case conversion to a slow backing store should be performed. + static MaybeObject* SetLengthWithoutNormalize(BackingStore* backing_store, + JSArray* array, + Object* length_object, + uint32_t length) { + uint32_t old_capacity = backing_store->length(); + + // Check whether the backing store should be shrunk. + if (length <= old_capacity) { + if (array->HasFastTypeElements()) { + MaybeObject* maybe_obj = array->EnsureWritableFastElements(); + if (!maybe_obj->To(&backing_store)) return maybe_obj; + } + if (2 * length <= old_capacity) { + // If more than half the elements won't be used, trim the array. + if (length == 0) { + array->initialize_elements(); + } else { + backing_store->set_length(length); + Address filler_start = backing_store->address() + + BackingStore::OffsetOfElementAt(length); + int filler_size = (old_capacity - length) * ElementSize; + array->GetHeap()->CreateFillerObjectAt(filler_start, filler_size); + } + } else { + // Otherwise, fill the unused tail with holes. + int old_length = FastD2I(array->length()->Number()); + for (int i = length; i < old_length; i++) { + backing_store->set_the_hole(i); + } + } + return length_object; + } + + // Check whether the backing store should be expanded. + uint32_t min = JSObject::NewElementsCapacity(old_capacity); + uint32_t new_capacity = length > min ? length : min; + if (!array->ShouldConvertToSlowElements(new_capacity)) { + MaybeObject* result = FastElementsAccessorSubclass:: + SetFastElementsCapacityAndLength(array, new_capacity, length); + if (result->IsFailure()) return result; + return length_object; + } + + // Request conversion to slow elements. + return array->GetHeap()->undefined_value(); + } +}; + + +class FastObjectElementsAccessor + : public FastElementsAccessor<FastObjectElementsAccessor, + FixedArray, + kPointerSize> { public: static MaybeObject* DeleteCommon(JSObject* obj, uint32_t key) { - ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements()); + ASSERT(obj->HasFastElements() || + obj->HasFastSmiOnlyElements() || + obj->HasFastArgumentsElements()); Heap* heap = obj->GetHeap(); FixedArray* backing_store = FixedArray::cast(obj->elements()); if (backing_store->map() == heap->non_strict_arguments_elements_map()) { @@ -269,7 +392,23 @@ class FastElementsAccessor return heap->true_value(); } + static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, + uint32_t capacity, + uint32_t length) { + JSObject::SetFastElementsCapacityMode set_capacity_mode = + obj->HasFastSmiOnlyElements() + ? JSObject::kAllowSmiOnlyElements + : JSObject::kDontAllowSmiOnlyElements; + return obj->SetFastElementsCapacityAndLength(capacity, + length, + set_capacity_mode); + } + protected: + friend class FastElementsAccessor<FastObjectElementsAccessor, + FixedArray, + kPointerSize>; + virtual MaybeObject* Delete(JSObject* obj, uint32_t key, JSReceiver::DeleteMode mode) { @@ -279,11 +418,21 @@ class FastElementsAccessor class FastDoubleElementsAccessor - : public ElementsAccessorBase<FastDoubleElementsAccessor, - FixedDoubleArray> { + : public FastElementsAccessor<FastDoubleElementsAccessor, + FixedDoubleArray, + kDoubleSize> { + static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj, + uint32_t capacity, + uint32_t length) { + return obj->SetFastDoubleElementsCapacityAndLength(capacity, length); + } + protected: friend class ElementsAccessorBase<FastDoubleElementsAccessor, FixedDoubleArray>; + friend class FastElementsAccessor<FastDoubleElementsAccessor, + FixedDoubleArray, + kDoubleSize>; virtual MaybeObject* Delete(JSObject* obj, uint32_t key, @@ -327,6 +476,14 @@ class ExternalElementsAccessor } } + static MaybeObject* SetLength(ExternalArray* backing_store, + JSObject* obj, + Object* length) { + // External arrays do not support changing their length. + UNREACHABLE(); + return obj; + } + virtual MaybeObject* Delete(JSObject* obj, uint32_t key, JSReceiver::DeleteMode mode) { @@ -394,6 +551,63 @@ class DictionaryElementsAccessor : public ElementsAccessorBase<DictionaryElementsAccessor, SeededNumberDictionary> { public: + // Adjusts the length of the dictionary backing store and returns the new + // length according to ES5 section 15.4.5.2 behavior. + static MaybeObject* SetLengthWithoutNormalize(SeededNumberDictionary* dict, + JSArray* array, + Object* length_object, + uint32_t length) { + if (length == 0) { + // If the length of a slow array is reset to zero, we clear + // the array and flush backing storage. This has the added + // benefit that the array returns to fast mode. + Object* obj; + MaybeObject* maybe_obj = array->ResetElements(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } else { + uint32_t new_length = length; + uint32_t old_length = static_cast<uint32_t>(array->length()->Number()); + if (new_length < old_length) { + // Find last non-deletable element in range of elements to be + // deleted and adjust range accordingly. + Heap* heap = array->GetHeap(); + int capacity = dict->Capacity(); + for (int i = 0; i < capacity; i++) { + Object* key = dict->KeyAt(i); + if (key->IsNumber()) { + uint32_t number = static_cast<uint32_t>(key->Number()); + if (new_length <= number && number < old_length) { + PropertyDetails details = dict->DetailsAt(i); + if (details.IsDontDelete()) new_length = number + 1; + } + } + } + if (new_length != length) { + MaybeObject* maybe_object = heap->NumberFromUint32(new_length); + if (!maybe_object->To(&length_object)) return maybe_object; + } + + // Remove elements that should be deleted. + int removed_entries = 0; + Object* the_hole_value = heap->the_hole_value(); + for (int i = 0; i < capacity; i++) { + Object* key = dict->KeyAt(i); + if (key->IsNumber()) { + uint32_t number = static_cast<uint32_t>(key->Number()); + if (new_length <= number && number < old_length) { + dict->SetEntry(i, the_hole_value, the_hole_value); + removed_entries++; + } + } + } + + // Update the number of elements. + dict->ElementsRemoved(removed_entries); + } + } + return length_object; + } + static MaybeObject* DeleteCommon(JSObject* obj, uint32_t key, JSReceiver::DeleteMode mode) { @@ -504,9 +718,17 @@ class NonStrictArgumentsElementsAccessor } } + static MaybeObject* SetLength(FixedArray* parameter_map, + JSObject* obj, + Object* length) { + // TODO(mstarzinger): This was never implemented but will be used once we + // correctly implement [[DefineOwnProperty]] on arrays. + UNIMPLEMENTED(); + return obj; + } + virtual MaybeObject* Delete(JSObject* obj, - uint32_t key - , + uint32_t key, JSReceiver::DeleteMode mode) { FixedArray* parameter_map = FixedArray::cast(obj->elements()); Object* probe = GetParameterMapArg(parameter_map, key); @@ -520,7 +742,7 @@ class NonStrictArgumentsElementsAccessor if (arguments->IsDictionary()) { return DictionaryElementsAccessor::DeleteCommon(obj, key, mode); } else { - return FastElementsAccessor::DeleteCommon(obj, key); + return FastObjectElementsAccessor::DeleteCommon(obj, key); } } return obj->GetHeap()->true_value(); @@ -596,40 +818,108 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { void ElementsAccessor::InitializeOncePerProcess() { + // First argument in list is the accessor class, the second argument is can + // be any arbitrary unique identifier, in this case chosen to be the + // corresponding enum. Use the fast element handler for smi-only arrays. + // The implementation is currently identical. Note that the order must match + // that of the ElementsKind enum for the |accessor_array[]| below to work. +#define ELEMENTS_LIST(V) \ + V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS) \ + V(FastObjectElementsAccessor, FAST_ELEMENTS) \ + V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS) \ + V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS) \ + V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS) \ + V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS) \ + V(ExternalUnsignedByteElementsAccessor, EXTERNAL_UNSIGNED_BYTE_ELEMENTS) \ + V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS) \ + V(ExternalUnsignedShortElementsAccessor, EXTERNAL_UNSIGNED_SHORT_ELEMENTS) \ + V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS) \ + V(ExternalUnsignedIntElementsAccessor, EXTERNAL_UNSIGNED_INT_ELEMENTS) \ + V(ExternalFloatElementsAccessor, EXTERNAL_FLOAT_ELEMENTS) \ + V(ExternalDoubleElementsAccessor, EXTERNAL_DOUBLE_ELEMENTS) \ + V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS) + static struct ConcreteElementsAccessors { - FastElementsAccessor fast_elements_handler; - FastDoubleElementsAccessor fast_double_elements_handler; - DictionaryElementsAccessor dictionary_elements_handler; - NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler; - ExternalByteElementsAccessor byte_elements_handler; - ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler; - ExternalShortElementsAccessor short_elements_handler; - ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler; - ExternalIntElementsAccessor int_elements_handler; - ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler; - ExternalFloatElementsAccessor float_elements_handler; - ExternalDoubleElementsAccessor double_elements_handler; - PixelElementsAccessor pixel_elements_handler; - } element_accessors; +#define ACCESSOR_STRUCT(Class, Name) Class* Name##_handler; + ELEMENTS_LIST(ACCESSOR_STRUCT) +#undef ACCESSOR_STRUCT + } element_accessors = { +#define ACCESSOR_INIT(Class, Name) new Class(), + ELEMENTS_LIST(ACCESSOR_INIT) +#undef ACCESSOR_INIT + }; static ElementsAccessor* accessor_array[] = { - &element_accessors.fast_elements_handler, - &element_accessors.fast_double_elements_handler, - &element_accessors.dictionary_elements_handler, - &element_accessors.non_strict_arguments_elements_handler, - &element_accessors.byte_elements_handler, - &element_accessors.unsigned_byte_elements_handler, - &element_accessors.short_elements_handler, - &element_accessors.unsigned_short_elements_handler, - &element_accessors.int_elements_handler, - &element_accessors.unsigned_int_elements_handler, - &element_accessors.float_elements_handler, - &element_accessors.double_elements_handler, - &element_accessors.pixel_elements_handler +#define ACCESSOR_ARRAY(Class, Name) element_accessors.Name##_handler, + ELEMENTS_LIST(ACCESSOR_ARRAY) +#undef ACCESSOR_ARRAY }; +#undef ELEMENTS_LIST + + STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) == + kElementsKindCount); + elements_accessors_ = accessor_array; } +template <typename ElementsAccessorSubclass, typename BackingStoreClass> +MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass, BackingStoreClass>:: + SetLength(BackingStoreClass* backing_store, + JSObject* obj, + Object* length) { + JSArray* array = JSArray::cast(obj); + + // Fast case: The new length fits into a Smi. + MaybeObject* maybe_smi_length = length->ToSmi(); + Object* smi_length = Smi::FromInt(0); + if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) { + const int value = Smi::cast(smi_length)->value(); + if (value >= 0) { + Object* new_length; + MaybeObject* result = ElementsAccessorSubclass:: + SetLengthWithoutNormalize(backing_store, array, smi_length, value); + if (!result->ToObject(&new_length)) return result; + ASSERT(new_length->IsSmi() || new_length->IsUndefined()); + if (new_length->IsSmi()) { + array->set_length(Smi::cast(new_length)); + return array; + } + } else { + return ThrowArrayLengthRangeError(array->GetHeap()); + } + } + + // Slow case: The new length does not fit into a Smi or conversion + // to slow elements is needed for other reasons. + if (length->IsNumber()) { + uint32_t value; + if (length->ToArrayIndex(&value)) { + SeededNumberDictionary* dictionary; + MaybeObject* maybe_object = array->NormalizeElements(); + if (!maybe_object->To(&dictionary)) return maybe_object; + Object* new_length; + MaybeObject* result = DictionaryElementsAccessor:: + SetLengthWithoutNormalize(dictionary, array, length, value); + if (!result->ToObject(&new_length)) return result; + ASSERT(new_length->IsNumber()); + array->set_length(new_length); + return array; + } else { + return ThrowArrayLengthRangeError(array->GetHeap()); + } + } + + // Fall-back case: The new length is not a number so make the array + // size one and set only element to length. + FixedArray* new_backing_store; + MaybeObject* maybe_obj = array->GetHeap()->AllocateFixedArray(1); + if (!maybe_obj->To(&new_backing_store)) return maybe_obj; + new_backing_store->set(0, length); + array->SetContent(new_backing_store); + return array; +} + + } } // namespace v8::internal diff --git a/deps/v8/src/elements.h b/deps/v8/src/elements.h index 851c8c3d97..a2a184d52c 100644 --- a/deps/v8/src/elements.h +++ b/deps/v8/src/elements.h @@ -44,6 +44,24 @@ class ElementsAccessor { JSObject* holder, Object* receiver) = 0; + // Modifies the length data property as specified for JSArrays and resizes the + // underlying backing store accordingly. The method honors the semantics of + // changing array sizes as defined in EcmaScript 5.1 15.4.5.2, i.e. array that + // have non-deletable elements can only be shrunk to the size of highest + // element that is non-deletable. + virtual MaybeObject* SetLength(JSObject* holder, + Object* new_length) = 0; + + // Modifies both the length and capacity of a JSArray, resizing the underlying + // backing store as necessary. This method does NOT honor the semantics of + // EcmaScript 5.1 15.4.5.2, arrays can be shrunk beyond non-deletable + // elements. This method should only be called for array expansion OR by + // runtime JavaScript code that use InternalArrays and don't care about + // EcmaScript 5.1 semantics. + virtual MaybeObject* SetCapacityAndLength(JSArray* array, + int capacity, + int length) = 0; + virtual MaybeObject* Delete(JSObject* holder, uint32_t key, JSReceiver::DeleteMode mode) = 0; diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index f36d4e4911..71e8ea34a1 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -33,6 +33,7 @@ #include "bootstrapper.h" #include "codegen.h" #include "debug.h" +#include "isolate-inl.h" #include "runtime-profiler.h" #include "simulator.h" #include "v8threads.h" @@ -65,13 +66,13 @@ void StackGuard::reset_limits(const ExecutionAccess& lock) { } -static Handle<Object> Invoke(bool construct, - Handle<JSFunction> func, +static Handle<Object> Invoke(bool is_construct, + Handle<JSFunction> function, Handle<Object> receiver, int argc, - Object*** args, + Handle<Object> args[], bool* has_pending_exception) { - Isolate* isolate = func->GetIsolate(); + Isolate* isolate = function->GetIsolate(); // Entering JavaScript. VMState state(isolate, JS); @@ -79,21 +80,15 @@ static Handle<Object> Invoke(bool construct, // Placeholder for return value. MaybeObject* value = reinterpret_cast<Object*>(kZapValue); - typedef Object* (*JSEntryFunction)( - byte* entry, - Object* function, - Object* receiver, - int argc, - Object*** args); - - Handle<Code> code; - if (construct) { - JSConstructEntryStub stub; - code = stub.GetCode(); - } else { - JSEntryStub stub; - code = stub.GetCode(); - } + typedef Object* (*JSEntryFunction)(byte* entry, + Object* function, + Object* receiver, + int argc, + Object*** args); + + Handle<Code> code = is_construct + ? isolate->factory()->js_construct_entry_code() + : isolate->factory()->js_entry_code(); // Convert calls on global objects to be calls on the global // receiver instead to avoid having a 'this' pointer which refers @@ -105,21 +100,22 @@ static Handle<Object> Invoke(bool construct, // Make sure that the global object of the context we're about to // make the current one is indeed a global object. - ASSERT(func->context()->global()->IsGlobalObject()); + ASSERT(function->context()->global()->IsGlobalObject()); { // Save and restore context around invocation and block the // allocation of handles without explicit handle scopes. SaveContext save(isolate); NoHandleAllocation na; - JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry()); + JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry()); // Call the function through the right JS entry stub. - byte* entry_address = func->code()->entry(); - JSFunction* function = *func; - Object* receiver_pointer = *receiver; - value = CALL_GENERATED_CODE(entry, entry_address, function, - receiver_pointer, argc, args); + byte* function_entry = function->code()->entry(); + JSFunction* func = *function; + Object* recv = *receiver; + Object*** argv = reinterpret_cast<Object***>(args); + value = + CALL_GENERATED_CODE(stub_entry, function_entry, func, recv, argc, argv); } #ifdef DEBUG @@ -148,9 +144,11 @@ static Handle<Object> Invoke(bool construct, Handle<Object> Execution::Call(Handle<Object> callable, Handle<Object> receiver, int argc, - Object*** args, + Handle<Object> argv[], bool* pending_exception, bool convert_receiver) { + *pending_exception = false; + if (!callable->IsJSFunction()) { callable = TryGetFunctionDelegate(callable, pending_exception); if (*pending_exception) return callable; @@ -159,7 +157,7 @@ Handle<Object> Execution::Call(Handle<Object> callable, // In non-strict mode, convert receiver. if (convert_receiver && !receiver->IsJSReceiver() && - !func->shared()->native() && !func->shared()->strict_mode()) { + !func->shared()->native() && func->shared()->is_classic_mode()) { if (receiver->IsUndefined() || receiver->IsNull()) { Object* global = func->context()->global()->global_receiver(); // Under some circumstances, 'global' can be the JSBuiltinsObject @@ -172,13 +170,15 @@ Handle<Object> Execution::Call(Handle<Object> callable, if (*pending_exception) return callable; } - return Invoke(false, func, receiver, argc, args, pending_exception); + return Invoke(false, func, receiver, argc, argv, pending_exception); } -Handle<Object> Execution::New(Handle<JSFunction> func, int argc, - Object*** args, bool* pending_exception) { - return Invoke(true, func, Isolate::Current()->global(), argc, args, +Handle<Object> Execution::New(Handle<JSFunction> func, + int argc, + Handle<Object> argv[], + bool* pending_exception) { + return Invoke(true, func, Isolate::Current()->global(), argc, argv, pending_exception); } @@ -186,7 +186,7 @@ Handle<Object> Execution::New(Handle<JSFunction> func, int argc, Handle<Object> Execution::TryCall(Handle<JSFunction> func, Handle<Object> receiver, int argc, - Object*** args, + Handle<Object> args[], bool* caught_exception) { // Enter a try-block while executing the JavaScript code. To avoid // duplicate error printing it must be non-verbose. Also, to avoid @@ -195,6 +195,7 @@ Handle<Object> Execution::TryCall(Handle<JSFunction> func, v8::TryCatch catcher; catcher.SetVerbose(false); catcher.SetCaptureMessage(false); + *caught_exception = false; Handle<Object> result = Invoke(false, func, receiver, argc, args, caught_exception); @@ -355,7 +356,7 @@ void StackGuard::EnableInterrupts() { void StackGuard::SetStackLimit(uintptr_t limit) { ExecutionAccess access(isolate_); - // If the current limits are special (eg due to a pending interrupt) then + // If the current limits are special (e.g. due to a pending interrupt) then // leave them alone. uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit); if (thread_local_.jslimit_ == thread_local_.real_jslimit_) { @@ -377,7 +378,7 @@ void StackGuard::DisableInterrupts() { bool StackGuard::IsInterrupted() { ExecutionAccess access(isolate_); - return thread_local_.interrupt_flags_ & INTERRUPT; + return (thread_local_.interrupt_flags_ & INTERRUPT) != 0; } @@ -403,7 +404,7 @@ void StackGuard::Preempt() { bool StackGuard::IsTerminateExecution() { ExecutionAccess access(isolate_); - return thread_local_.interrupt_flags_ & TERMINATE; + return (thread_local_.interrupt_flags_ & TERMINATE) != 0; } @@ -416,7 +417,7 @@ void StackGuard::TerminateExecution() { bool StackGuard::IsRuntimeProfilerTick() { ExecutionAccess access(isolate_); - return thread_local_.interrupt_flags_ & RUNTIME_PROFILER_TICK; + return (thread_local_.interrupt_flags_ & RUNTIME_PROFILER_TICK) != 0; } @@ -433,6 +434,22 @@ void StackGuard::RequestRuntimeProfilerTick() { } +bool StackGuard::IsGCRequest() { + ExecutionAccess access(isolate_); + return (thread_local_.interrupt_flags_ & GC_REQUEST) != 0; +} + + +void StackGuard::RequestGC() { + ExecutionAccess access(isolate_); + thread_local_.interrupt_flags_ |= GC_REQUEST; + if (thread_local_.postpone_interrupts_nesting_ == 0) { + thread_local_.jslimit_ = thread_local_.climit_ = kInterruptLimit; + isolate_->heap()->SetStackLimits(); + } +} + + #ifdef ENABLE_DEBUGGER_SUPPORT bool StackGuard::IsDebugBreak() { ExecutionAccess access(isolate_); @@ -555,14 +572,15 @@ void StackGuard::InitThread(const ExecutionAccess& lock) { // --- C a l l s t o n a t i v e s --- -#define RETURN_NATIVE_CALL(name, argc, argv, has_pending_exception) \ - do { \ - Isolate* isolate = Isolate::Current(); \ - Object** args[argc] = argv; \ - ASSERT(has_pending_exception != NULL); \ - return Call(isolate->name##_fun(), \ - isolate->js_builtins_object(), argc, args, \ - has_pending_exception); \ +#define RETURN_NATIVE_CALL(name, args, has_pending_exception) \ + do { \ + Isolate* isolate = Isolate::Current(); \ + Handle<Object> argv[] = args; \ + ASSERT(has_pending_exception != NULL); \ + return Call(isolate->name##_fun(), \ + isolate->js_builtins_object(), \ + ARRAY_SIZE(argv), argv, \ + has_pending_exception); \ } while (false) @@ -583,44 +601,44 @@ Handle<Object> Execution::ToBoolean(Handle<Object> obj) { Handle<Object> Execution::ToNumber(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_number, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_number, { obj }, exc); } Handle<Object> Execution::ToString(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_string, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_string, { obj }, exc); } Handle<Object> Execution::ToDetailString(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_detail_string, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_detail_string, { obj }, exc); } Handle<Object> Execution::ToObject(Handle<Object> obj, bool* exc) { if (obj->IsSpecObject()) return obj; - RETURN_NATIVE_CALL(to_object, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_object, { obj }, exc); } Handle<Object> Execution::ToInteger(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_integer, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_integer, { obj }, exc); } Handle<Object> Execution::ToUint32(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_uint32, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_uint32, { obj }, exc); } Handle<Object> Execution::ToInt32(Handle<Object> obj, bool* exc) { - RETURN_NATIVE_CALL(to_int32, 1, { obj.location() }, exc); + RETURN_NATIVE_CALL(to_int32, { obj }, exc); } Handle<Object> Execution::NewDate(double time, bool* exc) { Handle<Object> time_obj = FACTORY->NewNumber(time); - RETURN_NATIVE_CALL(create_date, 1, { time_obj.location() }, exc); + RETURN_NATIVE_CALL(create_date, { time_obj }, exc); } @@ -657,7 +675,7 @@ Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) { bool caught_exception; Handle<Object> index_object = factory->NewNumberFromInt(int_index); - Object** index_arg[] = { index_object.location() }; + Handle<Object> index_arg[] = { index_object }; Handle<Object> result = TryCall(Handle<JSFunction>::cast(char_at), string, ARRAY_SIZE(index_arg), @@ -671,7 +689,8 @@ Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) { Handle<JSFunction> Execution::InstantiateFunction( - Handle<FunctionTemplateInfo> data, bool* exc) { + Handle<FunctionTemplateInfo> data, + bool* exc) { Isolate* isolate = data->GetIsolate(); // Fast case: see if the function has already been instantiated int serial_number = Smi::cast(data->serial_number())->value(); @@ -680,10 +699,12 @@ Handle<JSFunction> Execution::InstantiateFunction( GetElementNoExceptionThrown(serial_number); if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm)); // The function has not yet been instantiated in this context; do it. - Object** args[1] = { Handle<Object>::cast(data).location() }; - Handle<Object> result = - Call(isolate->instantiate_fun(), - isolate->js_builtins_object(), 1, args, exc); + Handle<Object> args[] = { data }; + Handle<Object> result = Call(isolate->instantiate_fun(), + isolate->js_builtins_object(), + ARRAY_SIZE(args), + args, + exc); if (*exc) return Handle<JSFunction>::null(); return Handle<JSFunction>::cast(result); } @@ -710,10 +731,12 @@ Handle<JSObject> Execution::InstantiateObject(Handle<ObjectTemplateInfo> data, ASSERT(!*exc); return Handle<JSObject>(JSObject::cast(result)); } else { - Object** args[1] = { Handle<Object>::cast(data).location() }; - Handle<Object> result = - Call(isolate->instantiate_fun(), - isolate->js_builtins_object(), 1, args, exc); + Handle<Object> args[] = { data }; + Handle<Object> result = Call(isolate->instantiate_fun(), + isolate->js_builtins_object(), + ARRAY_SIZE(args), + args, + exc); if (*exc) return Handle<JSObject>::null(); return Handle<JSObject>::cast(result); } @@ -724,9 +747,12 @@ void Execution::ConfigureInstance(Handle<Object> instance, Handle<Object> instance_template, bool* exc) { Isolate* isolate = Isolate::Current(); - Object** args[2] = { instance.location(), instance_template.location() }; + Handle<Object> args[] = { instance, instance_template }; Execution::Call(isolate->configure_instance_fun(), - isolate->js_builtins_object(), 2, args, exc); + isolate->js_builtins_object(), + ARRAY_SIZE(args), + args, + exc); } @@ -735,16 +761,13 @@ Handle<String> Execution::GetStackTraceLine(Handle<Object> recv, Handle<Object> pos, Handle<Object> is_global) { Isolate* isolate = fun->GetIsolate(); - const int argc = 4; - Object** args[argc] = { recv.location(), - Handle<Object>::cast(fun).location(), - pos.location(), - is_global.location() }; - bool caught_exception = false; - Handle<Object> result = - TryCall(isolate->get_stack_trace_line_fun(), - isolate->js_builtins_object(), argc, args, - &caught_exception); + Handle<Object> args[] = { recv, fun, pos, is_global }; + bool caught_exception; + Handle<Object> result = TryCall(isolate->get_stack_trace_line_fun(), + isolate->js_builtins_object(), + ARRAY_SIZE(args), + args, + &caught_exception); if (caught_exception || !result->IsString()) { return isolate->factory()->empty_symbol(); } @@ -822,13 +845,13 @@ Object* Execution::DebugBreakHelper() { // Clear the debug break request flag. isolate->stack_guard()->Continue(DEBUGBREAK); - ProcessDebugMesssages(debug_command_only); + ProcessDebugMessages(debug_command_only); // Return to continue execution. return isolate->heap()->undefined_value(); } -void Execution::ProcessDebugMesssages(bool debug_command_only) { +void Execution::ProcessDebugMessages(bool debug_command_only) { Isolate* isolate = Isolate::Current(); // Clear the debug command request flag. isolate->stack_guard()->Continue(DEBUGCOMMAND); @@ -852,6 +875,12 @@ void Execution::ProcessDebugMesssages(bool debug_command_only) { MaybeObject* Execution::HandleStackGuardInterrupt() { Isolate* isolate = Isolate::Current(); StackGuard* stack_guard = isolate->stack_guard(); + + if (stack_guard->IsGCRequest()) { + isolate->heap()->CollectAllGarbage(false, "StackGuard GC request"); + stack_guard->Continue(GC_REQUEST); + } + isolate->counters()->stack_interrupts()->Increment(); if (stack_guard->IsRuntimeProfilerTick()) { isolate->counters()->runtime_profiler_ticks()->Increment(); diff --git a/deps/v8/src/execution.h b/deps/v8/src/execution.h index 5cd7141fc2..014736ee88 100644 --- a/deps/v8/src/execution.h +++ b/deps/v8/src/execution.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -41,7 +41,8 @@ enum InterruptFlag { DEBUGCOMMAND = 1 << 2, PREEMPT = 1 << 3, TERMINATE = 1 << 4, - RUNTIME_PROFILER_TICK = 1 << 5 + RUNTIME_PROFILER_TICK = 1 << 5, + GC_REQUEST = 1 << 6 }; class Execution : public AllStatic { @@ -60,7 +61,7 @@ class Execution : public AllStatic { static Handle<Object> Call(Handle<Object> callable, Handle<Object> receiver, int argc, - Object*** args, + Handle<Object> argv[], bool* pending_exception, bool convert_receiver = false); @@ -73,7 +74,7 @@ class Execution : public AllStatic { // static Handle<Object> New(Handle<JSFunction> func, int argc, - Object*** args, + Handle<Object> argv[], bool* pending_exception); // Call a function, just like Call(), but make sure to silently catch @@ -83,7 +84,7 @@ class Execution : public AllStatic { static Handle<Object> TryCall(Handle<JSFunction> func, Handle<Object> receiver, int argc, - Object*** args, + Handle<Object> argv[], bool* caught_exception); // ECMA-262 9.2 @@ -135,7 +136,7 @@ class Execution : public AllStatic { Handle<Object> is_global); #ifdef ENABLE_DEBUGGER_SUPPORT static Object* DebugBreakHelper(); - static void ProcessDebugMesssages(bool debug_command_only); + static void ProcessDebugMessages(bool debug_command_only); #endif // If the stack guard is triggered, but it is not an actual @@ -196,6 +197,8 @@ class StackGuard { bool IsDebugCommand(); void DebugCommand(); #endif + bool IsGCRequest(); + void RequestGC(); void Continue(InterruptFlag after_what); // This provides an asynchronous read of the stack limits for the current diff --git a/deps/v8/src/extensions/experimental/break-iterator.cc b/deps/v8/src/extensions/experimental/break-iterator.cc deleted file mode 100644 index e695a3e978..0000000000 --- a/deps/v8/src/extensions/experimental/break-iterator.cc +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/break-iterator.h" - -#include <string.h> - -#include "unicode/brkiter.h" -#include "unicode/locid.h" -#include "unicode/rbbi.h" - -namespace v8 { -namespace internal { - -v8::Persistent<v8::FunctionTemplate> BreakIterator::break_iterator_template_; - -icu::BreakIterator* BreakIterator::UnpackBreakIterator( - v8::Handle<v8::Object> obj) { - if (break_iterator_template_->HasInstance(obj)) { - return static_cast<icu::BreakIterator*>( - obj->GetPointerFromInternalField(0)); - } - - return NULL; -} - -icu::UnicodeString* BreakIterator::ResetAdoptedText( - v8::Handle<v8::Object> obj, v8::Handle<v8::Value> value) { - // Get the previous value from the internal field. - icu::UnicodeString* text = static_cast<icu::UnicodeString*>( - obj->GetPointerFromInternalField(1)); - delete text; - - // Assign new value to the internal pointer. - v8::String::Value text_value(value); - text = new icu::UnicodeString( - reinterpret_cast<const UChar*>(*text_value), text_value.length()); - obj->SetPointerInInternalField(1, text); - - // Return new unicode string pointer. - return text; -} - -void BreakIterator::DeleteBreakIterator(v8::Persistent<v8::Value> object, - void* param) { - v8::Persistent<v8::Object> persistent_object = - v8::Persistent<v8::Object>::Cast(object); - - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a break iterator. - delete UnpackBreakIterator(persistent_object); - - delete static_cast<icu::UnicodeString*>( - persistent_object->GetPointerFromInternalField(1)); - - // Then dispose of the persistent handle to JS object. - persistent_object.Dispose(); -} - -// Throws a JavaScript exception. -static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("BreakIterator method called on an object " - "that is not a BreakIterator."))); -} - -v8::Handle<v8::Value> BreakIterator::BreakIteratorAdoptText( - const v8::Arguments& args) { - if (args.Length() != 1 || !args[0]->IsString()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Text input is required."))); - } - - icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); - if (!break_iterator) { - return ThrowUnexpectedObjectError(); - } - - break_iterator->setText(*ResetAdoptedText(args.Holder(), args[0])); - - return v8::Undefined(); -} - -v8::Handle<v8::Value> BreakIterator::BreakIteratorFirst( - const v8::Arguments& args) { - icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); - if (!break_iterator) { - return ThrowUnexpectedObjectError(); - } - - return v8::Int32::New(break_iterator->first()); -} - -v8::Handle<v8::Value> BreakIterator::BreakIteratorNext( - const v8::Arguments& args) { - icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); - if (!break_iterator) { - return ThrowUnexpectedObjectError(); - } - - return v8::Int32::New(break_iterator->next()); -} - -v8::Handle<v8::Value> BreakIterator::BreakIteratorCurrent( - const v8::Arguments& args) { - icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); - if (!break_iterator) { - return ThrowUnexpectedObjectError(); - } - - return v8::Int32::New(break_iterator->current()); -} - -v8::Handle<v8::Value> BreakIterator::BreakIteratorBreakType( - const v8::Arguments& args) { - icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); - if (!break_iterator) { - return ThrowUnexpectedObjectError(); - } - - // TODO(cira): Remove cast once ICU fixes base BreakIterator class. - icu::RuleBasedBreakIterator* rule_based_iterator = - static_cast<icu::RuleBasedBreakIterator*>(break_iterator); - int32_t status = rule_based_iterator->getRuleStatus(); - // Keep return values in sync with JavaScript BreakType enum. - if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) { - return v8::Int32::New(UBRK_WORD_NONE); - } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) { - return v8::Int32::New(UBRK_WORD_NUMBER); - } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) { - return v8::Int32::New(UBRK_WORD_LETTER); - } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) { - return v8::Int32::New(UBRK_WORD_KANA); - } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) { - return v8::Int32::New(UBRK_WORD_IDEO); - } else { - return v8::Int32::New(-1); - } -} - -v8::Handle<v8::Value> BreakIterator::JSBreakIterator( - const v8::Arguments& args) { - v8::HandleScope handle_scope; - - if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Locale and iterator type are required."))); - } - - v8::String::Utf8Value locale(args[0]); - icu::Locale icu_locale(*locale); - - UErrorCode status = U_ZERO_ERROR; - icu::BreakIterator* break_iterator = NULL; - v8::String::Utf8Value type(args[1]); - if (!strcmp(*type, "character")) { - break_iterator = - icu::BreakIterator::createCharacterInstance(icu_locale, status); - } else if (!strcmp(*type, "word")) { - break_iterator = - icu::BreakIterator::createWordInstance(icu_locale, status); - } else if (!strcmp(*type, "sentence")) { - break_iterator = - icu::BreakIterator::createSentenceInstance(icu_locale, status); - } else if (!strcmp(*type, "line")) { - break_iterator = - icu::BreakIterator::createLineInstance(icu_locale, status); - } else { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Invalid iterator type."))); - } - - if (U_FAILURE(status)) { - delete break_iterator; - return v8::ThrowException(v8::Exception::Error( - v8::String::New("Failed to create break iterator."))); - } - - if (break_iterator_template_.IsEmpty()) { - v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New()); - - raw_template->SetClassName(v8::String::New("v8Locale.v8BreakIterator")); - - // Define internal field count on instance template. - v8::Local<v8::ObjectTemplate> object_template = - raw_template->InstanceTemplate(); - - // Set aside internal fields for icu break iterator and adopted text. - object_template->SetInternalFieldCount(2); - - // Define all of the prototype methods on prototype template. - v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate(); - proto->Set(v8::String::New("adoptText"), - v8::FunctionTemplate::New(BreakIteratorAdoptText)); - proto->Set(v8::String::New("first"), - v8::FunctionTemplate::New(BreakIteratorFirst)); - proto->Set(v8::String::New("next"), - v8::FunctionTemplate::New(BreakIteratorNext)); - proto->Set(v8::String::New("current"), - v8::FunctionTemplate::New(BreakIteratorCurrent)); - proto->Set(v8::String::New("breakType"), - v8::FunctionTemplate::New(BreakIteratorBreakType)); - - break_iterator_template_ = - v8::Persistent<v8::FunctionTemplate>::New(raw_template); - } - - // Create an empty object wrapper. - v8::Local<v8::Object> local_object = - break_iterator_template_->GetFunction()->NewInstance(); - v8::Persistent<v8::Object> wrapper = - v8::Persistent<v8::Object>::New(local_object); - - // Set break iterator as internal field of the resulting JS object. - wrapper->SetPointerInInternalField(0, break_iterator); - // Make sure that the pointer to adopted text is NULL. - wrapper->SetPointerInInternalField(1, NULL); - - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, DeleteBreakIterator); - - return wrapper; -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/break-iterator.h b/deps/v8/src/extensions/experimental/break-iterator.h deleted file mode 100644 index 73b9bbd567..0000000000 --- a/deps/v8/src/extensions/experimental/break-iterator.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_BREAK_ITERATOR_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_BREAK_ITERATOR_H_ - -#include "include/v8.h" - -#include "unicode/uversion.h" - -namespace U_ICU_NAMESPACE { -class BreakIterator; -class UnicodeString; -} - -namespace v8 { -namespace internal { - -class BreakIterator { - public: - static v8::Handle<v8::Value> JSBreakIterator(const v8::Arguments& args); - - // Helper methods for various bindings. - - // Unpacks break iterator object from corresponding JavaScript object. - static icu::BreakIterator* UnpackBreakIterator(v8::Handle<v8::Object> obj); - - // Deletes the old value and sets the adopted text in - // corresponding JavaScript object. - static icu::UnicodeString* ResetAdoptedText(v8::Handle<v8::Object> obj, - v8::Handle<v8::Value> text_value); - - // Release memory we allocated for the BreakIterator once the JS object that - // holds the pointer gets garbage collected. - static void DeleteBreakIterator(v8::Persistent<v8::Value> object, - void* param); - - // Assigns new text to the iterator. - static v8::Handle<v8::Value> BreakIteratorAdoptText( - const v8::Arguments& args); - - // Moves iterator to the beginning of the string and returns new position. - static v8::Handle<v8::Value> BreakIteratorFirst(const v8::Arguments& args); - - // Moves iterator to the next position and returns it. - static v8::Handle<v8::Value> BreakIteratorNext(const v8::Arguments& args); - - // Returns current iterator's current position. - static v8::Handle<v8::Value> BreakIteratorCurrent( - const v8::Arguments& args); - - // Returns type of the item from current position. - // This call is only valid for word break iterators. Others just return 0. - static v8::Handle<v8::Value> BreakIteratorBreakType( - const v8::Arguments& args); - - private: - BreakIterator() {} - - static v8::Persistent<v8::FunctionTemplate> break_iterator_template_; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_BREAK_ITERATOR_H_ diff --git a/deps/v8/src/extensions/experimental/collator.cc b/deps/v8/src/extensions/experimental/collator.cc deleted file mode 100644 index 5cf219256a..0000000000 --- a/deps/v8/src/extensions/experimental/collator.cc +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/collator.h" - -#include "unicode/coll.h" -#include "unicode/locid.h" -#include "unicode/ucol.h" - -namespace v8 { -namespace internal { - -v8::Persistent<v8::FunctionTemplate> Collator::collator_template_; - -icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) { - if (collator_template_->HasInstance(obj)) { - return static_cast<icu::Collator*>(obj->GetPointerFromInternalField(0)); - } - - return NULL; -} - -void Collator::DeleteCollator(v8::Persistent<v8::Value> object, void* param) { - v8::Persistent<v8::Object> persistent_object = - v8::Persistent<v8::Object>::Cast(object); - - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a collator. - delete UnpackCollator(persistent_object); - - // Then dispose of the persistent handle to JS object. - persistent_object.Dispose(); -} - -// Throws a JavaScript exception. -static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("Collator method called on an object " - "that is not a Collator."))); -} - -// Extract a boolean option named in |option| and set it to |result|. -// Return true if it's specified. Otherwise, return false. -static bool ExtractBooleanOption(const v8::Local<v8::Object>& options, - const char* option, - bool* result) { - v8::HandleScope handle_scope; - v8::TryCatch try_catch; - v8::Handle<v8::Value> value = options->Get(v8::String::New(option)); - if (try_catch.HasCaught()) { - return false; - } - // No need to check if |value| is empty because it's taken care of - // by TryCatch above. - if (!value->IsUndefined() && !value->IsNull()) { - if (value->IsBoolean()) { - *result = value->BooleanValue(); - return true; - } - } - return false; -} - -// When there's an ICU error, throw a JavaScript error with |message|. -static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) { - return v8::ThrowException(v8::Exception::Error(v8::String::New(message))); -} - -v8::Handle<v8::Value> Collator::CollatorCompare(const v8::Arguments& args) { - if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Two string arguments are required."))); - } - - icu::Collator* collator = UnpackCollator(args.Holder()); - if (!collator) { - return ThrowUnexpectedObjectError(); - } - - v8::String::Value string_value1(args[0]); - v8::String::Value string_value2(args[1]); - const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1); - const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2); - UErrorCode status = U_ZERO_ERROR; - UCollationResult result = collator->compare( - string1, string_value1.length(), string2, string_value2.length(), status); - - if (U_FAILURE(status)) { - return ThrowExceptionForICUError( - "Unexpected failure in Collator.compare."); - } - - return v8::Int32::New(result); -} - -v8::Handle<v8::Value> Collator::JSCollator(const v8::Arguments& args) { - v8::HandleScope handle_scope; - - if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Locale and collation options are required."))); - } - - v8::String::AsciiValue locale(args[0]); - icu::Locale icu_locale(*locale); - - icu::Collator* collator = NULL; - UErrorCode status = U_ZERO_ERROR; - collator = icu::Collator::createInstance(icu_locale, status); - - if (U_FAILURE(status)) { - delete collator; - return ThrowExceptionForICUError("Failed to create collator."); - } - - v8::Local<v8::Object> options(args[1]->ToObject()); - - // Below, we change collation options that are explicitly specified - // by a caller in JavaScript. Otherwise, we don't touch because - // we don't want to change the locale-dependent default value. - // The three options below are very likely to have the same default - // across locales, but I haven't checked them all. Others we may add - // in the future have certainly locale-dependent default (e.g. - // caseFirst is upperFirst for Danish while is off for most other locales). - - bool ignore_case, ignore_accents, numeric; - - if (ExtractBooleanOption(options, "ignoreCase", &ignore_case)) { - // We need to explicitly set the level to secondary to get case ignored. - // The default L3 ignores UCOL_CASE_LEVEL == UCOL_OFF ! - if (ignore_case) { - collator->setStrength(icu::Collator::SECONDARY); - } - collator->setAttribute(UCOL_CASE_LEVEL, ignore_case ? UCOL_OFF : UCOL_ON, - status); - if (U_FAILURE(status)) { - delete collator; - return ThrowExceptionForICUError("Failed to set ignoreCase."); - } - } - - // Accents are taken into account with strength secondary or higher. - if (ExtractBooleanOption(options, "ignoreAccents", &ignore_accents)) { - if (!ignore_accents) { - collator->setStrength(icu::Collator::SECONDARY); - } else { - collator->setStrength(icu::Collator::PRIMARY); - } - } - - if (ExtractBooleanOption(options, "numeric", &numeric)) { - collator->setAttribute(UCOL_NUMERIC_COLLATION, - numeric ? UCOL_ON : UCOL_OFF, status); - if (U_FAILURE(status)) { - delete collator; - return ThrowExceptionForICUError("Failed to set numeric sort option."); - } - } - - if (collator_template_.IsEmpty()) { - v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New()); - raw_template->SetClassName(v8::String::New("v8Locale.Collator")); - - // Define internal field count on instance template. - v8::Local<v8::ObjectTemplate> object_template = - raw_template->InstanceTemplate(); - - // Set aside internal fields for icu collator. - object_template->SetInternalFieldCount(1); - - // Define all of the prototype methods on prototype template. - v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate(); - proto->Set(v8::String::New("compare"), - v8::FunctionTemplate::New(CollatorCompare)); - - collator_template_ = - v8::Persistent<v8::FunctionTemplate>::New(raw_template); - } - - // Create an empty object wrapper. - v8::Local<v8::Object> local_object = - collator_template_->GetFunction()->NewInstance(); - v8::Persistent<v8::Object> wrapper = - v8::Persistent<v8::Object>::New(local_object); - - // Set collator as internal field of the resulting JS object. - wrapper->SetPointerInInternalField(0, collator); - - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, DeleteCollator); - - return wrapper; -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/collator.h b/deps/v8/src/extensions/experimental/collator.h deleted file mode 100644 index ca7e4dc9d4..0000000000 --- a/deps/v8/src/extensions/experimental/collator.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H -#define V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H_ - -#include "include/v8.h" - -#include "unicode/uversion.h" - -namespace U_ICU_NAMESPACE { -class Collator; -class UnicodeString; -} - -namespace v8 { -namespace internal { - -class Collator { - public: - static v8::Handle<v8::Value> JSCollator(const v8::Arguments& args); - - // Helper methods for various bindings. - - // Unpacks collator object from corresponding JavaScript object. - static icu::Collator* UnpackCollator(v8::Handle<v8::Object> obj); - - // Release memory we allocated for the Collator once the JS object that - // holds the pointer gets garbage collected. - static void DeleteCollator(v8::Persistent<v8::Value> object, void* param); - - // Compare two strings and returns -1, 0 and 1 depending on - // whether string1 is smaller than, equal to or larger than string2. - static v8::Handle<v8::Value> CollatorCompare(const v8::Arguments& args); - - private: - Collator() {} - - static v8::Persistent<v8::FunctionTemplate> collator_template_; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_COLLATOR diff --git a/deps/v8/src/extensions/experimental/datetime-format.cc b/deps/v8/src/extensions/experimental/datetime-format.cc deleted file mode 100644 index 94a29ac0ae..0000000000 --- a/deps/v8/src/extensions/experimental/datetime-format.cc +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/datetime-format.h" - -#include <string.h> - -#include "src/extensions/experimental/i18n-utils.h" -#include "unicode/dtfmtsym.h" -#include "unicode/dtptngen.h" -#include "unicode/locid.h" -#include "unicode/smpdtfmt.h" - -namespace v8 { -namespace internal { - -v8::Persistent<v8::FunctionTemplate> DateTimeFormat::datetime_format_template_; - -static icu::DateFormat* CreateDateTimeFormat(v8::Handle<v8::String>, - v8::Handle<v8::Object>); -static v8::Handle<v8::Value> GetSymbols( - const v8::Arguments&, - const icu::UnicodeString*, int32_t, - const icu::UnicodeString*, int32_t, - const icu::UnicodeString*, int32_t); -static v8::Handle<v8::Value> ThrowUnexpectedObjectError(); -static icu::DateFormat::EStyle GetDateTimeStyle(const icu::UnicodeString&); - -icu::SimpleDateFormat* DateTimeFormat::UnpackDateTimeFormat( - v8::Handle<v8::Object> obj) { - if (datetime_format_template_->HasInstance(obj)) { - return static_cast<icu::SimpleDateFormat*>( - obj->GetPointerFromInternalField(0)); - } - - return NULL; -} - -void DateTimeFormat::DeleteDateTimeFormat(v8::Persistent<v8::Value> object, - void* param) { - v8::Persistent<v8::Object> persistent_object = - v8::Persistent<v8::Object>::Cast(object); - - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a date time formatter. - delete UnpackDateTimeFormat(persistent_object); - - // Then dispose of the persistent handle to JS object. - persistent_object.Dispose(); -} - -v8::Handle<v8::Value> DateTimeFormat::Format(const v8::Arguments& args) { - v8::HandleScope handle_scope; - - double millis = 0.0; - if (args.Length() != 1 || !args[0]->IsDate()) { - // Create a new date. - v8::TryCatch try_catch; - v8::Local<v8::Script> date_script = - v8::Script::Compile(v8::String::New("eval('new Date()')")); - millis = date_script->Run()->NumberValue(); - if (try_catch.HasCaught()) { - return try_catch.ReThrow(); - } - } else { - millis = v8::Date::Cast(*args[0])->NumberValue(); - } - - icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); - if (!date_format) { - return ThrowUnexpectedObjectError(); - } - - icu::UnicodeString result; - date_format->format(millis, result); - - return v8::String::New( - reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()); -} - -v8::Handle<v8::Value> DateTimeFormat::GetMonths(const v8::Arguments& args) { - icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); - if (!date_format) { - return ThrowUnexpectedObjectError(); - } - - const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols(); - - int32_t narrow_count; - const icu::UnicodeString* narrow = symbols->getMonths( - narrow_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::NARROW); - int32_t abbrev_count; - const icu::UnicodeString* abbrev = symbols->getMonths( - abbrev_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::ABBREVIATED); - int32_t wide_count; - const icu::UnicodeString* wide = symbols->getMonths( - wide_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::WIDE); - - return GetSymbols( - args, narrow, narrow_count, abbrev, abbrev_count, wide, wide_count); -} - -v8::Handle<v8::Value> DateTimeFormat::GetWeekdays(const v8::Arguments& args) { - icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); - if (!date_format) { - return ThrowUnexpectedObjectError(); - } - - const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols(); - - int32_t narrow_count; - const icu::UnicodeString* narrow = symbols->getWeekdays( - narrow_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::NARROW); - int32_t abbrev_count; - const icu::UnicodeString* abbrev = symbols->getWeekdays( - abbrev_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::ABBREVIATED); - int32_t wide_count; - const icu::UnicodeString* wide = symbols->getWeekdays( - wide_count, - icu::DateFormatSymbols::STANDALONE, - icu::DateFormatSymbols::WIDE); - - // getXXXWeekdays always returns 8 elements - ICU stable API. - // We can't use ASSERT_EQ(8, narrow_count) because ASSERT is internal to v8. - if (narrow_count != 8 || abbrev_count != 8 || wide_count != 8) { - return v8::ThrowException(v8::Exception::Error( - v8::String::New("Failed to get weekday information."))); - } - - // ICU documentation says we should ignore element 0 of the returned array. - return GetSymbols(args, narrow + 1, narrow_count - 1, abbrev + 1, - abbrev_count -1 , wide + 1, wide_count - 1); -} - -v8::Handle<v8::Value> DateTimeFormat::GetEras(const v8::Arguments& args) { - icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); - if (!date_format) { - return ThrowUnexpectedObjectError(); - } - - const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols(); - - int32_t narrow_count; - const icu::UnicodeString* narrow = symbols->getNarrowEras(narrow_count); - int32_t abbrev_count; - const icu::UnicodeString* abbrev = symbols->getEras(abbrev_count); - int32_t wide_count; - const icu::UnicodeString* wide = symbols->getEraNames(wide_count); - - return GetSymbols( - args, narrow, narrow_count, abbrev, abbrev_count, wide, wide_count); -} - -v8::Handle<v8::Value> DateTimeFormat::GetAmPm(const v8::Arguments& args) { - icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder()); - if (!date_format) { - return ThrowUnexpectedObjectError(); - } - - const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols(); - - // In this case narrow == abbreviated == wide - int32_t count; - const icu::UnicodeString* wide = symbols->getAmPmStrings(count); - - return GetSymbols(args, wide, count, wide, count, wide, count); -} - -v8::Handle<v8::Value> DateTimeFormat::JSDateTimeFormat( - const v8::Arguments& args) { - v8::HandleScope handle_scope; - - if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Locale and date/time options are required."))); - } - - icu::SimpleDateFormat* date_format = static_cast<icu::SimpleDateFormat*>( - CreateDateTimeFormat(args[0]->ToString(), args[1]->ToObject())); - - if (datetime_format_template_.IsEmpty()) { - v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New()); - - raw_template->SetClassName(v8::String::New("v8Locale.DateTimeFormat")); - - // Define internal field count on instance template. - v8::Local<v8::ObjectTemplate> object_template = - raw_template->InstanceTemplate(); - - // Set aside internal field for icu date time formatter. - object_template->SetInternalFieldCount(1); - - // Define all of the prototype methods on prototype template. - v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate(); - proto->Set(v8::String::New("format"), - v8::FunctionTemplate::New(Format)); - proto->Set(v8::String::New("getMonths"), - v8::FunctionTemplate::New(GetMonths)); - proto->Set(v8::String::New("getWeekdays"), - v8::FunctionTemplate::New(GetWeekdays)); - proto->Set(v8::String::New("getEras"), - v8::FunctionTemplate::New(GetEras)); - proto->Set(v8::String::New("getAmPm"), - v8::FunctionTemplate::New(GetAmPm)); - - datetime_format_template_ = - v8::Persistent<v8::FunctionTemplate>::New(raw_template); - } - - // Create an empty object wrapper. - v8::Local<v8::Object> local_object = - datetime_format_template_->GetFunction()->NewInstance(); - v8::Persistent<v8::Object> wrapper = - v8::Persistent<v8::Object>::New(local_object); - - // Set date time formatter as internal field of the resulting JS object. - wrapper->SetPointerInInternalField(0, date_format); - - // Set resolved pattern in options.pattern. - icu::UnicodeString pattern; - date_format->toPattern(pattern); - v8::Local<v8::Object> options = v8::Object::New(); - options->Set(v8::String::New("pattern"), - v8::String::New(reinterpret_cast<const uint16_t*>( - pattern.getBuffer()), pattern.length())); - wrapper->Set(v8::String::New("options"), options); - - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, DeleteDateTimeFormat); - - return wrapper; -} - -// Returns SimpleDateFormat. -static icu::DateFormat* CreateDateTimeFormat( - v8::Handle<v8::String> locale, v8::Handle<v8::Object> settings) { - v8::HandleScope handle_scope; - - v8::String::AsciiValue ascii_locale(locale); - icu::Locale icu_locale(*ascii_locale); - - // Make formatter from skeleton. - icu::SimpleDateFormat* date_format = NULL; - UErrorCode status = U_ZERO_ERROR; - icu::UnicodeString skeleton; - if (I18NUtils::ExtractStringSetting(settings, "skeleton", &skeleton)) { - v8::Local<icu::DateTimePatternGenerator> generator( - icu::DateTimePatternGenerator::createInstance(icu_locale, status)); - icu::UnicodeString pattern = - generator->getBestPattern(skeleton, status); - - date_format = new icu::SimpleDateFormat(pattern, icu_locale, status); - if (U_SUCCESS(status)) { - return date_format; - } else { - delete date_format; - } - } - - // Extract date style and time style from settings. - icu::UnicodeString date_style; - icu::DateFormat::EStyle icu_date_style = icu::DateFormat::kNone; - if (I18NUtils::ExtractStringSetting(settings, "dateStyle", &date_style)) { - icu_date_style = GetDateTimeStyle(date_style); - } - - icu::UnicodeString time_style; - icu::DateFormat::EStyle icu_time_style = icu::DateFormat::kNone; - if (I18NUtils::ExtractStringSetting(settings, "timeStyle", &time_style)) { - icu_time_style = GetDateTimeStyle(time_style); - } - - // Try all combinations of date/time styles. - if (icu_date_style == icu::DateFormat::kNone && - icu_time_style == icu::DateFormat::kNone) { - // Return default short date, short - return icu::DateFormat::createDateTimeInstance( - icu::DateFormat::kShort, icu::DateFormat::kShort, icu_locale); - } else if (icu_date_style != icu::DateFormat::kNone && - icu_time_style != icu::DateFormat::kNone) { - return icu::DateFormat::createDateTimeInstance( - icu_date_style, icu_time_style, icu_locale); - } else if (icu_date_style != icu::DateFormat::kNone) { - return icu::DateFormat::createDateInstance(icu_date_style, icu_locale); - } else { - // icu_time_style != icu::DateFormat::kNone - return icu::DateFormat::createTimeInstance(icu_time_style, icu_locale); - } -} - -// Creates a v8::Array of narrow, abbrev or wide symbols. -static v8::Handle<v8::Value> GetSymbols(const v8::Arguments& args, - const icu::UnicodeString* narrow, - int32_t narrow_count, - const icu::UnicodeString* abbrev, - int32_t abbrev_count, - const icu::UnicodeString* wide, - int32_t wide_count) { - v8::HandleScope handle_scope; - - // Make wide width default. - const icu::UnicodeString* result = wide; - int32_t count = wide_count; - - if (args.Length() == 1 && args[0]->IsString()) { - v8::String::AsciiValue ascii_value(args[0]); - if (strcmp(*ascii_value, "abbreviated") == 0) { - result = abbrev; - count = abbrev_count; - } else if (strcmp(*ascii_value, "narrow") == 0) { - result = narrow; - count = narrow_count; - } - } - - v8::Handle<v8::Array> symbols = v8::Array::New(); - for (int32_t i = 0; i < count; ++i) { - symbols->Set(i, v8::String::New( - reinterpret_cast<const uint16_t*>(result[i].getBuffer()), - result[i].length())); - } - - return handle_scope.Close(symbols); -} - -// Throws a JavaScript exception. -static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("DateTimeFormat method called on an object " - "that is not a DateTimeFormat."))); -} - -// Returns icu date/time style. -static icu::DateFormat::EStyle GetDateTimeStyle( - const icu::UnicodeString& type) { - if (type == UNICODE_STRING_SIMPLE("medium")) { - return icu::DateFormat::kMedium; - } else if (type == UNICODE_STRING_SIMPLE("long")) { - return icu::DateFormat::kLong; - } else if (type == UNICODE_STRING_SIMPLE("full")) { - return icu::DateFormat::kFull; - } - - return icu::DateFormat::kShort; -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/datetime-format.h b/deps/v8/src/extensions/experimental/datetime-format.h deleted file mode 100644 index a6a228c77d..0000000000 --- a/deps/v8/src/extensions/experimental/datetime-format.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_ - -#include "include/v8.h" - -#include "unicode/uversion.h" - -namespace U_ICU_NAMESPACE { -class SimpleDateFormat; -} - -namespace v8 { -namespace internal { - -class DateTimeFormat { - public: - static v8::Handle<v8::Value> JSDateTimeFormat(const v8::Arguments& args); - - // Helper methods for various bindings. - - // Unpacks date format object from corresponding JavaScript object. - static icu::SimpleDateFormat* UnpackDateTimeFormat( - v8::Handle<v8::Object> obj); - - // Release memory we allocated for the DateFormat once the JS object that - // holds the pointer gets garbage collected. - static void DeleteDateTimeFormat(v8::Persistent<v8::Value> object, - void* param); - - // Formats date and returns corresponding string. - static v8::Handle<v8::Value> Format(const v8::Arguments& args); - - // All date time symbol methods below return stand-alone names in - // either narrow, abbreviated or wide width. - - // Get list of months. - static v8::Handle<v8::Value> GetMonths(const v8::Arguments& args); - - // Get list of weekdays. - static v8::Handle<v8::Value> GetWeekdays(const v8::Arguments& args); - - // Get list of eras. - static v8::Handle<v8::Value> GetEras(const v8::Arguments& args); - - // Get list of day periods. - static v8::Handle<v8::Value> GetAmPm(const v8::Arguments& args); - - private: - DateTimeFormat(); - - static v8::Persistent<v8::FunctionTemplate> datetime_format_template_; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_DATETIME_FORMAT_H_ diff --git a/deps/v8/src/extensions/experimental/experimental.gyp b/deps/v8/src/extensions/experimental/experimental.gyp deleted file mode 100644 index 24fb683160..0000000000 --- a/deps/v8/src/extensions/experimental/experimental.gyp +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2011 the V8 project authors. All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -{ - 'variables': { - # TODO(cira): Find out how to pass this value for arbitrary embedder. - # Chromium sets it in common.gypi and does force include of that file for - # all sub projects. - 'icu_src_dir%': '../../../../third_party/icu', - }, - 'targets': [ - { - 'target_name': 'i18n_api', - 'type': 'static_library', - 'sources': [ - 'break-iterator.cc', - 'break-iterator.h', - 'collator.cc', - 'collator.h', - 'datetime-format.cc', - 'datetime-format.h', - 'i18n-extension.cc', - 'i18n-extension.h', - 'i18n-locale.cc', - 'i18n-locale.h', - 'i18n-natives.h', - 'i18n-utils.cc', - 'i18n-utils.h', - 'language-matcher.cc', - 'language-matcher.h', - 'number-format.cc', - 'number-format.h', - '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', - ], - 'include_dirs': [ - '<(icu_src_dir)/public/common', - # v8/ is root for all includes. - '../../..' - ], - 'dependencies': [ - '<(icu_src_dir)/icu.gyp:*', - 'js2c_i18n#host', - '../../../tools/gyp/v8.gyp:v8', - ], - 'direct_dependent_settings': { - # Adds -Iv8 for embedders. - 'include_dirs': [ - '../../..' - ], - }, - }, - { - 'target_name': 'js2c_i18n', - 'type': 'none', - 'toolsets': ['host'], - 'variables': { - 'js_files': [ - 'i18n.js' - ], - }, - 'actions': [ - { - 'action_name': 'js2c_i18n', - 'inputs': [ - 'i18n-js2c.py', - '<@(js_files)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', - ], - 'action': [ - 'python', - 'i18n-js2c.py', - '<@(_outputs)', - '<@(js_files)' - ], - }, - ], - }, - ], # targets -} diff --git a/deps/v8/src/extensions/experimental/i18n-extension.cc b/deps/v8/src/extensions/experimental/i18n-extension.cc deleted file mode 100644 index c5afcf0bfc..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-extension.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/i18n-extension.h" - -#include "src/extensions/experimental/break-iterator.h" -#include "src/extensions/experimental/collator.h" -#include "src/extensions/experimental/datetime-format.h" -#include "src/extensions/experimental/i18n-locale.h" -#include "src/extensions/experimental/i18n-natives.h" -#include "src/extensions/experimental/number-format.h" - -namespace v8 { -namespace internal { - -I18NExtension* I18NExtension::extension_ = NULL; - -I18NExtension::I18NExtension() - : v8::Extension("v8/i18n", I18Natives::GetScriptSource()) { -} - -v8::Handle<v8::FunctionTemplate> I18NExtension::GetNativeFunction( - v8::Handle<v8::String> name) { - if (name->Equals(v8::String::New("NativeJSLocale"))) { - return v8::FunctionTemplate::New(I18NLocale::JSLocale); - } else if (name->Equals(v8::String::New("NativeJSBreakIterator"))) { - return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator); - } else if (name->Equals(v8::String::New("NativeJSCollator"))) { - return v8::FunctionTemplate::New(Collator::JSCollator); - } else if (name->Equals(v8::String::New("NativeJSDateTimeFormat"))) { - return v8::FunctionTemplate::New(DateTimeFormat::JSDateTimeFormat); - } else if (name->Equals(v8::String::New("NativeJSNumberFormat"))) { - return v8::FunctionTemplate::New(NumberFormat::JSNumberFormat); - } - - return v8::Handle<v8::FunctionTemplate>(); -} - -I18NExtension* I18NExtension::get() { - if (!extension_) { - extension_ = new I18NExtension(); - } - return extension_; -} - -void I18NExtension::Register() { - static v8::DeclareExtension i18n_extension_declaration(I18NExtension::get()); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/i18n-extension.h b/deps/v8/src/extensions/experimental/i18n-extension.h deleted file mode 100644 index 5401f2504e..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-extension.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ - -#include "include/v8.h" - -namespace v8 { -namespace internal { - - -class I18NExtension : public v8::Extension { - public: - I18NExtension(); - - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name); - - // V8 code prefers Register, while Chrome and WebKit use get kind of methods. - static void Register(); - static I18NExtension* get(); - - private: - static I18NExtension* extension_; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_EXTENSION_H_ diff --git a/deps/v8/src/extensions/experimental/i18n-js2c.py b/deps/v8/src/extensions/experimental/i18n-js2c.py deleted file mode 100644 index 9c3128bd2a..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-js2c.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2011 the V8 project authors. All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# This is a utility for converting I18N JavaScript source code into C-style -# char arrays. It is used for embedded JavaScript code in the V8 -# library. -# This is a pared down copy of v8/tools/js2c.py that avoids use of -# v8/src/natives.h and produces different cc template. - -import os, re, sys, string - - -def ToCArray(lines): - result = [] - for chr in lines: - value = ord(chr) - assert value < 128 - result.append(str(value)) - result.append("0") - return ", ".join(result) - - -def RemoveCommentsAndTrailingWhitespace(lines): - lines = re.sub(r'//.*\n', '\n', lines) # end-of-line comments - lines = re.sub(re.compile(r'/\*.*?\*/', re.DOTALL), '', lines) # comments. - lines = re.sub(r'\s+\n+', '\n', lines) # trailing whitespace - return lines - - -def ReadFile(filename): - file = open(filename, "rt") - try: - lines = file.read() - finally: - file.close() - return lines - - -EVAL_PATTERN = re.compile(r'\beval\s*\('); -WITH_PATTERN = re.compile(r'\bwith\s*\('); - - -def Validate(lines, file): - lines = RemoveCommentsAndTrailingWhitespace(lines) - # Because of simplified context setup, eval and with is not - # allowed in the natives files. - eval_match = EVAL_PATTERN.search(lines) - if eval_match: - raise ("Eval disallowed in natives: %s" % file) - with_match = WITH_PATTERN.search(lines) - if with_match: - raise ("With statements disallowed in natives: %s" % file) - - -HEADER_TEMPLATE = """\ -// Copyright 2011 Google Inc. All Rights Reserved. - -// This file was generated from .js source files by gyp. If you -// want to make changes to this file you should either change the -// javascript source files or the i18n-js2c.py script. - -#include "src/extensions/experimental/i18n-natives.h" - -namespace v8 { -namespace internal { - -// static -const char* I18Natives::GetScriptSource() { - // JavaScript source gets injected here. - static const char i18n_source[] = {%s}; - - return i18n_source; -} - -} // internal -} // v8 -""" - - -def JS2C(source, target): - filename = str(source) - - lines = ReadFile(filename) - Validate(lines, filename) - data = ToCArray(lines) - - # Emit result - output = open(target, "w") - output.write(HEADER_TEMPLATE % data) - output.close() - - -def main(): - target = sys.argv[1] - source = sys.argv[2] - JS2C(source, target) - - -if __name__ == "__main__": - main() diff --git a/deps/v8/src/extensions/experimental/i18n-locale.cc b/deps/v8/src/extensions/experimental/i18n-locale.cc deleted file mode 100644 index 46a5f87e11..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-locale.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/i18n-locale.h" - -#include "src/extensions/experimental/i18n-utils.h" -#include "src/extensions/experimental/language-matcher.h" -#include "unicode/locid.h" -#include "unicode/uloc.h" - -namespace v8 { -namespace internal { - -const char* const I18NLocale::kLocaleID = "localeID"; -const char* const I18NLocale::kRegionID = "regionID"; -const char* const I18NLocale::kICULocaleID = "icuLocaleID"; - -v8::Handle<v8::Value> I18NLocale::JSLocale(const v8::Arguments& args) { - v8::HandleScope handle_scope; - - if (args.Length() != 1 || !args[0]->IsObject()) { - return v8::Undefined(); - } - - v8::Local<v8::Object> settings = args[0]->ToObject(); - - // Get best match for locale. - v8::TryCatch try_catch; - v8::Handle<v8::Value> locale_id = settings->Get(v8::String::New(kLocaleID)); - if (try_catch.HasCaught()) { - return v8::Undefined(); - } - - LocaleIDMatch result; - if (locale_id->IsArray()) { - LanguageMatcher::GetBestMatchForPriorityList( - v8::Handle<v8::Array>::Cast(locale_id), &result); - } else if (locale_id->IsString()) { - LanguageMatcher::GetBestMatchForString(locale_id->ToString(), &result); - } else { - LanguageMatcher::GetBestMatchForString(v8::String::New(""), &result); - } - - // Get best match for region. - char region_id[ULOC_COUNTRY_CAPACITY]; - I18NUtils::StrNCopy(region_id, ULOC_COUNTRY_CAPACITY, ""); - - v8::Handle<v8::Value> region = settings->Get(v8::String::New(kRegionID)); - if (try_catch.HasCaught()) { - return v8::Undefined(); - } - - if (!GetBestMatchForRegionID(result.icu_id, region, region_id)) { - // Set region id to empty string because region couldn't be inferred. - I18NUtils::StrNCopy(region_id, ULOC_COUNTRY_CAPACITY, ""); - } - - // Build JavaScript object that contains bcp and icu locale ID and region ID. - v8::Handle<v8::Object> locale = v8::Object::New(); - locale->Set(v8::String::New(kLocaleID), v8::String::New(result.bcp47_id)); - locale->Set(v8::String::New(kICULocaleID), v8::String::New(result.icu_id)); - locale->Set(v8::String::New(kRegionID), v8::String::New(region_id)); - - return handle_scope.Close(locale); -} - -bool I18NLocale::GetBestMatchForRegionID( - const char* locale_id, v8::Handle<v8::Value> region_id, char* result) { - if (region_id->IsString() && region_id->ToString()->Length() != 0) { - icu::Locale user_locale( - icu::Locale("und", *v8::String::Utf8Value(region_id->ToString()))); - I18NUtils::StrNCopy( - result, ULOC_COUNTRY_CAPACITY, user_locale.getCountry()); - return true; - } - // Maximize locale_id to infer the region (e.g. expand "de" to "de-Latn-DE" - // and grab "DE" from the result). - UErrorCode status = U_ZERO_ERROR; - char maximized_locale[ULOC_FULLNAME_CAPACITY]; - uloc_addLikelySubtags( - locale_id, maximized_locale, ULOC_FULLNAME_CAPACITY, &status); - uloc_getCountry(maximized_locale, result, ULOC_COUNTRY_CAPACITY, &status); - - return !U_FAILURE(status); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/i18n-locale.h b/deps/v8/src/extensions/experimental/i18n-locale.h deleted file mode 100644 index 607818ce51..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-locale.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_ - -#include "include/v8.h" - -namespace v8 { -namespace internal { - -class I18NLocale { - public: - I18NLocale() {} - - // Implementations of window.Locale methods. - static v8::Handle<v8::Value> JSLocale(const v8::Arguments& args); - - // Infers region id given the locale id, or uses user specified region id. - // Result is canonicalized. - // Returns status of ICU operation (maximizing locale or get region call). - static bool GetBestMatchForRegionID( - const char* locale_id, v8::Handle<v8::Value> regions, char* result); - - private: - // Key name for localeID parameter. - static const char* const kLocaleID; - // Key name for regionID parameter. - static const char* const kRegionID; - // Key name for the icuLocaleID result. - static const char* const kICULocaleID; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_ diff --git a/deps/v8/src/extensions/experimental/i18n-natives.h b/deps/v8/src/extensions/experimental/i18n-natives.h deleted file mode 100644 index 37362d0ddc..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-natives.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_NATIVES_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_I18N_NATIVES_H_ - -namespace v8 { -namespace internal { - -class I18Natives { - public: - // Gets script source from generated file. - // Source is statically allocated string. - static const char* GetScriptSource(); -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_NATIVES_H_ diff --git a/deps/v8/src/extensions/experimental/i18n-utils.cc b/deps/v8/src/extensions/experimental/i18n-utils.cc deleted file mode 100644 index dc2be1a210..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-utils.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/i18n-utils.h" - -#include <string.h> - -#include "unicode/unistr.h" - -namespace v8 { -namespace internal { - -// static -void I18NUtils::StrNCopy(char* dest, int length, const char* src) { - if (!dest || !src) return; - - strncpy(dest, src, length); - dest[length - 1] = '\0'; -} - -// static -bool I18NUtils::ExtractStringSetting(const v8::Handle<v8::Object>& settings, - const char* setting, - icu::UnicodeString* result) { - if (!setting || !result) return false; - - v8::HandleScope handle_scope; - v8::TryCatch try_catch; - v8::Handle<v8::Value> value = settings->Get(v8::String::New(setting)); - if (try_catch.HasCaught()) { - return false; - } - // No need to check if |value| is empty because it's taken care of - // by TryCatch above. - if (!value->IsUndefined() && !value->IsNull() && value->IsString()) { - v8::String::Utf8Value utf8_value(value); - if (*utf8_value == NULL) return false; - result->setTo(icu::UnicodeString::fromUTF8(*utf8_value)); - return true; - } - return false; -} - -// static -void I18NUtils::AsciiToUChar(const char* source, - int32_t source_length, - UChar* target, - int32_t target_length) { - int32_t length = - source_length < target_length ? source_length : target_length; - - if (length <= 0) { - return; - } - - for (int32_t i = 0; i < length - 1; ++i) { - target[i] = static_cast<UChar>(source[i]); - } - - target[length - 1] = 0x0u; -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/i18n-utils.h b/deps/v8/src/extensions/experimental/i18n-utils.h deleted file mode 100644 index 7c31528be8..0000000000 --- a/deps/v8/src/extensions/experimental/i18n-utils.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_ - -#include "include/v8.h" - -#include "unicode/uversion.h" - -namespace U_ICU_NAMESPACE { -class UnicodeString; -} - -namespace v8 { -namespace internal { - -class I18NUtils { - public: - // Safe string copy. Null terminates the destination. Copies at most - // (length - 1) bytes. - // We can't use snprintf since it's not supported on all relevant platforms. - // We can't use OS::SNPrintF, it's only for internal code. - static void StrNCopy(char* dest, int length, const char* src); - - // Extract a string setting named in |settings| and set it to |result|. - // Return true if it's specified. Otherwise, return false. - static bool ExtractStringSetting(const v8::Handle<v8::Object>& settings, - const char* setting, - icu::UnicodeString* result); - - // Converts ASCII array into UChar array. - // Target is always \0 terminated. - static void AsciiToUChar(const char* source, - int32_t source_length, - UChar* target, - int32_t target_length); - - private: - I18NUtils() {} -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_ diff --git a/deps/v8/src/extensions/experimental/i18n.js b/deps/v8/src/extensions/experimental/i18n.js deleted file mode 100644 index 56bcf9e478..0000000000 --- a/deps/v8/src/extensions/experimental/i18n.js +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2006-2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// TODO(cira): Rename v8Locale into LocaleInfo once we have stable API. -/** - * LocaleInfo class is an aggregate class of all i18n API calls. - * @param {Object} settings - localeID and regionID to create LocaleInfo from. - * {Array.<string>|string} settings.localeID - - * Unicode identifier of the locale. - * See http://unicode.org/reports/tr35/#BCP_47_Conformance - * {string} settings.regionID - ISO3166 region ID with addition of - * invalid, undefined and reserved region codes. - * @constructor - */ -v8Locale = function(settings) { - native function NativeJSLocale(); - - // Assume user wanted to do v8Locale("sr"); - if (typeof(settings) === "string") { - settings = {'localeID': settings}; - } - - var properties = NativeJSLocale( - v8Locale.__createSettingsOrDefault(settings, {'localeID': 'root'})); - - // Keep the resolved ICU locale ID around to avoid resolving localeID to - // ICU locale ID every time BreakIterator, Collator and so forth are called. - this.__icuLocaleID = properties.icuLocaleID; - this.options = {'localeID': properties.localeID, - 'regionID': properties.regionID}; -}; - -/** - * Clones existing locale with possible overrides for some of the options. - * @param {!Object} settings - overrides for current locale settings. - * @returns {Object} - new LocaleInfo object. - */ -v8Locale.prototype.derive = function(settings) { - return new v8Locale( - v8Locale.__createSettingsOrDefault(settings, this.options)); -}; - -/** - * v8BreakIterator class implements locale aware segmenatation. - * It is not part of EcmaScript proposal. - * @param {Object} locale - locale object to pass to break - * iterator implementation. - * @param {string} type - type of segmenatation: - * - character - * - word - * - sentence - * - line - * @private - * @constructor - */ -v8Locale.v8BreakIterator = function(locale, type) { - native function NativeJSBreakIterator(); - - locale = v8Locale.__createLocaleOrDefault(locale); - // BCP47 ID would work in this case, but we use ICU locale for consistency. - var iterator = NativeJSBreakIterator(locale.__icuLocaleID, type); - iterator.type = type; - return iterator; -}; - -/** - * Type of the break we encountered during previous iteration. - * @type{Enum} - */ -v8Locale.v8BreakIterator.BreakType = { - 'unknown': -1, - 'none': 0, - 'number': 100, - 'word': 200, - 'kana': 300, - 'ideo': 400 -}; - -/** - * Creates new v8BreakIterator based on current locale. - * @param {string} - type of segmentation. See constructor. - * @returns {Object} - new v8BreakIterator object. - */ -v8Locale.prototype.v8CreateBreakIterator = function(type) { - return new v8Locale.v8BreakIterator(this, type); -}; - -// TODO(jungshik): Set |collator.options| to actually recognized / resolved -// values. -/** - * Collator class implements locale-aware sort. - * @param {Object} locale - locale object to pass to collator implementation. - * @param {Object} settings - collation flags: - * - ignoreCase - * - ignoreAccents - * - numeric - * @private - * @constructor - */ -v8Locale.Collator = function(locale, settings) { - native function NativeJSCollator(); - - locale = v8Locale.__createLocaleOrDefault(locale); - var collator = NativeJSCollator( - locale.__icuLocaleID, v8Locale.__createSettingsOrDefault(settings, {})); - return collator; -}; - -/** - * Creates new Collator based on current locale. - * @param {Object} - collation flags. See constructor. - * @returns {Object} - new Collator object. - */ -v8Locale.prototype.createCollator = function(settings) { - return new v8Locale.Collator(this, settings); -}; - -/** - * DateTimeFormat class implements locale-aware date and time formatting. - * Constructor is not part of public API. - * @param {Object} locale - locale object to pass to formatter. - * @param {Object} settings - formatting flags: - * - skeleton - * - dateStyle - * - timeStyle - * @private - * @constructor - */ -v8Locale.__DateTimeFormat = function(locale, settings) { - native function NativeJSDateTimeFormat(); - - settings = v8Locale.__createSettingsOrDefault(settings, {}); - - var cleanSettings = {}; - if (settings.hasOwnProperty('skeleton')) { - cleanSettings['skeleton'] = settings['skeleton']; - } else { - cleanSettings = {}; - if (settings.hasOwnProperty('dateStyle')) { - var ds = settings['dateStyle']; - if (!/^(short|medium|long|full)$/.test(ds)) ds = 'short'; - cleanSettings['dateStyle'] = ds; - } else if (settings.hasOwnProperty('dateType')) { - // Obsolete. New spec requires dateStyle, but we'll keep this around - // for current users. - // TODO(cira): Remove when all internal users switch to dateStyle. - var dt = settings['dateType']; - if (!/^(short|medium|long|full)$/.test(dt)) dt = 'short'; - cleanSettings['dateStyle'] = dt; - } - - if (settings.hasOwnProperty('timeStyle')) { - var ts = settings['timeStyle']; - if (!/^(short|medium|long|full)$/.test(ts)) ts = 'short'; - cleanSettings['timeStyle'] = ts; - } else if (settings.hasOwnProperty('timeType')) { - // TODO(cira): Remove when all internal users switch to timeStyle. - var tt = settings['timeType']; - if (!/^(short|medium|long|full)$/.test(tt)) tt = 'short'; - cleanSettings['timeStyle'] = tt; - } - } - - // Default is to show short date and time. - if (!cleanSettings.hasOwnProperty('skeleton') && - !cleanSettings.hasOwnProperty('dateStyle') && - !cleanSettings.hasOwnProperty('timeStyle')) { - cleanSettings = {'dateStyle': 'short', - 'timeStyle': 'short'}; - } - - locale = v8Locale.__createLocaleOrDefault(locale); - var formatter = NativeJSDateTimeFormat(locale.__icuLocaleID, cleanSettings); - - // NativeJSDateTimeFormat creates formatter.options for us, we just need - // to append actual settings to it. - for (key in cleanSettings) { - formatter.options[key] = cleanSettings[key]; - } - - /** - * Clones existing date time format with possible overrides for some - * of the options. - * @param {!Object} overrideSettings - overrides for current format settings. - * @returns {Object} - new DateTimeFormat object. - * @public - */ - formatter.derive = function(overrideSettings) { - // To remove a setting user can specify undefined as its value. We'll remove - // it from the map in that case. - for (var prop in overrideSettings) { - if (settings.hasOwnProperty(prop) && !overrideSettings[prop]) { - delete settings[prop]; - } - } - return new v8Locale.__DateTimeFormat( - locale, v8Locale.__createSettingsOrDefault(overrideSettings, settings)); - }; - - return formatter; -}; - -/** - * Creates new DateTimeFormat based on current locale. - * @param {Object} - formatting flags. See constructor. - * @returns {Object} - new DateTimeFormat object. - */ -v8Locale.prototype.createDateTimeFormat = function(settings) { - return new v8Locale.__DateTimeFormat(this, settings); -}; - -/** - * NumberFormat class implements locale-aware number formatting. - * Constructor is not part of public API. - * @param {Object} locale - locale object to pass to formatter. - * @param {Object} settings - formatting flags: - * - skeleton - * - pattern - * - style - decimal, currency, percent or scientific - * - currencyCode - ISO 4217 3-letter currency code - * @private - * @constructor - */ -v8Locale.__NumberFormat = function(locale, settings) { - native function NativeJSNumberFormat(); - - settings = v8Locale.__createSettingsOrDefault(settings, {}); - - var cleanSettings = {}; - if (settings.hasOwnProperty('skeleton')) { - // Assign skeleton to cleanSettings and fix invalid currency pattern - // if present - 'ooxo' becomes 'o'. - cleanSettings['skeleton'] = - settings['skeleton'].replace(/\u00a4+[^\u00a4]+\u00a4+/g, '\u00a4'); - } else if (settings.hasOwnProperty('pattern')) { - cleanSettings['pattern'] = settings['pattern']; - } else if (settings.hasOwnProperty('style')) { - var style = settings['style']; - if (!/^(decimal|currency|percent|scientific)$/.test(style)) { - style = 'decimal'; - } - cleanSettings['style'] = style; - } - - // Default is to show decimal style. - if (!cleanSettings.hasOwnProperty('skeleton') && - !cleanSettings.hasOwnProperty('pattern') && - !cleanSettings.hasOwnProperty('style')) { - cleanSettings = {'style': 'decimal'}; - } - - // Add currency code if available and valid (3-letter ASCII code). - if (settings.hasOwnProperty('currencyCode') && - /^[a-zA-Z]{3}$/.test(settings['currencyCode'])) { - cleanSettings['currencyCode'] = settings['currencyCode'].toUpperCase(); - } - - locale = v8Locale.__createLocaleOrDefault(locale); - // Pass in region ID for proper currency detection. Use ZZ if region is empty. - var region = locale.options.regionID !== '' ? locale.options.regionID : 'ZZ'; - var formatter = NativeJSNumberFormat( - locale.__icuLocaleID, 'und_' + region, cleanSettings); - - // ICU doesn't always uppercase the currency code. - if (formatter.options.hasOwnProperty('currencyCode')) { - formatter.options['currencyCode'] = - formatter.options['currencyCode'].toUpperCase(); - } - - for (key in cleanSettings) { - // Don't overwrite keys that are alredy in. - if (formatter.options.hasOwnProperty(key)) continue; - - formatter.options[key] = cleanSettings[key]; - } - - /** - * Clones existing number format with possible overrides for some - * of the options. - * @param {!Object} overrideSettings - overrides for current format settings. - * @returns {Object} - new or cached NumberFormat object. - * @public - */ - formatter.derive = function(overrideSettings) { - // To remove a setting user can specify undefined as its value. We'll remove - // it from the map in that case. - for (var prop in overrideSettings) { - if (settings.hasOwnProperty(prop) && !overrideSettings[prop]) { - delete settings[prop]; - } - } - return new v8Locale.__NumberFormat( - locale, v8Locale.__createSettingsOrDefault(overrideSettings, settings)); - }; - - return formatter; -}; - -/** - * Creates new NumberFormat based on current locale. - * @param {Object} - formatting flags. See constructor. - * @returns {Object} - new or cached NumberFormat object. - */ -v8Locale.prototype.createNumberFormat = function(settings) { - return new v8Locale.__NumberFormat(this, settings); -}; - -/** - * Merges user settings and defaults. - * Settings that are not of object type are rejected. - * Actual property values are not validated, but whitespace is trimmed if they - * are strings. - * @param {!Object} settings - user provided settings. - * @param {!Object} defaults - default values for this type of settings. - * @returns {Object} - valid settings object. - * @private - */ -v8Locale.__createSettingsOrDefault = function(settings, defaults) { - if (!settings || typeof(settings) !== 'object' ) { - return defaults; - } - for (var key in defaults) { - if (!settings.hasOwnProperty(key)) { - settings[key] = defaults[key]; - } - } - // Clean up settings. - for (var key in settings) { - // Trim whitespace. - if (typeof(settings[key]) === "string") { - settings[key] = settings[key].trim(); - } - // Remove all properties that are set to undefined/null. This allows - // derive method to remove a setting we don't need anymore. - if (!settings[key]) { - delete settings[key]; - } - } - - return settings; -}; - -/** - * If locale is valid (defined and of v8Locale type) we return it. If not - * we create default locale and return it. - * @param {!Object} locale - user provided locale. - * @returns {Object} - v8Locale object. - * @private - */ -v8Locale.__createLocaleOrDefault = function(locale) { - if (!locale || !(locale instanceof v8Locale)) { - return new v8Locale(); - } else { - return locale; - } -}; diff --git a/deps/v8/src/extensions/experimental/language-matcher.cc b/deps/v8/src/extensions/experimental/language-matcher.cc deleted file mode 100644 index 127e57178a..0000000000 --- a/deps/v8/src/extensions/experimental/language-matcher.cc +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// TODO(cira): Remove LanguageMatcher from v8 when ICU implements -// language matching API. - -#include "src/extensions/experimental/language-matcher.h" - -#include <string.h> - -#include "src/extensions/experimental/i18n-utils.h" -#include "unicode/datefmt.h" // For getAvailableLocales -#include "unicode/locid.h" -#include "unicode/uloc.h" - -namespace v8 { -namespace internal { - -const unsigned int LanguageMatcher::kLanguageWeight = 75; -const unsigned int LanguageMatcher::kScriptWeight = 20; -const unsigned int LanguageMatcher::kRegionWeight = 5; -const unsigned int LanguageMatcher::kThreshold = 50; -const unsigned int LanguageMatcher::kPositionBonus = 1; -const char* const LanguageMatcher::kDefaultLocale = "root"; - -static const char* GetLanguageException(const char*); -static bool BCP47ToICUFormat(const char*, char*); -static int CompareLocaleSubtags(const char*, const char*); -static bool BuildLocaleName(const char*, const char*, LocaleIDMatch*); - -LocaleIDMatch::LocaleIDMatch() - : score(-1) { - I18NUtils::StrNCopy( - bcp47_id, ULOC_FULLNAME_CAPACITY, LanguageMatcher::kDefaultLocale); - - I18NUtils::StrNCopy( - icu_id, ULOC_FULLNAME_CAPACITY, LanguageMatcher::kDefaultLocale); -} - -LocaleIDMatch& LocaleIDMatch::operator=(const LocaleIDMatch& rhs) { - I18NUtils::StrNCopy(this->bcp47_id, ULOC_FULLNAME_CAPACITY, rhs.bcp47_id); - I18NUtils::StrNCopy(this->icu_id, ULOC_FULLNAME_CAPACITY, rhs.icu_id); - this->score = rhs.score; - - return *this; -} - -// static -void LanguageMatcher::GetBestMatchForPriorityList( - v8::Handle<v8::Array> locales, LocaleIDMatch* result) { - v8::HandleScope handle_scope; - - unsigned int position_bonus = locales->Length() * kPositionBonus; - - int max_score = 0; - LocaleIDMatch match; - for (unsigned int i = 0; i < locales->Length(); ++i) { - position_bonus -= kPositionBonus; - - v8::TryCatch try_catch; - v8::Local<v8::Value> locale_id = locales->Get(v8::Integer::New(i)); - - // Return default if exception is raised when reading parameter. - if (try_catch.HasCaught()) break; - - // JavaScript arrays can be heterogenous so check each item - // if it's a string. - if (!locale_id->IsString()) continue; - - if (!CompareToSupportedLocaleIDList(locale_id->ToString(), &match)) { - continue; - } - - // Skip items under threshold. - if (match.score < kThreshold) continue; - - match.score += position_bonus; - if (match.score > max_score) { - *result = match; - - max_score = match.score; - } - } -} - -// static -void LanguageMatcher::GetBestMatchForString( - v8::Handle<v8::String> locale, LocaleIDMatch* result) { - LocaleIDMatch match; - - if (CompareToSupportedLocaleIDList(locale, &match) && - match.score >= kThreshold) { - *result = match; - } -} - -// static -bool LanguageMatcher::CompareToSupportedLocaleIDList( - v8::Handle<v8::String> locale_id, LocaleIDMatch* result) { - static int32_t available_count = 0; - // Depending on how ICU data is built, locales returned by - // Locale::getAvailableLocale() are not guaranteed to support DateFormat, - // Collation and other services. We can call getAvailableLocale() of all the - // services we want to support and take the intersection of them all, but - // using DateFormat::getAvailableLocales() should suffice. - // TODO(cira): Maybe make this thread-safe? - static const icu::Locale* available_locales = - icu::DateFormat::getAvailableLocales(available_count); - - // Skip this locale_id if it's not in ASCII. - static LocaleIDMatch default_match; - v8::String::AsciiValue ascii_value(locale_id); - if (*ascii_value == NULL) return false; - - char locale[ULOC_FULLNAME_CAPACITY]; - if (!BCP47ToICUFormat(*ascii_value, locale)) return false; - - icu::Locale input_locale(locale); - - // Position of the best match locale in list of available locales. - int position = -1; - const char* language = GetLanguageException(input_locale.getLanguage()); - const char* script = input_locale.getScript(); - const char* region = input_locale.getCountry(); - for (int32_t i = 0; i < available_count; ++i) { - int current_score = 0; - int sign = - CompareLocaleSubtags(language, available_locales[i].getLanguage()); - current_score += sign * kLanguageWeight; - - sign = CompareLocaleSubtags(script, available_locales[i].getScript()); - current_score += sign * kScriptWeight; - - sign = CompareLocaleSubtags(region, available_locales[i].getCountry()); - current_score += sign * kRegionWeight; - - if (current_score >= kThreshold && current_score > result->score) { - result->score = current_score; - position = i; - } - } - - // Didn't find any good matches so use defaults. - if (position == -1) return false; - - return BuildLocaleName(available_locales[position].getBaseName(), - input_locale.getName(), result); -} - -// For some unsupported language subtags it is better to fallback to related -// language that is supported than to default. -static const char* GetLanguageException(const char* language) { - // Serbo-croatian to Serbian. - if (!strcmp(language, "sh")) return "sr"; - - // Norweigan to Norweiaan to Norwegian Bokmal. - if (!strcmp(language, "no")) return "nb"; - - // Moldavian to Romanian. - if (!strcmp(language, "mo")) return "ro"; - - // Tagalog to Filipino. - if (!strcmp(language, "tl")) return "fil"; - - return language; -} - -// Converts user input from BCP47 locale id format to ICU compatible format. -// Returns false if uloc_forLanguageTag call fails or if extension is too long. -static bool BCP47ToICUFormat(const char* locale_id, char* result) { - UErrorCode status = U_ZERO_ERROR; - int32_t locale_size = 0; - - char locale[ULOC_FULLNAME_CAPACITY]; - I18NUtils::StrNCopy(locale, ULOC_FULLNAME_CAPACITY, locale_id); - - // uloc_forLanguageTag has a bug where long extension can crash the code. - // We need to check if extension part of language id conforms to the length. - // ICU bug: http://bugs.icu-project.org/trac/ticket/8519 - const char* extension = strstr(locale_id, "-u-"); - if (extension != NULL && - strlen(extension) > ULOC_KEYWORD_AND_VALUES_CAPACITY) { - // Truncate to get non-crashing string, but still preserve base language. - int base_length = strlen(locale_id) - strlen(extension); - locale[base_length] = '\0'; - } - - uloc_forLanguageTag(locale, result, ULOC_FULLNAME_CAPACITY, - &locale_size, &status); - return !U_FAILURE(status); -} - -// Compares locale id subtags. -// Returns 1 for match or -1 for mismatch. -static int CompareLocaleSubtags(const char* lsubtag, const char* rsubtag) { - return strcmp(lsubtag, rsubtag) == 0 ? 1 : -1; -} - -// Builds a BCP47 compliant locale id from base name of matched locale and -// full user specified locale. -// Returns false if uloc_toLanguageTag failed to convert locale id. -// Example: -// base_name of matched locale (ICU ID): de_DE -// input_locale_name (ICU ID): de_AT@collation=phonebk -// result (ICU ID): de_DE@collation=phonebk -// result (BCP47 ID): de-DE-u-co-phonebk -static bool BuildLocaleName(const char* base_name, - const char* input_locale_name, - LocaleIDMatch* result) { - I18NUtils::StrNCopy(result->icu_id, ULOC_LANG_CAPACITY, base_name); - - // Get extensions (if any) from the original locale. - const char* extension = strchr(input_locale_name, ULOC_KEYWORD_SEPARATOR); - if (extension != NULL) { - I18NUtils::StrNCopy(result->icu_id + strlen(base_name), - ULOC_KEYWORD_AND_VALUES_CAPACITY, extension); - } else { - I18NUtils::StrNCopy(result->icu_id, ULOC_LANG_CAPACITY, base_name); - } - - // Convert ICU locale name into BCP47 format. - UErrorCode status = U_ZERO_ERROR; - uloc_toLanguageTag(result->icu_id, result->bcp47_id, - ULOC_FULLNAME_CAPACITY, false, &status); - return !U_FAILURE(status); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/language-matcher.h b/deps/v8/src/extensions/experimental/language-matcher.h deleted file mode 100644 index dd2930458b..0000000000 --- a/deps/v8/src/extensions/experimental/language-matcher.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_ - -#include "include/v8.h" - -#include "unicode/uloc.h" - -namespace v8 { -namespace internal { - -struct LocaleIDMatch { - LocaleIDMatch(); - - LocaleIDMatch& operator=(const LocaleIDMatch& rhs); - - // Bcp47 locale id - "de-Latn-DE-u-co-phonebk". - char bcp47_id[ULOC_FULLNAME_CAPACITY]; - - // ICU locale id - "de_Latn_DE@collation=phonebk". - char icu_id[ULOC_FULLNAME_CAPACITY]; - - // Score for this locale. - int score; -}; - -class LanguageMatcher { - public: - // Default locale. - static const char* const kDefaultLocale; - - // Finds best supported locale for a given a list of locale identifiers. - // It preserves the extension for the locale id. - static void GetBestMatchForPriorityList( - v8::Handle<v8::Array> locale_list, LocaleIDMatch* result); - - // Finds best supported locale for a single locale identifier. - // It preserves the extension for the locale id. - static void GetBestMatchForString( - v8::Handle<v8::String> locale_id, LocaleIDMatch* result); - - private: - // If langauge subtags match add this amount to the score. - static const unsigned int kLanguageWeight; - - // If script subtags match add this amount to the score. - static const unsigned int kScriptWeight; - - // If region subtags match add this amount to the score. - static const unsigned int kRegionWeight; - - // LocaleID match score has to be over this number to accept the match. - static const unsigned int kThreshold; - - // For breaking ties in priority queue. - static const unsigned int kPositionBonus; - - LanguageMatcher(); - - // Compares locale_id to the supported list of locales and returns best - // match. - // Returns false if it fails to convert locale id from ICU to BCP47 format. - static bool CompareToSupportedLocaleIDList(v8::Handle<v8::String> locale_id, - LocaleIDMatch* result); -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_ diff --git a/deps/v8/src/extensions/experimental/number-format.cc b/deps/v8/src/extensions/experimental/number-format.cc deleted file mode 100644 index 2932c52854..0000000000 --- a/deps/v8/src/extensions/experimental/number-format.cc +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "src/extensions/experimental/number-format.h" - -#include <string.h> - -#include "src/extensions/experimental/i18n-utils.h" -#include "unicode/dcfmtsym.h" -#include "unicode/decimfmt.h" -#include "unicode/locid.h" -#include "unicode/numfmt.h" -#include "unicode/uchar.h" -#include "unicode/ucurr.h" -#include "unicode/unum.h" -#include "unicode/uversion.h" - -namespace v8 { -namespace internal { - -const int NumberFormat::kCurrencyCodeLength = 4; - -v8::Persistent<v8::FunctionTemplate> NumberFormat::number_format_template_; - -static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String>, - v8::Handle<v8::String>, - v8::Handle<v8::Object>); -static icu::DecimalFormat* CreateFormatterFromSkeleton( - const icu::Locale&, const icu::UnicodeString&, UErrorCode*); -static icu::DecimalFormatSymbols* GetFormatSymbols(const icu::Locale&); -static bool GetCurrencyCode(const icu::Locale&, - const char* const, - v8::Handle<v8::Object>, - UChar*); -static v8::Handle<v8::Value> ThrowUnexpectedObjectError(); - -icu::DecimalFormat* NumberFormat::UnpackNumberFormat( - v8::Handle<v8::Object> obj) { - if (number_format_template_->HasInstance(obj)) { - return static_cast<icu::DecimalFormat*>( - obj->GetPointerFromInternalField(0)); - } - - return NULL; -} - -void NumberFormat::DeleteNumberFormat(v8::Persistent<v8::Value> object, - void* param) { - v8::Persistent<v8::Object> persistent_object = - v8::Persistent<v8::Object>::Cast(object); - - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a number formatter. - delete UnpackNumberFormat(persistent_object); - - // Then dispose of the persistent handle to JS object. - persistent_object.Dispose(); -} - -v8::Handle<v8::Value> NumberFormat::Format(const v8::Arguments& args) { - v8::HandleScope handle_scope; - - if (args.Length() != 1 || !args[0]->IsNumber()) { - // Just return NaN on invalid input. - return v8::String::New("NaN"); - } - - icu::DecimalFormat* number_format = UnpackNumberFormat(args.Holder()); - if (!number_format) { - return ThrowUnexpectedObjectError(); - } - - // ICU will handle actual NaN value properly and return NaN string. - icu::UnicodeString result; - number_format->format(args[0]->NumberValue(), result); - - return v8::String::New( - reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()); -} - -v8::Handle<v8::Value> NumberFormat::JSNumberFormat(const v8::Arguments& args) { - v8::HandleScope handle_scope; - - // Expect locale id, region id and settings. - if (args.Length() != 3 || - !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsObject()) { - return v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Locale, region and number settings are required."))); - } - - icu::DecimalFormat* number_format = CreateNumberFormat( - args[0]->ToString(), args[1]->ToString(), args[2]->ToObject()); - - if (number_format_template_.IsEmpty()) { - v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New()); - - raw_template->SetClassName(v8::String::New("v8Locale.NumberFormat")); - - // Define internal field count on instance template. - v8::Local<v8::ObjectTemplate> object_template = - raw_template->InstanceTemplate(); - - // Set aside internal field for icu number formatter. - object_template->SetInternalFieldCount(1); - - // Define all of the prototype methods on prototype template. - v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate(); - proto->Set(v8::String::New("format"), - v8::FunctionTemplate::New(Format)); - - number_format_template_ = - v8::Persistent<v8::FunctionTemplate>::New(raw_template); - } - - // Create an empty object wrapper. - v8::Local<v8::Object> local_object = - number_format_template_->GetFunction()->NewInstance(); - v8::Persistent<v8::Object> wrapper = - v8::Persistent<v8::Object>::New(local_object); - - // Set number formatter as internal field of the resulting JS object. - wrapper->SetPointerInInternalField(0, number_format); - - // Create options key. - v8::Local<v8::Object> options = v8::Object::New(); - - // Show what ICU decided to use for easier problem tracking. - // Keep it as v8 specific extension. - icu::UnicodeString pattern; - number_format->toPattern(pattern); - options->Set(v8::String::New("v8ResolvedPattern"), - v8::String::New(reinterpret_cast<const uint16_t*>( - pattern.getBuffer()), pattern.length())); - - // Set resolved currency code in options.currency if not empty. - icu::UnicodeString currency(number_format->getCurrency()); - if (!currency.isEmpty()) { - options->Set(v8::String::New("currencyCode"), - v8::String::New(reinterpret_cast<const uint16_t*>( - currency.getBuffer()), currency.length())); - } - - wrapper->Set(v8::String::New("options"), options); - - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, DeleteNumberFormat); - - return wrapper; -} - -// Returns DecimalFormat. -static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String> locale, - v8::Handle<v8::String> region, - v8::Handle<v8::Object> settings) { - v8::HandleScope handle_scope; - - v8::String::AsciiValue ascii_locale(locale); - icu::Locale icu_locale(*ascii_locale); - - // Make formatter from skeleton. - icu::DecimalFormat* number_format = NULL; - UErrorCode status = U_ZERO_ERROR; - icu::UnicodeString setting; - - if (I18NUtils::ExtractStringSetting(settings, "skeleton", &setting)) { - // TODO(cira): Use ICU skeleton once - // http://bugs.icu-project.org/trac/ticket/8610 is resolved. - number_format = CreateFormatterFromSkeleton(icu_locale, setting, &status); - } else if (I18NUtils::ExtractStringSetting(settings, "pattern", &setting)) { - number_format = - new icu::DecimalFormat(setting, GetFormatSymbols(icu_locale), status); - } else if (I18NUtils::ExtractStringSetting(settings, "style", &setting)) { - if (setting == UNICODE_STRING_SIMPLE("currency")) { - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createCurrencyInstance(icu_locale, status)); - } else if (setting == UNICODE_STRING_SIMPLE("percent")) { - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createPercentInstance(icu_locale, status)); - } else if (setting == UNICODE_STRING_SIMPLE("scientific")) { - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createScientificInstance(icu_locale, status)); - } else { - // Make it decimal in any other case. - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, status)); - } - } - - if (U_FAILURE(status)) { - delete number_format; - status = U_ZERO_ERROR; - number_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, status)); - } - - // Attach appropriate currency code to the formatter. - // It affects currency formatters only. - // Region is full language identifier in form 'und_' + region id. - v8::String::AsciiValue ascii_region(region); - - UChar currency_code[NumberFormat::kCurrencyCodeLength]; - if (GetCurrencyCode(icu_locale, *ascii_region, settings, currency_code)) { - number_format->setCurrency(currency_code, status); - } - - return number_format; -} - -// Generates ICU number format pattern from given skeleton. -// TODO(cira): Remove once ICU includes equivalent method -// (see http://bugs.icu-project.org/trac/ticket/8610). -static icu::DecimalFormat* CreateFormatterFromSkeleton( - const icu::Locale& icu_locale, - const icu::UnicodeString& skeleton, - UErrorCode* status) { - icu::DecimalFormat skeleton_format( - skeleton, GetFormatSymbols(icu_locale), *status); - - // Find out if skeleton contains currency or percent symbol and create - // proper instance to tweak. - icu::DecimalFormat* base_format = NULL; - - // UChar representation of U+00A4 currency symbol. - const UChar currency_symbol = 0xA4u; - - int32_t index = skeleton.indexOf(currency_symbol); - if (index != -1) { - // Find how many U+00A4 are there. There is at least one. - // Case of non-consecutive U+00A4 is taken care of in i18n.js. - int32_t end_index = skeleton.lastIndexOf(currency_symbol, index); - -#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) - icu::NumberFormat::EStyles style; - switch (end_index - index) { - case 0: - style = icu::NumberFormat::kCurrencyStyle; - break; - case 1: - style = icu::NumberFormat::kIsoCurrencyStyle; - break; - default: - style = icu::NumberFormat::kPluralCurrencyStyle; - } -#else // ICU version is 4.8 or above (we ignore versions below 4.0). - UNumberFormatStyle style; - switch (end_index - index) { - case 0: - style = UNUM_CURRENCY; - break; - case 1: - style = UNUM_CURRENCY_ISO; - break; - default: - style = UNUM_CURRENCY_PLURAL; - } -#endif - - base_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, style, *status)); - } else if (skeleton.indexOf('%') != -1) { - base_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createPercentInstance(icu_locale, *status)); - } else { - // TODO(cira): Handle scientific skeleton. - base_format = static_cast<icu::DecimalFormat*>( - icu::NumberFormat::createInstance(icu_locale, *status)); - } - - if (U_FAILURE(*status)) { - delete base_format; - return NULL; - } - - // Copy important information from skeleton to the new formatter. - // TODO(cira): copy rounding information from skeleton? - base_format->setGroupingUsed(skeleton_format.isGroupingUsed()); - - base_format->setMinimumIntegerDigits( - skeleton_format.getMinimumIntegerDigits()); - - base_format->setMinimumFractionDigits( - skeleton_format.getMinimumFractionDigits()); - - base_format->setMaximumFractionDigits( - skeleton_format.getMaximumFractionDigits()); - - return base_format; -} - -// Gets decimal symbols for a locale. -static icu::DecimalFormatSymbols* GetFormatSymbols( - const icu::Locale& icu_locale) { - UErrorCode status = U_ZERO_ERROR; - icu::DecimalFormatSymbols* symbols = - new icu::DecimalFormatSymbols(icu_locale, status); - - if (U_FAILURE(status)) { - delete symbols; - // Use symbols from default locale. - symbols = new icu::DecimalFormatSymbols(status); - } - - return symbols; -} - -// Gets currency ISO 4217 3-letter code. -// Check currencyCode setting first, then @currency=code and in the end -// try to infer currency code from locale in the form 'und_' + region id. -// Returns false in case of error. -static bool GetCurrencyCode(const icu::Locale& icu_locale, - const char* const und_region_locale, - v8::Handle<v8::Object> settings, - UChar* code) { - UErrorCode status = U_ZERO_ERROR; - - // If there is user specified currency code, use it. - icu::UnicodeString currency; - if (I18NUtils::ExtractStringSetting(settings, "currencyCode", ¤cy)) { - currency.extract(code, NumberFormat::kCurrencyCodeLength, status); - return true; - } - - // If ICU locale has -cu- currency code use it. - char currency_code[NumberFormat::kCurrencyCodeLength]; - int32_t length = icu_locale.getKeywordValue( - "currency", currency_code, NumberFormat::kCurrencyCodeLength, status); - if (length != 0) { - I18NUtils::AsciiToUChar(currency_code, length + 1, - code, NumberFormat::kCurrencyCodeLength); - return true; - } - - // Otherwise infer currency code from the region id. - ucurr_forLocale( - und_region_locale, code, NumberFormat::kCurrencyCodeLength, &status); - - return !!U_SUCCESS(status); -} - -// Throws a JavaScript exception. -static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("NumberFormat method called on an object " - "that is not a NumberFormat."))); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/extensions/experimental/number-format.h b/deps/v8/src/extensions/experimental/number-format.h deleted file mode 100644 index bcfaed6fc1..0000000000 --- a/deps/v8/src/extensions/experimental/number-format.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_EXTENSIONS_EXPERIMENTAL_NUMBER_FORMAT_H_ -#define V8_EXTENSIONS_EXPERIMENTAL_NUMBER_FORMAT_H_ - -#include "include/v8.h" - -#include "unicode/uversion.h" - -namespace U_ICU_NAMESPACE { -class DecimalFormat; -} - -namespace v8 { -namespace internal { - -class NumberFormat { - public: - // 3-letter ISO 4217 currency code plus \0. - static const int kCurrencyCodeLength; - - static v8::Handle<v8::Value> JSNumberFormat(const v8::Arguments& args); - - // Helper methods for various bindings. - - // Unpacks date format object from corresponding JavaScript object. - static icu::DecimalFormat* UnpackNumberFormat( - v8::Handle<v8::Object> obj); - - // Release memory we allocated for the NumberFormat once the JS object that - // holds the pointer gets garbage collected. - static void DeleteNumberFormat(v8::Persistent<v8::Value> object, - void* param); - - // Formats number and returns corresponding string. - static v8::Handle<v8::Value> Format(const v8::Arguments& args); - - private: - NumberFormat(); - - static v8::Persistent<v8::FunctionTemplate> number_format_template_; -}; - -} } // namespace v8::internal - -#endif // V8_EXTENSIONS_EXPERIMENTAL_NUMBER_FORMAT_H_ diff --git a/deps/v8/src/extensions/gc-extension.cc b/deps/v8/src/extensions/gc-extension.cc index 3740c27aa8..573797e174 100644 --- a/deps/v8/src/extensions/gc-extension.cc +++ b/deps/v8/src/extensions/gc-extension.cc @@ -40,19 +40,15 @@ v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { - bool compact = false; - // All allocation spaces other than NEW_SPACE have the same effect. - if (args.Length() >= 1 && args[0]->IsBoolean()) { - compact = args[0]->BooleanValue(); - } - HEAP->CollectAllGarbage(compact); + HEAP->CollectAllGarbage(Heap::kNoGCFlags, "gc extension"); return v8::Undefined(); } void GCExtension::Register() { - static GCExtension gc_extension; - static v8::DeclareExtension gc_extension_declaration(&gc_extension); + static GCExtension* gc_extension = NULL; + if (gc_extension == NULL) gc_extension = new GCExtension(); + static v8::DeclareExtension gc_extension_declaration(gc_extension); } } } // namespace v8::internal diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 971f9f9cee..5915f487de 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -59,13 +59,13 @@ Handle<FixedArray> Factory::NewFixedArrayWithHoles(int size, } -Handle<FixedArray> Factory::NewFixedDoubleArray(int size, - PretenureFlag pretenure) { +Handle<FixedDoubleArray> Factory::NewFixedDoubleArray(int size, + PretenureFlag pretenure) { ASSERT(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure), - FixedArray); + FixedDoubleArray); } @@ -95,6 +95,14 @@ Handle<UnseededNumberDictionary> Factory::NewUnseededNumberDictionary( } +Handle<ObjectHashSet> Factory::NewObjectHashSet(int at_least_space_for) { + ASSERT(0 <= at_least_space_for); + CALL_HEAP_FUNCTION(isolate(), + ObjectHashSet::Allocate(at_least_space_for), + ObjectHashSet); +} + + Handle<ObjectHashTable> Factory::NewObjectHashTable(int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), @@ -133,6 +141,13 @@ Handle<DeoptimizationOutputData> Factory::NewDeoptimizationOutputData( } +Handle<AccessorPair> Factory::NewAccessorPair() { + CALL_HEAP_FUNCTION(isolate(), + isolate()->heap()->AllocateAccessorPair(), + AccessorPair); +} + + // Symbols are created in the old generation (data space). Handle<String> Factory::LookupSymbol(Vector<const char> string) { CALL_HEAP_FUNCTION(isolate(), @@ -244,7 +259,7 @@ Handle<String> Factory::NewProperSubString(Handle<String> str, Handle<String> Factory::NewExternalStringFromAscii( - ExternalAsciiString::Resource* resource) { + const ExternalAsciiString::Resource* resource) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateExternalStringFromAscii(resource), @@ -253,7 +268,7 @@ Handle<String> Factory::NewExternalStringFromAscii( Handle<String> Factory::NewExternalStringFromTwoByte( - ExternalTwoByteString::Resource* resource) { + const ExternalTwoByteString::Resource* resource) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateExternalStringFromTwoByte(resource), @@ -305,7 +320,7 @@ Handle<Context> Factory::NewWithContext(Handle<JSFunction> function, Handle<Context> Factory::NewBlockContext( Handle<JSFunction> function, Handle<Context> previous, - Handle<SerializedScopeInfo> scope_info) { + Handle<ScopeInfo> scope_info) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateBlockContext(*function, @@ -414,10 +429,12 @@ Handle<JSGlobalPropertyCell> Factory::NewJSGlobalPropertyCell( } -Handle<Map> Factory::NewMap(InstanceType type, int instance_size) { +Handle<Map> Factory::NewMap(InstanceType type, + int instance_size, + ElementsKind elements_kind) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->AllocateMap(type, instance_size), + isolate()->heap()->AllocateMap(type, instance_size, elements_kind), Map); } @@ -465,23 +482,12 @@ Handle<Map> Factory::CopyMapDropTransitions(Handle<Map> src) { } -Handle<Map> Factory::GetFastElementsMap(Handle<Map> src) { - CALL_HEAP_FUNCTION(isolate(), src->GetFastElementsMap(), Map); -} - - -Handle<Map> Factory::GetSlowElementsMap(Handle<Map> src) { - CALL_HEAP_FUNCTION(isolate(), src->GetSlowElementsMap(), Map); -} - - Handle<Map> Factory::GetElementsTransitionMap( - Handle<Map> src, - ElementsKind elements_kind, - bool safe_to_add_transition) { - CALL_HEAP_FUNCTION(isolate(), - src->GetElementsTransitionMap(elements_kind, - safe_to_add_transition), + Handle<JSObject> src, + ElementsKind elements_kind) { + Isolate* i = isolate(); + CALL_HEAP_FUNCTION(i, + src->GetElementsTransitionMap(i, elements_kind), Map); } @@ -491,6 +497,12 @@ Handle<FixedArray> Factory::CopyFixedArray(Handle<FixedArray> array) { } +Handle<FixedDoubleArray> Factory::CopyFixedDoubleArray( + Handle<FixedDoubleArray> array) { + CALL_HEAP_FUNCTION(isolate(), array->Copy(), FixedDoubleArray); +} + + Handle<JSFunction> Factory::BaseNewFunctionFromSharedFunctionInfo( Handle<SharedFunctionInfo> function_info, Handle<Map> function_map, @@ -511,22 +523,26 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo( PretenureFlag pretenure) { Handle<JSFunction> result = BaseNewFunctionFromSharedFunctionInfo( function_info, - function_info->strict_mode() - ? isolate()->strict_mode_function_map() - : isolate()->function_map(), + function_info->is_classic_mode() + ? isolate()->function_map() + : isolate()->strict_mode_function_map(), pretenure); result->set_context(*context); - int number_of_literals = function_info->num_literals(); - Handle<FixedArray> literals = NewFixedArray(number_of_literals, pretenure); - if (number_of_literals > 0) { - // Store the object, regexp and array functions in the literals - // array prefix. These functions will be used when creating - // object, regexp and array literals in this function. - literals->set(JSFunction::kLiteralGlobalContextIndex, - context->global_context()); + if (!function_info->bound()) { + int number_of_literals = function_info->num_literals(); + Handle<FixedArray> literals = NewFixedArray(number_of_literals, pretenure); + if (number_of_literals > 0) { + // Store the object, regexp and array functions in the literals + // array prefix. These functions will be used when creating + // object, regexp and array literals in this function. + literals->set(JSFunction::kLiteralGlobalContextIndex, + context->global_context()); + } + result->set_literals(*literals); + } else { + result->set_function_bindings(isolate()->heap()->empty_fixed_array()); } - result->set_literals(*literals); result->set_next_function_link(isolate()->heap()->undefined_value()); if (V8::UseCrankshaft() && @@ -548,17 +564,19 @@ Handle<Object> Factory::NewNumber(double value, } -Handle<Object> Factory::NewNumberFromInt(int value) { +Handle<Object> Factory::NewNumberFromInt(int32_t value, + PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->NumberFromInt32(value), Object); + isolate()->heap()->NumberFromInt32(value, pretenure), Object); } -Handle<Object> Factory::NewNumberFromUint(uint32_t value) { +Handle<Object> Factory::NewNumberFromUint(uint32_t value, + PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->NumberFromUint32(value), Object); + isolate()->heap()->NumberFromUint32(value, pretenure), Object); } @@ -651,14 +669,16 @@ Handle<Object> Factory::NewError(const char* maker, return undefined_value(); Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj); Handle<Object> type_obj = LookupAsciiSymbol(type); - Object** argv[2] = { type_obj.location(), - Handle<Object>::cast(args).location() }; + Handle<Object> argv[] = { type_obj, args }; // Invoke the JavaScript factory method. If an exception is thrown while // running the factory method, use the exception as the result. bool caught_exception; Handle<Object> result = Execution::TryCall(fun, - isolate()->js_builtins_object(), 2, argv, &caught_exception); + isolate()->js_builtins_object(), + ARRAY_SIZE(argv), + argv, + &caught_exception); return result; } @@ -674,13 +694,16 @@ Handle<Object> Factory::NewError(const char* constructor, Handle<JSFunction> fun = Handle<JSFunction>( JSFunction::cast(isolate()->js_builtins_object()-> GetPropertyNoExceptionThrown(*constr))); - Object** argv[1] = { Handle<Object>::cast(message).location() }; + Handle<Object> argv[] = { message }; // Invoke the JavaScript factory method. If an exception is thrown while // running the factory method, use the exception as the result. bool caught_exception; Handle<Object> result = Execution::TryCall(fun, - isolate()->js_builtins_object(), 1, argv, &caught_exception); + isolate()->js_builtins_object(), + ARRAY_SIZE(argv), + argv, + &caught_exception); return result; } @@ -693,7 +716,7 @@ Handle<JSFunction> Factory::NewFunction(Handle<String> name, // Allocate the function Handle<JSFunction> function = NewFunction(name, the_hole_value()); - // Setup the code pointer in both the shared function info and in + // Set up the code pointer in both the shared function info and in // the function itself. function->shared()->set_code(*code); function->set_code(*code); @@ -724,7 +747,7 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, // Allocate the function. Handle<JSFunction> function = NewFunction(name, prototype); - // Setup the code pointer in both the shared function info and in + // Set up the code pointer in both the shared function info and in // the function itself. function->shared()->set_code(*code); function->set_code(*code); @@ -732,7 +755,9 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, if (force_initial_map || type != JS_OBJECT_TYPE || instance_size != JSObject::kHeaderSize) { - Handle<Map> initial_map = NewMap(type, instance_size); + Handle<Map> initial_map = NewMap(type, + instance_size, + FAST_SMI_ONLY_ELEMENTS); function->set_initial_map(*initial_map); initial_map->set_constructor(*function); } @@ -741,7 +766,10 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, // property that refers to the function. SetPrototypeProperty(function, prototype); // Currently safe because it is only invoked from Genesis. - SetLocalPropertyNoThrow(prototype, constructor_symbol(), function, DONT_ENUM); + CHECK_NOT_EMPTY_HANDLE(isolate(), + JSObject::SetLocalPropertyIgnoreAttributes( + prototype, constructor_symbol(), + function, DONT_ENUM)); return function; } @@ -749,7 +777,7 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name, Handle<Code> code) { Handle<JSFunction> function = NewFunctionWithoutPrototype(name, - kNonStrictMode); + CLASSIC_MODE); function->shared()->set_code(*code); function->set_code(*code); ASSERT(!function->has_initial_map()); @@ -758,11 +786,11 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name, } -Handle<SerializedScopeInfo> Factory::NewSerializedScopeInfo(int length) { +Handle<ScopeInfo> Factory::NewScopeInfo(int length) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->AllocateSerializedScopeInfo(length), - SerializedScopeInfo); + isolate()->heap()->AllocateScopeInfo(length), + ScopeInfo); } @@ -831,10 +859,13 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors( // Number of descriptors added to the result so far. int descriptor_count = 0; + // Ensure that marking will not progress and change color of objects. + DescriptorArray::WhitenessWitness witness(*result); + // Copy the descriptors from the array. for (int i = 0; i < array->number_of_descriptors(); i++) { - if (array->GetType(i) != NULL_DESCRIPTOR) { - result->CopyFrom(descriptor_count++, *array, i); + if (!array->IsNullDescriptor(i)) { + result->CopyFrom(descriptor_count++, *array, i, witness); } } @@ -854,7 +885,7 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors( if (result->LinearSearch(*key, descriptor_count) == DescriptorArray::kNotFound) { CallbacksDescriptor desc(*key, *entry, entry->property_attributes()); - result->Set(descriptor_count, &desc); + result->Set(descriptor_count, &desc, witness); descriptor_count++; } else { duplicates++; @@ -868,13 +899,13 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors( Handle<DescriptorArray> new_result = NewDescriptorArray(number_of_descriptors); for (int i = 0; i < number_of_descriptors; i++) { - new_result->CopyFrom(i, *result, i); + new_result->CopyFrom(i, *result, i, witness); } result = new_result; } // Sort the result before returning. - result->Sort(); + result->Sort(witness); return result; } @@ -905,21 +936,62 @@ Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map) { Handle<JSArray> Factory::NewJSArray(int capacity, + ElementsKind elements_kind, PretenureFlag pretenure) { - Handle<JSObject> obj = NewJSObject(isolate()->array_function(), pretenure); CALL_HEAP_FUNCTION(isolate(), - Handle<JSArray>::cast(obj)->Initialize(capacity), + isolate()->heap()->AllocateJSArrayAndStorage( + elements_kind, + 0, + capacity, + INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE, + pretenure), JSArray); } -Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements, +Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArrayBase> elements, + ElementsKind elements_kind, PretenureFlag pretenure) { - Handle<JSArray> result = - Handle<JSArray>::cast(NewJSObject(isolate()->array_function(), - pretenure)); - result->SetContent(*elements); - return result; + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateJSArrayWithElements(*elements, + elements_kind, + pretenure), + JSArray); +} + + +void Factory::SetElementsCapacityAndLength(Handle<JSArray> array, + int capacity, + int length) { + ElementsAccessor* accessor = array->GetElementsAccessor(); + CALL_HEAP_FUNCTION_VOID( + isolate(), + accessor->SetCapacityAndLength(*array, capacity, length)); +} + + +void Factory::SetContent(Handle<JSArray> array, + Handle<FixedArrayBase> elements) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + array->SetContent(*elements)); +} + + +void Factory::EnsureCanContainHeapObjectElements(Handle<JSArray> array) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + array->EnsureCanContainHeapObjectElements()); +} + + +void Factory::EnsureCanContainElements(Handle<JSArray> array, + Handle<FixedArrayBase> elements, + EnsureElementsMode mode) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + array->EnsureCanContainElements(*elements, mode)); } @@ -948,11 +1020,18 @@ void Factory::BecomeJSFunction(Handle<JSReceiver> object) { } +void Factory::SetIdentityHash(Handle<JSObject> object, Object* hash) { + CALL_HEAP_FUNCTION_VOID( + isolate(), + object->SetIdentityHash(hash, ALLOW_CREATION)); +} + + Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo( Handle<String> name, int number_of_literals, Handle<Code> code, - Handle<SerializedScopeInfo> scope_info) { + Handle<ScopeInfo> scope_info) { Handle<SharedFunctionInfo> shared = NewSharedFunctionInfo(name); shared->set_code(*code); shared->set_scope_info(*scope_info); @@ -1000,6 +1079,12 @@ Handle<String> Factory::NumberToString(Handle<Object> number) { } +Handle<String> Factory::Uint32ToString(uint32_t value) { + CALL_HEAP_FUNCTION(isolate(), + isolate()->heap()->Uint32ToString(value), String); +} + + Handle<SeededNumberDictionary> Factory::DictionaryAtNumberPut( Handle<SeededNumberDictionary> dictionary, uint32_t key, @@ -1042,11 +1127,11 @@ Handle<JSFunction> Factory::NewFunction(Handle<String> name, Handle<JSFunction> Factory::NewFunctionWithoutPrototypeHelper( Handle<String> name, - StrictModeFlag strict_mode) { + LanguageMode language_mode) { Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name); - Handle<Map> map = strict_mode == kStrictMode - ? isolate()->strict_mode_function_without_prototype_map() - : isolate()->function_without_prototype_map(); + Handle<Map> map = (language_mode == CLASSIC_MODE) + ? isolate()->function_without_prototype_map() + : isolate()->strict_mode_function_without_prototype_map(); CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->AllocateFunction( *map, @@ -1058,8 +1143,9 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototypeHelper( Handle<JSFunction> Factory::NewFunctionWithoutPrototype( Handle<String> name, - StrictModeFlag strict_mode) { - Handle<JSFunction> fun = NewFunctionWithoutPrototypeHelper(name, strict_mode); + LanguageMode language_mode) { + Handle<JSFunction> fun = + NewFunctionWithoutPrototypeHelper(name, language_mode); fun->set_context(isolate()->context()->global_context()); return fun; } @@ -1319,4 +1405,20 @@ void Factory::ConfigureInstance(Handle<FunctionTemplateInfo> desc, } +Handle<Object> Factory::GlobalConstantFor(Handle<String> name) { + Heap* h = isolate()->heap(); + if (name->Equals(h->undefined_symbol())) return undefined_value(); + if (name->Equals(h->nan_symbol())) return nan_value(); + if (name->Equals(h->infinity_symbol())) return infinity_value(); + return Handle<Object>::null(); +} + + +Handle<Object> Factory::ToBoolean(bool value) { + return Handle<Object>(value + ? isolate()->heap()->true_value() + : isolate()->heap()->false_value()); +} + + } } // namespace v8::internal diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index c9817fefec..121d34cfed 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -50,7 +50,7 @@ class Factory { PretenureFlag pretenure = NOT_TENURED); // Allocate a new uninitialized fixed double array. - Handle<FixedArray> NewFixedDoubleArray( + Handle<FixedDoubleArray> NewFixedDoubleArray( int size, PretenureFlag pretenure = NOT_TENURED); @@ -62,6 +62,8 @@ class Factory { Handle<StringDictionary> NewStringDictionary(int at_least_space_for); + Handle<ObjectHashSet> NewObjectHashSet(int at_least_space_for); + Handle<ObjectHashTable> NewObjectHashTable(int at_least_space_for); Handle<DescriptorArray> NewDescriptorArray(int number_of_descriptors); @@ -71,6 +73,8 @@ class Factory { Handle<DeoptimizationOutputData> NewDeoptimizationOutputData( int deopt_entry_count, PretenureFlag pretenure); + // Allocates a pre-tenured empty AccessorPair. + Handle<AccessorPair> NewAccessorPair(); Handle<String> LookupSymbol(Vector<const char> str); Handle<String> LookupSymbol(Handle<String> str); @@ -149,9 +153,9 @@ class Factory { // not make sense to have a UTF-8 factory function for external strings, // because we cannot change the underlying buffer. Handle<String> NewExternalStringFromAscii( - ExternalAsciiString::Resource* resource); + const ExternalAsciiString::Resource* resource); Handle<String> NewExternalStringFromTwoByte( - ExternalTwoByteString::Resource* resource); + const ExternalTwoByteString::Resource* resource); // Create a global (but otherwise uninitialized) context. Handle<Context> NewGlobalContext(); @@ -174,7 +178,7 @@ class Factory { // Create a 'block' context. Handle<Context> NewBlockContext(Handle<JSFunction> function, Handle<Context> previous, - Handle<SerializedScopeInfo> scope_info); + Handle<ScopeInfo> scope_info); // Return the Symbol matching the passed in string. Handle<String> SymbolFromString(Handle<String> value); @@ -207,7 +211,9 @@ class Factory { Handle<JSGlobalPropertyCell> NewJSGlobalPropertyCell( Handle<Object> value); - Handle<Map> NewMap(InstanceType type, int instance_size); + Handle<Map> NewMap(InstanceType type, + int instance_size, + ElementsKind elements_kind = FAST_ELEMENTS); Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function); @@ -219,22 +225,22 @@ class Factory { Handle<Map> CopyMapDropTransitions(Handle<Map> map); - Handle<Map> GetFastElementsMap(Handle<Map> map); - - Handle<Map> GetSlowElementsMap(Handle<Map> map); - - Handle<Map> GetElementsTransitionMap(Handle<Map> map, - ElementsKind elements_kind, - bool safe_to_add_transition); + Handle<Map> GetElementsTransitionMap(Handle<JSObject> object, + ElementsKind elements_kind); Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array); - // Numbers (eg, literals) are pretenured by the parser. + Handle<FixedDoubleArray> CopyFixedDoubleArray( + Handle<FixedDoubleArray> array); + + // Numbers (e.g. literals) are pretenured by the parser. Handle<Object> NewNumber(double value, PretenureFlag pretenure = NOT_TENURED); - Handle<Object> NewNumberFromInt(int value); - Handle<Object> NewNumberFromUint(uint32_t value); + Handle<Object> NewNumberFromInt(int32_t value, + PretenureFlag pretenure = NOT_TENURED); + Handle<Object> NewNumberFromUint(uint32_t value, + PretenureFlag pretenure = NOT_TENURED); // These objects are used by the api to create env-independent data // structures in the heap. @@ -256,24 +262,39 @@ class Factory { // JS arrays are pretenured when allocated by the parser. Handle<JSArray> NewJSArray(int capacity, + ElementsKind elements_kind = FAST_ELEMENTS, PretenureFlag pretenure = NOT_TENURED); Handle<JSArray> NewJSArrayWithElements( - Handle<FixedArray> elements, + Handle<FixedArrayBase> elements, + ElementsKind elements_kind = FAST_ELEMENTS, PretenureFlag pretenure = NOT_TENURED); + void SetElementsCapacityAndLength(Handle<JSArray> array, + int capacity, + int length); + + void SetContent(Handle<JSArray> array, Handle<FixedArrayBase> elements); + + void EnsureCanContainHeapObjectElements(Handle<JSArray> array); + void EnsureCanContainElements(Handle<JSArray> array, + Handle<FixedArrayBase> elements, + EnsureElementsMode mode); + Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype); // Change the type of the argument into a JS object/function and reinitialize. void BecomeJSObject(Handle<JSReceiver> object); void BecomeJSFunction(Handle<JSReceiver> object); + void SetIdentityHash(Handle<JSObject> object, Object* hash); + Handle<JSFunction> NewFunction(Handle<String> name, Handle<Object> prototype); Handle<JSFunction> NewFunctionWithoutPrototype( Handle<String> name, - StrictModeFlag strict_mode); + LanguageMode language_mode); Handle<JSFunction> NewFunction(Handle<Object> super, bool is_global); @@ -287,7 +308,7 @@ class Factory { Handle<Context> context, PretenureFlag pretenure = TENURED); - Handle<SerializedScopeInfo> NewSerializedScopeInfo(int length); + Handle<ScopeInfo> NewScopeInfo(int length); Handle<Code> NewCode(const CodeDesc& desc, Code::Flags flags, @@ -360,6 +381,7 @@ class Factory { PropertyAttributes attributes); Handle<String> NumberToString(Handle<Object> number); + Handle<String> Uint32ToString(uint32_t value); enum ApiInstanceType { JavaScriptObject, @@ -404,7 +426,7 @@ class Factory { Handle<String> name, int number_of_literals, Handle<Code> code, - Handle<SerializedScopeInfo> scope_info); + Handle<ScopeInfo> scope_info); Handle<SharedFunctionInfo> NewSharedFunctionInfo(Handle<String> name); Handle<JSMessageObject> NewJSMessageObject( @@ -451,6 +473,14 @@ class Factory { JSRegExp::Flags flags, int capture_count); + // Returns the value for a known global constant (a property of the global + // object which is neither configurable nor writable) like 'undefined'. + // Returns a null handle when the given name is unknown. + Handle<Object> GlobalConstantFor(Handle<String> name); + + // Converts the given boolean condition to JavaScript boolean value. + Handle<Object> ToBoolean(bool value); + private: Isolate* isolate() { return reinterpret_cast<Isolate*>(this); } @@ -459,7 +489,7 @@ class Factory { Handle<JSFunction> NewFunctionWithoutPrototypeHelper( Handle<String> name, - StrictModeFlag strict_mode); + LanguageMode language_mode); Handle<DescriptorArray> CopyAppendCallbackDescriptors( Handle<DescriptorArray> array, diff --git a/deps/v8/src/fast-dtoa.h b/deps/v8/src/fast-dtoa.h index 94c22ecd7c..ef28557934 100644 --- a/deps/v8/src/fast-dtoa.h +++ b/deps/v8/src/fast-dtoa.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -43,7 +43,7 @@ enum FastDtoaMode { // FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not // include the terminating '\0' character. -static const int kFastDtoaMaximalLength = 17; +const int kFastDtoaMaximalLength = 17; // Provides a decimal representation of v. // The result should be interpreted as buffer * 10^(point - length). diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index e8f6349e0e..59e54dd354 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -41,6 +41,7 @@ extern ctype FLAG_##nam; #define FLAG_READONLY(ftype, ctype, nam, def, cmt) \ static ctype const FLAG_##nam = def; +#define DEFINE_implication(whenflag, thenflag) // We want to supply the actual storage and value for the flag variable in the // .cc file. We only do this for writable flags. @@ -48,6 +49,7 @@ #define FLAG_FULL(ftype, ctype, nam, def, cmt) \ ctype FLAG_##nam = def; #define FLAG_READONLY(ftype, ctype, nam, def, cmt) +#define DEFINE_implication(whenflag, thenflag) // We need to define all of our default values so that the Flag structure can // access them by pointer. These are just used internally inside of one .cc, @@ -56,7 +58,7 @@ #define FLAG_FULL(ftype, ctype, nam, def, cmt) \ static ctype const FLAGDEFAULT_##nam = def; #define FLAG_READONLY(ftype, ctype, nam, def, cmt) - +#define DEFINE_implication(whenflag, thenflag) // We want to write entries into our meta data table, for internal parsing and // printing / etc in the flag parser code. We only do this for writable flags. @@ -64,6 +66,14 @@ #define FLAG_FULL(ftype, ctype, nam, def, cmt) \ { Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt, false }, #define FLAG_READONLY(ftype, ctype, nam, def, cmt) +#define DEFINE_implication(whenflag, thenflag) + +// We produce the code to set flags when it is implied by another flag. +#elif defined(FLAG_MODE_DEFINE_IMPLICATIONS) +#define FLAG_FULL(ftype, ctype, nam, def, cmt) +#define FLAG_READONLY(ftype, ctype, nam, def, cmt) +#define DEFINE_implication(whenflag, thenflag) \ + if (FLAG_##whenflag) FLAG_##thenflag = true; #else #error No mode supplied when including flags.defs @@ -98,33 +108,40 @@ private: // Flags for experimental language features. DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof") +DEFINE_bool(harmony_scoping, false, "enable harmony block scoping") +DEFINE_bool(harmony_modules, false, "enable harmony modules") DEFINE_bool(harmony_proxies, false, "enable harmony proxies") -DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps") -DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping") +DEFINE_bool(harmony_collections, false, + "enable harmony collections (sets, maps, and weak maps)") +DEFINE_bool(harmony, false, "enable all harmony features (except typeof)") +DEFINE_implication(harmony, harmony_scoping) +DEFINE_implication(harmony, harmony_modules) +DEFINE_implication(harmony, harmony_proxies) +DEFINE_implication(harmony, harmony_collections) // Flags for experimental implementation features. +DEFINE_bool(smi_only_arrays, false, "tracks arrays with only smi values") +DEFINE_bool(clever_optimizations, + true, + "Optimize object size, Array shift, DOM strings and string +") + +// Flags for data representation optimizations DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles") -DEFINE_bool(string_slices, false, "use string slices") +DEFINE_bool(string_slices, true, "use string slices") // Flags for Crankshaft. -#ifdef V8_TARGET_ARCH_MIPS - DEFINE_bool(crankshaft, false, "use crankshaft") -#else - DEFINE_bool(crankshaft, true, "use crankshaft") -#endif +DEFINE_bool(crankshaft, true, "use crankshaft") DEFINE_string(hydrogen_filter, "", "hydrogen use/trace filter") -DEFINE_bool(use_hydrogen, true, "use generated hydrogen for compilation") -DEFINE_bool(build_lithium, true, "use lithium chunk builder") -DEFINE_bool(alloc_lithium, true, "use lithium register allocator") -DEFINE_bool(use_lithium, true, "use lithium code generator") DEFINE_bool(use_range, true, "use hydrogen range analysis") DEFINE_bool(eliminate_dead_phis, true, "eliminate dead phis") DEFINE_bool(use_gvn, true, "use hydrogen global value numbering") DEFINE_bool(use_canonicalizing, true, "use hydrogen instruction canonicalizing") DEFINE_bool(use_inlining, true, "use function inlining") DEFINE_bool(limit_inlining, true, "limit code size growth from inlining") -DEFINE_bool(eliminate_empty_blocks, true, "eliminate empty blocks") DEFINE_bool(loop_invariant_code_motion, true, "loop invariant code motion") +DEFINE_bool(collect_megamorphic_maps_from_stub_cache, + true, + "crankshaft harvests type feedback from stub cache") DEFINE_bool(hydrogen_stats, false, "print statistics for hydrogen") DEFINE_bool(trace_hydrogen, false, "trace generated hydrogen to file") DEFINE_bool(trace_inlining, false, "trace inlining decisions") @@ -146,13 +163,21 @@ DEFINE_bool(use_osr, true, "use on-stack replacement") DEFINE_bool(trace_osr, false, "trace on-stack replacement") DEFINE_int(stress_runs, 0, "number of stress runs") DEFINE_bool(optimize_closures, true, "optimize closures") +DEFINE_int(loop_weight, 1, "loop weight for representation inference") + +// Experimental profiler changes. +DEFINE_bool(experimental_profiler, false, "enable all profiler experiments") +DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability") +DEFINE_bool(self_optimization, false, + "primitive functions trigger their own optimization") + +DEFINE_implication(experimental_profiler, watch_ic_patching) +DEFINE_implication(experimental_profiler, self_optimization) // assembler-ia32.cc / assembler-arm.cc / assembler-x64.cc DEFINE_bool(debug_code, false, "generate extra code (assertions) for debugging") DEFINE_bool(code_comments, false, "emit comments in code disassembly") -DEFINE_bool(peephole_optimization, true, - "perform peephole optimizations in assembly code") DEFINE_bool(enable_sse2, true, "enable use of SSE2 instructions if available") DEFINE_bool(enable_sse3, true, @@ -180,6 +205,8 @@ DEFINE_bool(expose_gc, false, "expose gc extension") DEFINE_bool(expose_externalize_string, false, "expose externalize string extension") DEFINE_int(stack_trace_limit, 10, "number of stack frames to capture") +DEFINE_bool(builtins_in_stack_traces, false, + "show built-in functions in stack traces") DEFINE_bool(disable_native_files, false, "disable builtin natives files") // builtins-ia32.cc @@ -200,10 +227,8 @@ DEFINE_bool(lazy, true, "use lazy compilation") DEFINE_bool(trace_opt, false, "trace lazy optimization") DEFINE_bool(trace_opt_stats, false, "trace lazy optimization statistics") DEFINE_bool(opt, true, "use adaptive optimizations") -DEFINE_bool(opt_eagerly, false, "be more eager when adaptively optimizing") DEFINE_bool(always_opt, false, "always try to optimize functions") DEFINE_bool(prepare_always_opt, false, "prepare for turning on always opt") -DEFINE_bool(deopt, true, "support deoptimization") DEFINE_bool(trace_deopt, false, "trace deoptimization") // compiler.cc @@ -228,7 +253,7 @@ DEFINE_bool(enable_liveedit, true, "enable liveedit experimental feature") // execution.cc DEFINE_int(stack_size, kPointerSize * 128, - "default size of stack region v8 is allowed to use (in KkBytes)") + "default size of stack region v8 is allowed to use (in kBytes)") // frames.cc DEFINE_int(max_stack_trace_source_length, 300, @@ -253,10 +278,16 @@ DEFINE_bool(print_cumulative_gc_stat, false, "print cumulative GC statistics in name=value format on exit") DEFINE_bool(trace_gc_verbose, false, "print more details following each garbage collection") +DEFINE_bool(trace_fragmentation, false, + "report fragmentation for old pointer and data pages") DEFINE_bool(collect_maps, true, "garbage collect maps from which no objects can be reached") DEFINE_bool(flush_code, true, "flush code that we expect not to use again before full gc") +DEFINE_bool(incremental_marking, true, "use incremental marking") +DEFINE_bool(incremental_marking_steps, true, "do incremental marking steps") +DEFINE_bool(trace_incremental_marking, false, + "trace progress of the incremental marking") // v8.cc DEFINE_bool(use_idle_notification, true, @@ -276,8 +307,12 @@ DEFINE_bool(native_code_counters, false, // mark-compact.cc DEFINE_bool(always_compact, false, "Perform compaction on every full GC") +DEFINE_bool(lazy_sweeping, true, + "Use lazy sweeping for old pointer and data spaces") DEFINE_bool(never_compact, false, "Never perform compaction on full GC - testing only") +DEFINE_bool(compact_code_space, true, + "Compact code space on full non-incremental collections") DEFINE_bool(cleanup_code_caches_at_gc, true, "Flush inline caches prior to mark compact collection and " "flush code caches in maps during mark compact cycle.") @@ -285,31 +320,16 @@ DEFINE_int(random_seed, 0, "Default seed for initializing random generator " "(0, the default, means to use system random).") -DEFINE_bool(canonicalize_object_literal_maps, true, - "Canonicalize maps for object literals.") - -DEFINE_bool(use_big_map_space, true, - "Use big map space, but don't compact if it grew too big.") - -DEFINE_int(max_map_space_pages, MapSpace::kMaxMapPageIndex - 1, - "Maximum number of pages in map space which still allows to encode " - "forwarding pointers. That's actually a constant, but it's useful " - "to control it with a flag for better testing.") - -// mksnapshot.cc -DEFINE_bool(h, false, "print this message") -DEFINE_bool(new_snapshot, true, "use new snapshot implementation") - // objects.cc DEFINE_bool(use_verbose_printer, true, "allows verbose printing") // parser.cc DEFINE_bool(allow_natives_syntax, false, "allow natives syntax") -DEFINE_bool(strict_mode, true, "allow strict mode directives") // simulator-arm.cc and simulator-mips.cc DEFINE_bool(trace_sim, false, "Trace simulator execution") -DEFINE_bool(check_icache, false, "Check icache flushes in ARM simulator") +DEFINE_bool(check_icache, false, + "Check icache flushes in ARM and MIPS simulator") DEFINE_int(stop_sim_at, 0, "Simulator stop after x number of instructions") DEFINE_int(sim_stack_alignment, 8, "Stack alingment in bytes in simulator (4 or 8, 8 is default)") @@ -334,7 +354,6 @@ DEFINE_bool(preemption, false, // Regexp DEFINE_bool(regexp_optimization, true, "generate optimized regexp code") -DEFINE_bool(regexp_entry_native, true, "use native code to enter regexp") // Testing flags test/cctest/test-{flags,api,serialization}.cc DEFINE_bool(testing_bool_flag, true, "testing_bool_flag") @@ -356,11 +375,15 @@ DEFINE_string(testing_serialization_file, "/tmp/serdes", DEFINE_bool(help, false, "Print usage message, including flags, on console") DEFINE_bool(dump_counters, false, "Dump counters on exit") + +#ifdef ENABLE_DEBUGGER_SUPPORT DEFINE_bool(debugger, false, "Enable JavaScript debugger") DEFINE_bool(remote_debugger, false, "Connect JavaScript debugger to the " "debugger agent in another process") DEFINE_bool(debugger_agent, false, "Enable debugger agent") DEFINE_int(debugger_port, 5858, "Port to use for remote debugging") +#endif // ENABLE_DEBUGGER_SUPPORT + DEFINE_string(map_counters, "", "Map counters to a file") DEFINE_args(js_arguments, JSArguments(), "Pass all remaining arguments to the script. Alias for \"--\".") @@ -386,6 +409,15 @@ DEFINE_bool(gdbjit_dump, false, "dump elf objects with debug info to disk") DEFINE_string(gdbjit_dump_filter, "", "dump only objects containing this substring") +// mark-compact.cc +DEFINE_bool(force_marking_deque_overflows, false, + "force overflows of marking deque by reducing it's size " + "to 64 words") + +DEFINE_bool(stress_compaction, false, + "stress the GC compactor to flush out bugs (implies " + "--force_marking_deque_overflows)") + // // Debug only flags // @@ -408,11 +440,7 @@ DEFINE_bool(print_builtin_source, false, "pretty print source code for builtins") DEFINE_bool(print_ast, false, "print source AST") DEFINE_bool(print_builtin_ast, false, "print source AST for builtins") -DEFINE_bool(print_json_ast, false, "print source AST as JSON") -DEFINE_bool(print_builtin_json_ast, false, - "print source AST for builtins as JSON") DEFINE_string(stop_at, "", "function name where to insert a breakpoint") -DEFINE_bool(verify_stack_height, false, "verify stack height tracing on ia32") // compiler.cc DEFINE_bool(print_builtin_scopes, false, "print scopes for builtins") @@ -441,10 +469,6 @@ DEFINE_bool(trace_normalization, // runtime.cc DEFINE_bool(trace_lazy, false, "trace lazy compilation") -// serialize.cc -DEFINE_bool(debug_serialization, false, - "write debug information into the snapshot.") - // spaces.cc DEFINE_bool(collect_heap_spill_statistics, false, "report heap spill statistics along with heap_stats " @@ -509,6 +533,9 @@ DEFINE_bool(ll_prof, false, "Enable low-level linux profiler.") #define FLAG FLAG_READONLY #endif +// elements.cc +DEFINE_bool(trace_elements_transitions, false, "trace elements transitions") + // code-stubs.cc DEFINE_bool(print_code_stubs, false, "print code stubs") @@ -520,6 +547,20 @@ DEFINE_bool(print_unopt_code, false, "print unoptimized code before " DEFINE_bool(print_code_verbose, false, "print more information for code") DEFINE_bool(print_builtin_code, false, "print generated code for builtins") +#ifdef ENABLE_DISASSEMBLER +DEFINE_bool(print_all_code, false, "enable all flags related to printing code") +DEFINE_implication(print_all_code, print_code) +DEFINE_implication(print_all_code, print_opt_code) +DEFINE_implication(print_all_code, print_unopt_code) +DEFINE_implication(print_all_code, print_code_verbose) +DEFINE_implication(print_all_code, print_builtin_code) +DEFINE_implication(print_all_code, print_code_stubs) +DEFINE_implication(print_all_code, code_comments) +#ifdef DEBUG +DEFINE_implication(print_all_code, trace_codegen) +#endif +#endif + // Cleanup... #undef FLAG_FULL #undef FLAG_READONLY @@ -528,8 +569,10 @@ DEFINE_bool(print_builtin_code, false, "print generated code for builtins") #undef DEFINE_bool #undef DEFINE_int #undef DEFINE_string +#undef DEFINE_implication #undef FLAG_MODE_DECLARE #undef FLAG_MODE_DEFINE #undef FLAG_MODE_DEFINE_DEFAULTS #undef FLAG_MODE_META +#undef FLAG_MODE_DEFINE_IMPLICATIONS diff --git a/deps/v8/src/flags.cc b/deps/v8/src/flags.cc index ab5b57cedc..75e66ce34d 100644 --- a/deps/v8/src/flags.cc +++ b/deps/v8/src/flags.cc @@ -548,4 +548,9 @@ JSArguments& JSArguments::operator=(JSArguments args) { } +void FlagList::EnforceFlagImplications() { +#define FLAG_MODE_DEFINE_IMPLICATIONS +#include "flag-definitions.h" +} + } } // namespace v8::internal diff --git a/deps/v8/src/flags.h b/deps/v8/src/flags.h index f9cbde0bf7..f0b239b6f2 100644 --- a/deps/v8/src/flags.h +++ b/deps/v8/src/flags.h @@ -72,6 +72,9 @@ class FlagList { // Print help to stdout with flags, types, and default values. static void PrintHelp(); + + // Set flags as consequence of being implied by another flag. + static void EnforceFlagImplications(); }; } } // namespace v8::internal diff --git a/deps/v8/src/frames-inl.h b/deps/v8/src/frames-inl.h index 7ba79bf1b5..010233a0fa 100644 --- a/deps/v8/src/frames-inl.h +++ b/deps/v8/src/frames-inl.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -68,7 +68,7 @@ inline bool StackHandler::includes(Address address) const { inline void StackHandler::Iterate(ObjectVisitor* v, Code* holder) const { v->VisitPointer(context_address()); - StackFrame::IteratePc(v, pc_address(), holder); + v->VisitPointer(code_address()); } @@ -77,9 +77,24 @@ inline StackHandler* StackHandler::FromAddress(Address address) { } -inline StackHandler::State StackHandler::state() const { +inline bool StackHandler::is_js_entry() const { + return kind() == JS_ENTRY; +} + + +inline bool StackHandler::is_catch() const { + return kind() == CATCH; +} + + +inline bool StackHandler::is_finally() const { + return kind() == FINALLY; +} + + +inline StackHandler::Kind StackHandler::kind() const { const int offset = StackHandlerConstants::kStateOffset; - return static_cast<State>(Memory::int_at(address() + offset)); + return KindField::decode(Memory::unsigned_at(address() + offset)); } @@ -89,9 +104,9 @@ inline Object** StackHandler::context_address() const { } -inline Address* StackHandler::pc_address() const { - const int offset = StackHandlerConstants::kPCOffset; - return reinterpret_cast<Address*>(address() + offset); +inline Object** StackHandler::code_address() const { + const int offset = StackHandlerConstants::kCodeOffset; + return reinterpret_cast<Object**>(address() + offset); } @@ -105,8 +120,33 @@ inline StackHandler* StackFrame::top_handler() const { } +inline Code* StackFrame::LookupCode() const { + return GetContainingCode(isolate(), pc()); +} + + inline Code* StackFrame::GetContainingCode(Isolate* isolate, Address pc) { - return isolate->pc_to_code_cache()->GetCacheEntry(pc)->code; + return isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code; +} + + +inline EntryFrame::EntryFrame(StackFrameIterator* iterator) + : StackFrame(iterator) { +} + + +inline EntryConstructFrame::EntryConstructFrame(StackFrameIterator* iterator) + : EntryFrame(iterator) { +} + + +inline ExitFrame::ExitFrame(StackFrameIterator* iterator) + : StackFrame(iterator) { +} + + +inline StandardFrame::StandardFrame(StackFrameIterator* iterator) + : StackFrame(iterator) { } @@ -155,6 +195,11 @@ inline bool StandardFrame::IsConstructFrame(Address fp) { } +inline JavaScriptFrame::JavaScriptFrame(StackFrameIterator* iterator) + : StandardFrame(iterator) { +} + + Address JavaScriptFrame::GetParameterSlot(int index) const { int param_count = ComputeParametersCount(); ASSERT(-1 <= index && index < param_count); @@ -190,6 +235,26 @@ inline Object* JavaScriptFrame::function() const { } +inline OptimizedFrame::OptimizedFrame(StackFrameIterator* iterator) + : JavaScriptFrame(iterator) { +} + + +inline ArgumentsAdaptorFrame::ArgumentsAdaptorFrame( + StackFrameIterator* iterator) : JavaScriptFrame(iterator) { +} + + +inline InternalFrame::InternalFrame(StackFrameIterator* iterator) + : StandardFrame(iterator) { +} + + +inline ConstructFrame::ConstructFrame(StackFrameIterator* iterator) + : InternalFrame(iterator) { +} + + template<typename Iterator> inline JavaScriptFrameIteratorTemp<Iterator>::JavaScriptFrameIteratorTemp( Isolate* isolate) @@ -197,6 +262,15 @@ inline JavaScriptFrameIteratorTemp<Iterator>::JavaScriptFrameIteratorTemp( if (!done()) Advance(); } + +template<typename Iterator> +inline JavaScriptFrameIteratorTemp<Iterator>::JavaScriptFrameIteratorTemp( + Isolate* isolate, ThreadLocalTop* top) + : iterator_(isolate, top) { + if (!done()) Advance(); +} + + template<typename Iterator> inline JavaScriptFrame* JavaScriptFrameIteratorTemp<Iterator>::frame() const { // TODO(1233797): The frame hierarchy needs to change. It's diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index 60b1aadfca..40df12c437 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -366,16 +366,17 @@ void SafeStackTraceFrameIterator::Advance() { Code* StackFrame::GetSafepointData(Isolate* isolate, - Address pc, + Address inner_pointer, SafepointEntry* safepoint_entry, unsigned* stack_slots) { - PcToCodeCache::PcToCodeCacheEntry* entry = - isolate->pc_to_code_cache()->GetCacheEntry(pc); + InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry = + isolate->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer); if (!entry->safepoint_entry.is_valid()) { - entry->safepoint_entry = entry->code->GetSafepointEntry(pc); + entry->safepoint_entry = entry->code->GetSafepointEntry(inner_pointer); ASSERT(entry->safepoint_entry.is_valid()); } else { - ASSERT(entry->safepoint_entry.Equals(entry->code->GetSafepointEntry(pc))); + ASSERT(entry->safepoint_entry.Equals( + entry->code->GetSafepointEntry(inner_pointer))); } // Fill in the results and return the code. @@ -392,11 +393,16 @@ bool StackFrame::HasHandler() const { } +#ifdef DEBUG +static bool GcSafeCodeContains(HeapObject* object, Address addr); +#endif + + void StackFrame::IteratePc(ObjectVisitor* v, Address* pc_address, Code* holder) { Address pc = *pc_address; - ASSERT(holder->contains(pc)); + ASSERT(GcSafeCodeContains(holder, pc)); unsigned pc_offset = static_cast<unsigned>(pc - holder->instruction_start()); Object* code = holder; v->VisitPointer(&code); @@ -479,7 +485,7 @@ Code* ExitFrame::unchecked_code() const { void ExitFrame::ComputeCallerState(State* state) const { - // Setup the caller state. + // Set up the caller state. state->sp = caller_sp(); state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset); state->pc_address @@ -705,6 +711,74 @@ void JavaScriptFrame::Summarize(List<FrameSummary>* functions) { } +void JavaScriptFrame::PrintTop(FILE* file, + bool print_args, + bool print_line_number) { + // constructor calls + HandleScope scope; + AssertNoAllocation no_allocation; + JavaScriptFrameIterator it; + while (!it.done()) { + if (it.frame()->is_java_script()) { + JavaScriptFrame* frame = it.frame(); + if (frame->IsConstructor()) PrintF(file, "new "); + // function name + Object* maybe_fun = frame->function(); + if (maybe_fun->IsJSFunction()) { + JSFunction* fun = JSFunction::cast(maybe_fun); + fun->PrintName(); + Code* js_code = frame->unchecked_code(); + Address pc = frame->pc(); + int code_offset = + static_cast<int>(pc - js_code->instruction_start()); + PrintF("+%d", code_offset); + SharedFunctionInfo* shared = fun->shared(); + if (print_line_number) { + Code* code = Code::cast( + v8::internal::Isolate::Current()->heap()->FindCodeObject(pc)); + int source_pos = code->SourcePosition(pc); + Object* maybe_script = shared->script(); + if (maybe_script->IsScript()) { + Handle<Script> script(Script::cast(maybe_script)); + int line = GetScriptLineNumberSafe(script, source_pos) + 1; + Object* script_name_raw = script->name(); + if (script_name_raw->IsString()) { + String* script_name = String::cast(script->name()); + SmartArrayPointer<char> c_script_name = + script_name->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL); + PrintF(file, " at %s:%d", *c_script_name, line); + } else { + PrintF(file, "at <unknown>:%d", line); + } + } else { + PrintF(file, " at <unknown>:<unknown>"); + } + } + } else { + PrintF("<unknown>"); + } + + if (print_args) { + // function arguments + // (we are intentionally only printing the actually + // supplied parameters, not all parameters required) + PrintF(file, "(this="); + frame->receiver()->ShortPrint(file); + const int length = frame->ComputeParametersCount(); + for (int i = 0; i < length; i++) { + PrintF(file, ", "); + frame->GetParameter(i)->ShortPrint(file); + } + PrintF(file, ")"); + } + break; + } + it.Advance(); + } +} + + void FrameSummary::Print() { PrintF("receiver: "); receiver_->ShortPrint(); @@ -739,17 +813,18 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) { data->TranslationIndex(deopt_index)->value()); Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); ASSERT(opcode == Translation::BEGIN); - int frame_count = it.Next(); + it.Next(); // Drop frame count. + int jsframe_count = it.Next(); // We create the summary in reverse order because the frames // in the deoptimization translation are ordered bottom-to-top. - int i = frame_count; + int i = jsframe_count; while (i > 0) { opcode = static_cast<Translation::Opcode>(it.Next()); - if (opcode == Translation::FRAME) { + if (opcode == Translation::JS_FRAME) { // We don't inline constructor calls, so only the first, outermost // frame can be a constructor frame in case of inlining. - bool is_constructor = (i == frame_count) && IsConstructor(); + bool is_constructor = (i == jsframe_count) && IsConstructor(); i--; int ast_id = it.Next(); @@ -819,7 +894,8 @@ DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData( // back to a slow search in this case to find the original optimized // code object. if (!code->contains(pc())) { - code = isolate()->pc_to_code_cache()->GcSafeFindCodeForPc(pc()); + code = isolate()->inner_pointer_to_code_cache()-> + GcSafeFindCodeForInnerPointer(pc()); } ASSERT(code != NULL); ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); @@ -843,8 +919,9 @@ int OptimizedFrame::GetInlineCount() { Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); ASSERT(opcode == Translation::BEGIN); USE(opcode); - int frame_count = it.Next(); - return frame_count; + it.Next(); // Drop frame count. + int jsframe_count = it.Next(); + return jsframe_count; } @@ -859,14 +936,15 @@ void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) { data->TranslationIndex(deopt_index)->value()); Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); ASSERT(opcode == Translation::BEGIN); - int frame_count = it.Next(); + it.Next(); // Drop frame count. + int jsframe_count = it.Next(); // We insert the frames in reverse order because the frames // in the deoptimization translation are ordered bottom-to-top. - while (frame_count > 0) { + while (jsframe_count > 0) { opcode = static_cast<Translation::Opcode>(it.Next()); - if (opcode == Translation::FRAME) { - frame_count--; + if (opcode == Translation::JS_FRAME) { + jsframe_count--; it.Next(); // Skip ast id. int function_id = it.Next(); it.Next(); // Skip height. @@ -881,6 +959,11 @@ void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) { } +int ArgumentsAdaptorFrame::GetNumberOfIncomingArguments() const { + return Smi::cast(GetExpression(0))->value(); +} + + Address ArgumentsAdaptorFrame::GetCallerStackPointer() const { return fp() + StandardFrameConstants::kCallerSPOffset; } @@ -927,11 +1010,15 @@ void JavaScriptFrame::Print(StringStream* accumulator, if (IsConstructor()) accumulator->Add("new "); accumulator->PrintFunction(function, receiver, &code); - Handle<SerializedScopeInfo> scope_info(SerializedScopeInfo::Empty()); + // Get scope information for nicer output, if possible. If code is NULL, or + // doesn't contain scope info, scope_info will return 0 for the number of + // parameters, stack local variables, context local variables, stack slots, + // or context slots. + Handle<ScopeInfo> scope_info(ScopeInfo::Empty()); if (function->IsJSFunction()) { Handle<SharedFunctionInfo> shared(JSFunction::cast(function)->shared()); - scope_info = Handle<SerializedScopeInfo>(shared->scope_info()); + scope_info = Handle<ScopeInfo>(shared->scope_info()); Object* script_obj = shared->script(); if (script_obj->IsScript()) { Handle<Script> script(Script::cast(script_obj)); @@ -956,11 +1043,6 @@ void JavaScriptFrame::Print(StringStream* accumulator, accumulator->Add("(this=%o", receiver); - // Get scope information for nicer output, if possible. If code is - // NULL, or doesn't contain scope info, info will return 0 for the - // number of parameters, stack slots, or context slots. - ScopeInfo<PreallocatedStorage> info(*scope_info); - // Print the parameters. int parameters_count = ComputeParametersCount(); for (int i = 0; i < parameters_count; i++) { @@ -968,8 +1050,8 @@ void JavaScriptFrame::Print(StringStream* accumulator, // If we have a name for the parameter we print it. Nameless // parameters are either because we have more actual parameters // than formal parameters or because we have no scope information. - if (i < info.number_of_parameters()) { - accumulator->PrintName(*info.parameter_name(i)); + if (i < scope_info->ParameterCount()) { + accumulator->PrintName(scope_info->ParameterName(i)); accumulator->Add("="); } accumulator->Add("%o", GetParameter(i)); @@ -987,8 +1069,8 @@ void JavaScriptFrame::Print(StringStream* accumulator, accumulator->Add(" {\n"); // Compute the number of locals and expression stack elements. - int stack_locals_count = info.number_of_stack_slots(); - int heap_locals_count = info.number_of_context_slots(); + int stack_locals_count = scope_info->StackLocalCount(); + int heap_locals_count = scope_info->ContextLocalCount(); int expressions_count = ComputeExpressionsCount(); // Print stack-allocated local variables. @@ -997,7 +1079,7 @@ void JavaScriptFrame::Print(StringStream* accumulator, } for (int i = 0; i < stack_locals_count; i++) { accumulator->Add(" var "); - accumulator->PrintName(*info.stack_slot_name(i)); + accumulator->PrintName(scope_info->StackLocalName(i)); accumulator->Add(" = "); if (i < expressions_count) { accumulator->Add("%o", GetExpression(i)); @@ -1014,16 +1096,16 @@ void JavaScriptFrame::Print(StringStream* accumulator, } // Print heap-allocated local variables. - if (heap_locals_count > Context::MIN_CONTEXT_SLOTS) { + if (heap_locals_count > 0) { accumulator->Add(" // heap-allocated locals\n"); } - for (int i = Context::MIN_CONTEXT_SLOTS; i < heap_locals_count; i++) { + for (int i = 0; i < heap_locals_count; i++) { accumulator->Add(" var "); - accumulator->PrintName(*info.context_slot_name(i)); + accumulator->PrintName(scope_info->ContextLocalName(i)); accumulator->Add(" = "); if (context != NULL) { if (i < context->length()) { - accumulator->Add("%o", context->get(i)); + accumulator->Add("%o", context->get(Context::MIN_CONTEXT_SLOTS + i)); } else { accumulator->Add( "// warning: missing context slot - inconsistent frame?"); @@ -1092,7 +1174,7 @@ void EntryFrame::Iterate(ObjectVisitor* v) const { StackHandlerIterator it(this, top_handler()); ASSERT(!it.done()); StackHandler* handler = it.handler(); - ASSERT(handler->is_entry()); + ASSERT(handler->is_js_entry()); handler->Iterate(v, LookupCode()); #ifdef DEBUG // Make sure that the entry frame does not contain more than one @@ -1155,53 +1237,90 @@ JavaScriptFrame* StackFrameLocator::FindJavaScriptFrame(int n) { // ------------------------------------------------------------------------- -Code* PcToCodeCache::GcSafeCastToCode(HeapObject* object, Address pc) { +static Map* GcSafeMapOfCodeSpaceObject(HeapObject* object) { + MapWord map_word = object->map_word(); + return map_word.IsForwardingAddress() ? + map_word.ToForwardingAddress()->map() : map_word.ToMap(); +} + + +static int GcSafeSizeOfCodeSpaceObject(HeapObject* object) { + return object->SizeFromMap(GcSafeMapOfCodeSpaceObject(object)); +} + + +#ifdef DEBUG +static bool GcSafeCodeContains(HeapObject* code, Address addr) { + Map* map = GcSafeMapOfCodeSpaceObject(code); + ASSERT(map == code->GetHeap()->code_map()); + Address start = code->address(); + Address end = code->address() + code->SizeFromMap(map); + return start <= addr && addr < end; +} +#endif + + +Code* InnerPointerToCodeCache::GcSafeCastToCode(HeapObject* object, + Address inner_pointer) { Code* code = reinterpret_cast<Code*>(object); - ASSERT(code != NULL && code->contains(pc)); + ASSERT(code != NULL && GcSafeCodeContains(code, inner_pointer)); return code; } -Code* PcToCodeCache::GcSafeFindCodeForPc(Address pc) { +Code* InnerPointerToCodeCache::GcSafeFindCodeForInnerPointer( + Address inner_pointer) { Heap* heap = isolate_->heap(); - // Check if the pc points into a large object chunk. - LargeObjectChunk* chunk = heap->lo_space()->FindChunkContainingPc(pc); - if (chunk != NULL) return GcSafeCastToCode(chunk->GetObject(), pc); - - // Iterate through the 8K page until we reach the end or find an - // object starting after the pc. - Page* page = Page::FromAddress(pc); - HeapObjectIterator iterator(page, heap->GcSafeSizeOfOldObjectFunction()); - HeapObject* previous = NULL; + // Check if the inner pointer points into a large object chunk. + LargePage* large_page = heap->lo_space()->FindPageContainingPc(inner_pointer); + if (large_page != NULL) { + return GcSafeCastToCode(large_page->GetObject(), inner_pointer); + } + + // Iterate through the page until we reach the end or find an object starting + // after the inner pointer. + Page* page = Page::FromAddress(inner_pointer); + + Address addr = page->skip_list()->StartFor(inner_pointer); + + Address top = heap->code_space()->top(); + Address limit = heap->code_space()->limit(); + while (true) { - HeapObject* next = iterator.next(); - if (next == NULL || next->address() >= pc) { - return GcSafeCastToCode(previous, pc); + if (addr == top && addr != limit) { + addr = limit; + continue; } - previous = next; + + HeapObject* obj = HeapObject::FromAddress(addr); + int obj_size = GcSafeSizeOfCodeSpaceObject(obj); + Address next_addr = addr + obj_size; + if (next_addr > inner_pointer) return GcSafeCastToCode(obj, inner_pointer); + addr = next_addr; } } -PcToCodeCache::PcToCodeCacheEntry* PcToCodeCache::GetCacheEntry(Address pc) { +InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* + InnerPointerToCodeCache::GetCacheEntry(Address inner_pointer) { isolate_->counters()->pc_to_code()->Increment(); - ASSERT(IsPowerOf2(kPcToCodeCacheSize)); + ASSERT(IsPowerOf2(kInnerPointerToCodeCacheSize)); uint32_t hash = ComputeIntegerHash( - static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc)), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(inner_pointer)), v8::internal::kZeroHashSeed); - uint32_t index = hash & (kPcToCodeCacheSize - 1); - PcToCodeCacheEntry* entry = cache(index); - if (entry->pc == pc) { + uint32_t index = hash & (kInnerPointerToCodeCacheSize - 1); + InnerPointerToCodeCacheEntry* entry = cache(index); + if (entry->inner_pointer == inner_pointer) { isolate_->counters()->pc_to_code_cached()->Increment(); - ASSERT(entry->code == GcSafeFindCodeForPc(pc)); + ASSERT(entry->code == GcSafeFindCodeForInnerPointer(inner_pointer)); } else { // Because this code may be interrupted by a profiling signal that - // also queries the cache, we cannot update pc before the code has - // been set. Otherwise, we risk trying to use a cache entry before + // also queries the cache, we cannot update inner_pointer before the code + // has been set. Otherwise, we risk trying to use a cache entry before // the code has been computed. - entry->code = GcSafeFindCodeForPc(pc); + entry->code = GcSafeFindCodeForInnerPointer(inner_pointer); entry->safepoint_entry.Reset(); - entry->pc = pc; + entry->inner_pointer = inner_pointer; } return entry; } diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h index fed11c4faf..e550f765ca 100644 --- a/deps/v8/src/frames.h +++ b/deps/v8/src/frames.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -49,47 +49,54 @@ class StackFrameIterator; class ThreadLocalTop; class Isolate; -class PcToCodeCache { +class InnerPointerToCodeCache { public: - struct PcToCodeCacheEntry { - Address pc; + struct InnerPointerToCodeCacheEntry { + Address inner_pointer; Code* code; SafepointEntry safepoint_entry; }; - explicit PcToCodeCache(Isolate* isolate) : isolate_(isolate) { + explicit InnerPointerToCodeCache(Isolate* isolate) : isolate_(isolate) { Flush(); } - Code* GcSafeFindCodeForPc(Address pc); - Code* GcSafeCastToCode(HeapObject* object, Address pc); + Code* GcSafeFindCodeForInnerPointer(Address inner_pointer); + Code* GcSafeCastToCode(HeapObject* object, Address inner_pointer); void Flush() { memset(&cache_[0], 0, sizeof(cache_)); } - PcToCodeCacheEntry* GetCacheEntry(Address pc); + InnerPointerToCodeCacheEntry* GetCacheEntry(Address inner_pointer); private: - PcToCodeCacheEntry* cache(int index) { return &cache_[index]; } + InnerPointerToCodeCacheEntry* cache(int index) { return &cache_[index]; } Isolate* isolate_; - static const int kPcToCodeCacheSize = 1024; - PcToCodeCacheEntry cache_[kPcToCodeCacheSize]; + static const int kInnerPointerToCodeCacheSize = 1024; + InnerPointerToCodeCacheEntry cache_[kInnerPointerToCodeCacheSize]; - DISALLOW_COPY_AND_ASSIGN(PcToCodeCache); + DISALLOW_COPY_AND_ASSIGN(InnerPointerToCodeCache); }; class StackHandler BASE_EMBEDDED { public: - enum State { - ENTRY, - TRY_CATCH, - TRY_FINALLY + enum Kind { + JS_ENTRY, + CATCH, + FINALLY, + LAST_KIND = FINALLY }; + static const int kKindWidth = 2; + STATIC_ASSERT(LAST_KIND < (1 << kKindWidth)); + static const int kIndexWidth = 32 - kKindWidth; + class KindField: public BitField<StackHandler::Kind, 0, kKindWidth> {}; + class IndexField: public BitField<unsigned, kKindWidth, kIndexWidth> {}; + // Get the address of this stack handler. inline Address address() const; @@ -106,16 +113,16 @@ class StackHandler BASE_EMBEDDED { static inline StackHandler* FromAddress(Address address); // Testers - bool is_entry() { return state() == ENTRY; } - bool is_try_catch() { return state() == TRY_CATCH; } - bool is_try_finally() { return state() == TRY_FINALLY; } + inline bool is_js_entry() const; + inline bool is_catch() const; + inline bool is_finally() const; private: // Accessors. - inline State state() const; + inline Kind kind() const; inline Object** context_address() const; - inline Address* pc_address() const; + inline Object** code_address() const; DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler); }; @@ -139,7 +146,10 @@ class StackFrame BASE_EMBEDDED { enum Type { NONE = 0, STACK_FRAME_TYPE_LIST(DECLARE_TYPE) - NUMBER_OF_TYPES + NUMBER_OF_TYPES, + // Used by FrameScope to indicate that the stack frame is constructed + // manually and the FrameScope does not need to emit code. + MANUAL }; #undef DECLARE_TYPE @@ -215,9 +225,7 @@ class StackFrame BASE_EMBEDDED { virtual Code* unchecked_code() const = 0; // Get the code associated with this frame. - Code* LookupCode() const { - return GetContainingCode(isolate(), pc()); - } + inline Code* LookupCode() const; // Get the code object that contains the given pc. static inline Code* GetContainingCode(Isolate* isolate, Address pc); @@ -299,7 +307,7 @@ class EntryFrame: public StackFrame { virtual void SetCallerFp(Address caller_fp); protected: - explicit EntryFrame(StackFrameIterator* iterator) : StackFrame(iterator) { } + inline explicit EntryFrame(StackFrameIterator* iterator); // The caller stack pointer for entry frames is always zero. The // real information about the caller frame is available through the @@ -326,8 +334,7 @@ class EntryConstructFrame: public EntryFrame { } protected: - explicit EntryConstructFrame(StackFrameIterator* iterator) - : EntryFrame(iterator) { } + inline explicit EntryConstructFrame(StackFrameIterator* iterator); private: friend class StackFrameIterator; @@ -361,7 +368,7 @@ class ExitFrame: public StackFrame { static void FillState(Address fp, Address sp, State* state); protected: - explicit ExitFrame(StackFrameIterator* iterator) : StackFrame(iterator) { } + inline explicit ExitFrame(StackFrameIterator* iterator); virtual Address GetCallerStackPointer() const; @@ -394,8 +401,7 @@ class StandardFrame: public StackFrame { } protected: - explicit StandardFrame(StackFrameIterator* iterator) - : StackFrame(iterator) { } + inline explicit StandardFrame(StackFrameIterator* iterator); virtual void ComputeCallerState(State* state) const; @@ -513,9 +519,10 @@ class JavaScriptFrame: public StandardFrame { return static_cast<JavaScriptFrame*>(frame); } + static void PrintTop(FILE* file, bool print_args, bool print_line_number); + protected: - explicit JavaScriptFrame(StackFrameIterator* iterator) - : StandardFrame(iterator) { } + inline explicit JavaScriptFrame(StackFrameIterator* iterator); virtual Address GetCallerStackPointer() const; @@ -552,8 +559,7 @@ class OptimizedFrame : public JavaScriptFrame { DeoptimizationInputData* GetDeoptimizationData(int* deopt_index); protected: - explicit OptimizedFrame(StackFrameIterator* iterator) - : JavaScriptFrame(iterator) { } + inline explicit OptimizedFrame(StackFrameIterator* iterator); private: friend class StackFrameIterator; @@ -581,12 +587,9 @@ class ArgumentsAdaptorFrame: public JavaScriptFrame { int index) const; protected: - explicit ArgumentsAdaptorFrame(StackFrameIterator* iterator) - : JavaScriptFrame(iterator) { } + inline explicit ArgumentsAdaptorFrame(StackFrameIterator* iterator); - virtual int GetNumberOfIncomingArguments() const { - return Smi::cast(GetExpression(0))->value(); - } + virtual int GetNumberOfIncomingArguments() const; virtual Address GetCallerStackPointer() const; @@ -611,8 +614,7 @@ class InternalFrame: public StandardFrame { } protected: - explicit InternalFrame(StackFrameIterator* iterator) - : StandardFrame(iterator) { } + inline explicit InternalFrame(StackFrameIterator* iterator); virtual Address GetCallerStackPointer() const; @@ -633,8 +635,7 @@ class ConstructFrame: public InternalFrame { } protected: - explicit ConstructFrame(StackFrameIterator* iterator) - : InternalFrame(iterator) { } + inline explicit ConstructFrame(StackFrameIterator* iterator); private: friend class StackFrameIterator; @@ -710,20 +711,26 @@ class JavaScriptFrameIteratorTemp BASE_EMBEDDED { inline explicit JavaScriptFrameIteratorTemp(Isolate* isolate); + inline JavaScriptFrameIteratorTemp(Isolate* isolate, ThreadLocalTop* top); + // Skip frames until the frame with the given id is reached. explicit JavaScriptFrameIteratorTemp(StackFrame::Id id) { AdvanceToId(id); } inline JavaScriptFrameIteratorTemp(Isolate* isolate, StackFrame::Id id); - JavaScriptFrameIteratorTemp(Address fp, Address sp, - Address low_bound, Address high_bound) : + JavaScriptFrameIteratorTemp(Address fp, + Address sp, + Address low_bound, + Address high_bound) : iterator_(fp, sp, low_bound, high_bound) { if (!done()) Advance(); } JavaScriptFrameIteratorTemp(Isolate* isolate, - Address fp, Address sp, - Address low_bound, Address high_bound) : + Address fp, + Address sp, + Address low_bound, + Address high_bound) : iterator_(isolate, fp, sp, low_bound, high_bound) { if (!done()) Advance(); } diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index 8073874132..5c5ba6b256 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -51,7 +51,25 @@ void BreakableStatementChecker::Check(Expression* expr) { } -void BreakableStatementChecker::VisitDeclaration(Declaration* decl) { +void BreakableStatementChecker::VisitVariableDeclaration( + VariableDeclaration* decl) { +} + +void BreakableStatementChecker::VisitModuleDeclaration( + ModuleDeclaration* decl) { +} + + +void BreakableStatementChecker::VisitModuleLiteral(ModuleLiteral* module) { +} + +void BreakableStatementChecker::VisitModuleVariable(ModuleVariable* module) { +} + +void BreakableStatementChecker::VisitModulePath(ModulePath* module) { +} + +void BreakableStatementChecker::VisitModuleUrl(ModuleUrl* module) { } @@ -244,11 +262,6 @@ void BreakableStatementChecker::VisitBinaryOperation(BinaryOperation* expr) { } -void BreakableStatementChecker::VisitCompareToNull(CompareToNull* expr) { - Visit(expr->expression()); -} - - void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) { Visit(expr->left()); Visit(expr->right()); @@ -290,13 +303,21 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info); code->set_optimizable(info->IsOptimizable()); cgen.PopulateDeoptimizationData(code); + cgen.PopulateTypeFeedbackCells(code); code->set_has_deoptimization_support(info->HasDeoptimizationSupport()); + code->set_handler_table(*cgen.handler_table()); +#ifdef ENABLE_DEBUGGER_SUPPORT code->set_has_debug_break_slots( info->isolate()->debugger()->IsDebuggerActive()); + code->set_compiled_optimizable(info->IsOptimizable()); +#endif // ENABLE_DEBUGGER_SUPPORT code->set_allow_osr_at_loop_nesting_level(0); code->set_stack_check_table_offset(table_offset); CodeGenerator::PrintCode(code, info); - info->SetCode(code); // may be an empty handle. + info->SetCode(code); // May be an empty handle. + if (!code.is_null()) { + isolate->runtime_profiler()->NotifyCodeGenerated(code->instruction_size()); + } #ifdef ENABLE_GDB_JIT_INTERFACE if (FLAG_gdbjit && !code.is_null()) { GDBJITLineInfo* lineinfo = @@ -330,8 +351,7 @@ void FullCodeGenerator::PopulateDeoptimizationData(Handle<Code> code) { ASSERT(info_->HasDeoptimizationSupport() || bailout_entries_.is_empty()); if (!info_->HasDeoptimizationSupport()) return; int length = bailout_entries_.length(); - Handle<DeoptimizationOutputData> data = - isolate()->factory()-> + Handle<DeoptimizationOutputData> data = isolate()->factory()-> NewDeoptimizationOutputData(length, TENURED); for (int i = 0; i < length; i++) { data->SetAstId(i, Smi::FromInt(bailout_entries_[i].id)); @@ -341,6 +361,21 @@ void FullCodeGenerator::PopulateDeoptimizationData(Handle<Code> code) { } +void FullCodeGenerator::PopulateTypeFeedbackCells(Handle<Code> code) { + if (type_feedback_cells_.is_empty()) return; + int length = type_feedback_cells_.length(); + int array_size = TypeFeedbackCells::LengthOfFixedArray(length); + Handle<TypeFeedbackCells> cache = Handle<TypeFeedbackCells>::cast( + isolate()->factory()->NewFixedArray(array_size, TENURED)); + for (int i = 0; i < length; i++) { + cache->SetAstId(i, Smi::FromInt(type_feedback_cells_[i].ast_id)); + cache->SetCell(i, *type_feedback_cells_[i].cell); + } + code->set_type_feedback_cells(*cache); +} + + + void FullCodeGenerator::PrepareForBailout(Expression* node, State state) { PrepareForBailoutForId(node->id(), state); } @@ -363,20 +398,22 @@ void FullCodeGenerator::RecordJSReturnSite(Call* call) { } -void FullCodeGenerator::PrepareForBailoutForId(int id, State state) { +void FullCodeGenerator::PrepareForBailoutForId(unsigned id, State state) { // There's no need to prepare this code for bailouts from already optimized // code or code that can't be optimized. - if (!FLAG_deopt || !info_->HasDeoptimizationSupport()) return; + if (!info_->HasDeoptimizationSupport()) return; unsigned pc_and_state = StateField::encode(state) | PcField::encode(masm_->pc_offset()); BailoutEntry entry = { id, pc_and_state }; #ifdef DEBUG - // Assert that we don't have multiple bailout entries for the same node. - for (int i = 0; i < bailout_entries_.length(); i++) { - if (bailout_entries_.at(i).id == entry.id) { - AstPrinter printer; - PrintF("%s", printer.PrintProgram(info_->function())); - UNREACHABLE(); + if (FLAG_enable_slow_asserts) { + // Assert that we don't have multiple bailout entries for the same node. + for (int i = 0; i < bailout_entries_.length(); i++) { + if (bailout_entries_.at(i).id == entry.id) { + AstPrinter printer; + PrintF("%s", printer.PrintProgram(info_->function())); + UNREACHABLE(); + } } } #endif // DEBUG @@ -384,10 +421,18 @@ void FullCodeGenerator::PrepareForBailoutForId(int id, State state) { } -void FullCodeGenerator::RecordStackCheck(int ast_id) { +void FullCodeGenerator::RecordTypeFeedbackCell( + unsigned id, Handle<JSGlobalPropertyCell> cell) { + TypeFeedbackCellEntry entry = { id, cell }; + type_feedback_cells_.Add(entry); +} + + +void FullCodeGenerator::RecordStackCheck(unsigned ast_id) { // The pc offset does not need to be encoded and packed together with a // state. - BailoutEntry entry = { ast_id, masm_->pc_offset() }; + ASSERT(masm_->pc_offset() > 0); + BailoutEntry entry = { ast_id, static_cast<unsigned>(masm_->pc_offset()) }; stack_checks_.Add(entry); } @@ -412,27 +457,24 @@ void FullCodeGenerator::AccumulatorValueContext::Plug(Register reg) const { void FullCodeGenerator::StackValueContext::Plug(Register reg) const { __ push(reg); - codegen()->increment_stack_height(); } void FullCodeGenerator::TestContext::Plug(Register reg) const { // For simplicity we always test the accumulator register. __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } void FullCodeGenerator::EffectContext::PlugTOS() const { __ Drop(1); - codegen()->decrement_stack_height(); } void FullCodeGenerator::AccumulatorValueContext::PlugTOS() const { __ pop(result_register()); - codegen()->decrement_stack_height(); } @@ -443,8 +485,7 @@ void FullCodeGenerator::StackValueContext::PlugTOS() const { void FullCodeGenerator::TestContext::PlugTOS() const { // For simplicity we always test the accumulator register. __ pop(result_register()); - codegen()->decrement_stack_height(); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -505,39 +546,40 @@ void FullCodeGenerator::DoTest(const TestContext* context) { void FullCodeGenerator::VisitDeclarations( ZoneList<Declaration*>* declarations) { - int length = declarations->length(); - int global_count = 0; - for (int i = 0; i < length; i++) { - Declaration* decl = declarations->at(i); - EmitDeclaration(decl->proxy(), decl->mode(), decl->fun(), &global_count); - } + int save_global_count = global_count_; + global_count_ = 0; + + AstVisitor::VisitDeclarations(declarations); // Batch declare global functions and variables. - if (global_count > 0) { + if (global_count_ > 0) { Handle<FixedArray> array = - isolate()->factory()->NewFixedArray(2 * global_count, TENURED); + isolate()->factory()->NewFixedArray(2 * global_count_, TENURED); + int length = declarations->length(); for (int j = 0, i = 0; i < length; i++) { - Declaration* decl = declarations->at(i); - Variable* var = decl->proxy()->var(); - - if (var->IsUnallocated()) { - array->set(j++, *(var->name())); - if (decl->fun() == NULL) { - if (var->mode() == Variable::CONST) { - // In case this is const property use the hole. - array->set_the_hole(j++); + VariableDeclaration* decl = declarations->at(i)->AsVariableDeclaration(); + if (decl != NULL) { + Variable* var = decl->proxy()->var(); + + if (var->IsUnallocated()) { + array->set(j++, *(var->name())); + if (decl->fun() == NULL) { + if (var->binding_needs_init()) { + // In case this binding needs initialization use the hole. + array->set_the_hole(j++); + } else { + array->set_undefined(j++); + } } else { - array->set_undefined(j++); + Handle<SharedFunctionInfo> function = + Compiler::BuildFunctionInfo(decl->fun(), script()); + // Check for stack-overflow exception. + if (function.is_null()) { + SetStackOverflow(); + return; + } + array->set(j++, *function); } - } else { - Handle<SharedFunctionInfo> function = - Compiler::BuildFunctionInfo(decl->fun(), script()); - // Check for stack-overflow exception. - if (function.is_null()) { - SetStackOverflow(); - return; - } - array->set(j++, *function); } } } @@ -545,15 +587,46 @@ void FullCodeGenerator::VisitDeclarations( // declaration the global functions and variables. DeclareGlobals(array); } + + global_count_ = save_global_count; +} + + +void FullCodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { + EmitDeclaration(decl->proxy(), decl->mode(), decl->fun()); +} + + +void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* decl) { + // TODO(rossberg) +} + + +void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) { + // TODO(rossberg) +} + + +void FullCodeGenerator::VisitModuleVariable(ModuleVariable* module) { + // TODO(rossberg) +} + + +void FullCodeGenerator::VisitModulePath(ModulePath* module) { + // TODO(rossberg) +} + + +void FullCodeGenerator::VisitModuleUrl(ModuleUrl* decl) { + // TODO(rossberg) } int FullCodeGenerator::DeclareGlobalsFlags() { - int flags = 0; - if (is_eval()) flags |= kDeclareGlobalsEvalFlag; - if (is_strict_mode()) flags |= kDeclareGlobalsStrictModeFlag; - if (is_native()) flags |= kDeclareGlobalsNativeFlag; - return flags; + ASSERT(DeclareGlobalsLanguageMode::is_valid(language_mode())); + return DeclareGlobalsEvalFlag::encode(is_eval()) | + DeclareGlobalsNativeFlag::encode(is_native()) | + DeclareGlobalsLanguageMode::encode(language_mode()); } @@ -659,14 +732,13 @@ FullCodeGenerator::InlineFunctionGenerator } -void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* node) { - ZoneList<Expression*>* args = node->arguments(); - const Runtime::Function* function = node->function(); +void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { + const Runtime::Function* function = expr->function(); ASSERT(function != NULL); ASSERT(function->intrinsic_type == Runtime::INLINE); InlineFunctionGenerator generator = FindInlineFunctionGenerator(function->function_id); - ((*this).*(generator))(args); + ((*this).*(generator))(expr); } @@ -683,11 +755,25 @@ void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { } +void FullCodeGenerator::VisitInDuplicateContext(Expression* expr) { + if (context()->IsEffect()) { + VisitForEffect(expr); + } else if (context()->IsAccumulatorValue()) { + VisitForAccumulatorValue(expr); + } else if (context()->IsStackValue()) { + VisitForStackValue(expr); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + VisitForControl(expr, test->true_label(), test->false_label(), + test->fall_through()); + } +} + + void FullCodeGenerator::VisitComma(BinaryOperation* expr) { Comment cmnt(masm_, "[ Comma"); VisitForEffect(expr->left()); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitInCurrentContext(expr->right()); + VisitInDuplicateContext(expr->right()); } @@ -709,7 +795,6 @@ void FullCodeGenerator::VisitLogicalExpression(BinaryOperation* expr) { } PrepareForBailoutForId(right_id, NO_REGISTERS); __ bind(&eval_right); - ForwardBailoutToChild(expr); } else if (context()->IsAccumulatorValue()) { VisitForAccumulatorValue(left); @@ -717,7 +802,6 @@ void FullCodeGenerator::VisitLogicalExpression(BinaryOperation* expr) { // case we need it. __ push(result_register()); Label discard, restore; - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); if (is_logical_and) { DoTest(left, &discard, &restore, &restore); } else { @@ -736,7 +820,6 @@ void FullCodeGenerator::VisitLogicalExpression(BinaryOperation* expr) { // case we need it. __ push(result_register()); Label discard; - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); if (is_logical_and) { DoTest(left, &discard, &done, &discard); } else { @@ -758,7 +841,7 @@ void FullCodeGenerator::VisitLogicalExpression(BinaryOperation* expr) { __ bind(&eval_right); } - VisitInCurrentContext(right); + VisitInDuplicateContext(right); __ bind(&done); } @@ -785,34 +868,6 @@ void FullCodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) { } -void FullCodeGenerator::ForwardBailoutToChild(Expression* expr) { - if (!info_->HasDeoptimizationSupport()) return; - ASSERT(context()->IsTest()); - ASSERT(expr == forward_bailout_stack_->expr()); - forward_bailout_pending_ = forward_bailout_stack_; -} - - -void FullCodeGenerator::VisitInCurrentContext(Expression* expr) { - if (context()->IsTest()) { - ForwardBailoutStack stack(expr, forward_bailout_pending_); - ForwardBailoutStack* saved = forward_bailout_stack_; - forward_bailout_pending_ = NULL; - forward_bailout_stack_ = &stack; - Visit(expr); - forward_bailout_stack_ = saved; - } else { - ASSERT(forward_bailout_pending_ == NULL); - Visit(expr); - State state = context()->IsAccumulatorValue() ? TOS_REG : NO_REGISTERS; - PrepareForBailout(expr, state); - // Forwarding bailouts to children is a one shot operation. It should have - // been processed at this point. - ASSERT(forward_bailout_pending_ == NULL); - } -} - - void FullCodeGenerator::VisitBlock(Block* stmt) { Comment cmnt(masm_, "[ Block"); NestedBlock nested_block(this, stmt); @@ -823,9 +878,18 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { if (stmt->block_scope() != NULL) { { Comment cmnt(masm_, "[ Extend block context"); scope_ = stmt->block_scope(); - __ Push(scope_->GetSerializedScopeInfo()); + Handle<ScopeInfo> scope_info = scope_->GetScopeInfo(); + int heap_slots = scope_info->ContextLength() - Context::MIN_CONTEXT_SLOTS; + __ Push(scope_info); PushFunctionArgumentForContextAllocation(); - __ CallRuntime(Runtime::kPushBlockContext, 2); + if (heap_slots <= FastNewBlockContextStub::kMaximumSlots) { + FastNewBlockContextStub stub(heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kPushBlockContext, 2); + } + + // Replace the context stored in the frame. StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); } @@ -972,7 +1036,6 @@ void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) { VisitForStackValue(stmt->expression()); PushFunctionArgumentForContextAllocation(); __ CallRuntime(Runtime::kPushWithContext, 2); - decrement_stack_height(); StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); { WithOrCatch body(this); @@ -1103,20 +1166,17 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) { void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { Comment cmnt(masm_, "[ TryCatchStatement"); SetStatementPosition(stmt); - // The try block adds a handler to the exception handler chain - // before entering, and removes it again when exiting normally. - // If an exception is thrown during execution of the try block, - // control is passed to the handler, which also consumes the handler. - // At this point, the exception is in a register, and store it in - // the temporary local variable (prints as ".catch-var") before - // executing the catch block. The catch block has been rewritten - // to introduce a new scope to bind the catch variable and to remove - // that scope again afterwards. - - Label try_handler_setup, done; - __ Call(&try_handler_setup); - // Try handler code, exception in result register. - + // The try block adds a handler to the exception handler chain before + // entering, and removes it again when exiting normally. If an exception + // is thrown during execution of the try block, the handler is consumed + // and control is passed to the catch block with the exception in the + // result register. + + Label try_entry, handler_entry, exit; + __ jmp(&try_entry); + __ bind(&handler_entry); + handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos())); + // Exception handler code, the exception is in the result register. // Extend the context before executing the catch block. { Comment cmnt(masm_, "[ Extend catch context"); __ Push(stmt->variable()->name()); @@ -1130,27 +1190,23 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { Scope* saved_scope = scope(); scope_ = stmt->scope(); ASSERT(scope_->declarations()->is_empty()); - { WithOrCatch body(this); + { WithOrCatch catch_body(this); Visit(stmt->catch_block()); } // Restore the context. LoadContextField(context_register(), Context::PREVIOUS_INDEX); StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); scope_ = saved_scope; - __ jmp(&done); + __ jmp(&exit); // Try block code. Sets up the exception handler chain. - __ bind(&try_handler_setup); - { - const int delta = StackHandlerConstants::kSize / kPointerSize; - TryCatch try_block(this); - __ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER); - increment_stack_height(delta); + __ bind(&try_entry); + __ PushTryHandler(StackHandler::CATCH, stmt->index()); + { TryCatch try_body(this); Visit(stmt->try_block()); - __ PopTryHandler(); - decrement_stack_height(delta); } - __ bind(&done); + __ PopTryHandler(); + __ bind(&exit); } @@ -1162,12 +1218,12 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { // // The try-finally construct can enter the finally block in three ways: // 1. By exiting the try-block normally. This removes the try-handler and - // calls the finally block code before continuing. + // calls the finally block code before continuing. // 2. By exiting the try-block with a function-local control flow transfer // (break/continue/return). The site of the, e.g., break removes the // try handler and calls the finally block code before continuing // its outward control transfer. - // 3. by exiting the try-block with a thrown exception. + // 3. By exiting the try-block with a thrown exception. // This can happen in nested function calls. It traverses the try-handler // chain and consumes the try-handler entry before jumping to the // handler code. The handler code then calls the finally-block before @@ -1178,49 +1234,39 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { // exception) in the result register (rax/eax/r0), both of which must // be preserved. The return address isn't GC-safe, so it should be // cooked before GC. - Label finally_entry; - Label try_handler_setup; - const int original_stack_height = stack_height(); - - // Setup the try-handler chain. Use a call to - // Jump to try-handler setup and try-block code. Use call to put try-handler - // address on stack. - __ Call(&try_handler_setup); - // Try handler code. Return address of call is pushed on handler stack. - { - // This code is only executed during stack-handler traversal when an - // exception is thrown. The exception is in the result register, which - // is retained by the finally block. - // Call the finally block and then rethrow the exception if it returns. - __ Call(&finally_entry); - __ push(result_register()); - __ CallRuntime(Runtime::kReThrow, 1); - } + Label try_entry, handler_entry, finally_entry; + + // Jump to try-handler setup and try-block code. + __ jmp(&try_entry); + __ bind(&handler_entry); + handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos())); + // Exception handler code. This code is only executed when an exception + // is thrown. The exception is in the result register, and must be + // preserved by the finally block. Call the finally block and then + // rethrow the exception if it returns. + __ Call(&finally_entry); + __ push(result_register()); + __ CallRuntime(Runtime::kReThrow, 1); + // Finally block implementation. __ bind(&finally_entry); - { - // Finally block implementation. - Finally finally_block(this); - EnterFinallyBlock(); - set_stack_height(original_stack_height + Finally::kElementCount); + EnterFinallyBlock(); + { Finally finally_body(this); Visit(stmt->finally_block()); - ExitFinallyBlock(); // Return to the calling code. } + ExitFinallyBlock(); // Return to the calling code. - __ bind(&try_handler_setup); - { - // Setup try handler (stack pointer registers). - const int delta = StackHandlerConstants::kSize / kPointerSize; - TryFinally try_block(this, &finally_entry); - __ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER); - set_stack_height(original_stack_height + delta); + // Set up try handler. + __ bind(&try_entry); + __ PushTryHandler(StackHandler::FINALLY, stmt->index()); + { TryFinally try_body(this, &finally_entry); Visit(stmt->try_block()); - __ PopTryHandler(); - set_stack_height(original_stack_height); } + __ PopTryHandler(); // Execute the finally block on the way out. Clobber the unpredictable - // value in the accumulator with one that's safe for GC. The finally - // block will unconditionally preserve the accumulator on the stack. + // value in the result register with one that's safe for GC because the + // finally block will unconditionally preserve the result register on the + // stack. ClearAccumulator(); __ Call(&finally_entry); } @@ -1246,7 +1292,6 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) { __ bind(&true_case); SetExpressionPosition(expr->then_expression(), expr->then_expression_position()); - int start_stack_height = stack_height(); if (context()->IsTest()) { const TestContext* for_test = TestContext::cast(context()); VisitForControl(expr->then_expression(), @@ -1254,17 +1299,15 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) { for_test->false_label(), NULL); } else { - VisitInCurrentContext(expr->then_expression()); + VisitInDuplicateContext(expr->then_expression()); __ jmp(&done); } PrepareForBailoutForId(expr->ElseId(), NO_REGISTERS); __ bind(&false_case); - set_stack_height(start_stack_height); - if (context()->IsTest()) ForwardBailoutToChild(expr); SetExpressionPosition(expr->else_expression(), expr->else_expression_position()); - VisitInCurrentContext(expr->else_expression()); + VisitInDuplicateContext(expr->else_expression()); // If control flow falls through Visit, merge it with true case here. if (!context()->IsTest()) { __ bind(&done); @@ -1301,11 +1344,8 @@ void FullCodeGenerator::VisitSharedFunctionInfoLiteral( void FullCodeGenerator::VisitThrow(Throw* expr) { Comment cmnt(masm_, "[ Throw"); - // Throw has no effect on the stack height or the current expression context. - // Usually the expression context is null, because throw is a statement. VisitForStackValue(expr->exception()); __ CallRuntime(Runtime::kThrow, 1); - decrement_stack_height(); // Never returns here. } @@ -1321,19 +1361,21 @@ FullCodeGenerator::NestedStatement* FullCodeGenerator::TryCatch::Exit( } -bool FullCodeGenerator::TryLiteralCompare(CompareOperation* compare, - Label* if_true, - Label* if_false, - Label* fall_through) { - Expression *expr; +bool FullCodeGenerator::TryLiteralCompare(CompareOperation* expr) { + Expression* sub_expr; Handle<String> check; - if (compare->IsLiteralCompareTypeof(&expr, &check)) { - EmitLiteralCompareTypeof(expr, check, if_true, if_false, fall_through); + if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { + EmitLiteralCompareTypeof(expr, sub_expr, check); + return true; + } + + if (expr->IsLiteralCompareUndefined(&sub_expr)) { + EmitLiteralCompareNil(expr, sub_expr, kUndefinedValue); return true; } - if (compare->IsLiteralCompareUndefined(&expr)) { - EmitLiteralCompareUndefined(expr, if_true, if_false, fall_through); + if (expr->IsLiteralCompareNull(&sub_expr)) { + EmitLiteralCompareNil(expr, sub_expr, kNullValue); return true; } diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 803c618732..f9b7c3842a 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -83,18 +83,20 @@ class FullCodeGenerator: public AstVisitor { scope_(NULL), nesting_stack_(NULL), loop_depth_(0), - stack_height_(0), + global_count_(0), context_(NULL), bailout_entries_(0), stack_checks_(2), // There's always at least one. - forward_bailout_stack_(NULL), - forward_bailout_pending_(NULL) { + type_feedback_cells_(0) { } static bool MakeCode(CompilationInfo* info); void Generate(CompilationInfo* info); void PopulateDeoptimizationData(Handle<Code> code); + void PopulateTypeFeedbackCells(Handle<Code> code); + + Handle<FixedArray> handler_table() { return handler_table_; } class StateField : public BitField<State, 0, 8> { }; class PcField : public BitField<unsigned, 8, 32-8> { }; @@ -143,11 +145,13 @@ class FullCodeGenerator: public AstVisitor { return previous_; } - protected: + protected: MacroAssembler* masm() { return codegen_->masm(); } FullCodeGenerator* codegen_; NestedStatement* previous_; + + private: DISALLOW_COPY_AND_ASSIGN(NestedStatement); }; @@ -276,27 +280,8 @@ class FullCodeGenerator: public AstVisitor { } }; - // The forward bailout stack keeps track of the expressions that can - // bail out to just before the control flow is split in a child - // node. The stack elements are linked together through the parent - // link when visiting expressions in test contexts after requesting - // bailout in child forwarding. - class ForwardBailoutStack BASE_EMBEDDED { - public: - ForwardBailoutStack(Expression* expr, ForwardBailoutStack* parent) - : expr_(expr), parent_(parent) { } - - Expression* expr() const { return expr_; } - ForwardBailoutStack* parent() const { return parent_; } - - private: - Expression* const expr_; - ForwardBailoutStack* const parent_; - }; - // Type of a member function that generates inline code for a native function. - typedef void (FullCodeGenerator::*InlineFunctionGenerator) - (ZoneList<Expression*>*); + typedef void (FullCodeGenerator::*InlineFunctionGenerator)(CallRuntime* expr); static const InlineFunctionGenerator kInlineFunctionGenerators[]; @@ -357,23 +342,22 @@ class FullCodeGenerator: public AstVisitor { // need the write barrier if location is CONTEXT. MemOperand VarOperand(Variable* var, Register scratch); - // Forward the bailout responsibility for the given expression to - // the next child visited (which must be in a test context). - void ForwardBailoutToChild(Expression* expr); - void VisitForEffect(Expression* expr) { EffectContext context(this); - VisitInCurrentContext(expr); + Visit(expr); + PrepareForBailout(expr, NO_REGISTERS); } void VisitForAccumulatorValue(Expression* expr) { AccumulatorValueContext context(this); - VisitInCurrentContext(expr); + Visit(expr); + PrepareForBailout(expr, TOS_REG); } void VisitForStackValue(Expression* expr) { StackValueContext context(this); - VisitInCurrentContext(expr); + Visit(expr); + PrepareForBailout(expr, NO_REGISTERS); } void VisitForControl(Expression* expr, @@ -381,9 +365,14 @@ class FullCodeGenerator: public AstVisitor { Label* if_false, Label* fall_through) { TestContext context(this, expr, if_true, if_false, fall_through); - VisitInCurrentContext(expr); + Visit(expr); + // For test contexts, we prepare for bailout before branching, not at + // the end of the entire expression. This happens as part of visiting + // the expression. } + void VisitInDuplicateContext(Expression* expr); + void VisitDeclarations(ZoneList<Declaration*>* declarations); void DeclareGlobals(Handle<FixedArray> pairs); int DeclareGlobalsFlags(); @@ -391,29 +380,26 @@ class FullCodeGenerator: public AstVisitor { // Try to perform a comparison as a fast inlined literal compare if // the operands allow it. Returns true if the compare operations // has been matched and all code generated; false otherwise. - bool TryLiteralCompare(CompareOperation* compare, - Label* if_true, - Label* if_false, - Label* fall_through); + bool TryLiteralCompare(CompareOperation* compare); // Platform-specific code for comparing the type of a value with // a given literal string. void EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through); - - // Platform-specific code for strict equality comparison with - // the undefined value. - void EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through); + Expression* sub_expr, + Handle<String> check); + + // Platform-specific code for equality comparison with a nil-like value. + void EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil); // Bailout support. void PrepareForBailout(Expression* node, State state); - void PrepareForBailoutForId(int id, State state); + void PrepareForBailoutForId(unsigned id, State state); + + // Cache cell support. This associates AST ids with global property cells + // that will be cleared during GC and collected by the type-feedback oracle. + void RecordTypeFeedbackCell(unsigned id, Handle<JSGlobalPropertyCell> cell); // Record a call's return site offset, used to rebuild the frame if the // called function was inlined at the site. @@ -424,23 +410,23 @@ class FullCodeGenerator: public AstVisitor { // canonical JS true value so we will insert a (dead) test against true at // the actual bailout target from the optimized code. If not // should_normalize, the true and false labels are ignored. - void PrepareForBailoutBeforeSplit(State state, + void PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false); // Platform-specific code for a variable, constant, or function // declaration. Functions have an initial value. + // Increments global_count_ for unallocated variables. void EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function, - int* global_count); + VariableMode mode, + FunctionLiteral* function); // Platform-specific code for checking the stack limit at the back edge of // a loop. void EmitStackCheck(IterationStatement* stmt); // Record the OSR AST id corresponding to a stack check in the code. - void RecordStackCheck(int osr_ast_id); + void RecordStackCheck(unsigned osr_ast_id); // Emit a table of stack check ids and pcs into the code stream. Return // the offset of the start of the table. unsigned EmitStackCheckTable(); @@ -459,7 +445,7 @@ class FullCodeGenerator: public AstVisitor { void EmitInlineRuntimeCall(CallRuntime* expr); #define EMIT_INLINE_RUNTIME_CALL(name, x, y) \ - void Emit##name(ZoneList<Expression*>* arguments); + void Emit##name(CallRuntime* expr); INLINE_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL) INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL) #undef EMIT_INLINE_RUNTIME_CALL @@ -475,13 +461,8 @@ class FullCodeGenerator: public AstVisitor { Label* done); void EmitVariableLoad(VariableProxy* proxy); - enum ResolveEvalFlag { - SKIP_CONTEXT_LOOKUP, - PERFORM_CONTEXT_LOOKUP - }; - // Expects the arguments and the function already pushed. - void EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, int arg_count); + void EmitResolvePossiblyDirectEval(int arg_count); // Platform-specific support for allocating a new closure based on // the given function info. @@ -548,35 +529,6 @@ class FullCodeGenerator: public AstVisitor { loop_depth_--; } -#if defined(V8_TARGET_ARCH_IA32) - int stack_height() { return stack_height_; } - void set_stack_height(int depth) { stack_height_ = depth; } - void increment_stack_height() { stack_height_++; } - void increment_stack_height(int delta) { stack_height_ += delta; } - void decrement_stack_height() { - if (FLAG_verify_stack_height) { - ASSERT(stack_height_ > 0); - } - stack_height_--; - } - void decrement_stack_height(int delta) { - stack_height_-= delta; - if (FLAG_verify_stack_height) { - ASSERT(stack_height_ >= 0); - } - } - // Call this function only if FLAG_verify_stack_height is true. - void verify_stack_height(); // Generates a runtime check of esp - ebp. -#else - int stack_height() { return 0; } - void set_stack_height(int depth) {} - void increment_stack_height() {} - void increment_stack_height(int delta) {} - void decrement_stack_height() {} - void decrement_stack_height(int delta) {} - void verify_stack_height() {} -#endif // V8_TARGET_ARCH_IA32 - MacroAssembler* masm() { return masm_; } class ExpressionContext; @@ -586,9 +538,11 @@ class FullCodeGenerator: public AstVisitor { Handle<Script> script() { return info_->script(); } bool is_eval() { return info_->is_eval(); } bool is_native() { return info_->is_native(); } - bool is_strict_mode() { return function()->strict_mode(); } - StrictModeFlag strict_mode_flag() { - return is_strict_mode() ? kStrictMode : kNonStrictMode; + bool is_classic_mode() { + return language_mode() == CLASSIC_MODE; + } + LanguageMode language_mode() { + return function()->language_mode(); } FunctionLiteral* function() { return info_->function(); } Scope* scope() { return scope_; } @@ -618,7 +572,6 @@ class FullCodeGenerator: public AstVisitor { void VisitComma(BinaryOperation* expr); void VisitLogicalExpression(BinaryOperation* expr); void VisitArithmeticExpression(BinaryOperation* expr); - void VisitInCurrentContext(Expression* expr); void VisitForTypeofValue(Expression* expr); @@ -627,6 +580,11 @@ class FullCodeGenerator: public AstVisitor { unsigned pc_and_state; }; + struct TypeFeedbackCellEntry { + unsigned ast_id; + Handle<JSGlobalPropertyCell> cell; + }; + class ExpressionContext BASE_EMBEDDED { public: @@ -637,10 +595,6 @@ class FullCodeGenerator: public AstVisitor { virtual ~ExpressionContext() { codegen_->set_new_context(old_); - if (FLAG_verify_stack_height) { - ASSERT_EQ(expected_stack_height_, codegen()->stack_height()); - codegen()->verify_stack_height(); - } } Isolate* isolate() const { return codegen_->isolate(); } @@ -678,8 +632,8 @@ class FullCodeGenerator: public AstVisitor { Label** if_false, Label** fall_through) const = 0; - // Returns true if we are evaluating only for side effects (ie if the result - // will be discarded). + // Returns true if we are evaluating only for side effects (i.e. if the + // result will be discarded). virtual bool IsEffect() const { return false; } // Returns true if we are evaluating for the value (in accu/on stack). @@ -694,7 +648,6 @@ class FullCodeGenerator: public AstVisitor { FullCodeGenerator* codegen() const { return codegen_; } MacroAssembler* masm() const { return masm_; } MacroAssembler* masm_; - int expected_stack_height_; // The expected stack height esp - ebp on exit. private: const ExpressionContext* old_; @@ -704,9 +657,7 @@ class FullCodeGenerator: public AstVisitor { class AccumulatorValueContext : public ExpressionContext { public: explicit AccumulatorValueContext(FullCodeGenerator* codegen) - : ExpressionContext(codegen) { - expected_stack_height_ = codegen->stack_height(); - } + : ExpressionContext(codegen) { } virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; @@ -727,9 +678,7 @@ class FullCodeGenerator: public AstVisitor { class StackValueContext : public ExpressionContext { public: explicit StackValueContext(FullCodeGenerator* codegen) - : ExpressionContext(codegen) { - expected_stack_height_ = codegen->stack_height() + 1; - } + : ExpressionContext(codegen) { } virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; @@ -758,9 +707,7 @@ class FullCodeGenerator: public AstVisitor { condition_(condition), true_label_(true_label), false_label_(false_label), - fall_through_(fall_through) { - expected_stack_height_ = codegen->stack_height(); - } + fall_through_(fall_through) { } static const TestContext* cast(const ExpressionContext* context) { ASSERT(context->IsTest()); @@ -797,10 +744,7 @@ class FullCodeGenerator: public AstVisitor { class EffectContext : public ExpressionContext { public: explicit EffectContext(FullCodeGenerator* codegen) - : ExpressionContext(codegen) { - expected_stack_height_ = codegen->stack_height(); - } - + : ExpressionContext(codegen) { } virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; @@ -824,12 +768,12 @@ class FullCodeGenerator: public AstVisitor { Label return_label_; NestedStatement* nesting_stack_; int loop_depth_; - int stack_height_; + int global_count_; const ExpressionContext* context_; ZoneList<BailoutEntry> bailout_entries_; ZoneList<BailoutEntry> stack_checks_; - ForwardBailoutStack* forward_bailout_stack_; - ForwardBailoutStack* forward_bailout_pending_; + ZoneList<TypeFeedbackCellEntry> type_feedback_cells_; + Handle<FixedArray> handler_table_; friend class NestedStatement; diff --git a/deps/v8/src/gdb-jit.cc b/deps/v8/src/gdb-jit.cc index 68cb0533b8..4192222f90 100644 --- a/deps/v8/src/gdb-jit.cc +++ b/deps/v8/src/gdb-jit.cc @@ -1115,13 +1115,13 @@ class DebugInfoSection : public DebugSection { int context_slots = scope_info.number_of_context_slots(); // The real slot ID is internal_slots + context_slot_id. int internal_slots = Context::MIN_CONTEXT_SLOTS; - int locals = scope_info.NumberOfLocals(); + int locals = scope_info.LocalCount(); int current_abbreviation = 4; for (int param = 0; param < params; ++param) { w->WriteULEB128(current_abbreviation++); w->WriteString( - *scope_info.parameter_name(param)->ToCString(DISALLOW_NULLS)); + *scope_info.ParameterName(param)->ToCString(DISALLOW_NULLS)); w->Write<uint32_t>(ty_offset); Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>(); uintptr_t block_start = w->position(); @@ -1312,7 +1312,7 @@ class DebugAbbrevSection : public DebugSection { int context_slots = scope_info.number_of_context_slots(); // The real slot ID is internal_slots + context_slot_id. int internal_slots = Context::MIN_CONTEXT_SLOTS; - int locals = scope_info.NumberOfLocals(); + int locals = scope_info.LocalCount(); int total_children = params + slots + context_slots + internal_slots + locals + 2; @@ -1556,23 +1556,23 @@ class DebugLineSection : public DebugSection { class UnwindInfoSection : public DebugSection { public: - explicit UnwindInfoSection(CodeDescription *desc); - virtual bool WriteBody(Writer *w); + explicit UnwindInfoSection(CodeDescription* desc); + virtual bool WriteBody(Writer* w); - int WriteCIE(Writer *w); - void WriteFDE(Writer *w, int); + int WriteCIE(Writer* w); + void WriteFDE(Writer* w, int); - void WriteFDEStateOnEntry(Writer *w); - void WriteFDEStateAfterRBPPush(Writer *w); - void WriteFDEStateAfterRBPSet(Writer *w); - void WriteFDEStateAfterRBPPop(Writer *w); + void WriteFDEStateOnEntry(Writer* w); + void WriteFDEStateAfterRBPPush(Writer* w); + void WriteFDEStateAfterRBPSet(Writer* w); + void WriteFDEStateAfterRBPPop(Writer* w); - void WriteLength(Writer *w, + void WriteLength(Writer* w, Writer::Slot<uint32_t>* length_slot, int initial_position); private: - CodeDescription *desc_; + CodeDescription* desc_; // DWARF3 Specification, Table 7.23 enum CFIInstructions { @@ -1623,7 +1623,7 @@ class UnwindInfoSection : public DebugSection { }; -void UnwindInfoSection::WriteLength(Writer *w, +void UnwindInfoSection::WriteLength(Writer* w, Writer::Slot<uint32_t>* length_slot, int initial_position) { uint32_t align = (w->position() - initial_position) % kPointerSize; @@ -1639,7 +1639,7 @@ void UnwindInfoSection::WriteLength(Writer *w, } -UnwindInfoSection::UnwindInfoSection(CodeDescription *desc) +UnwindInfoSection::UnwindInfoSection(CodeDescription* desc) #ifdef __ELF : ELFSection(".eh_frame", TYPE_X86_64_UNWIND, 1), #else @@ -1648,7 +1648,7 @@ UnwindInfoSection::UnwindInfoSection(CodeDescription *desc) #endif desc_(desc) { } -int UnwindInfoSection::WriteCIE(Writer *w) { +int UnwindInfoSection::WriteCIE(Writer* w) { Writer::Slot<uint32_t> cie_length_slot = w->CreateSlotHere<uint32_t>(); uint32_t cie_position = w->position(); @@ -1668,7 +1668,7 @@ int UnwindInfoSection::WriteCIE(Writer *w) { } -void UnwindInfoSection::WriteFDE(Writer *w, int cie_position) { +void UnwindInfoSection::WriteFDE(Writer* w, int cie_position) { // The only FDE for this function. The CFA is the current RBP. Writer::Slot<uint32_t> fde_length_slot = w->CreateSlotHere<uint32_t>(); int fde_position = w->position(); @@ -1686,7 +1686,7 @@ void UnwindInfoSection::WriteFDE(Writer *w, int cie_position) { } -void UnwindInfoSection::WriteFDEStateOnEntry(Writer *w) { +void UnwindInfoSection::WriteFDEStateOnEntry(Writer* w) { // The first state, just after the control has been transferred to the the // function. @@ -1713,7 +1713,7 @@ void UnwindInfoSection::WriteFDEStateOnEntry(Writer *w) { } -void UnwindInfoSection::WriteFDEStateAfterRBPPush(Writer *w) { +void UnwindInfoSection::WriteFDEStateAfterRBPPush(Writer* w) { // The second state, just after RBP has been pushed. // RBP / CFA for this function is now the current RSP, so just set the @@ -1734,7 +1734,7 @@ void UnwindInfoSection::WriteFDEStateAfterRBPPush(Writer *w) { } -void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer *w) { +void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer* w) { // The third state, after the RBP has been set. // The CFA can now directly be set to RBP. @@ -1749,7 +1749,7 @@ void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer *w) { } -void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer *w) { +void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer* w) { // The fourth (final) state. The RBP has been popped (just before issuing a // return). @@ -1769,7 +1769,7 @@ void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer *w) { } -bool UnwindInfoSection::WriteBody(Writer *w) { +bool UnwindInfoSection::WriteBody(Writer* w) { uint32_t cie_position = WriteCIE(w); WriteFDE(w, cie_position); return true; @@ -1810,8 +1810,8 @@ extern "C" { struct JITDescriptor { uint32_t version_; uint32_t action_flag_; - JITCodeEntry *relevant_entry_; - JITCodeEntry *first_entry_; + JITCodeEntry* relevant_entry_; + JITCodeEntry* first_entry_; }; // GDB will place breakpoint into this function. @@ -1998,7 +1998,7 @@ void GDBJITInterface::AddCode(Handle<String> name, } } -static void AddUnwindInfo(CodeDescription *desc) { +static void AddUnwindInfo(CodeDescription* desc) { #ifdef V8_TARGET_ARCH_X64 if (desc->tag() == GDBJITInterface::FUNCTION) { // To avoid propagating unwinding information through diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 87066faeaf..471f5a336c 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -232,7 +232,7 @@ class GlobalHandles::Node { VMState state(isolate, EXTERNAL); func(object, par); } - // Absense of explicit cleanup or revival of weak handle + // Absence of explicit cleanup or revival of weak handle // in most of the cases would lead to memory leak. ASSERT(state_ != NEAR_DEATH); return true; diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h index 6c6966aee5..30b676c8bd 100644 --- a/deps/v8/src/globals.h +++ b/deps/v8/src/globals.h @@ -230,6 +230,9 @@ const int kPointerSize = sizeof(void*); // NOLINT const int kDoubleSizeLog2 = 3; +// Size of the state of a the random number generator. +const int kRandomStateSize = 2 * kIntSize; + #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; const intptr_t kIntptrSignBit = V8_INT64_C(0x8000000000000000); @@ -255,6 +258,10 @@ const int kBinary32MinExponent = 0x01; const int kBinary32MantissaBits = 23; const int kBinary32ExponentShift = 23; +// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no +// other bits set. +const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; + // ASCII/UC16 constants // Code-point values in Unicode 4.0 are 21 bits wide. typedef uint16_t uc16; @@ -287,7 +294,7 @@ const uint32_t kMaxAsciiCharCodeU = 0x7fu; // The USE(x) template is used to silence C++ compiler warnings // issued for (yet) unused variables (typically parameters). template <typename T> -static inline void USE(T) { } +inline void USE(T) { } // FUNCTION_ADDR(f) gets the address of a C function f. @@ -351,6 +358,39 @@ F FUNCTION_CAST(Address addr) { class FreeStoreAllocationPolicy; template <typename T, class P = FreeStoreAllocationPolicy> class List; +// ----------------------------------------------------------------------------- +// Declarations for use in both the preparser and the rest of V8. + +// The different language modes that V8 implements. ES5 defines two language +// modes: an unrestricted mode respectively a strict mode which are indicated by +// CLASSIC_MODE respectively STRICT_MODE in the enum. The harmony spec drafts +// for the next ES standard specify a new third mode which is called 'extended +// mode'. The extended mode is only available if the harmony flag is set. It is +// based on the 'strict mode' and adds new functionality to it. This means that +// most of the semantics of these two modes coincide. +// +// In the current draft the term 'base code' is used to refer to code that is +// neither in strict nor extended mode. However, the more distinguishing term +// 'classic mode' is used in V8 instead to avoid mix-ups. + +enum LanguageMode { + CLASSIC_MODE, + STRICT_MODE, + EXTENDED_MODE +}; + + +// The Strict Mode (ECMA-262 5th edition, 4.2.2). +// +// This flag is used in the backend to represent the language mode. So far +// there is no semantic difference between the strict and the extended mode in +// the backend, so both modes are represented by the kStrictMode value. +enum StrictModeFlag { + kNonStrictMode, + kStrictMode +}; + + } } // namespace v8::internal #endif // V8_GLOBALS_H_ diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index c482fa6f68..943a1c0b6a 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -190,7 +190,11 @@ static int ExpectedNofPropertiesFromEstimate(int estimate) { // Inobject slack tracking will reclaim redundant inobject space later, // so we can afford to adjust the estimate generously. - return estimate + 8; + if (FLAG_clever_optimizations) { + return estimate + 8; + } else { + return estimate + 3; + } } @@ -204,42 +208,6 @@ void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared, } -void NormalizeProperties(Handle<JSObject> object, - PropertyNormalizationMode mode, - int expected_additional_properties) { - CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), - object->NormalizeProperties( - mode, - expected_additional_properties)); -} - - -Handle<SeededNumberDictionary> NormalizeElements(Handle<JSObject> object) { - CALL_HEAP_FUNCTION(object->GetIsolate(), - object->NormalizeElements(), - SeededNumberDictionary); -} - - -void TransformToFastProperties(Handle<JSObject> object, - int unused_property_fields) { - CALL_HEAP_FUNCTION_VOID( - object->GetIsolate(), - object->TransformToFastProperties(unused_property_fields)); -} - - -Handle<SeededNumberDictionary> SeededNumberDictionarySet( - Handle<SeededNumberDictionary> dictionary, - uint32_t index, - Handle<Object> value, - PropertyDetails details) { - CALL_HEAP_FUNCTION(dictionary->GetIsolate(), - dictionary->Set(index, *value, details), - SeededNumberDictionary); -} - - void FlattenString(Handle<String> string) { CALL_HEAP_FUNCTION_VOID(string->GetIsolate(), string->TryFlatten()); } @@ -261,17 +229,6 @@ Handle<Object> SetPrototype(Handle<JSFunction> function, } -Handle<Object> SetProperty(Handle<JSReceiver> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes, - StrictModeFlag strict_mode) { - CALL_HEAP_FUNCTION(object->GetIsolate(), - object->SetProperty(*key, *value, attributes, strict_mode), - Object); -} - - Handle<Object> SetProperty(Handle<Object> object, Handle<Object> key, Handle<Object> value, @@ -299,16 +256,6 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object, } -Handle<Object> SetNormalizedProperty(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyDetails details) { - CALL_HEAP_FUNCTION(object->GetIsolate(), - object->SetNormalizedProperty(*key, *value, details), - Object); -} - - Handle<Object> ForceDeleteProperty(Handle<JSObject> object, Handle<Object> key) { Isolate* isolate = object->GetIsolate(); @@ -318,30 +265,6 @@ Handle<Object> ForceDeleteProperty(Handle<JSObject> object, } -Handle<Object> SetLocalPropertyIgnoreAttributes( - Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes) { - CALL_HEAP_FUNCTION( - object->GetIsolate(), - object->SetLocalPropertyIgnoreAttributes(*key, *value, attributes), - Object); -} - - -void SetLocalPropertyNoThrow(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes) { - Isolate* isolate = object->GetIsolate(); - ASSERT(!isolate->has_pending_exception()); - CHECK(!SetLocalPropertyIgnoreAttributes( - object, key, value, attributes).is_null()); - CHECK(!isolate->has_pending_exception()); -} - - Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object, Handle<String> key, Handle<Object> value, @@ -372,24 +295,6 @@ Handle<Object> GetProperty(Handle<Object> obj, } -Handle<Object> GetProperty(Handle<JSReceiver> obj, - Handle<String> name, - LookupResult* result) { - PropertyAttributes attributes; - Isolate* isolate = Isolate::Current(); - CALL_HEAP_FUNCTION(isolate, - obj->GetProperty(*obj, result, *name, &attributes), - Object); -} - - -Handle<Object> GetElement(Handle<Object> obj, - uint32_t index) { - Isolate* isolate = Isolate::Current(); - CALL_HEAP_FUNCTION(isolate, Runtime::GetElement(obj, index), Object); -} - - Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver, Handle<JSObject> holder, Handle<String> name, @@ -403,12 +308,6 @@ Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver, } -Handle<Object> GetPrototype(Handle<Object> obj) { - Handle<Object> result(obj->GetPrototype()); - return result; -} - - Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) { const bool skip_hidden_prototypes = false; CALL_HEAP_FUNCTION(obj->GetIsolate(), @@ -416,43 +315,6 @@ Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) { } -Handle<Object> PreventExtensions(Handle<JSObject> object) { - CALL_HEAP_FUNCTION(object->GetIsolate(), object->PreventExtensions(), Object); -} - - -Handle<Object> GetHiddenProperties(Handle<JSObject> obj, - JSObject::HiddenPropertiesFlag flag) { - CALL_HEAP_FUNCTION(obj->GetIsolate(), - obj->GetHiddenProperties(flag), - Object); -} - - -int GetIdentityHash(Handle<JSObject> obj) { - CALL_AND_RETRY(obj->GetIsolate(), - obj->GetIdentityHash(JSObject::ALLOW_CREATION), - return Smi::cast(__object__)->value(), - return 0); -} - - -Handle<Object> DeleteElement(Handle<JSObject> obj, - uint32_t index) { - CALL_HEAP_FUNCTION(obj->GetIsolate(), - obj->DeleteElement(index, JSObject::NORMAL_DELETION), - Object); -} - - -Handle<Object> DeleteProperty(Handle<JSObject> obj, - Handle<String> prop) { - CALL_HEAP_FUNCTION(obj->GetIsolate(), - obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION), - Object); -} - - Handle<Object> LookupSingleCharacterStringFromCode(uint32_t index) { Isolate* isolate = Isolate::Current(); CALL_HEAP_FUNCTION( @@ -470,35 +332,6 @@ Handle<String> SubString(Handle<String> str, } -Handle<Object> SetElement(Handle<JSObject> object, - uint32_t index, - Handle<Object> value, - StrictModeFlag strict_mode) { - if (object->HasExternalArrayElements()) { - if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) { - bool has_exception; - Handle<Object> number = Execution::ToNumber(value, &has_exception); - if (has_exception) return Handle<Object>(); - value = number; - } - } - CALL_HEAP_FUNCTION(object->GetIsolate(), - object->SetElement(index, *value, strict_mode, true), - Object); -} - - -Handle<Object> SetOwnElement(Handle<JSObject> object, - uint32_t index, - Handle<Object> value, - StrictModeFlag strict_mode) { - ASSERT(!object->HasExternalArrayElements()); - CALL_HEAP_FUNCTION(object->GetIsolate(), - object->SetElement(index, *value, strict_mode, false), - Object); -} - - Handle<JSObject> Copy(Handle<JSObject> obj) { Isolate* isolate = obj->GetIsolate(); CALL_HEAP_FUNCTION(isolate, @@ -521,8 +354,9 @@ static void ClearWrapperCache(Persistent<v8::Value> handle, void*) { Handle<Object> cache = Utils::OpenHandle(*handle); JSValue* wrapper = JSValue::cast(*cache); Foreign* foreign = Script::cast(wrapper->value())->wrapper(); - ASSERT(foreign->address() == reinterpret_cast<Address>(cache.location())); - foreign->set_address(0); + ASSERT(foreign->foreign_address() == + reinterpret_cast<Address>(cache.location())); + foreign->set_foreign_address(0); Isolate* isolate = Isolate::Current(); isolate->global_handles()->Destroy(cache.location()); isolate->counters()->script_wrappers()->Decrement(); @@ -530,10 +364,10 @@ static void ClearWrapperCache(Persistent<v8::Value> handle, void*) { Handle<JSValue> GetScriptWrapper(Handle<Script> script) { - if (script->wrapper()->address() != NULL) { + if (script->wrapper()->foreign_address() != NULL) { // Return the script wrapper directly from the cache. return Handle<JSValue>( - reinterpret_cast<JSValue**>(script->wrapper()->address())); + reinterpret_cast<JSValue**>(script->wrapper()->foreign_address())); } Isolate* isolate = Isolate::Current(); // Construct a new script wrapper. @@ -549,7 +383,8 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) { Handle<Object> handle = isolate->global_handles()->Create(*result); isolate->global_handles()->MakeWeak(handle.location(), NULL, &ClearWrapperCache); - script->wrapper()->set_address(reinterpret_cast<Address>(handle.location())); + script->wrapper()->set_foreign_address( + reinterpret_cast<Address>(handle.location())); return result; } @@ -665,6 +500,19 @@ int GetScriptLineNumber(Handle<Script> script, int code_pos) { return right + script->line_offset()->value(); } +// Convert code position into column number. +int GetScriptColumnNumber(Handle<Script> script, int code_pos) { + int line_number = GetScriptLineNumber(script, code_pos); + if (line_number == -1) return -1; + + AssertNoAllocation no_allocation; + FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); + line_number = line_number - script->line_offset()->value(); + if (line_number == 0) return code_pos + script->column_offset()->value(); + int prev_line_end_pos = + Smi::cast(line_ends_array->get(line_number - 1))->value(); + return code_pos - (prev_line_end_pos + 1); +} int GetScriptLineNumberSafe(Handle<Script> script, int code_pos) { AssertNoAllocation no_allocation; @@ -696,7 +544,7 @@ void CustomArguments::IterateInstance(ObjectVisitor* v) { // Compute the property keys from the interceptor. -v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver, +v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver, Handle<JSObject> object) { Isolate* isolate = receiver->GetIsolate(); Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor()); @@ -718,7 +566,7 @@ v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver, // Compute the element keys from the interceptor. -v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver, +v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver, Handle<JSObject> object) { Isolate* isolate = receiver->GetIsolate(); Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); @@ -749,8 +597,9 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { } -Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, - KeyCollectionType type) { +Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object, + KeyCollectionType type, + bool* threw) { USE(ContainsOnlyValidKeys); Isolate* isolate = object->GetIsolate(); Handle<FixedArray> content = isolate->factory()->empty_fixed_array(); @@ -765,6 +614,16 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, for (Handle<Object> p = object; *p != isolate->heap()->null_value(); p = Handle<Object>(p->GetPrototype(), isolate)) { + if (p->IsJSProxy()) { + Handle<JSProxy> proxy(JSProxy::cast(*p), isolate); + Handle<Object> args[] = { proxy }; + Handle<Object> names = Execution::Call( + isolate->proxy_enumerate(), object, ARRAY_SIZE(args), args, threw); + if (*threw) return content; + content = AddKeysFromJSArray(content, Handle<JSArray>::cast(names)); + break; + } + Handle<JSObject> current(JSObject::cast(*p), isolate); // Check access rights if required. @@ -831,11 +690,11 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, } -Handle<JSArray> GetKeysFor(Handle<JSObject> object) { +Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw) { Isolate* isolate = object->GetIsolate(); isolate->counters()->for_in()->Increment(); - Handle<FixedArray> elements = GetKeysInFixedArrayFor(object, - INCLUDE_PROTOS); + Handle<FixedArray> elements = + GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, threw); return isolate->factory()->NewJSArrayWithElements(elements); } @@ -852,7 +711,7 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, isolate); } isolate->counters()->enum_cache_misses()->Increment(); - int num_enum = object->NumberOfEnumProperties(); + int num_enum = object->NumberOfLocalProperties(DONT_ENUM); Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum); Handle<FixedArray> sort_array = isolate->factory()->NewFixedArray(num_enum); Handle<DescriptorArray> descs = @@ -876,7 +735,7 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, ASSERT(storage->length() == index); return storage; } else { - int num_enum = object->NumberOfEnumProperties(); + int num_enum = object->NumberOfLocalProperties(DONT_ENUM); Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum); Handle<FixedArray> sort_array = isolate->factory()->NewFixedArray(num_enum); object->property_dictionary()->CopyEnumKeysTo(*storage, *sort_array); @@ -885,62 +744,29 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, } -Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table, - Handle<JSObject> key, - Handle<Object> value) { +Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table, + Handle<Object> key) { CALL_HEAP_FUNCTION(table->GetIsolate(), - table->Put(*key, *value), - ObjectHashTable); -} - - -bool EnsureCompiled(Handle<SharedFunctionInfo> shared, - ClearExceptionFlag flag) { - return shared->is_compiled() || CompileLazyShared(shared, flag); -} - - -static bool CompileLazyHelper(CompilationInfo* info, - ClearExceptionFlag flag) { - // Compile the source information to a code object. - ASSERT(info->IsOptimizing() || !info->shared_info()->is_compiled()); - ASSERT(!info->isolate()->has_pending_exception()); - bool result = Compiler::CompileLazy(info); - ASSERT(result != Isolate::Current()->has_pending_exception()); - if (!result && flag == CLEAR_EXCEPTION) { - info->isolate()->clear_pending_exception(); - } - return result; + table->Add(*key), + ObjectHashSet); } -bool CompileLazyShared(Handle<SharedFunctionInfo> shared, - ClearExceptionFlag flag) { - CompilationInfo info(shared); - return CompileLazyHelper(&info, flag); +Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table, + Handle<Object> key) { + CALL_HEAP_FUNCTION(table->GetIsolate(), + table->Remove(*key), + ObjectHashSet); } -bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag) { - bool result = true; - if (function->shared()->is_compiled()) { - function->ReplaceCode(function->shared()->code()); - function->shared()->set_code_age(0); - } else { - CompilationInfo info(function); - result = CompileLazyHelper(&info, flag); - ASSERT(!result || function->is_compiled()); - } - return result; +Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table, + Handle<Object> key, + Handle<Object> value) { + CALL_HEAP_FUNCTION(table->GetIsolate(), + table->Put(*key, *value), + ObjectHashTable); } -bool CompileOptimized(Handle<JSFunction> function, - int osr_ast_id, - ClearExceptionFlag flag) { - CompilationInfo info(function); - info.SetOptimizing(osr_ast_id); - return CompileLazyHelper(&info, flag); -} - } } // namespace v8::internal diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index 5674120643..42089134e4 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -167,18 +167,6 @@ class HandleScope { // an object of expected type, or the handle is an error if running out // of space or encountering an internal error. -void NormalizeProperties(Handle<JSObject> object, - PropertyNormalizationMode mode, - int expected_additional_properties); -Handle<SeededNumberDictionary> NormalizeElements(Handle<JSObject> object); -void TransformToFastProperties(Handle<JSObject> object, - int unused_property_fields); -MUST_USE_RESULT Handle<SeededNumberDictionary> SeededNumberDictionarySet( - Handle<SeededNumberDictionary> dictionary, - uint32_t index, - Handle<Object> value, - PropertyDetails details); - // Flattens a string. void FlattenString(Handle<String> str); @@ -186,12 +174,6 @@ void FlattenString(Handle<String> str); // string. Handle<String> FlattenGetString(Handle<String> str); -Handle<Object> SetProperty(Handle<JSReceiver> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes, - StrictModeFlag strict_mode); - Handle<Object> SetProperty(Handle<Object> object, Handle<Object> key, Handle<Object> value, @@ -203,78 +185,22 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object, Handle<Object> value, PropertyAttributes attributes); -Handle<Object> SetNormalizedProperty(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyDetails details); - Handle<Object> ForceDeleteProperty(Handle<JSObject> object, Handle<Object> key); -Handle<Object> SetLocalPropertyIgnoreAttributes( - Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes); - -// Used to set local properties on the object we totally control -// and which therefore has no accessors and alikes. -void SetLocalPropertyNoThrow(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes = NONE); - -Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object, - Handle<String> key, - Handle<Object> value, - PropertyAttributes attributes, - StrictModeFlag strict_mode); - -MUST_USE_RESULT Handle<Object> SetElement(Handle<JSObject> object, - uint32_t index, - Handle<Object> value, - StrictModeFlag strict_mode); - -Handle<Object> SetOwnElement(Handle<JSObject> object, - uint32_t index, - Handle<Object> value, - StrictModeFlag strict_mode); - Handle<Object> GetProperty(Handle<JSReceiver> obj, const char* name); Handle<Object> GetProperty(Handle<Object> obj, Handle<Object> key); -Handle<Object> GetProperty(Handle<JSReceiver> obj, - Handle<String> name, - LookupResult* result); - - -Handle<Object> GetElement(Handle<Object> obj, - uint32_t index); - Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver, Handle<JSObject> holder, Handle<String> name, PropertyAttributes* attributes); -Handle<Object> GetPrototype(Handle<Object> obj); - Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value); -// Return the object's hidden properties object. If the object has no hidden -// properties and HiddenPropertiesFlag::ALLOW_CREATION is passed, then a new -// hidden property object will be allocated. Otherwise Heap::undefined_value -// is returned. -Handle<Object> GetHiddenProperties(Handle<JSObject> obj, - JSObject::HiddenPropertiesFlag flag); - -int GetIdentityHash(Handle<JSObject> obj); - -Handle<Object> DeleteElement(Handle<JSObject> obj, uint32_t index); -Handle<Object> DeleteProperty(Handle<JSObject> obj, Handle<String> prop); - Handle<Object> LookupSingleCharacterStringFromCode(uint32_t index); Handle<JSObject> Copy(Handle<JSObject> obj); @@ -298,21 +224,23 @@ Handle<FixedArray> CalculateLineEnds(Handle<String> string, int GetScriptLineNumber(Handle<Script> script, int code_position); // The safe version does not make heap allocations but may work much slower. int GetScriptLineNumberSafe(Handle<Script> script, int code_position); +int GetScriptColumnNumber(Handle<Script> script, int code_position); // Computes the enumerable keys from interceptors. Used for debug mirrors and // by GetKeysInFixedArrayFor below. -v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver, +v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver, Handle<JSObject> object); -v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver, +v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver, Handle<JSObject> object); enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS }; // Computes the enumerable keys for a JSObject. Used for implementing // "for (n in object) { }". -Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object, - KeyCollectionType type); -Handle<JSArray> GetKeysFor(Handle<JSObject> object); +Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object, + KeyCollectionType type, + bool* threw); +Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw); Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, bool cache_result); @@ -326,7 +254,6 @@ Handle<String> SubString(Handle<String> str, int end, PretenureFlag pretenure = NOT_TENURED); - // Sets the expected number of properties for the function's instances. void SetExpectedNofProperties(Handle<JSFunction> func, int nof); @@ -345,28 +272,16 @@ Handle<JSGlobalProxy> ReinitializeJSGlobalProxy( Handle<Object> SetPrototype(Handle<JSFunction> function, Handle<Object> prototype); -Handle<Object> PreventExtensions(Handle<JSObject> object); +Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table, + Handle<Object> key); + +Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table, + Handle<Object> key); Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table, - Handle<JSObject> key, + Handle<Object> key, Handle<Object> value); -// Does lazy compilation of the given function. Returns true on success and -// false if the compilation resulted in a stack overflow. -enum ClearExceptionFlag { KEEP_EXCEPTION, CLEAR_EXCEPTION }; - -bool EnsureCompiled(Handle<SharedFunctionInfo> shared, - ClearExceptionFlag flag); - -bool CompileLazyShared(Handle<SharedFunctionInfo> shared, - ClearExceptionFlag flag); - -bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag); - -bool CompileOptimized(Handle<JSFunction> function, - int osr_ast_id, - ClearExceptionFlag flag); - class NoHandleAllocation BASE_EMBEDDED { public: #ifndef DEBUG diff --git a/deps/v8/src/hashmap.cc b/deps/v8/src/hashmap.cc index 1422afdc7e..0b404a97e1 100644 --- a/deps/v8/src/hashmap.cc +++ b/deps/v8/src/hashmap.cc @@ -36,13 +36,7 @@ namespace v8 { namespace internal { -Allocator HashMap::DefaultAllocator; - - -HashMap::HashMap() { - allocator_ = NULL; - match_ = NULL; -} +Allocator* HashMap::DefaultAllocator = ::new Allocator(); HashMap::HashMap(MatchFun match, diff --git a/deps/v8/src/hashmap.h b/deps/v8/src/hashmap.h index 5c13212eba..d2d1fafa32 100644 --- a/deps/v8/src/hashmap.h +++ b/deps/v8/src/hashmap.h @@ -46,19 +46,14 @@ class Allocator BASE_EMBEDDED { class HashMap { public: - static Allocator DefaultAllocator; + static Allocator* DefaultAllocator; typedef bool (*MatchFun) (void* key1, void* key2); - // Dummy constructor. This constructor doesn't set up the hash - // map properly so don't use it unless you have good reason (e.g., - // you know that the HashMap will never be used). - HashMap(); - // initial_capacity is the size of the initial hash map; // it must be a power of 2 (and thus must not be 0). explicit HashMap(MatchFun match, - Allocator* allocator = &DefaultAllocator, + Allocator* allocator = DefaultAllocator, uint32_t initial_capacity = 8); ~HashMap(); diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 7b666af5b0..39cdf139e8 100644 --- a/deps/v8/src/heap-inl.h +++ b/deps/v8/src/heap-inl.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -33,15 +33,51 @@ #include "list-inl.h" #include "objects.h" #include "v8-counters.h" +#include "store-buffer.h" +#include "store-buffer-inl.h" namespace v8 { namespace internal { void PromotionQueue::insert(HeapObject* target, int size) { + if (emergency_stack_ != NULL) { + emergency_stack_->Add(Entry(target, size)); + return; + } + + if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(rear_))) { + NewSpacePage* rear_page = + NewSpacePage::FromAddress(reinterpret_cast<Address>(rear_)); + ASSERT(!rear_page->prev_page()->is_anchor()); + rear_ = reinterpret_cast<intptr_t*>(rear_page->prev_page()->body_limit()); + ActivateGuardIfOnTheSamePage(); + } + + if (guard_) { + ASSERT(GetHeadPage() == + Page::FromAllocationTop(reinterpret_cast<Address>(limit_))); + + if ((rear_ - 2) < limit_) { + RelocateQueueHead(); + emergency_stack_->Add(Entry(target, size)); + return; + } + } + *(--rear_) = reinterpret_cast<intptr_t>(target); *(--rear_) = size; // Assert no overflow into live objects. - ASSERT(reinterpret_cast<Address>(rear_) >= HEAP->new_space()->top()); +#ifdef DEBUG + SemiSpace::AssertValidRange(HEAP->new_space()->top(), + reinterpret_cast<Address>(rear_)); +#endif +} + + +void PromotionQueue::ActivateGuardIfOnTheSamePage() { + guard_ = guard_ || + heap_->new_space()->active_space()->current_page()->address() == + GetHeadPage()->address(); } @@ -84,12 +120,13 @@ MaybeObject* Heap::AllocateAsciiSymbol(Vector<const char> str, // Allocate string. Object* result; { MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace()) - ? lo_space_->AllocateRaw(size) + ? lo_space_->AllocateRaw(size, NOT_EXECUTABLE) : old_data_space_->AllocateRaw(size); if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<HeapObject*>(result)->set_map(map); + // String maps are all immortal immovable objects. + reinterpret_cast<HeapObject*>(result)->set_map_no_write_barrier(map); // Set length and hash fields of the allocated string. String* answer = String::cast(result); answer->set_length(str.length()); @@ -117,7 +154,7 @@ MaybeObject* Heap::AllocateTwoByteSymbol(Vector<const uc16> str, // Allocate string. Object* result; { MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace()) - ? lo_space_->AllocateRaw(size) + ? lo_space_->AllocateRaw(size, NOT_EXECUTABLE) : old_data_space_->AllocateRaw(size); if (!maybe_result->ToObject(&result)) return maybe_result; } @@ -181,7 +218,7 @@ MaybeObject* Heap::AllocateRaw(int size_in_bytes, } else if (CODE_SPACE == space) { result = code_space_->AllocateRaw(size_in_bytes); } else if (LO_SPACE == space) { - result = lo_space_->AllocateRaw(size_in_bytes); + result = lo_space_->AllocateRaw(size_in_bytes, NOT_EXECUTABLE); } else if (CELL_SPACE == space) { result = cell_space_->AllocateRaw(size_in_bytes); } else { @@ -193,19 +230,21 @@ MaybeObject* Heap::AllocateRaw(int size_in_bytes, } -MaybeObject* Heap::NumberFromInt32(int32_t value) { +MaybeObject* Heap::NumberFromInt32( + int32_t value, PretenureFlag pretenure) { if (Smi::IsValid(value)) return Smi::FromInt(value); // Bypass NumberFromDouble to avoid various redundant checks. - return AllocateHeapNumber(FastI2D(value)); + return AllocateHeapNumber(FastI2D(value), pretenure); } -MaybeObject* Heap::NumberFromUint32(uint32_t value) { +MaybeObject* Heap::NumberFromUint32( + uint32_t value, PretenureFlag pretenure) { if ((int32_t)value >= 0 && Smi::IsValid((int32_t)value)) { return Smi::FromInt((int32_t)value); } // Bypass NumberFromDouble to avoid various redundant checks. - return AllocateHeapNumber(FastUI2D(value)); + return AllocateHeapNumber(FastUI2D(value), pretenure); } @@ -220,10 +259,8 @@ void Heap::FinalizeExternalString(String* string) { // Dispose of the C++ object if it has not already been disposed. if (*resource_addr != NULL) { (*resource_addr)->Dispose(); + *resource_addr = NULL; } - - // Clear the resource pointer in the string. - *resource_addr = NULL; } @@ -265,6 +302,11 @@ bool Heap::InNewSpace(Object* object) { } +bool Heap::InNewSpace(Address addr) { + return new_space_.Contains(addr); +} + + bool Heap::InFromSpace(Object* object) { return new_space_.FromSpaceContains(object); } @@ -275,29 +317,36 @@ bool Heap::InToSpace(Object* object) { } +bool Heap::OldGenerationAllocationLimitReached() { + if (!incremental_marking()->IsStopped()) return false; + return OldGenerationSpaceAvailable() < 0; +} + + bool Heap::ShouldBePromoted(Address old_address, int object_size) { // An object should be promoted if: // - the object has survived a scavenge operation or // - to space is already 25% full. - return old_address < new_space_.age_mark() - || (new_space_.Size() + object_size) >= (new_space_.Capacity() >> 2); + NewSpacePage* page = NewSpacePage::FromAddress(old_address); + Address age_mark = new_space_.age_mark(); + bool below_mark = page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK) && + (!page->ContainsLimit(age_mark) || old_address < age_mark); + return below_mark || (new_space_.Size() + object_size) >= + (new_space_.EffectiveCapacity() >> 2); } void Heap::RecordWrite(Address address, int offset) { - if (new_space_.Contains(address)) return; - ASSERT(!new_space_.FromSpaceContains(address)); - SLOW_ASSERT(Contains(address + offset)); - Page::FromAddress(address)->MarkRegionDirty(address + offset); + if (!InNewSpace(address)) store_buffer_.Mark(address + offset); } void Heap::RecordWrites(Address address, int start, int len) { - if (new_space_.Contains(address)) return; - ASSERT(!new_space_.FromSpaceContains(address)); - Page* page = Page::FromAddress(address); - page->SetRegionMarks(page->GetRegionMarks() | - page->GetRegionMaskForSpan(address + start, len * kPointerSize)); + if (!InNewSpace(address)) { + for (int i = 0; i < len; i++) { + store_buffer_.Mark(address + start + i * kPointerSize); + } + } } @@ -336,38 +385,12 @@ AllocationSpace Heap::TargetSpaceId(InstanceType type) { void Heap::CopyBlock(Address dst, Address src, int byte_size) { - ASSERT(IsAligned(byte_size, kPointerSize)); CopyWords(reinterpret_cast<Object**>(dst), reinterpret_cast<Object**>(src), byte_size / kPointerSize); } -void Heap::CopyBlockToOldSpaceAndUpdateRegionMarks(Address dst, - Address src, - int byte_size) { - ASSERT(IsAligned(byte_size, kPointerSize)); - - Page* page = Page::FromAddress(dst); - uint32_t marks = page->GetRegionMarks(); - - for (int remaining = byte_size / kPointerSize; - remaining > 0; - remaining--) { - Memory::Object_at(dst) = Memory::Object_at(src); - - if (InNewSpace(Memory::Object_at(dst))) { - marks |= page->GetRegionMaskForAddress(dst); - } - - dst += kPointerSize; - src += kPointerSize; - } - - page->SetRegionMarks(marks); -} - - void Heap::MoveBlock(Address dst, Address src, int byte_size) { ASSERT(IsAligned(byte_size, kPointerSize)); @@ -387,16 +410,6 @@ void Heap::MoveBlock(Address dst, Address src, int byte_size) { } -void Heap::MoveBlockToOldSpaceAndUpdateRegionMarks(Address dst, - Address src, - int byte_size) { - ASSERT(IsAligned(byte_size, kPointerSize)); - ASSERT((dst < src) || (dst >= (src + byte_size))); - - CopyBlockToOldSpaceAndUpdateRegionMarks(dst, src, byte_size); -} - - void Heap::ScavengePointer(HeapObject** p) { ScavengeObject(p, *p); } @@ -414,7 +427,9 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { // If the first word is a forwarding address, the object has already been // copied. if (first_word.IsForwardingAddress()) { - *p = first_word.ToForwardingAddress(); + HeapObject* dest = first_word.ToForwardingAddress(); + ASSERT(HEAP->InFromSpace(*p)); + *p = dest; return; } @@ -423,8 +438,10 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { } -bool Heap::CollectGarbage(AllocationSpace space) { - return CollectGarbage(space, SelectGarbageCollector(space)); +bool Heap::CollectGarbage(AllocationSpace space, const char* gc_reason) { + const char* collector_reason = NULL; + GarbageCollector collector = SelectGarbageCollector(space, &collector_reason); + return CollectGarbage(space, collector, gc_reason, collector_reason); } @@ -448,7 +465,7 @@ MaybeObject* Heap::PrepareForCompare(String* str) { int Heap::AdjustAmountOfExternalAllocatedMemory(int change_in_bytes) { - ASSERT(HasBeenSetup()); + ASSERT(HasBeenSetUp()); int amount = amount_of_external_allocated_memory_ + change_in_bytes; if (change_in_bytes >= 0) { // Avoid overflow. @@ -459,7 +476,7 @@ int Heap::AdjustAmountOfExternalAllocatedMemory(int change_in_bytes) { amount_of_external_allocated_memory_ - amount_of_external_allocated_memory_at_last_global_gc_; if (amount_since_last_global_gc > external_allocation_limit_) { - CollectAllGarbage(false); + CollectAllGarbage(kNoGCFlags, "external memory allocation limit reached"); } } else { // Avoid underflow. @@ -476,6 +493,7 @@ void Heap::SetLastScriptId(Object* last_script_id) { roots_[kLastScriptIdRootIndex] = last_script_id; } + Isolate* Heap::isolate() { return reinterpret_cast<Isolate*>(reinterpret_cast<intptr_t>(this) - reinterpret_cast<size_t>(reinterpret_cast<Isolate*>(4)->heap()) + 4); @@ -489,7 +507,6 @@ Isolate* Heap::isolate() { #define GC_GREEDY_CHECK() { } #endif - // Calls the FUNCTION_CALL function and retries it up to three times // to guarantee that any allocations performed during the call will // succeed if there's enough memory. @@ -508,7 +525,8 @@ Isolate* Heap::isolate() { } \ if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \ ISOLATE->heap()->CollectGarbage(Failure::cast(__maybe_object__)-> \ - allocation_space()); \ + allocation_space(), \ + "allocation failure"); \ __maybe_object__ = FUNCTION_CALL; \ if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \ if (__maybe_object__->IsOutOfMemory()) { \ @@ -516,7 +534,7 @@ Isolate* Heap::isolate() { } \ if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \ ISOLATE->counters()->gc_last_resort_from_handles()->Increment(); \ - ISOLATE->heap()->CollectAllAvailableGarbage(); \ + ISOLATE->heap()->CollectAllAvailableGarbage("last resort gc"); \ { \ AlwaysAllocateScope __scope__; \ __maybe_object__ = FUNCTION_CALL; \ @@ -581,11 +599,11 @@ void ExternalStringTable::Verify() { #ifdef DEBUG for (int i = 0; i < new_space_strings_.length(); ++i) { ASSERT(heap_->InNewSpace(new_space_strings_[i])); - ASSERT(new_space_strings_[i] != HEAP->raw_unchecked_null_value()); + ASSERT(new_space_strings_[i] != HEAP->raw_unchecked_the_hole_value()); } for (int i = 0; i < old_space_strings_.length(); ++i) { ASSERT(!heap_->InNewSpace(old_space_strings_[i])); - ASSERT(old_space_strings_[i] != HEAP->raw_unchecked_null_value()); + ASSERT(old_space_strings_[i] != HEAP->raw_unchecked_the_hole_value()); } #endif } @@ -600,7 +618,9 @@ void ExternalStringTable::AddOldString(String* string) { void ExternalStringTable::ShrinkNewStrings(int position) { new_space_strings_.Rewind(position); - Verify(); + if (FLAG_verify_heap) { + Verify(); + } } @@ -683,20 +703,94 @@ MaybeObject* TranscendentalCache::SubCache::Get(double input) { } -Heap* _inline_get_heap_() { - return HEAP; +AlwaysAllocateScope::AlwaysAllocateScope() { + // We shouldn't hit any nested scopes, because that requires + // non-handle code to call handle code. The code still works but + // performance will degrade, so we want to catch this situation + // in debug mode. + ASSERT(HEAP->always_allocate_scope_depth_ == 0); + HEAP->always_allocate_scope_depth_++; +} + + +AlwaysAllocateScope::~AlwaysAllocateScope() { + HEAP->always_allocate_scope_depth_--; + ASSERT(HEAP->always_allocate_scope_depth_ == 0); +} + + +LinearAllocationScope::LinearAllocationScope() { + HEAP->linear_allocation_scope_depth_++; +} + + +LinearAllocationScope::~LinearAllocationScope() { + HEAP->linear_allocation_scope_depth_--; + ASSERT(HEAP->linear_allocation_scope_depth_ >= 0); +} + + +#ifdef DEBUG +void VerifyPointersVisitor::VisitPointers(Object** start, Object** end) { + for (Object** current = start; current < end; current++) { + if ((*current)->IsHeapObject()) { + HeapObject* object = HeapObject::cast(*current); + ASSERT(HEAP->Contains(object)); + ASSERT(object->map()->IsMap()); + } + } +} +#endif + + +double GCTracer::SizeOfHeapObjects() { + return (static_cast<double>(HEAP->SizeOfObjects())) / MB; } -void MarkCompactCollector::SetMark(HeapObject* obj) { - tracer_->increment_marked_count(); #ifdef DEBUG - UpdateLiveObjectCount(obj); +DisallowAllocationFailure::DisallowAllocationFailure() { + old_state_ = HEAP->disallow_allocation_failure_; + HEAP->disallow_allocation_failure_ = true; +} + + +DisallowAllocationFailure::~DisallowAllocationFailure() { + HEAP->disallow_allocation_failure_ = old_state_; +} #endif - obj->SetMark(); + + +#ifdef DEBUG +AssertNoAllocation::AssertNoAllocation() { + old_state_ = HEAP->allow_allocation(false); } +AssertNoAllocation::~AssertNoAllocation() { + HEAP->allow_allocation(old_state_); +} + + +DisableAssertNoAllocation::DisableAssertNoAllocation() { + old_state_ = HEAP->allow_allocation(true); +} + + +DisableAssertNoAllocation::~DisableAssertNoAllocation() { + HEAP->allow_allocation(old_state_); +} + +#else + +AssertNoAllocation::AssertNoAllocation() { } +AssertNoAllocation::~AssertNoAllocation() { } +DisableAssertNoAllocation::DisableAssertNoAllocation() { } +DisableAssertNoAllocation::~DisableAssertNoAllocation() { } + +#endif + + } } // namespace v8::internal #endif // V8_HEAP_INL_H_ diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index 7e613e9173..8be6f27685 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -51,7 +51,7 @@ void HeapProfiler::ResetSnapshots() { } -void HeapProfiler::Setup() { +void HeapProfiler::SetUp() { Isolate* isolate = Isolate::Current(); if (isolate->heap_profiler() == NULL) { isolate->set_heap_profiler(new HeapProfiler()); @@ -114,7 +114,6 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, bool generation_completed = true; switch (s_type) { case HeapSnapshot::kFull: { - HEAP->CollectAllGarbage(true); HeapSnapshotGenerator generator(result, control); generation_completed = generator.GenerateSnapshot(); break; diff --git a/deps/v8/src/heap-profiler.h b/deps/v8/src/heap-profiler.h index b1bc91c307..ef5c4f4b4a 100644 --- a/deps/v8/src/heap-profiler.h +++ b/deps/v8/src/heap-profiler.h @@ -48,7 +48,7 @@ class HeapSnapshotsCollection; // to generate .hp files for use by the GHC/Valgrind tool hp2ps. class HeapProfiler { public: - static void Setup(); + static void SetUp(); static void TearDown(); static HeapSnapshot* TakeSnapshot(const char* name, diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 82c6636410..b082886394 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,13 +36,16 @@ #include "deoptimizer.h" #include "global-handles.h" #include "heap-profiler.h" +#include "incremental-marking.h" #include "liveobjectlist-inl.h" #include "mark-compact.h" #include "natives.h" #include "objects-visiting.h" +#include "objects-visiting-inl.h" #include "runtime-profiler.h" #include "scopeinfo.h" #include "snapshot.h" +#include "store-buffer.h" #include "v8threads.h" #include "vm-state-inl.h" #if V8_TARGET_ARCH_ARM && !V8_INTERPRETED_REGEXP @@ -58,10 +61,6 @@ namespace v8 { namespace internal { -static const intptr_t kMinimumPromotionLimit = 2 * MB; -static const intptr_t kMinimumAllocationLimit = 8 * MB; - - static Mutex* gc_initializer_mutex = OS::CreateMutex(); @@ -70,27 +69,21 @@ Heap::Heap() // semispace_size_ should be a power of 2 and old_generation_size_ should be // a multiple of Page::kPageSize. #if defined(ANDROID) - reserved_semispace_size_(2*MB), - max_semispace_size_(2*MB), - initial_semispace_size_(128*KB), - max_old_generation_size_(192*MB), - max_executable_size_(max_old_generation_size_), +#define LUMP_OF_MEMORY (128 * KB) code_range_size_(0), #elif defined(V8_TARGET_ARCH_X64) - reserved_semispace_size_(16*MB), - max_semispace_size_(16*MB), - initial_semispace_size_(1*MB), - max_old_generation_size_(1400*MB), - max_executable_size_(256*MB), +#define LUMP_OF_MEMORY (2 * MB) code_range_size_(512*MB), #else - reserved_semispace_size_(8*MB), - max_semispace_size_(8*MB), - initial_semispace_size_(512*KB), - max_old_generation_size_(700*MB), - max_executable_size_(128*MB), +#define LUMP_OF_MEMORY MB code_range_size_(0), #endif + reserved_semispace_size_(8 * Max(LUMP_OF_MEMORY, Page::kPageSize)), + max_semispace_size_(8 * Max(LUMP_OF_MEMORY, Page::kPageSize)), + initial_semispace_size_(Page::kPageSize), + max_old_generation_size_(700ul * LUMP_OF_MEMORY), + max_executable_size_(128l * LUMP_OF_MEMORY), + // Variables set based on semispace_size_ and old_generation_size_ in // ConfigureHeap (survived_since_last_expansion_, external_allocation_limit_) // Will be 4 * reserved_semispace_size_ to ensure that young @@ -100,6 +93,7 @@ Heap::Heap() always_allocate_scope_depth_(0), linear_allocation_scope_depth_(0), contexts_disposed_(0), + scan_on_scavenge_pages_(0), new_space_(this), old_pointer_space_(NULL), old_data_space_(NULL), @@ -109,7 +103,6 @@ Heap::Heap() lo_space_(NULL), gc_state_(NOT_IN_GC), gc_post_processing_depth_(0), - mc_count_(0), ms_count_(0), gc_count_(0), unflattened_strings_length_(0), @@ -119,12 +112,16 @@ Heap::Heap() disallow_allocation_failure_(false), debug_utils_(NULL), #endif // DEBUG + new_space_high_promotion_mode_active_(false), old_gen_promotion_limit_(kMinimumPromotionLimit), old_gen_allocation_limit_(kMinimumAllocationLimit), + old_gen_limit_factor_(1), + size_of_old_gen_at_last_old_space_gc_(0), external_allocation_limit_(0), amount_of_external_allocated_memory_(0), amount_of_external_allocated_memory_at_last_global_gc_(0), old_gen_exhausted_(false), + store_buffer_rebuilder_(store_buffer()), hidden_symbol_(NULL), global_gc_prologue_callback_(NULL), global_gc_epilogue_callback_(NULL), @@ -141,12 +138,20 @@ Heap::Heap() min_in_mutator_(kMaxInt), alive_after_last_gc_(0), last_gc_end_timestamp_(0.0), - page_watermark_invalidated_mark_(1 << Page::WATERMARK_INVALIDATED), + store_buffer_(this), + marking_(this), + incremental_marking_(this), number_idle_notifications_(0), last_idle_notification_gc_count_(0), last_idle_notification_gc_count_init_(false), + idle_notification_will_schedule_next_gc_(false), + mark_sweeps_since_idle_round_started_(0), + ms_count_at_last_idle_notification_(0), + gc_count_at_last_idle_gc_(0), + scavenges_since_last_idle_round_(kIdleScavengeThreshold), + promotion_queue_(this), configured_(false), - is_safe_to_read_maps_(true) { + chunks_queued_for_free_(NULL) { // Allow build-time customization of the max semispace size. Building // V8 with snapshots and a non-default max semispace size is much // easier if you can define it as part of the build environment. @@ -171,7 +176,7 @@ Heap::Heap() intptr_t Heap::Capacity() { - if (!HasBeenSetup()) return 0; + if (!HasBeenSetUp()) return 0; return new_space_.Capacity() + old_pointer_space_->Capacity() + @@ -183,7 +188,7 @@ intptr_t Heap::Capacity() { intptr_t Heap::CommittedMemory() { - if (!HasBeenSetup()) return 0; + if (!HasBeenSetUp()) return 0; return new_space_.CommittedMemory() + old_pointer_space_->CommittedMemory() + @@ -195,14 +200,14 @@ intptr_t Heap::CommittedMemory() { } intptr_t Heap::CommittedMemoryExecutable() { - if (!HasBeenSetup()) return 0; + if (!HasBeenSetUp()) return 0; return isolate()->memory_allocator()->SizeExecutable(); } intptr_t Heap::Available() { - if (!HasBeenSetup()) return 0; + if (!HasBeenSetUp()) return 0; return new_space_.Available() + old_pointer_space_->Available() + @@ -213,7 +218,7 @@ intptr_t Heap::Available() { } -bool Heap::HasBeenSetup() { +bool Heap::HasBeenSetUp() { return old_pointer_space_ != NULL && old_data_space_ != NULL && code_space_ != NULL && @@ -224,42 +229,26 @@ bool Heap::HasBeenSetup() { int Heap::GcSafeSizeOfOldObject(HeapObject* object) { - ASSERT(!HEAP->InNewSpace(object)); // Code only works for old objects. - ASSERT(!HEAP->mark_compact_collector()->are_map_pointers_encoded()); - MapWord map_word = object->map_word(); - map_word.ClearMark(); - map_word.ClearOverflow(); - return object->SizeFromMap(map_word.ToMap()); -} - - -int Heap::GcSafeSizeOfOldObjectWithEncodedMap(HeapObject* object) { - ASSERT(!HEAP->InNewSpace(object)); // Code only works for old objects. - ASSERT(HEAP->mark_compact_collector()->are_map_pointers_encoded()); - uint32_t marker = Memory::uint32_at(object->address()); - if (marker == MarkCompactCollector::kSingleFreeEncoding) { - return kIntSize; - } else if (marker == MarkCompactCollector::kMultiFreeEncoding) { - return Memory::int_at(object->address() + kIntSize); - } else { - MapWord map_word = object->map_word(); - Address map_address = map_word.DecodeMapAddress(HEAP->map_space()); - Map* map = reinterpret_cast<Map*>(HeapObject::FromAddress(map_address)); - return object->SizeFromMap(map); + if (IntrusiveMarking::IsMarked(object)) { + return IntrusiveMarking::SizeOfMarkedObject(object); } + return object->SizeFromMap(object->map()); } -GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space) { +GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space, + const char** reason) { // Is global GC requested? if (space != NEW_SPACE || FLAG_gc_global) { isolate_->counters()->gc_compactor_caused_by_request()->Increment(); + *reason = "GC in old space requested"; return MARK_COMPACTOR; } // Is enough data promoted to justify a global GC? if (OldGenerationPromotionLimitReached()) { isolate_->counters()->gc_compactor_caused_by_promoted_data()->Increment(); + *reason = "promotion limit reached"; return MARK_COMPACTOR; } @@ -267,6 +256,7 @@ GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space) { if (old_gen_exhausted_) { isolate_->counters()-> gc_compactor_caused_by_oldspace_exhaustion()->Increment(); + *reason = "old generations exhausted"; return MARK_COMPACTOR; } @@ -282,10 +272,12 @@ GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space) { if (isolate_->memory_allocator()->MaxAvailable() <= new_space_.Size()) { isolate_->counters()-> gc_compactor_caused_by_oldspace_exhaustion()->Increment(); + *reason = "scavenge might not succeed"; return MARK_COMPACTOR; } // Default + *reason = NULL; return SCAVENGER; } @@ -400,6 +392,7 @@ void Heap::GarbageCollectionPrologue() { #endif // DEBUG LiveObjectList::GCPrologue(); + store_buffer()->GCPrologue(); } intptr_t Heap::SizeOfObjects() { @@ -412,6 +405,7 @@ intptr_t Heap::SizeOfObjects() { } void Heap::GarbageCollectionEpilogue() { + store_buffer()->GCEpilogue(); LiveObjectList::GCEpilogue(); #ifdef DEBUG allow_allocation(true); @@ -443,22 +437,20 @@ void Heap::GarbageCollectionEpilogue() { } -void Heap::CollectAllGarbage(bool force_compaction) { +void Heap::CollectAllGarbage(int flags, const char* gc_reason) { // Since we are ignoring the return value, the exact choice of space does // not matter, so long as we do not specify NEW_SPACE, which would not // cause a full GC. - mark_compact_collector_.SetForceCompaction(force_compaction); - CollectGarbage(OLD_POINTER_SPACE); - mark_compact_collector_.SetForceCompaction(false); + mark_compact_collector_.SetFlags(flags); + CollectGarbage(OLD_POINTER_SPACE, gc_reason); + mark_compact_collector_.SetFlags(kNoGCFlags); } -void Heap::CollectAllAvailableGarbage() { +void Heap::CollectAllAvailableGarbage(const char* gc_reason) { // Since we are ignoring the return value, the exact choice of space does // not matter, so long as we do not specify NEW_SPACE, which would not // cause a full GC. - mark_compact_collector()->SetForceCompaction(true); - // Major GC would invoke weak handle callbacks on weakly reachable // handles, but won't collect weakly reachable objects until next // major GC. Therefore if we collect aggressively and weak handle callback @@ -467,17 +459,27 @@ void Heap::CollectAllAvailableGarbage() { // Note: as weak callbacks can execute arbitrary code, we cannot // hope that eventually there will be no weak callbacks invocations. // Therefore stop recollecting after several attempts. + mark_compact_collector()->SetFlags(kMakeHeapIterableMask | + kReduceMemoryFootprintMask); + isolate_->compilation_cache()->Clear(); const int kMaxNumberOfAttempts = 7; for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) { - if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR)) { + if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR, gc_reason, NULL)) { break; } } - mark_compact_collector()->SetForceCompaction(false); + mark_compact_collector()->SetFlags(kNoGCFlags); + new_space_.Shrink(); + UncommitFromSpace(); + Shrink(); + incremental_marking()->UncommitMarkingDeque(); } -bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) { +bool Heap::CollectGarbage(AllocationSpace space, + GarbageCollector collector, + const char* gc_reason, + const char* collector_reason) { // The VM is in the GC state until exiting this function. VMState state(isolate_, GC); @@ -490,9 +492,27 @@ bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) { allocation_timeout_ = Max(6, FLAG_gc_interval); #endif + if (collector == SCAVENGER && !incremental_marking()->IsStopped()) { + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Scavenge during marking.\n"); + } + } + + if (collector == MARK_COMPACTOR && + !mark_compact_collector()->PreciseSweepingRequired() && + !incremental_marking()->IsStopped() && + !incremental_marking()->should_hurry() && + FLAG_incremental_marking_steps) { + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Delaying MarkSweep.\n"); + } + collector = SCAVENGER; + collector_reason = "incremental marking delaying mark-sweep"; + } + bool next_gc_likely_to_collect_more = false; - { GCTracer tracer(this); + { GCTracer tracer(this, gc_reason, collector_reason); GarbageCollectionPrologue(); // The GC count was incremented in the prologue. Tell the tracer about // it. @@ -512,13 +532,24 @@ bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) { GarbageCollectionEpilogue(); } + ASSERT(collector == SCAVENGER || incremental_marking()->IsStopped()); + if (incremental_marking()->IsStopped()) { + if (incremental_marking()->WorthActivating() && NextGCIsLikelyToBeFull()) { + incremental_marking()->Start(); + } + } + return next_gc_likely_to_collect_more; } void Heap::PerformScavenge() { - GCTracer tracer(this); - PerformGarbageCollection(SCAVENGER, &tracer); + GCTracer tracer(this, NULL, NULL); + if (incremental_marking()->IsStopped()) { + PerformGarbageCollection(SCAVENGER, &tracer); + } else { + PerformGarbageCollection(MARK_COMPACTOR, &tracer); + } } @@ -531,7 +562,7 @@ class SymbolTableVerifier : public ObjectVisitor { for (Object** p = start; p < end; p++) { if ((*p)->IsHeapObject()) { // Check that the symbol is actually a symbol. - ASSERT((*p)->IsNull() || (*p)->IsUndefined() || (*p)->IsSymbol()); + ASSERT((*p)->IsTheHole() || (*p)->IsUndefined() || (*p)->IsSymbol()); } } } @@ -568,27 +599,33 @@ void Heap::ReserveSpace( while (gc_performed && counter++ < kThreshold) { gc_performed = false; if (!new_space->ReserveSpace(new_space_size)) { - Heap::CollectGarbage(NEW_SPACE); + Heap::CollectGarbage(NEW_SPACE, + "failed to reserve space in the new space"); gc_performed = true; } if (!old_pointer_space->ReserveSpace(pointer_space_size)) { - Heap::CollectGarbage(OLD_POINTER_SPACE); + Heap::CollectGarbage(OLD_POINTER_SPACE, + "failed to reserve space in the old pointer space"); gc_performed = true; } if (!(old_data_space->ReserveSpace(data_space_size))) { - Heap::CollectGarbage(OLD_DATA_SPACE); + Heap::CollectGarbage(OLD_DATA_SPACE, + "failed to reserve space in the old data space"); gc_performed = true; } if (!(code_space->ReserveSpace(code_space_size))) { - Heap::CollectGarbage(CODE_SPACE); + Heap::CollectGarbage(CODE_SPACE, + "failed to reserve space in the code space"); gc_performed = true; } if (!(map_space->ReserveSpace(map_space_size))) { - Heap::CollectGarbage(MAP_SPACE); + Heap::CollectGarbage(MAP_SPACE, + "failed to reserve space in the map space"); gc_performed = true; } if (!(cell_space->ReserveSpace(cell_space_size))) { - Heap::CollectGarbage(CELL_SPACE); + Heap::CollectGarbage(CELL_SPACE, + "failed to reserve space in the cell space"); gc_performed = true; } // We add a slack-factor of 2 in order to have space for a series of @@ -600,7 +637,8 @@ void Heap::ReserveSpace( large_object_size += cell_space_size + map_space_size + code_space_size + data_space_size + pointer_space_size; if (!(lo_space->ReserveSpace(large_object_size))) { - Heap::CollectGarbage(LO_SPACE); + Heap::CollectGarbage(LO_SPACE, + "failed to reserve space in the large object space"); gc_performed = true; } } @@ -617,13 +655,6 @@ void Heap::EnsureFromSpaceIsCommitted() { // Committing memory to from space failed. // Try shrinking and try again. - PagedSpaces spaces; - for (PagedSpace* space = spaces.next(); - space != NULL; - space = spaces.next()) { - space->RelinkPageListInChunkOrder(true); - } - Shrink(); if (new_space_.CommitFromSpaceIfNeeded()) return; @@ -638,13 +669,17 @@ void Heap::ClearJSFunctionResultCaches() { Object* context = global_contexts_list_; while (!context->IsUndefined()) { - // Get the caches for this context: - FixedArray* caches = - Context::cast(context)->jsfunction_result_caches(); - // Clear the caches: - int length = caches->length(); - for (int i = 0; i < length; i++) { - JSFunctionResultCache::cast(caches->get(i))->Clear(); + // Get the caches for this context. GC can happen when the context + // is not fully initialized, so the caches can be undefined. + Object* caches_or_undefined = + Context::cast(context)->get(Context::JSFUNCTION_RESULT_CACHES_INDEX); + if (!caches_or_undefined->IsUndefined()) { + FixedArray* caches = FixedArray::cast(caches_or_undefined); + // Clear the caches: + int length = caches->length(); + for (int i = 0; i < length; i++) { + JSFunctionResultCache::cast(caches->get(i))->Clear(); + } } // Get the next context: context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); @@ -654,45 +689,42 @@ void Heap::ClearJSFunctionResultCaches() { void Heap::ClearNormalizedMapCaches() { - if (isolate_->bootstrapper()->IsActive()) return; + if (isolate_->bootstrapper()->IsActive() && + !incremental_marking()->IsMarking()) { + return; + } Object* context = global_contexts_list_; while (!context->IsUndefined()) { - Context::cast(context)->normalized_map_cache()->Clear(); + // GC can happen when the context is not fully initialized, + // so the cache can be undefined. + Object* cache = + Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX); + if (!cache->IsUndefined()) { + NormalizedMapCache::cast(cache)->Clear(); + } context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); } } -#ifdef DEBUG - -enum PageWatermarkValidity { - ALL_VALID, - ALL_INVALID -}; - -static void VerifyPageWatermarkValidity(PagedSpace* space, - PageWatermarkValidity validity) { - PageIterator it(space, PageIterator::PAGES_IN_USE); - bool expected_value = (validity == ALL_VALID); - while (it.has_next()) { - Page* page = it.next(); - ASSERT(page->IsWatermarkValid() == expected_value); - } -} -#endif - void Heap::UpdateSurvivalRateTrend(int start_new_space_size) { double survival_rate = (static_cast<double>(young_survivors_after_last_gc_) * 100) / start_new_space_size; - if (survival_rate > kYoungSurvivalRateThreshold) { + if (survival_rate > kYoungSurvivalRateHighThreshold) { high_survival_rate_period_length_++; } else { high_survival_rate_period_length_ = 0; } + if (survival_rate < kYoungSurvivalRateLowThreshold) { + low_survival_rate_period_length_++; + } else { + low_survival_rate_period_length_ = 0; + } + double survival_rate_diff = survival_rate_ - survival_rate; if (survival_rate_diff > kYoungSurvivalRateAllowedDeviation) { @@ -714,7 +746,9 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, PROFILE(isolate_, CodeMovingGCEvent()); } - VerifySymbolTable(); + if (FLAG_verify_heap) { + VerifySymbolTable(); + } if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) { ASSERT(!allocation_allowed_); GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL); @@ -734,6 +768,13 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, int start_new_space_size = Heap::new_space()->SizeAsInt(); + if (IsHighSurvivalRate()) { + // We speed up the incremental marker if it is running so that it + // does not fall behind the rate of promotion, which would cause a + // constantly growing old space. + incremental_marking()->NotifyOfHighPromotionRate(); + } + if (collector == MARK_COMPACTOR) { // Perform mark-sweep with optional compaction. MarkCompact(tracer); @@ -743,11 +784,7 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, UpdateSurvivalRateTrend(start_new_space_size); - intptr_t old_gen_size = PromotedSpaceSize(); - old_gen_promotion_limit_ = - old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3); - old_gen_allocation_limit_ = - old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2); + size_of_old_gen_at_last_old_space_gc_ = PromotedSpaceSize(); if (high_survival_rate_during_scavenges && IsStableOrIncreasingSurvivalTrend()) { @@ -757,10 +794,16 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, // In this case we aggressively raise old generation memory limits to // postpone subsequent mark-sweep collection and thus trade memory // space for the mutation speed. - old_gen_promotion_limit_ *= 2; - old_gen_allocation_limit_ *= 2; + old_gen_limit_factor_ = 2; + } else { + old_gen_limit_factor_ = 1; } + old_gen_promotion_limit_ = + OldGenPromotionLimit(size_of_old_gen_at_last_old_space_gc_); + old_gen_allocation_limit_ = + OldGenAllocationLimit(size_of_old_gen_at_last_old_space_gc_); + old_gen_exhausted_ = false; } else { tracer_ = tracer; @@ -770,6 +813,37 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, UpdateSurvivalRateTrend(start_new_space_size); } + if (!new_space_high_promotion_mode_active_ && + new_space_.Capacity() == new_space_.MaximumCapacity() && + IsStableOrIncreasingSurvivalTrend() && + IsHighSurvivalRate()) { + // Stable high survival rates even though young generation is at + // maximum capacity indicates that most objects will be promoted. + // To decrease scavenger pauses and final mark-sweep pauses, we + // have to limit maximal capacity of the young generation. + new_space_high_promotion_mode_active_ = true; + if (FLAG_trace_gc) { + PrintF("Limited new space size due to high promotion rate: %d MB\n", + new_space_.InitialCapacity() / MB); + } + } else if (new_space_high_promotion_mode_active_ && + IsStableOrDecreasingSurvivalTrend() && + IsLowSurvivalRate()) { + // Decreasing low survival rates might indicate that the above high + // promotion mode is over and we should allow the young generation + // to grow again. + new_space_high_promotion_mode_active_ = false; + if (FLAG_trace_gc) { + PrintF("Unlimited new space size due to low promotion rate: %d MB\n", + new_space_.MaximumCapacity() / MB); + } + } + + if (new_space_high_promotion_mode_active_ && + new_space_.Capacity() > new_space_.InitialCapacity()) { + new_space_.Shrink(); + } + isolate_->counters()->objs_since_last_young()->Set(0); gc_post_processing_depth_++; @@ -789,9 +863,7 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, amount_of_external_allocated_memory_; } - GCCallbackFlags callback_flags = tracer->is_compacting() - ? kGCCallbackFlagCompacted - : kNoGCCallbackFlags; + GCCallbackFlags callback_flags = kNoGCCallbackFlags; for (int i = 0; i < gc_epilogue_callbacks_.length(); ++i) { if (gc_type & gc_epilogue_callbacks_[i].gc_type) { gc_epilogue_callbacks_[i].callback(gc_type, callback_flags); @@ -803,7 +875,9 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector, GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL); global_gc_epilogue_callback_(); } - VerifySymbolTable(); + if (FLAG_verify_heap) { + VerifySymbolTable(); + } return next_gc_likely_to_collect_more; } @@ -815,34 +889,26 @@ void Heap::MarkCompact(GCTracer* tracer) { mark_compact_collector_.Prepare(tracer); - bool is_compacting = mark_compact_collector_.IsCompacting(); - - if (is_compacting) { - mc_count_++; - } else { - ms_count_++; - } - tracer->set_full_gc_count(mc_count_ + ms_count_); + ms_count_++; + tracer->set_full_gc_count(ms_count_); - MarkCompactPrologue(is_compacting); + MarkCompactPrologue(); - is_safe_to_read_maps_ = false; mark_compact_collector_.CollectGarbage(); - is_safe_to_read_maps_ = true; LOG(isolate_, ResourceEvent("markcompact", "end")); gc_state_ = NOT_IN_GC; - Shrink(); - isolate_->counters()->objs_since_last_full()->Set(0); contexts_disposed_ = 0; + + isolate_->set_context_exit_happened(false); } -void Heap::MarkCompactPrologue(bool is_compacting) { +void Heap::MarkCompactPrologue() { // At any old GC clear the keyed lookup cache to enable collection of unused // maps. isolate_->keyed_lookup_cache()->Clear(); @@ -854,7 +920,7 @@ void Heap::MarkCompactPrologue(bool is_compacting) { CompletelyClearInstanceofCache(); - if (is_compacting) FlushNumberStringCache(); + FlushNumberStringCache(); if (FLAG_cleanup_code_caches_at_gc) { polymorphic_code_cache()->set_cache(undefined_value()); } @@ -864,13 +930,8 @@ void Heap::MarkCompactPrologue(bool is_compacting) { Object* Heap::FindCodeObject(Address a) { - Object* obj = NULL; // Initialization to please compiler. - { MaybeObject* maybe_obj = code_space_->FindObject(a); - if (!maybe_obj->ToObject(&obj)) { - obj = lo_space_->FindObject(a)->ToObjectUnchecked(); - } - } - return obj; + return isolate()->inner_pointer_to_code_cache()-> + GcSafeFindCodeForInnerPointer(a); } @@ -918,23 +979,29 @@ static void VerifyNonPointerSpacePointers() { // do not expect them. VerifyNonPointerSpacePointersVisitor v; HeapObjectIterator code_it(HEAP->code_space()); - for (HeapObject* object = code_it.next(); - object != NULL; object = code_it.next()) + for (HeapObject* object = code_it.Next(); + object != NULL; object = code_it.Next()) object->Iterate(&v); - HeapObjectIterator data_it(HEAP->old_data_space()); - for (HeapObject* object = data_it.next(); - object != NULL; object = data_it.next()) - object->Iterate(&v); + // The old data space was normally swept conservatively so that the iterator + // doesn't work, so we normally skip the next bit. + if (!HEAP->old_data_space()->was_swept_conservatively()) { + HeapObjectIterator data_it(HEAP->old_data_space()); + for (HeapObject* object = data_it.Next(); + object != NULL; object = data_it.Next()) + object->Iterate(&v); + } } #endif void Heap::CheckNewSpaceExpansionCriteria() { if (new_space_.Capacity() < new_space_.MaximumCapacity() && - survived_since_last_expansion_ > new_space_.Capacity()) { - // Grow the size of new space if there is room to grow and enough - // data has survived scavenge since the last expansion. + survived_since_last_expansion_ > new_space_.Capacity() && + !new_space_high_promotion_mode_active_) { + // Grow the size of new space if there is room to grow, enough data + // has survived scavenge since the last expansion and we are not in + // high promotion mode. new_space_.Grow(); survived_since_last_expansion_ = 0; } @@ -947,28 +1014,106 @@ static bool IsUnscavengedHeapObject(Heap* heap, Object** p) { } -void Heap::Scavenge() { -#ifdef DEBUG - if (FLAG_enable_slow_asserts) VerifyNonPointerSpacePointers(); -#endif +void Heap::ScavengeStoreBufferCallback( + Heap* heap, + MemoryChunk* page, + StoreBufferEvent event) { + heap->store_buffer_rebuilder_.Callback(page, event); +} + + +void StoreBufferRebuilder::Callback(MemoryChunk* page, StoreBufferEvent event) { + if (event == kStoreBufferStartScanningPagesEvent) { + start_of_current_page_ = NULL; + current_page_ = NULL; + } else if (event == kStoreBufferScanningPageEvent) { + if (current_page_ != NULL) { + // If this page already overflowed the store buffer during this iteration. + if (current_page_->scan_on_scavenge()) { + // Then we should wipe out the entries that have been added for it. + store_buffer_->SetTop(start_of_current_page_); + } else if (store_buffer_->Top() - start_of_current_page_ >= + (store_buffer_->Limit() - store_buffer_->Top()) >> 2) { + // Did we find too many pointers in the previous page? The heuristic is + // that no page can take more then 1/5 the remaining slots in the store + // buffer. + current_page_->set_scan_on_scavenge(true); + store_buffer_->SetTop(start_of_current_page_); + } else { + // In this case the page we scanned took a reasonable number of slots in + // the store buffer. It has now been rehabilitated and is no longer + // marked scan_on_scavenge. + ASSERT(!current_page_->scan_on_scavenge()); + } + } + start_of_current_page_ = store_buffer_->Top(); + current_page_ = page; + } else if (event == kStoreBufferFullEvent) { + // The current page overflowed the store buffer again. Wipe out its entries + // in the store buffer and mark it scan-on-scavenge again. This may happen + // several times while scanning. + if (current_page_ == NULL) { + // Store Buffer overflowed while scanning promoted objects. These are not + // in any particular page, though they are likely to be clustered by the + // allocation routines. + store_buffer_->EnsureSpace(StoreBuffer::kStoreBufferSize); + } else { + // Store Buffer overflowed while scanning a particular old space page for + // pointers to new space. + ASSERT(current_page_ == page); + ASSERT(page != NULL); + current_page_->set_scan_on_scavenge(true); + ASSERT(start_of_current_page_ != store_buffer_->Top()); + store_buffer_->SetTop(start_of_current_page_); + } + } else { + UNREACHABLE(); + } +} - gc_state_ = SCAVENGE; - SwitchScavengingVisitorsTableIfProfilingWasEnabled(); +void PromotionQueue::Initialize() { + // Assumes that a NewSpacePage exactly fits a number of promotion queue + // entries (where each is a pair of intptr_t). This allows us to simplify + // the test fpr when to switch pages. + ASSERT((Page::kPageSize - MemoryChunk::kBodyOffset) % (2 * kPointerSize) + == 0); + limit_ = reinterpret_cast<intptr_t*>(heap_->new_space()->ToSpaceStart()); + front_ = rear_ = + reinterpret_cast<intptr_t*>(heap_->new_space()->ToSpaceEnd()); + emergency_stack_ = NULL; + guard_ = false; +} + + +void PromotionQueue::RelocateQueueHead() { + ASSERT(emergency_stack_ == NULL); + + Page* p = Page::FromAllocationTop(reinterpret_cast<Address>(rear_)); + intptr_t* head_start = rear_; + intptr_t* head_end = + Min(front_, reinterpret_cast<intptr_t*>(p->body_limit())); + + int entries_count = + static_cast<int>(head_end - head_start) / kEntrySizeInWords; - Page::FlipMeaningOfInvalidatedWatermarkFlag(this); + emergency_stack_ = new List<Entry>(2 * entries_count); + + while (head_start != head_end) { + int size = static_cast<int>(*(head_start++)); + HeapObject* obj = reinterpret_cast<HeapObject*>(*(head_start++)); + emergency_stack_->Add(Entry(obj, size)); + } + rear_ = head_end; +} + + +void Heap::Scavenge() { #ifdef DEBUG - VerifyPageWatermarkValidity(old_pointer_space_, ALL_VALID); - VerifyPageWatermarkValidity(map_space_, ALL_VALID); + if (FLAG_verify_heap) VerifyNonPointerSpacePointers(); #endif - // We do not update an allocation watermark of the top page during linear - // allocation to avoid overhead. So to maintain the watermark invariant - // we have to manually cache the watermark and mark the top page as having an - // invalid watermark. This guarantees that dirty regions iteration will use a - // correct watermark even if a linear allocation happens. - old_pointer_space_->FlushTopPageWatermark(); - map_space_->FlushTopPageWatermark(); + gc_state_ = SCAVENGE; // Implements Cheney's copying algorithm LOG(isolate_, ResourceEvent("scavenge", "begin")); @@ -977,10 +1122,16 @@ void Heap::Scavenge() { isolate_->descriptor_lookup_cache()->Clear(); // Used for updating survived_since_last_expansion_ at function end. - intptr_t survived_watermark = PromotedSpaceSize(); + intptr_t survived_watermark = PromotedSpaceSizeOfObjects(); CheckNewSpaceExpansionCriteria(); + SelectScavengingVisitorsTable(); + + incremental_marking()->PrepareForScavenge(); + + AdvanceSweepers(static_cast<int>(new_space_.Size())); + // Flip the semispaces. After flipping, to space is empty, from space has // live objects. new_space_.Flip(); @@ -1003,32 +1154,29 @@ void Heap::Scavenge() { // for the addresses of promoted objects: every object promoted // frees up its size in bytes from the top of the new space, and // objects are at least one pointer in size. - Address new_space_front = new_space_.ToSpaceLow(); - promotion_queue_.Initialize(new_space_.ToSpaceHigh()); + Address new_space_front = new_space_.ToSpaceStart(); + promotion_queue_.Initialize(); + +#ifdef DEBUG + store_buffer()->Clean(); +#endif - is_safe_to_read_maps_ = false; ScavengeVisitor scavenge_visitor(this); // Copy roots. IterateRoots(&scavenge_visitor, VISIT_ALL_IN_SCAVENGE); - // Copy objects reachable from the old generation. By definition, - // there are no intergenerational pointers in code or data spaces. - IterateDirtyRegions(old_pointer_space_, - &Heap::IteratePointersInDirtyRegion, - &ScavengePointer, - WATERMARK_CAN_BE_INVALID); - - IterateDirtyRegions(map_space_, - &IteratePointersInDirtyMapsRegion, - &ScavengePointer, - WATERMARK_CAN_BE_INVALID); - - lo_space_->IterateDirtyRegions(&ScavengePointer); + // Copy objects reachable from the old generation. + { + StoreBufferRebuildScope scope(this, + store_buffer(), + &ScavengeStoreBufferCallback); + store_buffer()->IteratePointersToNewSpace(&ScavengeObject); + } // Copy objects reachable from cells by scavenging cell values directly. HeapObjectIterator cell_iterator(cell_space_); - for (HeapObject* cell = cell_iterator.next(); - cell != NULL; cell = cell_iterator.next()) { + for (HeapObject* cell = cell_iterator.Next(); + cell != NULL; cell = cell_iterator.Next()) { if (cell->IsJSGlobalPropertyCell()) { Address value_address = reinterpret_cast<Address>(cell) + @@ -1047,27 +1195,34 @@ void Heap::Scavenge() { &scavenge_visitor); new_space_front = DoScavenge(&scavenge_visitor, new_space_front); - UpdateNewSpaceReferencesInExternalStringTable( &UpdateNewSpaceReferenceInExternalStringTableEntry); + promotion_queue_.Destroy(); + LiveObjectList::UpdateReferencesForScavengeGC(); - isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); + if (!FLAG_watch_ic_patching) { + isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); + } + incremental_marking()->UpdateMarkingDequeAfterScavenge(); ASSERT(new_space_front == new_space_.top()); - is_safe_to_read_maps_ = true; - // Set age mark. new_space_.set_age_mark(new_space_.top()); + new_space_.LowerInlineAllocationLimit( + new_space_.inline_allocation_limit_step()); + // Update how much has survived scavenge. IncrementYoungSurvivorsCounter(static_cast<int>( - (PromotedSpaceSize() - survived_watermark) + new_space_.Size())); + (PromotedSpaceSizeOfObjects() - survived_watermark) + new_space_.Size())); LOG(isolate_, ResourceEvent("scavenge", "end")); gc_state_ = NOT_IN_GC; + + scavenges_since_last_idle_round_++; } @@ -1088,7 +1243,9 @@ String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, void Heap::UpdateNewSpaceReferencesInExternalStringTable( ExternalStringTableUpdaterCallback updater_func) { - external_string_table_.Verify(); + if (FLAG_verify_heap) { + external_string_table_.Verify(); + } if (external_string_table_.new_space_strings_.is_empty()) return; @@ -1119,35 +1276,56 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable( } +void Heap::UpdateReferencesInExternalStringTable( + ExternalStringTableUpdaterCallback updater_func) { + + // Update old space string references. + if (external_string_table_.old_space_strings_.length() > 0) { + Object** start = &external_string_table_.old_space_strings_[0]; + Object** end = start + external_string_table_.old_space_strings_.length(); + for (Object** p = start; p < end; ++p) *p = updater_func(this, p); + } + + UpdateNewSpaceReferencesInExternalStringTable(updater_func); +} + + static Object* ProcessFunctionWeakReferences(Heap* heap, Object* function, WeakObjectRetainer* retainer) { - Object* head = heap->undefined_value(); + Object* undefined = heap->undefined_value(); + Object* head = undefined; JSFunction* tail = NULL; Object* candidate = function; - while (candidate != heap->undefined_value()) { + while (candidate != undefined) { // Check whether to keep the candidate in the list. JSFunction* candidate_function = reinterpret_cast<JSFunction*>(candidate); Object* retain = retainer->RetainAs(candidate); if (retain != NULL) { - if (head == heap->undefined_value()) { + if (head == undefined) { // First element in the list. - head = candidate_function; + head = retain; } else { // Subsequent elements in the list. ASSERT(tail != NULL); - tail->set_next_function_link(candidate_function); + tail->set_next_function_link(retain); } // Retained function is new tail. + candidate_function = reinterpret_cast<JSFunction*>(retain); tail = candidate_function; + + ASSERT(retain->IsUndefined() || retain->IsJSFunction()); + + if (retain == undefined) break; } + // Move to next element in the list. candidate = candidate_function->next_function_link(); } // Terminate the list if there is one or more elements. if (tail != NULL) { - tail->set_next_function_link(heap->undefined_value()); + tail->set_next_function_link(undefined); } return head; @@ -1155,28 +1333,32 @@ static Object* ProcessFunctionWeakReferences(Heap* heap, void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { - Object* head = undefined_value(); + Object* undefined = undefined_value(); + Object* head = undefined; Context* tail = NULL; Object* candidate = global_contexts_list_; - while (candidate != undefined_value()) { + while (candidate != undefined) { // Check whether to keep the candidate in the list. Context* candidate_context = reinterpret_cast<Context*>(candidate); Object* retain = retainer->RetainAs(candidate); if (retain != NULL) { - if (head == undefined_value()) { + if (head == undefined) { // First element in the list. - head = candidate_context; + head = retain; } else { // Subsequent elements in the list. ASSERT(tail != NULL); tail->set_unchecked(this, Context::NEXT_CONTEXT_LINK, - candidate_context, + retain, UPDATE_WRITE_BARRIER); } // Retained context is new tail. + candidate_context = reinterpret_cast<Context*>(retain); tail = candidate_context; + if (retain == undefined) break; + // Process the weak list of optimized functions for the context. Object* function_list_head = ProcessFunctionWeakReferences( @@ -1188,6 +1370,7 @@ void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { function_list_head, UPDATE_WRITE_BARRIER); } + // Move to next element in the list. candidate = candidate_context->get(Context::NEXT_CONTEXT_LINK); } @@ -1205,6 +1388,28 @@ void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { } +void Heap::VisitExternalResources(v8::ExternalResourceVisitor* visitor) { + AssertNoAllocation no_allocation; + + class VisitorAdapter : public ObjectVisitor { + public: + explicit VisitorAdapter(v8::ExternalResourceVisitor* visitor) + : visitor_(visitor) {} + virtual void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) { + if ((*p)->IsExternalString()) { + visitor_->VisitExternalString(Utils::ToLocal( + Handle<String>(String::cast(*p)))); + } + } + } + private: + v8::ExternalResourceVisitor* visitor_; + } visitor_adapter(visitor); + external_string_table_.Iterate(&visitor_adapter); +} + + class NewSpaceScavenger : public StaticNewSpaceVisitor<NewSpaceScavenger> { public: static inline void VisitPointer(Heap* heap, Object** p) { @@ -1219,35 +1424,45 @@ class NewSpaceScavenger : public StaticNewSpaceVisitor<NewSpaceScavenger> { Address Heap::DoScavenge(ObjectVisitor* scavenge_visitor, Address new_space_front) { do { - ASSERT(new_space_front <= new_space_.top()); - + SemiSpace::AssertValidRange(new_space_front, new_space_.top()); // The addresses new_space_front and new_space_.top() define a // queue of unprocessed copied objects. Process them until the // queue is empty. - while (new_space_front < new_space_.top()) { - HeapObject* object = HeapObject::FromAddress(new_space_front); - new_space_front += NewSpaceScavenger::IterateBody(object->map(), object); + while (new_space_front != new_space_.top()) { + if (!NewSpacePage::IsAtEnd(new_space_front)) { + HeapObject* object = HeapObject::FromAddress(new_space_front); + new_space_front += + NewSpaceScavenger::IterateBody(object->map(), object); + } else { + new_space_front = + NewSpacePage::FromLimit(new_space_front)->next_page()->body(); + } } // Promote and process all the to-be-promoted objects. - while (!promotion_queue_.is_empty()) { - HeapObject* target; - int size; - promotion_queue_.remove(&target, &size); - - // Promoted object might be already partially visited - // during dirty regions iteration. Thus we search specificly - // for pointers to from semispace instead of looking for pointers - // to new space. - ASSERT(!target->IsMap()); - IterateAndMarkPointersToFromSpace(target->address(), - target->address() + size, - &ScavengePointer); + { + StoreBufferRebuildScope scope(this, + store_buffer(), + &ScavengeStoreBufferCallback); + while (!promotion_queue()->is_empty()) { + HeapObject* target; + int size; + promotion_queue()->remove(&target, &size); + + // Promoted object might be already partially visited + // during old space pointer iteration. Thus we search specificly + // for pointers to from semispace instead of looking for pointers + // to new space. + ASSERT(!target->IsMap()); + IterateAndMarkPointersToFromSpace(target->address(), + target->address() + size, + &ScavengeObject); + } } // Take another spin if there are now unswept objects in new space // (there are currently no more unswept promoted objects). - } while (new_space_front < new_space_.top()); + } while (new_space_front != new_space_.top()); return new_space_front; } @@ -1259,26 +1474,11 @@ enum LoggingAndProfiling { }; -typedef void (*ScavengingCallback)(Map* map, - HeapObject** slot, - HeapObject* object); - - -static Atomic32 scavenging_visitors_table_mode_; -static VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_; - - -INLINE(static void DoScavengeObject(Map* map, - HeapObject** slot, - HeapObject* obj)); - - -void DoScavengeObject(Map* map, HeapObject** slot, HeapObject* obj) { - scavenging_visitors_table_.GetVisitor(map)(map, slot, obj); -} +enum MarksHandling { TRANSFER_MARKS, IGNORE_MARKS }; -template<LoggingAndProfiling logging_and_profiling_mode> +template<MarksHandling marks_handling, + LoggingAndProfiling logging_and_profiling_mode> class ScavengingVisitor : public StaticVisitorBase { public: static void Initialize() { @@ -1313,9 +1513,13 @@ class ScavengingVisitor : public StaticVisitorBase { &ObjectEvacuationStrategy<POINTER_OBJECT>:: Visit); - table_.Register(kVisitJSFunction, - &ObjectEvacuationStrategy<POINTER_OBJECT>:: - template VisitSpecialized<JSFunction::kSize>); + if (marks_handling == IGNORE_MARKS) { + table_.Register(kVisitJSFunction, + &ObjectEvacuationStrategy<POINTER_OBJECT>:: + template VisitSpecialized<JSFunction::kSize>); + } else { + table_.Register(kVisitJSFunction, &EvacuateJSFunction); + } table_.RegisterSpecializations<ObjectEvacuationStrategy<DATA_OBJECT>, kVisitDataObject, @@ -1356,10 +1560,10 @@ class ScavengingVisitor : public StaticVisitorBase { // Helper function used by CopyObject to copy a source object to an // allocated target object and update the forwarding pointer in the source // object. Returns the target object. - INLINE(static HeapObject* MigrateObject(Heap* heap, - HeapObject* source, - HeapObject* target, - int size)) { + INLINE(static void MigrateObject(Heap* heap, + HeapObject* source, + HeapObject* target, + int size)) { // Copy the content of source to target. heap->CopyBlock(target->address(), source->address(), size); @@ -1380,26 +1584,30 @@ class ScavengingVisitor : public StaticVisitorBase { } } - return target; + if (marks_handling == TRANSFER_MARKS) { + if (Marking::TransferColor(source, target)) { + MemoryChunk::IncrementLiveBytesFromGC(target->address(), size); + } + } } - template<ObjectContents object_contents, SizeRestriction size_restriction> static inline void EvacuateObject(Map* map, HeapObject** slot, HeapObject* object, int object_size) { - ASSERT((size_restriction != SMALL) || - (object_size <= Page::kMaxHeapObjectSize)); - ASSERT(object->Size() == object_size); + SLOW_ASSERT((size_restriction != SMALL) || + (object_size <= Page::kMaxHeapObjectSize)); + SLOW_ASSERT(object->Size() == object_size); - Heap* heap = map->heap(); + Heap* heap = map->GetHeap(); if (heap->ShouldBePromoted(object->address(), object_size)) { MaybeObject* maybe_result; if ((size_restriction != SMALL) && (object_size > Page::kMaxHeapObjectSize)) { - maybe_result = heap->lo_space()->AllocateRawFixedArray(object_size); + maybe_result = heap->lo_space()->AllocateRaw(object_size, + NOT_EXECUTABLE); } else { if (object_contents == DATA_OBJECT) { maybe_result = heap->old_data_space()->AllocateRaw(object_size); @@ -1411,7 +1619,12 @@ class ScavengingVisitor : public StaticVisitorBase { Object* result = NULL; // Initialization to please compiler. if (maybe_result->ToObject(&result)) { HeapObject* target = HeapObject::cast(result); - *slot = MigrateObject(heap, object , target, object_size); + + // Order is important: slot might be inside of the target if target + // was allocated over a dead object and slot comes from the store + // buffer. + *slot = target; + MigrateObject(heap, object, target, object_size); if (object_contents == POINTER_OBJECT) { heap->promotion_queue()->insert(target, object_size); @@ -1421,13 +1634,42 @@ class ScavengingVisitor : public StaticVisitorBase { return; } } - Object* result = - heap->new_space()->AllocateRaw(object_size)->ToObjectUnchecked(); - *slot = MigrateObject(heap, object, HeapObject::cast(result), object_size); + MaybeObject* allocation = heap->new_space()->AllocateRaw(object_size); + heap->promotion_queue()->SetNewLimit(heap->new_space()->top()); + Object* result = allocation->ToObjectUnchecked(); + HeapObject* target = HeapObject::cast(result); + + // Order is important: slot might be inside of the target if target + // was allocated over a dead object and slot comes from the store + // buffer. + *slot = target; + MigrateObject(heap, object, target, object_size); return; } + static inline void EvacuateJSFunction(Map* map, + HeapObject** slot, + HeapObject* object) { + ObjectEvacuationStrategy<POINTER_OBJECT>:: + template VisitSpecialized<JSFunction::kSize>(map, slot, object); + + HeapObject* target = *slot; + MarkBit mark_bit = Marking::MarkBitFrom(target); + if (Marking::IsBlack(mark_bit)) { + // This object is black and it might not be rescanned by marker. + // We should explicitly record code entry slot for compaction because + // promotion queue processing (IterateAndMarkPointersToFromSpace) will + // miss it as it is not HeapObject-tagged. + Address code_entry_slot = + target->address() + JSFunction::kCodeEntryOffset; + Code* code = Code::cast(Code::GetObjectFromEntryAddress(code_entry_slot)); + map->GetHeap()->mark_compact_collector()-> + RecordCodeEntrySlot(code_entry_slot, code); + } + } + + static inline void EvacuateFixedArray(Map* map, HeapObject** slot, HeapObject* object) { @@ -1486,14 +1728,17 @@ class ScavengingVisitor : public StaticVisitorBase { HeapObject* object) { ASSERT(IsShortcutCandidate(map->instance_type())); - if (ConsString::cast(object)->unchecked_second() == - map->heap()->empty_string()) { + Heap* heap = map->GetHeap(); + + if (marks_handling == IGNORE_MARKS && + ConsString::cast(object)->unchecked_second() == + heap->empty_string()) { HeapObject* first = HeapObject::cast(ConsString::cast(object)->unchecked_first()); *slot = first; - if (!map->heap()->InNewSpace(first)) { + if (!heap->InNewSpace(first)) { object->set_map_word(MapWord::FromForwardingAddress(first)); return; } @@ -1507,7 +1752,7 @@ class ScavengingVisitor : public StaticVisitorBase { return; } - DoScavengeObject(first->map(), slot, first); + heap->DoScavengeObject(first->map(), slot, first); object->set_map_word(MapWord::FromForwardingAddress(*slot)); return; } @@ -1538,55 +1783,70 @@ class ScavengingVisitor : public StaticVisitorBase { }; -template<LoggingAndProfiling logging_and_profiling_mode> +template<MarksHandling marks_handling, + LoggingAndProfiling logging_and_profiling_mode> VisitorDispatchTable<ScavengingCallback> - ScavengingVisitor<logging_and_profiling_mode>::table_; + ScavengingVisitor<marks_handling, logging_and_profiling_mode>::table_; static void InitializeScavengingVisitorsTables() { - ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::Initialize(); - ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::Initialize(); - scavenging_visitors_table_.CopyFrom( - ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::GetTable()); - scavenging_visitors_table_mode_ = LOGGING_AND_PROFILING_DISABLED; + ScavengingVisitor<TRANSFER_MARKS, + LOGGING_AND_PROFILING_DISABLED>::Initialize(); + ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_DISABLED>::Initialize(); + ScavengingVisitor<TRANSFER_MARKS, + LOGGING_AND_PROFILING_ENABLED>::Initialize(); + ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_ENABLED>::Initialize(); } -void Heap::SwitchScavengingVisitorsTableIfProfilingWasEnabled() { - if (scavenging_visitors_table_mode_ == LOGGING_AND_PROFILING_ENABLED) { - // Table was already updated by some isolate. - return; - } - - if (isolate()->logger()->is_logging() | +void Heap::SelectScavengingVisitorsTable() { + bool logging_and_profiling = + isolate()->logger()->is_logging() || CpuProfiler::is_profiling(isolate()) || (isolate()->heap_profiler() != NULL && - isolate()->heap_profiler()->is_profiling())) { - // If one of the isolates is doing scavenge at this moment of time - // it might see this table in an inconsitent state when - // some of the callbacks point to - // ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED> and others - // to ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>. - // However this does not lead to any bugs as such isolate does not have - // profiling enabled and any isolate with enabled profiling is guaranteed - // to see the table in the consistent state. - scavenging_visitors_table_.CopyFrom( - ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::GetTable()); + isolate()->heap_profiler()->is_profiling()); + + if (!incremental_marking()->IsMarking()) { + if (!logging_and_profiling) { + scavenging_visitors_table_.CopyFrom( + ScavengingVisitor<IGNORE_MARKS, + LOGGING_AND_PROFILING_DISABLED>::GetTable()); + } else { + scavenging_visitors_table_.CopyFrom( + ScavengingVisitor<IGNORE_MARKS, + LOGGING_AND_PROFILING_ENABLED>::GetTable()); + } + } else { + if (!logging_and_profiling) { + scavenging_visitors_table_.CopyFrom( + ScavengingVisitor<TRANSFER_MARKS, + LOGGING_AND_PROFILING_DISABLED>::GetTable()); + } else { + scavenging_visitors_table_.CopyFrom( + ScavengingVisitor<TRANSFER_MARKS, + LOGGING_AND_PROFILING_ENABLED>::GetTable()); + } - // We use Release_Store to prevent reordering of this write before writes - // to the table. - Release_Store(&scavenging_visitors_table_mode_, - LOGGING_AND_PROFILING_ENABLED); + if (incremental_marking()->IsCompacting()) { + // When compacting forbid short-circuiting of cons-strings. + // Scavenging code relies on the fact that new space object + // can't be evacuated into evacuation candidate but + // short-circuiting violates this assumption. + scavenging_visitors_table_.Register( + StaticVisitorBase::kVisitShortcutCandidate, + scavenging_visitors_table_.GetVisitorById( + StaticVisitorBase::kVisitConsString)); + } } } void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) { - ASSERT(HEAP->InFromSpace(object)); + SLOW_ASSERT(HEAP->InFromSpace(object)); MapWord first_word = object->map_word(); - ASSERT(!first_word.IsForwardingAddress()); + SLOW_ASSERT(!first_word.IsForwardingAddress()); Map* map = first_word.ToMap(); - DoScavengeObject(map, p, object); + map->GetHeap()->DoScavengeObject(map, p, object); } @@ -1612,29 +1872,31 @@ MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type, } -MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) { +MaybeObject* Heap::AllocateMap(InstanceType instance_type, + int instance_size, + ElementsKind elements_kind) { Object* result; { MaybeObject* maybe_result = AllocateRawMap(); if (!maybe_result->ToObject(&result)) return maybe_result; } Map* map = reinterpret_cast<Map*>(result); - map->set_map(meta_map()); + map->set_map_no_write_barrier(meta_map()); map->set_instance_type(instance_type); map->set_visitor_id( StaticVisitorBase::GetVisitorId(instance_type, instance_size)); - map->set_prototype(null_value()); - map->set_constructor(null_value()); + map->set_prototype(null_value(), SKIP_WRITE_BARRIER); + map->set_constructor(null_value(), SKIP_WRITE_BARRIER); map->set_instance_size(instance_size); map->set_inobject_properties(0); map->set_pre_allocated_property_fields(0); map->init_instance_descriptors(); - map->set_code_cache(empty_fixed_array()); - map->set_prototype_transitions(empty_fixed_array()); + map->set_code_cache(empty_fixed_array(), SKIP_WRITE_BARRIER); + map->set_prototype_transitions(empty_fixed_array(), SKIP_WRITE_BARRIER); map->set_unused_property_fields(0); map->set_bit_field(0); map->set_bit_field2(1 << Map::kIsExtensible); - map->set_elements_kind(FAST_ELEMENTS); + map->set_elements_kind(elements_kind); // If the map object is aligned fill the padding area with Smi 0 objects. if (Map::kPadStart < Map::kSize) { @@ -1652,8 +1914,8 @@ MaybeObject* Heap::AllocateCodeCache() { if (!maybe_result->ToObject(&result)) return maybe_result; } CodeCache* code_cache = CodeCache::cast(result); - code_cache->set_default_cache(empty_fixed_array()); - code_cache->set_normal_type_cache(undefined_value()); + code_cache->set_default_cache(empty_fixed_array(), SKIP_WRITE_BARRIER); + code_cache->set_normal_type_cache(undefined_value(), SKIP_WRITE_BARRIER); return code_cache; } @@ -1663,6 +1925,19 @@ MaybeObject* Heap::AllocatePolymorphicCodeCache() { } +MaybeObject* Heap::AllocateAccessorPair() { + Object* result; + { MaybeObject* maybe_result = AllocateStruct(ACCESSOR_PAIR_TYPE); + if (!maybe_result->ToObject(&result)) return maybe_result; + } + AccessorPair* accessors = AccessorPair::cast(result); + // Later we will have to distinguish between undefined and the hole... + // accessors->set_getter(the_hole_value(), SKIP_WRITE_BARRIER); + // accessors->set_setter(the_hole_value(), SKIP_WRITE_BARRIER); + return accessors; +} + + const Heap::StringTypeTable Heap::string_type_table[] = { #define STRING_TYPE_ELEMENT(type, size, name, camel_name) \ {type, size, k##camel_name##MapRootIndex}, @@ -1714,12 +1989,19 @@ bool Heap::CreateInitialMaps() { } set_empty_fixed_array(FixedArray::cast(obj)); - { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_DATA_SPACE); + { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_POINTER_SPACE); if (!maybe_obj->ToObject(&obj)) return false; } - set_null_value(obj); + set_null_value(Oddball::cast(obj)); Oddball::cast(obj)->set_kind(Oddball::kNull); + { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_POINTER_SPACE); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_undefined_value(Oddball::cast(obj)); + Oddball::cast(obj)->set_kind(Oddball::kUndefined); + ASSERT(!InNewSpace(undefined_value())); + // Allocate the empty descriptor array. { MaybeObject* maybe_obj = AllocateEmptyFixedArray(); if (!maybe_obj->ToObject(&obj)) return false; @@ -1760,7 +2042,7 @@ bool Heap::CreateInitialMaps() { AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); if (!maybe_obj->ToObject(&obj)) return false; } - set_serialized_scope_info_map(Map::cast(obj)); + set_scope_info_map(Map::cast(obj)); { MaybeObject* maybe_obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize); if (!maybe_obj->ToObject(&obj)) return false; @@ -1805,6 +2087,12 @@ bool Heap::CreateInitialMaps() { } set_byte_array_map(Map::cast(obj)); + { MaybeObject* maybe_obj = + AllocateMap(FREE_SPACE_TYPE, kVariableSizeSentinel); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_free_space_map(Map::cast(obj)); + { MaybeObject* maybe_obj = AllocateByteArray(0, TENURED); if (!maybe_obj->ToObject(&obj)) return false; } @@ -1966,7 +2254,7 @@ MaybeObject* Heap::AllocateHeapNumber(double value, PretenureFlag pretenure) { if (!maybe_result->ToObject(&result)) return maybe_result; } - HeapObject::cast(result)->set_map(heap_number_map()); + HeapObject::cast(result)->set_map_no_write_barrier(heap_number_map()); HeapNumber::cast(result)->set_value(value); return result; } @@ -1984,7 +2272,7 @@ MaybeObject* Heap::AllocateHeapNumber(double value) { { MaybeObject* maybe_result = new_space_.AllocateRaw(HeapNumber::kSize); if (!maybe_result->ToObject(&result)) return maybe_result; } - HeapObject::cast(result)->set_map(heap_number_map()); + HeapObject::cast(result)->set_map_no_write_barrier(heap_number_map()); HeapNumber::cast(result)->set_value(value); return result; } @@ -1995,7 +2283,8 @@ MaybeObject* Heap::AllocateJSGlobalPropertyCell(Object* value) { { MaybeObject* maybe_result = AllocateRawCell(); if (!maybe_result->ToObject(&result)) return maybe_result; } - HeapObject::cast(result)->set_map(global_property_cell_map()); + HeapObject::cast(result)->set_map_no_write_barrier( + global_property_cell_map()); JSGlobalPropertyCell::cast(result)->set_value(value); return result; } @@ -2005,7 +2294,7 @@ MaybeObject* Heap::CreateOddball(const char* to_string, Object* to_number, byte kind) { Object* result; - { MaybeObject* maybe_result = Allocate(oddball_map(), OLD_DATA_SPACE); + { MaybeObject* maybe_result = Allocate(oddball_map(), OLD_POINTER_SPACE); if (!maybe_result->ToObject(&result)) return maybe_result; } return Oddball::cast(result)->Initialize(to_string, to_number, kind); @@ -2018,7 +2307,13 @@ bool Heap::CreateApiObjects() { { MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); if (!maybe_obj->ToObject(&obj)) return false; } - set_neander_map(Map::cast(obj)); + // Don't use Smi-only elements optimizations for objects with the neander + // map. There are too many cases where element values are set directly with a + // bottleneck to trap the Smi-only -> fast elements transition, and there + // appears to be no benefit for optimize this case. + Map* new_neander_map = Map::cast(obj); + new_neander_map->set_elements_kind(FAST_ELEMENTS); + set_neander_map(new_neander_map); { MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map()); if (!maybe_obj->ToObject(&obj)) return false; @@ -2063,6 +2358,12 @@ void Heap::CreateFixedStubs() { // To workaround the problem, make separate functions without inlining. Heap::CreateJSEntryStub(); Heap::CreateJSConstructEntryStub(); + + // Create stubs that should be there, so we don't unexpectedly have to + // create them if we need them during the creation of another stub. + // Stub creation mixes raw pointers and handles in an unsafe manner so + // we cannot create stubs while we are creating stubs. + CodeStub::GenerateStubsAheadOfTime(); } @@ -2073,20 +2374,22 @@ bool Heap::CreateInitialObjects() { { MaybeObject* maybe_obj = AllocateHeapNumber(-0.0, TENURED); if (!maybe_obj->ToObject(&obj)) return false; } - set_minus_zero_value(obj); + set_minus_zero_value(HeapNumber::cast(obj)); ASSERT(signbit(minus_zero_value()->Number()) != 0); { MaybeObject* maybe_obj = AllocateHeapNumber(OS::nan_value(), TENURED); if (!maybe_obj->ToObject(&obj)) return false; } - set_nan_value(obj); + set_nan_value(HeapNumber::cast(obj)); - { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_DATA_SPACE); + { MaybeObject* maybe_obj = AllocateHeapNumber(V8_INFINITY, TENURED); if (!maybe_obj->ToObject(&obj)) return false; } - set_undefined_value(obj); - Oddball::cast(obj)->set_kind(Oddball::kUndefined); - ASSERT(!InNewSpace(undefined_value())); + set_infinity_value(HeapNumber::cast(obj)); + + // The hole has not been created yet, but we want to put something + // predictable in the gaps in the symbol table, so lets make that Smi zero. + set_the_hole_value(reinterpret_cast<Oddball*>(Smi::FromInt(0))); // Allocate initial symbol table. { MaybeObject* maybe_obj = SymbolTable::Allocate(kInitialSymbolTableSize); @@ -2095,19 +2398,17 @@ bool Heap::CreateInitialObjects() { // Don't use set_symbol_table() due to asserts. roots_[kSymbolTableRootIndex] = obj; - // Assign the print strings for oddballs after creating symboltable. - Object* symbol; - { MaybeObject* maybe_symbol = LookupAsciiSymbol("undefined"); - if (!maybe_symbol->ToObject(&symbol)) return false; + // Finish initializing oddballs after creating symboltable. + { MaybeObject* maybe_obj = + undefined_value()->Initialize("undefined", + nan_value(), + Oddball::kUndefined); + if (!maybe_obj->ToObject(&obj)) return false; } - Oddball::cast(undefined_value())->set_to_string(String::cast(symbol)); - Oddball::cast(undefined_value())->set_to_number(nan_value()); - // Allocate the null_value + // Initialize the null_value. { MaybeObject* maybe_obj = - Oddball::cast(null_value())->Initialize("null", - Smi::FromInt(0), - Oddball::kNull); + null_value()->Initialize("null", Smi::FromInt(0), Oddball::kNull); if (!maybe_obj->ToObject(&obj)) return false; } @@ -2116,43 +2417,51 @@ bool Heap::CreateInitialObjects() { Oddball::kTrue); if (!maybe_obj->ToObject(&obj)) return false; } - set_true_value(obj); + set_true_value(Oddball::cast(obj)); { MaybeObject* maybe_obj = CreateOddball("false", Smi::FromInt(0), Oddball::kFalse); if (!maybe_obj->ToObject(&obj)) return false; } - set_false_value(obj); + set_false_value(Oddball::cast(obj)); { MaybeObject* maybe_obj = CreateOddball("hole", Smi::FromInt(-1), Oddball::kTheHole); if (!maybe_obj->ToObject(&obj)) return false; } - set_the_hole_value(obj); + set_the_hole_value(Oddball::cast(obj)); { MaybeObject* maybe_obj = CreateOddball("arguments_marker", - Smi::FromInt(-4), + Smi::FromInt(-2), Oddball::kArgumentMarker); if (!maybe_obj->ToObject(&obj)) return false; } - set_arguments_marker(obj); + set_arguments_marker(Oddball::cast(obj)); { MaybeObject* maybe_obj = CreateOddball("no_interceptor_result_sentinel", - Smi::FromInt(-2), + Smi::FromInt(-3), Oddball::kOther); if (!maybe_obj->ToObject(&obj)) return false; } set_no_interceptor_result_sentinel(obj); { MaybeObject* maybe_obj = CreateOddball("termination_exception", - Smi::FromInt(-3), + Smi::FromInt(-4), Oddball::kOther); if (!maybe_obj->ToObject(&obj)) return false; } set_termination_exception(obj); + { MaybeObject* maybe_obj = CreateOddball("frame_alignment_marker", + Smi::FromInt(-5), + Oddball::kOther); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_frame_alignment_marker(Oddball::cast(obj)); + STATIC_ASSERT(Oddball::kLeastHiddenOddballNumber == -5); + // Allocate the empty string. { MaybeObject* maybe_obj = AllocateRawAsciiString(0, TENURED); if (!maybe_obj->ToObject(&obj)) return false; @@ -2193,6 +2502,7 @@ bool Heap::CreateInitialObjects() { } set_code_stubs(UnseededNumberDictionary::cast(obj)); + // Allocate the non_monomorphic_cache used in stub-cache.cc. The initial size // is set to avoid expanding the dictionary during bootstrapping. { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(64); @@ -2221,7 +2531,10 @@ bool Heap::CreateInitialObjects() { } set_intrinsic_function_names(StringDictionary::cast(obj)); - if (InitializeNumberStringCache()->IsFailure()) return false; + { MaybeObject* maybe_obj = AllocateInitialNumberStringCache(); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_number_string_cache(FixedArray::cast(obj)); // Allocate cache for single character ASCII strings. { MaybeObject* maybe_obj = @@ -2320,7 +2633,7 @@ void StringSplitCache::Enter(Heap* heap, } } } - array->set_map(heap->fixed_cow_array_map()); + array->set_map_no_write_barrier(heap->fixed_cow_array_map()); } @@ -2331,20 +2644,44 @@ void StringSplitCache::Clear(FixedArray* cache) { } -MaybeObject* Heap::InitializeNumberStringCache() { - // Compute the size of the number string cache based on the max heap size. - // max_semispace_size_ == 512 KB => number_string_cache_size = 32. - // max_semispace_size_ == 8 MB => number_string_cache_size = 16KB. - int number_string_cache_size = max_semispace_size_ / 512; - number_string_cache_size = Max(32, Min(16*KB, number_string_cache_size)); - Object* obj; +MaybeObject* Heap::AllocateInitialNumberStringCache() { MaybeObject* maybe_obj = - AllocateFixedArray(number_string_cache_size * 2, TENURED); - if (maybe_obj->ToObject(&obj)) set_number_string_cache(FixedArray::cast(obj)); + AllocateFixedArray(kInitialNumberStringCacheSize * 2, TENURED); return maybe_obj; } +int Heap::FullSizeNumberStringCacheLength() { + // Compute the size of the number string cache based on the max newspace size. + // The number string cache has a minimum size based on twice the initial cache + // size to ensure that it is bigger after being made 'full size'. + int number_string_cache_size = max_semispace_size_ / 512; + number_string_cache_size = Max(kInitialNumberStringCacheSize * 2, + Min(0x4000, number_string_cache_size)); + // There is a string and a number per entry so the length is twice the number + // of entries. + return number_string_cache_size * 2; +} + + +void Heap::AllocateFullSizeNumberStringCache() { + // The idea is to have a small number string cache in the snapshot to keep + // boot-time memory usage down. If we expand the number string cache already + // while creating the snapshot then that didn't work out. + ASSERT(!Serializer::enabled()); + MaybeObject* maybe_obj = + AllocateFixedArray(FullSizeNumberStringCacheLength(), TENURED); + Object* new_cache; + if (maybe_obj->ToObject(&new_cache)) { + // We don't bother to repopulate the cache with entries from the old cache. + // It will be repopulated soon enough with new strings. + set_number_string_cache(FixedArray::cast(new_cache)); + } + // If allocation fails then we just return without doing anything. It is only + // a cache, so best effort is OK here. +} + + void Heap::FlushNumberStringCache() { // Flush the number to string cache. int len = number_string_cache()->length(); @@ -2390,11 +2727,17 @@ void Heap::SetNumberStringCache(Object* number, String* string) { int mask = (number_string_cache()->length() >> 1) - 1; if (number->IsSmi()) { hash = smi_get_hash(Smi::cast(number)) & mask; - number_string_cache()->set(hash * 2, Smi::cast(number)); } else { hash = double_get_hash(number->Number()) & mask; - number_string_cache()->set(hash * 2, number); } + if (number_string_cache()->get(hash * 2) != undefined_value() && + number_string_cache()->length() != FullSizeNumberStringCacheLength()) { + // The first time we have a hash collision, we move to the full sized + // number string cache. + AllocateFullSizeNumberStringCache(); + return; + } + number_string_cache()->set(hash * 2, number); number_string_cache()->set(hash * 2 + 1, string); } @@ -2429,6 +2772,15 @@ MaybeObject* Heap::NumberToString(Object* number, } +MaybeObject* Heap::Uint32ToString(uint32_t value, + bool check_number_string_cache) { + Object* number; + MaybeObject* maybe = NumberFromUint32(value); + if (!maybe->To<Object>(&number)) return maybe; + return NumberToString(number, check_number_string_cache); +} + + Map* Heap::MapForExternalArrayType(ExternalArrayType array_type) { return Map::cast(roots_[RootIndexForExternalArrayType(array_type)]); } @@ -2487,12 +2839,10 @@ MaybeObject* Heap::AllocateForeign(Address address, PretenureFlag pretenure) { // Statically ensure that it is safe to allocate foreigns in paged spaces. STATIC_ASSERT(Foreign::kSize <= Page::kMaxHeapObjectSize); AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE; - Object* result; - { MaybeObject* maybe_result = Allocate(foreign_map(), space); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - - Foreign::cast(result)->set_address(address); + Foreign* result; + MaybeObject* maybe_result = Allocate(foreign_map(), space); + if (!maybe_result->To(&result)) return maybe_result; + result->set_foreign_address(address); return result; } @@ -2506,18 +2856,20 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) { share->set_name(name); Code* illegal = isolate_->builtins()->builtin(Builtins::kIllegal); share->set_code(illegal); - share->set_scope_info(SerializedScopeInfo::Empty()); + share->set_scope_info(ScopeInfo::Empty()); Code* construct_stub = isolate_->builtins()->builtin(Builtins::kJSConstructStubGeneric); share->set_construct_stub(construct_stub); share->set_instance_class_name(Object_symbol()); - share->set_function_data(undefined_value()); - share->set_script(undefined_value()); - share->set_debug_info(undefined_value()); - share->set_inferred_name(empty_string()); - share->set_initial_map(undefined_value()); - share->set_this_property_assignments(undefined_value()); - share->set_deopt_counter(Smi::FromInt(FLAG_deopt_every_n_times)); + share->set_function_data(undefined_value(), SKIP_WRITE_BARRIER); + share->set_script(undefined_value(), SKIP_WRITE_BARRIER); + share->set_debug_info(undefined_value(), SKIP_WRITE_BARRIER); + share->set_inferred_name(empty_string(), SKIP_WRITE_BARRIER); + share->set_initial_map(undefined_value(), SKIP_WRITE_BARRIER); + share->set_this_property_assignments(undefined_value(), SKIP_WRITE_BARRIER); + share->set_deopt_counter(FLAG_deopt_every_n_times); + share->set_profiler_ticks(0); + share->set_ast_node_count(0); // Set integer fields (smi or int, depending on the architecture). share->set_length(0); @@ -2548,8 +2900,8 @@ MaybeObject* Heap::AllocateJSMessageObject(String* type, if (!maybe_result->ToObject(&result)) return maybe_result; } JSMessageObject* message = JSMessageObject::cast(result); - message->set_properties(Heap::empty_fixed_array()); - message->set_elements(Heap::empty_fixed_array()); + message->set_properties(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER); + message->set_elements(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER); message->set_type(type); message->set_arguments(arguments); message->set_start_position(start_position); @@ -2640,8 +2992,8 @@ MaybeObject* Heap::AllocateConsString(String* first, String* second) { bool is_ascii_data_in_two_byte_string = false; if (!is_ascii) { // At least one of the strings uses two-byte representation so we - // can't use the fast case code for short ascii strings below, but - // we can try to save memory if all chars actually fit in ascii. + // can't use the fast case code for short ASCII strings below, but + // we can try to save memory if all chars actually fit in ASCII. is_ascii_data_in_two_byte_string = first->HasOnlyAsciiChars() && second->HasOnlyAsciiChars(); if (is_ascii_data_in_two_byte_string) { @@ -2650,9 +3002,9 @@ MaybeObject* Heap::AllocateConsString(String* first, String* second) { } // If the resulting string is small make a flat string. - if (length < String::kMinNonFlatLength) { + if (length < ConsString::kMinLength) { // Note that neither of the two inputs can be a slice because: - STATIC_ASSERT(String::kMinNonFlatLength <= SlicedString::kMinLength); + STATIC_ASSERT(ConsString::kMinLength <= SlicedString::kMinLength); ASSERT(first->IsFlat()); ASSERT(second->IsFlat()); if (is_ascii) { @@ -2665,14 +3017,14 @@ MaybeObject* Heap::AllocateConsString(String* first, String* second) { // Copy first part. const char* src; if (first->IsExternalString()) { - src = ExternalAsciiString::cast(first)->resource()->data(); + src = ExternalAsciiString::cast(first)->GetChars(); } else { src = SeqAsciiString::cast(first)->GetChars(); } for (int i = 0; i < first_length; i++) *dest++ = src[i]; // Copy second part. if (second->IsExternalString()) { - src = ExternalAsciiString::cast(second)->resource()->data(); + src = ExternalAsciiString::cast(second)->GetChars(); } else { src = SeqAsciiString::cast(second)->GetChars(); } @@ -2728,7 +3080,7 @@ MaybeObject* Heap::AllocateSubString(String* buffer, int end, PretenureFlag pretenure) { int length = end - start; - if (length == 0) { + if (length <= 0) { return empty_string(); } else if (length == 1) { return LookupSingleCharacterStringFromCode(buffer->Get(start)); @@ -2744,25 +3096,23 @@ MaybeObject* Heap::AllocateSubString(String* buffer, // Make an attempt to flatten the buffer to reduce access time. buffer = buffer->TryFlattenGetString(); - // TODO(1626): For now slicing external strings is not supported. However, - // a flat cons string can have an external string as first part in some cases. - // Therefore we have to single out this case as well. if (!FLAG_string_slices || - (buffer->IsConsString() && - (!buffer->IsFlat() || - !ConsString::cast(buffer)->first()->IsSeqString())) || - buffer->IsExternalString() || + !buffer->IsFlat() || length < SlicedString::kMinLength || pretenure == TENURED) { Object* result; - { MaybeObject* maybe_result = buffer->IsAsciiRepresentation() - ? AllocateRawAsciiString(length, pretenure) - : AllocateRawTwoByteString(length, pretenure); + // WriteToFlat takes care of the case when an indirect string has a + // different encoding from its underlying string. These encodings may + // differ because of externalization. + bool is_ascii = buffer->IsAsciiRepresentation(); + { MaybeObject* maybe_result = is_ascii + ? AllocateRawAsciiString(length, pretenure) + : AllocateRawTwoByteString(length, pretenure); if (!maybe_result->ToObject(&result)) return maybe_result; } String* string_result = String::cast(result); // Copy the characters into the new object. - if (buffer->IsAsciiRepresentation()) { + if (is_ascii) { ASSERT(string_result->IsAsciiRepresentation()); char* dest = SeqAsciiString::cast(string_result)->GetChars(); String::WriteToFlat(buffer, dest, start, end); @@ -2775,12 +3125,19 @@ MaybeObject* Heap::AllocateSubString(String* buffer, } ASSERT(buffer->IsFlat()); - ASSERT(!buffer->IsExternalString()); #if DEBUG - buffer->StringVerify(); + if (FLAG_verify_heap) { + buffer->StringVerify(); + } #endif Object* result; + // When slicing an indirect string we use its encoding for a newly created + // slice and don't check the encoding of the underlying string. This is safe + // even if the encodings are different because of externalization. If an + // indirect ASCII string is pointing to a two-byte string, the two-byte char + // codes of the underlying string must still fit into ASCII (because + // externalization must not change char codes). { Map* map = buffer->IsAsciiRepresentation() ? sliced_ascii_string_map() : sliced_string_map(); @@ -2806,13 +3163,14 @@ MaybeObject* Heap::AllocateSubString(String* buffer, sliced_string->set_parent(buffer); sliced_string->set_offset(start); } - ASSERT(sliced_string->parent()->IsSeqString()); + ASSERT(sliced_string->parent()->IsSeqString() || + sliced_string->parent()->IsExternalString()); return result; } MaybeObject* Heap::AllocateExternalStringFromAscii( - ExternalAsciiString::Resource* resource) { + const ExternalAsciiString::Resource* resource) { size_t length = resource->length(); if (length > static_cast<size_t>(String::kMaxLength)) { isolate()->context()->mark_out_of_memory(); @@ -2835,7 +3193,7 @@ MaybeObject* Heap::AllocateExternalStringFromAscii( MaybeObject* Heap::AllocateExternalStringFromTwoByte( - ExternalTwoByteString::Resource* resource) { + const ExternalTwoByteString::Resource* resource) { size_t length = resource->length(); if (length > static_cast<size_t>(String::kMaxLength)) { isolate()->context()->mark_out_of_memory(); @@ -2899,11 +3257,12 @@ MaybeObject* Heap::AllocateByteArray(int length, PretenureFlag pretenure) { Object* result; { MaybeObject* maybe_result = (size <= MaxObjectSizeInPagedSpace()) ? old_data_space_->AllocateRaw(size) - : lo_space_->AllocateRaw(size); + : lo_space_->AllocateRaw(size, NOT_EXECUTABLE); if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<ByteArray*>(result)->set_map(byte_array_map()); + reinterpret_cast<ByteArray*>(result)->set_map_no_write_barrier( + byte_array_map()); reinterpret_cast<ByteArray*>(result)->set_length(length); return result; } @@ -2921,7 +3280,8 @@ MaybeObject* Heap::AllocateByteArray(int length) { if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<ByteArray*>(result)->set_map(byte_array_map()); + reinterpret_cast<ByteArray*>(result)->set_map_no_write_barrier( + byte_array_map()); reinterpret_cast<ByteArray*>(result)->set_length(length); return result; } @@ -2931,12 +3291,12 @@ void Heap::CreateFillerObjectAt(Address addr, int size) { if (size == 0) return; HeapObject* filler = HeapObject::FromAddress(addr); if (size == kPointerSize) { - filler->set_map(one_pointer_filler_map()); + filler->set_map_no_write_barrier(one_pointer_filler_map()); } else if (size == 2 * kPointerSize) { - filler->set_map(two_pointer_filler_map()); + filler->set_map_no_write_barrier(two_pointer_filler_map()); } else { - filler->set_map(byte_array_map()); - ByteArray::cast(filler)->set_length(ByteArray::LengthFor(size)); + filler->set_map_no_write_barrier(free_space_map()); + FreeSpace::cast(filler)->set_size(size); } } @@ -2953,7 +3313,7 @@ MaybeObject* Heap::AllocateExternalArray(int length, if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<ExternalArray*>(result)->set_map( + reinterpret_cast<ExternalArray*>(result)->set_map_no_write_barrier( MapForExternalArrayType(array_type)); reinterpret_cast<ExternalArray*>(result)->set_length(length); reinterpret_cast<ExternalArray*>(result)->set_external_pointer( @@ -2969,10 +3329,9 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, bool immovable) { // Allocate ByteArray before the Code object, so that we do not risk // leaving uninitialized Code object (and breaking the heap). - Object* reloc_info; - { MaybeObject* maybe_reloc_info = AllocateByteArray(desc.reloc_size, TENURED); - if (!maybe_reloc_info->ToObject(&reloc_info)) return maybe_reloc_info; - } + ByteArray* reloc_info; + MaybeObject* maybe_reloc_info = AllocateByteArray(desc.reloc_size, TENURED); + if (!maybe_reloc_info->To(&reloc_info)) return maybe_reloc_info; // Compute size. int body_size = RoundUp(desc.instr_size, kObjectAlignment); @@ -2982,7 +3341,7 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, // Large code objects and code objects which should stay at a fixed address // are allocated in large object space. if (obj_size > MaxObjectSizeInPagedSpace() || immovable) { - maybe_result = lo_space_->AllocateRawCode(obj_size); + maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE); } else { maybe_result = code_space_->AllocateRaw(obj_size); } @@ -2991,18 +3350,21 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, if (!maybe_result->ToObject(&result)) return maybe_result; // Initialize the object - HeapObject::cast(result)->set_map(code_map()); + HeapObject::cast(result)->set_map_no_write_barrier(code_map()); Code* code = Code::cast(result); ASSERT(!isolate_->code_range()->exists() || isolate_->code_range()->contains(code->address())); code->set_instruction_size(desc.instr_size); - code->set_relocation_info(ByteArray::cast(reloc_info)); + code->set_relocation_info(reloc_info); code->set_flags(flags); if (code->is_call_stub() || code->is_keyed_call_stub()) { code->set_check_type(RECEIVER_MAP_CHECK); } - code->set_deoptimization_data(empty_fixed_array()); - code->set_next_code_flushing_candidate(undefined_value()); + code->set_deoptimization_data(empty_fixed_array(), SKIP_WRITE_BARRIER); + code->set_type_feedback_cells(TypeFeedbackCells::cast(empty_fixed_array()), + SKIP_WRITE_BARRIER); + code->set_handler_table(empty_fixed_array(), SKIP_WRITE_BARRIER); + code->set_gc_metadata(Smi::FromInt(0)); // Allow self references to created code object by patching the handle to // point to the newly allocated Code object. if (!self_reference.is_null()) { @@ -3016,7 +3378,9 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, code->CopyFrom(desc); #ifdef DEBUG - code->Verify(); + if (FLAG_verify_heap) { + code->Verify(); + } #endif return code; } @@ -3027,7 +3391,7 @@ MaybeObject* Heap::CopyCode(Code* code) { int obj_size = code->Size(); MaybeObject* maybe_result; if (obj_size > MaxObjectSizeInPagedSpace()) { - maybe_result = lo_space_->AllocateRawCode(obj_size); + maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE); } else { maybe_result = code_space_->AllocateRaw(obj_size); } @@ -3070,7 +3434,7 @@ MaybeObject* Heap::CopyCode(Code* code, Vector<byte> reloc_info) { MaybeObject* maybe_result; if (new_obj_size > MaxObjectSizeInPagedSpace()) { - maybe_result = lo_space_->AllocateRawCode(new_obj_size); + maybe_result = lo_space_->AllocateRaw(new_obj_size, EXECUTABLE); } else { maybe_result = code_space_->AllocateRaw(new_obj_size); } @@ -3096,7 +3460,9 @@ MaybeObject* Heap::CopyCode(Code* code, Vector<byte> reloc_info) { new_code->Relocate(new_addr - old_addr); #ifdef DEBUG - code->Verify(); + if (FLAG_verify_heap) { + code->Verify(); + } #endif return new_code; } @@ -3114,14 +3480,15 @@ MaybeObject* Heap::Allocate(Map* map, AllocationSpace space) { AllocateRaw(map->instance_size(), space, retry_space); if (!maybe_result->ToObject(&result)) return maybe_result; } - HeapObject::cast(result)->set_map(map); + // No need for write barrier since object is white and map is in old space. + HeapObject::cast(result)->set_map_no_write_barrier(map); return result; } -MaybeObject* Heap::InitializeFunction(JSFunction* function, - SharedFunctionInfo* shared, - Object* prototype) { +void Heap::InitializeFunction(JSFunction* function, + SharedFunctionInfo* shared, + Object* prototype) { ASSERT(!prototype->IsMap()); function->initialize_properties(); function->initialize_elements(); @@ -3129,9 +3496,8 @@ MaybeObject* Heap::InitializeFunction(JSFunction* function, function->set_code(shared->code()); function->set_prototype_or_initial_map(prototype); function->set_context(undefined_value()); - function->set_literals(empty_fixed_array()); + function->set_literals_or_bindings(empty_fixed_array()); function->set_next_function_link(undefined_value()); - return function; } @@ -3141,8 +3507,18 @@ MaybeObject* Heap::AllocateFunctionPrototype(JSFunction* function) { // different context. JSFunction* object_function = function->context()->global_context()->object_function(); + + // Each function prototype gets a copy of the object function map. + // This avoid unwanted sharing of maps between prototypes of different + // constructors. + Map* new_map; + ASSERT(object_function->has_initial_map()); + { MaybeObject* maybe_map = + object_function->initial_map()->CopyDropTransitions(); + if (!maybe_map->To<Map>(&new_map)) return maybe_map; + } Object* prototype; - { MaybeObject* maybe_prototype = AllocateJSObject(object_function); + { MaybeObject* maybe_prototype = AllocateJSObjectFromMap(new_map); if (!maybe_prototype->ToObject(&prototype)) return maybe_prototype; } // When creating the prototype for the function we must set its @@ -3167,7 +3543,8 @@ MaybeObject* Heap::AllocateFunction(Map* function_map, { MaybeObject* maybe_result = Allocate(function_map, space); if (!maybe_result->ToObject(&result)) return maybe_result; } - return InitializeFunction(JSFunction::cast(result), shared, prototype); + InitializeFunction(JSFunction::cast(result), shared, prototype); + return result; } @@ -3178,7 +3555,7 @@ MaybeObject* Heap::AllocateArgumentsObject(Object* callee, int length) { JSObject* boilerplate; int arguments_object_size; bool strict_mode_callee = callee->IsJSFunction() && - JSFunction::cast(callee)->shared()->strict_mode(); + !JSFunction::cast(callee)->shared()->is_classic_mode(); if (strict_mode_callee) { boilerplate = isolate()->context()->global_context()-> @@ -3284,22 +3661,22 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) { // Inline constructor can only handle inobject properties. fun->shared()->ForbidInlineConstructor(); } else { - Object* descriptors_obj; + DescriptorArray* descriptors; { MaybeObject* maybe_descriptors_obj = DescriptorArray::Allocate(count); - if (!maybe_descriptors_obj->ToObject(&descriptors_obj)) { + if (!maybe_descriptors_obj->To<DescriptorArray>(&descriptors)) { return maybe_descriptors_obj; } } - DescriptorArray* descriptors = DescriptorArray::cast(descriptors_obj); + DescriptorArray::WhitenessWitness witness(descriptors); for (int i = 0; i < count; i++) { String* name = fun->shared()->GetThisPropertyAssignmentName(i); ASSERT(name->IsSymbol()); FieldDescriptor field(name, i, NONE); field.SetEnumerationIndex(i); - descriptors->Set(i, &field); + descriptors->Set(i, &field, witness); } descriptors->SetNextEnumerationIndex(count); - descriptors->SortUnchecked(); + descriptors->SortUnchecked(witness); // The descriptors may contain duplicates because the compiler does not // guarantee the uniqueness of property names (it would have required @@ -3329,14 +3706,17 @@ void Heap::InitializeJSObjectFromMap(JSObject* obj, // TODO(1240798): Initialize the object's body using valid initial values // according to the object's initial map. For example, if the map's // instance type is JS_ARRAY_TYPE, the length field should be initialized - // to a number (eg, Smi::FromInt(0)) and the elements initialized to a - // fixed array (eg, Heap::empty_fixed_array()). Currently, the object + // to a number (e.g. Smi::FromInt(0)) and the elements initialized to a + // fixed array (e.g. Heap::empty_fixed_array()). Currently, the object // verification code has to cope with (temporarily) invalid objects. See // for example, JSArray::JSArrayVerify). Object* filler; // We cannot always fill with one_pointer_filler_map because objects // created from API functions expect their internal fields to be initialized // with undefined_value. + // Pre-allocated fields need to be initialized with undefined_value as well + // so that object accesses before the constructor completes (e.g. in the + // debugger) will not cause a crash. if (map->constructor()->IsJSFunction() && JSFunction::cast(map->constructor())->shared()-> IsInobjectSlackTrackingInProgress()) { @@ -3346,7 +3726,7 @@ void Heap::InitializeJSObjectFromMap(JSObject* obj, } else { filler = Heap::undefined_value(); } - obj->InitializeBody(map->instance_size(), filler); + obj->InitializeBody(map, Heap::undefined_value(), filler); } @@ -3384,7 +3764,8 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { InitializeJSObjectFromMap(JSObject::cast(obj), FixedArray::cast(properties), map); - ASSERT(JSObject::cast(obj)->HasFastElements()); + ASSERT(JSObject::cast(obj)->HasFastSmiOnlyElements() || + JSObject::cast(obj)->HasFastElements()); return obj; } @@ -3401,8 +3782,8 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor, Map::cast(initial_map)->set_constructor(constructor); } // Allocate the object based on the constructors initial map. - MaybeObject* result = - AllocateJSObjectFromMap(constructor->initial_map(), pretenure); + MaybeObject* result = AllocateJSObjectFromMap( + constructor->initial_map(), pretenure); #ifdef DEBUG // Make sure result is NOT a global object if valid. Object* non_failure; @@ -3412,6 +3793,64 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor, } +MaybeObject* Heap::AllocateJSArrayAndStorage( + ElementsKind elements_kind, + int length, + int capacity, + ArrayStorageAllocationMode mode, + PretenureFlag pretenure) { + ASSERT(capacity >= length); + MaybeObject* maybe_array = AllocateJSArray(elements_kind, pretenure); + JSArray* array; + if (!maybe_array->To(&array)) return maybe_array; + + if (capacity == 0) { + array->set_length(Smi::FromInt(0)); + array->set_elements(empty_fixed_array()); + return array; + } + + FixedArrayBase* elms; + MaybeObject* maybe_elms = NULL; + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + if (mode == DONT_INITIALIZE_ARRAY_ELEMENTS) { + maybe_elms = AllocateUninitializedFixedDoubleArray(capacity); + } else { + ASSERT(mode == INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); + maybe_elms = AllocateFixedDoubleArrayWithHoles(capacity); + } + } else { + ASSERT(elements_kind == FAST_ELEMENTS || + elements_kind == FAST_SMI_ONLY_ELEMENTS); + if (mode == DONT_INITIALIZE_ARRAY_ELEMENTS) { + maybe_elms = AllocateUninitializedFixedArray(capacity); + } else { + ASSERT(mode == INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); + maybe_elms = AllocateFixedArrayWithHoles(capacity); + } + } + if (!maybe_elms->To(&elms)) return maybe_elms; + + array->set_elements(elms); + array->set_length(Smi::FromInt(length)); + return array; +} + + +MaybeObject* Heap::AllocateJSArrayWithElements( + FixedArrayBase* elements, + ElementsKind elements_kind, + PretenureFlag pretenure) { + MaybeObject* maybe_array = AllocateJSArray(elements_kind, pretenure); + JSArray* array; + if (!maybe_array->To(&array)) return maybe_array; + + array->set_elements(elements); + array->set_length(Smi::FromInt(elements->length())); + return array; +} + + MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) { // Allocate map. // TODO(rossberg): Once we optimize proxies, think about a scheme to share @@ -3427,6 +3866,7 @@ MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) { if (!maybe_result->To<JSProxy>(&result)) return maybe_result; result->InitializeBody(map->instance_size(), Smi::FromInt(0)); result->set_handler(handler); + result->set_hash(undefined_value(), SKIP_WRITE_BARRIER); return result; } @@ -3450,6 +3890,7 @@ MaybeObject* Heap::AllocateJSFunctionProxy(Object* handler, if (!maybe_result->To<JSFunctionProxy>(&result)) return maybe_result; result->InitializeBody(map->instance_size(), Smi::FromInt(0)); result->set_handler(handler); + result->set_hash(undefined_value(), SKIP_WRITE_BARRIER); result->set_call_trap(call_trap); result->set_construct_trap(construct_trap); return result; @@ -3517,7 +3958,7 @@ MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) { } Map* new_map = Map::cast(obj); - // Setup the global object as a normalized object. + // Set up the global object as a normalized object. global->set_map(new_map); global->map()->clear_instance_descriptors(); global->set_properties(dictionary); @@ -3532,13 +3973,15 @@ MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) { MaybeObject* Heap::CopyJSObject(JSObject* source) { // Never used to copy functions. If functions need to be copied we // have to be careful to clear the literals array. - ASSERT(!source->IsJSFunction()); + SLOW_ASSERT(!source->IsJSFunction()); // Make the clone. Map* map = source->map(); int object_size = map->instance_size(); Object* clone; + WriteBarrierMode wb_mode = UPDATE_WRITE_BARRIER; + // If we're forced to always allocate, we use the general allocation // functions which may leave us with an object in old space. if (always_allocate()) { @@ -3555,10 +3998,11 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { JSObject::kHeaderSize, (object_size - JSObject::kHeaderSize) / kPointerSize); } else { + wb_mode = SKIP_WRITE_BARRIER; { MaybeObject* maybe_clone = new_space_.AllocateRaw(object_size); if (!maybe_clone->ToObject(&clone)) return maybe_clone; } - ASSERT(InNewSpace(clone)); + SLOW_ASSERT(InNewSpace(clone)); // Since we know the clone is allocated in new space, we can copy // the contents without worrying about updating the write barrier. CopyBlock(HeapObject::cast(clone)->address(), @@ -3566,6 +4010,8 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { object_size); } + SLOW_ASSERT( + JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind()); FixedArrayBase* elements = FixedArrayBase::cast(source->elements()); FixedArray* properties = FixedArray::cast(source->properties()); // Update elements if necessary. @@ -3581,7 +4027,7 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { } if (!maybe_elem->ToObject(&elem)) return maybe_elem; } - JSObject::cast(clone)->set_elements(FixedArrayBase::cast(elem)); + JSObject::cast(clone)->set_elements(FixedArrayBase::cast(elem), wb_mode); } // Update properties if necessary. if (properties->length() > 0) { @@ -3589,7 +4035,7 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { { MaybeObject* maybe_prop = CopyFixedArray(properties); if (!maybe_prop->ToObject(&prop)) return maybe_prop; } - JSObject::cast(clone)->set_properties(FixedArray::cast(prop)); + JSObject::cast(clone)->set_properties(FixedArray::cast(prop), wb_mode); } // Return the new clone. return clone; @@ -3598,13 +4044,13 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { MaybeObject* Heap::ReinitializeJSReceiver( JSReceiver* object, InstanceType type, int size) { - ASSERT(type >= FIRST_JS_RECEIVER_TYPE); + ASSERT(type >= FIRST_JS_OBJECT_TYPE); // Allocate fresh map. // TODO(rossberg): Once we optimize proxies, cache these maps. Map* map; - MaybeObject* maybe_map_obj = AllocateMap(type, size); - if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj; + MaybeObject* maybe = AllocateMap(type, size); + if (!maybe->To<Map>(&map)) return maybe; // Check that the receiver has at least the size of the fresh object. int size_difference = object->map()->instance_size() - map->instance_size(); @@ -3615,30 +4061,35 @@ MaybeObject* Heap::ReinitializeJSReceiver( // Allocate the backing storage for the properties. int prop_size = map->unused_property_fields() - map->inobject_properties(); Object* properties; - { MaybeObject* maybe_properties = AllocateFixedArray(prop_size, TENURED); - if (!maybe_properties->ToObject(&properties)) return maybe_properties; + maybe = AllocateFixedArray(prop_size, TENURED); + if (!maybe->ToObject(&properties)) return maybe; + + // Functions require some allocation, which might fail here. + SharedFunctionInfo* shared = NULL; + if (type == JS_FUNCTION_TYPE) { + String* name; + maybe = LookupAsciiSymbol("<freezing call trap>"); + if (!maybe->To<String>(&name)) return maybe; + maybe = AllocateSharedFunctionInfo(name); + if (!maybe->To<SharedFunctionInfo>(&shared)) return maybe; } + // Because of possible retries of this function after failure, + // we must NOT fail after this point, where we have changed the type! + // Reset the map for the object. object->set_map(map); + JSObject* jsobj = JSObject::cast(object); // Reinitialize the object from the constructor map. - InitializeJSObjectFromMap(JSObject::cast(object), - FixedArray::cast(properties), map); + InitializeJSObjectFromMap(jsobj, FixedArray::cast(properties), map); // Functions require some minimal initialization. if (type == JS_FUNCTION_TYPE) { - String* name; - MaybeObject* maybe_name = LookupAsciiSymbol("<freezing call trap>"); - if (!maybe_name->To<String>(&name)) return maybe_name; - SharedFunctionInfo* shared; - MaybeObject* maybe_shared = AllocateSharedFunctionInfo(name); - if (!maybe_shared->To<SharedFunctionInfo>(&shared)) return maybe_shared; - JSFunction* func; - MaybeObject* maybe_func = - InitializeFunction(JSFunction::cast(object), shared, the_hole_value()); - if (!maybe_func->To<JSFunction>(&func)) return maybe_func; - func->set_context(isolate()->context()->global_context()); + map->set_function_with_prototype(true); + InitializeFunction(JSFunction::cast(object), shared, the_hole_value()); + JSFunction::cast(object)->set_context( + isolate()->context()->global_context()); } // Put in filler if the new object is smaller than the old. @@ -3756,31 +4207,22 @@ Map* Heap::SymbolMapForString(String* string) { if (InNewSpace(string)) return NULL; // Find the corresponding symbol map for strings. - Map* map = string->map(); - if (map == ascii_string_map()) { - return ascii_symbol_map(); + switch (string->map()->instance_type()) { + case STRING_TYPE: return symbol_map(); + case ASCII_STRING_TYPE: return ascii_symbol_map(); + case CONS_STRING_TYPE: return cons_symbol_map(); + case CONS_ASCII_STRING_TYPE: return cons_ascii_symbol_map(); + case EXTERNAL_STRING_TYPE: return external_symbol_map(); + case EXTERNAL_ASCII_STRING_TYPE: return external_ascii_symbol_map(); + case EXTERNAL_STRING_WITH_ASCII_DATA_TYPE: + return external_symbol_with_ascii_data_map(); + case SHORT_EXTERNAL_STRING_TYPE: return short_external_symbol_map(); + case SHORT_EXTERNAL_ASCII_STRING_TYPE: + return short_external_ascii_symbol_map(); + case SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE: + return short_external_symbol_with_ascii_data_map(); + default: return NULL; // No match found. } - if (map == string_map()) { - return symbol_map(); - } - if (map == cons_string_map()) { - return cons_symbol_map(); - } - if (map == cons_ascii_string_map()) { - return cons_ascii_symbol_map(); - } - if (map == external_string_map()) { - return external_symbol_map(); - } - if (map == external_ascii_string_map()) { - return external_ascii_symbol_map(); - } - if (map == external_string_with_ascii_data_map()) { - return external_symbol_with_ascii_data_map(); - } - - // No match found. - return NULL; } @@ -3790,7 +4232,7 @@ MaybeObject* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, ASSERT(chars >= 0); // Ensure the chars matches the number of characters in the buffer. ASSERT(static_cast<unsigned>(chars) == buffer->Length()); - // Determine whether the string is ascii. + // Determine whether the string is ASCII. bool is_ascii = true; while (buffer->has_more()) { if (buffer->GetNext() > unibrow::Utf8::kMaxOneByteChar) { @@ -3821,12 +4263,12 @@ MaybeObject* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, // Allocate string. Object* result; { MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace()) - ? lo_space_->AllocateRaw(size) + ? lo_space_->AllocateRaw(size, NOT_EXECUTABLE) : old_data_space_->AllocateRaw(size); if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<HeapObject*>(result)->set_map(map); + reinterpret_cast<HeapObject*>(result)->set_map_no_write_barrier(map); // Set length and hash fields of the allocated string. String* answer = String::cast(result); answer->set_length(chars); @@ -3870,7 +4312,7 @@ MaybeObject* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) { } // Partially initialize the object. - HeapObject::cast(result)->set_map(ascii_string_map()); + HeapObject::cast(result)->set_map_no_write_barrier(ascii_string_map()); String::cast(result)->set_length(length); String::cast(result)->set_hash_field(String::kEmptyHashField); ASSERT_EQ(size, HeapObject::cast(result)->Size()); @@ -3905,7 +4347,7 @@ MaybeObject* Heap::AllocateRawTwoByteString(int length, } // Partially initialize the object. - HeapObject::cast(result)->set_map(string_map()); + HeapObject::cast(result)->set_map_no_write_barrier(string_map()); String::cast(result)->set_length(length); String::cast(result)->set_hash_field(String::kEmptyHashField); ASSERT_EQ(size, HeapObject::cast(result)->Size()); @@ -3913,6 +4355,25 @@ MaybeObject* Heap::AllocateRawTwoByteString(int length, } +MaybeObject* Heap::AllocateJSArray( + ElementsKind elements_kind, + PretenureFlag pretenure) { + Context* global_context = isolate()->context()->global_context(); + JSFunction* array_function = global_context->array_function(); + Map* map = array_function->initial_map(); + if (elements_kind == FAST_ELEMENTS || !FLAG_smi_only_arrays) { + map = Map::cast(global_context->object_js_array_map()); + } else if (elements_kind == FAST_DOUBLE_ELEMENTS) { + map = Map::cast(global_context->double_js_array_map()); + } else { + ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); + ASSERT(map == global_context->smi_js_array_map()); + } + + return AllocateJSObjectFromMap(map, pretenure); +} + + MaybeObject* Heap::AllocateEmptyFixedArray() { int size = FixedArray::SizeFor(0); Object* result; @@ -3921,7 +4382,8 @@ MaybeObject* Heap::AllocateEmptyFixedArray() { if (!maybe_result->ToObject(&result)) return maybe_result; } // Initialize the object. - reinterpret_cast<FixedArray*>(result)->set_map(fixed_array_map()); + reinterpret_cast<FixedArray*>(result)->set_map_no_write_barrier( + fixed_array_map()); reinterpret_cast<FixedArray*>(result)->set_length(0); return result; } @@ -3938,7 +4400,7 @@ MaybeObject* Heap::AllocateRawFixedArray(int length) { int size = FixedArray::SizeFor(length); return size <= kMaxObjectSizeInNewSpace ? new_space_.AllocateRaw(size) - : lo_space_->AllocateRawFixedArray(size); + : lo_space_->AllocateRaw(size, NOT_EXECUTABLE); } @@ -3950,13 +4412,13 @@ MaybeObject* Heap::CopyFixedArrayWithMap(FixedArray* src, Map* map) { } if (InNewSpace(obj)) { HeapObject* dst = HeapObject::cast(obj); - dst->set_map(map); + dst->set_map_no_write_barrier(map); CopyBlock(dst->address() + kPointerSize, src->address() + kPointerSize, FixedArray::SizeFor(len) - kPointerSize); return obj; } - HeapObject::cast(obj)->set_map(map); + HeapObject::cast(obj)->set_map_no_write_barrier(map); FixedArray* result = FixedArray::cast(obj); result->set_length(len); @@ -3976,7 +4438,7 @@ MaybeObject* Heap::CopyFixedDoubleArrayWithMap(FixedDoubleArray* src, if (!maybe_obj->ToObject(&obj)) return maybe_obj; } HeapObject* dst = HeapObject::cast(obj); - dst->set_map(map); + dst->set_map_no_write_barrier(map); CopyBlock( dst->address() + FixedDoubleArray::kLengthOffset, src->address() + FixedDoubleArray::kLengthOffset, @@ -3994,7 +4456,7 @@ MaybeObject* Heap::AllocateFixedArray(int length) { } // Initialize header. FixedArray* array = reinterpret_cast<FixedArray*>(result); - array->set_map(fixed_array_map()); + array->set_map_no_write_barrier(fixed_array_map()); array->set_length(length); // Initialize body. ASSERT(!InNewSpace(undefined_value())); @@ -4042,7 +4504,7 @@ MUST_USE_RESULT static MaybeObject* AllocateFixedArrayWithFiller( if (!maybe_result->ToObject(&result)) return maybe_result; } - HeapObject::cast(result)->set_map(heap->fixed_array_map()); + HeapObject::cast(result)->set_map_no_write_barrier(heap->fixed_array_map()); FixedArray* array = FixedArray::cast(result); array->set_length(length); MemsetPointer(array->data_start(), filler, length); @@ -4075,7 +4537,8 @@ MaybeObject* Heap::AllocateUninitializedFixedArray(int length) { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - reinterpret_cast<FixedArray*>(obj)->set_map(fixed_array_map()); + reinterpret_cast<FixedArray*>(obj)->set_map_no_write_barrier( + fixed_array_map()); FixedArray::cast(obj)->set_length(length); return obj; } @@ -4089,7 +4552,7 @@ MaybeObject* Heap::AllocateEmptyFixedDoubleArray() { if (!maybe_result->ToObject(&result)) return maybe_result; } // Initialize the object. - reinterpret_cast<FixedDoubleArray*>(result)->set_map( + reinterpret_cast<FixedDoubleArray*>(result)->set_map_no_write_barrier( fixed_double_array_map()); reinterpret_cast<FixedDoubleArray*>(result)->set_length(0); return result; @@ -4101,14 +4564,36 @@ MaybeObject* Heap::AllocateUninitializedFixedDoubleArray( PretenureFlag pretenure) { if (length == 0) return empty_fixed_double_array(); - Object* obj; - { MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(length, pretenure); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; + Object* elements_object; + MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(length, pretenure); + if (!maybe_obj->ToObject(&elements_object)) return maybe_obj; + FixedDoubleArray* elements = + reinterpret_cast<FixedDoubleArray*>(elements_object); + + elements->set_map_no_write_barrier(fixed_double_array_map()); + elements->set_length(length); + return elements; +} + + +MaybeObject* Heap::AllocateFixedDoubleArrayWithHoles( + int length, + PretenureFlag pretenure) { + if (length == 0) return empty_fixed_double_array(); + + Object* elements_object; + MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(length, pretenure); + if (!maybe_obj->ToObject(&elements_object)) return maybe_obj; + FixedDoubleArray* elements = + reinterpret_cast<FixedDoubleArray*>(elements_object); + + for (int i = 0; i < length; ++i) { + elements->set_the_hole(i); } - reinterpret_cast<FixedDoubleArray*>(obj)->set_map(fixed_double_array_map()); - FixedDoubleArray::cast(obj)->set_length(length); - return obj; + elements->set_map_no_write_barrier(fixed_double_array_map()); + elements->set_length(length); + return elements; } @@ -4142,7 +4627,8 @@ MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) { { MaybeObject* maybe_result = AllocateFixedArray(length, pretenure); if (!maybe_result->ToObject(&result)) return maybe_result; } - reinterpret_cast<HeapObject*>(result)->set_map(hash_table_map()); + reinterpret_cast<HeapObject*>(result)->set_map_no_write_barrier( + hash_table_map()); ASSERT(result->IsHashTable()); return result; } @@ -4155,7 +4641,10 @@ MaybeObject* Heap::AllocateGlobalContext() { if (!maybe_result->ToObject(&result)) return maybe_result; } Context* context = reinterpret_cast<Context*>(result); - context->set_map(global_context_map()); + context->set_map_no_write_barrier(global_context_map()); + context->set_smi_js_array_map(undefined_value()); + context->set_double_js_array_map(undefined_value()); + context->set_object_js_array_map(undefined_value()); ASSERT(context->IsGlobalContext()); ASSERT(result->IsContext()); return result; @@ -4169,7 +4658,7 @@ MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) { if (!maybe_result->ToObject(&result)) return maybe_result; } Context* context = reinterpret_cast<Context*>(result); - context->set_map(function_context_map()); + context->set_map_no_write_barrier(function_context_map()); context->set_closure(function); context->set_previous(function->context()); context->set_extension(NULL); @@ -4189,7 +4678,7 @@ MaybeObject* Heap::AllocateCatchContext(JSFunction* function, if (!maybe_result->ToObject(&result)) return maybe_result; } Context* context = reinterpret_cast<Context*>(result); - context->set_map(catch_context_map()); + context->set_map_no_write_barrier(catch_context_map()); context->set_closure(function); context->set_previous(previous); context->set_extension(name); @@ -4207,7 +4696,7 @@ MaybeObject* Heap::AllocateWithContext(JSFunction* function, if (!maybe_result->ToObject(&result)) return maybe_result; } Context* context = reinterpret_cast<Context*>(result); - context->set_map(with_context_map()); + context->set_map_no_write_barrier(with_context_map()); context->set_closure(function); context->set_previous(previous); context->set_extension(extension); @@ -4218,14 +4707,14 @@ MaybeObject* Heap::AllocateWithContext(JSFunction* function, MaybeObject* Heap::AllocateBlockContext(JSFunction* function, Context* previous, - SerializedScopeInfo* scope_info) { + ScopeInfo* scope_info) { Object* result; { MaybeObject* maybe_result = - AllocateFixedArrayWithHoles(scope_info->NumberOfContextSlots()); + AllocateFixedArrayWithHoles(scope_info->ContextLength()); if (!maybe_result->ToObject(&result)) return maybe_result; } Context* context = reinterpret_cast<Context*>(result); - context->set_map(block_context_map()); + context->set_map_no_write_barrier(block_context_map()); context->set_closure(function); context->set_previous(previous); context->set_extension(scope_info); @@ -4234,14 +4723,11 @@ MaybeObject* Heap::AllocateBlockContext(JSFunction* function, } -MaybeObject* Heap::AllocateSerializedScopeInfo(int length) { - Object* result; - { MaybeObject* maybe_result = AllocateFixedArray(length, TENURED); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - SerializedScopeInfo* scope_info = - reinterpret_cast<SerializedScopeInfo*>(result); - scope_info->set_map(serialized_scope_info_map()); +MaybeObject* Heap::AllocateScopeInfo(int length) { + FixedArray* scope_info; + MaybeObject* maybe_scope_info = AllocateFixedArray(length, TENURED); + if (!maybe_scope_info->To(&scope_info)) return maybe_scope_info; + scope_info->set_map_no_write_barrier(scope_info_map()); return scope_info; } @@ -4269,7 +4755,97 @@ STRUCT_LIST(MAKE_CASE) } -bool Heap::IdleNotification() { +bool Heap::IsHeapIterable() { + return (!old_pointer_space()->was_swept_conservatively() && + !old_data_space()->was_swept_conservatively()); +} + + +void Heap::EnsureHeapIsIterable() { + ASSERT(IsAllocationAllowed()); + if (!IsHeapIterable()) { + CollectAllGarbage(kMakeHeapIterableMask, "Heap::EnsureHeapIsIterable"); + } + ASSERT(IsHeapIterable()); +} + + +bool Heap::IdleNotification(int hint) { + if (hint >= 1000) return IdleGlobalGC(); + if (contexts_disposed_ > 0 || !FLAG_incremental_marking || + FLAG_expose_gc || Serializer::enabled()) { + return true; + } + + // By doing small chunks of GC work in each IdleNotification, + // perform a round of incremental GCs and after that wait until + // the mutator creates enough garbage to justify a new round. + // An incremental GC progresses as follows: + // 1. many incremental marking steps, + // 2. one old space mark-sweep-compact, + // 3. many lazy sweep steps. + // Use mark-sweep-compact events to count incremental GCs in a round. + + intptr_t size_factor = Min(Max(hint, 30), 1000) / 10; + // The size factor is in range [3..100]. + intptr_t step_size = size_factor * IncrementalMarking::kAllocatedThreshold; + + if (incremental_marking()->IsStopped()) { + if (!IsSweepingComplete() && + !AdvanceSweepers(static_cast<int>(step_size))) { + return false; + } + } + + if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) { + if (EnoughGarbageSinceLastIdleRound()) { + StartIdleRound(); + } else { + return true; + } + } + + int new_mark_sweeps = ms_count_ - ms_count_at_last_idle_notification_; + mark_sweeps_since_idle_round_started_ += new_mark_sweeps; + ms_count_at_last_idle_notification_ = ms_count_; + + if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) { + FinishIdleRound(); + return true; + } + + if (incremental_marking()->IsStopped()) { + if (hint < 1000 && !WorthStartingGCWhenIdle()) { + FinishIdleRound(); + return true; + } + incremental_marking()->Start(); + } + + // This flag prevents incremental marking from requesting GC via stack guard + idle_notification_will_schedule_next_gc_ = true; + incremental_marking()->Step(step_size); + idle_notification_will_schedule_next_gc_ = false; + + if (incremental_marking()->IsComplete()) { + bool uncommit = false; + if (gc_count_at_last_idle_gc_ == gc_count_) { + // No GC since the last full GC, the mutator is probably not active. + isolate_->compilation_cache()->Clear(); + uncommit = true; + } + CollectAllGarbage(kNoGCFlags, "idle notification: finalize incremental"); + gc_count_at_last_idle_gc_ = gc_count_; + if (uncommit) { + new_space_.Shrink(); + UncommitFromSpace(); + } + } + return false; +} + + +bool Heap::IdleGlobalGC() { static const int kIdlesBeforeScavenge = 4; static const int kIdlesBeforeMarkSweep = 7; static const int kIdlesBeforeMarkCompact = 8; @@ -4299,9 +4875,10 @@ bool Heap::IdleNotification() { if (number_idle_notifications_ == kIdlesBeforeScavenge) { if (contexts_disposed_ > 0) { HistogramTimerScope scope(isolate_->counters()->gc_context()); - CollectAllGarbage(false); + CollectAllGarbage(kReduceMemoryFootprintMask, + "idle notification: contexts disposed"); } else { - CollectGarbage(NEW_SPACE); + CollectGarbage(NEW_SPACE, "idle notification"); } new_space_.Shrink(); last_idle_notification_gc_count_ = gc_count_; @@ -4311,12 +4888,12 @@ bool Heap::IdleNotification() { // generated code for cached functions. isolate_->compilation_cache()->Clear(); - CollectAllGarbage(false); + CollectAllGarbage(kReduceMemoryFootprintMask, "idle notification"); new_space_.Shrink(); last_idle_notification_gc_count_ = gc_count_; } else if (number_idle_notifications_ == kIdlesBeforeMarkCompact) { - CollectAllGarbage(true); + CollectAllGarbage(kReduceMemoryFootprintMask, "idle notification"); new_space_.Shrink(); last_idle_notification_gc_count_ = gc_count_; number_idle_notifications_ = 0; @@ -4326,7 +4903,8 @@ bool Heap::IdleNotification() { contexts_disposed_ = 0; } else { HistogramTimerScope scope(isolate_->counters()->gc_context()); - CollectAllGarbage(false); + CollectAllGarbage(kReduceMemoryFootprintMask, + "idle notification: contexts disposed"); last_idle_notification_gc_count_ = gc_count_; } // If this is the first idle notification, we reset the @@ -4346,8 +4924,11 @@ bool Heap::IdleNotification() { // Make sure that we have no pending context disposals and // conditionally uncommit from space. - ASSERT(contexts_disposed_ == 0); + // Take into account that we might have decided to delay full collection + // because incremental marking is in progress. + ASSERT((contexts_disposed_ == 0) || !incremental_marking()->IsStopped()); if (uncommit) UncommitFromSpace(); + return finished; } @@ -4355,7 +4936,7 @@ bool Heap::IdleNotification() { #ifdef DEBUG void Heap::Print() { - if (!HasBeenSetup()) return; + if (!HasBeenSetUp()) return; isolate()->PrintStack(); AllSpaces spaces; for (Space* space = spaces.next(); space != NULL; space = spaces.next()) @@ -4381,11 +4962,11 @@ void Heap::ReportHeapStatistics(const char* title) { USE(title); PrintF(">>>>>> =============== %s (%d) =============== >>>>>>\n", title, gc_count_); - PrintF("mark-compact GC : %d\n", mc_count_); PrintF("old_gen_promotion_limit_ %" V8_PTR_PREFIX "d\n", old_gen_promotion_limit_); PrintF("old_gen_allocation_limit_ %" V8_PTR_PREFIX "d\n", old_gen_allocation_limit_); + PrintF("old_gen_limit_factor_ %d\n", old_gen_limit_factor_); PrintF("\n"); PrintF("Number of handles : %d\n", HandleScope::NumberOfHandles()); @@ -4420,7 +5001,7 @@ bool Heap::Contains(HeapObject* value) { bool Heap::Contains(Address addr) { if (OS::IsOutsideAllocatedSpace(addr)) return false; - return HasBeenSetup() && + return HasBeenSetUp() && (new_space_.ToSpaceContains(addr) || old_pointer_space_->Contains(addr) || old_data_space_->Contains(addr) || @@ -4438,7 +5019,7 @@ bool Heap::InSpace(HeapObject* value, AllocationSpace space) { bool Heap::InSpace(Address addr, AllocationSpace space) { if (OS::IsOutsideAllocatedSpace(addr)) return false; - if (!HasBeenSetup()) return false; + if (!HasBeenSetUp()) return false; switch (space) { case NEW_SPACE: @@ -4462,69 +5043,18 @@ bool Heap::InSpace(Address addr, AllocationSpace space) { #ifdef DEBUG -static void DummyScavengePointer(HeapObject** p) { -} - - -static void VerifyPointersUnderWatermark( - PagedSpace* space, - DirtyRegionCallback visit_dirty_region) { - PageIterator it(space, PageIterator::PAGES_IN_USE); - - while (it.has_next()) { - Page* page = it.next(); - Address start = page->ObjectAreaStart(); - Address end = page->AllocationWatermark(); - - HEAP->IterateDirtyRegions(Page::kAllRegionsDirtyMarks, - start, - end, - visit_dirty_region, - &DummyScavengePointer); - } -} - - -static void VerifyPointersUnderWatermark(LargeObjectSpace* space) { - LargeObjectIterator it(space); - for (HeapObject* object = it.next(); object != NULL; object = it.next()) { - if (object->IsFixedArray()) { - Address slot_address = object->address(); - Address end = object->address() + object->Size(); - - while (slot_address < end) { - HeapObject** slot = reinterpret_cast<HeapObject**>(slot_address); - // When we are not in GC the Heap::InNewSpace() predicate - // checks that pointers which satisfy predicate point into - // the active semispace. - HEAP->InNewSpace(*slot); - slot_address += kPointerSize; - } - } - } -} - - void Heap::Verify() { - ASSERT(HasBeenSetup()); + ASSERT(HasBeenSetUp()); + + store_buffer()->Verify(); VerifyPointersVisitor visitor; IterateRoots(&visitor, VISIT_ONLY_STRONG); new_space_.Verify(); - VerifyPointersAndDirtyRegionsVisitor dirty_regions_visitor; - old_pointer_space_->Verify(&dirty_regions_visitor); - map_space_->Verify(&dirty_regions_visitor); - - VerifyPointersUnderWatermark(old_pointer_space_, - &IteratePointersInDirtyRegion); - VerifyPointersUnderWatermark(map_space_, - &IteratePointersInDirtyMapsRegion); - VerifyPointersUnderWatermark(lo_space_); - - VerifyPageWatermarkValidity(old_pointer_space_, ALL_INVALID); - VerifyPageWatermarkValidity(map_space_, ALL_INVALID); + old_pointer_space_->Verify(&visitor); + map_space_->Verify(&visitor); VerifyPointersVisitor no_dirty_regions_visitor; old_data_space_->Verify(&no_dirty_regions_visitor); @@ -4533,6 +5063,7 @@ void Heap::Verify() { lo_space_->Verify(); } + #endif // DEBUG @@ -4628,275 +5159,221 @@ bool Heap::LookupSymbolIfExists(String* string, String** symbol) { #ifdef DEBUG void Heap::ZapFromSpace() { - ASSERT(reinterpret_cast<Object*>(kFromSpaceZapValue)->IsFailure()); - for (Address a = new_space_.FromSpaceLow(); - a < new_space_.FromSpaceHigh(); - a += kPointerSize) { - Memory::Address_at(a) = kFromSpaceZapValue; + NewSpacePageIterator it(new_space_.FromSpaceStart(), + new_space_.FromSpaceEnd()); + while (it.has_next()) { + NewSpacePage* page = it.next(); + for (Address cursor = page->body(), limit = page->body_limit(); + cursor < limit; + cursor += kPointerSize) { + Memory::Address_at(cursor) = kFromSpaceZapValue; + } } } #endif // DEBUG -bool Heap::IteratePointersInDirtyRegion(Heap* heap, - Address start, - Address end, - ObjectSlotCallback copy_object_func) { +void Heap::IterateAndMarkPointersToFromSpace(Address start, + Address end, + ObjectSlotCallback callback) { Address slot_address = start; - bool pointers_to_new_space_found = false; + + // We are not collecting slots on new space objects during mutation + // thus we have to scan for pointers to evacuation candidates when we + // promote objects. But we should not record any slots in non-black + // objects. Grey object's slots would be rescanned. + // White object might not survive until the end of collection + // it would be a violation of the invariant to record it's slots. + bool record_slots = false; + if (incremental_marking()->IsCompacting()) { + MarkBit mark_bit = Marking::MarkBitFrom(HeapObject::FromAddress(start)); + record_slots = Marking::IsBlack(mark_bit); + } while (slot_address < end) { Object** slot = reinterpret_cast<Object**>(slot_address); - if (heap->InNewSpace(*slot)) { - ASSERT((*slot)->IsHeapObject()); - copy_object_func(reinterpret_cast<HeapObject**>(slot)); - if (heap->InNewSpace(*slot)) { - ASSERT((*slot)->IsHeapObject()); - pointers_to_new_space_found = true; + Object* object = *slot; + // If the store buffer becomes overfull we mark pages as being exempt from + // the store buffer. These pages are scanned to find pointers that point + // to the new space. In that case we may hit newly promoted objects and + // fix the pointers before the promotion queue gets to them. Thus the 'if'. + if (object->IsHeapObject()) { + if (Heap::InFromSpace(object)) { + callback(reinterpret_cast<HeapObject**>(slot), + HeapObject::cast(object)); + Object* new_object = *slot; + if (InNewSpace(new_object)) { + SLOW_ASSERT(Heap::InToSpace(new_object)); + SLOW_ASSERT(new_object->IsHeapObject()); + store_buffer_.EnterDirectlyIntoStoreBuffer( + reinterpret_cast<Address>(slot)); + } + SLOW_ASSERT(!MarkCompactCollector::IsOnEvacuationCandidate(new_object)); + } else if (record_slots && + MarkCompactCollector::IsOnEvacuationCandidate(object)) { + mark_compact_collector()->RecordSlot(slot, slot, object); } } slot_address += kPointerSize; } - return pointers_to_new_space_found; } -// Compute start address of the first map following given addr. -static inline Address MapStartAlign(Address addr) { - Address page = Page::FromAddress(addr)->ObjectAreaStart(); - return page + (((addr - page) + (Map::kSize - 1)) / Map::kSize * Map::kSize); -} +#ifdef DEBUG +typedef bool (*CheckStoreBufferFilter)(Object** addr); -// Compute end address of the first map preceding given addr. -static inline Address MapEndAlign(Address addr) { - Address page = Page::FromAllocationTop(addr)->ObjectAreaStart(); - return page + ((addr - page) / Map::kSize * Map::kSize); +bool IsAMapPointerAddress(Object** addr) { + uintptr_t a = reinterpret_cast<uintptr_t>(addr); + int mod = a % Map::kSize; + return mod >= Map::kPointerFieldsBeginOffset && + mod < Map::kPointerFieldsEndOffset; } -static bool IteratePointersInDirtyMaps(Address start, - Address end, - ObjectSlotCallback copy_object_func) { - ASSERT(MapStartAlign(start) == start); - ASSERT(MapEndAlign(end) == end); - - Address map_address = start; - bool pointers_to_new_space_found = false; - - Heap* heap = HEAP; - while (map_address < end) { - ASSERT(!heap->InNewSpace(Memory::Object_at(map_address))); - ASSERT(Memory::Object_at(map_address)->IsMap()); +bool EverythingsAPointer(Object** addr) { + return true; +} - Address pointer_fields_start = map_address + Map::kPointerFieldsBeginOffset; - Address pointer_fields_end = map_address + Map::kPointerFieldsEndOffset; - if (Heap::IteratePointersInDirtyRegion(heap, - pointer_fields_start, - pointer_fields_end, - copy_object_func)) { - pointers_to_new_space_found = true; +static void CheckStoreBuffer(Heap* heap, + Object** current, + Object** limit, + Object**** store_buffer_position, + Object*** store_buffer_top, + CheckStoreBufferFilter filter, + Address special_garbage_start, + Address special_garbage_end) { + Map* free_space_map = heap->free_space_map(); + for ( ; current < limit; current++) { + Object* o = *current; + Address current_address = reinterpret_cast<Address>(current); + // Skip free space. + if (o == free_space_map) { + Address current_address = reinterpret_cast<Address>(current); + FreeSpace* free_space = + FreeSpace::cast(HeapObject::FromAddress(current_address)); + int skip = free_space->Size(); + ASSERT(current_address + skip <= reinterpret_cast<Address>(limit)); + ASSERT(skip > 0); + current_address += skip - kPointerSize; + current = reinterpret_cast<Object**>(current_address); + continue; + } + // Skip the current linear allocation space between top and limit which is + // unmarked with the free space map, but can contain junk. + if (current_address == special_garbage_start && + special_garbage_end != special_garbage_start) { + current_address = special_garbage_end - kPointerSize; + current = reinterpret_cast<Object**>(current_address); + continue; + } + if (!(*filter)(current)) continue; + ASSERT(current_address < special_garbage_start || + current_address >= special_garbage_end); + ASSERT(reinterpret_cast<uintptr_t>(o) != kFreeListZapValue); + // We have to check that the pointer does not point into new space + // without trying to cast it to a heap object since the hash field of + // a string can contain values like 1 and 3 which are tagged null + // pointers. + if (!heap->InNewSpace(o)) continue; + while (**store_buffer_position < current && + *store_buffer_position < store_buffer_top) { + (*store_buffer_position)++; + } + if (**store_buffer_position != current || + *store_buffer_position == store_buffer_top) { + Object** obj_start = current; + while (!(*obj_start)->IsMap()) obj_start--; + UNREACHABLE(); } - - map_address += Map::kSize; } - - return pointers_to_new_space_found; } -bool Heap::IteratePointersInDirtyMapsRegion( - Heap* heap, - Address start, - Address end, - ObjectSlotCallback copy_object_func) { - Address map_aligned_start = MapStartAlign(start); - Address map_aligned_end = MapEndAlign(end); - - bool contains_pointers_to_new_space = false; - - if (map_aligned_start != start) { - Address prev_map = map_aligned_start - Map::kSize; - ASSERT(Memory::Object_at(prev_map)->IsMap()); +// Check that the store buffer contains all intergenerational pointers by +// scanning a page and ensuring that all pointers to young space are in the +// store buffer. +void Heap::OldPointerSpaceCheckStoreBuffer() { + OldSpace* space = old_pointer_space(); + PageIterator pages(space); - Address pointer_fields_start = - Max(start, prev_map + Map::kPointerFieldsBeginOffset); + store_buffer()->SortUniq(); - Address pointer_fields_end = - Min(prev_map + Map::kPointerFieldsEndOffset, end); + while (pages.has_next()) { + Page* page = pages.next(); + Object** current = reinterpret_cast<Object**>(page->ObjectAreaStart()); - contains_pointers_to_new_space = - IteratePointersInDirtyRegion(heap, - pointer_fields_start, - pointer_fields_end, - copy_object_func) - || contains_pointers_to_new_space; - } - - contains_pointers_to_new_space = - IteratePointersInDirtyMaps(map_aligned_start, - map_aligned_end, - copy_object_func) - || contains_pointers_to_new_space; - - if (map_aligned_end != end) { - ASSERT(Memory::Object_at(map_aligned_end)->IsMap()); - - Address pointer_fields_start = - map_aligned_end + Map::kPointerFieldsBeginOffset; + Address end = page->ObjectAreaEnd(); - Address pointer_fields_end = - Min(end, map_aligned_end + Map::kPointerFieldsEndOffset); + Object*** store_buffer_position = store_buffer()->Start(); + Object*** store_buffer_top = store_buffer()->Top(); - contains_pointers_to_new_space = - IteratePointersInDirtyRegion(heap, - pointer_fields_start, - pointer_fields_end, - copy_object_func) - || contains_pointers_to_new_space; + Object** limit = reinterpret_cast<Object**>(end); + CheckStoreBuffer(this, + current, + limit, + &store_buffer_position, + store_buffer_top, + &EverythingsAPointer, + space->top(), + space->limit()); } - - return contains_pointers_to_new_space; } -void Heap::IterateAndMarkPointersToFromSpace(Address start, - Address end, - ObjectSlotCallback callback) { - Address slot_address = start; - Page* page = Page::FromAddress(start); - - uint32_t marks = page->GetRegionMarks(); - - while (slot_address < end) { - Object** slot = reinterpret_cast<Object**>(slot_address); - if (InFromSpace(*slot)) { - ASSERT((*slot)->IsHeapObject()); - callback(reinterpret_cast<HeapObject**>(slot)); - if (InNewSpace(*slot)) { - ASSERT((*slot)->IsHeapObject()); - marks |= page->GetRegionMaskForAddress(slot_address); - } - } - slot_address += kPointerSize; - } - - page->SetRegionMarks(marks); -} - - -uint32_t Heap::IterateDirtyRegions( - uint32_t marks, - Address area_start, - Address area_end, - DirtyRegionCallback visit_dirty_region, - ObjectSlotCallback copy_object_func) { - uint32_t newmarks = 0; - uint32_t mask = 1; - - if (area_start >= area_end) { - return newmarks; - } - - Address region_start = area_start; +void Heap::MapSpaceCheckStoreBuffer() { + MapSpace* space = map_space(); + PageIterator pages(space); - // area_start does not necessarily coincide with start of the first region. - // Thus to calculate the beginning of the next region we have to align - // area_start by Page::kRegionSize. - Address second_region = - reinterpret_cast<Address>( - reinterpret_cast<intptr_t>(area_start + Page::kRegionSize) & - ~Page::kRegionAlignmentMask); + store_buffer()->SortUniq(); - // Next region might be beyond area_end. - Address region_end = Min(second_region, area_end); + while (pages.has_next()) { + Page* page = pages.next(); + Object** current = reinterpret_cast<Object**>(page->ObjectAreaStart()); - if (marks & mask) { - if (visit_dirty_region(this, region_start, region_end, copy_object_func)) { - newmarks |= mask; - } - } - mask <<= 1; - - // Iterate subsequent regions which fully lay inside [area_start, area_end[. - region_start = region_end; - region_end = region_start + Page::kRegionSize; - - while (region_end <= area_end) { - if (marks & mask) { - if (visit_dirty_region(this, - region_start, - region_end, - copy_object_func)) { - newmarks |= mask; - } - } + Address end = page->ObjectAreaEnd(); - region_start = region_end; - region_end = region_start + Page::kRegionSize; - - mask <<= 1; - } + Object*** store_buffer_position = store_buffer()->Start(); + Object*** store_buffer_top = store_buffer()->Top(); - if (region_start != area_end) { - // A small piece of area left uniterated because area_end does not coincide - // with region end. Check whether region covering last part of area is - // dirty. - if (marks & mask) { - if (visit_dirty_region(this, region_start, area_end, copy_object_func)) { - newmarks |= mask; - } - } + Object** limit = reinterpret_cast<Object**>(end); + CheckStoreBuffer(this, + current, + limit, + &store_buffer_position, + store_buffer_top, + &IsAMapPointerAddress, + space->top(), + space->limit()); } - - return newmarks; } - -void Heap::IterateDirtyRegions( - PagedSpace* space, - DirtyRegionCallback visit_dirty_region, - ObjectSlotCallback copy_object_func, - ExpectedPageWatermarkState expected_page_watermark_state) { - - PageIterator it(space, PageIterator::PAGES_IN_USE); - - while (it.has_next()) { - Page* page = it.next(); - uint32_t marks = page->GetRegionMarks(); - - if (marks != Page::kAllRegionsCleanMarks) { - Address start = page->ObjectAreaStart(); - - // Do not try to visit pointers beyond page allocation watermark. - // Page can contain garbage pointers there. - Address end; - - if ((expected_page_watermark_state == WATERMARK_SHOULD_BE_VALID) || - page->IsWatermarkValid()) { - end = page->AllocationWatermark(); - } else { - end = page->CachedAllocationWatermark(); - } - - ASSERT(space == old_pointer_space_ || - (space == map_space_ && - ((page->ObjectAreaStart() - end) % Map::kSize == 0))); - - page->SetRegionMarks(IterateDirtyRegions(marks, - start, - end, - visit_dirty_region, - copy_object_func)); +void Heap::LargeObjectSpaceCheckStoreBuffer() { + LargeObjectIterator it(lo_space()); + for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { + // We only have code, sequential strings, or fixed arrays in large + // object space, and only fixed arrays can possibly contain pointers to + // the young generation. + if (object->IsFixedArray()) { + Object*** store_buffer_position = store_buffer()->Start(); + Object*** store_buffer_top = store_buffer()->Top(); + Object** current = reinterpret_cast<Object**>(object->address()); + Object** limit = + reinterpret_cast<Object**>(object->address() + object->Size()); + CheckStoreBuffer(this, + current, + limit, + &store_buffer_position, + store_buffer_top, + &EverythingsAPointer, + NULL, + NULL); } - - // Mark page watermark as invalid to maintain watermark validity invariant. - // See Page::FlipMeaningOfInvalidatedWatermarkFlag() for details. - page->InvalidateWatermark(true); } } +#endif void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) { @@ -4907,29 +5384,29 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) { void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) { v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex])); - v->Synchronize("symbol_table"); + v->Synchronize(VisitorSynchronization::kSymbolTable); if (mode != VISIT_ALL_IN_SCAVENGE && mode != VISIT_ALL_IN_SWEEP_NEWSPACE) { // Scavenge collections have special processing for this. external_string_table_.Iterate(v); } - v->Synchronize("external_string_table"); + v->Synchronize(VisitorSynchronization::kExternalStringsTable); } void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]); - v->Synchronize("strong_root_list"); + v->Synchronize(VisitorSynchronization::kStrongRootList); v->VisitPointer(BitCast<Object**>(&hidden_symbol_)); - v->Synchronize("symbol"); + v->Synchronize(VisitorSynchronization::kSymbol); isolate_->bootstrapper()->Iterate(v); - v->Synchronize("bootstrapper"); + v->Synchronize(VisitorSynchronization::kBootstrapper); isolate_->Iterate(v); - v->Synchronize("top"); + v->Synchronize(VisitorSynchronization::kTop); Relocatable::Iterate(v); - v->Synchronize("relocatable"); + v->Synchronize(VisitorSynchronization::kRelocatable); #ifdef ENABLE_DEBUGGER_SUPPORT isolate_->debug()->Iterate(v); @@ -4937,22 +5414,21 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { isolate_->deoptimizer_data()->Iterate(v); } #endif - v->Synchronize("debug"); + v->Synchronize(VisitorSynchronization::kDebug); isolate_->compilation_cache()->Iterate(v); - v->Synchronize("compilationcache"); + v->Synchronize(VisitorSynchronization::kCompilationCache); // Iterate over local handles in handle scopes. isolate_->handle_scope_implementer()->Iterate(v); - v->Synchronize("handlescope"); + v->Synchronize(VisitorSynchronization::kHandleScope); // Iterate over the builtin code objects and code stubs in the // heap. Note that it is not necessary to iterate over code objects // on scavenge collections. - if (mode != VISIT_ALL_IN_SCAVENGE && - mode != VISIT_ALL_IN_SWEEP_NEWSPACE) { + if (mode != VISIT_ALL_IN_SCAVENGE) { isolate_->builtins()->IterateBuiltins(v); } - v->Synchronize("builtins"); + v->Synchronize(VisitorSynchronization::kBuiltins); // Iterate over global handles. switch (mode) { @@ -4967,11 +5443,11 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { isolate_->global_handles()->IterateAllRoots(v); break; } - v->Synchronize("globalhandles"); + v->Synchronize(VisitorSynchronization::kGlobalHandles); // Iterate over pointers being held by inactive threads. isolate_->thread_manager()->Iterate(v); - v->Synchronize("threadmanager"); + v->Synchronize(VisitorSynchronization::kThreadManager); // Iterate over the pointers the Serialization/Deserialization code is // holding. @@ -4993,11 +5469,20 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { // and through the API, we should gracefully handle the case that the heap // size is not big enough to fit all the initial objects. bool Heap::ConfigureHeap(int max_semispace_size, - int max_old_gen_size, - int max_executable_size) { - if (HasBeenSetup()) return false; - - if (max_semispace_size > 0) max_semispace_size_ = max_semispace_size; + intptr_t max_old_gen_size, + intptr_t max_executable_size) { + if (HasBeenSetUp()) return false; + + if (max_semispace_size > 0) { + if (max_semispace_size < Page::kPageSize) { + max_semispace_size = Page::kPageSize; + if (FLAG_trace_gc) { + PrintF("Max semispace size cannot be less than %dkbytes\n", + Page::kPageSize >> 10); + } + } + max_semispace_size_ = max_semispace_size; + } if (Snapshot::IsEnabled()) { // If we are using a snapshot we always reserve the default amount @@ -5007,6 +5492,10 @@ bool Heap::ConfigureHeap(int max_semispace_size, // than the default reserved semispace size. if (max_semispace_size_ > reserved_semispace_size_) { max_semispace_size_ = reserved_semispace_size_; + if (FLAG_trace_gc) { + PrintF("Max semispace size cannot be more than %dkbytes\n", + reserved_semispace_size_ >> 10); + } } } else { // If we are not using snapshots we reserve space for the actual @@ -5032,8 +5521,12 @@ bool Heap::ConfigureHeap(int max_semispace_size, initial_semispace_size_ = Min(initial_semispace_size_, max_semispace_size_); external_allocation_limit_ = 10 * max_semispace_size_; - // The old generation is paged. - max_old_generation_size_ = RoundUp(max_old_generation_size_, Page::kPageSize); + // The old generation is paged and needs at least one page for each space. + int paged_space_count = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1; + max_old_generation_size_ = Max(static_cast<intptr_t>(paged_space_count * + Page::kPageSize), + RoundUp(max_old_generation_size_, + Page::kPageSize)); configured_ = true; return true; @@ -5041,9 +5534,9 @@ bool Heap::ConfigureHeap(int max_semispace_size, bool Heap::ConfigureHeapDefault() { - return ConfigureHeap(FLAG_max_new_space_size / 2 * KB, - FLAG_max_old_space_size * MB, - FLAG_max_executable_size * MB); + return ConfigureHeap(static_cast<intptr_t>(FLAG_max_new_space_size / 2) * KB, + static_cast<intptr_t>(FLAG_max_old_space_size) * MB, + static_cast<intptr_t>(FLAG_max_executable_size) * MB); } @@ -5071,7 +5564,7 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) { *stats->os_error = OS::GetLastError(); isolate()->memory_allocator()->Available(); if (take_snapshot) { - HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapIterator iterator; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { @@ -5094,6 +5587,16 @@ intptr_t Heap::PromotedSpaceSize() { } +intptr_t Heap::PromotedSpaceSizeOfObjects() { + return old_pointer_space_->SizeOfObjects() + + old_data_space_->SizeOfObjects() + + code_space_->SizeOfObjects() + + map_space_->SizeOfObjects() + + cell_space_->SizeOfObjects() + + lo_space_->SizeOfObjects(); +} + + int Heap::PromotedExternalMemorySize() { if (amount_of_external_allocated_memory_ <= amount_of_external_allocated_memory_at_last_global_gc_) return 0; @@ -5154,7 +5657,7 @@ class HeapDebugUtils { Address map_addr = map_p->address(); - obj->set_map(reinterpret_cast<Map*>(map_addr + kMarkTag)); + obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_addr + kMarkTag)); MarkObjectRecursively(&map); @@ -5201,7 +5704,7 @@ class HeapDebugUtils { HeapObject* map_p = HeapObject::FromAddress(map_addr); - obj->set_map(reinterpret_cast<Map*>(map_p)); + obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_p)); UnmarkObjectRecursively(reinterpret_cast<Object**>(&map_p)); @@ -5267,8 +5770,9 @@ class HeapDebugUtils { #endif -bool Heap::Setup(bool create_heap_objects) { +bool Heap::SetUp(bool create_heap_objects) { #ifdef DEBUG + allocation_timeout_ = FLAG_gc_interval; debug_utils_ = new HeapDebugUtils(this); #endif @@ -5276,7 +5780,7 @@ bool Heap::Setup(bool create_heap_objects) { // goes wrong, just return false. The caller should check the results and // call Heap::TearDown() to release allocated memory. // - // If the heap is not yet configured (eg, through the API), configure it. + // If the heap is not yet configured (e.g. through the API), configure it. // Configuration is based on the flags new-space-size (really the semispace // size) and old-space-size if set or the initial values of semispace_size_ // and old_generation_size_ otherwise. @@ -5287,31 +5791,21 @@ bool Heap::Setup(bool create_heap_objects) { gc_initializer_mutex->Lock(); static bool initialized_gc = false; if (!initialized_gc) { - initialized_gc = true; - InitializeScavengingVisitorsTables(); - NewSpaceScavenger::Initialize(); - MarkCompactCollector::Initialize(); + initialized_gc = true; + InitializeScavengingVisitorsTables(); + NewSpaceScavenger::Initialize(); + MarkCompactCollector::Initialize(); } gc_initializer_mutex->Unlock(); MarkMapPointersAsEncoded(false); - // Setup memory allocator and reserve a chunk of memory for new - // space. The chunk is double the size of the requested reserved - // new space size to ensure that we can find a pair of semispaces that - // are contiguous and aligned to their size. - if (!isolate_->memory_allocator()->Setup(MaxReserved(), MaxExecutableSize())) + // Set up memory allocator. + if (!isolate_->memory_allocator()->SetUp(MaxReserved(), MaxExecutableSize())) return false; - void* chunk = - isolate_->memory_allocator()->ReserveInitialChunk( - 4 * reserved_semispace_size_); - if (chunk == NULL) return false; - - // Align the pair of semispaces to their size, which must be a power - // of 2. - Address new_space_start = - RoundUp(reinterpret_cast<byte*>(chunk), 2 * reserved_semispace_size_); - if (!new_space_.Setup(new_space_start, 2 * reserved_semispace_size_)) { + + // Set up new space. + if (!new_space_.SetUp(reserved_semispace_size_, max_semispace_size_)) { return false; } @@ -5322,7 +5816,7 @@ bool Heap::Setup(bool create_heap_objects) { OLD_POINTER_SPACE, NOT_EXECUTABLE); if (old_pointer_space_ == NULL) return false; - if (!old_pointer_space_->Setup(NULL, 0)) return false; + if (!old_pointer_space_->SetUp()) return false; // Initialize old data space. old_data_space_ = @@ -5331,14 +5825,14 @@ bool Heap::Setup(bool create_heap_objects) { OLD_DATA_SPACE, NOT_EXECUTABLE); if (old_data_space_ == NULL) return false; - if (!old_data_space_->Setup(NULL, 0)) return false; + if (!old_data_space_->SetUp()) return false; // Initialize the code space, set its maximum capacity to the old // generation size. It needs executable memory. // On 64-bit platform(s), we put all code objects in a 2 GB range of // virtual address space, so that they can call each other with near calls. if (code_range_size_ > 0) { - if (!isolate_->code_range()->Setup(code_range_size_)) { + if (!isolate_->code_range()->SetUp(code_range_size_)) { return false; } } @@ -5346,30 +5840,26 @@ bool Heap::Setup(bool create_heap_objects) { code_space_ = new OldSpace(this, max_old_generation_size_, CODE_SPACE, EXECUTABLE); if (code_space_ == NULL) return false; - if (!code_space_->Setup(NULL, 0)) return false; + if (!code_space_->SetUp()) return false; // Initialize map space. - map_space_ = new MapSpace(this, FLAG_use_big_map_space - ? max_old_generation_size_ - : MapSpace::kMaxMapPageIndex * Page::kPageSize, - FLAG_max_map_space_pages, - MAP_SPACE); + map_space_ = new MapSpace(this, max_old_generation_size_, MAP_SPACE); if (map_space_ == NULL) return false; - if (!map_space_->Setup(NULL, 0)) return false; + if (!map_space_->SetUp()) return false; // Initialize global property cell space. cell_space_ = new CellSpace(this, max_old_generation_size_, CELL_SPACE); if (cell_space_ == NULL) return false; - if (!cell_space_->Setup(NULL, 0)) return false; + if (!cell_space_->SetUp()) return false; // The large object code space may contain code or data. We set the memory // to be non-executable here for safety, but this means we need to enable it // explicitly when allocating large code objects. - lo_space_ = new LargeObjectSpace(this, LO_SPACE); + lo_space_ = new LargeObjectSpace(this, max_old_generation_size_, LO_SPACE); if (lo_space_ == NULL) return false; - if (!lo_space_->Setup()) return false; + if (!lo_space_->SetUp()) return false; - // Setup the seed that is used to randomize the string hash function. + // Set up the seed that is used to randomize the string hash function. ASSERT(hash_seed() == 0); if (FLAG_randomize_hashes) { if (FLAG_hash_seed == 0) { @@ -5394,6 +5884,8 @@ bool Heap::Setup(bool create_heap_objects) { LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity())); LOG(isolate_, IntPtrTEvent("heap-available", Available())); + store_buffer()->SetUp(); + return true; } @@ -5420,7 +5912,6 @@ void Heap::TearDown() { PrintF("\n\n"); PrintF("gc_count=%d ", gc_count_); PrintF("mark_sweep_count=%d ", ms_count_); - PrintF("mark_compact_count=%d ", mc_count_); PrintF("max_gc_pause=%d ", get_max_gc_pause()); PrintF("min_in_mutator=%d ", get_min_in_mutator()); PrintF("max_alive_after_gc=%" V8_PTR_PREFIX "d ", @@ -5470,6 +5961,9 @@ void Heap::TearDown() { lo_space_ = NULL; } + store_buffer()->TearDown(); + incremental_marking()->TearDown(); + isolate_->memory_allocator()->TearDown(); #ifdef DEBUG @@ -5482,8 +5976,11 @@ void Heap::TearDown() { void Heap::Shrink() { // Try to shrink all paged spaces. PagedSpaces spaces; - for (PagedSpace* space = spaces.next(); space != NULL; space = spaces.next()) - space->Shrink(); + for (PagedSpace* space = spaces.next(); + space != NULL; + space = spaces.next()) { + space->ReleaseAllUnusedPages(); + } } @@ -5686,98 +6183,54 @@ class HeapObjectsFilter { }; -class FreeListNodesFilter : public HeapObjectsFilter { - public: - FreeListNodesFilter() { - MarkFreeListNodes(); - } - - bool SkipObject(HeapObject* object) { - if (object->IsMarked()) { - object->ClearMark(); - return true; - } else { - return false; - } - } - - private: - void MarkFreeListNodes() { - Heap* heap = HEAP; - heap->old_pointer_space()->MarkFreeListNodes(); - heap->old_data_space()->MarkFreeListNodes(); - MarkCodeSpaceFreeListNodes(heap); - heap->map_space()->MarkFreeListNodes(); - heap->cell_space()->MarkFreeListNodes(); - } - - void MarkCodeSpaceFreeListNodes(Heap* heap) { - // For code space, using FreeListNode::IsFreeListNode is OK. - HeapObjectIterator iter(heap->code_space()); - for (HeapObject* obj = iter.next_object(); - obj != NULL; - obj = iter.next_object()) { - if (FreeListNode::IsFreeListNode(obj)) obj->SetMark(); - } - } - - AssertNoAllocation no_alloc; -}; - - class UnreachableObjectsFilter : public HeapObjectsFilter { public: UnreachableObjectsFilter() { - MarkUnreachableObjects(); + MarkReachableObjects(); + } + + ~UnreachableObjectsFilter() { + Isolate::Current()->heap()->mark_compact_collector()->ClearMarkbits(); } bool SkipObject(HeapObject* object) { - if (object->IsMarked()) { - object->ClearMark(); - return true; - } else { - return false; - } + MarkBit mark_bit = Marking::MarkBitFrom(object); + return !mark_bit.Get(); } private: - class UnmarkingVisitor : public ObjectVisitor { + class MarkingVisitor : public ObjectVisitor { public: - UnmarkingVisitor() : list_(10) {} + MarkingVisitor() : marking_stack_(10) {} void VisitPointers(Object** start, Object** end) { for (Object** p = start; p < end; p++) { if (!(*p)->IsHeapObject()) continue; HeapObject* obj = HeapObject::cast(*p); - if (obj->IsMarked()) { - obj->ClearMark(); - list_.Add(obj); + MarkBit mark_bit = Marking::MarkBitFrom(obj); + if (!mark_bit.Get()) { + mark_bit.Set(); + marking_stack_.Add(obj); } } } - bool can_process() { return !list_.is_empty(); } - - void ProcessNext() { - HeapObject* obj = list_.RemoveLast(); - obj->Iterate(this); + void TransitiveClosure() { + while (!marking_stack_.is_empty()) { + HeapObject* obj = marking_stack_.RemoveLast(); + obj->Iterate(this); + } } private: - List<HeapObject*> list_; + List<HeapObject*> marking_stack_; }; - void MarkUnreachableObjects() { - HeapIterator iterator; - for (HeapObject* obj = iterator.next(); - obj != NULL; - obj = iterator.next()) { - obj->SetMark(); - } - UnmarkingVisitor visitor; - HEAP->IterateRoots(&visitor, VISIT_ALL); - while (visitor.can_process()) - visitor.ProcessNext(); + void MarkReachableObjects() { + Heap* heap = Isolate::Current()->heap(); + MarkingVisitor visitor; + heap->IterateRoots(&visitor, VISIT_ALL); + visitor.TransitiveClosure(); } AssertNoAllocation no_alloc; @@ -5805,12 +6258,8 @@ HeapIterator::~HeapIterator() { void HeapIterator::Init() { // Start the iteration. - space_iterator_ = filtering_ == kNoFiltering ? new SpaceIterator : - new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject); + space_iterator_ = new SpaceIterator; switch (filtering_) { - case kFilterFreeListNodes: - filter_ = new FreeListNodesFilter; - break; case kFilterUnreachable: filter_ = new UnreachableObjectsFilter; break; @@ -5946,6 +6395,11 @@ void PathTracer::TracePathFrom(Object** root) { } +static bool SafeIsGlobalContext(HeapObject* obj) { + return obj->map() == obj->GetHeap()->raw_unchecked_global_context_map(); +} + + void PathTracer::MarkRecursively(Object** p, MarkVisitor* mark_visitor) { if (!(*p)->IsHeapObject()) return; @@ -5964,14 +6418,14 @@ void PathTracer::MarkRecursively(Object** p, MarkVisitor* mark_visitor) { return; } - bool is_global_context = obj->IsGlobalContext(); + bool is_global_context = SafeIsGlobalContext(obj); // not visited yet Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map)); Address map_addr = map_p->address(); - obj->set_map(reinterpret_cast<Map*>(map_addr + kMarkTag)); + obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_addr + kMarkTag)); // Scan the object body. if (is_global_context && (visit_mode_ == VISIT_ONLY_STRONG)) { @@ -6013,7 +6467,7 @@ void PathTracer::UnmarkRecursively(Object** p, UnmarkVisitor* unmark_visitor) { HeapObject* map_p = HeapObject::FromAddress(map_addr); - obj->set_map(reinterpret_cast<Map*>(map_p)); + obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_p)); UnmarkRecursively(reinterpret_cast<Object**>(&map_p), unmark_visitor); @@ -6072,31 +6526,30 @@ static intptr_t CountTotalHolesSize() { for (OldSpace* space = spaces.next(); space != NULL; space = spaces.next()) { - holes_size += space->Waste() + space->AvailableFree(); + holes_size += space->Waste() + space->Available(); } return holes_size; } -GCTracer::GCTracer(Heap* heap) +GCTracer::GCTracer(Heap* heap, + const char* gc_reason, + const char* collector_reason) : start_time_(0.0), - start_size_(0), + start_object_size_(0), + start_memory_size_(0), gc_count_(0), full_gc_count_(0), - is_compacting_(false), - marked_count_(0), allocated_since_last_gc_(0), spent_in_mutator_(0), promoted_objects_size_(0), - heap_(heap) { - // These two fields reflect the state of the previous full collection. - // Set them before they are changed by the collector. - previous_has_compacted_ = heap_->mark_compact_collector_.HasCompacted(); - previous_marked_count_ = - heap_->mark_compact_collector_.previous_marked_count(); + heap_(heap), + gc_reason_(gc_reason), + collector_reason_(collector_reason) { if (!FLAG_trace_gc && !FLAG_print_cumulative_gc_stat) return; start_time_ = OS::TimeCurrentMillis(); - start_size_ = heap_->SizeOfObjects(); + start_object_size_ = heap_->SizeOfObjects(); + start_memory_size_ = heap_->isolate()->memory_allocator()->Size(); for (int i = 0; i < Scope::kNumberOfScopes; i++) { scopes_[i] = 0; @@ -6110,6 +6563,14 @@ GCTracer::GCTracer(Heap* heap) if (heap_->last_gc_end_timestamp_ > 0) { spent_in_mutator_ = Max(start_time_ - heap_->last_gc_end_timestamp_, 0.0); } + + steps_count_ = heap_->incremental_marking()->steps_count(); + steps_took_ = heap_->incremental_marking()->steps_took(); + longest_step_ = heap_->incremental_marking()->longest_step(); + steps_count_since_last_gc_ = + heap_->incremental_marking()->steps_count_since_last_gc(); + steps_took_since_last_gc_ = + heap_->incremental_marking()->steps_took_since_last_gc(); } @@ -6135,16 +6596,46 @@ GCTracer::~GCTracer() { } } + PrintF("%8.0f ms: ", heap_->isolate()->time_millis_since_init()); + if (!FLAG_trace_gc_nvp) { int external_time = static_cast<int>(scopes_[Scope::EXTERNAL]); - PrintF("%s %.1f -> %.1f MB, ", + double end_memory_size_mb = + static_cast<double>(heap_->isolate()->memory_allocator()->Size()) / MB; + + PrintF("%s %.1f (%.1f) -> %.1f (%.1f) MB, ", CollectorString(), - static_cast<double>(start_size_) / MB, - SizeOfHeapObjects()); + static_cast<double>(start_object_size_) / MB, + static_cast<double>(start_memory_size_) / MB, + SizeOfHeapObjects(), + end_memory_size_mb); if (external_time > 0) PrintF("%d / ", external_time); - PrintF("%d ms.\n", time); + PrintF("%d ms", time); + if (steps_count_ > 0) { + if (collector_ == SCAVENGER) { + PrintF(" (+ %d ms in %d steps since last GC)", + static_cast<int>(steps_took_since_last_gc_), + steps_count_since_last_gc_); + } else { + PrintF(" (+ %d ms in %d steps since start of marking, " + "biggest step %f ms)", + static_cast<int>(steps_took_), + steps_count_, + longest_step_); + } + } + + if (gc_reason_ != NULL) { + PrintF(" [%s]", gc_reason_); + } + + if (collector_reason_ != NULL) { + PrintF(" [%s]", collector_reason_); + } + + PrintF(".\n"); } else { PrintF("pause=%d ", time); PrintF("mutator=%d ", @@ -6156,8 +6647,7 @@ GCTracer::~GCTracer() { PrintF("s"); break; case MARK_COMPACTOR: - PrintF("%s", - heap_->mark_compact_collector_.HasCompacted() ? "mc" : "ms"); + PrintF("ms"); break; default: UNREACHABLE(); @@ -6168,9 +6658,21 @@ GCTracer::~GCTracer() { PrintF("mark=%d ", static_cast<int>(scopes_[Scope::MC_MARK])); PrintF("sweep=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP])); PrintF("sweepns=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP_NEWSPACE])); - PrintF("compact=%d ", static_cast<int>(scopes_[Scope::MC_COMPACT])); - - PrintF("total_size_before=%" V8_PTR_PREFIX "d ", start_size_); + PrintF("evacuate=%d ", static_cast<int>(scopes_[Scope::MC_EVACUATE_PAGES])); + PrintF("new_new=%d ", + static_cast<int>(scopes_[Scope::MC_UPDATE_NEW_TO_NEW_POINTERS])); + PrintF("root_new=%d ", + static_cast<int>(scopes_[Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS])); + PrintF("old_new=%d ", + static_cast<int>(scopes_[Scope::MC_UPDATE_OLD_TO_NEW_POINTERS])); + PrintF("compaction_ptrs=%d ", + static_cast<int>(scopes_[Scope::MC_UPDATE_POINTERS_TO_EVACUATED])); + PrintF("intracompaction_ptrs=%d ", static_cast<int>(scopes_[ + Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED])); + PrintF("misc_compaction=%d ", + static_cast<int>(scopes_[Scope::MC_UPDATE_MISC_POINTERS])); + + PrintF("total_size_before=%" V8_PTR_PREFIX "d ", start_object_size_); PrintF("total_size_after=%" V8_PTR_PREFIX "d ", heap_->SizeOfObjects()); PrintF("holes_size_before=%" V8_PTR_PREFIX "d ", in_free_list_or_wasted_before_gc_); @@ -6179,6 +6681,14 @@ GCTracer::~GCTracer() { PrintF("allocated=%" V8_PTR_PREFIX "d ", allocated_since_last_gc_); PrintF("promoted=%" V8_PTR_PREFIX "d ", promoted_objects_size_); + if (collector_ == SCAVENGER) { + PrintF("stepscount=%d ", steps_count_since_last_gc_); + PrintF("stepstook=%d ", static_cast<int>(steps_took_since_last_gc_)); + } else { + PrintF("stepscount=%d ", steps_count_); + PrintF("stepstook=%d ", static_cast<int>(steps_took_)); + } + PrintF("\n"); } @@ -6191,8 +6701,7 @@ const char* GCTracer::CollectorString() { case SCAVENGER: return "Scavenge"; case MARK_COMPACTOR: - return heap_->mark_compact_collector_.HasCompacted() ? "Mark-compact" - : "Mark-sweep"; + return "Mark-sweep"; } return "Unknown GC"; } @@ -6207,10 +6716,12 @@ int KeyedLookupCache::Hash(Map* map, String* name) { int KeyedLookupCache::Lookup(Map* map, String* name) { - int index = Hash(map, name); - Key& key = keys_[index]; - if ((key.map == map) && key.name->Equals(name)) { - return field_offsets_[index]; + int index = (Hash(map, name) & kHashMask); + for (int i = 0; i < kEntriesPerBucket; i++) { + Key& key = keys_[index + i]; + if ((key.map == map) && key.name->Equals(name)) { + return field_offsets_[index + i]; + } } return kNotFound; } @@ -6219,7 +6730,29 @@ int KeyedLookupCache::Lookup(Map* map, String* name) { void KeyedLookupCache::Update(Map* map, String* name, int field_offset) { String* symbol; if (HEAP->LookupSymbolIfExists(name, &symbol)) { - int index = Hash(map, symbol); + int index = (Hash(map, symbol) & kHashMask); + // After a GC there will be free slots, so we use them in order (this may + // help to get the most frequently used one in position 0). + for (int i = 0; i< kEntriesPerBucket; i++) { + Key& key = keys_[index]; + Object* free_entry_indicator = NULL; + if (key.map == free_entry_indicator) { + key.map = map; + key.name = symbol; + field_offsets_[index + i] = field_offset; + return; + } + } + // No free entry found in this bucket, so we move them all down one and + // put the new entry at position zero. + for (int i = kEntriesPerBucket - 1; i > 0; i--) { + Key& key = keys_[index + i]; + Key& key2 = keys_[index + i - 1]; + key = key2; + field_offsets_[index + i] = field_offsets_[index + i - 1]; + } + + // Write the new first entry. Key& key = keys_[index]; key.map = map; key.name = symbol; @@ -6274,7 +6807,9 @@ void TranscendentalCache::Clear() { void ExternalStringTable::CleanUp() { int last = 0; for (int i = 0; i < new_space_strings_.length(); ++i) { - if (new_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; + if (new_space_strings_[i] == heap_->raw_unchecked_the_hole_value()) { + continue; + } if (heap_->InNewSpace(new_space_strings_[i])) { new_space_strings_[last++] = new_space_strings_[i]; } else { @@ -6284,12 +6819,16 @@ void ExternalStringTable::CleanUp() { new_space_strings_.Rewind(last); last = 0; for (int i = 0; i < old_space_strings_.length(); ++i) { - if (old_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; + if (old_space_strings_[i] == heap_->raw_unchecked_the_hole_value()) { + continue; + } ASSERT(!heap_->InNewSpace(old_space_strings_[i])); old_space_strings_[last++] = old_space_strings_[i]; } old_space_strings_.Rewind(last); - Verify(); + if (FLAG_verify_heap) { + Verify(); + } } @@ -6299,4 +6838,53 @@ void ExternalStringTable::TearDown() { } +void Heap::QueueMemoryChunkForFree(MemoryChunk* chunk) { + chunk->set_next_chunk(chunks_queued_for_free_); + chunks_queued_for_free_ = chunk; +} + + +void Heap::FreeQueuedChunks() { + if (chunks_queued_for_free_ == NULL) return; + MemoryChunk* next; + MemoryChunk* chunk; + for (chunk = chunks_queued_for_free_; chunk != NULL; chunk = next) { + next = chunk->next_chunk(); + chunk->SetFlag(MemoryChunk::ABOUT_TO_BE_FREED); + + if (chunk->owner()->identity() == LO_SPACE) { + // StoreBuffer::Filter relies on MemoryChunk::FromAnyPointerAddress. + // If FromAnyPointerAddress encounters a slot that belongs to a large + // chunk queued for deletion it will fail to find the chunk because + // it try to perform a search in the list of pages owned by of the large + // object space and queued chunks were detached from that list. + // To work around this we split large chunk into normal kPageSize aligned + // pieces and initialize size, owner and flags field of every piece. + // If FromAnyPointerAddress encounters a slot that belongs to one of + // these smaller pieces it will treat it as a slot on a normal Page. + MemoryChunk* inner = MemoryChunk::FromAddress( + chunk->address() + Page::kPageSize); + MemoryChunk* inner_last = MemoryChunk::FromAddress( + chunk->address() + chunk->size() - 1); + while (inner <= inner_last) { + // Size of a large chunk is always a multiple of + // OS::AllocateAlignment() so there is always + // enough space for a fake MemoryChunk header. + inner->set_size(Page::kPageSize); + inner->set_owner(lo_space()); + inner->SetFlag(MemoryChunk::ABOUT_TO_BE_FREED); + inner = MemoryChunk::FromAddress( + inner->address() + Page::kPageSize); + } + } + } + isolate_->heap()->store_buffer()->Compact(); + isolate_->heap()->store_buffer()->Filter(MemoryChunk::ABOUT_TO_BE_FREED); + for (chunk = chunks_queued_for_free_; chunk != NULL; chunk = next) { + next = chunk->next_chunk(); + isolate_->memory_allocator()->Free(chunk); + } + chunks_queued_for_free_ = NULL; +} + } } // namespace v8::internal diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index b1948a933c..83e9b61e84 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -32,46 +32,57 @@ #include "allocation.h" #include "globals.h" +#include "incremental-marking.h" #include "list.h" #include "mark-compact.h" +#include "objects-visiting.h" #include "spaces.h" #include "splay-tree-inl.h" +#include "store-buffer.h" #include "v8-counters.h" +#include "v8globals.h" namespace v8 { namespace internal { -// TODO(isolates): remove HEAP here -#define HEAP (_inline_get_heap_()) -class Heap; -inline Heap* _inline_get_heap_(); - - // Defines all the roots in Heap. -#define STRONG_ROOT_LIST(V) \ - /* Put the byte array map early. We need it to be in place by the time */ \ - /* the deserializer hits the next page, since it wants to put a byte */ \ - /* array in the unused space at the end of the page. */ \ +#define STRONG_ROOT_LIST(V) \ V(Map, byte_array_map, ByteArrayMap) \ + V(Map, free_space_map, FreeSpaceMap) \ V(Map, one_pointer_filler_map, OnePointerFillerMap) \ V(Map, two_pointer_filler_map, TwoPointerFillerMap) \ /* Cluster the most popular ones in a few cache lines here at the top. */ \ - V(Object, undefined_value, UndefinedValue) \ - V(Object, the_hole_value, TheHoleValue) \ - V(Object, null_value, NullValue) \ - V(Object, true_value, TrueValue) \ - V(Object, false_value, FalseValue) \ - V(Object, arguments_marker, ArgumentsMarker) \ + V(Smi, store_buffer_top, StoreBufferTop) \ + V(Oddball, undefined_value, UndefinedValue) \ + V(Oddball, the_hole_value, TheHoleValue) \ + V(Oddball, null_value, NullValue) \ + V(Oddball, true_value, TrueValue) \ + V(Oddball, false_value, FalseValue) \ + V(Map, global_property_cell_map, GlobalPropertyCellMap) \ + V(Map, shared_function_info_map, SharedFunctionInfoMap) \ + V(Map, meta_map, MetaMap) \ + V(Map, ascii_symbol_map, AsciiSymbolMap) \ + V(Map, ascii_string_map, AsciiStringMap) \ V(Map, heap_number_map, HeapNumberMap) \ V(Map, global_context_map, GlobalContextMap) \ V(Map, fixed_array_map, FixedArrayMap) \ - V(Map, serialized_scope_info_map, SerializedScopeInfoMap) \ + V(Map, code_map, CodeMap) \ + V(Map, scope_info_map, ScopeInfoMap) \ V(Map, fixed_cow_array_map, FixedCOWArrayMap) \ V(Map, fixed_double_array_map, FixedDoubleArrayMap) \ V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \ - V(Map, meta_map, MetaMap) \ V(Map, hash_table_map, HashTableMap) \ + V(FixedArray, empty_fixed_array, EmptyFixedArray) \ + V(ByteArray, empty_byte_array, EmptyByteArray) \ + V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \ + V(String, empty_string, EmptyString) \ + V(DescriptorArray, empty_descriptor_array, EmptyDescriptorArray) \ V(Smi, stack_limit, StackLimit) \ + V(Oddball, frame_alignment_marker, FrameAlignmentMarker) \ + V(Oddball, arguments_marker, ArgumentsMarker) \ + /* The first 32 roots above this line should be boring from a GC point of */ \ + /* view. This means they are never in new space and never on a page that */ \ + /* is being compacted. */ \ V(FixedArray, number_string_cache, NumberStringCache) \ V(Object, instanceof_cache_function, InstanceofCacheFunction) \ V(Object, instanceof_cache_map, InstanceofCacheMap) \ @@ -80,19 +91,12 @@ inline Heap* _inline_get_heap_(); V(FixedArray, string_split_cache, StringSplitCache) \ V(Object, termination_exception, TerminationException) \ V(Smi, hash_seed, HashSeed) \ - V(FixedArray, empty_fixed_array, EmptyFixedArray) \ - V(ByteArray, empty_byte_array, EmptyByteArray) \ - V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \ - V(String, empty_string, EmptyString) \ - V(DescriptorArray, empty_descriptor_array, EmptyDescriptorArray) \ V(Map, string_map, StringMap) \ - V(Map, ascii_string_map, AsciiStringMap) \ V(Map, symbol_map, SymbolMap) \ V(Map, cons_string_map, ConsStringMap) \ V(Map, cons_ascii_string_map, ConsAsciiStringMap) \ V(Map, sliced_string_map, SlicedStringMap) \ V(Map, sliced_ascii_string_map, SlicedAsciiStringMap) \ - V(Map, ascii_symbol_map, AsciiSymbolMap) \ V(Map, cons_symbol_map, ConsSymbolMap) \ V(Map, cons_ascii_symbol_map, ConsAsciiSymbolMap) \ V(Map, external_symbol_map, ExternalSymbolMap) \ @@ -101,6 +105,16 @@ inline Heap* _inline_get_heap_(); V(Map, external_string_map, ExternalStringMap) \ V(Map, external_string_with_ascii_data_map, ExternalStringWithAsciiDataMap) \ V(Map, external_ascii_string_map, ExternalAsciiStringMap) \ + V(Map, short_external_symbol_map, ShortExternalSymbolMap) \ + V(Map, \ + short_external_symbol_with_ascii_data_map, \ + ShortExternalSymbolWithAsciiDataMap) \ + V(Map, short_external_ascii_symbol_map, ShortExternalAsciiSymbolMap) \ + V(Map, short_external_string_map, ShortExternalStringMap) \ + V(Map, \ + short_external_string_with_ascii_data_map, \ + ShortExternalStringWithAsciiDataMap) \ + V(Map, short_external_ascii_string_map, ShortExternalAsciiStringMap) \ V(Map, undetectable_string_map, UndetectableStringMap) \ V(Map, undetectable_ascii_string_map, UndetectableAsciiStringMap) \ V(Map, external_pixel_array_map, ExternalPixelArrayMap) \ @@ -117,14 +131,12 @@ inline Heap* _inline_get_heap_(); V(Map, catch_context_map, CatchContextMap) \ V(Map, with_context_map, WithContextMap) \ V(Map, block_context_map, BlockContextMap) \ - V(Map, code_map, CodeMap) \ V(Map, oddball_map, OddballMap) \ - V(Map, global_property_cell_map, GlobalPropertyCellMap) \ - V(Map, shared_function_info_map, SharedFunctionInfoMap) \ V(Map, message_object_map, JSMessageObjectMap) \ V(Map, foreign_map, ForeignMap) \ - V(Object, nan_value, NanValue) \ - V(Object, minus_zero_value, MinusZeroValue) \ + V(HeapNumber, nan_value, NanValue) \ + V(HeapNumber, infinity_value, InfinityValue) \ + V(HeapNumber, minus_zero_value, MinusZeroValue) \ V(Map, neander_map, NeanderMap) \ V(JSObject, message_listeners, MessageListeners) \ V(Foreign, prototype_accessors, PrototypeAccessors) \ @@ -138,6 +150,7 @@ inline Heap* _inline_get_heap_(); V(Script, empty_script, EmptyScript) \ V(Smi, real_stack_limit, RealStackLimit) \ V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames) \ + V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) #define ROOT_LIST(V) \ STRONG_ROOT_LIST(V) \ @@ -227,7 +240,11 @@ inline Heap* _inline_get_heap_(); V(closure_symbol, "(closure)") \ V(use_strict, "use strict") \ V(dot_symbol, ".") \ - V(anonymous_function_symbol, "(anonymous function)") + V(anonymous_function_symbol, "(anonymous function)") \ + V(compare_ic_symbol, ".compare_ic") \ + V(infinity_symbol, "Infinity") \ + V(minus_infinity_symbol, "-Infinity") \ + V(hidden_stack_trace_symbol, "v8::hidden_stack_trace") // Forward declarations. class GCTracer; @@ -239,10 +256,26 @@ class WeakObjectRetainer; typedef String* (*ExternalStringTableUpdaterCallback)(Heap* heap, Object** pointer); -typedef bool (*DirtyRegionCallback)(Heap* heap, - Address start, - Address end, - ObjectSlotCallback copy_object_func); +class StoreBufferRebuilder { + public: + explicit StoreBufferRebuilder(StoreBuffer* store_buffer) + : store_buffer_(store_buffer) { + } + + void Callback(MemoryChunk* page, StoreBufferEvent event); + + private: + StoreBuffer* store_buffer_; + + // We record in this variable how full the store buffer was when we started + // iterating over the current page, finding pointers to new space. If the + // store buffer overflows again we can exempt the page from the store buffer + // by rewinding to this point instead of having to search the store buffer. + Object*** start_of_current_page_; + // The current page we are scanning in the store buffer iterator. + MemoryChunk* current_page_; +}; + // The all static Heap captures the interface to the global object heap. @@ -257,32 +290,103 @@ class HeapDebugUtils; // by it's size to avoid dereferencing a map pointer for scanning. class PromotionQueue { public: - PromotionQueue() : front_(NULL), rear_(NULL) { } + explicit PromotionQueue(Heap* heap) + : front_(NULL), + rear_(NULL), + limit_(NULL), + emergency_stack_(0), + heap_(heap) { } + + void Initialize(); + + void Destroy() { + ASSERT(is_empty()); + delete emergency_stack_; + emergency_stack_ = NULL; + } + + inline void ActivateGuardIfOnTheSamePage(); + + Page* GetHeadPage() { + return Page::FromAllocationTop(reinterpret_cast<Address>(rear_)); + } + + void SetNewLimit(Address limit) { + if (!guard_) { + return; + } - void Initialize(Address start_address) { - front_ = rear_ = reinterpret_cast<intptr_t*>(start_address); + ASSERT(GetHeadPage() == Page::FromAllocationTop(limit)); + limit_ = reinterpret_cast<intptr_t*>(limit); + + if (limit_ <= rear_) { + return; + } + + RelocateQueueHead(); } - bool is_empty() { return front_ <= rear_; } + bool is_empty() { + return (front_ == rear_) && + (emergency_stack_ == NULL || emergency_stack_->length() == 0); + } inline void insert(HeapObject* target, int size); void remove(HeapObject** target, int* size) { + ASSERT(!is_empty()); + if (front_ == rear_) { + Entry e = emergency_stack_->RemoveLast(); + *target = e.obj_; + *size = e.size_; + return; + } + + if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(front_))) { + NewSpacePage* front_page = + NewSpacePage::FromAddress(reinterpret_cast<Address>(front_)); + ASSERT(!front_page->prev_page()->is_anchor()); + front_ = + reinterpret_cast<intptr_t*>(front_page->prev_page()->body_limit()); + } *target = reinterpret_cast<HeapObject*>(*(--front_)); *size = static_cast<int>(*(--front_)); // Assert no underflow. - ASSERT(front_ >= rear_); + SemiSpace::AssertValidRange(reinterpret_cast<Address>(rear_), + reinterpret_cast<Address>(front_)); } private: - // The front of the queue is higher in memory than the rear. + // The front of the queue is higher in the memory page chain than the rear. intptr_t* front_; intptr_t* rear_; + intptr_t* limit_; + + bool guard_; + + static const int kEntrySizeInWords = 2; + + struct Entry { + Entry(HeapObject* obj, int size) : obj_(obj), size_(size) { } + + HeapObject* obj_; + int size_; + }; + List<Entry>* emergency_stack_; + + Heap* heap_; + + void RelocateQueueHead(); DISALLOW_COPY_AND_ASSIGN(PromotionQueue); }; +typedef void (*ScavengingCallback)(Map* map, + HeapObject** slot, + HeapObject* object); + + // External strings table is a place where all external strings are // registered. We need to keep track of such strings to properly // finalize them. @@ -323,19 +427,24 @@ class ExternalStringTable { }; +enum ArrayStorageAllocationMode { + DONT_INITIALIZE_ARRAY_ELEMENTS, + INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE +}; + class Heap { public: // Configure heap size before setup. Return false if the heap has been - // setup already. + // set up already. bool ConfigureHeap(int max_semispace_size, - int max_old_gen_size, - int max_executable_size); + intptr_t max_old_gen_size, + intptr_t max_executable_size); bool ConfigureHeapDefault(); // Initializes the global object heap. If create_heap_objects is true, // also creates the basic non-mutable objects. // Returns whether it succeeded. - bool Setup(bool create_heap_objects); + bool SetUp(bool create_heap_objects); // Destroys all memory allocated by the heap. void TearDown(); @@ -345,8 +454,8 @@ class Heap { // jslimit_/real_jslimit_ variable in the StackGuard. void SetStackLimits(); - // Returns whether Setup has been called. - bool HasBeenSetup(); + // Returns whether SetUp has been called. + bool HasBeenSetUp(); // Returns the maximum amount of memory reserved for the heap. For // the young generation, we reserve 4 times the amount needed for a @@ -424,6 +533,30 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateJSObject( JSFunction* constructor, PretenureFlag pretenure = NOT_TENURED); + // Allocate a JSArray with no elements + MUST_USE_RESULT MaybeObject* AllocateEmptyJSArray( + ElementsKind elements_kind, + PretenureFlag pretenure = NOT_TENURED) { + return AllocateJSArrayAndStorage(elements_kind, 0, 0, + DONT_INITIALIZE_ARRAY_ELEMENTS, + pretenure); + } + + // Allocate a JSArray with a specified length but elements that are left + // uninitialized. + MUST_USE_RESULT MaybeObject* AllocateJSArrayAndStorage( + ElementsKind elements_kind, + int length, + int capacity, + ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS, + PretenureFlag pretenure = NOT_TENURED); + + // Allocate a JSArray with no elements + MUST_USE_RESULT MaybeObject* AllocateJSArrayWithElements( + FixedArrayBase* array_base, + ElementsKind elements_kind, + PretenureFlag pretenure = NOT_TENURED); + // Allocates and initializes a new global object based on a constructor. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. @@ -457,6 +590,7 @@ class Heap { // size, but keeping the original prototype. The receiver must have at least // the size of the new object. The object is reinitialized and behaves as an // object that has been freshly allocated. + // Returns failure if an error occured, otherwise object. MUST_USE_RESULT MaybeObject* ReinitializeJSReceiver(JSReceiver* object, InstanceType type, int size); @@ -485,8 +619,10 @@ class Heap { // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. // Please note this function does not perform a garbage collection. - MUST_USE_RESULT MaybeObject* AllocateMap(InstanceType instance_type, - int instance_size); + MUST_USE_RESULT MaybeObject* AllocateMap( + InstanceType instance_type, + int instance_size, + ElementsKind elements_kind = FAST_ELEMENTS); // Allocates a partial map for bootstrapping. MUST_USE_RESULT MaybeObject* AllocatePartialMap(InstanceType instance_type, @@ -499,11 +635,14 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateCodeCache(); // Allocates a serialized scope info. - MUST_USE_RESULT MaybeObject* AllocateSerializedScopeInfo(int length); + MUST_USE_RESULT MaybeObject* AllocateScopeInfo(int length); // Allocates an empty PolymorphicCodeCache. MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache(); + // Allocates a pre-tenured empty AccessorPair. + MUST_USE_RESULT MaybeObject* AllocateAccessorPair(); + // Clear the Instanceof cache (used when a prototype changes). inline void ClearInstanceofCache(); @@ -576,7 +715,7 @@ class Heap { PretenureFlag pretenure = NOT_TENURED); // Computes a single character string where the character has code. - // A cache is used for ascii codes. + // A cache is used for ASCII codes. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. Please note this does not perform a garbage collection. MUST_USE_RESULT MaybeObject* LookupSingleCharacterStringFromCode( @@ -664,6 +803,13 @@ class Heap { int length, PretenureFlag pretenure = NOT_TENURED); + // Allocates a fixed double array with hole values. Returns + // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed. + // Please note this does not perform a garbage collection. + MUST_USE_RESULT MaybeObject* AllocateFixedDoubleArrayWithHoles( + int length, + PretenureFlag pretenure = NOT_TENURED); + // AllocateHashTable is identical to AllocateFixedArray except // that the resulting object has hash_table_map as map. MUST_USE_RESULT MaybeObject* AllocateHashTable( @@ -689,7 +835,7 @@ class Heap { // Allocate a block context. MUST_USE_RESULT MaybeObject* AllocateBlockContext(JSFunction* function, Context* previous, - SerializedScopeInfo* info); + ScopeInfo* info); // Allocates a new utility object in the old generation. MUST_USE_RESULT MaybeObject* AllocateStruct(InstanceType type); @@ -738,13 +884,15 @@ class Heap { // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. // Please note this does not perform a garbage collection. - MUST_USE_RESULT inline MaybeObject* NumberFromInt32(int32_t value); + MUST_USE_RESULT inline MaybeObject* NumberFromInt32( + int32_t value, PretenureFlag pretenure = NOT_TENURED); // Converts an int into either a Smi or a HeapNumber object. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. // Please note this does not perform a garbage collection. - MUST_USE_RESULT inline MaybeObject* NumberFromUint32(uint32_t value); + MUST_USE_RESULT inline MaybeObject* NumberFromUint32( + uint32_t value, PretenureFlag pretenure = NOT_TENURED); // Allocates a new foreign object. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation @@ -797,9 +945,9 @@ class Heap { // failed. // Please note this does not perform a garbage collection. MUST_USE_RESULT MaybeObject* AllocateExternalStringFromAscii( - ExternalAsciiString::Resource* resource); + const ExternalAsciiString::Resource* resource); MUST_USE_RESULT MaybeObject* AllocateExternalStringFromTwoByte( - ExternalTwoByteString::Resource* resource); + const ExternalTwoByteString::Resource* resource); // Finalizes an external string by deleting the associated external // data and clearing the resource pointer. @@ -878,19 +1026,35 @@ class Heap { // Performs garbage collection operation. // Returns whether there is a chance that another major GC could // collect more garbage. - bool CollectGarbage(AllocationSpace space, GarbageCollector collector); + bool CollectGarbage(AllocationSpace space, + GarbageCollector collector, + const char* gc_reason, + const char* collector_reason); // Performs garbage collection operation. // Returns whether there is a chance that another major GC could // collect more garbage. - inline bool CollectGarbage(AllocationSpace space); + inline bool CollectGarbage(AllocationSpace space, + const char* gc_reason = NULL); + + static const int kNoGCFlags = 0; + static const int kMakeHeapIterableMask = 1; + static const int kReduceMemoryFootprintMask = 2; - // Performs a full garbage collection. Force compaction if the - // parameter is true. - void CollectAllGarbage(bool force_compaction); + // Performs a full garbage collection. If (flags & kMakeHeapIterableMask) is + // non-zero, then the slower precise sweeper is used, which leaves the heap + // in a state where we can iterate over the heap visiting all objects. + void CollectAllGarbage(int flags, const char* gc_reason = NULL); // Last hope GC, should try to squeeze as much as possible. - void CollectAllAvailableGarbage(); + void CollectAllAvailableGarbage(const char* gc_reason = NULL); + + // Check whether the heap is currently iterable. + bool IsHeapIterable(); + + // Ensure that we have swept all spaces in such a way that we can iterate + // over all objects. May cause a GC. + void EnsureHeapIsIterable(); // Notify the heap that a context has been disposed. int NotifyContextDisposed() { return ++contexts_disposed_; } @@ -899,6 +1063,20 @@ class Heap { // ensure correct callback for weak global handles. void PerformScavenge(); + inline void increment_scan_on_scavenge_pages() { + scan_on_scavenge_pages_++; + if (FLAG_gc_verbose) { + PrintF("Scan-on-scavenge pages: %d\n", scan_on_scavenge_pages_); + } + } + + inline void decrement_scan_on_scavenge_pages() { + scan_on_scavenge_pages_--; + if (FLAG_gc_verbose) { + PrintF("Scan-on-scavenge pages: %d\n", scan_on_scavenge_pages_); + } + } + PromotionQueue* promotion_queue() { return &promotion_queue_; } #ifdef DEBUG @@ -925,6 +1103,8 @@ class Heap { // Heap root getters. We have versions with and without type::cast() here. // You can't use type::cast during GC because the assert fails. + // TODO(1490): Try removing the unchecked accessors, now that GC marking does + // not corrupt the map. #define ROOT_ACCESSOR(type, name, camel_name) \ type* name() { \ return type::cast(roots_[k##camel_name##RootIndex]); \ @@ -958,6 +1138,9 @@ class Heap { } Object* global_contexts_list() { return global_contexts_list_; } + // Number of mark-sweeps. + int ms_count() { return ms_count_; } + // Iterates over all roots in the heap. void IterateRoots(ObjectVisitor* v, VisitMode mode); // Iterates over all strong roots in the heap. @@ -965,60 +1148,16 @@ class Heap { // Iterates over all the other roots in the heap. void IterateWeakRoots(ObjectVisitor* v, VisitMode mode); - enum ExpectedPageWatermarkState { - WATERMARK_SHOULD_BE_VALID, - WATERMARK_CAN_BE_INVALID - }; - - // For each dirty region on a page in use from an old space call - // visit_dirty_region callback. - // If either visit_dirty_region or callback can cause an allocation - // in old space and changes in allocation watermark then - // can_preallocate_during_iteration should be set to true. - // All pages will be marked as having invalid watermark upon - // iteration completion. - void IterateDirtyRegions( - PagedSpace* space, - DirtyRegionCallback visit_dirty_region, - ObjectSlotCallback callback, - ExpectedPageWatermarkState expected_page_watermark_state); - - // Interpret marks as a bitvector of dirty marks for regions of size - // Page::kRegionSize aligned by Page::kRegionAlignmentMask and covering - // memory interval from start to top. For each dirty region call a - // visit_dirty_region callback. Return updated bitvector of dirty marks. - uint32_t IterateDirtyRegions(uint32_t marks, - Address start, - Address end, - DirtyRegionCallback visit_dirty_region, - ObjectSlotCallback callback); - // Iterate pointers to from semispace of new space found in memory interval // from start to end. - // Update dirty marks for page containing start address. void IterateAndMarkPointersToFromSpace(Address start, Address end, ObjectSlotCallback callback); - // Iterate pointers to new space found in memory interval from start to end. - // Return true if pointers to new space was found. - static bool IteratePointersInDirtyRegion(Heap* heap, - Address start, - Address end, - ObjectSlotCallback callback); - - - // Iterate pointers to new space found in memory interval from start to end. - // This interval is considered to belong to the map space. - // Return true if pointers to new space was found. - static bool IteratePointersInDirtyMapsRegion(Heap* heap, - Address start, - Address end, - ObjectSlotCallback callback); - - // Returns whether the object resides in new space. inline bool InNewSpace(Object* object); + inline bool InNewSpace(Address addr); + inline bool InNewSpacePage(Address addr); inline bool InFromSpace(Object* object); inline bool InToSpace(Object* object); @@ -1057,11 +1196,19 @@ class Heap { roots_[kEmptyScriptRootIndex] = script; } + void public_set_store_buffer_top(Address* top) { + roots_[kStoreBufferTopRootIndex] = reinterpret_cast<Smi*>(top); + } + // Update the next script id. inline void SetLastScriptId(Object* last_script_id); // Generated code can embed this address to get access to the roots. - Object** roots_address() { return roots_; } + Object** roots_array_start() { return roots_; } + + Address* store_buffer_top_address() { + return reinterpret_cast<Address*>(&roots_[kStoreBufferTopRootIndex]); + } // Get address of global contexts list for serialization support. Object** global_contexts_list_address() { @@ -1075,6 +1222,10 @@ class Heap { // Verify the heap is in its normal state before or after a GC. void Verify(); + void OldPointerSpaceCheckStoreBuffer(); + void MapSpaceCheckStoreBuffer(); + void LargeObjectSpaceCheckStoreBuffer(); + // Report heap statistics. void ReportHeapStatistics(const char* title); void ReportCodeStatistics(const char* title); @@ -1170,26 +1321,59 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateRawFixedArray(int length, PretenureFlag pretenure); + inline intptr_t PromotedTotalSize() { + return PromotedSpaceSize() + PromotedExternalMemorySize(); + } + // True if we have reached the allocation limit in the old generation that // should force the next GC (caused normally) to be a full one. - bool OldGenerationPromotionLimitReached() { - return (PromotedSpaceSize() + PromotedExternalMemorySize()) - > old_gen_promotion_limit_; + inline bool OldGenerationPromotionLimitReached() { + return PromotedTotalSize() > old_gen_promotion_limit_; } - intptr_t OldGenerationSpaceAvailable() { - return old_gen_allocation_limit_ - - (PromotedSpaceSize() + PromotedExternalMemorySize()); + inline intptr_t OldGenerationSpaceAvailable() { + return old_gen_allocation_limit_ - PromotedTotalSize(); } - // True if we have reached the allocation limit in the old generation that - // should artificially cause a GC right now. - bool OldGenerationAllocationLimitReached() { - return OldGenerationSpaceAvailable() < 0; + static const intptr_t kMinimumPromotionLimit = 5 * Page::kPageSize; + static const intptr_t kMinimumAllocationLimit = + 8 * (Page::kPageSize > MB ? Page::kPageSize : MB); + + // When we sweep lazily we initially guess that there is no garbage on the + // heap and set the limits for the next GC accordingly. As we sweep we find + // out that some of the pages contained garbage and we have to adjust + // downwards the size of the heap. This means the limits that control the + // timing of the next GC also need to be adjusted downwards. + void LowerOldGenLimits(intptr_t adjustment) { + size_of_old_gen_at_last_old_space_gc_ -= adjustment; + old_gen_promotion_limit_ = + OldGenPromotionLimit(size_of_old_gen_at_last_old_space_gc_); + old_gen_allocation_limit_ = + OldGenAllocationLimit(size_of_old_gen_at_last_old_space_gc_); } - // Can be called when the embedding application is idle. - bool IdleNotification(); + intptr_t OldGenPromotionLimit(intptr_t old_gen_size) { + const int divisor = FLAG_stress_compaction ? 10 : 3; + intptr_t limit = + Max(old_gen_size + old_gen_size / divisor, kMinimumPromotionLimit); + limit += new_space_.Capacity(); + limit *= old_gen_limit_factor_; + intptr_t halfway_to_the_max = (old_gen_size + max_old_generation_size_) / 2; + return Min(limit, halfway_to_the_max); + } + + intptr_t OldGenAllocationLimit(intptr_t old_gen_size) { + const int divisor = FLAG_stress_compaction ? 8 : 2; + intptr_t limit = + Max(old_gen_size + old_gen_size / divisor, kMinimumAllocationLimit); + limit += new_space_.Capacity(); + limit *= old_gen_limit_factor_; + intptr_t halfway_to_the_max = (old_gen_size + max_old_generation_size_) / 2; + return Min(limit, halfway_to_the_max); + } + + // Implements the corresponding V8 API function. + bool IdleNotification(int hint); // Declare all the root indices. enum RootListIndex { @@ -1213,6 +1397,8 @@ class Heap { MUST_USE_RESULT MaybeObject* NumberToString( Object* number, bool check_number_string_cache = true); + MUST_USE_RESULT MaybeObject* Uint32ToString( + uint32_t value, bool check_number_string_cache = true); Map* MapForExternalArrayType(ExternalArrayType array_type); RootListIndex RootIndexForExternalArrayType( @@ -1224,31 +1410,48 @@ class Heap { // by pointer size. static inline void CopyBlock(Address dst, Address src, int byte_size); - inline void CopyBlockToOldSpaceAndUpdateRegionMarks(Address dst, - Address src, - int byte_size); - // Optimized version of memmove for blocks with pointer size aligned sizes and // pointer size aligned addresses. static inline void MoveBlock(Address dst, Address src, int byte_size); - inline void MoveBlockToOldSpaceAndUpdateRegionMarks(Address dst, - Address src, - int byte_size); - // Check new space expansion criteria and expand semispaces if it was hit. void CheckNewSpaceExpansionCriteria(); inline void IncrementYoungSurvivorsCounter(int survived) { + ASSERT(survived >= 0); young_survivors_after_last_gc_ = survived; survived_since_last_expansion_ += survived; } + inline bool NextGCIsLikelyToBeFull() { + if (FLAG_gc_global) return true; + + intptr_t total_promoted = PromotedTotalSize(); + + intptr_t adjusted_promotion_limit = + old_gen_promotion_limit_ - new_space_.Capacity(); + + if (total_promoted >= adjusted_promotion_limit) return true; + + intptr_t adjusted_allocation_limit = + old_gen_allocation_limit_ - new_space_.Capacity() / 5; + + if (PromotedSpaceSize() >= adjusted_allocation_limit) return true; + + return false; + } + + void UpdateNewSpaceReferencesInExternalStringTable( ExternalStringTableUpdaterCallback updater_func); + void UpdateReferencesInExternalStringTable( + ExternalStringTableUpdaterCallback updater_func); + void ProcessWeakReferences(WeakObjectRetainer* retainer); + void VisitExternalResources(v8::ExternalResourceVisitor* visitor); + // Helper function that governs the promotion policy from new space to // old. If the object's old address lies below the new space's age // mark or if we've already filled the bottom 1/16th of the to space, @@ -1263,6 +1466,10 @@ class Heap { GCTracer* tracer() { return tracer_; } + // Returns the size of objects residing in non new spaces. + intptr_t PromotedSpaceSize(); + intptr_t PromotedSpaceSizeOfObjects(); + double total_regexp_code_generated() { return total_regexp_code_generated_; } void IncreaseTotalRegexpCodeGenerated(int size) { total_regexp_code_generated_ += size; @@ -1281,6 +1488,29 @@ class Heap { return &mark_compact_collector_; } + StoreBuffer* store_buffer() { + return &store_buffer_; + } + + Marking* marking() { + return &marking_; + } + + IncrementalMarking* incremental_marking() { + return &incremental_marking_; + } + + bool IsSweepingComplete() { + return old_data_space()->IsSweepingComplete() && + old_pointer_space()->IsSweepingComplete(); + } + + bool AdvanceSweepers(int step_size) { + bool sweeping_complete = old_data_space()->AdvanceSweeper(step_size); + sweeping_complete &= old_pointer_space()->AdvanceSweeper(step_size); + return sweeping_complete; + } + ExternalStringTable* external_string_table() { return &external_string_table_; } @@ -1291,22 +1521,46 @@ class Heap { } inline Isolate* isolate(); - bool is_safe_to_read_maps() { return is_safe_to_read_maps_; } - void CallGlobalGCPrologueCallback() { + inline void CallGlobalGCPrologueCallback() { if (global_gc_prologue_callback_ != NULL) global_gc_prologue_callback_(); } - void CallGlobalGCEpilogueCallback() { + inline void CallGlobalGCEpilogueCallback() { if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_(); } + inline bool OldGenerationAllocationLimitReached(); + + inline void DoScavengeObject(Map* map, HeapObject** slot, HeapObject* obj) { + scavenging_visitors_table_.GetVisitor(map)(map, slot, obj); + } + + void QueueMemoryChunkForFree(MemoryChunk* chunk); + void FreeQueuedChunks(); + + // Completely clear the Instanceof cache (to stop it keeping objects alive + // around a GC). + inline void CompletelyClearInstanceofCache(); + + // The roots that have an index less than this are always in old space. + static const int kOldSpaceRoots = 0x20; + + bool idle_notification_will_schedule_next_gc() { + return idle_notification_will_schedule_next_gc_; + } + uint32_t HashSeed() { uint32_t seed = static_cast<uint32_t>(hash_seed()->value()); ASSERT(FLAG_randomize_hashes || seed == 0); return seed; } + void SetArgumentsAdaptorDeoptPCOffset(int pc_offset) { + ASSERT(arguments_adaptor_deopt_pc_offset() == Smi::FromInt(0)); + set_arguments_adaptor_deopt_pc_offset(Smi::FromInt(pc_offset)); + } + private: Heap(); @@ -1314,12 +1568,12 @@ class Heap { // more expedient to get at the isolate directly from within Heap methods. Isolate* isolate_; + intptr_t code_range_size_; int reserved_semispace_size_; int max_semispace_size_; int initial_semispace_size_; intptr_t max_old_generation_size_; intptr_t max_executable_size_; - intptr_t code_range_size_; // For keeping track of how much data has survived // scavenge since last new space expansion. @@ -1334,6 +1588,8 @@ class Heap { // For keeping track of context disposals. int contexts_disposed_; + int scan_on_scavenge_pages_; + #if defined(V8_TARGET_ARCH_X64) static const int kMaxObjectSizeInNewSpace = 1024*KB; #else @@ -1350,13 +1606,9 @@ class Heap { HeapState gc_state_; int gc_post_processing_depth_; - // Returns the size of object residing in non new spaces. - intptr_t PromotedSpaceSize(); - // Returns the amount of external memory registered since last global gc. int PromotedExternalMemorySize(); - int mc_count_; // how many mark-compact collections happened int ms_count_; // how many mark-sweep collections happened unsigned int gc_count_; // how many gc happened @@ -1364,7 +1616,10 @@ class Heap { int unflattened_strings_length_; #define ROOT_ACCESSOR(type, name, camel_name) \ - inline void set_##name(type* value) { \ + inline void set_##name(type* value) { \ + /* The deserializer makes use of the fact that these common roots are */ \ + /* never in new space and never on a page that is being compacted. */ \ + ASSERT(k##camel_name##RootIndex >= kOldSpaceRoots || !InNewSpace(value)); \ roots_[k##camel_name##RootIndex] = value; \ } ROOT_LIST(ROOT_ACCESSOR) @@ -1385,6 +1640,10 @@ class Heap { HeapDebugUtils* debug_utils_; #endif // DEBUG + // Indicates that the new space should be kept small due to high promotion + // rates caused by the mutator allocating a lot of long-lived objects. + bool new_space_high_promotion_mode_active_; + // Limit that triggers a global GC on the next (normally caused) GC. This // is checked when we have already decided to do a GC to help determine // which collector to invoke. @@ -1395,6 +1654,13 @@ class Heap { // every allocation in large object space. intptr_t old_gen_allocation_limit_; + // Sometimes the heuristics dictate that those limits are increased. This + // variable records that fact. + int old_gen_limit_factor_; + + // Used to adjust the limits that control the timing of the next GC. + intptr_t size_of_old_gen_at_last_old_space_gc_; + // Limit on the amount of externally allocated memory allowed // between global GCs. If reached a global GC is forced. intptr_t external_allocation_limit_; @@ -1414,6 +1680,8 @@ class Heap { Object* global_contexts_list_; + StoreBufferRebuilder store_buffer_rebuilder_; + struct StringTypeTable { InstanceType type; int size; @@ -1471,17 +1739,16 @@ class Heap { // Support for computing object sizes during GC. HeapObjectCallback gc_safe_size_of_old_object_; static int GcSafeSizeOfOldObject(HeapObject* object); - static int GcSafeSizeOfOldObjectWithEncodedMap(HeapObject* object); // Update the GC state. Called from the mark-compact collector. void MarkMapPointersAsEncoded(bool encoded) { - gc_safe_size_of_old_object_ = encoded - ? &GcSafeSizeOfOldObjectWithEncodedMap - : &GcSafeSizeOfOldObject; + ASSERT(!encoded); + gc_safe_size_of_old_object_ = &GcSafeSizeOfOldObject; } // Checks whether a global GC is necessary - GarbageCollector SelectGarbageCollector(AllocationSpace space); + GarbageCollector SelectGarbageCollector(AllocationSpace space, + const char** reason); // Performs garbage collection // Returns whether there is a chance another major GC could @@ -1489,11 +1756,10 @@ class Heap { bool PerformGarbageCollection(GarbageCollector collector, GCTracer* tracer); - static const intptr_t kMinimumPromotionLimit = 2 * MB; - static const intptr_t kMinimumAllocationLimit = 8 * MB; inline void UpdateOldSpaceLimits(); + // Allocate an uninitialized object in map space. The behavior is identical // to Heap::AllocateRaw(size_in_bytes, MAP_SPACE), except that (a) it doesn't // have to test the allocation space argument and (b) can reduce code size @@ -1522,14 +1788,17 @@ class Heap { Object* to_number, byte kind); + // Allocate a JSArray with no elements + MUST_USE_RESULT MaybeObject* AllocateJSArray( + ElementsKind elements_kind, + PretenureFlag pretenure = NOT_TENURED); + // Allocate empty fixed array. MUST_USE_RESULT MaybeObject* AllocateEmptyFixedArray(); // Allocate empty fixed double array. MUST_USE_RESULT MaybeObject* AllocateEmptyFixedDoubleArray(); - void SwitchScavengingVisitorsTableIfProfilingWasEnabled(); - // Performs a minor collection in new generation. void Scavenge(); @@ -1538,16 +1807,15 @@ class Heap { Object** pointer); Address DoScavenge(ObjectVisitor* scavenge_visitor, Address new_space_front); + static void ScavengeStoreBufferCallback(Heap* heap, + MemoryChunk* page, + StoreBufferEvent event); // Performs a major collection in the whole heap. void MarkCompact(GCTracer* tracer); // Code to be run before and after mark-compact. - void MarkCompactPrologue(bool is_compacting); - - // Completely clear the Instanceof cache (to stop it keeping objects alive - // around a GC). - inline void CompletelyClearInstanceofCache(); + void MarkCompactPrologue(); // Record statistics before and after garbage collection. void ReportStatisticsBeforeGC(); @@ -1557,12 +1825,11 @@ class Heap { static void ScavengeObjectSlow(HeapObject** p, HeapObject* object); // Initializes a function with a shared part and prototype. - // Returns the function. // Note: this code was factored out of AllocateFunction such that // other parts of the VM could use it. Specifically, a function that creates // instances of type JS_FUNCTION_TYPE benefit from the use of this function. // Please note this does not perform a garbage collection. - MUST_USE_RESULT inline MaybeObject* InitializeFunction( + inline void InitializeFunction( JSFunction* function, SharedFunctionInfo* shared, Object* prototype); @@ -1573,8 +1840,13 @@ class Heap { GCTracer* tracer_; - // Initializes the number to string cache based on the max semispace size. - MUST_USE_RESULT MaybeObject* InitializeNumberStringCache(); + // Allocates a small number to string cache. + MUST_USE_RESULT MaybeObject* AllocateInitialNumberStringCache(); + // Creates and installs the full-sized number string cache. + void AllocateFullSizeNumberStringCache(); + // Get the length of the number to string cache based on the max semispace + // size. + int FullSizeNumberStringCacheLength(); // Flush the number to string cache. void FlushNumberStringCache(); @@ -1582,11 +1854,13 @@ class Heap { enum SurvivalRateTrend { INCREASING, STABLE, DECREASING, FLUCTUATING }; - static const int kYoungSurvivalRateThreshold = 90; + static const int kYoungSurvivalRateHighThreshold = 90; + static const int kYoungSurvivalRateLowThreshold = 10; static const int kYoungSurvivalRateAllowedDeviation = 15; int young_survivors_after_last_gc_; int high_survival_rate_period_length_; + int low_survival_rate_period_length_; double survival_rate_; SurvivalRateTrend previous_survival_rate_trend_; SurvivalRateTrend survival_rate_trend_; @@ -1619,6 +1893,16 @@ class Heap { } } + bool IsStableOrDecreasingSurvivalTrend() { + switch (survival_rate_trend()) { + case STABLE: + case DECREASING: + return true; + default: + return false; + } + } + bool IsIncreasingSurvivalTrend() { return survival_rate_trend() == INCREASING; } @@ -1627,8 +1911,39 @@ class Heap { return high_survival_rate_period_length_ > 0; } + bool IsLowSurvivalRate() { + return low_survival_rate_period_length_ > 0; + } + + void SelectScavengingVisitorsTable(); + + void StartIdleRound() { + mark_sweeps_since_idle_round_started_ = 0; + ms_count_at_last_idle_notification_ = ms_count_; + } + + void FinishIdleRound() { + mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound; + scavenges_since_last_idle_round_ = 0; + } + + bool EnoughGarbageSinceLastIdleRound() { + return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold); + } + + bool WorthStartingGCWhenIdle() { + if (contexts_disposed_ > 0) { + return true; + } + return incremental_marking()->WorthActivating(); + } + + // Returns true if no more GC work is left. + bool IdleGlobalGC(); + static const int kInitialSymbolTableSize = 2048; static const int kInitialEvalCacheSize = 64; + static const int kInitialNumberStringCacheSize = 256; // Maximum GC pause. int max_gc_pause_; @@ -1646,25 +1961,37 @@ class Heap { MarkCompactCollector mark_compact_collector_; - // This field contains the meaning of the WATERMARK_INVALIDATED flag. - // Instead of clearing this flag from all pages we just flip - // its meaning at the beginning of a scavenge. - intptr_t page_watermark_invalidated_mark_; + StoreBuffer store_buffer_; + + Marking marking_; + + IncrementalMarking incremental_marking_; int number_idle_notifications_; unsigned int last_idle_notification_gc_count_; bool last_idle_notification_gc_count_init_; + bool idle_notification_will_schedule_next_gc_; + int mark_sweeps_since_idle_round_started_; + int ms_count_at_last_idle_notification_; + unsigned int gc_count_at_last_idle_gc_; + int scavenges_since_last_idle_round_; + + static const int kMaxMarkSweepsInIdleRound = 7; + static const int kIdleScavengeThreshold = 5; + // Shared state read by the scavenge collector and set by ScavengeObject. PromotionQueue promotion_queue_; // Flag is set when the heap has been configured. The heap can be repeatedly - // configured through the API until it is setup. + // configured through the API until it is set up. bool configured_; ExternalStringTable external_string_table_; - bool is_safe_to_read_maps_; + VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_; + + MemoryChunk* chunks_queued_for_free_; friend class Factory; friend class GCTracer; @@ -1716,32 +2043,15 @@ class HeapStats { class AlwaysAllocateScope { public: - AlwaysAllocateScope() { - // We shouldn't hit any nested scopes, because that requires - // non-handle code to call handle code. The code still works but - // performance will degrade, so we want to catch this situation - // in debug mode. - ASSERT(HEAP->always_allocate_scope_depth_ == 0); - HEAP->always_allocate_scope_depth_++; - } - - ~AlwaysAllocateScope() { - HEAP->always_allocate_scope_depth_--; - ASSERT(HEAP->always_allocate_scope_depth_ == 0); - } + inline AlwaysAllocateScope(); + inline ~AlwaysAllocateScope(); }; class LinearAllocationScope { public: - LinearAllocationScope() { - HEAP->linear_allocation_scope_depth_++; - } - - ~LinearAllocationScope() { - HEAP->linear_allocation_scope_depth_--; - ASSERT(HEAP->linear_allocation_scope_depth_ >= 0); - } + inline LinearAllocationScope(); + inline ~LinearAllocationScope(); }; @@ -1753,38 +2063,7 @@ class LinearAllocationScope { // objects in a heap space but above the allocation pointer. class VerifyPointersVisitor: public ObjectVisitor { public: - void VisitPointers(Object** start, Object** end) { - for (Object** current = start; current < end; current++) { - if ((*current)->IsHeapObject()) { - HeapObject* object = HeapObject::cast(*current); - ASSERT(HEAP->Contains(object)); - ASSERT(object->map()->IsMap()); - } - } - } -}; - - -// Visitor class to verify interior pointers in spaces that use region marks -// to keep track of intergenerational references. -// As VerifyPointersVisitor but also checks that dirty marks are set -// for regions covering intergenerational references. -class VerifyPointersAndDirtyRegionsVisitor: public ObjectVisitor { - public: - void VisitPointers(Object** start, Object** end) { - for (Object** current = start; current < end; current++) { - if ((*current)->IsHeapObject()) { - HeapObject* object = HeapObject::cast(*current); - ASSERT(HEAP->Contains(object)); - ASSERT(object->map()->IsMap()); - if (HEAP->InNewSpace(object)) { - ASSERT(HEAP->InToSpace(object)); - Address addr = reinterpret_cast<Address>(current); - ASSERT(Page::FromAddress(addr)->IsRegionDirty(addr)); - } - } - } - } + inline void VisitPointers(Object** start, Object** end); }; #endif @@ -1860,7 +2139,6 @@ class HeapIterator BASE_EMBEDDED { public: enum HeapObjectsFiltering { kNoFiltering, - kFilterFreeListNodes, kFilterUnreachable }; @@ -1900,11 +2178,17 @@ class KeyedLookupCache { // Clear the cache. void Clear(); - static const int kLength = 64; + static const int kLength = 256; static const int kCapacityMask = kLength - 1; - static const int kMapHashShift = 2; + static const int kMapHashShift = 5; + static const int kHashMask = -4; // Zero the last two bits. + static const int kEntriesPerBucket = 4; static const int kNotFound = -1; + // kEntriesPerBucket should be a power of 2. + STATIC_ASSERT((kEntriesPerBucket & (kEntriesPerBucket - 1)) == 0); + STATIC_ASSERT(kEntriesPerBucket == -kHashMask); + private: KeyedLookupCache() { for (int i = 0; i < kLength; ++i) { @@ -2005,72 +2289,47 @@ class DescriptorLookupCache { }; -// A helper class to document/test C++ scopes where we do not -// expect a GC. Usage: -// -// /* Allocation not allowed: we cannot handle a GC in this scope. */ -// { AssertNoAllocation nogc; -// ... -// } - #ifdef DEBUG - class DisallowAllocationFailure { public: - DisallowAllocationFailure() { - old_state_ = HEAP->disallow_allocation_failure_; - HEAP->disallow_allocation_failure_ = true; - } - ~DisallowAllocationFailure() { - HEAP->disallow_allocation_failure_ = old_state_; - } + inline DisallowAllocationFailure(); + inline ~DisallowAllocationFailure(); + private: bool old_state_; }; +#endif + +// A helper class to document/test C++ scopes where we do not +// expect a GC. Usage: +// +// /* Allocation not allowed: we cannot handle a GC in this scope. */ +// { AssertNoAllocation nogc; +// ... +// } class AssertNoAllocation { public: - AssertNoAllocation() { - old_state_ = HEAP->allow_allocation(false); - } - - ~AssertNoAllocation() { - HEAP->allow_allocation(old_state_); - } + inline AssertNoAllocation(); + inline ~AssertNoAllocation(); +#ifdef DEBUG private: bool old_state_; +#endif }; + class DisableAssertNoAllocation { public: - DisableAssertNoAllocation() { - old_state_ = HEAP->allow_allocation(true); - } - - ~DisableAssertNoAllocation() { - HEAP->allow_allocation(old_state_); - } + inline DisableAssertNoAllocation(); + inline ~DisableAssertNoAllocation(); +#ifdef DEBUG private: bool old_state_; -}; - -#else // ndef DEBUG - -class AssertNoAllocation { - public: - AssertNoAllocation() { } - ~AssertNoAllocation() { } -}; - -class DisableAssertNoAllocation { - public: - DisableAssertNoAllocation() { } - ~DisableAssertNoAllocation() { } -}; - #endif +}; // GCTracer collects and prints ONE line after each garbage collector // invocation IFF --trace_gc is used. @@ -2084,7 +2343,13 @@ class GCTracer BASE_EMBEDDED { MC_MARK, MC_SWEEP, MC_SWEEP_NEWSPACE, - MC_COMPACT, + MC_EVACUATE_PAGES, + MC_UPDATE_NEW_TO_NEW_POINTERS, + MC_UPDATE_ROOT_TO_NEW_POINTERS, + MC_UPDATE_OLD_TO_NEW_POINTERS, + MC_UPDATE_POINTERS_TO_EVACUATED, + MC_UPDATE_POINTERS_BETWEEN_EVACUATED, + MC_UPDATE_MISC_POINTERS, MC_FLUSH_CODE, kNumberOfScopes }; @@ -2106,7 +2371,9 @@ class GCTracer BASE_EMBEDDED { double start_time_; }; - explicit GCTracer(Heap* heap); + explicit GCTracer(Heap* heap, + const char* gc_reason, + const char* collector_reason); ~GCTracer(); // Sets the collector. @@ -2118,16 +2385,6 @@ class GCTracer BASE_EMBEDDED { // Sets the full GC count. void set_full_gc_count(int count) { full_gc_count_ = count; } - // Sets the flag that this is a compacting full GC. - void set_is_compacting() { is_compacting_ = true; } - bool is_compacting() const { return is_compacting_; } - - // Increment and decrement the count of marked objects. - void increment_marked_count() { ++marked_count_; } - void decrement_marked_count() { --marked_count_; } - - int marked_count() { return marked_count_; } - void increment_promoted_objects_size(int object_size) { promoted_objects_size_ += object_size; } @@ -2137,38 +2394,27 @@ class GCTracer BASE_EMBEDDED { const char* CollectorString(); // Returns size of object in heap (in MB). - double SizeOfHeapObjects() { - return (static_cast<double>(HEAP->SizeOfObjects())) / MB; - } + inline double SizeOfHeapObjects(); + + // Timestamp set in the constructor. + double start_time_; + + // Size of objects in heap set in constructor. + intptr_t start_object_size_; + + // Size of memory allocated from OS set in constructor. + intptr_t start_memory_size_; - double start_time_; // Timestamp set in the constructor. - intptr_t start_size_; // Size of objects in heap set in constructor. - GarbageCollector collector_; // Type of collector. + // Type of collector. + GarbageCollector collector_; - // A count (including this one, eg, the first collection is 1) of the + // A count (including this one, e.g. the first collection is 1) of the // number of garbage collections. unsigned int gc_count_; // A count (including this one) of the number of full garbage collections. int full_gc_count_; - // True if the current GC is a compacting full collection, false - // otherwise. - bool is_compacting_; - - // True if the *previous* full GC cwas a compacting collection (will be - // false if there has not been a previous full GC). - bool previous_has_compacted_; - - // On a full GC, a count of the number of marked objects. Incremented - // when an object is marked and decremented when an object's mark bit is - // cleared. Will be zero on a scavenge collection. - int marked_count_; - - // The count from the end of the previous full GC. Will be zero if there - // was no previous full GC. - int previous_marked_count_; - // Amounts of time spent in different scopes during GC. double scopes_[Scope::kNumberOfScopes]; @@ -2187,7 +2433,17 @@ class GCTracer BASE_EMBEDDED { // Size of objects promoted during the current collection. intptr_t promoted_objects_size_; + // Incremental marking steps counters. + int steps_count_; + double steps_took_; + double longest_step_; + int steps_count_since_last_gc_; + double steps_took_since_last_gc_; + Heap* heap_; + + const char* gc_reason_; + const char* collector_reason_; }; @@ -2298,6 +2554,46 @@ class WeakObjectRetainer { }; +// Intrusive object marking uses least significant bit of +// heap object's map word to mark objects. +// Normally all map words have least significant bit set +// because they contain tagged map pointer. +// If the bit is not set object is marked. +// All objects should be unmarked before resuming +// JavaScript execution. +class IntrusiveMarking { + public: + static bool IsMarked(HeapObject* object) { + return (object->map_word().ToRawValue() & kNotMarkedBit) == 0; + } + + static void ClearMark(HeapObject* object) { + uintptr_t map_word = object->map_word().ToRawValue(); + object->set_map_word(MapWord::FromRawValue(map_word | kNotMarkedBit)); + ASSERT(!IsMarked(object)); + } + + static void SetMark(HeapObject* object) { + uintptr_t map_word = object->map_word().ToRawValue(); + object->set_map_word(MapWord::FromRawValue(map_word & ~kNotMarkedBit)); + ASSERT(IsMarked(object)); + } + + static Map* MapOfMarkedObject(HeapObject* object) { + uintptr_t map_word = object->map_word().ToRawValue(); + return MapWord::FromRawValue(map_word | kNotMarkedBit).ToMap(); + } + + static int SizeOfMarkedObject(HeapObject* object) { + return object->SizeFromMap(MapOfMarkedObject(object)); + } + + private: + static const uintptr_t kNotMarkedBit = 0x1; + STATIC_ASSERT((kHeapObjectTag & kNotMarkedBit) != 0); +}; + + #if defined(DEBUG) || defined(LIVE_OBJECT_LIST) // Helper class for tracing paths to a search target Object from all roots. // The TracePathFrom() method can be used to trace paths from a specific @@ -2352,13 +2648,11 @@ class PathTracer : public ObjectVisitor { AssertNoAllocation no_alloc; // i.e. no gc allowed. + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PathTracer); }; #endif // DEBUG || LIVE_OBJECT_LIST - } } // namespace v8::internal -#undef HEAP - #endif // V8_HEAP_H_ diff --git a/deps/v8/src/hydrogen-instructions.cc b/deps/v8/src/hydrogen-instructions.cc index 5630ce3913..cdc3e233f2 100644 --- a/deps/v8/src/hydrogen-instructions.cc +++ b/deps/v8/src/hydrogen-instructions.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -67,6 +67,14 @@ const char* Representation::Mnemonic() const { } +int HValue::LoopWeight() const { + const int w = FLAG_loop_weight; + static const int weights[] = { 1, w, w*w, w*w*w, w*w*w*w }; + return weights[Min(block()->LoopNestingDepth(), + static_cast<int>(ARRAY_SIZE(weights)-1))]; +} + + void HValue::AssumeRepresentation(Representation r) { if (CheckFlag(kFlexibleRepresentation)) { ChangeRepresentation(r); @@ -126,7 +134,9 @@ void Range::AddConstant(int32_t value) { bool may_overflow = false; // Overflow is ignored here. lower_ = AddWithoutOverflow(lower_, value, &may_overflow); upper_ = AddWithoutOverflow(upper_, value, &may_overflow); +#ifdef DEBUG Verify(); +#endif } @@ -173,7 +183,9 @@ bool Range::AddAndCheckOverflow(Range* other) { lower_ = AddWithoutOverflow(lower_, other->lower(), &may_overflow); upper_ = AddWithoutOverflow(upper_, other->upper(), &may_overflow); KeepOrder(); +#ifdef DEBUG Verify(); +#endif return may_overflow; } @@ -183,7 +195,9 @@ bool Range::SubAndCheckOverflow(Range* other) { lower_ = SubWithoutOverflow(lower_, other->upper(), &may_overflow); upper_ = SubWithoutOverflow(upper_, other->lower(), &may_overflow); KeepOrder(); +#ifdef DEBUG Verify(); +#endif return may_overflow; } @@ -197,9 +211,11 @@ void Range::KeepOrder() { } +#ifdef DEBUG void Range::Verify() const { ASSERT(lower_ <= upper_); } +#endif bool Range::MulAndCheckOverflow(Range* other) { @@ -210,7 +226,9 @@ bool Range::MulAndCheckOverflow(Range* other) { int v4 = MulWithoutOverflow(upper_, other->upper(), &may_overflow); lower_ = Min(Min(v1, v2), Min(v3, v4)); upper_ = Max(Max(v1, v2), Max(v3, v4)); +#ifdef DEBUG Verify(); +#endif return may_overflow; } @@ -234,25 +252,6 @@ const char* HType::ToString() { } -const char* HType::ToShortString() { - switch (type_) { - case kTagged: return "t"; - case kTaggedPrimitive: return "p"; - case kTaggedNumber: return "n"; - case kSmi: return "m"; - case kHeapNumber: return "h"; - case kString: return "s"; - case kBoolean: return "b"; - case kNonPrimitive: return "r"; - case kJSArray: return "a"; - case kJSObject: return "o"; - case kUninitialized: return "z"; - } - UNREACHABLE(); - return "Unreachable code"; -} - - HType HType::TypeFromValue(Handle<Object> value) { HType result = HType::Tagged(); if (value->IsSmi()) { @@ -425,18 +424,18 @@ void HValue::PrintRangeTo(StringStream* stream) { void HValue::PrintChangesTo(StringStream* stream) { - int changes_flags = ChangesFlags(); - if (changes_flags == 0) return; + GVNFlagSet changes_flags = ChangesFlags(); + if (changes_flags.IsEmpty()) return; stream->Add(" changes["); - if (changes_flags == AllSideEffects()) { + if (changes_flags == AllSideEffectsFlagSet()) { stream->Add("*"); } else { bool add_comma = false; -#define PRINT_DO(type) \ - if (changes_flags & (1 << kChanges##type)) { \ - if (add_comma) stream->Add(","); \ - add_comma = true; \ - stream->Add(#type); \ +#define PRINT_DO(type) \ + if (changes_flags.Contains(kChanges##type)) { \ + if (add_comma) stream->Add(","); \ + add_comma = true; \ + stream->Add(#type); \ } GVN_FLAG_LIST(PRINT_DO); #undef PRINT_DO @@ -564,7 +563,7 @@ void HInstruction::InsertAfter(HInstruction* previous) { // followed by a simulate instruction, we need to insert after the // simulate instruction instead. HInstruction* next = previous->next_; - if (previous->HasSideEffects() && next != NULL) { + if (previous->HasObservableSideEffects() && next != NULL) { ASSERT(next->IsSimulate()); previous = next; next = previous->next_; @@ -587,11 +586,10 @@ void HInstruction::Verify() { HBasicBlock* other_block = other_operand->block(); if (cur_block == other_block) { if (!other_operand->IsPhi()) { - HInstruction* cur = cur_block->first(); + HInstruction* cur = this->previous(); while (cur != NULL) { - ASSERT(cur != this); // We should reach other_operand before! if (cur == other_operand) break; - cur = cur->next(); + cur = cur->previous(); } // Must reach other operand in the same block! ASSERT(cur == other_operand); @@ -605,7 +603,7 @@ void HInstruction::Verify() { // Verify that instructions that may have side-effects are followed // by a simulate instruction. - if (HasSideEffects() && !IsOsrEntry()) { + if (HasObservableSideEffects() && !IsOsrEntry()) { ASSERT(next()->IsSimulate()); } @@ -707,6 +705,14 @@ void HUnaryControlInstruction::PrintDataTo(StringStream* stream) { } +void HIsNilAndBranch::PrintDataTo(StringStream* stream) { + value()->PrintNameTo(stream); + stream->Add(kind() == kStrictEquality ? " === " : " == "); + stream->Add(nil() == kNullValue ? "null" : "undefined"); + HControlInstruction::PrintDataTo(stream); +} + + void HReturn::PrintDataTo(StringStream* stream) { value()->PrintNameTo(stream); } @@ -775,17 +781,56 @@ void HHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { void HTypeofIsAndBranch::PrintDataTo(StringStream* stream) { value()->PrintNameTo(stream); - stream->Add(" == "); - stream->Add(type_literal_->GetFlatContent().ToAsciiVector()); + stream->Add(" == %o", *type_literal_); + HControlInstruction::PrintDataTo(stream); +} + + +HValue* HConstant::Canonicalize() { + return HasNoUses() && !IsBlockEntry() ? NULL : this; +} + + +HValue* HTypeof::Canonicalize() { + return HasNoUses() && !IsBlockEntry() ? NULL : this; +} + + +HValue* HBitwise::Canonicalize() { + if (!representation().IsInteger32()) return this; + // If x is an int32, then x & -1 == x, x | 0 == x and x ^ 0 == x. + int32_t nop_constant = (op() == Token::BIT_AND) ? -1 : 0; + if (left()->IsConstant() && + HConstant::cast(left())->HasInteger32Value() && + HConstant::cast(left())->Integer32Value() == nop_constant) { + return right(); + } + if (right()->IsConstant() && + HConstant::cast(right())->HasInteger32Value() && + HConstant::cast(right())->Integer32Value() == nop_constant) { + return left(); + } + return this; +} + + +HValue* HChange::Canonicalize() { + return (from().Equals(to())) ? value() : this; +} + + +void HTypeof::PrintDataTo(StringStream* stream) { + value()->PrintNameTo(stream); } void HChange::PrintDataTo(StringStream* stream) { HUnaryOperation::PrintDataTo(stream); - stream->Add(" %s to %s", from_.Mnemonic(), to().Mnemonic()); + stream->Add(" %s to %s", from().Mnemonic(), to().Mnemonic()); if (CanTruncateToInt32()) stream->Add(" truncating-int32"); if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?"); + if (CheckFlag(kDeoptimizeOnUndefined)) stream->Add(" deopt-on-undefined"); } @@ -848,6 +893,13 @@ void HCheckInstanceType::GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag) { void HCheckMap::PrintDataTo(StringStream* stream) { value()->PrintNameTo(stream); stream->Add(" %p", *map()); + if (mode() == REQUIRE_EXACT_MAP) { + stream->Add(" [EXACT]"); + } else if (!has_element_transitions_) { + stream->Add(" [EXACT*]"); + } else { + stream->Add(" [MATCH ELEMENTS]"); + } } @@ -857,6 +909,23 @@ void HCheckFunction::PrintDataTo(StringStream* stream) { } +const char* HCheckInstanceType::GetCheckName() { + switch (check_) { + case IS_SPEC_OBJECT: return "object"; + case IS_JS_ARRAY: return "array"; + case IS_STRING: return "string"; + case IS_SYMBOL: return "symbol"; + } + UNREACHABLE(); + return ""; +} + +void HCheckInstanceType::PrintDataTo(StringStream* stream) { + stream->Add("%s ", GetCheckName()); + HUnaryOperation::PrintDataTo(stream); +} + + void HCallStub::PrintDataTo(StringStream* stream) { stream->Add("%s ", CodeStub::MajorName(major_key_, false)); @@ -1085,7 +1154,7 @@ void HPhi::InitRealUses(int phi_id) { HValue* value = it.value(); if (!value->IsPhi()) { Representation rep = value->RequiredInputRepresentation(it.index()); - ++non_phi_uses_[rep.kind()]; + non_phi_uses_[rep.kind()] += value->LoopWeight(); } } } @@ -1106,15 +1175,16 @@ void HPhi::AddIndirectUsesTo(int* dest) { void HSimulate::PrintDataTo(StringStream* stream) { - stream->Add("id=%d ", ast_id()); - if (pop_count_ > 0) stream->Add("pop %d", pop_count_); + stream->Add("id=%d", ast_id()); + if (pop_count_ > 0) stream->Add(" pop %d", pop_count_); if (values_.length() > 0) { if (pop_count_ > 0) stream->Add(" /"); for (int i = 0; i < values_.length(); ++i) { - if (!HasAssignedIndexAt(i)) { - stream->Add(" push "); - } else { + if (i > 0) stream->Add(","); + if (HasAssignedIndexAt(i)) { stream->Add(" var[%d] = ", GetAssignedIndexAt(i)); + } else { + stream->Add(" push "); } values_[i]->PrintNameTo(stream); } @@ -1195,7 +1265,9 @@ void HConstant::PrintDataTo(StringStream* stream) { bool HArrayLiteral::IsCopyOnWrite() const { - return constant_elements()->map() == HEAP->fixed_cow_array_map(); + if (!boilerplate_object_->IsJSObject()) return false; + return Handle<JSObject>::cast(boilerplate_object_)->elements()->map() == + HEAP->fixed_cow_array_map(); } @@ -1208,28 +1280,17 @@ void HBinaryOperation::PrintDataTo(StringStream* stream) { } -Range* HBitAnd::InferRange() { +Range* HBitwise::InferRange() { + if (op() == Token::BIT_XOR) return HValue::InferRange(); int32_t left_mask = (left()->range() != NULL) ? left()->range()->Mask() : 0xffffffff; int32_t right_mask = (right()->range() != NULL) ? right()->range()->Mask() : 0xffffffff; - int32_t result_mask = left_mask & right_mask; - return (result_mask >= 0) - ? new Range(0, result_mask) - : HValue::InferRange(); -} - - -Range* HBitOr::InferRange() { - int32_t left_mask = (left()->range() != NULL) - ? left()->range()->Mask() - : 0xffffffff; - int32_t right_mask = (right()->range() != NULL) - ? right()->range()->Mask() - : 0xffffffff; - int32_t result_mask = left_mask | right_mask; + int32_t result_mask = (op() == Token::BIT_AND) + ? left_mask & right_mask + : left_mask | right_mask; return (result_mask >= 0) ? new Range(0, result_mask) : HValue::InferRange(); @@ -1293,6 +1354,23 @@ Range* HShl::InferRange() { } +Range* HLoadKeyedSpecializedArrayElement::InferRange() { + switch (elements_kind()) { + case EXTERNAL_PIXEL_ELEMENTS: + return new Range(0, 255); + case EXTERNAL_BYTE_ELEMENTS: + return new Range(-128, 127); + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + return new Range(0, 255); + case EXTERNAL_SHORT_ELEMENTS: + return new Range(-32768, 32767); + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + return new Range(0, 65535); + default: + return HValue::InferRange(); + } +} + void HCompareGeneric::PrintDataTo(StringStream* stream) { stream->Add(Token::Name(token())); @@ -1301,6 +1379,13 @@ void HCompareGeneric::PrintDataTo(StringStream* stream) { } +void HStringCompareAndBranch::PrintDataTo(StringStream* stream) { + stream->Add(Token::Name(token())); + stream->Add(" "); + HControlInstruction::PrintDataTo(stream); +} + + void HCompareIDAndBranch::PrintDataTo(StringStream* stream) { stream->Add(Token::Name(token())); stream->Add(" "); @@ -1311,6 +1396,14 @@ void HCompareIDAndBranch::PrintDataTo(StringStream* stream) { } +void HCompareObjectEqAndBranch::PrintDataTo(StringStream* stream) { + left()->PrintNameTo(stream); + stream->Add(" "); + right()->PrintNameTo(stream); + HControlInstruction::PrintDataTo(stream); +} + + void HGoto::PrintDataTo(StringStream* stream) { stream->Add("B%d", SuccessorAt(0)->block_id()); } @@ -1347,21 +1440,21 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context, SetOperandAt(0, context); SetOperandAt(1, object); set_representation(Representation::Tagged()); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); for (int i = 0; i < types->length() && types_.length() < kMaxLoadPolymorphism; ++i) { Handle<Map> map = types->at(i); - LookupResult lookup; + LookupResult lookup(map->GetIsolate()); map->LookupInDescriptors(NULL, *name, &lookup); - if (lookup.IsProperty()) { + if (lookup.IsFound()) { switch (lookup.type()) { case FIELD: { int index = lookup.GetLocalFieldIndexFromMap(*map); if (index < 0) { - SetFlag(kDependsOnInobjectFields); + SetGVNFlag(kDependsOnInobjectFields); } else { - SetFlag(kDependsOnBackingStoreFields); + SetGVNFlag(kDependsOnBackingStoreFields); } types_.Add(types->at(i)); break; @@ -1405,14 +1498,14 @@ bool HLoadNamedFieldPolymorphic::DataEquals(HValue* value) { void HLoadNamedFieldPolymorphic::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); - stream->Add(" ."); + stream->Add("."); stream->Add(*String::cast(*name())->ToCString()); } void HLoadNamedGeneric::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); - stream->Add(" ."); + stream->Add("."); stream->Add(*String::cast(*name())->ToCString()); } @@ -1425,7 +1518,7 @@ void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) { } -bool HLoadKeyedFastElement::RequiresHoleCheck() const { +bool HLoadKeyedFastElement::RequiresHoleCheck() { for (HUseIterator it(uses()); !it.Done(); it.Advance()) { HValue* use = it.value(); if (!use->IsChange()) return true; @@ -1442,11 +1535,6 @@ void HLoadKeyedFastDoubleElement::PrintDataTo(StringStream* stream) { } -bool HLoadKeyedFastDoubleElement::RequiresHoleCheck() const { - return true; -} - - void HLoadKeyedGeneric::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); stream->Add("["); @@ -1488,6 +1576,7 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo( stream->Add("pixel"); break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -1513,10 +1602,10 @@ void HStoreNamedGeneric::PrintDataTo(StringStream* stream) { void HStoreNamedField::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); stream->Add("."); - ASSERT(name()->IsString()); stream->Add(*String::cast(*name())->ToCString()); stream->Add(" = "); value()->PrintNameTo(stream); + stream->Add(" @%d%s", offset(), is_in_object() ? "[in-object]" : ""); if (!transition().is_null()) { stream->Add(" (transition map %p)", *transition()); } @@ -1582,6 +1671,7 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( case EXTERNAL_PIXEL_ELEMENTS: stream->Add("pixel"); break; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -1596,9 +1686,26 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( } +void HTransitionElementsKind::PrintDataTo(StringStream* stream) { + object()->PrintNameTo(stream); + stream->Add(" %p -> %p", *original_map(), *transitioned_map()); +} + + void HLoadGlobalCell::PrintDataTo(StringStream* stream) { stream->Add("[%p]", *cell()); - if (check_hole_value()) stream->Add(" (deleteable/read-only)"); + if (!details_.IsDontDelete()) stream->Add(" (deleteable)"); + if (details_.IsReadOnly()) stream->Add(" (read-only)"); +} + + +bool HLoadGlobalCell::RequiresHoleCheck() { + if (details_.IsDontDelete() && !details_.IsReadOnly()) return false; + for (HUseIterator it(uses()); !it.Done(); it.Advance()) { + HValue* use = it.value(); + if (!use->IsChange()) return true; + } + return false; } @@ -1610,6 +1717,8 @@ void HLoadGlobalGeneric::PrintDataTo(StringStream* stream) { void HStoreGlobalCell::PrintDataTo(StringStream* stream) { stream->Add("[%p] = ", *cell()); value()->PrintNameTo(stream); + if (!details_.IsDontDelete()) stream->Add(" (deleteable)"); + if (details_.IsReadOnly()) stream->Add(" (read-only)"); } @@ -1696,6 +1805,12 @@ HType HInstanceOfKnownGlobal::CalculateInferredType() { } +HType HChange::CalculateInferredType() { + if (from().IsDouble() && to().IsTagged()) return HType::HeapNumber(); + return type(); +} + + HType HBitwiseBinaryOperation::CalculateInferredType() { return HType::TaggedNumber(); } @@ -1711,43 +1826,43 @@ HType HAdd::CalculateInferredType() { } -HType HBitAnd::CalculateInferredType() { +HType HBitNot::CalculateInferredType() { return HType::TaggedNumber(); } -HType HBitXor::CalculateInferredType() { +HType HUnaryMathOperation::CalculateInferredType() { return HType::TaggedNumber(); } -HType HBitOr::CalculateInferredType() { - return HType::TaggedNumber(); +HType HStringCharFromCode::CalculateInferredType() { + return HType::String(); } -HType HBitNot::CalculateInferredType() { - return HType::TaggedNumber(); +HType HArrayLiteral::CalculateInferredType() { + return HType::JSArray(); } -HType HUnaryMathOperation::CalculateInferredType() { - return HType::TaggedNumber(); +HType HObjectLiteralFast::CalculateInferredType() { + return HType::JSObject(); } -HType HShl::CalculateInferredType() { - return HType::TaggedNumber(); +HType HObjectLiteralGeneric::CalculateInferredType() { + return HType::JSObject(); } -HType HShr::CalculateInferredType() { - return HType::TaggedNumber(); +HType HRegExpLiteral::CalculateInferredType() { + return HType::JSObject(); } -HType HSar::CalculateInferredType() { - return HType::TaggedNumber(); +HType HFunctionLiteral::CalculateInferredType() { + return HType::JSObject(); } @@ -1838,6 +1953,167 @@ HValue* HAdd::EnsureAndPropagateNotMinusZero(BitVector* visited) { } +#define H_CONSTANT_INT32(val) \ +new(zone) HConstant(FACTORY->NewNumberFromInt(val, TENURED), \ + Representation::Integer32()) +#define H_CONSTANT_DOUBLE(val) \ +new(zone) HConstant(FACTORY->NewNumber(val, TENURED), \ + Representation::Double()) + +#define DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR(HInstr, op) \ +HInstruction* HInstr::New##HInstr(Zone* zone, \ + HValue* context, \ + HValue* left, \ + HValue* right) { \ + if (left->IsConstant() && right->IsConstant()) { \ + HConstant* c_left = HConstant::cast(left); \ + HConstant* c_right = HConstant::cast(right); \ + if ((c_left->HasNumberValue() && c_right->HasNumberValue())) { \ + double double_res = c_left->DoubleValue() op c_right->DoubleValue(); \ + if (TypeInfo::IsInt32Double(double_res)) { \ + return H_CONSTANT_INT32(static_cast<int32_t>(double_res)); \ + } \ + return H_CONSTANT_DOUBLE(double_res); \ + } \ + } \ + return new(zone) HInstr(context, left, right); \ +} + + +DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR(HAdd, +) +DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR(HMul, *) +DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR(HSub, -) + +#undef DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR + + +HInstruction* HMod::NewHMod(Zone* zone, + HValue* context, + HValue* left, + HValue* right) { + if (left->IsConstant() && right->IsConstant()) { + HConstant* c_left = HConstant::cast(left); + HConstant* c_right = HConstant::cast(right); + if (c_left->HasInteger32Value() && c_right->HasInteger32Value()) { + int32_t dividend = c_left->Integer32Value(); + int32_t divisor = c_right->Integer32Value(); + if (divisor != 0) { + int32_t res = dividend % divisor; + if ((res == 0) && (dividend < 0)) { + return H_CONSTANT_DOUBLE(-0.0); + } + return H_CONSTANT_INT32(res); + } + } + } + return new(zone) HMod(context, left, right); +} + + +HInstruction* HDiv::NewHDiv(Zone* zone, + HValue* context, + HValue* left, + HValue* right) { + // If left and right are constant values, try to return a constant value. + if (left->IsConstant() && right->IsConstant()) { + HConstant* c_left = HConstant::cast(left); + HConstant* c_right = HConstant::cast(right); + if ((c_left->HasNumberValue() && c_right->HasNumberValue())) { + if (c_right->DoubleValue() != 0) { + double double_res = c_left->DoubleValue() / c_right->DoubleValue(); + if (TypeInfo::IsInt32Double(double_res)) { + return H_CONSTANT_INT32(static_cast<int32_t>(double_res)); + } + return H_CONSTANT_DOUBLE(double_res); + } + } + } + return new(zone) HDiv(context, left, right); +} + + +HInstruction* HBitwise::NewHBitwise(Zone* zone, + Token::Value op, + HValue* context, + HValue* left, + HValue* right) { + if (left->IsConstant() && right->IsConstant()) { + HConstant* c_left = HConstant::cast(left); + HConstant* c_right = HConstant::cast(right); + if ((c_left->HasNumberValue() && c_right->HasNumberValue())) { + int32_t result; + int32_t v_left = c_left->NumberValueAsInteger32(); + int32_t v_right = c_right->NumberValueAsInteger32(); + switch (op) { + case Token::BIT_XOR: + result = v_left ^ v_right; + break; + case Token::BIT_AND: + result = v_left & v_right; + break; + case Token::BIT_OR: + result = v_left | v_right; + break; + default: + result = 0; // Please the compiler. + UNREACHABLE(); + } + return H_CONSTANT_INT32(result); + } + } + return new(zone) HBitwise(op, context, left, right); +} + + +#define DEFINE_NEW_H_BITWISE_INSTR(HInstr, result) \ +HInstruction* HInstr::New##HInstr(Zone* zone, \ + HValue* context, \ + HValue* left, \ + HValue* right) { \ + if (left->IsConstant() && right->IsConstant()) { \ + HConstant* c_left = HConstant::cast(left); \ + HConstant* c_right = HConstant::cast(right); \ + if ((c_left->HasNumberValue() && c_right->HasNumberValue())) { \ + return H_CONSTANT_INT32(result); \ + } \ + } \ + return new(zone) HInstr(context, left, right); \ +} + + +DEFINE_NEW_H_BITWISE_INSTR(HSar, +c_left->NumberValueAsInteger32() >> (c_right->NumberValueAsInteger32() & 0x1f)) +DEFINE_NEW_H_BITWISE_INSTR(HShl, +c_left->NumberValueAsInteger32() << (c_right->NumberValueAsInteger32() & 0x1f)) + +#undef DEFINE_NEW_H_BITWISE_INSTR + + +HInstruction* HShr::NewHShr(Zone* zone, + HValue* context, + HValue* left, + HValue* right) { + if (left->IsConstant() && right->IsConstant()) { + HConstant* c_left = HConstant::cast(left); + HConstant* c_right = HConstant::cast(right); + if ((c_left->HasNumberValue() && c_right->HasNumberValue())) { + int32_t left_val = c_left->NumberValueAsInteger32(); + int32_t right_val = c_right->NumberValueAsInteger32() & 0x1f; + if ((right_val == 0) && (left_val < 0)) { + return H_CONSTANT_DOUBLE( + static_cast<double>(static_cast<uint32_t>(left_val))); + } + return H_CONSTANT_INT32(static_cast<uint32_t>(left_val) >> right_val); + } + } + return new(zone) HShr(context, left, right); +} + + +#undef H_CONSTANT_INT32 +#undef H_CONSTANT_DOUBLE + + void HIn::PrintDataTo(StringStream* stream) { key()->PrintNameTo(stream); stream->Add(" "); diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index 0af5489bed..39e3950251 100644 --- a/deps/v8/src/hydrogen-instructions.h +++ b/deps/v8/src/hydrogen-instructions.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -67,10 +67,8 @@ class LChunkBuilder; V(ArgumentsLength) \ V(ArgumentsObject) \ V(ArrayLiteral) \ - V(BitAnd) \ + V(Bitwise) \ V(BitNot) \ - V(BitOr) \ - V(BitXor) \ V(BlockEntry) \ V(BoundsCheck) \ V(Branch) \ @@ -118,10 +116,12 @@ class LChunkBuilder; V(InstanceOfKnownGlobal) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ - V(IsNullAndBranch) \ + V(IsNilAndBranch) \ V(IsObjectAndBranch) \ + V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ + V(StringCompareAndBranch) \ V(JSArrayLength) \ V(LeaveInlined) \ V(LoadContextSlot) \ @@ -139,12 +139,14 @@ class LChunkBuilder; V(LoadNamedGeneric) \ V(Mod) \ V(Mul) \ - V(ObjectLiteral) \ + V(ObjectLiteralFast) \ + V(ObjectLiteralGeneric) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(Sar) \ @@ -171,6 +173,7 @@ class LChunkBuilder; V(Throw) \ V(ToFastProperties) \ V(ToInt32) \ + V(TransitionElementsKind) \ V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ @@ -182,6 +185,8 @@ class LChunkBuilder; V(Calls) \ V(InobjectFields) \ V(BackingStoreFields) \ + V(ElementsKind) \ + V(ElementsPointer) \ V(ArrayElements) \ V(DoubleArrayElements) \ V(SpecializedArrayElements) \ @@ -245,7 +250,9 @@ class Range: public ZoneObject { return lower_ >= Smi::kMinValue && upper_ <= Smi::kMaxValue; } void KeepOrder(); +#ifdef DEBUG void Verify() const; +#endif void StackUpon(Range* other) { Intersect(other); @@ -397,10 +404,14 @@ class HType { return type_ == kUninitialized; } + bool IsHeapObject() { + ASSERT(type_ != kUninitialized); + return IsHeapNumber() || IsString() || IsNonPrimitive(); + } + static HType TypeFromValue(Handle<Object> value); const char* ToString(); - const char* ToShortString(); private: enum Type { @@ -482,18 +493,26 @@ class HUseIterator BASE_EMBEDDED { }; +// There must be one corresponding kDepends flag for every kChanges flag and +// the order of the kChanges flags must be exactly the same as of the kDepends +// flags. +enum GVNFlag { + // Declare global value numbering flags. +#define DECLARE_FLAG(type) kChanges##type, kDependsOn##type, + GVN_FLAG_LIST(DECLARE_FLAG) +#undef DECLARE_FLAG + kAfterLastFlag, + kLastFlag = kAfterLastFlag - 1 +}; + +typedef EnumSet<GVNFlag> GVNFlagSet; + + class HValue: public ZoneObject { public: static const int kNoNumber = -1; - // There must be one corresponding kDepends flag for every kChanges flag and - // the order of the kChanges flags must be exactly the same as of the kDepends - // flags. enum Flag { - // Declare global value numbering flags. - #define DECLARE_DO(type) kChanges##type, kDependsOn##type, - GVN_FLAG_LIST(DECLARE_DO) - #undef DECLARE_DO kFlexibleRepresentation, // Participate in Global Value Numbering, i.e. elimination of // unnecessary recomputations. If an instruction sets this flag, it must @@ -513,8 +532,8 @@ class HValue: public ZoneObject { static const int kChangesToDependsFlagsLeftShift = 1; - static int ConvertChangesToDependsFlags(int flags) { - return flags << kChangesToDependsFlagsLeftShift; + static GVNFlagSet ConvertChangesToDependsFlags(GVNFlagSet flags) { + return GVNFlagSet(flags.ToIntegral() << kChangesToDependsFlagsLeftShift); } static HValue* cast(HValue* value) { return value; } @@ -551,6 +570,7 @@ class HValue: public ZoneObject { HBasicBlock* block() const { return block_; } void SetBlock(HBasicBlock* block); + int LoopWeight() const; int id() const { return id_; } void set_id(int id) { id_ = id; } @@ -612,11 +632,45 @@ class HValue: public ZoneObject { void ClearFlag(Flag f) { flags_ &= ~(1 << f); } bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; } - void SetAllSideEffects() { flags_ |= AllSideEffects(); } - void ClearAllSideEffects() { flags_ &= ~AllSideEffects(); } - bool HasSideEffects() const { return (flags_ & AllSideEffects()) != 0; } + GVNFlagSet gvn_flags() const { return gvn_flags_; } + void SetGVNFlag(GVNFlag f) { gvn_flags_.Add(f); } + void ClearGVNFlag(GVNFlag f) { gvn_flags_.Remove(f); } + bool CheckGVNFlag(GVNFlag f) const { return gvn_flags_.Contains(f); } + void SetAllSideEffects() { gvn_flags_.Add(AllSideEffectsFlagSet()); } + void ClearAllSideEffects() { + gvn_flags_.Remove(AllSideEffectsFlagSet()); + } + bool HasSideEffects() const { + return gvn_flags_.ContainsAnyOf(AllSideEffectsFlagSet()); + } + bool HasObservableSideEffects() const { + return gvn_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet()); + } - int ChangesFlags() const { return flags_ & ChangesFlagsMask(); } + GVNFlagSet DependsOnFlags() const { + GVNFlagSet result = gvn_flags_; + result.Intersect(AllDependsOnFlagSet()); + return result; + } + + GVNFlagSet SideEffectFlags() const { + GVNFlagSet result = gvn_flags_; + result.Intersect(AllSideEffectsFlagSet()); + return result; + } + + GVNFlagSet ChangesFlags() const { + GVNFlagSet result = gvn_flags_; + result.Intersect(AllChangesFlagSet()); + return result; + } + + GVNFlagSet ObservableChangesFlags() const { + GVNFlagSet result = gvn_flags_; + result.Intersect(AllChangesFlagSet()); + result.Intersect(AllObservableSideEffectsFlagSet()); + return result; + } Range* range() const { return range_; } bool HasRange() const { return range_ != NULL; } @@ -625,7 +679,7 @@ class HValue: public ZoneObject { void ComputeInitialRange(); // Representation helpers. - virtual Representation RequiredInputRepresentation(int index) const = 0; + virtual Representation RequiredInputRepresentation(int index) = 0; virtual Representation InferredRepresentation() { return representation(); @@ -681,19 +735,39 @@ class HValue: public ZoneObject { representation_ = r; } - private: - static int ChangesFlagsMask() { - int result = 0; + static GVNFlagSet AllDependsOnFlagSet() { + GVNFlagSet result; // Create changes mask. -#define ADD_FLAG(type) result |= (1 << kChanges##type); +#define ADD_FLAG(type) result.Add(kDependsOn##type); + GVN_FLAG_LIST(ADD_FLAG) +#undef ADD_FLAG + return result; + } + + static GVNFlagSet AllChangesFlagSet() { + GVNFlagSet result; + // Create changes mask. +#define ADD_FLAG(type) result.Add(kChanges##type); GVN_FLAG_LIST(ADD_FLAG) #undef ADD_FLAG return result; } // A flag mask to mark an instruction as having arbitrary side effects. - static int AllSideEffects() { - return ChangesFlagsMask() & ~(1 << kChangesOsrEntries); + static GVNFlagSet AllSideEffectsFlagSet() { + GVNFlagSet result = AllChangesFlagSet(); + result.Remove(kChangesOsrEntries); + return result; + } + + // A flag mask of all side effects that can make observable changes in + // an executing program (i.e. are not safe to repeat, move or remove); + static GVNFlagSet AllObservableSideEffectsFlagSet() { + GVNFlagSet result = AllChangesFlagSet(); + result.Remove(kChangesElementsKind); + result.Remove(kChangesElementsPointer); + result.Remove(kChangesMaps); + return result; } // Remove the matching use from the use list if present. Returns the @@ -713,7 +787,9 @@ class HValue: public ZoneObject { HUseListNode* use_list_; Range* range_; int flags_; + GVNFlagSet gvn_flags_; + private: DISALLOW_COPY_AND_ASSIGN(HValue); }; @@ -750,7 +826,7 @@ class HInstruction: public HValue { : next_(NULL), previous_(NULL), position_(RelocInfo::kNoPosition) { - SetFlag(kDependsOnOsrEntries); + SetGVNFlag(kDependsOnOsrEntries); } virtual void DeleteFromGraph() { Unlink(); } @@ -841,7 +917,7 @@ class HTemplateControlInstruction: public HControlInstruction { class HBlockEntry: public HTemplateInstruction<0> { public: - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -854,7 +930,7 @@ class HBlockEntry: public HTemplateInstruction<0> { // HSoftDeoptimize does not end a basic block as opposed to HDeoptimize. class HSoftDeoptimize: public HTemplateInstruction<0> { public: - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -866,7 +942,7 @@ class HDeoptimize: public HControlInstruction { public: explicit HDeoptimize(int environment_length) : values_(environment_length) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -908,10 +984,10 @@ class HDeoptimize: public HControlInstruction { class HGoto: public HTemplateControlInstruction<1, 0> { public: explicit HGoto(HBasicBlock* target) { - SetSuccessorAt(0, target); - } + SetSuccessorAt(0, target); + } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -951,7 +1027,7 @@ class HBranch: public HUnaryControlInstruction { : HUnaryControlInstruction(value, NULL, NULL) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -983,7 +1059,7 @@ class HCompareMap: public HUnaryControlInstruction { Handle<Map> map() const { return map_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1000,7 +1076,7 @@ class HReturn: public HTemplateControlInstruction<0, 1> { SetOperandAt(0, value); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1014,7 +1090,7 @@ class HReturn: public HTemplateControlInstruction<0, 1> { class HAbnormalExit: public HTemplateControlInstruction<0, 0> { public: - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1049,7 +1125,7 @@ class HThrow: public HTemplateInstruction<2> { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1064,7 +1140,7 @@ class HUseConst: public HUnaryOperation { public: explicit HUseConst(HValue* old_value) : HUnaryOperation(old_value) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1083,7 +1159,7 @@ class HForceRepresentation: public HTemplateInstruction<1> { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return representation(); // Same as the output representation. } @@ -1094,27 +1170,33 @@ class HForceRepresentation: public HTemplateInstruction<1> { class HChange: public HUnaryOperation { public: HChange(HValue* value, - Representation from, Representation to, bool is_truncating, bool deoptimize_on_undefined) - : HUnaryOperation(value), - from_(from), - deoptimize_on_undefined_(deoptimize_on_undefined) { - ASSERT(!from.IsNone() && !to.IsNone()); - ASSERT(!from.Equals(to)); + : HUnaryOperation(value) { + ASSERT(!value->representation().IsNone() && !to.IsNone()); + ASSERT(!value->representation().Equals(to)); set_representation(to); + set_type(HType::TaggedNumber()); SetFlag(kUseGVN); + if (deoptimize_on_undefined) SetFlag(kDeoptimizeOnUndefined); if (is_truncating) SetFlag(kTruncatingToInt32); } virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); + virtual HType CalculateInferredType(); + virtual HValue* Canonicalize(); - Representation from() const { return from_; } - Representation to() const { return representation(); } - bool deoptimize_on_undefined() const { return deoptimize_on_undefined_; } - virtual Representation RequiredInputRepresentation(int index) const { - return from_; + Representation from() { return value()->representation(); } + Representation to() { return representation(); } + bool deoptimize_on_undefined() const { + return CheckFlag(kDeoptimizeOnUndefined); + } + bool deoptimize_on_minus_zero() const { + return CheckFlag(kBailoutOnMinusZero); + } + virtual Representation RequiredInputRepresentation(int index) { + return from(); } virtual Range* InferRange(); @@ -1124,16 +1206,7 @@ class HChange: public HUnaryOperation { DECLARE_CONCRETE_INSTRUCTION(Change) protected: - virtual bool DataEquals(HValue* other) { - if (!other->IsChange()) return false; - HChange* change = HChange::cast(other); - return to().Equals(change->to()) - && deoptimize_on_undefined() == change->deoptimize_on_undefined(); - } - - private: - Representation from_; - bool deoptimize_on_undefined_; + virtual bool DataEquals(HValue* other) { return true; } }; @@ -1145,7 +1218,7 @@ class HClampToUint8: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1164,7 +1237,7 @@ class HToInt32: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1223,7 +1296,7 @@ class HSimulate: public HInstruction { virtual int OperandCount() { return values_.length(); } virtual HValue* OperandAt(int index) { return values_[index]; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1268,7 +1341,7 @@ class HStackCheck: public HTemplateInstruction<1> { HValue* context() { return OperandAt(0); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1293,9 +1366,11 @@ class HStackCheck: public HTemplateInstruction<1> { class HEnterInlined: public HTemplateInstruction<0> { public: HEnterInlined(Handle<JSFunction> closure, + int arguments_count, FunctionLiteral* function, CallKind call_kind) : closure_(closure), + arguments_count_(arguments_count), function_(function), call_kind_(call_kind) { } @@ -1303,10 +1378,11 @@ class HEnterInlined: public HTemplateInstruction<0> { virtual void PrintDataTo(StringStream* stream); Handle<JSFunction> closure() const { return closure_; } + int arguments_count() const { return arguments_count_; } FunctionLiteral* function() const { return function_; } CallKind call_kind() const { return call_kind_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1314,6 +1390,7 @@ class HEnterInlined: public HTemplateInstruction<0> { private: Handle<JSFunction> closure_; + int arguments_count_; FunctionLiteral* function_; CallKind call_kind_; }; @@ -1323,7 +1400,7 @@ class HLeaveInlined: public HTemplateInstruction<0> { public: HLeaveInlined() {} - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1337,7 +1414,7 @@ class HPushArgument: public HUnaryOperation { set_representation(Representation::Tagged()); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1349,19 +1426,27 @@ class HPushArgument: public HUnaryOperation { class HThisFunction: public HTemplateInstruction<0> { public: - HThisFunction() { + explicit HThisFunction(Handle<JSFunction> closure) : closure_(closure) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } + Handle<JSFunction> closure() const { return closure_; } + DECLARE_CONCRETE_INSTRUCTION(ThisFunction) protected: - virtual bool DataEquals(HValue* other) { return true; } + virtual bool DataEquals(HValue* other) { + HThisFunction* b = HThisFunction::cast(other); + return *closure() == *b->closure(); + } + + private: + Handle<JSFunction> closure_; }; @@ -1372,7 +1457,7 @@ class HContext: public HTemplateInstruction<0> { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1392,7 +1477,7 @@ class HOuterContext: public HUnaryOperation { DECLARE_CONCRETE_INSTRUCTION(OuterContext); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1410,7 +1495,7 @@ class HGlobalObject: public HUnaryOperation { DECLARE_CONCRETE_INSTRUCTION(GlobalObject) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1429,7 +1514,7 @@ class HGlobalReceiver: public HUnaryOperation { DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1465,7 +1550,7 @@ class HUnaryCall: public HCall<1> { SetOperandAt(0, value); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1485,7 +1570,7 @@ class HBinaryCall: public HCall<2> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1500,7 +1585,7 @@ class HInvokeFunction: public HBinaryCall { : HBinaryCall(context, function, argument_count) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1525,7 +1610,7 @@ class HCallConstantFunction: public HCall<0> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1542,7 +1627,7 @@ class HCallKeyed: public HBinaryCall { : HBinaryCall(context, key, argument_count) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1566,7 +1651,7 @@ class HCallNamed: public HUnaryCall { DECLARE_CONCRETE_INSTRUCTION(CallNamed) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1575,15 +1660,16 @@ class HCallNamed: public HUnaryCall { }; -class HCallFunction: public HUnaryCall { +class HCallFunction: public HBinaryCall { public: - HCallFunction(HValue* context, int argument_count) - : HUnaryCall(context, argument_count) { + HCallFunction(HValue* context, HValue* function, int argument_count) + : HBinaryCall(context, function, argument_count) { } - HValue* context() { return value(); } + HValue* context() { return first(); } + HValue* function() { return second(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1602,7 +1688,7 @@ class HCallGlobal: public HUnaryCall { HValue* context() { return value(); } Handle<String> name() const { return name_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1622,7 +1708,7 @@ class HCallKnownGlobal: public HCall<0> { Handle<JSFunction> target() const { return target_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -1639,7 +1725,7 @@ class HCallNew: public HBinaryCall { : HBinaryCall(context, constructor, argument_count) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1666,7 +1752,7 @@ class HCallRuntime: public HCall<1> { const Runtime::Function* function() const { return c_function_; } Handle<String> name() const { return name_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1688,11 +1774,11 @@ class HJSArrayLength: public HTemplateInstruction<2> { SetOperandAt(1, typecheck); set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnArrayLengths); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnArrayLengths); + SetGVNFlag(kDependsOnMaps); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1713,10 +1799,10 @@ class HFixedArrayBaseLength: public HUnaryOperation { explicit HFixedArrayBaseLength(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnArrayLengths); + SetGVNFlag(kDependsOnArrayLengths); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1732,10 +1818,10 @@ class HElementsKind: public HUnaryOperation { explicit HElementsKind(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Integer32()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnElementsKind); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1754,7 +1840,7 @@ class HBitNot: public HUnaryOperation { SetFlag(kTruncatingToInt32); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Integer32(); } virtual HType CalculateInferredType(); @@ -1804,7 +1890,7 @@ class HUnaryMathOperation: public HTemplateInstruction<2> { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { if (index == 0) { return Representation::Tagged(); } else { @@ -1858,10 +1944,10 @@ class HLoadElements: public HUnaryOperation { explicit HLoadElements(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnElementsPointer); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1884,7 +1970,7 @@ class HLoadExternalArrayPointer: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -1897,18 +1983,29 @@ class HLoadExternalArrayPointer: public HUnaryOperation { class HCheckMap: public HTemplateInstruction<2> { public: - HCheckMap(HValue* value, Handle<Map> map, HValue* typecheck = NULL) - : map_(map) { + HCheckMap(HValue* value, Handle<Map> map, + HValue* typecheck = NULL, + CompareMapMode mode = REQUIRE_EXACT_MAP) + : map_(map), + mode_(mode) { SetOperandAt(0, value); // If callers don't depend on a typecheck, they can pass in NULL. In that // case we use a copy of the |value| argument as a dummy value. SetOperandAt(1, typecheck != NULL ? typecheck : value); set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); + // If the map to check doesn't have the untransitioned elements, it must not + // be hoisted above TransitionElements instructions. + if (mode == REQUIRE_EXACT_MAP || !map->has_fast_smi_only_elements()) { + SetGVNFlag(kDependsOnElementsKind); + } + has_element_transitions_ = + map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL) != NULL || + map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL) != NULL; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual void PrintDataTo(StringStream* stream); @@ -1916,17 +2013,24 @@ class HCheckMap: public HTemplateInstruction<2> { HValue* value() { return OperandAt(0); } Handle<Map> map() const { return map_; } + CompareMapMode mode() const { return mode_; } DECLARE_CONCRETE_INSTRUCTION(CheckMap) protected: virtual bool DataEquals(HValue* other) { HCheckMap* b = HCheckMap::cast(other); - return map_.is_identical_to(b->map()); + // Two CheckMaps instructions are DataEqual if their maps are identical and + // they have the same mode. The mode comparison can be ignored if the map + // has no elements transitions. + return map_.is_identical_to(b->map()) && + (b->mode() == mode() || !has_element_transitions_); } private: + bool has_element_transitions_; Handle<Map> map_; + CompareMapMode mode_; }; @@ -1938,7 +2042,7 @@ class HCheckFunction: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual void PrintDataTo(StringStream* stream); @@ -1978,7 +2082,9 @@ class HCheckInstanceType: public HUnaryOperation { return new HCheckInstanceType(value, IS_SYMBOL); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual void PrintDataTo(StringStream* stream); + + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2008,6 +2114,8 @@ class HCheckInstanceType: public HUnaryOperation { LAST_INTERVAL_CHECK = IS_JS_ARRAY }; + const char* GetCheckName(); + HCheckInstanceType(HValue* value, Check check) : HUnaryOperation(value), check_(check) { set_representation(Representation::Tagged()); @@ -2025,7 +2133,7 @@ class HCheckNonSmi: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2059,7 +2167,7 @@ class HCheckPrototypeMaps: public HTemplateInstruction<0> { HCheckPrototypeMaps(Handle<JSObject> prototype, Handle<JSObject> holder) : prototype_(prototype), holder_(holder) { SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); } #ifdef DEBUG @@ -2071,7 +2179,7 @@ class HCheckPrototypeMaps: public HTemplateInstruction<0> { DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -2102,7 +2210,7 @@ class HCheckSmi: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual HType CalculateInferredType(); @@ -2151,7 +2259,7 @@ class HPhi: public HValue { } virtual Range* InferRange(); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return representation(); } virtual HType CalculateInferredType(); @@ -2243,7 +2351,7 @@ class HArgumentsObject: public HTemplateInstruction<0> { SetFlag(kIsArguments); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -2259,7 +2367,20 @@ class HConstant: public HTemplateInstruction<0> { bool InOldSpace() const { return !HEAP->InNewSpace(*handle_); } - virtual Representation RequiredInputRepresentation(int index) const { + bool ImmortalImmovable() const { + Heap* heap = HEAP; + if (*handle_ == heap->undefined_value()) return true; + if (*handle_ == heap->null_value()) return true; + if (*handle_ == heap->true_value()) return true; + if (*handle_ == heap->false_value()) return true; + if (*handle_ == heap->the_hole_value()) return true; + if (*handle_ == heap->minus_zero_value()) return true; + if (*handle_ == heap->nan_value()) return true; + if (*handle_ == heap->empty_string()) return true; + return false; + } + + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -2272,6 +2393,7 @@ class HConstant: public HTemplateInstruction<0> { } virtual bool EmitAtUses() { return !representation().IsDouble(); } + virtual HValue* Canonicalize(); virtual void PrintDataTo(StringStream* stream); virtual HType CalculateInferredType(); bool IsInteger() const { return handle_->IsSmi(); } @@ -2287,6 +2409,12 @@ class HConstant: public HTemplateInstruction<0> { ASSERT(HasDoubleValue()); return double_value_; } + bool HasNumberValue() const { return has_int32_value_ || has_double_value_; } + int32_t NumberValueAsInteger32() const { + ASSERT(HasNumberValue()); + if (has_int32_value_) return int32_value_; + return DoubleToInt32(double_value_); + } bool HasStringValue() const { return handle_->IsString(); } bool ToBoolean() const; @@ -2367,7 +2495,7 @@ class HApplyArguments: public HTemplateInstruction<4> { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The length is untagged, all other inputs are tagged. return (index == 2) ? Representation::Integer32() @@ -2394,7 +2522,7 @@ class HArgumentsElements: public HTemplateInstruction<0> { DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -2410,7 +2538,7 @@ class HArgumentsLength: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2433,7 +2561,7 @@ class HAccessArgumentsAt: public HTemplateInstruction<3> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The arguments elements is considered tagged. return index == 0 ? Representation::Tagged() @@ -2459,7 +2587,7 @@ class HBoundsCheck: public HTemplateInstruction<2> { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Integer32(); } @@ -2484,7 +2612,7 @@ class HBitwiseBinaryOperation: public HBinaryOperation { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return index == 0 ? Representation::Tagged() : representation(); @@ -2522,7 +2650,7 @@ class HArithmeticBinaryOperation: public HBinaryOperation { } virtual HType CalculateInferredType(); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return index == 0 ? Representation::Tagged() : representation(); @@ -2549,7 +2677,7 @@ class HCompareGeneric: public HBinaryOperation { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2587,7 +2715,7 @@ class HCompareIDAndBranch: public HTemplateControlInstruction<2, 2> { return input_representation_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return input_representation_; } virtual void PrintDataTo(StringStream* stream); @@ -2610,7 +2738,9 @@ class HCompareObjectEqAndBranch: public HTemplateControlInstruction<2, 2> { HValue* left() { return OperandAt(0); } HValue* right() { return OperandAt(1); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual void PrintDataTo(StringStream* stream); + + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2629,7 +2759,7 @@ class HCompareConstantEqAndBranch: public HUnaryControlInstruction { HValue* left() { return value(); } int right() const { return right_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Integer32(); } @@ -2641,21 +2771,25 @@ class HCompareConstantEqAndBranch: public HUnaryControlInstruction { }; -class HIsNullAndBranch: public HUnaryControlInstruction { +class HIsNilAndBranch: public HUnaryControlInstruction { public: - HIsNullAndBranch(HValue* value, bool is_strict) - : HUnaryControlInstruction(value, NULL, NULL), is_strict_(is_strict) { } + HIsNilAndBranch(HValue* value, EqualityKind kind, NilValue nil) + : HUnaryControlInstruction(value, NULL, NULL), kind_(kind), nil_(nil) { } - bool is_strict() const { return is_strict_; } + EqualityKind kind() const { return kind_; } + NilValue nil() const { return nil_; } + + virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } - DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch) + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch) private: - bool is_strict_; + EqualityKind kind_; + NilValue nil_; }; @@ -2664,13 +2798,25 @@ class HIsObjectAndBranch: public HUnaryControlInstruction { explicit HIsObjectAndBranch(HValue* value) : HUnaryControlInstruction(value, NULL, NULL) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch) }; +class HIsStringAndBranch: public HUnaryControlInstruction { + public: + explicit HIsStringAndBranch(HValue* value) + : HUnaryControlInstruction(value, NULL, NULL) { } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch) +}; + class HIsSmiAndBranch: public HUnaryControlInstruction { public: @@ -2679,7 +2825,7 @@ class HIsSmiAndBranch: public HUnaryControlInstruction { DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2693,7 +2839,7 @@ class HIsUndetectableAndBranch: public HUnaryControlInstruction { explicit HIsUndetectableAndBranch(HValue* value) : HUnaryControlInstruction(value, NULL, NULL) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2701,9 +2847,45 @@ class HIsUndetectableAndBranch: public HUnaryControlInstruction { }; +class HStringCompareAndBranch: public HTemplateControlInstruction<2, 3> { + public: + HStringCompareAndBranch(HValue* context, + HValue* left, + HValue* right, + Token::Value token) + : token_(token) { + ASSERT(Token::IsCompareOp(token)); + SetOperandAt(0, context); + SetOperandAt(1, left); + SetOperandAt(2, right); + set_representation(Representation::Tagged()); + } + + HValue* context() { return OperandAt(0); } + HValue* left() { return OperandAt(1); } + HValue* right() { return OperandAt(2); } + Token::Value token() const { return token_; } + + virtual void PrintDataTo(StringStream* stream); + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + Representation GetInputRepresentation() const { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch) + + private: + Token::Value token_; +}; + + class HIsConstructCallAndBranch: public HTemplateControlInstruction<2, 0> { public: - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -2725,7 +2907,7 @@ class HHasInstanceTypeAndBranch: public HUnaryControlInstruction { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2742,7 +2924,7 @@ class HHasCachedArrayIndexAndBranch: public HUnaryControlInstruction { explicit HHasCachedArrayIndexAndBranch(HValue* value) : HUnaryControlInstruction(value, NULL, NULL) { } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2757,7 +2939,7 @@ class HGetCachedArrayIndex: public HUnaryOperation { SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2776,7 +2958,7 @@ class HClassOfTestAndBranch: public HUnaryControlInstruction { DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2800,7 +2982,7 @@ class HTypeofIsAndBranch: public HUnaryControlInstruction { DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2817,7 +2999,7 @@ class HInstanceOf: public HBinaryOperation { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2845,7 +3027,7 @@ class HInstanceOfKnownGlobal: public HTemplateInstruction<2> { HValue* left() { return OperandAt(1); } Handle<JSFunction> function() { return function_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -2870,7 +3052,7 @@ class HPower: public HTemplateInstruction<2> { HValue* left() { return OperandAt(0); } HValue* right() { return OperandAt(1); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return index == 0 ? Representation::Double() : Representation::None(); @@ -2883,6 +3065,23 @@ class HPower: public HTemplateInstruction<2> { }; +class HRandom: public HTemplateInstruction<1> { + public: + explicit HRandom(HValue* global_object) { + SetOperandAt(0, global_object); + set_representation(Representation::Double()); + } + + HValue* global_object() { return OperandAt(0); } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(Random) +}; + + class HAdd: public HArithmeticBinaryOperation { public: HAdd(HValue* context, HValue* left, HValue* right) @@ -2898,6 +3097,11 @@ class HAdd: public HArithmeticBinaryOperation { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); + static HInstruction* NewHAdd(Zone* zone, + HValue* context, + HValue* left, + HValue* right); + virtual HType CalculateInferredType(); DECLARE_CONCRETE_INSTRUCTION(Add) @@ -2918,6 +3122,11 @@ class HSub: public HArithmeticBinaryOperation { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); + static HInstruction* NewHSub(Zone* zone, + HValue* context, + HValue* left, + HValue* right); + DECLARE_CONCRETE_INSTRUCTION(Sub) protected: @@ -2941,6 +3150,11 @@ class HMul: public HArithmeticBinaryOperation { return !representation().IsTagged(); } + static HInstruction* NewHMul(Zone* zone, + HValue* context, + HValue* left, + HValue* right); + DECLARE_CONCRETE_INSTRUCTION(Mul) protected: @@ -2969,6 +3183,11 @@ class HMod: public HArithmeticBinaryOperation { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); + static HInstruction* NewHMod(Zone* zone, + HValue* context, + HValue* left, + HValue* right); + DECLARE_CONCRETE_INSTRUCTION(Mod) protected: @@ -2988,24 +3207,13 @@ class HDiv: public HArithmeticBinaryOperation { virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); - DECLARE_CONCRETE_INSTRUCTION(Div) - protected: - virtual bool DataEquals(HValue* other) { return true; } + static HInstruction* NewHDiv(Zone* zone, + HValue* context, + HValue* left, + HValue* right); - virtual Range* InferRange(); -}; - - -class HBitAnd: public HBitwiseBinaryOperation { - public: - HBitAnd(HValue* context, HValue* left, HValue* right) - : HBitwiseBinaryOperation(context, left, right) { } - - virtual bool IsCommutative() const { return true; } - virtual HType CalculateInferredType(); - - DECLARE_CONCRETE_INSTRUCTION(BitAnd) + DECLARE_CONCRETE_INSTRUCTION(Div) protected: virtual bool DataEquals(HValue* other) { return true; } @@ -3014,35 +3222,38 @@ class HBitAnd: public HBitwiseBinaryOperation { }; -class HBitXor: public HBitwiseBinaryOperation { +class HBitwise: public HBitwiseBinaryOperation { public: - HBitXor(HValue* context, HValue* left, HValue* right) - : HBitwiseBinaryOperation(context, left, right) { } - - virtual bool IsCommutative() const { return true; } - virtual HType CalculateInferredType(); - - DECLARE_CONCRETE_INSTRUCTION(BitXor) + HBitwise(Token::Value op, HValue* context, HValue* left, HValue* right) + : HBitwiseBinaryOperation(context, left, right), op_(op) { + ASSERT(op == Token::BIT_AND || + op == Token::BIT_OR || + op == Token::BIT_XOR); + } - protected: - virtual bool DataEquals(HValue* other) { return true; } -}; + Token::Value op() const { return op_; } + virtual bool IsCommutative() const { return true; } -class HBitOr: public HBitwiseBinaryOperation { - public: - HBitOr(HValue* context, HValue* left, HValue* right) - : HBitwiseBinaryOperation(context, left, right) { } + virtual HValue* Canonicalize(); - virtual bool IsCommutative() const { return true; } - virtual HType CalculateInferredType(); + static HInstruction* NewHBitwise(Zone* zone, + Token::Value op, + HValue* context, + HValue* left, + HValue* right); - DECLARE_CONCRETE_INSTRUCTION(BitOr) + DECLARE_CONCRETE_INSTRUCTION(Bitwise) protected: - virtual bool DataEquals(HValue* other) { return true; } + virtual bool DataEquals(HValue* other) { + return op() == HBitwise::cast(other)->op(); + } virtual Range* InferRange(); + + private: + Token::Value op_; }; @@ -3052,7 +3263,11 @@ class HShl: public HBitwiseBinaryOperation { : HBitwiseBinaryOperation(context, left, right) { } virtual Range* InferRange(); - virtual HType CalculateInferredType(); + + static HInstruction* NewHShl(Zone* zone, + HValue* context, + HValue* left, + HValue* right); DECLARE_CONCRETE_INSTRUCTION(Shl) @@ -3067,7 +3282,11 @@ class HShr: public HBitwiseBinaryOperation { : HBitwiseBinaryOperation(context, left, right) { } virtual Range* InferRange(); - virtual HType CalculateInferredType(); + + static HInstruction* NewHShr(Zone* zone, + HValue* context, + HValue* left, + HValue* right); DECLARE_CONCRETE_INSTRUCTION(Shr) @@ -3082,7 +3301,11 @@ class HSar: public HBitwiseBinaryOperation { : HBitwiseBinaryOperation(context, left, right) { } virtual Range* InferRange(); - virtual HType CalculateInferredType(); + + static HInstruction* NewHSar(Zone* zone, + HValue* context, + HValue* left, + HValue* right); DECLARE_CONCRETE_INSTRUCTION(Sar) @@ -3094,12 +3317,12 @@ class HSar: public HBitwiseBinaryOperation { class HOsrEntry: public HTemplateInstruction<0> { public: explicit HOsrEntry(int ast_id) : ast_id_(ast_id) { - SetFlag(kChangesOsrEntries); + SetGVNFlag(kChangesOsrEntries); } int ast_id() const { return ast_id_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -3120,7 +3343,7 @@ class HParameter: public HTemplateInstruction<0> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -3152,7 +3375,7 @@ class HCallStub: public HUnaryCall { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3168,7 +3391,7 @@ class HUnknownOSRValue: public HTemplateInstruction<0> { public: HUnknownOSRValue() { set_representation(Representation::Tagged()); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -3178,15 +3401,15 @@ class HUnknownOSRValue: public HTemplateInstruction<0> { class HLoadGlobalCell: public HTemplateInstruction<0> { public: - HLoadGlobalCell(Handle<JSGlobalPropertyCell> cell, bool check_hole_value) - : cell_(cell), check_hole_value_(check_hole_value) { + HLoadGlobalCell(Handle<JSGlobalPropertyCell> cell, PropertyDetails details) + : cell_(cell), details_(details) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnGlobalVars); + SetGVNFlag(kDependsOnGlobalVars); } Handle<JSGlobalPropertyCell> cell() const { return cell_; } - bool check_hole_value() const { return check_hole_value_; } + bool RequiresHoleCheck(); virtual void PrintDataTo(StringStream* stream); @@ -3195,7 +3418,7 @@ class HLoadGlobalCell: public HTemplateInstruction<0> { return reinterpret_cast<intptr_t>(*cell_); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -3209,7 +3432,7 @@ class HLoadGlobalCell: public HTemplateInstruction<0> { private: Handle<JSGlobalPropertyCell> cell_; - bool check_hole_value_; + PropertyDetails details_; }; @@ -3234,7 +3457,7 @@ class HLoadGlobalGeneric: public HTemplateInstruction<2> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3246,21 +3469,33 @@ class HLoadGlobalGeneric: public HTemplateInstruction<2> { }; +inline bool StoringValueNeedsWriteBarrier(HValue* value) { + return !value->type().IsBoolean() + && !value->type().IsSmi() + && !(value->IsConstant() && HConstant::cast(value)->ImmortalImmovable()); +} + + class HStoreGlobalCell: public HUnaryOperation { public: HStoreGlobalCell(HValue* value, Handle<JSGlobalPropertyCell> cell, - bool check_hole_value) + PropertyDetails details) : HUnaryOperation(value), cell_(cell), - check_hole_value_(check_hole_value) { - SetFlag(kChangesGlobalVars); + details_(details) { + SetGVNFlag(kChangesGlobalVars); } Handle<JSGlobalPropertyCell> cell() const { return cell_; } - bool check_hole_value() const { return check_hole_value_; } + bool RequiresHoleCheck() { + return !details_.IsDontDelete() || details_.IsReadOnly(); + } + bool NeedsWriteBarrier() { + return StoringValueNeedsWriteBarrier(value()); + } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual void PrintDataTo(StringStream* stream); @@ -3269,7 +3504,7 @@ class HStoreGlobalCell: public HUnaryOperation { private: Handle<JSGlobalPropertyCell> cell_; - bool check_hole_value_; + PropertyDetails details_; }; @@ -3279,9 +3514,9 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> { HValue* global_object, Handle<Object> name, HValue* value, - bool strict_mode) + StrictModeFlag strict_mode_flag) : name_(name), - strict_mode_(strict_mode) { + strict_mode_flag_(strict_mode_flag) { SetOperandAt(0, context); SetOperandAt(1, global_object); SetOperandAt(2, value); @@ -3293,11 +3528,11 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> { HValue* global_object() { return OperandAt(1); } Handle<Object> name() const { return name_; } HValue* value() { return OperandAt(2); } - bool strict_mode() { return strict_mode_; } + StrictModeFlag strict_mode_flag() { return strict_mode_flag_; } virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3305,22 +3540,56 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> { private: Handle<Object> name_; - bool strict_mode_; + StrictModeFlag strict_mode_flag_; }; class HLoadContextSlot: public HUnaryOperation { public: - HLoadContextSlot(HValue* context , int slot_index) - : HUnaryOperation(context), slot_index_(slot_index) { + enum Mode { + // Perform a normal load of the context slot without checking its value. + kNoCheck, + // Load and check the value of the context slot. Deoptimize if it's the + // hole value. This is used for checking for loading of uninitialized + // harmony bindings where we deoptimize into full-codegen generated code + // which will subsequently throw a reference error. + kCheckDeoptimize, + // Load and check the value of the context slot. Return undefined if it's + // the hole value. This is used for non-harmony const assignments + kCheckReturnUndefined + }; + + HLoadContextSlot(HValue* context, Variable* var) + : HUnaryOperation(context), slot_index_(var->index()) { + ASSERT(var->IsContextSlot()); + switch (var->mode()) { + case LET: + case CONST_HARMONY: + mode_ = kCheckDeoptimize; + break; + case CONST: + mode_ = kCheckReturnUndefined; + break; + default: + mode_ = kNoCheck; + } set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnContextSlots); + SetGVNFlag(kDependsOnContextSlots); } int slot_index() const { return slot_index_; } + Mode mode() const { return mode_; } + + bool DeoptimizesOnHole() { + return mode_ == kCheckDeoptimize; + } + + bool RequiresHoleCheck() { + return mode_ != kNoCheck; + } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3336,34 +3605,50 @@ class HLoadContextSlot: public HUnaryOperation { private: int slot_index_; + Mode mode_; }; -static inline bool StoringValueNeedsWriteBarrier(HValue* value) { - return !value->type().IsBoolean() - && !value->type().IsSmi() - && !(value->IsConstant() && HConstant::cast(value)->InOldSpace()); -} - - class HStoreContextSlot: public HTemplateInstruction<2> { public: - HStoreContextSlot(HValue* context, int slot_index, HValue* value) - : slot_index_(slot_index) { + enum Mode { + // Perform a normal store to the context slot without checking its previous + // value. + kNoCheck, + // Check the previous value of the context slot and deoptimize if it's the + // hole value. This is used for checking for assignments to uninitialized + // harmony bindings where we deoptimize into full-codegen generated code + // which will subsequently throw a reference error. + kCheckDeoptimize, + // Check the previous value and ignore assignment if it isn't a hole value + kCheckIgnoreAssignment + }; + + HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value) + : slot_index_(slot_index), mode_(mode) { SetOperandAt(0, context); SetOperandAt(1, value); - SetFlag(kChangesContextSlots); + SetGVNFlag(kChangesContextSlots); } HValue* context() { return OperandAt(0); } HValue* value() { return OperandAt(1); } int slot_index() const { return slot_index_; } + Mode mode() const { return mode_; } bool NeedsWriteBarrier() { return StoringValueNeedsWriteBarrier(value()); } - virtual Representation RequiredInputRepresentation(int index) const { + bool DeoptimizesOnHole() { + return mode_ == kCheckDeoptimize; + } + + bool RequiresHoleCheck() { + return mode_ != kNoCheck; + } + + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3373,6 +3658,7 @@ class HStoreContextSlot: public HTemplateInstruction<2> { private: int slot_index_; + Mode mode_; }; @@ -3384,11 +3670,11 @@ class HLoadNamedField: public HUnaryOperation { offset_(offset) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); if (is_in_object) { - SetFlag(kDependsOnInobjectFields); + SetGVNFlag(kDependsOnInobjectFields); } else { - SetFlag(kDependsOnBackingStoreFields); + SetGVNFlag(kDependsOnBackingStoreFields); } } @@ -3396,7 +3682,7 @@ class HLoadNamedField: public HUnaryOperation { bool is_in_object() const { return is_in_object_; } int offset() const { return offset_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual void PrintDataTo(StringStream* stream); @@ -3428,7 +3714,7 @@ class HLoadNamedFieldPolymorphic: public HTemplateInstruction<2> { Handle<String> name() { return name_; } bool need_generic() { return need_generic_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3463,7 +3749,7 @@ class HLoadNamedGeneric: public HTemplateInstruction<2> { HValue* object() { return OperandAt(1); } Handle<Object> name() const { return name_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3482,12 +3768,12 @@ class HLoadFunctionPrototype: public HUnaryOperation { : HUnaryOperation(function) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnCalls); + SetGVNFlag(kDependsOnCalls); } HValue* function() { return OperandAt(0); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3504,14 +3790,14 @@ class HLoadKeyedFastElement: public HTemplateInstruction<2> { SetOperandAt(0, obj); SetOperandAt(1, key); set_representation(Representation::Tagged()); - SetFlag(kDependsOnArrayElements); + SetGVNFlag(kDependsOnArrayElements); SetFlag(kUseGVN); } HValue* object() { return OperandAt(0); } HValue* key() { return OperandAt(1); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The key is supposed to be Integer32. return index == 0 ? Representation::Tagged() @@ -3520,7 +3806,7 @@ class HLoadKeyedFastElement: public HTemplateInstruction<2> { virtual void PrintDataTo(StringStream* stream); - bool RequiresHoleCheck() const; + bool RequiresHoleCheck(); DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement) @@ -3535,14 +3821,14 @@ class HLoadKeyedFastDoubleElement: public HTemplateInstruction<2> { SetOperandAt(0, elements); SetOperandAt(1, key); set_representation(Representation::Double()); - SetFlag(kDependsOnDoubleArrayElements); + SetGVNFlag(kDependsOnDoubleArrayElements); SetFlag(kUseGVN); } HValue* elements() { return OperandAt(0); } HValue* key() { return OperandAt(1); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The key is supposed to be Integer32. return index == 0 ? Representation::Tagged() @@ -3551,8 +3837,6 @@ class HLoadKeyedFastDoubleElement: public HTemplateInstruction<2> { virtual void PrintDataTo(StringStream* stream); - bool RequiresHoleCheck() const; - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastDoubleElement) protected: @@ -3574,15 +3858,15 @@ class HLoadKeyedSpecializedArrayElement: public HTemplateInstruction<2> { } else { set_representation(Representation::Integer32()); } - SetFlag(kDependsOnSpecializedArrayElements); + SetGVNFlag(kDependsOnSpecializedArrayElements); // Native code could change the specialized array. - SetFlag(kDependsOnCalls); + SetGVNFlag(kDependsOnCalls); SetFlag(kUseGVN); } virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The key is supposed to be Integer32, but the base pointer // for the element load is a naked pointer. return index == 0 @@ -3594,6 +3878,8 @@ class HLoadKeyedSpecializedArrayElement: public HTemplateInstruction<2> { HValue* key() { return OperandAt(1); } ElementsKind elements_kind() const { return elements_kind_; } + virtual Range* InferRange(); + DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement) protected: @@ -3625,7 +3911,7 @@ class HLoadKeyedGeneric: public HTemplateInstruction<3> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3646,15 +3932,15 @@ class HStoreNamedField: public HTemplateInstruction<2> { SetOperandAt(0, obj); SetOperandAt(1, val); if (is_in_object_) { - SetFlag(kChangesInobjectFields); + SetGVNFlag(kChangesInobjectFields); } else { - SetFlag(kChangesBackingStoreFields); + SetGVNFlag(kChangesBackingStoreFields); } } DECLARE_CONCRETE_INSTRUCTION(StoreNamedField) - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } virtual void PrintDataTo(StringStream* stream); @@ -3686,9 +3972,9 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> { HValue* object, Handle<String> name, HValue* value, - bool strict_mode) + StrictModeFlag strict_mode_flag) : name_(name), - strict_mode_(strict_mode) { + strict_mode_flag_(strict_mode_flag) { SetOperandAt(0, object); SetOperandAt(1, value); SetOperandAt(2, context); @@ -3699,11 +3985,11 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> { HValue* value() { return OperandAt(1); } HValue* context() { return OperandAt(2); } Handle<String> name() { return name_; } - bool strict_mode() { return strict_mode_; } + StrictModeFlag strict_mode_flag() { return strict_mode_flag_; } virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3711,20 +3997,22 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> { private: Handle<String> name_; - bool strict_mode_; + StrictModeFlag strict_mode_flag_; }; class HStoreKeyedFastElement: public HTemplateInstruction<3> { public: - HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val) { + HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val, + ElementsKind elements_kind = FAST_ELEMENTS) + : elements_kind_(elements_kind) { SetOperandAt(0, obj); SetOperandAt(1, key); SetOperandAt(2, val); - SetFlag(kChangesArrayElements); + SetGVNFlag(kChangesArrayElements); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The key is supposed to be Integer32. return index == 1 ? Representation::Integer32() @@ -3734,14 +4022,24 @@ class HStoreKeyedFastElement: public HTemplateInstruction<3> { HValue* object() { return OperandAt(0); } HValue* key() { return OperandAt(1); } HValue* value() { return OperandAt(2); } + bool value_is_smi() { + return elements_kind_ == FAST_SMI_ONLY_ELEMENTS; + } bool NeedsWriteBarrier() { - return StoringValueNeedsWriteBarrier(value()); + if (value_is_smi()) { + return false; + } else { + return StoringValueNeedsWriteBarrier(value()); + } } virtual void PrintDataTo(StringStream* stream); DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement) + + private: + ElementsKind elements_kind_; }; @@ -3753,10 +4051,10 @@ class HStoreKeyedFastDoubleElement: public HTemplateInstruction<3> { SetOperandAt(0, elements); SetOperandAt(1, key); SetOperandAt(2, val); - SetFlag(kChangesDoubleArrayElements); + SetGVNFlag(kChangesDoubleArrayElements); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { if (index == 1) { return Representation::Integer32(); } else if (index == 2) { @@ -3787,7 +4085,7 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { HValue* val, ElementsKind elements_kind) : elements_kind_(elements_kind) { - SetFlag(kChangesSpecializedArrayElements); + SetGVNFlag(kChangesSpecializedArrayElements); SetOperandAt(0, external_elements); SetOperandAt(1, key); SetOperandAt(2, val); @@ -3795,7 +4093,7 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { virtual void PrintDataTo(StringStream* stream); - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { if (index == 0) { return Representation::External(); } else { @@ -3828,8 +4126,8 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> { HValue* object, HValue* key, HValue* value, - bool strict_mode) - : strict_mode_(strict_mode) { + StrictModeFlag strict_mode_flag) + : strict_mode_flag_(strict_mode_flag) { SetOperandAt(0, object); SetOperandAt(1, key); SetOperandAt(2, value); @@ -3841,9 +4139,9 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> { HValue* key() { return OperandAt(1); } HValue* value() { return OperandAt(2); } HValue* context() { return OperandAt(3); } - bool strict_mode() { return strict_mode_; } + StrictModeFlag strict_mode_flag() { return strict_mode_flag_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3852,7 +4150,55 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> { DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric) private: - bool strict_mode_; + StrictModeFlag strict_mode_flag_; +}; + + +class HTransitionElementsKind: public HTemplateInstruction<1> { + public: + HTransitionElementsKind(HValue* object, + Handle<Map> original_map, + Handle<Map> transitioned_map) + : original_map_(original_map), + transitioned_map_(transitioned_map) { + SetOperandAt(0, object); + SetFlag(kUseGVN); + SetGVNFlag(kDependsOnMaps); + SetGVNFlag(kChangesElementsKind); + if (original_map->has_fast_double_elements()) { + SetGVNFlag(kChangesElementsPointer); + SetGVNFlag(kDependsOnElementsPointer); + SetGVNFlag(kDependsOnDoubleArrayElements); + } else if (transitioned_map->has_fast_double_elements()) { + SetGVNFlag(kChangesElementsPointer); + SetGVNFlag(kDependsOnElementsPointer); + SetGVNFlag(kDependsOnArrayElements); + } + set_representation(Representation::Tagged()); + } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + + HValue* object() { return OperandAt(0); } + Handle<Map> original_map() { return original_map_; } + Handle<Map> transitioned_map() { return transitioned_map_; } + + virtual void PrintDataTo(StringStream* stream); + + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind) + + protected: + virtual bool DataEquals(HValue* other) { + HTransitionElementsKind* instr = HTransitionElementsKind::cast(other); + return original_map_.is_identical_to(instr->original_map()) && + transitioned_map_.is_identical_to(instr->transitioned_map()); + } + + private: + Handle<Map> original_map_; + Handle<Map> transitioned_map_; }; @@ -3862,10 +4208,10 @@ class HStringAdd: public HBinaryOperation { : HBinaryOperation(context, left, right) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3888,10 +4234,10 @@ class HStringCharCodeAt: public HTemplateInstruction<3> { SetOperandAt(2, index); set_representation(Representation::Integer32()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { // The index is supposed to be Integer32. return index == 2 ? Representation::Integer32() @@ -3918,15 +4264,16 @@ class HStringCharFromCode: public HTemplateInstruction<2> { HStringCharFromCode(HValue* context, HValue* char_code) { SetOperandAt(0, context); SetOperandAt(1, char_code); - set_representation(Representation::Tagged()); + set_representation(Representation::Tagged()); SetFlag(kUseGVN); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return index == 0 ? Representation::Tagged() : Representation::Integer32(); } + virtual HType CalculateInferredType(); HValue* context() { return OperandAt(0); } HValue* value() { return OperandAt(1); } @@ -3942,10 +4289,10 @@ class HStringLength: public HUnaryOperation { explicit HStringLength(HValue* string) : HUnaryOperation(string) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); - SetFlag(kDependsOnMaps); + SetGVNFlag(kDependsOnMaps); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3985,42 +4332,84 @@ class HMaterializedLiteral: public HTemplateInstruction<V> { class HArrayLiteral: public HMaterializedLiteral<1> { public: HArrayLiteral(HValue* context, - Handle<FixedArray> constant_elements, + Handle<HeapObject> boilerplate_object, int length, int literal_index, int depth) : HMaterializedLiteral<1>(literal_index, depth), length_(length), - constant_elements_(constant_elements) { + boilerplate_object_(boilerplate_object) { SetOperandAt(0, context); } HValue* context() { return OperandAt(0); } - Handle<FixedArray> constant_elements() const { return constant_elements_; } + ElementsKind boilerplate_elements_kind() const { + if (!boilerplate_object_->IsJSObject()) { + return FAST_ELEMENTS; + } + return Handle<JSObject>::cast(boilerplate_object_)->GetElementsKind(); + } + Handle<HeapObject> boilerplate_object() const { return boilerplate_object_; } int length() const { return length_; } bool IsCopyOnWrite() const; - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } + virtual HType CalculateInferredType(); DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral) private: int length_; - Handle<FixedArray> constant_elements_; + Handle<HeapObject> boilerplate_object_; +}; + + +class HObjectLiteralFast: public HMaterializedLiteral<1> { + public: + HObjectLiteralFast(HValue* context, + Handle<JSObject> boilerplate, + int total_size, + int literal_index, + int depth) + : HMaterializedLiteral<1>(literal_index, depth), + boilerplate_(boilerplate), + total_size_(total_size) { + SetOperandAt(0, context); + } + + // Maximum depth and total number of properties for object literal + // graphs to be considered for fast deep-copying. + static const int kMaxObjectLiteralDepth = 3; + static const int kMaxObjectLiteralProperties = 8; + + HValue* context() { return OperandAt(0); } + Handle<JSObject> boilerplate() const { return boilerplate_; } + int total_size() const { return total_size_; } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + virtual HType CalculateInferredType(); + + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast) + + private: + Handle<JSObject> boilerplate_; + int total_size_; }; -class HObjectLiteral: public HMaterializedLiteral<1> { +class HObjectLiteralGeneric: public HMaterializedLiteral<1> { public: - HObjectLiteral(HValue* context, - Handle<FixedArray> constant_properties, - bool fast_elements, - int literal_index, - int depth, - bool has_function) + HObjectLiteralGeneric(HValue* context, + Handle<FixedArray> constant_properties, + bool fast_elements, + int literal_index, + int depth, + bool has_function) : HMaterializedLiteral<1>(literal_index, depth), constant_properties_(constant_properties), fast_elements_(fast_elements), @@ -4035,11 +4424,12 @@ class HObjectLiteral: public HMaterializedLiteral<1> { bool fast_elements() const { return fast_elements_; } bool has_function() const { return has_function_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } + virtual HType CalculateInferredType(); - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric) private: Handle<FixedArray> constant_properties_; @@ -4065,9 +4455,10 @@ class HRegExpLiteral: public HMaterializedLiteral<1> { Handle<String> pattern() { return pattern_; } Handle<String> flags() { return flags_; } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } + virtual HType CalculateInferredType(); DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral) @@ -4089,9 +4480,10 @@ class HFunctionLiteral: public HTemplateInstruction<1> { HValue* context() { return OperandAt(0); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } + virtual HType CalculateInferredType(); DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral) @@ -4115,7 +4507,10 @@ class HTypeof: public HTemplateInstruction<2> { HValue* context() { return OperandAt(0); } HValue* value() { return OperandAt(1); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual HValue* Canonicalize(); + virtual void PrintDataTo(StringStream* stream); + + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -4129,11 +4524,11 @@ class HToFastProperties: public HUnaryOperation { // This instruction is not marked as having side effects, but // changes the map of the input operand. Use it only when creating // object literals. - ASSERT(value->IsObjectLiteral()); + ASSERT(value->IsObjectLiteralGeneric() || value->IsObjectLiteralFast()); set_representation(Representation::Tagged()); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -4147,7 +4542,7 @@ class HValueOf: public HUnaryOperation { set_representation(Representation::Tagged()); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -4163,7 +4558,7 @@ class HDeleteProperty: public HBinaryOperation { SetAllSideEffects(); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -4190,7 +4585,7 @@ class HIn: public HTemplateInstruction<3> { HValue* key() { return OperandAt(1); } HValue* object() { return OperandAt(2); } - virtual Representation RequiredInputRepresentation(int index) const { + virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index c625fba8db..e2505876d8 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -70,7 +70,8 @@ HBasicBlock::HBasicBlock(HGraph* graph) deleted_phis_(4), parent_loop_header_(NULL), is_inline_return_target_(false), - is_deoptimizing_(false) { } + is_deoptimizing_(false), + dominates_loop_successors_(false) { } void HBasicBlock::AttachLoopInformation() { @@ -164,10 +165,10 @@ void HBasicBlock::Finish(HControlInstruction* end) { } -void HBasicBlock::Goto(HBasicBlock* block) { +void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) { if (block->IsInlineReturnTarget()) { AddInstruction(new(zone()) HLeaveInlined); - last_environment_ = last_environment()->outer(); + last_environment_ = last_environment()->DiscardInlined(drop_extra); } AddSimulate(AstNode::kNoNumber); HGoto* instr = new(zone()) HGoto(block); @@ -175,11 +176,13 @@ void HBasicBlock::Goto(HBasicBlock* block) { } -void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { +void HBasicBlock::AddLeaveInlined(HValue* return_value, + HBasicBlock* target, + bool drop_extra) { ASSERT(target->IsInlineReturnTarget()); ASSERT(return_value != NULL); AddInstruction(new(zone()) HLeaveInlined); - last_environment_ = last_environment()->outer(); + last_environment_ = last_environment()->DiscardInlined(drop_extra); last_environment()->Push(return_value); AddSimulate(AstNode::kNoNumber); HGoto* instr = new(zone()) HGoto(target); @@ -313,6 +316,62 @@ void HBasicBlock::AssignCommonDominator(HBasicBlock* other) { } +void HBasicBlock::AssignLoopSuccessorDominators() { + // Mark blocks that dominate all subsequent reachable blocks inside their + // loop. Exploit the fact that blocks are sorted in reverse post order. When + // the loop is visited in increasing block id order, if the number of + // non-loop-exiting successor edges at the dominator_candidate block doesn't + // exceed the number of previously encountered predecessor edges, there is no + // path from the loop header to any block with higher id that doesn't go + // through the dominator_candidate block. In this case, the + // dominator_candidate block is guaranteed to dominate all blocks reachable + // from it with higher ids. + HBasicBlock* last = loop_information()->GetLastBackEdge(); + int outstanding_successors = 1; // one edge from the pre-header + // Header always dominates everything. + MarkAsLoopSuccessorDominator(); + for (int j = block_id(); j <= last->block_id(); ++j) { + HBasicBlock* dominator_candidate = graph_->blocks()->at(j); + for (HPredecessorIterator it(dominator_candidate); !it.Done(); + it.Advance()) { + HBasicBlock* predecessor = it.Current(); + // Don't count back edges. + if (predecessor->block_id() < dominator_candidate->block_id()) { + outstanding_successors--; + } + } + + // If more successors than predecessors have been seen in the loop up to + // now, it's not possible to guarantee that the current block dominates + // all of the blocks with higher IDs. In this case, assume conservatively + // that those paths through loop that don't go through the current block + // contain all of the loop's dependencies. Also be careful to record + // dominator information about the current loop that's being processed, + // and not nested loops, which will be processed when + // AssignLoopSuccessorDominators gets called on their header. + ASSERT(outstanding_successors >= 0); + HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header(); + if (outstanding_successors == 0 && + (parent_loop_header == this && !dominator_candidate->IsLoopHeader())) { + dominator_candidate->MarkAsLoopSuccessorDominator(); + } + HControlInstruction* end = dominator_candidate->end(); + for (HSuccessorIterator it(end); !it.Done(); it.Advance()) { + HBasicBlock* successor = it.Current(); + // Only count successors that remain inside the loop and don't loop back + // to a loop header. + if (successor->block_id() > dominator_candidate->block_id() && + successor->block_id() <= last->block_id()) { + // Backwards edges must land on loop headers. + ASSERT(successor->block_id() > dominator_candidate->block_id() || + successor->IsLoopHeader()); + outstanding_successors++; + } + } + } +} + + int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const { for (int i = 0; i < predecessors_.length(); ++i) { if (predecessors_[i] == predecessor) return i; @@ -422,7 +481,7 @@ class ReachabilityAnalyzer BASE_EMBEDDED { }; -void HGraph::Verify() const { +void HGraph::Verify(bool do_full_verify) const { for (int i = 0; i < blocks_.length(); i++) { HBasicBlock* block = blocks_.at(i); @@ -473,25 +532,27 @@ void HGraph::Verify() const { // Check special property of first block to have no predecessors. ASSERT(blocks_.at(0)->predecessors()->is_empty()); - // Check that the graph is fully connected. - ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); - ASSERT(analyzer.visited_count() == blocks_.length()); + if (do_full_verify) { + // Check that the graph is fully connected. + ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); + ASSERT(analyzer.visited_count() == blocks_.length()); - // Check that entry block dominator is NULL. - ASSERT(entry_block_->dominator() == NULL); + // Check that entry block dominator is NULL. + ASSERT(entry_block_->dominator() == NULL); - // Check dominators. - for (int i = 0; i < blocks_.length(); ++i) { - HBasicBlock* block = blocks_.at(i); - if (block->dominator() == NULL) { - // Only start block may have no dominator assigned to. - ASSERT(i == 0); - } else { - // Assert that block is unreachable if dominator must not be visited. - ReachabilityAnalyzer dominator_analyzer(entry_block_, - blocks_.length(), - block->dominator()); - ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); + // Check dominators. + for (int i = 0; i < blocks_.length(); ++i) { + HBasicBlock* block = blocks_.at(i); + if (block->dominator() == NULL) { + // Only start block may have no dominator assigned to. + ASSERT(i == 0); + } else { + // Assert that block is unreachable if dominator must not be visited. + ReachabilityAnalyzer dominator_analyzer(entry_block_, + blocks_.length(), + block->dominator()); + ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); + } } } } @@ -539,7 +600,7 @@ HConstant* HGraph::GetConstantHole() { HGraphBuilder::HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle) : function_state_(NULL), - initial_function_state_(this, info, oracle), + initial_function_state_(this, info, oracle, false), ast_context_(NULL), break_scope_(NULL), graph_(NULL), @@ -621,28 +682,28 @@ HGraph::HGraph(CompilationInfo* info) Handle<Code> HGraph::Compile(CompilationInfo* info) { int values = GetMaximumValueID(); - if (values > LAllocator::max_initial_value_ids()) { - if (FLAG_trace_bailout) PrintF("Function is too big\n"); + if (values > LUnallocated::kMaxVirtualRegisters) { + if (FLAG_trace_bailout) { + PrintF("Not enough virtual registers for (values).\n"); + } return Handle<Code>::null(); } - LAllocator allocator(values, this); LChunkBuilder builder(info, this, &allocator); LChunk* chunk = builder.Build(); if (chunk == NULL) return Handle<Code>::null(); - if (!FLAG_alloc_lithium) return Handle<Code>::null(); - - allocator.Allocate(chunk); - - if (!FLAG_use_lithium) return Handle<Code>::null(); + if (!allocator.Allocate(chunk)) { + if (FLAG_trace_bailout) { + PrintF("Not enough virtual registers (regalloc).\n"); + } + return Handle<Code>::null(); + } MacroAssembler assembler(info->isolate(), NULL, 0); LCodeGen generator(chunk, &assembler, info); - if (FLAG_eliminate_empty_blocks) { - chunk->MarkEmptyBlocks(); - } + chunk->MarkEmptyBlocks(); if (generator.GenerateCode()) { if (FLAG_trace_codegen) { @@ -728,6 +789,7 @@ void HGraph::Postorder(HBasicBlock* block, Postorder(it.Current(), visited, order, block); } } else { + ASSERT(block->IsFinished()); for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) { Postorder(it.Current(), visited, order, loop_header); } @@ -745,12 +807,14 @@ void HGraph::Postorder(HBasicBlock* block, void HGraph::AssignDominators() { HPhase phase("Assign dominators", this); for (int i = 0; i < blocks_.length(); ++i) { - if (blocks_[i]->IsLoopHeader()) { + HBasicBlock* block = blocks_[i]; + if (block->IsLoopHeader()) { // Only the first predecessor of a loop header is from outside the loop. // All others are back edges, and thus cannot dominate the loop header. - blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first()); + block->AssignCommonDominator(block->predecessors()->first()); + block->AssignLoopSuccessorDominators(); } else { - for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) { + for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) { blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j)); } } @@ -850,7 +914,7 @@ void HGraph::EliminateUnreachablePhis() { } -bool HGraph::CheckPhis() { +bool HGraph::CheckArgumentsPhiUses() { int block_count = blocks_.length(); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { @@ -863,13 +927,11 @@ bool HGraph::CheckPhis() { } -bool HGraph::CollectPhis() { +bool HGraph::CheckConstPhiUses() { int block_count = blocks_.length(); - phi_list_ = new ZoneList<HPhi*>(block_count); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { HPhi* phi = blocks_[i]->phis()->at(j); - phi_list_->Add(phi); // Check for the hole value (from an uninitialized const). for (int k = 0; k < phi->OperandCount(); k++) { if (phi->OperandAt(k) == GetConstantHole()) return false; @@ -880,6 +942,18 @@ bool HGraph::CollectPhis() { } +void HGraph::CollectPhis() { + int block_count = blocks_.length(); + phi_list_ = new ZoneList<HPhi*>(block_count); + for (int i = 0; i < block_count; ++i) { + for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { + HPhi* phi = blocks_[i]->phis()->at(j); + phi_list_->Add(phi); + } + } +} + + void HGraph::InferTypes(ZoneList<HValue*>* worklist) { BitVector in_worklist(GetMaximumValueID()); for (int i = 0; i < worklist->length(); ++i) { @@ -1089,10 +1163,10 @@ HValueMap::HValueMap(Zone* zone, const HValueMap* other) } -void HValueMap::Kill(int flags) { - int depends_flags = HValue::ConvertChangesToDependsFlags(flags); - if ((present_flags_ & depends_flags) == 0) return; - present_flags_ = 0; +void HValueMap::Kill(GVNFlagSet flags) { + GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags); + if (!present_flags_.ContainsAnyOf(depends_flags)) return; + present_flags_.RemoveAll(); for (int i = 0; i < array_size_; ++i) { HValue* value = array_[i].value; if (value != NULL) { @@ -1101,7 +1175,8 @@ void HValueMap::Kill(int flags) { int next; for (int current = array_[i].next; current != kNil; current = next) { next = lists_[current].next; - if ((lists_[current].value->flags() & depends_flags) != 0) { + HValue* value = lists_[current].value; + if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it. count_--; lists_[current].next = free_list_head_; @@ -1110,13 +1185,14 @@ void HValueMap::Kill(int flags) { // Keep it. lists_[current].next = kept; kept = current; - present_flags_ |= lists_[current].value->flags(); + present_flags_.Add(value->gvn_flags()); } } array_[i].next = kept; // Now possibly drop directly indexed element. - if ((array_[i].value->flags() & depends_flags) != 0) { // Drop it. + value = array_[i].value; + if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it. count_--; int head = array_[i].next; if (head == kNil) { @@ -1128,7 +1204,7 @@ void HValueMap::Kill(int flags) { free_list_head_ = head; } } else { - present_flags_ |= array_[i].value->flags(); // Keep it. + present_flags_.Add(value->gvn_flags()); // Keep it. } } } @@ -1330,28 +1406,32 @@ class HGlobalValueNumberer BASE_EMBEDDED { explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info) : graph_(graph), info_(info), + removed_side_effects_(false), block_side_effects_(graph->blocks()->length()), loop_side_effects_(graph->blocks()->length()), visited_on_paths_(graph->zone(), graph->blocks()->length()) { ASSERT(info->isolate()->heap()->allow_allocation(false)); - block_side_effects_.AddBlock(0, graph_->blocks()->length()); - loop_side_effects_.AddBlock(0, graph_->blocks()->length()); + block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length()); + loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length()); } ~HGlobalValueNumberer() { ASSERT(!info_->isolate()->heap()->allow_allocation(true)); } - void Analyze(); + // Returns true if values with side effects are removed. + bool Analyze(); private: - int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator, - HBasicBlock* dominated); + GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock( + HBasicBlock* dominator, + HBasicBlock* dominated); void AnalyzeBlock(HBasicBlock* block, HValueMap* map); void ComputeBlockSideEffects(); void LoopInvariantCodeMotion(); void ProcessLoopBlock(HBasicBlock* block, HBasicBlock* before_loop, - int loop_kills); + GVNFlagSet loop_kills, + GVNFlagSet* accumulated_first_time_depends); bool AllowCodeMotion(); bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); @@ -1361,12 +1441,13 @@ class HGlobalValueNumberer BASE_EMBEDDED { HGraph* graph_; CompilationInfo* info_; + bool removed_side_effects_; // A map of block IDs to their side effects. - ZoneList<int> block_side_effects_; + ZoneList<GVNFlagSet> block_side_effects_; // A map of loop header block IDs to their loop's side effects. - ZoneList<int> loop_side_effects_; + ZoneList<GVNFlagSet> loop_side_effects_; // Used when collecting side effects on paths from dominator to // dominated. @@ -1374,39 +1455,48 @@ class HGlobalValueNumberer BASE_EMBEDDED { }; -void HGlobalValueNumberer::Analyze() { +bool HGlobalValueNumberer::Analyze() { + removed_side_effects_ = false; ComputeBlockSideEffects(); if (FLAG_loop_invariant_code_motion) { LoopInvariantCodeMotion(); } HValueMap* map = new(zone()) HValueMap(); AnalyzeBlock(graph_->entry_block(), map); + return removed_side_effects_; } void HGlobalValueNumberer::ComputeBlockSideEffects() { + // The Analyze phase of GVN can be called multiple times. Clear loop side + // effects before computing them to erase the contents from previous Analyze + // passes. + for (int i = 0; i < loop_side_effects_.length(); ++i) { + loop_side_effects_[i].RemoveAll(); + } for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { // Compute side effects for the block. HBasicBlock* block = graph_->blocks()->at(i); HInstruction* instr = block->first(); int id = block->block_id(); - int side_effects = 0; + GVNFlagSet side_effects; while (instr != NULL) { - side_effects |= instr->ChangesFlags(); + side_effects.Add(instr->ChangesFlags()); instr = instr->next(); } - block_side_effects_[id] |= side_effects; + block_side_effects_[id].Add(side_effects); // Loop headers are part of their loop. if (block->IsLoopHeader()) { - loop_side_effects_[id] |= side_effects; + loop_side_effects_[id].Add(side_effects); } // Propagate loop side effects upwards. if (block->HasParentLoopHeader()) { int header_id = block->parent_loop_header()->block_id(); - loop_side_effects_[header_id] |= - block->IsLoopHeader() ? loop_side_effects_[id] : side_effects; + loop_side_effects_[header_id].Add(block->IsLoopHeader() + ? loop_side_effects_[id] + : side_effects); } } } @@ -1416,50 +1506,94 @@ void HGlobalValueNumberer::LoopInvariantCodeMotion() { for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { HBasicBlock* block = graph_->blocks()->at(i); if (block->IsLoopHeader()) { - int side_effects = loop_side_effects_[block->block_id()]; + GVNFlagSet side_effects = loop_side_effects_[block->block_id()]; TraceGVN("Try loop invariant motion for block B%d effects=0x%x\n", block->block_id(), - side_effects); + side_effects.ToIntegral()); + GVNFlagSet accumulated_first_time_depends; HBasicBlock* last = block->loop_information()->GetLastBackEdge(); for (int j = block->block_id(); j <= last->block_id(); ++j) { - ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects); + ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects, + &accumulated_first_time_depends); } } } } -void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block, - HBasicBlock* loop_header, - int loop_kills) { +void HGlobalValueNumberer::ProcessLoopBlock( + HBasicBlock* block, + HBasicBlock* loop_header, + GVNFlagSet loop_kills, + GVNFlagSet* accumulated_first_time_depends) { HBasicBlock* pre_header = loop_header->predecessors()->at(0); - int depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); + GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n", block->block_id(), - depends_flags); + depends_flags.ToIntegral()); HInstruction* instr = block->first(); while (instr != NULL) { HInstruction* next = instr->next(); - if (instr->CheckFlag(HValue::kUseGVN) && - (instr->flags() & depends_flags) == 0) { - TraceGVN("Checking instruction %d (%s)\n", + bool hoisted = false; + if (instr->CheckFlag(HValue::kUseGVN)) { + TraceGVN("Checking instruction %d (%s) instruction GVN flags 0x%X, " + "loop kills 0x%X\n", instr->id(), - instr->Mnemonic()); - bool inputs_loop_invariant = true; - for (int i = 0; i < instr->OperandCount(); ++i) { - if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { - inputs_loop_invariant = false; - } + instr->Mnemonic(), + instr->gvn_flags().ToIntegral(), + depends_flags.ToIntegral()); + bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); + if (!can_hoist && instr->IsTransitionElementsKind()) { + // It's only possible to hoist one time side effects if there are no + // dependencies on their changes from the loop header to the current + // instruction. + GVNFlagSet converted_changes = + HValue::ConvertChangesToDependsFlags(instr->ChangesFlags()); + TraceGVN("Checking dependencies on one-time instruction %d (%s) " + "converted changes 0x%X, accumulated depends 0x%X\n", + instr->id(), + instr->Mnemonic(), + converted_changes.ToIntegral(), + accumulated_first_time_depends->ToIntegral()); + // It's possible to hoist one-time side effects from the current loop + // loop only if they dominate all of the successor blocks in the same + // loop and there are not any instructions that have Changes/DependsOn + // that intervene between it and the beginning of the loop header. + bool in_nested_loop = block != loop_header && + ((block->parent_loop_header() != loop_header) || + block->IsLoopHeader()); + can_hoist = !in_nested_loop && + block->IsLoopSuccessorDominator() && + !accumulated_first_time_depends->ContainsAnyOf(converted_changes); } - if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { - TraceGVN("Found loop invariant instruction %d\n", instr->id()); - // Move the instruction out of the loop. - instr->Unlink(); - instr->InsertBefore(pre_header->end()); + if (can_hoist) { + bool inputs_loop_invariant = true; + for (int i = 0; i < instr->OperandCount(); ++i) { + if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { + inputs_loop_invariant = false; + } + } + + if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { + TraceGVN("Hoisting loop invariant instruction %d\n", instr->id()); + // Move the instruction out of the loop. + instr->Unlink(); + instr->InsertBefore(pre_header->end()); + if (instr->HasSideEffects()) removed_side_effects_ = true; + hoisted = true; + } } } + if (!hoisted) { + // If an instruction is not hoisted, we have to account for its side + // effects when hoisting later HTransitionElementsKind instructions. + accumulated_first_time_depends->Add(instr->DependsOnFlags()); + GVNFlagSet converted_changes = + HValue::ConvertChangesToDependsFlags(instr->SideEffectFlags()); + accumulated_first_time_depends->Add(converted_changes); + } instr = next; } } @@ -1478,20 +1612,20 @@ bool HGlobalValueNumberer::ShouldMove(HInstruction* instr, } -int HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock( +GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock( HBasicBlock* dominator, HBasicBlock* dominated) { - int side_effects = 0; + GVNFlagSet side_effects; for (int i = 0; i < dominated->predecessors()->length(); ++i) { HBasicBlock* block = dominated->predecessors()->at(i); if (dominator->block_id() < block->block_id() && block->block_id() < dominated->block_id() && visited_on_paths_.Add(block->block_id())) { - side_effects |= block_side_effects_[block->block_id()]; + side_effects.Add(block_side_effects_[block->block_id()]); if (block->IsLoopHeader()) { - side_effects |= loop_side_effects_[block->block_id()]; + side_effects.Add(loop_side_effects_[block->block_id()]); } - side_effects |= CollectSideEffectsOnPathsToDominatedBlock( - dominator, block); + side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock( + dominator, block)); } } return side_effects; @@ -1512,13 +1646,14 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { HInstruction* instr = block->first(); while (instr != NULL) { HInstruction* next = instr->next(); - int flags = instr->ChangesFlags(); - if (flags != 0) { - ASSERT(!instr->CheckFlag(HValue::kUseGVN)); + GVNFlagSet flags = instr->ChangesFlags(); + if (!flags.IsEmpty()) { // Clear all instructions in the map that are affected by side effects. map->Kill(flags); TraceGVN("Instruction %d kills\n", instr->id()); - } else if (instr->CheckFlag(HValue::kUseGVN)) { + } + if (instr->CheckFlag(HValue::kUseGVN)) { + ASSERT(!instr->HasObservableSideEffects()); HValue* other = map->Lookup(instr); if (other != NULL) { ASSERT(instr->Equals(other) && other->Equals(instr)); @@ -1527,6 +1662,7 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { instr->Mnemonic(), other->id(), other->Mnemonic()); + if (instr->HasSideEffects()) removed_side_effects_ = true; instr->DeleteAndReplaceWith(other); } else { map->Add(instr); @@ -1643,7 +1779,7 @@ Representation HInferRepresentation::TryChange(HValue* value) { Representation rep = use->RequiredInputRepresentation(it.index()); if (rep.IsNone()) continue; if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]); - ++use_count[rep.kind()]; + use_count[rep.kind()] += use->LoopWeight(); } int tagged_count = use_count[Representation::kTagged]; int double_count = use_count[Representation::kDouble]; @@ -1656,7 +1792,7 @@ Representation HInferRepresentation::TryChange(HValue* value) { } // Prefer unboxing over boxing, the latter is more expensive. - if (tagged_count > non_tagged_count) Representation::None(); + if (tagged_count > non_tagged_count) return Representation::None(); // Prefer Integer32 over Double, if possible. if (int32_count > 0 && value->IsConvertibleToInteger()) { @@ -1851,7 +1987,7 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value, } if (new_value == NULL) { - new_value = new(zone()) HChange(value, value->representation(), to, + new_value = new(zone()) HChange(value, to, is_truncating, deoptimize_on_undefined); } @@ -1996,11 +2132,13 @@ void HGraph::ComputeMinusZeroChecks() { // a (possibly inlined) function. FunctionState::FunctionState(HGraphBuilder* owner, CompilationInfo* info, - TypeFeedbackOracle* oracle) + TypeFeedbackOracle* oracle, + bool drop_extra) : owner_(owner), compilation_info_(info), oracle_(oracle), call_context_(NULL), + drop_extra_(drop_extra), function_return_(NULL), test_context_(NULL), outer_(owner->function_state()) { @@ -2043,6 +2181,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) for_typeof_(false) { owner->set_ast_context(this); // Push. #ifdef DEBUG + ASSERT(!owner->environment()->is_arguments_adaptor()); original_length_ = owner->environment()->length(); #endif } @@ -2056,14 +2195,16 @@ AstContext::~AstContext() { EffectContext::~EffectContext() { ASSERT(owner()->HasStackOverflow() || owner()->current_block() == NULL || - owner()->environment()->length() == original_length_); + (owner()->environment()->length() == original_length_ && + !owner()->environment()->is_arguments_adaptor())); } ValueContext::~ValueContext() { ASSERT(owner()->HasStackOverflow() || owner()->current_block() == NULL || - owner()->environment()->length() == original_length_ + 1); + (owner()->environment()->length() == original_length_ + 1 && + !owner()->environment()->is_arguments_adaptor())); } @@ -2090,12 +2231,12 @@ void TestContext::ReturnValue(HValue* value) { void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) { ASSERT(!instr->IsControlInstruction()); owner()->AddInstruction(instr); - if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id); } void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); instr->SetSuccessorAt(0, empty_true); @@ -2113,12 +2254,12 @@ void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { } owner()->AddInstruction(instr); owner()->Push(instr); - if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id); } void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) { return owner()->Bailout("bad value context for arguments object value"); } @@ -2143,7 +2284,7 @@ void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { builder->AddInstruction(instr); // We expect a simulate after every expression with side effects, though // this one isn't actually needed (and wouldn't work if it were targeted). - if (instr->HasSideEffects()) { + if (instr->HasObservableSideEffects()) { builder->Push(instr); builder->AddSimulate(ast_id); builder->Pop(); @@ -2153,14 +2294,14 @@ void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); instr->SetSuccessorAt(0, empty_true); instr->SetSuccessorAt(1, empty_false); owner()->current_block()->Finish(instr); - empty_true->Goto(if_true()); - empty_false->Goto(if_false()); + empty_true->Goto(if_true(), owner()->function_state()->drop_extra()); + empty_false->Goto(if_false(), owner()->function_state()->drop_extra()); owner()->set_current_block(NULL); } @@ -2181,8 +2322,8 @@ void TestContext::BuildBranch(HValue* value) { HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected); builder->current_block()->Finish(test); - empty_true->Goto(if_true()); - empty_false->Goto(if_false()); + empty_true->Goto(if_true(), owner()->function_state()->drop_extra()); + empty_false->Goto(if_false(), owner()->function_state()->drop_extra()); builder->set_current_block(NULL); } @@ -2276,7 +2417,7 @@ HGraph* HGraphBuilder::CreateGraph() { Bailout("function with illegal redeclaration"); return NULL; } - SetupScope(scope); + SetUpScope(scope); // Add an edge to the body entry. This is warty: the graph's start // environment will be used by the Lithium translation as the initial @@ -2302,7 +2443,7 @@ HGraph* HGraphBuilder::CreateGraph() { // Handle implicit declaration of the function name in named function // expressions before other declarations. if (scope->is_function_scope() && scope->function() != NULL) { - HandleDeclaration(scope->function(), Variable::CONST, NULL); + HandleVariableDeclaration(scope->function(), CONST, NULL); } VisitDeclarations(scope->declarations()); AddSimulate(AstNode::kDeclarationsId); @@ -2323,17 +2464,24 @@ HGraph* HGraphBuilder::CreateGraph() { graph()->OrderBlocks(); graph()->AssignDominators(); + +#ifdef DEBUG + // Do a full verify after building the graph and computing dominators. + graph()->Verify(true); +#endif + graph()->PropagateDeoptimizingMark(); - graph()->EliminateRedundantPhis(); - if (!graph()->CheckPhis()) { - Bailout("Unsupported phi use of arguments object"); + if (!graph()->CheckConstPhiUses()) { + Bailout("Unsupported phi use of const variable"); return NULL; } - if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); - if (!graph()->CollectPhis()) { - Bailout("Unsupported phi use of uninitialized constant"); + graph()->EliminateRedundantPhis(); + if (!graph()->CheckArgumentsPhiUses()) { + Bailout("Unsupported phi use of arguments"); return NULL; } + if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); + graph()->CollectPhis(); HInferRepresentation rep(graph()); rep.Analyze(); @@ -2348,7 +2496,14 @@ HGraph* HGraphBuilder::CreateGraph() { if (FLAG_use_gvn) { HPhase phase("Global value numbering", graph()); HGlobalValueNumberer gvn(graph(), info()); - gvn.Analyze(); + bool removed_side_effects = gvn.Analyze(); + // Trigger a second analysis pass to further eliminate duplicate values that + // could only be discovered by removing side-effect-generating instructions + // during the first pass. + if (FLAG_smi_only_arrays && removed_side_effects) { + removed_side_effects = gvn.Analyze(); + ASSERT(!removed_side_effects); + } } if (FLAG_use_range) { @@ -2427,7 +2582,7 @@ HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) { } -void HGraphBuilder::SetupScope(Scope* scope) { +void HGraphBuilder::SetUpScope(Scope* scope) { HConstant* undefined_constant = new(zone()) HConstant( isolate()->factory()->undefined_value(), Representation::Tagged()); AddInstruction(undefined_constant); @@ -2636,12 +2791,14 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { test->if_false()); } else if (context->IsEffect()) { CHECK_ALIVE(VisitForEffect(stmt->expression())); - current_block()->Goto(function_return()); + current_block()->Goto(function_return(), function_state()->drop_extra()); } else { ASSERT(context->IsValue()); CHECK_ALIVE(VisitForValue(stmt->expression())); HValue* return_value = environment()->Pop(); - current_block()->AddLeaveInlined(return_value, function_return()); + current_block()->AddLeaveInlined(return_value, + function_return(), + function_state()->drop_extra()); } set_current_block(NULL); } @@ -2669,43 +2826,98 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { return Bailout("SwitchStatement: too many clauses"); } + HValue* context = environment()->LookupContext(); + CHECK_ALIVE(VisitForValue(stmt->tag())); AddSimulate(stmt->EntryId()); HValue* tag_value = Pop(); HBasicBlock* first_test_block = current_block(); - // 1. Build all the tests, with dangling true branches. Unconditionally - // deoptimize if we encounter a non-smi comparison. + SwitchType switch_type = UNKNOWN_SWITCH; + + // 1. Extract clause type for (int i = 0; i < clause_count; ++i) { CaseClause* clause = clauses->at(i); if (clause->is_default()) continue; - if (!clause->label()->IsSmiLiteral()) { - return Bailout("SwitchStatement: non-literal switch label"); + + if (switch_type == UNKNOWN_SWITCH) { + if (clause->label()->IsSmiLiteral()) { + switch_type = SMI_SWITCH; + } else if (clause->label()->IsStringLiteral()) { + switch_type = STRING_SWITCH; + } else { + return Bailout("SwitchStatement: non-literal switch label"); + } + } else if ((switch_type == STRING_SWITCH && + !clause->label()->IsStringLiteral()) || + (switch_type == SMI_SWITCH && + !clause->label()->IsSmiLiteral())) { + return Bailout("SwitchStatemnt: mixed label types are not supported"); } + } - // Unconditionally deoptimize on the first non-smi compare. - clause->RecordTypeFeedback(oracle()); - if (!clause->IsSmiCompare()) { - // Finish with deoptimize and add uses of enviroment values to - // account for invisible uses. - current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); - set_current_block(NULL); - break; + HUnaryControlInstruction* string_check = NULL; + HBasicBlock* not_string_block = NULL; + + // Test switch's tag value if all clauses are string literals + if (switch_type == STRING_SWITCH) { + string_check = new(zone()) HIsStringAndBranch(tag_value); + first_test_block = graph()->CreateBasicBlock(); + not_string_block = graph()->CreateBasicBlock(); + + string_check->SetSuccessorAt(0, first_test_block); + string_check->SetSuccessorAt(1, not_string_block); + current_block()->Finish(string_check); + + set_current_block(first_test_block); + } + + // 2. Build all the tests, with dangling true branches + int default_id = AstNode::kNoNumber; + for (int i = 0; i < clause_count; ++i) { + CaseClause* clause = clauses->at(i); + if (clause->is_default()) { + default_id = clause->EntryId(); + continue; + } + if (switch_type == SMI_SWITCH) { + clause->RecordTypeFeedback(oracle()); } - // Otherwise generate a compare and branch. + // Generate a compare and branch. CHECK_ALIVE(VisitForValue(clause->label())); HValue* label_value = Pop(); - HCompareIDAndBranch* compare = - new(zone()) HCompareIDAndBranch(tag_value, - label_value, - Token::EQ_STRICT); - compare->SetInputRepresentation(Representation::Integer32()); - HBasicBlock* body_block = graph()->CreateBasicBlock(); + HBasicBlock* next_test_block = graph()->CreateBasicBlock(); + HBasicBlock* body_block = graph()->CreateBasicBlock(); + + HControlInstruction* compare; + + if (switch_type == SMI_SWITCH) { + if (!clause->IsSmiCompare()) { + // Finish with deoptimize and add uses of enviroment values to + // account for invisible uses. + current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); + set_current_block(NULL); + break; + } + + HCompareIDAndBranch* compare_ = + new(zone()) HCompareIDAndBranch(tag_value, + label_value, + Token::EQ_STRICT); + compare_->SetInputRepresentation(Representation::Integer32()); + compare = compare_; + } else { + compare = new(zone()) HStringCompareAndBranch(context, tag_value, + label_value, + Token::EQ_STRICT); + } + compare->SetSuccessorAt(0, body_block); compare->SetSuccessorAt(1, next_test_block); current_block()->Finish(compare); + set_current_block(next_test_block); } @@ -2713,10 +2925,18 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // exit. This block is NULL if we deoptimized. HBasicBlock* last_block = current_block(); - // 2. Loop over the clauses and the linked list of tests in lockstep, + if (not_string_block != NULL) { + int join_id = (default_id != AstNode::kNoNumber) + ? default_id + : stmt->ExitId(); + last_block = CreateJoin(last_block, not_string_block, join_id); + } + + // 3. Loop over the clauses and the linked list of tests in lockstep, // translating the clause bodies. HBasicBlock* curr_test_block = first_test_block; HBasicBlock* fall_through_block = NULL; + BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); for (int i = 0; i < clause_count; ++i) { @@ -3096,7 +3316,7 @@ HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty( } Handle<GlobalObject> global(info()->global_object()); global->Lookup(*var->name(), lookup); - if (!lookup->IsProperty() || + if (!lookup->IsFound() || lookup->type() != NORMAL || (is_store && lookup->IsReadOnly()) || lookup->holder() != *global) { @@ -3125,12 +3345,22 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); Variable* variable = expr->var(); - if (variable->mode() == Variable::LET) { - return Bailout("reference to let variable"); - } switch (variable->location()) { case Variable::UNALLOCATED: { - LookupResult lookup; + if (variable->mode() == LET || variable->mode() == CONST_HARMONY) { + return Bailout("reference to global harmony declared variable"); + } + // Handle known global constants like 'undefined' specially to avoid a + // load from a global cell for them. + Handle<Object> constant_value = + isolate()->factory()->GlobalConstantFor(variable->name()); + if (!constant_value.is_null()) { + HConstant* instr = + new(zone()) HConstant(constant_value, Representation::Tagged()); + return ast_context()->ReturnInstruction(instr, expr->id()); + } + + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false); @@ -3142,8 +3372,8 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { if (type == kUseCell) { Handle<GlobalObject> global(info()->global_object()); Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); - HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole); + HLoadGlobalCell* instr = + new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails()); return ast_context()->ReturnInstruction(instr, expr->id()); } else { HValue* context = environment()->LookupContext(); @@ -3162,20 +3392,18 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { case Variable::PARAMETER: case Variable::LOCAL: { HValue* value = environment()->Lookup(variable); - if (variable->mode() == Variable::CONST && - value == graph()->GetConstantHole()) { - return Bailout("reference to uninitialized const variable"); + if (value == graph()->GetConstantHole()) { + ASSERT(variable->mode() == CONST || + variable->mode() == CONST_HARMONY || + variable->mode() == LET); + return Bailout("reference to uninitialized variable"); } return ast_context()->ReturnValue(value); } case Variable::CONTEXT: { - if (variable->mode() == Variable::CONST) { - return Bailout("reference to const context slot"); - } HValue* context = BuildContextChainWalk(variable); - HLoadContextSlot* instr = - new(zone()) HLoadContextSlot(context, variable->index()); + HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable); return ast_context()->ReturnInstruction(instr, expr->id()); } @@ -3209,18 +3437,78 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { } +// Determines whether the given object literal boilerplate satisfies all +// limits to be considered for fast deep-copying and computes the total +// size of all objects that are part of the graph. +static bool IsFastObjectLiteral(Handle<JSObject> boilerplate, + int max_depth, + int* max_properties, + int* total_size) { + if (max_depth <= 0) return false; + + Handle<FixedArrayBase> elements(boilerplate->elements()); + if (elements->length() > 0 && + elements->map() != HEAP->fixed_cow_array_map()) { + return false; + } + + Handle<FixedArray> properties(boilerplate->properties()); + if (properties->length() > 0) { + return false; + } else { + int nof = boilerplate->map()->inobject_properties(); + for (int i = 0; i < nof; i++) { + if ((*max_properties)-- <= 0) return false; + Handle<Object> value(boilerplate->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + if (!IsFastObjectLiteral(value_object, + max_depth - 1, + max_properties, + total_size)) { + return false; + } + } + } + } + + *total_size += boilerplate->map()->instance_size(); + return true; +} + + void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + Handle<JSFunction> closure = function_state()->compilation_info()->closure(); HValue* context = environment()->LookupContext(); - HObjectLiteral* literal = - new(zone()) HObjectLiteral(context, - expr->constant_properties(), - expr->fast_elements(), - expr->literal_index(), - expr->depth(), - expr->has_function()); + HInstruction* literal; + + // Check whether to use fast or slow deep-copying for boilerplate. + int total_size = 0; + int max_properties = HObjectLiteralFast::kMaxObjectLiteralProperties; + Handle<Object> boilerplate(closure->literals()->get(expr->literal_index())); + if (boilerplate->IsJSObject() && + IsFastObjectLiteral(Handle<JSObject>::cast(boilerplate), + HObjectLiteralFast::kMaxObjectLiteralDepth, + &max_properties, + &total_size)) { + Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate); + literal = new(zone()) HObjectLiteralFast(context, + boilerplate_object, + total_size, + expr->literal_index(), + expr->depth()); + } else { + literal = new(zone()) HObjectLiteralGeneric(context, + expr->constant_properties(), + expr->fast_elements(), + expr->literal_index(), + expr->depth(), + expr->has_function()); + } + // The object is expected in the bailout environment during computation // of the property values and is the value of the entire expression. PushAndAdd(literal); @@ -3250,7 +3538,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { literal, name, value, - function_strict_mode()); + function_strict_mode_flag()); AddInstruction(store); AddSimulate(key->id()); } else { @@ -3290,11 +3578,33 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { int length = subexprs->length(); HValue* context = environment()->LookupContext(); - HArrayLiteral* literal = new(zone()) HArrayLiteral(context, - expr->constant_elements(), - length, - expr->literal_index(), - expr->depth()); + Handle<FixedArray> literals(environment()->closure()->literals()); + Handle<Object> raw_boilerplate(literals->get(expr->literal_index())); + + if (raw_boilerplate->IsUndefined()) { + raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate( + isolate(), literals, expr->constant_elements()); + if (raw_boilerplate.is_null()) { + return Bailout("array boilerplate creation failed"); + } + literals->set(expr->literal_index(), *raw_boilerplate); + if (JSObject::cast(*raw_boilerplate)->elements()->map() == + isolate()->heap()->fixed_cow_array_map()) { + isolate()->counters()->cow_arrays_created_runtime()->Increment(); + } + } + + Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate); + ElementsKind boilerplate_elements_kind = + Handle<JSObject>::cast(boilerplate)->GetElementsKind(); + + HArrayLiteral* literal = new(zone()) HArrayLiteral( + context, + boilerplate, + length, + expr->literal_index(), + expr->depth()); + // The array is expected in the bailout environment during computation // of the property values and is the value of the entire expression. PushAndAdd(literal); @@ -3311,16 +3621,35 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { HValue* value = Pop(); if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal"); - // Load the elements array before the first store. - if (elements == NULL) { - elements = new(zone()) HLoadElements(literal); - AddInstruction(elements); - } + elements = new(zone()) HLoadElements(literal); + AddInstruction(elements); HValue* key = AddInstruction( new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)), Representation::Integer32())); - AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); + + switch (boilerplate_elements_kind) { + case FAST_SMI_ONLY_ELEMENTS: + // Smi-only arrays need a smi check. + AddInstruction(new(zone()) HCheckSmi(value)); + // Fall through. + case FAST_ELEMENTS: + AddInstruction(new(zone()) HStoreKeyedFastElement( + elements, + key, + value, + boilerplate_elements_kind)); + break; + case FAST_DOUBLE_ELEMENTS: + AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements, + key, + value)); + break; + default: + UNREACHABLE(); + break; + } + AddSimulate(expr->GetIdForElement(i)); } return ast_context()->ReturnValue(Pop()); @@ -3332,7 +3661,7 @@ static bool ComputeStoredField(Handle<Map> type, Handle<String> name, LookupResult* lookup) { type->LookupInDescriptors(NULL, *name, lookup); - if (!lookup->IsPropertyOrTransition()) return false; + if (!lookup->IsFound()) return false; if (lookup->type() == FIELD) return true; return (lookup->type() == MAP_TRANSITION) && (type->unused_property_fields() > 0); @@ -3360,7 +3689,8 @@ HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object, bool smi_and_map_check) { if (smi_and_map_check) { AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(new(zone()) HCheckMap(object, type)); + AddInstruction(new(zone()) HCheckMap(object, type, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); } int index = ComputeStoredFieldIndex(type, name, lookup); @@ -3380,7 +3710,7 @@ HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object, instr->set_transition(transition); // TODO(fschneider): Record the new map type of the object in the IR to // enable elimination of redundant checks after the transition store. - instr->SetFlag(HValue::kChangesMaps); + instr->SetGVNFlag(kChangesMaps); } return instr; } @@ -3395,7 +3725,7 @@ HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object, object, name, value, - function_strict_mode()); + function_strict_mode_flag()); } @@ -3409,7 +3739,7 @@ HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object, Handle<String> name = Handle<String>::cast(key->handle()); ASSERT(!name.is_null()); - LookupResult lookup; + LookupResult lookup(isolate()); SmallMapList* types = expr->GetReceiverTypes(); bool is_monomorphic = expr->IsMonomorphic() && ComputeStoredField(types->first(), name, &lookup); @@ -3433,7 +3763,7 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, HBasicBlock* join = NULL; for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) { Handle<Map> map = types->at(i); - LookupResult lookup; + LookupResult lookup(isolate()); if (ComputeStoredField(map, name, &lookup)) { if (count == 0) { AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once. @@ -3476,7 +3806,7 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, // The HSimulate for the store should not see the stored value in // effect contexts (it is not materialized at expr->id() in the // unoptimized code). - if (instr->HasSideEffects()) { + if (instr->HasObservableSideEffects()) { if (ast_context()->IsEffect()) { AddSimulate(expr->id()); } else { @@ -3516,7 +3846,7 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { ASSERT(!name.is_null()); SmallMapList* types = expr->GetReceiverTypes(); - LookupResult lookup; + LookupResult lookup(isolate()); if (expr->IsMonomorphic()) { instr = BuildStoreNamed(object, value, expr); @@ -3549,7 +3879,7 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Push(value); instr->set_position(expr->position()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); return ast_context()->ReturnValue(Pop()); } @@ -3561,16 +3891,16 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, HValue* value, int position, int ast_id) { - LookupResult lookup; + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true); if (type == kUseCell) { - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); Handle<GlobalObject> global(info()->global_object()); Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); - HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole); + HInstruction* instr = + new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails()); instr->set_position(position); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) AddSimulate(ast_id); } else { HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); @@ -3580,11 +3910,11 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, global_object, var->name(), value, - function_strict_mode()); + function_strict_mode_flag()); instr->set_position(position); AddInstruction(instr); - ASSERT(instr->HasSideEffects()); - if (instr->HasSideEffects()) AddSimulate(ast_id); + ASSERT(instr->HasObservableSideEffects()); + if (instr->HasObservableSideEffects()) AddSimulate(ast_id); } } @@ -3601,8 +3931,8 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST || var->mode() == Variable::LET) { - return Bailout("unsupported let or const compound assignment"); + if (var->mode() == LET) { + return Bailout("unsupported let compound assignment"); } CHECK_ALIVE(VisitForValue(operation)); @@ -3617,6 +3947,9 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { case Variable::PARAMETER: case Variable::LOCAL: + if (var->mode() == CONST) { + return Bailout("unsupported const compound assignment"); + } Bind(var, Top()); break; @@ -3637,11 +3970,29 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { } } + HStoreContextSlot::Mode mode; + + switch (var->mode()) { + case LET: + mode = HStoreContextSlot::kCheckDeoptimize; + break; + case CONST: + return ast_context()->ReturnValue(Pop()); + case CONST_HARMONY: + // This case is checked statically so no need to + // perform checks here + UNREACHABLE(); + default: + mode = HStoreContextSlot::kNoCheck; + } + HValue* context = BuildContextChainWalk(var); HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, var->index(), Top()); + new(zone()) HStoreContextSlot(context, var->index(), mode, Top()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } break; } @@ -3667,7 +4018,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId()); + if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId()); CHECK_ALIVE(VisitForValue(expr->value())); HValue* right = Pop(); @@ -3675,14 +4026,14 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* instr = BuildBinaryOperation(operation, left, right); PushAndAdd(instr); - if (instr->HasSideEffects()) AddSimulate(operation->id()); + if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); HInstruction* store = BuildStoreNamed(obj, instr, prop); AddInstruction(store); // Drop the simulated receiver and value. Return the value. Drop(2); Push(instr); - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); return ast_context()->ReturnValue(Pop()); } else { @@ -3707,7 +4058,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* instr = BuildBinaryOperation(operation, left, right); PushAndAdd(instr); - if (instr->HasSideEffects()) AddSimulate(operation->id()); + if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); expr->RecordTypeFeedback(oracle()); HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(), @@ -3746,19 +4097,23 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { HandlePropertyAssignment(expr); } else if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST) { + + if (var->mode() == CONST) { if (expr->op() != Token::INIT_CONST) { - return Bailout("non-initializer assignment to const"); + CHECK_ALIVE(VisitForValue(expr->value())); + return ast_context()->ReturnValue(Pop()); + } + + if (var->IsStackAllocated()) { + // We insert a use of the old value to detect unsupported uses of const + // variables (e.g. initialization inside a loop). + HValue* old_value = environment()->Lookup(var); + AddInstruction(new HUseConst(old_value)); } - if (!var->IsStackAllocated()) { - return Bailout("assignment to const context slot"); + } else if (var->mode() == CONST_HARMONY) { + if (expr->op() != Token::INIT_CONST_HARMONY) { + return Bailout("non-initializer assignment to const"); } - // We insert a use of the old value to detect unsupported uses of const - // variables (e.g. initialization inside a loop). - HValue* old_value = environment()->Lookup(var); - AddInstruction(new HUseConst(old_value)); - } else if (var->mode() == Variable::LET) { - return Bailout("unsupported assignment to let"); } if (proxy->IsArguments()) return Bailout("assignment to arguments"); @@ -3775,6 +4130,14 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { case Variable::PARAMETER: case Variable::LOCAL: { + // Perform an initialization check for let declared variables + // or parameters. + if (var->mode() == LET && expr->op() == Token::ASSIGN) { + HValue* env_value = environment()->Lookup(var); + if (env_value == graph()->GetConstantHole()) { + return Bailout("assignment to let variable before initialization"); + } + } // We do not allow the arguments object to occur in a context where it // may escape, but assignments to stack-allocated locals are // permitted. @@ -3785,7 +4148,6 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { } case Variable::CONTEXT: { - ASSERT(var->mode() != Variable::CONST); // Bail out if we try to mutate a parameter value in a function using // the arguments object. We do not (yet) correctly handle the // arguments property of the function. @@ -3801,11 +4163,38 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { } CHECK_ALIVE(VisitForValue(expr->value())); + HStoreContextSlot::Mode mode; + if (expr->op() == Token::ASSIGN) { + switch (var->mode()) { + case LET: + mode = HStoreContextSlot::kCheckDeoptimize; + break; + case CONST: + return ast_context()->ReturnValue(Pop()); + case CONST_HARMONY: + // This case is checked statically so no need to + // perform checks here + UNREACHABLE(); + default: + mode = HStoreContextSlot::kNoCheck; + } + } else if (expr->op() == Token::INIT_VAR || + expr->op() == Token::INIT_LET || + expr->op() == Token::INIT_CONST_HARMONY) { + mode = HStoreContextSlot::kNoCheck; + } else { + ASSERT(expr->op() == Token::INIT_CONST); + + mode = HStoreContextSlot::kCheckIgnoreAssignment; + } + HValue* context = BuildContextChainWalk(var); - HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, var->index(), Top()); + HStoreContextSlot* instr = new(zone()) HStoreContextSlot( + context, var->index(), mode, Top()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } return ast_context()->ReturnValue(Pop()); } @@ -3846,7 +4235,8 @@ HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object, bool smi_and_map_check) { if (smi_and_map_check) { AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(new(zone()) HCheckMap(object, type)); + AddInstruction(new(zone()) HCheckMap(object, type, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); } int index = lookup->GetLocalFieldIndexFromMap(*type); @@ -3876,17 +4266,18 @@ HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj, Property* expr, Handle<Map> map, Handle<String> name) { - LookupResult lookup; + LookupResult lookup(isolate()); map->LookupInDescriptors(NULL, *name, &lookup); - if (lookup.IsProperty() && lookup.type() == FIELD) { + if (lookup.IsFound() && lookup.type() == FIELD) { return BuildLoadNamedField(obj, expr, map, &lookup, true); - } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) { + } else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) { AddInstruction(new(zone()) HCheckNonSmi(obj)); - AddInstruction(new(zone()) HCheckMap(obj, map)); + AddInstruction(new(zone()) HCheckMap(obj, map, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map)); return new(zone()) HConstant(function, Representation::Tagged()); } else { @@ -3931,6 +4322,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: break; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -3947,24 +4339,48 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( } +HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, + HValue* checked_key, + HValue* val, + ElementsKind elements_kind, + bool is_store) { + if (is_store) { + ASSERT(val != NULL); + switch (elements_kind) { + case FAST_DOUBLE_ELEMENTS: + return new(zone()) HStoreKeyedFastDoubleElement( + elements, checked_key, val); + case FAST_SMI_ONLY_ELEMENTS: + // Smi-only arrays need a smi check. + AddInstruction(new(zone()) HCheckSmi(val)); + // Fall through. + case FAST_ELEMENTS: + return new(zone()) HStoreKeyedFastElement( + elements, checked_key, val, elements_kind); + default: + UNREACHABLE(); + return NULL; + } + } + // It's an element load (!is_store). + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); + } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. + return new(zone()) HLoadKeyedFastElement(elements, checked_key); + } +} + + HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, - Expression* expr, + Handle<Map> map, bool is_store) { - ASSERT(expr->IsMonomorphic()); - Handle<Map> map = expr->GetMonomorphicReceiverType(); - if (!map->has_fast_elements() && - !map->has_fast_double_elements() && - !map->has_external_array_elements()) { - return is_store ? BuildStoreKeyedGeneric(object, key, val) - : BuildLoadKeyedGeneric(object, key); - } - AddInstruction(new(zone()) HCheckNonSmi(object)); HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map)); + bool fast_smi_only_elements = map->has_fast_smi_only_elements(); + bool fast_elements = map->has_fast_elements(); HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); - bool fast_double_elements = map->has_fast_double_elements(); - if (is_store && map->has_fast_elements()) { + if (is_store && (fast_elements || fast_smi_only_elements)) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map())); } @@ -3979,28 +4395,17 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, return BuildExternalArrayElementAccess(external_elements, checked_key, val, map->elements_kind(), is_store); } - ASSERT(map->has_fast_elements() || fast_double_elements); + ASSERT(fast_smi_only_elements || + fast_elements || + map->has_fast_double_elements()); if (map->instance_type() == JS_ARRAY_TYPE) { length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck)); } else { length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); } checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - return new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val); - } else { - return new(zone()) HStoreKeyedFastElement(elements, checked_key, val); - } - } else { - if (fast_double_elements) { - return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); - } else { - return new(zone()) HLoadKeyedFastElement(elements, checked_key); - } - } + return BuildFastElementAccess(elements, checked_key, val, + map->elements_kind(), is_store); } @@ -4014,7 +4419,6 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, bool* has_side_effects) { *has_side_effects = false; AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(object)); SmallMapList* maps = prop->GetReceiverTypes(); bool todo_external_array = false; @@ -4024,15 +4428,61 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, type_todo[i] = false; } + // Elements_kind transition support. + MapHandleList transition_target(maps->length()); + // Collect possible transition targets. + MapHandleList possible_transitioned_maps(maps->length()); + for (int i = 0; i < maps->length(); ++i) { + Handle<Map> map = maps->at(i); + ElementsKind elements_kind = map->elements_kind(); + if (elements_kind == FAST_DOUBLE_ELEMENTS || + elements_kind == FAST_ELEMENTS) { + possible_transitioned_maps.Add(map); + } + } + // Get transition target for each map (NULL == no transition). + for (int i = 0; i < maps->length(); ++i) { + Handle<Map> map = maps->at(i); + Handle<Map> transitioned_map = + map->FindTransitionedMap(&possible_transitioned_maps); + transition_target.Add(transitioned_map); + } + + int num_untransitionable_maps = 0; + Handle<Map> untransitionable_map; for (int i = 0; i < maps->length(); ++i) { - ASSERT(maps->at(i)->IsMap()); - type_todo[maps->at(i)->elements_kind()] = true; - if (maps->at(i)->elements_kind() - >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { - todo_external_array = true; + Handle<Map> map = maps->at(i); + ASSERT(map->IsMap()); + if (!transition_target.at(i).is_null()) { + object = AddInstruction(new(zone()) HTransitionElementsKind( + object, map, transition_target.at(i))); + } else { + type_todo[map->elements_kind()] = true; + if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { + todo_external_array = true; + } + num_untransitionable_maps++; + untransitionable_map = map; } } + // If only one map is left after transitioning, handle this case + // monomorphically. + if (num_untransitionable_maps == 1) { + HInstruction* instr = NULL; + if (untransitionable_map->has_slow_elements_kind()) { + instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val) + : BuildLoadKeyedGeneric(object, key)); + } else { + instr = AddInstruction(BuildMonomorphicElementAccess( + object, key, val, untransitionable_map, is_store)); + } + *has_side_effects |= instr->HasObservableSideEffects(); + instr->set_position(position); + return is_store ? NULL : instr; + } + + AddInstruction(HCheckInstanceType::NewIsSpecObject(object)); HBasicBlock* join = graph()->CreateBasicBlock(); HInstruction* elements_kind_instr = @@ -4042,14 +4492,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HLoadExternalArrayPointer* external_elements = NULL; HInstruction* checked_key = NULL; - // FAST_ELEMENTS is assumed to be the first case. - STATIC_ASSERT(FAST_ELEMENTS == 0); + // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external + // arrays. + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); - for (ElementsKind elements_kind = FAST_ELEMENTS; + for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND; elements_kind <= LAST_ELEMENTS_KIND; elements_kind = ElementsKind(elements_kind + 1)) { - // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we - // need to add some code that's executed for all external array cases. + // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code + // that's executed for all external array cases. STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == LAST_ELEMENTS_KIND); if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND @@ -4071,15 +4527,22 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_true); HInstruction* access; - if (elements_kind == FAST_ELEMENTS || + if (elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_ELEMENTS || elements_kind == FAST_DOUBLE_ELEMENTS) { - bool fast_double_elements = - elements_kind == FAST_DOUBLE_ELEMENTS; - if (is_store && elements_kind == FAST_ELEMENTS) { + if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map(), elements_kind_branch)); } + // TODO(jkummerow): The need for these two blocks could be avoided + // in one of two ways: + // (1) Introduce ElementsKinds for JSArrays that are distinct from + // those for fast objects. + // (2) Put the common instructions into a third "join" block. This + // requires additional AST IDs that we can deopt to from inside + // that join block. They must be added to the Property class (when + // it's a keyed property) and registered in the full codegen. HBasicBlock* if_jsarray = graph()->CreateBasicBlock(); HBasicBlock* if_fastobject = graph()->CreateBasicBlock(); HHasInstanceTypeAndBranch* typecheck = @@ -4089,30 +4552,16 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, current_block()->Finish(typecheck); set_current_block(if_jsarray); - HInstruction* length = new(zone()) HJSArrayLength(object, typecheck); - AddInstruction(length); + HInstruction* length; + length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val)); - } else { - access = AddInstruction( - new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); - } - } else { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); - } else { - access = AddInstruction( - new(zone()) HLoadKeyedFastElement(elements, checked_key)); - } + access = AddInstruction(BuildFastElementAccess( + elements, checked_key, val, elements_kind, is_store)); + if (!is_store) { Push(access); } - *has_side_effects |= access->HasSideEffects(); + + *has_side_effects |= access->HasObservableSideEffects(); if (position != -1) { access->set_position(position); } @@ -4121,25 +4570,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_fastobject); length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val)); - } else { - access = AddInstruction( - new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); - } - } else { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); - } else { - access = AddInstruction( - new(zone()) HLoadKeyedFastElement(elements, checked_key)); - } - } + access = AddInstruction(BuildFastElementAccess( + elements, checked_key, val, elements_kind, is_store)); } else if (elements_kind == DICTIONARY_ELEMENTS) { if (is_store) { access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); @@ -4150,7 +4582,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, access = AddInstruction(BuildExternalArrayElementAccess( external_elements, checked_key, val, elements_kind, is_store)); } - *has_side_effects |= access->HasSideEffects(); + *has_side_effects |= access->HasObservableSideEffects(); access->set_position(position); if (!is_store) { Push(access); @@ -4179,7 +4611,14 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, ASSERT(!expr->IsPropertyName()); HInstruction* instr = NULL; if (expr->IsMonomorphic()) { - instr = BuildMonomorphicElementAccess(obj, key, val, expr, is_store); + Handle<Map> map = expr->GetMonomorphicReceiverType(); + if (map->has_slow_elements_kind()) { + instr = is_store ? BuildStoreKeyedGeneric(obj, key, val) + : BuildLoadKeyedGeneric(obj, key); + } else { + AddInstruction(new(zone()) HCheckNonSmi(obj)); + instr = BuildMonomorphicElementAccess(obj, key, val, map, is_store); + } } else if (expr->GetReceiverTypes() != NULL && !expr->GetReceiverTypes()->is_empty()) { return HandlePolymorphicElementAccess( @@ -4193,7 +4632,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, } instr->set_position(position); AddInstruction(instr); - *has_side_effects = instr->HasSideEffects(); + *has_side_effects = instr->HasObservableSideEffects(); return instr; } @@ -4207,7 +4646,7 @@ HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object, object, key, value, - function_strict_mode()); + function_strict_mode_flag()); } bool HGraphBuilder::TryArgumentsAccess(Property* expr) { @@ -4260,7 +4699,7 @@ void HGraphBuilder::VisitProperty(Property* expr) { CHECK_ALIVE(VisitForValue(expr->obj())); HInstruction* instr = NULL; - if (expr->IsArrayLength()) { + if (expr->AsProperty()->IsArrayLength()) { HValue* array = Pop(); AddInstruction(new(zone()) HCheckNonSmi(array)); HInstruction* mapcheck = @@ -4338,7 +4777,8 @@ void HGraphBuilder::AddCheckConstantFunction(Call* expr, // its prototypes. if (smi_and_map_check) { AddInstruction(new(zone()) HCheckNonSmi(receiver)); - AddInstruction(new(zone()) HCheckMap(receiver, receiver_map)); + AddInstruction(new(zone()) HCheckMap(receiver, receiver_map, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); } if (!expr->holder().is_null()) { AddInstruction(new(zone()) HCheckPrototypeMaps( @@ -4449,7 +4889,7 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target, } -bool HGraphBuilder::TryInline(Call* expr) { +bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { if (!FLAG_use_inlining) return false; // The function call we are inlining is a method call if the call @@ -4466,7 +4906,8 @@ bool HGraphBuilder::TryInline(Call* expr) { // Do a quick check on source code length to avoid parsing large // inlining candidates. - if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) { + if ((FLAG_limit_inlining && target_shared->SourceSize() > kMaxSourceSize) + || target_shared->SourceSize() > kUnlimitedMaxSourceSize) { TraceInline(target, caller, "target text too big"); return false; } @@ -4476,8 +4917,20 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "target not inlineable"); return false; } + if (target_shared->dont_inline() || target_shared->dont_crankshaft()) { + TraceInline(target, caller, "target contains unsupported syntax [early]"); + return false; + } - // No context change required. + int nodes_added = target_shared->ast_node_count(); + if ((FLAG_limit_inlining && nodes_added > kMaxInlinedSize) || + nodes_added > kUnlimitedMaxInlinedSize) { + TraceInline(target, caller, "target AST is too large [early]"); + return false; + } + +#if !defined(V8_TARGET_ARCH_IA32) + // Target must be able to use caller's context. CompilationInfo* outer_info = info(); if (target->context() != outer_info->closure()->context() || outer_info->scope()->contains_with() || @@ -4485,6 +4938,8 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "target requires context change"); return false; } +#endif + // Don't inline deeper than kMaxInliningLevels calls. HEnvironment* env = environment(); @@ -4494,27 +4949,32 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "inline depth limit reached"); return false; } - current_level++; + if (!env->outer()->is_arguments_adaptor()) { + current_level++; + } env = env->outer(); } // Don't inline recursive functions. - if (*target_shared == outer_info->closure()->shared()) { - TraceInline(target, caller, "target is recursive"); - return false; + for (FunctionState* state = function_state(); + state != NULL; + state = state->outer()) { + if (state->compilation_info()->closure()->shared() == *target_shared) { + TraceInline(target, caller, "target is recursive"); + return false; + } } // We don't want to add more than a certain number of nodes from inlining. - if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) { + if ((FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) || + inlined_count_ > kUnlimitedMaxInlinedNodes) { TraceInline(target, caller, "cumulative AST node limit reached"); return false; } - int count_before = AstNode::Count(); - // Parse and allocate variables. CompilationInfo target_info(target); - if (!ParserApi::Parse(&target_info) || + if (!ParserApi::Parse(&target_info, kNoParsingFlags) || !Scope::Analyze(&target_info)) { if (target_info.isolate()->has_pending_exception()) { // Parse or scope error, never optimize this function. @@ -4531,18 +4991,22 @@ bool HGraphBuilder::TryInline(Call* expr) { } FunctionLiteral* function = target_info.function(); - // Count the number of AST nodes added by inlining this call. - int nodes_added = AstNode::Count() - count_before; - if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) { - TraceInline(target, caller, "target AST is too large"); + // The following conditions must be checked again after re-parsing, because + // earlier the information might not have been complete due to lazy parsing. + nodes_added = function->ast_node_count(); + if ((FLAG_limit_inlining && nodes_added > kMaxInlinedSize) || + nodes_added > kUnlimitedMaxInlinedSize) { + TraceInline(target, caller, "target AST is too large [late]"); + return false; + } + AstProperties::Flags* flags(function->flags()); + if (flags->Contains(kDontInline) || flags->Contains(kDontOptimize)) { + TraceInline(target, caller, "target contains unsupported syntax [late]"); return false; } - // Don't inline functions that uses the arguments object or that - // have a mismatching number of parameters. - int arity = expr->arguments()->length(); - if (function->scope()->arguments() != NULL || - arity != target_shared->formal_parameter_count()) { + // Don't inline functions that uses the arguments object. + if (function->scope()->arguments() != NULL) { TraceInline(target, caller, "target requires special argument handling"); return false; } @@ -4556,13 +5020,6 @@ bool HGraphBuilder::TryInline(Call* expr) { return false; } } - // All statements in the body must be inlineable. - for (int i = 0, count = function->body()->length(); i < count; ++i) { - if (!function->body()->at(i)->IsInlineable()) { - TraceInline(target, caller, "target contains unsupported syntax"); - return false; - } - } // Generate the deoptimization data for the unoptimized version of // the target function if we don't already have it. @@ -4574,11 +5031,11 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "could not generate deoptimization info"); return false; } - if (target_shared->scope_info() == SerializedScopeInfo::Empty()) { + if (target_shared->scope_info() == ScopeInfo::Empty()) { // The scope info might not have been set if a lazily compiled // function is inlined before being called for the first time. - Handle<SerializedScopeInfo> target_scope_info = - SerializedScopeInfo::Create(target_info.scope()); + Handle<ScopeInfo> target_scope_info = + ScopeInfo::Create(target_info.scope()); target_shared->set_scope_info(*target_scope_info); } target_shared->EnableDeoptimizationSupport(*target_info.code()); @@ -4596,20 +5053,37 @@ bool HGraphBuilder::TryInline(Call* expr) { ASSERT(target_shared->has_deoptimization_support()); TypeFeedbackOracle target_oracle( Handle<Code>(target_shared->code()), - Handle<Context>(target->context()->global_context())); - FunctionState target_state(this, &target_info, &target_oracle); + Handle<Context>(target->context()->global_context()), + isolate()); + // The function state is new-allocated because we need to delete it + // in two different places. + FunctionState* target_state = + new FunctionState(this, &target_info, &target_oracle, drop_extra); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner_env = environment()->CopyForInlining(target, + expr->arguments()->length(), function, undefined, call_kind); +#ifdef V8_TARGET_ARCH_IA32 + // IA32 only, overwrite the caller's context in the deoptimization + // environment with the correct one. + // + // TODO(kmillikin): implement the same inlining on other platforms so we + // can remove the unsightly ifdefs in this function. + HConstant* context = new HConstant(Handle<Context>(target->context()), + Representation::Tagged()); + AddInstruction(context); + inner_env->BindContext(context); +#endif HBasicBlock* body_entry = CreateBasicBlock(inner_env); current_block()->Goto(body_entry); body_entry->SetJoinId(expr->ReturnId()); set_current_block(body_entry); AddInstruction(new(zone()) HEnterInlined(target, + expr->arguments()->length(), function, call_kind)); VisitDeclarations(target_info.scope()->declarations()); @@ -4620,6 +5094,7 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "inline graph construction failed"); target_shared->DisableOptimization(*target); inline_bailout_ = true; + delete target_state; return true; } @@ -4635,9 +5110,11 @@ bool HGraphBuilder::TryInline(Call* expr) { ASSERT(function_return() != NULL); ASSERT(call_context()->IsEffect() || call_context()->IsValue()); if (call_context()->IsEffect()) { - current_block()->Goto(function_return()); + current_block()->Goto(function_return(), drop_extra); } else { - current_block()->AddLeaveInlined(undefined, function_return()); + current_block()->AddLeaveInlined(undefined, + function_return(), + drop_extra); } } else { // The graph builder assumes control can reach both branches of a @@ -4645,13 +5122,14 @@ bool HGraphBuilder::TryInline(Call* expr) { // simply jumping to the false target. // // TODO(3168478): refactor to avoid this. + ASSERT(call_context()->IsTest()); HBasicBlock* empty_true = graph()->CreateBasicBlock(); HBasicBlock* empty_false = graph()->CreateBasicBlock(); HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false); current_block()->Finish(test); - empty_true->Goto(inlined_test_context()->if_true()); - empty_false->Goto(inlined_test_context()->if_false()); + empty_true->Goto(inlined_test_context()->if_true(), drop_extra); + empty_false->Goto(inlined_test_context()->if_false(), drop_extra); } } @@ -4663,19 +5141,21 @@ bool HGraphBuilder::TryInline(Call* expr) { // Pop the return test context from the expression context stack. ASSERT(ast_context() == inlined_test_context()); ClearInlinedTestContext(); + delete target_state; // Forward to the real test context. if (if_true->HasPredecessor()) { if_true->SetJoinId(expr->id()); HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); - if_true->Goto(true_target); + if_true->Goto(true_target, function_state()->drop_extra()); } if (if_false->HasPredecessor()) { if_false->SetJoinId(expr->id()); HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); - if_false->Goto(false_target); + if_false->Goto(false_target, function_state()->drop_extra()); } set_current_block(NULL); + return true; } else if (function_return()->HasPredecessor()) { function_return()->SetJoinId(expr->id()); @@ -4683,15 +5163,46 @@ bool HGraphBuilder::TryInline(Call* expr) { } else { set_current_block(NULL); } - + delete target_state; return true; } -bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, - HValue* receiver, - Handle<Map> receiver_map, - CheckType check_type) { +bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) { + if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; + BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); + switch (id) { + case kMathRound: + case kMathFloor: + case kMathAbs: + case kMathSqrt: + case kMathLog: + case kMathSin: + case kMathCos: + if (expr->arguments()->length() == 1) { + HValue* argument = Pop(); + HValue* context = environment()->LookupContext(); + Drop(1); // Receiver. + HUnaryMathOperation* op = + new(zone()) HUnaryMathOperation(context, argument, id); + op->set_position(expr->position()); + if (drop_extra) Drop(1); // Optionally drop the function. + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; + default: + // Not supported for inlining yet. + break; + } + return false; +} + + +bool HGraphBuilder::TryInlineBuiltinMethodCall(Call* expr, + HValue* receiver, + Handle<Map> receiver_map, + CheckType check_type) { ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null()); // Try to inline calls like Math.* as operations in the calling function. if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; @@ -4764,7 +5275,7 @@ bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, AddInstruction(square_root); // MathPowHalf doesn't have side effects so there's no need for // an environment simulation here. - ASSERT(!square_root->HasSideEffects()); + ASSERT(!square_root->HasObservableSideEffects()); result = new(zone()) HDiv(context, double_one, square_root); } else if (exponent == 2.0) { result = new(zone()) HMul(context, left, left); @@ -4782,6 +5293,69 @@ bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, return true; } break; + case kMathRandom: + if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) { + AddCheckConstantFunction(expr, receiver, receiver_map, true); + Drop(1); // Receiver. + HValue* context = environment()->LookupContext(); + HGlobalObject* global_object = new(zone()) HGlobalObject(context); + AddInstruction(global_object); + HRandom* result = new(zone()) HRandom(global_object); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } + break; + case kMathMax: + case kMathMin: + if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { + AddCheckConstantFunction(expr, receiver, receiver_map, true); + HValue* right = Pop(); + HValue* left = Pop(); + // Do not inline if the return representation is not certain. + if (!left->representation().Equals(right->representation())) { + Push(left); + Push(right); + return false; + } + + Pop(); // Pop receiver. + Token::Value op = (id == kMathMin) ? Token::LT : Token::GT; + HCompareIDAndBranch* compare = NULL; + + if (left->representation().IsTagged()) { + HChange* left_cvt = + new(zone()) HChange(left, Representation::Double(), false, true); + left_cvt->SetFlag(HValue::kBailoutOnMinusZero); + AddInstruction(left_cvt); + HChange* right_cvt = + new(zone()) HChange(right, Representation::Double(), false, true); + right_cvt->SetFlag(HValue::kBailoutOnMinusZero); + AddInstruction(right_cvt); + compare = new(zone()) HCompareIDAndBranch(left_cvt, right_cvt, op); + compare->SetInputRepresentation(Representation::Double()); + } else { + compare = new(zone()) HCompareIDAndBranch(left, right, op); + compare->SetInputRepresentation(left->representation()); + } + + HBasicBlock* return_left = graph()->CreateBasicBlock(); + HBasicBlock* return_right = graph()->CreateBasicBlock(); + + compare->SetSuccessorAt(0, return_left); + compare->SetSuccessorAt(1, return_right); + current_block()->Finish(compare); + + set_current_block(return_left); + Push(left); + set_current_block(return_right); + Push(right); + + HBasicBlock* join = CreateJoin(return_left, return_right, expr->id()); + set_current_block(join); + ast_context()->ReturnValue(Pop()); + return true; + } + break; default: // Not yet supported for inlining. break; @@ -4890,14 +5464,19 @@ void HGraphBuilder::VisitCall(Call* expr) { Handle<Map> receiver_map = (types == NULL || types->is_empty()) ? Handle<Map>::null() : types->first(); - if (TryInlineBuiltinFunction(expr, - receiver, - receiver_map, - expr->check_type())) { + if (TryInlineBuiltinMethodCall(expr, + receiver, + receiver_map, + expr->check_type())) { + if (FLAG_trace_inlining) { + PrintF("Inlining builtin "); + expr->target()->ShortPrint(); + PrintF("\n"); + } return; } - if (CallStubCompiler::HasCustomCallGenerator(*expr->target()) || + if (CallStubCompiler::HasCustomCallGenerator(expr->target()) || expr->check_type() != RECEIVER_MAP_CHECK) { // When the target has a custom call IC generator, use the IC, // because it is likely to generate better code. Also use the IC @@ -4925,8 +5504,8 @@ void HGraphBuilder::VisitCall(Call* expr) { } } else { + expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION); VariableProxy* proxy = expr->expression()->AsVariableProxy(); - // FIXME. bool global_call = proxy != NULL && proxy->var()->IsUnallocated(); if (global_call) { @@ -4935,7 +5514,7 @@ void HGraphBuilder::VisitCall(Call* expr) { // If there is a global property cell for the name at compile time and // access check is not enabled we assume that the function will not change // and generate optimized code for calling the function. - LookupResult lookup; + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false); if (type == kUseCell && !info()->global_object()->IsAccessCheckNeeded()) { @@ -4964,6 +5543,14 @@ void HGraphBuilder::VisitCall(Call* expr) { IsGlobalObject()); environment()->SetExpressionStackAt(receiver_index, global_receiver); + if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop. + if (FLAG_trace_inlining) { + PrintF("Inlining builtin "); + expr->target()->ShortPrint(); + PrintF("\n"); + } + return; + } if (TryInline(expr)) return; call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(), argument_count)); @@ -4978,8 +5565,40 @@ void HGraphBuilder::VisitCall(Call* expr) { Drop(argument_count); } + } else if (expr->IsMonomorphic()) { + // The function is on the stack in the unoptimized code during + // evaluation of the arguments. + CHECK_ALIVE(VisitForValue(expr->expression())); + HValue* function = Top(); + HValue* context = environment()->LookupContext(); + HGlobalObject* global = new(zone()) HGlobalObject(context); + HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global); + AddInstruction(global); + PushAndAdd(receiver); + CHECK_ALIVE(VisitExpressions(expr->arguments())); + AddInstruction(new(zone()) HCheckFunction(function, expr->target())); + + if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function. + if (FLAG_trace_inlining) { + PrintF("Inlining builtin "); + expr->target()->ShortPrint(); + PrintF("\n"); + } + return; + } + + if (TryInline(expr, true)) { // Drop function from environment. + return; + } else { + call = PreProcessCall(new(zone()) HInvokeFunction(context, + function, + argument_count)); + Drop(1); // The function. + } + } else { - CHECK_ALIVE(VisitArgument(expr->expression())); + CHECK_ALIVE(VisitForValue(expr->expression())); + HValue* function = Top(); HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object); @@ -4988,9 +5607,7 @@ void HGraphBuilder::VisitCall(Call* expr) { PushAndAdd(new(zone()) HPushArgument(receiver)); CHECK_ALIVE(VisitArgumentList(expr->arguments())); - // The function to call is treated as an argument to the call function - // stub. - call = new(zone()) HCallFunction(context, argument_count + 1); + call = new(zone()) HCallFunction(context, function, argument_count); Drop(argument_count + 1); } } @@ -5185,7 +5802,6 @@ void HGraphBuilder::VisitBitNot(UnaryOperation* expr) { void HGraphBuilder::VisitNot(UnaryOperation* expr) { - // TODO(svenpanne) Perhaps a switch/virtual function is nicer here. if (ast_context()->IsTest()) { TestContext* context = TestContext::cast(ast_context()); VisitForControl(expr->expression(), @@ -5207,7 +5823,7 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) { materialize_true)); if (materialize_false->HasPredecessor()) { - materialize_false->SetJoinId(expr->expression()->id()); + materialize_false->SetJoinId(expr->MaterializeFalseId()); set_current_block(materialize_false); Push(graph()->GetConstantFalse()); } else { @@ -5215,7 +5831,7 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) { } if (materialize_true->HasPredecessor()) { - materialize_true->SetJoinId(expr->expression()->id()); + materialize_true->SetJoinId(expr->MaterializeTrueId()); set_current_block(materialize_true); Push(graph()->GetConstantTrue()); } else { @@ -5284,7 +5900,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST) { + if (var->mode() == CONST) { return Bailout("unsupported count operation with const"); } // Argument of the count operation is a variable, not a property. @@ -5325,10 +5941,15 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } HValue* context = BuildContextChainWalk(var); + HStoreContextSlot::Mode mode = + (var->mode() == LET || var->mode() == CONST_HARMONY) + ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck; HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, var->index(), after); + new(zone()) HStoreContextSlot(context, var->index(), mode, after); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } break; } @@ -5357,7 +5978,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) AddSimulate(expr->CountId()); + if (load->HasObservableSideEffects()) AddSimulate(expr->CountId()); after = BuildIncrement(returns_original_input, expr); input = Pop(); @@ -5370,7 +5991,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { // necessary. environment()->SetExpressionStackAt(0, after); if (returns_original_input) environment()->SetExpressionStackAt(1, input); - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); } else { // Keyed property. @@ -5447,38 +6068,34 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, AddInstruction(HCheckInstanceType::NewIsString(right)); instr = new(zone()) HStringAdd(context, left, right); } else { - instr = new(zone()) HAdd(context, left, right); + instr = HAdd::NewHAdd(zone(), context, left, right); } break; case Token::SUB: - instr = new(zone()) HSub(context, left, right); + instr = HSub::NewHSub(zone(), context, left, right); break; case Token::MUL: - instr = new(zone()) HMul(context, left, right); + instr = HMul::NewHMul(zone(), context, left, right); break; case Token::MOD: - instr = new(zone()) HMod(context, left, right); + instr = HMod::NewHMod(zone(), context, left, right); break; case Token::DIV: - instr = new(zone()) HDiv(context, left, right); + instr = HDiv::NewHDiv(zone(), context, left, right); break; case Token::BIT_XOR: - instr = new(zone()) HBitXor(context, left, right); - break; case Token::BIT_AND: - instr = new(zone()) HBitAnd(context, left, right); - break; case Token::BIT_OR: - instr = new(zone()) HBitOr(context, left, right); + instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); break; case Token::SAR: - instr = new(zone()) HSar(context, left, right); + instr = HSar::NewHSar(zone(), context, left, right); break; case Token::SHR: - instr = new(zone()) HShr(context, left, right); + instr = HShr::NewHShr(zone(), context, left, right); break; case Token::SHL: - instr = new(zone()) HShl(context, left, right); + instr = HShl::NewHShl(zone(), context, left, right); break; default: UNREACHABLE(); @@ -5671,26 +6288,75 @@ Representation HGraphBuilder::ToRepresentation(TypeInfo info) { } -void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr, - Expression* expr, +void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr, + HTypeof* typeof_expr, Handle<String> check) { - CHECK_ALIVE(VisitForTypeOf(expr)); - HValue* expr_value = Pop(); - HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(expr_value, check); - instr->set_position(compare_expr->position()); - return ast_context()->ReturnControl(instr, compare_expr->id()); + // Note: The HTypeof itself is removed during canonicalization, if possible. + HValue* value = typeof_expr->value(); + HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check); + instr->set_position(expr->position()); + return ast_context()->ReturnControl(instr, expr->id()); +} + + +static bool MatchLiteralCompareNil(HValue* left, + Token::Value op, + HValue* right, + Handle<Object> nil, + HValue** expr) { + if (left->IsConstant() && + HConstant::cast(left)->handle().is_identical_to(nil) && + Token::IsEqualityOp(op)) { + *expr = right; + return true; + } + return false; +} + + +static bool MatchLiteralCompareTypeof(HValue* left, + Token::Value op, + HValue* right, + HTypeof** typeof_expr, + Handle<String>* check) { + if (left->IsTypeof() && + Token::IsEqualityOp(op) && + right->IsConstant() && + HConstant::cast(right)->HasStringValue()) { + *typeof_expr = HTypeof::cast(left); + *check = Handle<String>::cast(HConstant::cast(right)->handle()); + return true; + } + return false; } -void HGraphBuilder::HandleLiteralCompareUndefined( - CompareOperation* compare_expr, Expression* expr) { - CHECK_ALIVE(VisitForValue(expr)); - HValue* lhs = Pop(); - HValue* rhs = graph()->GetConstantUndefined(); - HCompareObjectEqAndBranch* instr = - new(zone()) HCompareObjectEqAndBranch(lhs, rhs); - instr->set_position(compare_expr->position()); - return ast_context()->ReturnControl(instr, compare_expr->id()); +static bool IsLiteralCompareTypeof(HValue* left, + Token::Value op, + HValue* right, + HTypeof** typeof_expr, + Handle<String>* check) { + return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) || + MatchLiteralCompareTypeof(right, op, left, typeof_expr, check); +} + + +static bool IsLiteralCompareNil(HValue* left, + Token::Value op, + HValue* right, + Handle<Object> nil, + HValue** expr) { + return MatchLiteralCompareNil(left, op, right, nil, expr) || + MatchLiteralCompareNil(right, op, left, nil, expr); +} + + +static bool IsLiteralCompareBool(HValue* left, + Token::Value op, + HValue* right) { + return op == Token::EQ_STRICT && + ((left->IsConstant() && HConstant::cast(left)->handle()->IsBoolean()) || + (right->IsConstant() && HConstant::cast(right)->handle()->IsBoolean())); } @@ -5711,21 +6377,9 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { return ast_context()->ReturnControl(instr, expr->id()); } - // Check for special cases that compare against literals. - Expression *sub_expr; - Handle<String> check; - if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { - HandleLiteralCompareTypeof(expr, sub_expr, check); - return; - } - - if (expr->IsLiteralCompareUndefined(&sub_expr)) { - HandleLiteralCompareUndefined(expr, sub_expr); - return; - } - TypeInfo type_info = oracle()->CompareType(expr); // Check if this expression was ever executed according to type feedback. + // Note that for the special typeof/null/undefined cases we get unknown here. if (type_info.IsUninitialized()) { AddInstruction(new(zone()) HSoftDeoptimize); current_block()->MarkAsDeoptimizing(); @@ -5740,6 +6394,26 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { HValue* left = Pop(); Token::Value op = expr->op(); + HTypeof* typeof_expr = NULL; + Handle<String> check; + if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) { + return HandleLiteralCompareTypeof(expr, typeof_expr, check); + } + HValue* sub_expr = NULL; + Factory* f = graph()->isolate()->factory(); + if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) { + return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue); + } + if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) { + return HandleLiteralCompareNil(expr, sub_expr, kNullValue); + } + if (IsLiteralCompareBool(left, op, right)) { + HCompareObjectEqAndBranch* result = + new(zone()) HCompareObjectEqAndBranch(left, right); + result->set_position(expr->position()); + return ast_context()->ReturnControl(result, expr->id()); + } + if (op == Token::INSTANCEOF) { // Check to see if the rhs of the instanceof is a global function not // residing in new space. If it is we assume that the function will stay the @@ -5752,9 +6426,9 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { !info()->global_object()->IsAccessCheckNeeded()) { Handle<String> name = proxy->name(); Handle<GlobalObject> global(info()->global_object()); - LookupResult lookup; + LookupResult lookup(isolate()); global->Lookup(*name, &lookup); - if (lookup.IsProperty() && + if (lookup.IsFound() && lookup.type() == NORMAL && lookup.GetValue()->IsJSFunction()) { Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue())); @@ -5787,14 +6461,29 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { switch (op) { case Token::EQ: case Token::EQ_STRICT: { - AddInstruction(new(zone()) HCheckNonSmi(left)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(left)); - AddInstruction(new(zone()) HCheckNonSmi(right)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(right)); - HCompareObjectEqAndBranch* result = - new(zone()) HCompareObjectEqAndBranch(left, right); - result->set_position(expr->position()); - return ast_context()->ReturnControl(result, expr->id()); + // Can we get away with map check and not instance type check? + Handle<Map> map = oracle()->GetCompareMap(expr); + if (!map.is_null()) { + AddInstruction(new(zone()) HCheckNonSmi(left)); + AddInstruction(new(zone()) HCheckMap(left, map, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); + AddInstruction(new(zone()) HCheckNonSmi(right)); + AddInstruction(new(zone()) HCheckMap(right, map, NULL, + ALLOW_ELEMENT_TRANSITION_MAPS)); + HCompareObjectEqAndBranch* result = + new(zone()) HCompareObjectEqAndBranch(left, right); + result->set_position(expr->position()); + return ast_context()->ReturnControl(result, expr->id()); + } else { + AddInstruction(new(zone()) HCheckNonSmi(left)); + AddInstruction(HCheckInstanceType::NewIsSpecObject(left)); + AddInstruction(new(zone()) HCheckNonSmi(right)); + AddInstruction(HCheckInstanceType::NewIsSpecObject(right)); + HCompareObjectEqAndBranch* result = + new(zone()) HCompareObjectEqAndBranch(left, right); + result->set_position(expr->position()); + return ast_context()->ReturnControl(result, expr->id()); + } } default: return Bailout("Unsupported non-primitive compare"); @@ -5827,14 +6516,16 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { } -void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { +void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, + HValue* value, + NilValue nil) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - CHECK_ALIVE(VisitForValue(expr->expression())); - HValue* value = Pop(); - HIsNullAndBranch* instr = - new(zone()) HIsNullAndBranch(value, expr->is_strict()); + EqualityKind kind = + expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; + HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil); + instr->set_position(expr->position()); return ast_context()->ReturnControl(instr, expr->id()); } @@ -5843,41 +6534,43 @@ void HGraphBuilder::VisitThisFunction(ThisFunction* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - HThisFunction* self = new(zone()) HThisFunction; + HThisFunction* self = new(zone()) HThisFunction( + function_state()->compilation_info()->closure()); return ast_context()->ReturnInstruction(self, expr->id()); } -void HGraphBuilder::VisitDeclaration(Declaration* decl) { - HandleDeclaration(decl->proxy(), decl->mode(), decl->fun()); +void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* decl) { + HandleVariableDeclaration(decl->proxy(), decl->mode(), decl->fun()); } -void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function) { - if (mode == Variable::LET) return Bailout("unsupported let declaration"); +void HGraphBuilder::HandleVariableDeclaration(VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* function) { Variable* var = proxy->var(); + bool binding_needs_init = + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (var->location()) { case Variable::UNALLOCATED: return Bailout("unsupported global declaration"); case Variable::PARAMETER: case Variable::LOCAL: case Variable::CONTEXT: - if (mode == Variable::CONST || function != NULL) { + if (binding_needs_init || function != NULL) { HValue* value = NULL; - if (mode == Variable::CONST) { - value = graph()->GetConstantHole(); - } else { + if (function != NULL) { VisitForValue(function); value = Pop(); + } else { + value = graph()->GetConstantHole(); } if (var->IsContextSlot()) { HValue* context = environment()->LookupContext(); - HStoreContextSlot* store = - new HStoreContextSlot(context, var->index(), value); + HStoreContextSlot* store = new HStoreContextSlot( + context, var->index(), HStoreContextSlot::kNoCheck, value); AddInstruction(store); - if (store->HasSideEffects()) AddSimulate(proxy->id()); + if (store->HasObservableSideEffects()) AddSimulate(proxy->id()); } else { environment()->Bind(var, value); } @@ -5889,6 +6582,31 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, } +void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) { + // TODO(rossberg) +} + + +void HGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) { + // TODO(rossberg) +} + + +void HGraphBuilder::VisitModuleVariable(ModuleVariable* module) { + // TODO(rossberg) +} + + +void HGraphBuilder::VisitModulePath(ModulePath* module) { + // TODO(rossberg) +} + + +void HGraphBuilder::VisitModuleUrl(ModuleUrl* module) { + // TODO(rossberg) +} + + // Generators for inline runtime functions. // Support for types. void HGraphBuilder::GenerateIsSmi(CallRuntime* call) { @@ -5917,9 +6635,7 @@ void HGraphBuilder::GenerateIsFunction(CallRuntime* call) { CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); HValue* value = Pop(); HHasInstanceTypeAndBranch* result = - new(zone()) HHasInstanceTypeAndBranch(value, - JS_FUNCTION_TYPE, - JS_FUNCTION_PROXY_TYPE); + new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE); return ast_context()->ReturnControl(result, call->id()); } @@ -6047,7 +6763,44 @@ void HGraphBuilder::GenerateValueOf(CallRuntime* call) { void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) { - return Bailout("inlined runtime function: SetValueOf"); + ASSERT(call->arguments()->length() == 2); + CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); + CHECK_ALIVE(VisitForValue(call->arguments()->at(1))); + HValue* value = Pop(); + HValue* object = Pop(); + // Check if object is a not a smi. + HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object); + HBasicBlock* if_smi = graph()->CreateBasicBlock(); + HBasicBlock* if_heap_object = graph()->CreateBasicBlock(); + HBasicBlock* join = graph()->CreateBasicBlock(); + smicheck->SetSuccessorAt(0, if_smi); + smicheck->SetSuccessorAt(1, if_heap_object); + current_block()->Finish(smicheck); + if_smi->Goto(join); + + // Check if object is a JSValue. + set_current_block(if_heap_object); + HHasInstanceTypeAndBranch* typecheck = + new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE); + HBasicBlock* if_js_value = graph()->CreateBasicBlock(); + HBasicBlock* not_js_value = graph()->CreateBasicBlock(); + typecheck->SetSuccessorAt(0, if_js_value); + typecheck->SetSuccessorAt(1, not_js_value); + current_block()->Finish(typecheck); + not_js_value->Goto(join); + + // Create in-object property store to kValueOffset. + set_current_block(if_js_value); + Handle<String> name = isolate()->factory()->undefined_symbol(); + AddInstruction(new HStoreNamedField(object, + name, + value, + true, // in-object store. + JSValue::kValueOffset)); + if_js_value->Goto(join); + join->SetJoinId(call->id()); + set_current_block(join); + return ast_context()->ReturnValue(value); } @@ -6113,7 +6866,11 @@ void HGraphBuilder::GenerateLog(CallRuntime* call) { // Fast support for Math.random(). void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) { - return Bailout("inlined runtime function: RandomHeapNumber"); + HValue* context = environment()->LookupContext(); + HGlobalObject* global_object = new(zone()) HGlobalObject(context); + AddInstruction(global_object); + HRandom* result = new(zone()) HRandom(global_object); + return ast_context()->ReturnInstruction(result, call->id()); } @@ -6210,12 +6967,37 @@ void HGraphBuilder::GenerateCallFunction(CallRuntime* call) { CHECK_ALIVE(VisitArgument(call->arguments()->at(i))); } CHECK_ALIVE(VisitForValue(call->arguments()->last())); + HValue* function = Pop(); HValue* context = environment()->LookupContext(); - HInvokeFunction* result = - new(zone()) HInvokeFunction(context, function, arg_count); + + // Branch for function proxies, or other non-functions. + HHasInstanceTypeAndBranch* typecheck = + new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE); + HBasicBlock* if_jsfunction = graph()->CreateBasicBlock(); + HBasicBlock* if_nonfunction = graph()->CreateBasicBlock(); + HBasicBlock* join = graph()->CreateBasicBlock(); + typecheck->SetSuccessorAt(0, if_jsfunction); + typecheck->SetSuccessorAt(1, if_nonfunction); + current_block()->Finish(typecheck); + + set_current_block(if_jsfunction); + HInstruction* invoke_result = AddInstruction( + new(zone()) HInvokeFunction(context, function, arg_count)); Drop(arg_count); - return ast_context()->ReturnInstruction(result, call->id()); + Push(invoke_result); + if_jsfunction->Goto(join); + + set_current_block(if_nonfunction); + HInstruction* call_result = AddInstruction( + new(zone()) HCallFunction(context, function, arg_count)); + Drop(arg_count); + Push(call_result); + if_nonfunction->Goto(join); + + set_current_block(join); + join->SetJoinId(call->id()); + return ast_context()->ReturnValue(Pop()); } @@ -6255,6 +7037,18 @@ void HGraphBuilder::GenerateMathCos(CallRuntime* call) { } +void HGraphBuilder::GenerateMathTan(CallRuntime* call) { + ASSERT_EQ(1, call->arguments()->length()); + CHECK_ALIVE(VisitArgumentList(call->arguments())); + HValue* context = environment()->LookupContext(); + HCallStub* result = + new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1); + result->set_transcendental_type(TranscendentalCache::TAN); + Drop(1); + return ast_context()->ReturnInstruction(result, call->id()); +} + + void HGraphBuilder::GenerateMathLog(CallRuntime* call) { ASSERT_EQ(1, call->arguments()->length()); CHECK_ALIVE(VisitArgumentList(call->arguments())); @@ -6308,7 +7102,8 @@ HEnvironment::HEnvironment(HEnvironment* outer, outer_(outer), pop_count_(0), push_count_(0), - ast_id_(AstNode::kNoNumber) { + ast_id_(AstNode::kNoNumber), + arguments_adaptor_(false) { Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0); } @@ -6322,11 +7117,28 @@ HEnvironment::HEnvironment(const HEnvironment* other) outer_(NULL), pop_count_(0), push_count_(0), - ast_id_(other->ast_id()) { + ast_id_(other->ast_id()), + arguments_adaptor_(false) { Initialize(other); } +HEnvironment::HEnvironment(HEnvironment* outer, + Handle<JSFunction> closure, + int arguments) + : closure_(closure), + values_(arguments), + assigned_variables_(0), + parameter_count_(arguments), + local_count_(0), + outer_(outer), + pop_count_(0), + push_count_(0), + ast_id_(AstNode::kNoNumber), + arguments_adaptor_(true) { +} + + void HEnvironment::Initialize(int parameter_count, int local_count, int stack_height) { @@ -6350,6 +7162,7 @@ void HEnvironment::Initialize(const HEnvironment* other) { pop_count_ = other->pop_count_; push_count_ = other->push_count_; ast_id_ = other->ast_id_; + arguments_adaptor_ = other->arguments_adaptor_; } @@ -6453,30 +7266,46 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const { HEnvironment* HEnvironment::CopyForInlining( Handle<JSFunction> target, + int arguments, FunctionLiteral* function, HConstant* undefined, CallKind call_kind) const { + ASSERT(!is_arguments_adaptor()); + + Zone* zone = closure()->GetIsolate()->zone(); + // Outer environment is a copy of this one without the arguments. int arity = function->scope()->num_parameters(); + HEnvironment* outer = Copy(); - outer->Drop(arity + 1); // Including receiver. + outer->Drop(arguments + 1); // Including receiver. outer->ClearHistory(); - Zone* zone = closure()->GetIsolate()->zone(); + + if (arity != arguments) { + // Create artificial arguments adaptation environment. + outer = new(zone) HEnvironment(outer, target, arguments + 1); + for (int i = 0; i <= arguments; ++i) { // Include receiver. + outer->Push(ExpressionStackAt(arguments - i)); + } + outer->ClearHistory(); + } + HEnvironment* inner = new(zone) HEnvironment(outer, function->scope(), target); // Get the argument values from the original environment. for (int i = 0; i <= arity; ++i) { // Include receiver. - HValue* push = ExpressionStackAt(arity - i); + HValue* push = (i <= arguments) ? + ExpressionStackAt(arguments - i) : undefined; inner->SetValueAt(i, push); } // If the function we are inlining is a strict mode function or a // builtin function, pass undefined as the receiver for function // calls (instead of the global receiver). - if ((target->shared()->native() || function->strict_mode()) && + if ((target->shared()->native() || !function->is_classic_mode()) && call_kind == CALL_AS_FUNCTION) { inner->SetValueAt(0, undefined); } - inner->SetValueAt(arity + 1, outer->LookupContext()); + inner->SetValueAt(arity + 1, LookupContext()); for (int i = arity + 2; i < inner->length(); ++i) { inner->SetValueAt(i, undefined); } @@ -6492,7 +7321,7 @@ void HEnvironment::PrintTo(StringStream* stream) { if (i == parameter_count()) stream->Add("specials\n"); if (i == parameter_count() + specials_count()) stream->Add("locals\n"); if (i == parameter_count() + specials_count() + local_count()) { - stream->Add("expressions"); + stream->Add("expressions\n"); } HValue* val = values_.at(i); stream->Add("%d: ", i); @@ -6503,6 +7332,7 @@ void HEnvironment::PrintTo(StringStream* stream) { } stream->Add("\n"); } + PrintF("\n"); } @@ -6567,7 +7397,10 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) { } PrintEmptyProperty("xhandlers"); - PrintEmptyProperty("flags"); + const char* flags = current->IsLoopSuccessorDominator() + ? "dom-loop-succ" + : ""; + PrintStringProperty("flags", flags); if (current->dominator() != NULL) { PrintBlockProperty("dominator", current->dominator()->block_id()); @@ -6694,7 +7527,9 @@ void HTracer::TraceLiveRange(LiveRange* range, const char* type) { } LOperand* op = range->FirstHint(); int hint_index = -1; - if (op != NULL && op->IsUnallocated()) hint_index = op->VirtualRegister(); + if (op != NULL && op->IsUnallocated()) { + hint_index = LUnallocated::cast(op)->virtual_register(); + } trace_.Add(" %d %d", parent_index, hint_index); UseInterval* cur_interval = range->first_interval(); while (cur_interval != NULL && range->Covers(cur_interval->start())) { @@ -6819,7 +7654,7 @@ void HPhase::End() const { } #ifdef DEBUG - if (graph_ != NULL) graph_->Verify(); + if (graph_ != NULL) graph_->Verify(false); // No full verify. if (allocator_ != NULL) allocator_->Verify(); #endif } diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h index 03fbc73220..bbd4841f7a 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -121,11 +121,12 @@ class HBasicBlock: public ZoneObject { void Finish(HControlInstruction* last); void FinishExit(HControlInstruction* instruction); - void Goto(HBasicBlock* block); + void Goto(HBasicBlock* block, bool drop_extra = false); int PredecessorIndexOf(HBasicBlock* predecessor) const; void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); } void AssignCommonDominator(HBasicBlock* other); + void AssignLoopSuccessorDominators(); void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) { FinishExit(CreateDeoptimize(has_uses)); @@ -133,7 +134,9 @@ class HBasicBlock: public ZoneObject { // Add the inlined function exit sequence, adding an HLeaveInlined // instruction and updating the bailout environment. - void AddLeaveInlined(HValue* return_value, HBasicBlock* target); + void AddLeaveInlined(HValue* return_value, + HBasicBlock* target, + bool drop_extra = false); // If a target block is tagged as an inline function return, all // predecessors should contain the inlined exit sequence: @@ -147,6 +150,13 @@ class HBasicBlock: public ZoneObject { bool IsDeoptimizing() const { return is_deoptimizing_; } void MarkAsDeoptimizing() { is_deoptimizing_ = true; } + bool IsLoopSuccessorDominator() const { + return dominates_loop_successors_; + } + void MarkAsLoopSuccessorDominator() { + dominates_loop_successors_ = true; + } + inline Zone* zone(); #ifdef DEBUG @@ -180,6 +190,22 @@ class HBasicBlock: public ZoneObject { HBasicBlock* parent_loop_header_; bool is_inline_return_target_; bool is_deoptimizing_; + bool dominates_loop_successors_; +}; + + +class HPredecessorIterator BASE_EMBEDDED { + public: + explicit HPredecessorIterator(HBasicBlock* block) + : predecessor_list_(block->predecessors()), current_(0) { } + + bool Done() { return current_ >= predecessor_list_->length(); } + HBasicBlock* Current() { return predecessor_list_->at(current_); } + void Advance() { current_++; } + + private: + const ZoneList<HBasicBlock*>* predecessor_list_; + int current_; }; @@ -243,11 +269,13 @@ class HGraph: public ZoneObject { // Returns false if there are phi-uses of the arguments-object // which are not supported by the optimizing compiler. - bool CheckPhis(); + bool CheckArgumentsPhiUses(); + + // Returns false if there are phi-uses of an uninitialized const + // which are not supported by the optimizing compiler. + bool CheckConstPhiUses(); - // Returns false if there are phi-uses of hole values comming - // from uninitialized consts. - bool CollectPhis(); + void CollectPhis(); Handle<Code> Compile(CompilationInfo* info); @@ -283,7 +311,7 @@ class HGraph: public ZoneObject { } #ifdef DEBUG - void Verify() const; + void Verify(bool do_full_verify) const; #endif private: @@ -339,6 +367,17 @@ class HEnvironment: public ZoneObject { Scope* scope, Handle<JSFunction> closure); + bool is_arguments_adaptor() const { + return arguments_adaptor_; + } + + HEnvironment* DiscardInlined(bool drop_extra) { + HEnvironment* outer = outer_->is_arguments_adaptor() ? + outer_->outer_ : outer_; + if (drop_extra) outer->Drop(1); + return outer; + } + // Simple accessors. Handle<JSFunction> closure() const { return closure_; } const ZoneList<HValue*>* values() const { return &values_; } @@ -423,6 +462,7 @@ class HEnvironment: public ZoneObject { // environment is the outer environment but the top expression stack // elements are moved to an inner environment as parameters. HEnvironment* CopyForInlining(Handle<JSFunction> target, + int arguments, FunctionLiteral* function, HConstant* undefined, CallKind call_kind) const; @@ -446,6 +486,10 @@ class HEnvironment: public ZoneObject { private: explicit HEnvironment(const HEnvironment* other); + // Create an argument adaptor environment. + HEnvironment(HEnvironment* outer, Handle<JSFunction> closure, int arguments); + + // True if index is included in the expression stack part of the environment. bool HasExpressionAt(int index) const; @@ -474,6 +518,7 @@ class HEnvironment: public ZoneObject { int pop_count_; int push_count_; int ast_id_; + bool arguments_adaptor_; }; @@ -601,16 +646,18 @@ class TestContext: public AstContext { }; -class FunctionState BASE_EMBEDDED { +class FunctionState { public: FunctionState(HGraphBuilder* owner, CompilationInfo* info, - TypeFeedbackOracle* oracle); + TypeFeedbackOracle* oracle, + bool drop_extra); ~FunctionState(); CompilationInfo* compilation_info() { return compilation_info_; } TypeFeedbackOracle* oracle() { return oracle_; } AstContext* call_context() { return call_context_; } + bool drop_extra() { return drop_extra_; } HBasicBlock* function_return() { return function_return_; } TestContext* test_context() { return test_context_; } void ClearInlinedTestContext() { @@ -630,6 +677,10 @@ class FunctionState BASE_EMBEDDED { // inlined. NULL when not inlining. AstContext* call_context_; + // Indicate if we have to drop an extra value from the environment on + // return from inlined functions. + bool drop_extra_; + // When inlining in an effect of value context, this is the return block. // It is NULL otherwise. When inlining in a test context, there are a // pair of return blocks in the context. When not inlining, there is no @@ -647,6 +698,7 @@ class FunctionState BASE_EMBEDDED { class HGraphBuilder: public AstVisitor { public: enum BreakType { BREAK, CONTINUE }; + enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH }; // A class encapsulating (lazily-allocated) break and continue blocks for // a breakable statement. Separated from BreakAndContinueScope so that it @@ -726,6 +778,8 @@ class HGraphBuilder: public AstVisitor { TypeFeedbackOracle* oracle() const { return function_state()->oracle(); } + FunctionState* function_state() const { return function_state_; } + private: // Type of a member function that generates inline code for a native function. typedef void (HGraphBuilder::*InlineFunctionGenerator)(CallRuntime* call); @@ -743,8 +797,13 @@ class HGraphBuilder: public AstVisitor { static const int kMaxInlinedSize = 196; static const int kMaxSourceSize = 600; + // Even in the 'unlimited' case we have to have some limit in order not to + // overflow the stack. + static const int kUnlimitedMaxInlinedNodes = 1000; + static const int kUnlimitedMaxInlinedSize = 1000; + static const int kUnlimitedMaxSourceSize = 600; + // Simple accessors. - FunctionState* function_state() const { return function_state_; } void set_function_state(FunctionState* state) { function_state_ = state; } AstContext* ast_context() const { return ast_context_; } @@ -767,8 +826,9 @@ class HGraphBuilder: public AstVisitor { void ClearInlinedTestContext() { function_state()->ClearInlinedTestContext(); } - bool function_strict_mode() { - return function_state()->compilation_info()->is_strict_mode(); + StrictModeFlag function_strict_mode_flag() { + return function_state()->compilation_info()->is_classic_mode() + ? kNonStrictMode : kStrictMode; } // Generators for inline runtime functions. @@ -779,9 +839,9 @@ class HGraphBuilder: public AstVisitor { INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) #undef INLINE_FUNCTION_GENERATOR_DECLARATION - void HandleDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function); + void HandleVariableDeclaration(VariableProxy* proxy, + VariableMode mode, + FunctionLiteral* function); void VisitDelete(UnaryOperation* expr); void VisitVoid(UnaryOperation* expr); @@ -857,7 +917,7 @@ class HGraphBuilder: public AstVisitor { Representation rep); static Representation ToRepresentation(TypeInfo info); - void SetupScope(Scope* scope); + void SetUpScope(Scope* scope); virtual void VisitStatements(ZoneList<Statement*>* statements); #define DECLARE_VISIT(type) virtual void Visit##type(type* node); @@ -881,11 +941,12 @@ class HGraphBuilder: public AstVisitor { // Try to optimize fun.apply(receiver, arguments) pattern. bool TryCallApply(Call* expr); - bool TryInline(Call* expr); - bool TryInlineBuiltinFunction(Call* expr, + bool TryInline(Call* expr, bool drop_extra = false); + bool TryInlineBuiltinMethodCall(Call* expr, HValue* receiver, Handle<Map> receiver_map, CheckType check_type); + bool TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra); // If --trace-inlining, print a line of the inlining trace. Inlining // succeeded if the reason string is NULL and failed if there is a @@ -910,11 +971,12 @@ class HGraphBuilder: public AstVisitor { HValue* receiver, SmallMapList* types, Handle<String> name); - void HandleLiteralCompareTypeof(CompareOperation* compare_expr, - Expression* expr, + void HandleLiteralCompareTypeof(CompareOperation* expr, + HTypeof* typeof_expr, Handle<String> check); - void HandleLiteralCompareUndefined(CompareOperation* compare_expr, - Expression* expr); + void HandleLiteralCompareNil(CompareOperation* expr, + HValue* value, + NilValue nil); HStringCharCodeAt* BuildStringCharCodeAt(HValue* context, HValue* string, @@ -938,11 +1000,16 @@ class HGraphBuilder: public AstVisitor { HValue* val, ElementsKind elements_kind, bool is_store); + HInstruction* BuildFastElementAccess(HValue* elements, + HValue* checked_key, + HValue* val, + ElementsKind elements_kind, + bool is_store); HInstruction* BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, - Expression* expr, + Handle<Map> map, bool is_store); HValue* HandlePolymorphicElementAccess(HValue* object, HValue* key, @@ -1037,10 +1104,10 @@ class HValueMap: public ZoneObject { Resize(kInitialSize); } - void Kill(int flags); + void Kill(GVNFlagSet flags); void Add(HValue* value) { - present_flags_ |= value->flags(); + present_flags_.Add(value->gvn_flags()); Insert(value); } @@ -1073,7 +1140,8 @@ class HValueMap: public ZoneObject { int array_size_; int lists_size_; int count_; // The number of values stored in the HValueMap. - int present_flags_; // All flags that are in any value in the HValueMap. + GVNFlagSet present_flags_; // All flags that are in any value in the + // HValueMap. HValueMapListElement* array_; // Primary store - contains the first value // with a given hash. Colliding elements are stored in linked lists. HValueMapListElement* lists_; // The linked lists containing hash collisions. diff --git a/deps/v8/src/ia32/assembler-ia32-inl.h b/deps/v8/src/ia32/assembler-ia32-inl.h index 0ca2d6b4a8..ef109229a2 100644 --- a/deps/v8/src/ia32/assembler-ia32-inl.h +++ b/deps/v8/src/ia32/assembler-ia32-inl.h @@ -30,13 +30,15 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // A light-weight IA32 Assembler. #ifndef V8_IA32_ASSEMBLER_IA32_INL_H_ #define V8_IA32_ASSEMBLER_IA32_INL_H_ +#include "ia32/assembler-ia32.h" + #include "cpu.h" #include "debug.h" @@ -78,7 +80,9 @@ Address RelocInfo::target_address() { Address RelocInfo::target_address_address() { - ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); + ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY + || rmode_ == EMBEDDED_OBJECT + || rmode_ == EXTERNAL_REFERENCE); return reinterpret_cast<Address>(pc_); } @@ -88,9 +92,14 @@ int RelocInfo::target_address_size() { } -void RelocInfo::set_target_address(Address target) { - ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); +void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { Assembler::set_target_address_at(pc_, target); + ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); + if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -112,10 +121,16 @@ Object** RelocInfo::target_object_address() { } -void RelocInfo::set_target_object(Object* target) { +void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); Memory::Object_at(pc_) = target; CPU::FlushICache(pc_, sizeof(Address)); + if (mode == UPDATE_WRITE_BARRIER && + host() != NULL && + target->IsHeapObject()) { + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), &Memory::Object_at(pc_), HeapObject::cast(target)); + } } @@ -142,11 +157,18 @@ JSGlobalPropertyCell* RelocInfo::target_cell() { } -void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell) { +void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, + WriteBarrierMode mode) { ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; Memory::Address_at(pc_) = address; CPU::FlushICache(pc_, sizeof(Address)); + if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { + // TODO(1550) We are passing NULL as a slot because cell can never be on + // evacuation candidate. + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), NULL, cell); + } } @@ -161,6 +183,11 @@ void RelocInfo::set_call_address(Address target) { ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); Assembler::set_target_address_at(pc_ + 1, target); + if (host() != NULL) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -194,14 +221,14 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() { void RelocInfo::Visit(ObjectVisitor* visitor) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - visitor->VisitPointer(target_object_address()); + visitor->VisitEmbeddedPointer(this); CPU::FlushICache(pc_, sizeof(Address)); } else if (RelocInfo::IsCodeTarget(mode)) { visitor->VisitCodeTarget(this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { visitor->VisitGlobalPropertyCell(this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - visitor->VisitExternalReference(target_reference_address()); + visitor->VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); #ifdef ENABLE_DEBUGGER_SUPPORT // TODO(isolates): Get a cached isolate below. @@ -222,14 +249,14 @@ template<typename StaticVisitor> void RelocInfo::Visit(Heap* heap) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitPointer(heap, target_object_address()); + StaticVisitor::VisitEmbeddedPointer(heap, this); CPU::FlushICache(pc_, sizeof(Address)); } else if (RelocInfo::IsCodeTarget(mode)) { StaticVisitor::VisitCodeTarget(heap, this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { StaticVisitor::VisitGlobalPropertyCell(heap, this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - StaticVisitor::VisitExternalReference(target_reference_address()); + StaticVisitor::VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); #ifdef ENABLE_DEBUGGER_SUPPORT } else if (heap->isolate()->debug()->has_break_points() && diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 999647487e..bb050b63f9 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -55,6 +55,8 @@ uint64_t CpuFeatures::supported_ = 0; uint64_t CpuFeatures::found_by_runtime_probing_ = 0; +// The Probe method needs executable memory, so it uses Heap::CreateCode. +// Allocation failure is silent and leads to safe default. void CpuFeatures::Probe() { ASSERT(!initialized_); ASSERT(supported_ == 0); @@ -86,23 +88,23 @@ void CpuFeatures::Probe() { __ pushfd(); __ push(ecx); __ push(ebx); - __ mov(ebp, Operand(esp)); + __ mov(ebp, esp); // If we can modify bit 21 of the EFLAGS register, then CPUID is supported. __ pushfd(); __ pop(eax); - __ mov(edx, Operand(eax)); + __ mov(edx, eax); __ xor_(eax, 0x200000); // Flip bit 21. __ push(eax); __ popfd(); __ pushfd(); __ pop(eax); - __ xor_(eax, Operand(edx)); // Different if CPUID is supported. + __ xor_(eax, edx); // Different if CPUID is supported. __ j(not_zero, &cpuid); // CPUID not supported. Clear the supported features in edx:eax. - __ xor_(eax, Operand(eax)); - __ xor_(edx, Operand(edx)); + __ xor_(eax, eax); + __ xor_(edx, edx); __ jmp(&done); // Invoke CPUID with 1 in eax to get feature information in @@ -118,13 +120,13 @@ void CpuFeatures::Probe() { // Move the result from ecx:edx to edx:eax and make sure to mark the // CPUID feature as supported. - __ mov(eax, Operand(edx)); + __ mov(eax, edx); __ or_(eax, 1 << CPUID); - __ mov(edx, Operand(ecx)); + __ mov(edx, ecx); // Done. __ bind(&done); - __ mov(esp, Operand(ebp)); + __ mov(esp, ebp); __ pop(ebx); __ pop(ecx); __ popfd(); @@ -286,6 +288,18 @@ bool Operand::is_reg(Register reg) const { && ((buf_[0] & 0x07) == reg.code()); // register codes match. } + +bool Operand::is_reg_only() const { + return (buf_[0] & 0xF8) == 0xC0; // Addressing mode is register only. +} + + +Register Operand::reg() const { + ASSERT(is_reg_only()); + return Register::from_code(buf_[0] & 0x07); +} + + // ----------------------------------------------------------------------------- // Implementation of Assembler. @@ -336,7 +350,7 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) } #endif - // Setup buffer pointers. + // Set up buffer pointers. ASSERT(buffer_ != NULL); pc_ = buffer_; reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); @@ -363,7 +377,7 @@ void Assembler::GetCode(CodeDesc* desc) { // Finalize code (at this point overflow() may be true, but the gap ensures // that we are still not overlapping instructions and relocation info). ASSERT(pc_ <= reloc_info_writer.pos()); // No overlap. - // Setup code descriptor. + // Set up code descriptor. desc->buffer = buffer_; desc->buffer_size = buffer_size_; desc->instr_size = pc_offset(); @@ -374,8 +388,91 @@ void Assembler::GetCode(CodeDesc* desc) { void Assembler::Align(int m) { ASSERT(IsPowerOf2(m)); - while ((pc_offset() & (m - 1)) != 0) { - nop(); + int mask = m - 1; + int addr = pc_offset(); + Nop((m - (addr & mask)) & mask); +} + + +bool Assembler::IsNop(Address addr) { + Address a = addr; + while (*a == 0x66) a++; + if (*a == 0x90) return true; + if (a[0] == 0xf && a[1] == 0x1f) return true; + return false; +} + + +void Assembler::Nop(int bytes) { + EnsureSpace ensure_space(this); + + if (!CpuFeatures::IsSupported(SSE2)) { + // Older CPUs that do not support SSE2 may not support multibyte NOP + // instructions. + for (; bytes > 0; bytes--) { + EMIT(0x90); + } + return; + } + + // Multi byte nops from http://support.amd.com/us/Processor_TechDocs/40546.pdf + while (bytes > 0) { + switch (bytes) { + case 2: + EMIT(0x66); + case 1: + EMIT(0x90); + return; + case 3: + EMIT(0xf); + EMIT(0x1f); + EMIT(0); + return; + case 4: + EMIT(0xf); + EMIT(0x1f); + EMIT(0x40); + EMIT(0); + return; + case 6: + EMIT(0x66); + case 5: + EMIT(0xf); + EMIT(0x1f); + EMIT(0x44); + EMIT(0); + EMIT(0); + return; + case 7: + EMIT(0xf); + EMIT(0x1f); + EMIT(0x80); + EMIT(0); + EMIT(0); + EMIT(0); + EMIT(0); + return; + default: + case 11: + EMIT(0x66); + bytes--; + case 10: + EMIT(0x66); + bytes--; + case 9: + EMIT(0x66); + bytes--; + case 8: + EMIT(0xf); + EMIT(0x1f); + EMIT(0x84); + EMIT(0); + EMIT(0); + EMIT(0); + EMIT(0); + EMIT(0); + bytes -= 8; + } } } @@ -449,13 +546,6 @@ void Assembler::push(const Operand& src) { } -void Assembler::push(Handle<Object> handle) { - EnsureSpace ensure_space(this); - EMIT(0x68); - emit(handle); -} - - void Assembler::pop(Register dst) { ASSERT(reloc_info_writer.last_pc() != NULL); EnsureSpace ensure_space(this); @@ -614,26 +704,6 @@ void Assembler::movzx_w(Register dst, const Operand& src) { } -void Assembler::cmov(Condition cc, Register dst, int32_t imm32) { - ASSERT(CpuFeatures::IsEnabled(CMOV)); - EnsureSpace ensure_space(this); - UNIMPLEMENTED(); - USE(cc); - USE(dst); - USE(imm32); -} - - -void Assembler::cmov(Condition cc, Register dst, Handle<Object> handle) { - ASSERT(CpuFeatures::IsEnabled(CMOV)); - EnsureSpace ensure_space(this); - UNIMPLEMENTED(); - USE(cc); - USE(dst); - USE(handle); -} - - void Assembler::cmov(Condition cc, Register dst, const Operand& src) { ASSERT(CpuFeatures::IsEnabled(CMOV)); EnsureSpace ensure_space(this); @@ -701,6 +771,13 @@ void Assembler::add(Register dst, const Operand& src) { } +void Assembler::add(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x01); + emit_operand(src, dst); +} + + void Assembler::add(const Operand& dst, const Immediate& x) { ASSERT(reloc_info_writer.last_pc() != NULL); EnsureSpace ensure_space(this); @@ -741,25 +818,29 @@ void Assembler::and_(const Operand& dst, Register src) { void Assembler::cmpb(const Operand& op, int8_t imm8) { EnsureSpace ensure_space(this); - EMIT(0x80); - emit_operand(edi, op); // edi == 7 + if (op.is_reg(eax)) { + EMIT(0x3C); + } else { + EMIT(0x80); + emit_operand(edi, op); // edi == 7 + } EMIT(imm8); } -void Assembler::cmpb(const Operand& dst, Register src) { - ASSERT(src.is_byte_register()); +void Assembler::cmpb(const Operand& op, Register reg) { + ASSERT(reg.is_byte_register()); EnsureSpace ensure_space(this); EMIT(0x38); - emit_operand(src, dst); + emit_operand(reg, op); } -void Assembler::cmpb(Register dst, const Operand& src) { - ASSERT(dst.is_byte_register()); +void Assembler::cmpb(Register reg, const Operand& op) { + ASSERT(reg.is_byte_register()); EnsureSpace ensure_space(this); EMIT(0x3A); - emit_operand(dst, src); + emit_operand(reg, op); } @@ -1069,18 +1150,6 @@ void Assembler::shr_cl(Register dst) { } -void Assembler::subb(const Operand& op, int8_t imm8) { - EnsureSpace ensure_space(this); - if (op.is_reg(eax)) { - EMIT(0x2c); - } else { - EMIT(0x80); - emit_operand(ebp, op); // ebp == 5 - } - EMIT(imm8); -} - - void Assembler::sub(const Operand& dst, const Immediate& x) { EnsureSpace ensure_space(this); emit_arith(5, dst, x); @@ -1094,14 +1163,6 @@ void Assembler::sub(Register dst, const Operand& src) { } -void Assembler::subb(Register dst, const Operand& src) { - ASSERT(dst.code() < 4); - EnsureSpace ensure_space(this); - EMIT(0x2A); - emit_operand(dst, src); -} - - void Assembler::sub(const Operand& dst, Register src) { EnsureSpace ensure_space(this); EMIT(0x29); @@ -1158,6 +1219,10 @@ void Assembler::test(const Operand& op, const Immediate& imm) { void Assembler::test_b(const Operand& op, uint8_t imm8) { + if (op.is_reg_only() && op.reg().code() >= 4) { + test(op, Immediate(imm8)); + return; + } EnsureSpace ensure_space(this); EMIT(0xF6); emit_operand(eax, op); @@ -1178,10 +1243,10 @@ void Assembler::xor_(Register dst, const Operand& src) { } -void Assembler::xor_(const Operand& src, Register dst) { +void Assembler::xor_(const Operand& dst, Register src) { EnsureSpace ensure_space(this); EMIT(0x31); - emit_operand(dst, src); + emit_operand(src, dst); } @@ -1637,6 +1702,13 @@ void Assembler::fsin() { } +void Assembler::fptan() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF2); +} + + void Assembler::fyl2x() { EnsureSpace ensure_space(this); EMIT(0xD9); @@ -1644,6 +1716,27 @@ void Assembler::fyl2x() { } +void Assembler::f2xm1() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF0); +} + + +void Assembler::fscale() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFD); +} + + +void Assembler::fninit() { + EnsureSpace ensure_space(this); + EMIT(0xDB); + EMIT(0xE3); +} + + void Assembler::fadd(int i) { EnsureSpace ensure_space(this); emit_farith(0xDC, 0xC0, i); @@ -1957,6 +2050,16 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { } +void Assembler::ucomisd(XMMRegister dst, const Operand& src) { + ASSERT(CpuFeatures::IsEnabled(SSE2)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x2E); + emit_sse_operand(dst, src); +} + + void Assembler::roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode) { ASSERT(CpuFeatures::IsEnabled(SSE4_1)); EnsureSpace ensure_space(this); @@ -2162,6 +2265,19 @@ void Assembler::movd(const Operand& dst, XMMRegister src) { } +void Assembler::extractps(Register dst, XMMRegister src, byte imm8) { + ASSERT(CpuFeatures::IsSupported(SSE4_1)); + ASSERT(is_uint8(imm8)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x3A); + EMIT(0x17); + emit_sse_operand(dst, src); + EMIT(imm8); +} + + void Assembler::pand(XMMRegister dst, XMMRegister src) { ASSERT(CpuFeatures::IsEnabled(SSE2)); EnsureSpace ensure_space(this); @@ -2341,7 +2457,7 @@ void Assembler::GrowBuffer() { V8::FatalProcessOutOfMemory("Assembler::GrowBuffer"); } - // Setup new buffer. + // Set up new buffer. desc.buffer = NewArray<byte>(desc.buffer_size); desc.instr_size = pc_offset(); desc.reloc_size = (buffer_ + buffer_size_) - (reloc_info_writer.pos()); @@ -2471,7 +2587,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { return; } } - RelocInfo rinfo(pc_, rmode, data); + RelocInfo rinfo(pc_, rmode, data, NULL); reloc_info_writer.Write(&rinfo); } diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 4698e3ed1b..e5ae475e71 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -75,6 +75,8 @@ struct Register { static inline Register FromAllocationIndex(int index); static Register from_code(int code) { + ASSERT(code >= 0); + ASSERT(code < kNumRegisters); Register r = { code }; return r; } @@ -300,9 +302,6 @@ enum ScaleFactor { class Operand BASE_EMBEDDED { public: - // reg - INLINE(explicit Operand(Register reg)); - // XMM reg INLINE(explicit Operand(XMMRegister xmm_reg)); @@ -347,12 +346,16 @@ class Operand BASE_EMBEDDED { // Returns true if this Operand is a wrapper for the specified register. bool is_reg(Register reg) const; + // Returns true if this Operand is a wrapper for one register. + bool is_reg_only() const; + + // Asserts that this Operand is a wrapper for one register and returns the + // register. + Register reg() const; + private: - byte buf_[6]; - // The number of bytes in buf_. - unsigned int len_; - // Only valid if len_ > 4. - RelocInfo::Mode rmode_; + // reg + INLINE(explicit Operand(Register reg)); // Set the ModRM byte without an encoded 'reg' register. The // register is encoded later as part of the emit_operand operation. @@ -362,7 +365,15 @@ class Operand BASE_EMBEDDED { inline void set_disp8(int8_t disp); inline void set_dispr(int32_t disp, RelocInfo::Mode rmode); + byte buf_[6]; + // The number of bytes in buf_. + unsigned int len_; + // Only valid if len_ > 4. + RelocInfo::Mode rmode_; + friend class Assembler; + friend class MacroAssembler; + friend class LCodeGen; }; @@ -610,8 +621,6 @@ class Assembler : public AssemblerBase { // The debug break slot must be able to contain a call instruction. static const int kDebugBreakSlotLength = kCallInstructionLength; - // One byte opcode for test eax,0xXXXXXXXX. - static const byte kTestEaxByte = 0xA9; // One byte opcode for test al, 0xXX. static const byte kTestAlByte = 0xA8; // One byte opcode for nop. @@ -648,6 +657,7 @@ class Assembler : public AssemblerBase { // possible to align the pc offset to a multiple // of m. m must be a power of 2. void Align(int m); + void Nop(int bytes = 1); // Aligns code to something that's optimal for a jump target for the platform. void CodeTargetAlign(); @@ -662,7 +672,6 @@ class Assembler : public AssemblerBase { void push_imm32(int32_t imm32); void push(Register src); void push(const Operand& src); - void push(Handle<Object> handle); void pop(Register dst); void pop(const Operand& dst); @@ -671,7 +680,9 @@ class Assembler : public AssemblerBase { void leave(); // Moves + void mov_b(Register dst, Register src) { mov_b(dst, Operand(src)); } void mov_b(Register dst, const Operand& src); + void mov_b(Register dst, int8_t imm8) { mov_b(Operand(dst), imm8); } void mov_b(const Operand& dst, int8_t imm8); void mov_b(const Operand& dst, Register src); @@ -687,17 +698,22 @@ class Assembler : public AssemblerBase { void mov(const Operand& dst, Handle<Object> handle); void mov(const Operand& dst, Register src); + void movsx_b(Register dst, Register src) { movsx_b(dst, Operand(src)); } void movsx_b(Register dst, const Operand& src); + void movsx_w(Register dst, Register src) { movsx_w(dst, Operand(src)); } void movsx_w(Register dst, const Operand& src); + void movzx_b(Register dst, Register src) { movzx_b(dst, Operand(src)); } void movzx_b(Register dst, const Operand& src); + void movzx_w(Register dst, Register src) { movzx_w(dst, Operand(src)); } void movzx_w(Register dst, const Operand& src); // Conditional moves - void cmov(Condition cc, Register dst, int32_t imm32); - void cmov(Condition cc, Register dst, Handle<Object> handle); + void cmov(Condition cc, Register dst, Register src) { + cmov(cc, dst, Operand(src)); + } void cmov(Condition cc, Register dst, const Operand& src); // Flag management. @@ -715,24 +731,31 @@ class Assembler : public AssemblerBase { void adc(Register dst, int32_t imm32); void adc(Register dst, const Operand& src); + void add(Register dst, Register src) { add(dst, Operand(src)); } void add(Register dst, const Operand& src); + void add(const Operand& dst, Register src); + void add(Register dst, const Immediate& imm) { add(Operand(dst), imm); } void add(const Operand& dst, const Immediate& x); void and_(Register dst, int32_t imm32); void and_(Register dst, const Immediate& x); + void and_(Register dst, Register src) { and_(dst, Operand(src)); } void and_(Register dst, const Operand& src); - void and_(const Operand& src, Register dst); + void and_(const Operand& dst, Register src); void and_(const Operand& dst, const Immediate& x); + void cmpb(Register reg, int8_t imm8) { cmpb(Operand(reg), imm8); } void cmpb(const Operand& op, int8_t imm8); - void cmpb(Register src, const Operand& dst); - void cmpb(const Operand& dst, Register src); + void cmpb(Register reg, const Operand& op); + void cmpb(const Operand& op, Register reg); void cmpb_al(const Operand& op); void cmpw_ax(const Operand& op); void cmpw(const Operand& op, Immediate imm16); void cmp(Register reg, int32_t imm32); void cmp(Register reg, Handle<Object> handle); + void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); } void cmp(Register reg, const Operand& op); + void cmp(Register reg, const Immediate& imm) { cmp(Operand(reg), imm); } void cmp(const Operand& op, const Immediate& imm); void cmp(const Operand& op, Handle<Object> handle); @@ -748,6 +771,7 @@ class Assembler : public AssemblerBase { // Signed multiply instructions. void imul(Register src); // edx:eax = eax * src. + void imul(Register dst, Register src) { imul(dst, Operand(src)); } void imul(Register dst, const Operand& src); // dst = dst * src. void imul(Register dst, Register src, int32_t imm32); // dst = src * imm32. @@ -764,8 +788,10 @@ class Assembler : public AssemblerBase { void not_(Register dst); void or_(Register dst, int32_t imm32); + void or_(Register dst, Register src) { or_(dst, Operand(src)); } void or_(Register dst, const Operand& src); void or_(const Operand& dst, Register src); + void or_(Register dst, const Immediate& imm) { or_(Operand(dst), imm); } void or_(const Operand& dst, const Immediate& x); void rcl(Register dst, uint8_t imm8); @@ -776,35 +802,42 @@ class Assembler : public AssemblerBase { void sbb(Register dst, const Operand& src); + void shld(Register dst, Register src) { shld(dst, Operand(src)); } void shld(Register dst, const Operand& src); void shl(Register dst, uint8_t imm8); void shl_cl(Register dst); + void shrd(Register dst, Register src) { shrd(dst, Operand(src)); } void shrd(Register dst, const Operand& src); void shr(Register dst, uint8_t imm8); void shr_cl(Register dst); - void subb(const Operand& dst, int8_t imm8); - void subb(Register dst, const Operand& src); + void sub(Register dst, const Immediate& imm) { sub(Operand(dst), imm); } void sub(const Operand& dst, const Immediate& x); + void sub(Register dst, Register src) { sub(dst, Operand(src)); } void sub(Register dst, const Operand& src); void sub(const Operand& dst, Register src); void test(Register reg, const Immediate& imm); + void test(Register reg0, Register reg1) { test(reg0, Operand(reg1)); } void test(Register reg, const Operand& op); void test_b(Register reg, const Operand& op); void test(const Operand& op, const Immediate& imm); + void test_b(Register reg, uint8_t imm8) { test_b(Operand(reg), imm8); } void test_b(const Operand& op, uint8_t imm8); void xor_(Register dst, int32_t imm32); + void xor_(Register dst, Register src) { xor_(dst, Operand(src)); } void xor_(Register dst, const Operand& src); - void xor_(const Operand& src, Register dst); + void xor_(const Operand& dst, Register src); + void xor_(Register dst, const Immediate& imm) { xor_(Operand(dst), imm); } void xor_(const Operand& dst, const Immediate& x); // Bit operations. void bt(const Operand& dst, Register src); + void bts(Register dst, Register src) { bts(Operand(dst), src); } void bts(const Operand& dst, Register src); // Miscellaneous @@ -835,6 +868,7 @@ class Assembler : public AssemblerBase { void call(Label* L); void call(byte* entry, RelocInfo::Mode rmode); int CallSize(const Operand& adr); + void call(Register reg) { call(Operand(reg)); } void call(const Operand& adr); int CallSize(Handle<Code> code, RelocInfo::Mode mode); void call(Handle<Code> code, @@ -845,6 +879,7 @@ class Assembler : public AssemblerBase { // unconditional jump to L void jmp(Label* L, Label::Distance distance = Label::kFar); void jmp(byte* entry, RelocInfo::Mode rmode); + void jmp(Register reg) { jmp(Operand(reg)); } void jmp(const Operand& adr); void jmp(Handle<Code> code, RelocInfo::Mode rmode); @@ -887,7 +922,11 @@ class Assembler : public AssemblerBase { void fchs(); void fcos(); void fsin(); + void fptan(); void fyl2x(); + void f2xm1(); + void fscale(); + void fninit(); void fadd(int i); void fsub(int i); @@ -929,6 +968,7 @@ class Assembler : public AssemblerBase { void cvttss2si(Register dst, const Operand& src); void cvttsd2si(Register dst, const Operand& src); + void cvtsi2sd(XMMRegister dst, Register src) { cvtsi2sd(dst, Operand(src)); } void cvtsi2sd(XMMRegister dst, const Operand& src); void cvtss2sd(XMMRegister dst, XMMRegister src); void cvtsd2ss(XMMRegister dst, XMMRegister src); @@ -944,6 +984,7 @@ class Assembler : public AssemblerBase { void andpd(XMMRegister dst, XMMRegister src); void ucomisd(XMMRegister dst, XMMRegister src); + void ucomisd(XMMRegister dst, const Operand& src); enum RoundingMode { kRoundToNearest = 0x0, @@ -969,13 +1010,16 @@ class Assembler : public AssemblerBase { void movdbl(XMMRegister dst, const Operand& src); void movdbl(const Operand& dst, XMMRegister src); + void movd(XMMRegister dst, Register src) { movd(dst, Operand(src)); } void movd(XMMRegister dst, const Operand& src); - void movd(const Operand& src, XMMRegister dst); + void movd(Register dst, XMMRegister src) { movd(Operand(dst), src); } + void movd(const Operand& dst, XMMRegister src); void movsd(XMMRegister dst, XMMRegister src); void movss(XMMRegister dst, const Operand& src); - void movss(const Operand& src, XMMRegister dst); + void movss(const Operand& dst, XMMRegister src); void movss(XMMRegister dst, XMMRegister src); + void extractps(Register dst, XMMRegister src, byte imm8); void pand(XMMRegister dst, XMMRegister src); void pxor(XMMRegister dst, XMMRegister src); @@ -987,11 +1031,17 @@ class Assembler : public AssemblerBase { void psrlq(XMMRegister reg, int8_t shift); void psrlq(XMMRegister dst, XMMRegister src); void pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle); + void pextrd(Register dst, XMMRegister src, int8_t offset) { + pextrd(Operand(dst), src, offset); + } void pextrd(const Operand& dst, XMMRegister src, int8_t offset); + void pinsrd(XMMRegister dst, Register src, int8_t offset) { + pinsrd(dst, Operand(src), offset); + } void pinsrd(XMMRegister dst, const Operand& src, int8_t offset); // Parallel XMM operations. - void movntdqa(XMMRegister src, const Operand& dst); + void movntdqa(XMMRegister dst, const Operand& src); void movntdq(const Operand& dst, XMMRegister src); // Prefetch src position into cache level. // Level 1, 2 or 3 specifies CPU cache level. Level 0 specifies a @@ -1033,7 +1083,7 @@ class Assembler : public AssemblerBase { // Get the number of bytes available in the buffer. inline int available_space() const { return reloc_info_writer.pos() - pc_; } - static bool IsNop(Address addr) { return *addr == 0x90; } + static bool IsNop(Address addr); PositionsRecorder* positions_recorder() { return &positions_recorder_; } @@ -1045,6 +1095,9 @@ class Assembler : public AssemblerBase { static const int kMaximalBufferSize = 512*MB; static const int kMinimalBufferSize = 4*KB; + byte byte_at(int pos) { return buffer_[pos]; } + void set_byte_at(int pos, byte value) { buffer_[pos] = value; } + protected: bool emit_debug_code() const { return emit_debug_code_; } @@ -1057,9 +1110,8 @@ class Assembler : public AssemblerBase { byte* addr_at(int pos) { return buffer_ + pos; } + private: - byte byte_at(int pos) { return buffer_[pos]; } - void set_byte_at(int pos, byte value) { buffer_[pos] = value; } uint32_t long_at(int pos) { return *reinterpret_cast<uint32_t*>(addr_at(pos)); } diff --git a/deps/v8/src/ia32/builtins-ia32.cc b/deps/v8/src/ia32/builtins-ia32.cc index 310ea3d123..efa3456d8e 100644 --- a/deps/v8/src/ia32/builtins-ia32.cc +++ b/deps/v8/src/ia32/builtins-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -69,308 +69,288 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, // JumpToExternalReference expects eax to contain the number of arguments // including the receiver and the extra arguments. - __ add(Operand(eax), Immediate(num_extra_args + 1)); + __ add(eax, Immediate(num_extra_args + 1)); __ JumpToExternalReference(ExternalReference(id, masm->isolate())); } -void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { +static void Generate_JSConstructStubHelper(MacroAssembler* masm, + bool is_api_function, + bool count_constructions) { // ----------- S t a t e ------------- // -- eax: number of arguments // -- edi: constructor function // ----------------------------------- - Label non_function_call; - // Check that function is not a smi. - __ JumpIfSmi(edi, &non_function_call); - // Check that function is a JSFunction. - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &non_function_call); - - // Jump to the function-specific construct stub. - __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset)); - __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); - __ jmp(Operand(ebx)); - - // edi: called object - // eax: number of arguments - __ bind(&non_function_call); - // Set expected number of arguments to zero (not changing eax). - __ Set(ebx, Immediate(0)); - __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); - Handle<Code> arguments_adaptor = - masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); - __ SetCallKind(ecx, CALL_AS_METHOD); - __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET); -} - - -static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool count_constructions) { // Should never count constructions for api objects. ASSERT(!is_api_function || !count_constructions); // Enter a construct frame. - __ EnterConstructFrame(); + { + FrameScope scope(masm, StackFrame::CONSTRUCT); - // Store a smi-tagged arguments count on the stack. - __ SmiTag(eax); - __ push(eax); + // Store a smi-tagged arguments count on the stack. + __ SmiTag(eax); + __ push(eax); - // Push the function to invoke on the stack. - __ push(edi); + // Push the function to invoke on the stack. + __ push(edi); - // Try to allocate the object without transitioning into C code. If any of the - // preconditions is not met, the code bails out to the runtime call. - Label rt_call, allocated; - if (FLAG_inline_new) { - Label undo_allocation; + // Try to allocate the object without transitioning into C code. If any of + // the preconditions is not met, the code bails out to the runtime call. + Label rt_call, allocated; + if (FLAG_inline_new) { + Label undo_allocation; #ifdef ENABLE_DEBUGGER_SUPPORT - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(masm->isolate()); - __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); - __ j(not_equal, &rt_call); + ExternalReference debug_step_in_fp = + ExternalReference::debug_step_in_fp_address(masm->isolate()); + __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); + __ j(not_equal, &rt_call); #endif - // Verified that the constructor is a JSFunction. - // Load the initial map and verify that it is in fact a map. - // edi: constructor - __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi - __ JumpIfSmi(eax, &rt_call); - // edi: constructor - // eax: initial map (if proven valid below) - __ CmpObjectType(eax, MAP_TYPE, ebx); - __ j(not_equal, &rt_call); - - // Check that the constructor is not constructing a JSFunction (see comments - // in Runtime_NewObject in runtime.cc). In which case the initial map's - // instance type would be JS_FUNCTION_TYPE. - // edi: constructor - // eax: initial map - __ CmpInstanceType(eax, JS_FUNCTION_TYPE); - __ j(equal, &rt_call); - - if (count_constructions) { - Label allocate; - // Decrease generous allocation count. - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ dec_b(FieldOperand(ecx, SharedFunctionInfo::kConstructionCountOffset)); - __ j(not_zero, &allocate); + // Verified that the constructor is a JSFunction. + // Load the initial map and verify that it is in fact a map. + // edi: constructor + __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi + __ JumpIfSmi(eax, &rt_call); + // edi: constructor + // eax: initial map (if proven valid below) + __ CmpObjectType(eax, MAP_TYPE, ebx); + __ j(not_equal, &rt_call); + + // Check that the constructor is not constructing a JSFunction (see + // comments in Runtime_NewObject in runtime.cc). In which case the + // initial map's instance type would be JS_FUNCTION_TYPE. + // edi: constructor + // eax: initial map + __ CmpInstanceType(eax, JS_FUNCTION_TYPE); + __ j(equal, &rt_call); - __ push(eax); - __ push(edi); + if (count_constructions) { + Label allocate; + // Decrease generous allocation count. + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ dec_b(FieldOperand(ecx, + SharedFunctionInfo::kConstructionCountOffset)); + __ j(not_zero, &allocate); - __ push(edi); // constructor - // The call will replace the stub, so the countdown is only done once. - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); + __ push(eax); + __ push(edi); - __ pop(edi); - __ pop(eax); + __ push(edi); // constructor + // The call will replace the stub, so the countdown is only done once. + __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - __ bind(&allocate); - } + __ pop(edi); + __ pop(eax); - // Now allocate the JSObject on the heap. - // edi: constructor - // eax: initial map - __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset)); - __ shl(edi, kPointerSizeLog2); - __ AllocateInNewSpace(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS); - // Allocated the JSObject, now initialize the fields. - // eax: initial map - // ebx: JSObject - // edi: start of next object - __ mov(Operand(ebx, JSObject::kMapOffset), eax); - Factory* factory = masm->isolate()->factory(); - __ mov(ecx, factory->empty_fixed_array()); - __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx); - __ mov(Operand(ebx, JSObject::kElementsOffset), ecx); - // Set extra fields in the newly allocated object. - // eax: initial map - // ebx: JSObject - // edi: start of next object - { Label loop, entry; - // To allow for truncation. + __ bind(&allocate); + } + + // Now allocate the JSObject on the heap. + // edi: constructor + // eax: initial map + __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset)); + __ shl(edi, kPointerSizeLog2); + __ AllocateInNewSpace( + edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS); + // Allocated the JSObject, now initialize the fields. + // eax: initial map + // ebx: JSObject + // edi: start of next object + __ mov(Operand(ebx, JSObject::kMapOffset), eax); + Factory* factory = masm->isolate()->factory(); + __ mov(ecx, factory->empty_fixed_array()); + __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx); + __ mov(Operand(ebx, JSObject::kElementsOffset), ecx); + // Set extra fields in the newly allocated object. + // eax: initial map + // ebx: JSObject + // edi: start of next object + __ lea(ecx, Operand(ebx, JSObject::kHeaderSize)); + __ mov(edx, factory->undefined_value()); if (count_constructions) { + __ movzx_b(esi, + FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); + __ lea(esi, + Operand(ebx, esi, times_pointer_size, JSObject::kHeaderSize)); + // esi: offset of first field after pre-allocated fields + if (FLAG_debug_code) { + __ cmp(esi, edi); + __ Assert(less_equal, + "Unexpected number of pre-allocated property fields."); + } + __ InitializeFieldsWithFiller(ecx, esi, edx); __ mov(edx, factory->one_pointer_filler_map()); - } else { + } + __ InitializeFieldsWithFiller(ecx, edi, edx); + + // Add the object tag to make the JSObject real, so that we can continue + // and jump into the continuation code at any time from now on. Any + // failures need to undo the allocation, so that the heap is in a + // consistent state and verifiable. + // eax: initial map + // ebx: JSObject + // edi: start of next object + __ or_(ebx, Immediate(kHeapObjectTag)); + + // Check if a non-empty properties array is needed. + // Allocate and initialize a FixedArray if it is. + // eax: initial map + // ebx: JSObject + // edi: start of next object + // Calculate the total number of properties described by the map. + __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); + __ movzx_b(ecx, + FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); + __ add(edx, ecx); + // Calculate unused properties past the end of the in-object properties. + __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); + __ sub(edx, ecx); + // Done if no extra properties are to be allocated. + __ j(zero, &allocated); + __ Assert(positive, "Property allocation count failed."); + + // Scale the number of elements by pointer size and add the header for + // FixedArrays to the start of the next object calculation from above. + // ebx: JSObject + // edi: start of next object (will be start of FixedArray) + // edx: number of elements in properties array + __ AllocateInNewSpace(FixedArray::kHeaderSize, + times_pointer_size, + edx, + edi, + ecx, + no_reg, + &undo_allocation, + RESULT_CONTAINS_TOP); + + // Initialize the FixedArray. + // ebx: JSObject + // edi: FixedArray + // edx: number of elements + // ecx: start of next object + __ mov(eax, factory->fixed_array_map()); + __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map + __ SmiTag(edx); + __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length + + // Initialize the fields to undefined. + // ebx: JSObject + // edi: FixedArray + // ecx: start of next object + { Label loop, entry; __ mov(edx, factory->undefined_value()); + __ lea(eax, Operand(edi, FixedArray::kHeaderSize)); + __ jmp(&entry); + __ bind(&loop); + __ mov(Operand(eax, 0), edx); + __ add(eax, Immediate(kPointerSize)); + __ bind(&entry); + __ cmp(eax, ecx); + __ j(below, &loop); } - __ lea(ecx, Operand(ebx, JSObject::kHeaderSize)); - __ jmp(&entry); - __ bind(&loop); - __ mov(Operand(ecx, 0), edx); - __ add(Operand(ecx), Immediate(kPointerSize)); - __ bind(&entry); - __ cmp(ecx, Operand(edi)); - __ j(less, &loop); - } - - // Add the object tag to make the JSObject real, so that we can continue and - // jump into the continuation code at any time from now on. Any failures - // need to undo the allocation, so that the heap is in a consistent state - // and verifiable. - // eax: initial map - // ebx: JSObject - // edi: start of next object - __ or_(Operand(ebx), Immediate(kHeapObjectTag)); - - // Check if a non-empty properties array is needed. - // Allocate and initialize a FixedArray if it is. - // eax: initial map - // ebx: JSObject - // edi: start of next object - // Calculate the total number of properties described by the map. - __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); - __ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); - __ add(edx, Operand(ecx)); - // Calculate unused properties past the end of the in-object properties. - __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); - __ sub(edx, Operand(ecx)); - // Done if no extra properties are to be allocated. - __ j(zero, &allocated); - __ Assert(positive, "Property allocation count failed."); - - // Scale the number of elements by pointer size and add the header for - // FixedArrays to the start of the next object calculation from above. - // ebx: JSObject - // edi: start of next object (will be start of FixedArray) - // edx: number of elements in properties array - __ AllocateInNewSpace(FixedArray::kHeaderSize, - times_pointer_size, - edx, - edi, - ecx, - no_reg, - &undo_allocation, - RESULT_CONTAINS_TOP); - - // Initialize the FixedArray. - // ebx: JSObject - // edi: FixedArray - // edx: number of elements - // ecx: start of next object - __ mov(eax, factory->fixed_array_map()); - __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map - __ SmiTag(edx); - __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length - - // Initialize the fields to undefined. - // ebx: JSObject - // edi: FixedArray - // ecx: start of next object - { Label loop, entry; - __ mov(edx, factory->undefined_value()); - __ lea(eax, Operand(edi, FixedArray::kHeaderSize)); - __ jmp(&entry); - __ bind(&loop); - __ mov(Operand(eax, 0), edx); - __ add(Operand(eax), Immediate(kPointerSize)); - __ bind(&entry); - __ cmp(eax, Operand(ecx)); - __ j(below, &loop); - } - // Store the initialized FixedArray into the properties field of - // the JSObject - // ebx: JSObject - // edi: FixedArray - __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag - __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi); + // Store the initialized FixedArray into the properties field of + // the JSObject + // ebx: JSObject + // edi: FixedArray + __ or_(edi, Immediate(kHeapObjectTag)); // add the heap tag + __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi); - // Continue with JSObject being successfully allocated - // ebx: JSObject - __ jmp(&allocated); + // Continue with JSObject being successfully allocated + // ebx: JSObject + __ jmp(&allocated); - // Undo the setting of the new top so that the heap is verifiable. For - // example, the map's unused properties potentially do not match the - // allocated objects unused properties. - // ebx: JSObject (previous new top) - __ bind(&undo_allocation); - __ UndoAllocationInNewSpace(ebx); - } + // Undo the setting of the new top so that the heap is verifiable. For + // example, the map's unused properties potentially do not match the + // allocated objects unused properties. + // ebx: JSObject (previous new top) + __ bind(&undo_allocation); + __ UndoAllocationInNewSpace(ebx); + } - // Allocate the new receiver object using the runtime call. - __ bind(&rt_call); - // Must restore edi (constructor) before calling runtime. - __ mov(edi, Operand(esp, 0)); - // edi: function (constructor) - __ push(edi); - __ CallRuntime(Runtime::kNewObject, 1); - __ mov(ebx, Operand(eax)); // store result in ebx + // Allocate the new receiver object using the runtime call. + __ bind(&rt_call); + // Must restore edi (constructor) before calling runtime. + __ mov(edi, Operand(esp, 0)); + // edi: function (constructor) + __ push(edi); + __ CallRuntime(Runtime::kNewObject, 1); + __ mov(ebx, eax); // store result in ebx - // New object allocated. - // ebx: newly allocated object - __ bind(&allocated); - // Retrieve the function from the stack. - __ pop(edi); + // New object allocated. + // ebx: newly allocated object + __ bind(&allocated); + // Retrieve the function from the stack. + __ pop(edi); - // Retrieve smi-tagged arguments count from the stack. - __ mov(eax, Operand(esp, 0)); - __ SmiUntag(eax); + // Retrieve smi-tagged arguments count from the stack. + __ mov(eax, Operand(esp, 0)); + __ SmiUntag(eax); - // Push the allocated receiver to the stack. We need two copies - // because we may have to return the original one and the calling - // conventions dictate that the called function pops the receiver. - __ push(ebx); - __ push(ebx); + // Push the allocated receiver to the stack. We need two copies + // because we may have to return the original one and the calling + // conventions dictate that the called function pops the receiver. + __ push(ebx); + __ push(ebx); - // Setup pointer to last argument. - __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); + // Set up pointer to last argument. + __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(ecx, Operand(eax)); - __ jmp(&entry); - __ bind(&loop); - __ push(Operand(ebx, ecx, times_4, 0)); - __ bind(&entry); - __ dec(ecx); - __ j(greater_equal, &loop); + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ mov(ecx, eax); + __ jmp(&entry); + __ bind(&loop); + __ push(Operand(ebx, ecx, times_4, 0)); + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); + + // Call the function. + if (is_api_function) { + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + Handle<Code> code = + masm->isolate()->builtins()->HandleApiCallConstruct(); + ParameterCount expected(0); + __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET, + CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); + } else { + ParameterCount actual(eax); + __ InvokeFunction(edi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - // Call the function. - if (is_api_function) { - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - Handle<Code> code = - masm->isolate()->builtins()->HandleApiCallConstruct(); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET, - CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); - } else { - ParameterCount actual(eax); - __ InvokeFunction(edi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - } + // Restore context from the frame. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - // Restore context from the frame. - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, exit; - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; + // If the result is a smi, it is *not* an object in the ECMA sense. + __ JumpIfSmi(eax, &use_receiver); - // If the result is a smi, it is *not* an object in the ECMA sense. - __ JumpIfSmi(eax, &use_receiver); + // If the type of the result (stored in its map) is less than + // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. + __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); + __ j(above_equal, &exit); - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); - __ j(above_equal, &exit); + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ mov(eax, Operand(esp, 0)); - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ mov(eax, Operand(esp, 0)); + // Restore the arguments count and leave the construct frame. + __ bind(&exit); + __ mov(ebx, Operand(esp, kPointerSize)); // Get arguments count. - // Restore the arguments count and leave the construct frame. - __ bind(&exit); - __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count - __ LeaveConstructFrame(); + // Leave construct frame. + } // Remove caller arguments from the stack and return. STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); @@ -399,57 +379,58 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { - // Clear the context before we push it when entering the JS frame. + // Clear the context before we push it when entering the internal frame. __ Set(esi, Immediate(0)); - // Enter an internal frame. - __ EnterInternalFrame(); - - // Load the previous frame pointer (ebx) to access C arguments - __ mov(ebx, Operand(ebp, 0)); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Get the function from the frame and setup the context. - __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); - __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset)); + // Load the previous frame pointer (ebx) to access C arguments + __ mov(ebx, Operand(ebp, 0)); - // Push the function and the receiver onto the stack. - __ push(ecx); - __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); + // Get the function from the frame and setup the context. + __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); + __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset)); - // Load the number of arguments and setup pointer to the arguments. - __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset)); - __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset)); + // Push the function and the receiver onto the stack. + __ push(ecx); + __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); - // Copy arguments to the stack in a loop. - Label loop, entry; - __ Set(ecx, Immediate(0)); - __ jmp(&entry); - __ bind(&loop); - __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv - __ push(Operand(edx, 0)); // dereference handle - __ inc(Operand(ecx)); - __ bind(&entry); - __ cmp(ecx, Operand(eax)); - __ j(not_equal, &loop); + // Load the number of arguments and setup pointer to the arguments. + __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset)); + __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset)); - // Get the function from the stack and call it. - __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver + // Copy arguments to the stack in a loop. + Label loop, entry; + __ Set(ecx, Immediate(0)); + __ jmp(&entry); + __ bind(&loop); + __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv + __ push(Operand(edx, 0)); // dereference handle + __ inc(ecx); + __ bind(&entry); + __ cmp(ecx, eax); + __ j(not_equal, &loop); + + // Get the function from the stack and call it. + // kPointerSize for the receiver. + __ mov(edi, Operand(esp, eax, times_4, kPointerSize)); + + // Invoke the code. + if (is_construct) { + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); + __ CallStub(&stub); + } else { + ParameterCount actual(eax); + __ InvokeFunction(edi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - // Invoke the code. - if (is_construct) { - __ call(masm->isolate()->builtins()->JSConstructCall(), - RelocInfo::CODE_TARGET); - } else { - ParameterCount actual(eax); - __ InvokeFunction(edi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Exit the internal frame. Notice that this also removes the empty. + // context and the function left on the stack by the code + // invocation. } - - // Exit the JS frame. Notice that this also removes the empty - // context and the function left on the stack by the code - // invocation. - __ LeaveInternalFrame(); - __ ret(1 * kPointerSize); // remove receiver + __ ret(kPointerSize); // Remove receiver. } @@ -464,68 +445,68 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { void Builtins::Generate_LazyCompile(MacroAssembler* masm) { - // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function. - __ push(edi); - // Push call kind information. - __ push(ecx); + // Push a copy of the function. + __ push(edi); + // Push call kind information. + __ push(ecx); - __ push(edi); // Function is also the parameter to the runtime call. - __ CallRuntime(Runtime::kLazyCompile, 1); + __ push(edi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kLazyCompile, 1); - // Restore call kind information. - __ pop(ecx); - // Restore receiver. - __ pop(edi); + // Restore call kind information. + __ pop(ecx); + // Restore receiver. + __ pop(edi); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ lea(eax, FieldOperand(eax, Code::kHeaderSize)); - __ jmp(Operand(eax)); + __ jmp(eax); } void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { - // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function onto the stack. - __ push(edi); - // Push call kind information. - __ push(ecx); + // Push a copy of the function onto the stack. + __ push(edi); + // Push call kind information. + __ push(ecx); - __ push(edi); // Function is also the parameter to the runtime call. - __ CallRuntime(Runtime::kLazyRecompile, 1); + __ push(edi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kLazyRecompile, 1); - // Restore call kind information. - __ pop(ecx); - // Restore receiver. - __ pop(edi); + // Restore call kind information. + __ pop(ecx); + // Restore receiver. + __ pop(edi); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ lea(eax, FieldOperand(eax, Code::kHeaderSize)); - __ jmp(Operand(eax)); + __ jmp(eax); } static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, Deoptimizer::BailoutType type) { - // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Pass the function and deoptimization type to the runtime system. - __ push(Immediate(Smi::FromInt(static_cast<int>(type)))); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + // Pass deoptimization type to the runtime system. + __ push(Immediate(Smi::FromInt(static_cast<int>(type)))); + __ CallRuntime(Runtime::kNotifyDeoptimized, 1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Get the full codegen state from the stack and untag it. __ mov(ecx, Operand(esp, 1 * kPointerSize)); @@ -566,9 +547,10 @@ void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { // the registers without worrying about which of them contain // pointers. This seems a bit fragile. __ pushad(); - __ EnterInternalFrame(); - __ CallRuntime(Runtime::kNotifyOSR, 0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kNotifyOSR, 0); + } __ popad(); __ ret(0); } @@ -579,7 +561,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 1. Make sure we have at least one argument. { Label done; - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(not_zero, &done); __ pop(ebx); __ push(Immediate(factory->undefined_value())); @@ -631,18 +613,21 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ j(above_equal, &shift_arguments); __ bind(&convert_to_object); - __ EnterInternalFrame(); // In order to preserve argument count. - __ SmiTag(eax); - __ push(eax); - __ push(ebx); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(ebx, eax); - __ Set(edx, Immediate(0)); // restore + { // In order to preserve argument count. + FrameScope scope(masm, StackFrame::INTERNAL); + __ SmiTag(eax); + __ push(eax); + + __ push(ebx); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ mov(ebx, eax); + __ Set(edx, Immediate(0)); // restore + + __ pop(eax); + __ SmiUntag(eax); + } - __ pop(eax); - __ SmiUntag(eax); - __ LeaveInternalFrame(); // Restore the function to edi. __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize)); __ jmp(&patch_receiver); @@ -695,22 +680,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin, // or a function proxy via CALL_FUNCTION_PROXY. { Label function, non_proxy; - __ test(edx, Operand(edx)); + __ test(edx, edx); __ j(zero, &function); __ Set(ebx, Immediate(0)); - __ SetCallKind(ecx, CALL_AS_METHOD); - __ cmp(Operand(edx), Immediate(1)); + __ cmp(edx, Immediate(1)); __ j(not_equal, &non_proxy); __ pop(edx); // return address __ push(edi); // re-add proxy object as additional argument __ push(edx); __ inc(eax); + __ SetCallKind(ecx, CALL_AS_FUNCTION); __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY); __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), RelocInfo::CODE_TARGET); __ bind(&non_proxy); + __ SetCallKind(ecx, CALL_AS_METHOD); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), RelocInfo::CODE_TARGET); @@ -726,13 +712,13 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset)); __ SmiUntag(ebx); __ SetCallKind(ecx, CALL_AS_METHOD); - __ cmp(eax, Operand(ebx)); + __ cmp(eax, ebx); __ j(not_equal, masm->isolate()->builtins()->ArgumentsAdaptorTrampoline()); ParameterCount expected(0); - __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + __ InvokeCode(edx, expected, expected, JUMP_FUNCTION, NullCallWrapper(), + CALL_AS_METHOD); } @@ -740,161 +726,158 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { static const int kArgumentsOffset = 2 * kPointerSize; static const int kReceiverOffset = 3 * kPointerSize; static const int kFunctionOffset = 4 * kPointerSize; + { + FrameScope frame_scope(masm, StackFrame::INTERNAL); + + __ push(Operand(ebp, kFunctionOffset)); // push this + __ push(Operand(ebp, kArgumentsOffset)); // push arguments + __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); + + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + Label okay; + ExternalReference real_stack_limit = + ExternalReference::address_of_real_stack_limit(masm->isolate()); + __ mov(edi, Operand::StaticVariable(real_stack_limit)); + // Make ecx the space we have left. The stack might already be overflowed + // here which will cause ecx to become negative. + __ mov(ecx, esp); + __ sub(ecx, edi); + // Make edx the space we need for the array when it is unrolled onto the + // stack. + __ mov(edx, eax); + __ shl(edx, kPointerSizeLog2 - kSmiTagSize); + // Check if the arguments will overflow the stack. + __ cmp(ecx, edx); + __ j(greater, &okay); // Signed comparison. + + // Out of stack space. + __ push(Operand(ebp, 4 * kPointerSize)); // push this + __ push(eax); + __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); + __ bind(&okay); + // End of stack check. + + // Push current index and limit. + const int kLimitOffset = + StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; + const int kIndexOffset = kLimitOffset - 1 * kPointerSize; + __ push(eax); // limit + __ push(Immediate(0)); // index + + // Get the receiver. + __ mov(ebx, Operand(ebp, kReceiverOffset)); + + // Check that the function is a JS function (otherwise it must be a proxy). + Label push_receiver; + __ mov(edi, Operand(ebp, kFunctionOffset)); + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &push_receiver); + + // Change context eagerly to get the right global object if necessary. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - __ EnterInternalFrame(); - - __ push(Operand(ebp, kFunctionOffset)); // push this - __ push(Operand(ebp, kArgumentsOffset)); // push arguments - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); - - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - Label okay; - ExternalReference real_stack_limit = - ExternalReference::address_of_real_stack_limit(masm->isolate()); - __ mov(edi, Operand::StaticVariable(real_stack_limit)); - // Make ecx the space we have left. The stack might already be overflowed - // here which will cause ecx to become negative. - __ mov(ecx, Operand(esp)); - __ sub(ecx, Operand(edi)); - // Make edx the space we need for the array when it is unrolled onto the - // stack. - __ mov(edx, Operand(eax)); - __ shl(edx, kPointerSizeLog2 - kSmiTagSize); - // Check if the arguments will overflow the stack. - __ cmp(ecx, Operand(edx)); - __ j(greater, &okay); // Signed comparison. - - // Out of stack space. - __ push(Operand(ebp, 4 * kPointerSize)); // push this - __ push(eax); - __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); - __ bind(&okay); - // End of stack check. - - // Push current index and limit. - const int kLimitOffset = - StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; - const int kIndexOffset = kLimitOffset - 1 * kPointerSize; - __ push(eax); // limit - __ push(Immediate(0)); // index - - // Get the receiver. - __ mov(ebx, Operand(ebp, kReceiverOffset)); - - // Check that the function is a JS function (otherwise it must be a proxy). - Label push_receiver; - __ mov(edi, Operand(ebp, kFunctionOffset)); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &push_receiver); + // Compute the receiver. + // Do not transform the receiver for strict mode functions. + Label call_to_object, use_global_receiver; + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset), + 1 << SharedFunctionInfo::kStrictModeBitWithinByte); + __ j(not_equal, &push_receiver); - // Change context eagerly to get the right global object if necessary. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + Factory* factory = masm->isolate()->factory(); - // Compute the receiver. - // Do not transform the receiver for strict mode functions. - Label call_to_object, use_global_receiver; - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset), - 1 << SharedFunctionInfo::kStrictModeBitWithinByte); - __ j(not_equal, &push_receiver); + // Do not transform the receiver for natives (shared already in ecx). + __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset), + 1 << SharedFunctionInfo::kNativeBitWithinByte); + __ j(not_equal, &push_receiver); - Factory* factory = masm->isolate()->factory(); + // Compute the receiver in non-strict mode. + // Call ToObject on the receiver if it is not an object, or use the + // global object if it is null or undefined. + __ JumpIfSmi(ebx, &call_to_object); + __ cmp(ebx, factory->null_value()); + __ j(equal, &use_global_receiver); + __ cmp(ebx, factory->undefined_value()); + __ j(equal, &use_global_receiver); + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx); + __ j(above_equal, &push_receiver); - // Do not transform the receiver for natives (shared already in ecx). - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset), - 1 << SharedFunctionInfo::kNativeBitWithinByte); - __ j(not_equal, &push_receiver); - - // Compute the receiver in non-strict mode. - // Call ToObject on the receiver if it is not an object, or use the - // global object if it is null or undefined. - __ JumpIfSmi(ebx, &call_to_object); - __ cmp(ebx, factory->null_value()); - __ j(equal, &use_global_receiver); - __ cmp(ebx, factory->undefined_value()); - __ j(equal, &use_global_receiver); - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx); - __ j(above_equal, &push_receiver); - - __ bind(&call_to_object); - __ push(ebx); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(ebx, Operand(eax)); - __ jmp(&push_receiver); - - // Use the current global receiver object as the receiver. - __ bind(&use_global_receiver); - const int kGlobalOffset = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; - __ mov(ebx, FieldOperand(esi, kGlobalOffset)); - __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset)); - __ mov(ebx, FieldOperand(ebx, kGlobalOffset)); - __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); - - // Push the receiver. - __ bind(&push_receiver); - __ push(ebx); + __ bind(&call_to_object); + __ push(ebx); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ mov(ebx, eax); + __ jmp(&push_receiver); - // Copy all arguments from the array to the stack. - Label entry, loop; - __ mov(eax, Operand(ebp, kIndexOffset)); - __ jmp(&entry); - __ bind(&loop); - __ mov(edx, Operand(ebp, kArgumentsOffset)); // load arguments + // Use the current global receiver object as the receiver. + __ bind(&use_global_receiver); + const int kGlobalOffset = + Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + __ mov(ebx, FieldOperand(esi, kGlobalOffset)); + __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset)); + __ mov(ebx, FieldOperand(ebx, kGlobalOffset)); + __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); - // Use inline caching to speed up access to arguments. - Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET); - // It is important that we do not have a test instruction after the - // call. A test instruction after the call is used to indicate that - // we have generated an inline version of the keyed load. In this - // case, we know that we are not generating a test instruction next. + // Push the receiver. + __ bind(&push_receiver); + __ push(ebx); - // Push the nth argument. - __ push(eax); + // Copy all arguments from the array to the stack. + Label entry, loop; + __ mov(eax, Operand(ebp, kIndexOffset)); + __ jmp(&entry); + __ bind(&loop); + __ mov(edx, Operand(ebp, kArgumentsOffset)); // load arguments - // Update the index on the stack and in register eax. - __ mov(eax, Operand(ebp, kIndexOffset)); - __ add(Operand(eax), Immediate(1 << kSmiTagSize)); - __ mov(Operand(ebp, kIndexOffset), eax); + // Use inline caching to speed up access to arguments. + Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Initialize(); + __ call(ic, RelocInfo::CODE_TARGET); + // It is important that we do not have a test instruction after the + // call. A test instruction after the call is used to indicate that + // we have generated an inline version of the keyed load. In this + // case, we know that we are not generating a test instruction next. - __ bind(&entry); - __ cmp(eax, Operand(ebp, kLimitOffset)); - __ j(not_equal, &loop); + // Push the nth argument. + __ push(eax); - // Invoke the function. - Label call_proxy; - ParameterCount actual(eax); - __ SmiUntag(eax); - __ mov(edi, Operand(ebp, kFunctionOffset)); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &call_proxy); - __ InvokeFunction(edi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Update the index on the stack and in register eax. + __ mov(eax, Operand(ebp, kIndexOffset)); + __ add(eax, Immediate(1 << kSmiTagSize)); + __ mov(Operand(ebp, kIndexOffset), eax); - __ LeaveInternalFrame(); - __ ret(3 * kPointerSize); // remove this, receiver, and arguments + __ bind(&entry); + __ cmp(eax, Operand(ebp, kLimitOffset)); + __ j(not_equal, &loop); - // Invoke the function proxy. - __ bind(&call_proxy); - __ push(edi); // add function proxy as last argument - __ inc(eax); - __ Set(ebx, Immediate(0)); - __ SetCallKind(ecx, CALL_AS_METHOD); - __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY); - __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); + // Invoke the function. + Label call_proxy; + ParameterCount actual(eax); + __ SmiUntag(eax); + __ mov(edi, Operand(ebp, kFunctionOffset)); + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &call_proxy); + __ InvokeFunction(edi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); - __ LeaveInternalFrame(); - __ ret(3 * kPointerSize); // remove this, receiver, and arguments -} + frame_scope.GenerateLeaveFrame(); + __ ret(3 * kPointerSize); // remove this, receiver, and arguments + // Invoke the function proxy. + __ bind(&call_proxy); + __ push(edi); // add function proxy as last argument + __ inc(eax); + __ Set(ebx, Immediate(0)); + __ SetCallKind(ecx, CALL_AS_METHOD); + __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY); + __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); -// Number of empty elements to allocate for an empty array. -static const int kPreallocatedArrayElements = 4; + // Leave internal frame. + } + __ ret(3 * kPointerSize); // remove this, receiver, and arguments +} // Allocate an empty JSArray. The allocated array is put into the result @@ -907,13 +890,11 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Register scratch1, Register scratch2, Register scratch3, - int initial_capacity, Label* gc_required) { - ASSERT(initial_capacity >= 0); + const int initial_capacity = JSArray::kPreallocatedArrayElements; + STATIC_ASSERT(initial_capacity >= 0); - // Load the initial map from the array function. - __ mov(scratch1, FieldOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + __ LoadInitialArrayMap(array_function, scratch2, scratch1); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -968,7 +949,6 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // Fill the FixedArray with the hole value. Inline the code if short. // Reconsider loop unfolding if kPreallocatedArrayElements gets changed. static const int kLoopUnfoldLimit = 4; - STATIC_ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit); if (initial_capacity <= kLoopUnfoldLimit) { // Use a scratch register here to have only one reloc info when unfolding // the loop. @@ -980,13 +960,17 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, } } else { Label loop, entry; + __ mov(scratch2, Immediate(initial_capacity)); __ jmp(&entry); __ bind(&loop); - __ mov(Operand(scratch1, 0), factory->the_hole_value()); - __ add(Operand(scratch1), Immediate(kPointerSize)); + __ mov(FieldOperand(scratch1, + scratch2, + times_pointer_size, + FixedArray::kHeaderSize), + factory->the_hole_value()); __ bind(&entry); - __ cmp(scratch1, Operand(scratch2)); - __ j(below, &loop); + __ dec(scratch2); + __ j(not_sign, &loop); } } @@ -1013,10 +997,7 @@ static void AllocateJSArray(MacroAssembler* masm, ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count ASSERT(!fill_with_hole || !result.is(eax)); // result is never eax - // Load the initial map from the array function. - __ mov(elements_array, - FieldOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + __ LoadInitialArrayMap(array_function, scratch, elements_array); // Allocate the JSArray object together with space for a FixedArray with the // requested elements. @@ -1082,7 +1063,7 @@ static void AllocateJSArray(MacroAssembler* masm, __ bind(&loop); __ stos(); __ bind(&entry); - __ cmp(edi, Operand(elements_array_end)); + __ cmp(edi, elements_array_end); __ j(below, &loop); __ bind(&done); } @@ -1120,7 +1101,7 @@ static void ArrayNativeCode(MacroAssembler* masm, __ push(eax); // Check for array construction with zero arguments. - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(not_zero, &argc_one_or_more); __ bind(&empty_array); @@ -1131,7 +1112,6 @@ static void ArrayNativeCode(MacroAssembler* masm, ebx, ecx, edi, - kPreallocatedArrayElements, &prepare_generic_code_call); __ IncrementCounter(masm->isolate()->counters()->array_function_native(), 1); __ pop(ebx); @@ -1147,7 +1127,7 @@ static void ArrayNativeCode(MacroAssembler* masm, __ j(not_equal, &argc_two_or_more); STATIC_ASSERT(kSmiTag == 0); __ mov(ecx, Operand(esp, (push_count + 1) * kPointerSize)); - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(not_zero, ¬_empty_array); // The single argument passed is zero, so we jump to the code above used to @@ -1160,7 +1140,7 @@ static void ArrayNativeCode(MacroAssembler* masm, __ mov(eax, Operand(esp, i * kPointerSize)); __ mov(Operand(esp, (i + 1) * kPointerSize), eax); } - __ add(Operand(esp), Immediate(2 * kPointerSize)); // Drop two stack slots. + __ Drop(2); // Drop two stack slots. __ push(Immediate(0)); // Treat this as a call with argc of zero. __ jmp(&empty_array); @@ -1218,39 +1198,44 @@ static void ArrayNativeCode(MacroAssembler* masm, false, &prepare_generic_code_call); __ IncrementCounter(counters->array_function_native(), 1); - __ mov(eax, ebx); - __ pop(ebx); - if (construct_call) { - __ pop(edi); - } - __ push(eax); - // eax: JSArray + __ push(ebx); + __ mov(ebx, Operand(esp, kPointerSize)); // ebx: argc // edx: elements_array_end (untagged) // esp[0]: JSArray - // esp[4]: return address - // esp[8]: last argument + // esp[4]: argc + // esp[8]: constructor (only if construct_call) + // esp[12]: return address + // esp[16]: last argument // Location of the last argument - __ lea(edi, Operand(esp, 2 * kPointerSize)); + int last_arg_offset = (construct_call ? 4 : 3) * kPointerSize; + __ lea(edi, Operand(esp, last_arg_offset)); // Location of the first array element (Parameter fill_with_holes to - // AllocateJSArrayis false, so the FixedArray is returned in ecx). + // AllocateJSArray is false, so the FixedArray is returned in ecx). __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag)); + Label has_non_smi_element; + // ebx: argc // edx: location of the first array element // edi: location of the last argument // esp[0]: JSArray - // esp[4]: return address - // esp[8]: last argument + // esp[4]: argc + // esp[8]: constructor (only if construct_call) + // esp[12]: return address + // esp[16]: last argument Label loop, entry; __ mov(ecx, ebx); __ jmp(&entry); __ bind(&loop); __ mov(eax, Operand(edi, ecx, times_pointer_size, 0)); + if (FLAG_smi_only_arrays) { + __ JumpIfNotSmi(eax, &has_non_smi_element); + } __ mov(Operand(edx, 0), eax); - __ add(Operand(edx), Immediate(kPointerSize)); + __ add(edx, Immediate(kPointerSize)); __ bind(&entry); __ dec(ecx); __ j(greater_equal, &loop); @@ -1258,13 +1243,21 @@ static void ArrayNativeCode(MacroAssembler* masm, // Remove caller arguments from the stack and return. // ebx: argc // esp[0]: JSArray - // esp[4]: return address - // esp[8]: last argument + // esp[4]: argc + // esp[8]: constructor (only if construct_call) + // esp[12]: return address + // esp[16]: last argument + __ mov(ecx, Operand(esp, last_arg_offset - kPointerSize)); __ pop(eax); - __ pop(ecx); - __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize)); - __ push(ecx); - __ ret(0); + __ pop(ebx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, + last_arg_offset - kPointerSize)); + __ jmp(ecx); + + __ bind(&has_non_smi_element); + // Throw away the array that's only been partially constructed. + __ pop(eax); + __ UndoAllocationInNewSpace(eax); // Restore argc and constructor before running the generic code. __ bind(&prepare_generic_code_call); @@ -1276,6 +1269,40 @@ static void ArrayNativeCode(MacroAssembler* masm, } +void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + Label generic_array_code; + + // Get the InternalArray function. + __ LoadGlobalFunction(Context::INTERNAL_ARRAY_FUNCTION_INDEX, edi); + + if (FLAG_debug_code) { + // Initial map for the builtin InternalArray function should be a map. + __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ebx, Immediate(kSmiTagMask)); + __ Assert(not_zero, "Unexpected initial map for InternalArray function"); + __ CmpObjectType(ebx, MAP_TYPE, ecx); + __ Assert(equal, "Unexpected initial map for InternalArray function"); + } + + // Run the native code for the InternalArray function called as a normal + // function. + ArrayNativeCode(masm, false, &generic_array_code); + + // Jump to the generic internal array code in case the specialized code cannot + // handle the construction. + __ bind(&generic_array_code); + Handle<Code> array_code = + masm->isolate()->builtins()->InternalArrayCodeGeneric(); + __ jmp(array_code, RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArrayCode(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : argc @@ -1288,7 +1315,7 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi); if (FLAG_debug_code) { - // Initial map for the builtin Array function shoud be a map. + // Initial map for the builtin Array function should be a map. __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); // Will both indicate a NULL and a Smi. __ test(ebx, Immediate(kSmiTagMask)); @@ -1356,14 +1383,14 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { if (FLAG_debug_code) { __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, ecx); - __ cmp(edi, Operand(ecx)); + __ cmp(edi, ecx); __ Assert(equal, "Unexpected String function"); } // Load the first argument into eax and get rid of the rest // (including the receiver). Label no_arguments; - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(zero, &no_arguments); __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); __ pop(ecx); @@ -1439,12 +1466,13 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // Invoke the conversion builtin and put the result into ebx. __ bind(&convert_argument); __ IncrementCounter(counters->string_ctor_conversions(), 1); - __ EnterInternalFrame(); - __ push(edi); // Preserve the function. - __ push(eax); - __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); - __ pop(edi); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(edi); // Preserve the function. + __ push(eax); + __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); + __ pop(edi); + } __ mov(ebx, eax); __ jmp(&argument_is_string); @@ -1461,17 +1489,18 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // create a string wrapper. __ bind(&gc_required); __ IncrementCounter(counters->string_ctor_gc_required(), 1); - __ EnterInternalFrame(); - __ push(ebx); - __ CallRuntime(Runtime::kNewStringWrapper, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(ebx); + __ CallRuntime(Runtime::kNewStringWrapper, 1); + } __ ret(0); } static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { __ push(ebp); - __ mov(ebp, Operand(esp)); + __ mov(ebp, esp); // Store the arguments adaptor context sentinel. __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); @@ -1515,7 +1544,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ IncrementCounter(masm->isolate()->counters()->arguments_adaptors(), 1); Label enough, too_few; - __ cmp(eax, Operand(ebx)); + __ cmp(eax, ebx); __ j(less, &too_few); __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel); __ j(equal, &dont_adapt_arguments); @@ -1533,8 +1562,8 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(©); __ inc(edi); __ push(Operand(eax, 0)); - __ sub(Operand(eax), Immediate(kPointerSize)); - __ cmp(edi, Operand(ebx)); + __ sub(eax, Immediate(kPointerSize)); + __ cmp(edi, ebx); __ j(less, ©); __ jmp(&invoke); } @@ -1547,17 +1576,17 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { const int offset = StandardFrameConstants::kCallerSPOffset; __ lea(edi, Operand(ebp, eax, times_4, offset)); // ebx = expected - actual. - __ sub(ebx, Operand(eax)); + __ sub(ebx, eax); // eax = -actual - 1 __ neg(eax); - __ sub(Operand(eax), Immediate(1)); + __ sub(eax, Immediate(1)); Label copy; __ bind(©); __ inc(eax); __ push(Operand(edi, 0)); - __ sub(Operand(edi), Immediate(kPointerSize)); - __ test(eax, Operand(eax)); + __ sub(edi, Immediate(kPointerSize)); + __ test(eax, eax); __ j(not_zero, ©); // Fill remaining expected arguments with undefined values. @@ -1565,7 +1594,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&fill); __ inc(eax); __ push(Immediate(masm->isolate()->factory()->undefined_value())); - __ cmp(eax, Operand(ebx)); + __ cmp(eax, ebx); __ j(less, &fill); } @@ -1573,8 +1602,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&invoke); // Restore function pointer. __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ call(Operand(edx)); + __ call(edx); + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); // Leave frame and return. LeaveArgumentsAdaptorFrame(masm); __ ret(0); @@ -1583,13 +1613,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Dont adapt arguments. // ------------------------------------------- __ bind(&dont_adapt_arguments); - __ jmp(Operand(edx)); + __ jmp(edx); } void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { CpuFeatures::TryForceFeatureScope scope(SSE2); - if (!CpuFeatures::IsSupported(SSE2)) { + if (!CpuFeatures::IsSupported(SSE2) && FLAG_debug_code) { __ Abort("Unreachable code: Cannot optimize without SSE2 support."); return; } @@ -1616,15 +1646,16 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Pass the function to optimize as the argument to the on-stack // replacement runtime function. - __ EnterInternalFrame(); - __ push(eax); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(eax); + __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + } // If the result was -1 it means that we couldn't optimize the // function. Just return and continue in the unoptimized version. Label skip; - __ cmp(Operand(eax), Immediate(Smi::FromInt(-1))); + __ cmp(eax, Immediate(Smi::FromInt(-1))); __ j(not_equal, &skip, Label::kNear); __ ret(0); @@ -1638,7 +1669,9 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { __ j(above_equal, &ok, Label::kNear); StackCheckStub stub; __ TailCallStub(&stub); - __ Abort("Unreachable code: returned from tail call."); + if (FLAG_debug_code) { + __ Abort("Unreachable code: returned from tail call."); + } __ bind(&ok); __ ret(0); diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index 8a5bd50ece..b3a0b9538e 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,6 +34,8 @@ #include "isolate.h" #include "jsregexp.h" #include "regexp-macro-assembler.h" +#include "stub-cache.h" +#include "codegen.h" namespace v8 { namespace internal { @@ -49,7 +51,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ bind(&check_heap_number); __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); Factory* factory = masm->isolate()->factory(); - __ cmp(Operand(ebx), Immediate(factory->heap_number_map())); + __ cmp(ebx, Immediate(factory->heap_number_map())); __ j(not_equal, &call_builtin, Label::kNear); __ ret(0); @@ -70,9 +72,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Get the function info from the stack. __ mov(edx, Operand(esp, 1 * kPointerSize)); - int map_index = strict_mode_ == kStrictMode - ? Context::STRICT_MODE_FUNCTION_MAP_INDEX - : Context::FUNCTION_MAP_INDEX; + int map_index = (language_mode_ == CLASSIC_MODE) + ? Context::FUNCTION_MAP_INDEX + : Context::STRICT_MODE_FUNCTION_MAP_INDEX; // Compute the function map in the current global context and set that // as the map of the allocated object. @@ -126,14 +128,14 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { // Get the function from the stack. __ mov(ecx, Operand(esp, 1 * kPointerSize)); - // Setup the object header. + // Set up the object header. Factory* factory = masm->isolate()->factory(); __ mov(FieldOperand(eax, HeapObject::kMapOffset), factory->function_context_map()); __ mov(FieldOperand(eax, Context::kLengthOffset), Immediate(Smi::FromInt(length))); - // Setup the fixed slots. + // Set up the fixed slots. __ Set(ebx, Immediate(0)); // Set to NULL. __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx); __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi); @@ -150,7 +152,7 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } // Return and remove the on-stack parameter. - __ mov(esi, Operand(eax)); + __ mov(esi, eax); __ ret(1 * kPointerSize); // Need to collect. Call into runtime system. @@ -159,6 +161,139 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastNewBlockContextStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [esp + (1 * kPointerSize)]: function + // [esp + (2 * kPointerSize)]: serialized scope info + + // Try to allocate the context in new space. + Label gc; + int length = slots_ + Context::MIN_CONTEXT_SLOTS; + __ AllocateInNewSpace(FixedArray::SizeFor(length), + eax, ebx, ecx, &gc, TAG_OBJECT); + + // Get the function or sentinel from the stack. + __ mov(ecx, Operand(esp, 1 * kPointerSize)); + + // Get the serialized scope info from the stack. + __ mov(ebx, Operand(esp, 2 * kPointerSize)); + + // Set up the object header. + Factory* factory = masm->isolate()->factory(); + __ mov(FieldOperand(eax, HeapObject::kMapOffset), + factory->block_context_map()); + __ mov(FieldOperand(eax, Context::kLengthOffset), + Immediate(Smi::FromInt(length))); + + // If this block context is nested in the global context we get a smi + // sentinel instead of a function. The block context should get the + // canonical empty function of the global context as its closure which + // we still have to look up. + Label after_sentinel; + __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear); + if (FLAG_debug_code) { + const char* message = "Expected 0 as a Smi sentinel"; + __ cmp(ecx, 0); + __ Assert(equal, message); + } + __ mov(ecx, GlobalObjectOperand()); + __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset)); + __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX)); + __ bind(&after_sentinel); + + // Set up the fixed slots. + __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx); + __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi); + __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx); + + // Copy the global object from the previous context. + __ mov(ebx, ContextOperand(esi, Context::GLOBAL_INDEX)); + __ mov(ContextOperand(eax, Context::GLOBAL_INDEX), ebx); + + // Initialize the rest of the slots to the hole value. + if (slots_ == 1) { + __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS), + factory->the_hole_value()); + } else { + __ mov(ebx, factory->the_hole_value()); + for (int i = 0; i < slots_; i++) { + __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx); + } + } + + // Return and remove the on-stack parameters. + __ mov(esi, eax); + __ ret(2 * kPointerSize); + + // Need to collect. Call into runtime system. + __ bind(&gc); + __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1); +} + + +static void GenerateFastCloneShallowArrayCommon( + MacroAssembler* masm, + int length, + FastCloneShallowArrayStub::Mode mode, + Label* fail) { + // Registers on entry: + // + // ecx: boilerplate literal array. + ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS); + + // All sizes here are multiples of kPointerSize. + int elements_size = 0; + if (length > 0) { + elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + ? FixedDoubleArray::SizeFor(length) + : FixedArray::SizeFor(length); + } + int size = JSArray::kSize + elements_size; + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size, eax, ebx, edx, fail, TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length == 0)) { + __ mov(ebx, FieldOperand(ecx, i)); + __ mov(FieldOperand(eax, i), ebx); + } + } + + if (length > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset)); + __ lea(edx, Operand(eax, JSArray::kSize)); + __ mov(FieldOperand(eax, JSArray::kElementsOffset), edx); + + // Copy the elements array. + if (mode == FastCloneShallowArrayStub::CLONE_ELEMENTS) { + for (int i = 0; i < elements_size; i += kPointerSize) { + __ mov(ebx, FieldOperand(ecx, i)); + __ mov(FieldOperand(edx, i), ebx); + } + } else { + ASSERT(mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS); + int i; + for (i = 0; i < FixedDoubleArray::kHeaderSize; i += kPointerSize) { + __ mov(ebx, FieldOperand(ecx, i)); + __ mov(FieldOperand(edx, i), ebx); + } + while (i < elements_size) { + __ fld_d(FieldOperand(ecx, i)); + __ fstp_d(FieldOperand(edx, i)); + i += kDoubleSize; + } + ASSERT(i == elements_size); + } + } +} + + void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // Stack layout on entry: // @@ -166,13 +301,8 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // [esp + (2 * kPointerSize)]: literal index. // [esp + (3 * kPointerSize)]: literals array. - // All sizes here are multiples of kPointerSize. - int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; - int size = JSArray::kSize + elements_size; - // Load boilerplate object into ecx and check if we need to create a // boilerplate. - Label slow_case; __ mov(ecx, Operand(esp, 3 * kPointerSize)); __ mov(eax, Operand(esp, 2 * kPointerSize)); STATIC_ASSERT(kPointerSize == 4); @@ -182,16 +312,43 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { FixedArray::kHeaderSize)); Factory* factory = masm->isolate()->factory(); __ cmp(ecx, factory->undefined_value()); + Label slow_case; __ j(equal, &slow_case); + FastCloneShallowArrayStub::Mode mode = mode_; + // ecx is boilerplate object. + if (mode == CLONE_ANY_ELEMENTS) { + Label double_elements, check_fast_elements; + __ mov(ebx, FieldOperand(ecx, JSArray::kElementsOffset)); + __ CheckMap(ebx, factory->fixed_cow_array_map(), + &check_fast_elements, DONT_DO_SMI_CHECK); + GenerateFastCloneShallowArrayCommon(masm, 0, + COPY_ON_WRITE_ELEMENTS, &slow_case); + __ ret(3 * kPointerSize); + + __ bind(&check_fast_elements); + __ CheckMap(ebx, factory->fixed_array_map(), + &double_elements, DONT_DO_SMI_CHECK); + GenerateFastCloneShallowArrayCommon(masm, length_, + CLONE_ELEMENTS, &slow_case); + __ ret(3 * kPointerSize); + + __ bind(&double_elements); + mode = CLONE_DOUBLE_ELEMENTS; + // Fall through to generate the code to handle double elements. + } + if (FLAG_debug_code) { const char* message; Handle<Map> expected_map; - if (mode_ == CLONE_ELEMENTS) { + if (mode == CLONE_ELEMENTS) { message = "Expected (writable) fixed array"; expected_map = factory->fixed_array_map(); + } else if (mode == CLONE_DOUBLE_ELEMENTS) { + message = "Expected (writable) fixed double array"; + expected_map = factory->fixed_double_array_map(); } else { - ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS); + ASSERT(mode == COPY_ON_WRITE_ELEMENTS); message = "Expected copy-on-write fixed array"; expected_map = factory->fixed_cow_array_map(); } @@ -202,43 +359,66 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ pop(ecx); } - // Allocate both the JS array and the elements array in one big - // allocation. This avoids multiple limit checks. - __ AllocateInNewSpace(size, eax, ebx, edx, &slow_case, TAG_OBJECT); + GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case); + // Return and remove the on-stack parameters. + __ ret(3 * kPointerSize); - // Copy the JS array part. - for (int i = 0; i < JSArray::kSize; i += kPointerSize) { - if ((i != JSArray::kElementsOffset) || (length_ == 0)) { - __ mov(ebx, FieldOperand(ecx, i)); - __ mov(FieldOperand(eax, i), ebx); - } - } + __ bind(&slow_case); + __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); +} - if (length_ > 0) { - // Get hold of the elements array of the boilerplate and setup the - // elements pointer in the resulting object. - __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset)); - __ lea(edx, Operand(eax, JSArray::kSize)); - __ mov(FieldOperand(eax, JSArray::kElementsOffset), edx); - // Copy the elements array. - for (int i = 0; i < elements_size; i += kPointerSize) { - __ mov(ebx, FieldOperand(ecx, i)); - __ mov(FieldOperand(edx, i), ebx); - } +void FastCloneShallowObjectStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [esp + kPointerSize]: object literal flags. + // [esp + (2 * kPointerSize)]: constant properties. + // [esp + (3 * kPointerSize)]: literal index. + // [esp + (4 * kPointerSize)]: literals array. + + // Load boilerplate object into ecx and check if we need to create a + // boilerplate. + Label slow_case; + __ mov(ecx, Operand(esp, 4 * kPointerSize)); + __ mov(eax, Operand(esp, 3 * kPointerSize)); + STATIC_ASSERT(kPointerSize == 4); + STATIC_ASSERT(kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0); + __ mov(ecx, FieldOperand(ecx, eax, times_half_pointer_size, + FixedArray::kHeaderSize)); + Factory* factory = masm->isolate()->factory(); + __ cmp(ecx, factory->undefined_value()); + __ j(equal, &slow_case); + + // Check that the boilerplate contains only fast properties and we can + // statically determine the instance size. + int size = JSObject::kHeaderSize + length_ * kPointerSize; + __ mov(eax, FieldOperand(ecx, HeapObject::kMapOffset)); + __ movzx_b(eax, FieldOperand(eax, Map::kInstanceSizeOffset)); + __ cmp(eax, Immediate(size >> kPointerSizeLog2)); + __ j(not_equal, &slow_case); + + // Allocate the JS object and copy header together with all in-object + // properties from the boilerplate. + __ AllocateInNewSpace(size, eax, ebx, edx, &slow_case, TAG_OBJECT); + for (int i = 0; i < size; i += kPointerSize) { + __ mov(ebx, FieldOperand(ecx, i)); + __ mov(FieldOperand(eax, i), ebx); } // Return and remove the on-stack parameters. - __ ret(3 * kPointerSize); + __ ret(4 * kPointerSize); __ bind(&slow_case); - __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); + __ TailCallRuntime(Runtime::kCreateObjectLiteralShallow, 4, 1); } // The stub expects its argument on the stack and returns its result in tos_: // zero for false, and a non-zero value for true. void ToBooleanStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. Label patch; Factory* factory = masm->isolate()->factory(); const Register argument = eax; @@ -336,6 +516,41 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } +void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + __ pushad(); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters)); + for (int i = 0; i < XMMRegister::kNumRegisters; i++) { + XMMRegister reg = XMMRegister::from_code(i); + __ movdbl(Operand(esp, i * kDoubleSize), reg); + } + } + const int argument_count = 1; + + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(argument_count, ecx); + __ mov(Operand(esp, 0 * kPointerSize), + Immediate(ExternalReference::isolate_address())); + __ CallCFunction( + ExternalReference::store_buffer_overflow_function(masm->isolate()), + argument_count); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + for (int i = 0; i < XMMRegister::kNumRegisters; i++) { + XMMRegister reg = XMMRegister::from_code(i); + __ movdbl(reg, Operand(esp, i * kDoubleSize)); + } + __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters)); + } + __ popad(); + __ ret(0); +} + + void ToBooleanStub::CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, @@ -470,27 +685,27 @@ static void IntegerConvert(MacroAssembler* masm, // Check whether the exponent is too big for a 64 bit signed integer. static const uint32_t kTooBigExponent = (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift; - __ cmp(Operand(scratch2), Immediate(kTooBigExponent)); + __ cmp(scratch2, Immediate(kTooBigExponent)); __ j(greater_equal, conversion_failure); // Load x87 register with heap number. __ fld_d(FieldOperand(source, HeapNumber::kValueOffset)); // Reserve space for 64 bit answer. - __ sub(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint. + __ sub(esp, Immediate(sizeof(uint64_t))); // Nolint. // Do conversion, which cannot fail because we checked the exponent. __ fisttp_d(Operand(esp, 0)); __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx. - __ add(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint. + __ add(esp, Immediate(sizeof(uint64_t))); // Nolint. } else { // Load ecx with zero. We use this either for the final shift or // for the answer. - __ xor_(ecx, Operand(ecx)); + __ xor_(ecx, ecx); // Check whether the exponent matches a 32 bit signed int that cannot be // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the // exponent is 30 (biased). This is the exponent that we are fastest at and // also the highest exponent we can handle here. const uint32_t non_smi_exponent = (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; - __ cmp(Operand(scratch2), Immediate(non_smi_exponent)); + __ cmp(scratch2, Immediate(non_smi_exponent)); // If we have a match of the int32-but-not-Smi exponent then skip some // logic. __ j(equal, &right_exponent, Label::kNear); @@ -503,7 +718,7 @@ static void IntegerConvert(MacroAssembler* masm, // >>> operator has a tendency to generate numbers with an exponent of 31. const uint32_t big_non_smi_exponent = (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift; - __ cmp(Operand(scratch2), Immediate(big_non_smi_exponent)); + __ cmp(scratch2, Immediate(big_non_smi_exponent)); __ j(not_equal, conversion_failure); // We have the big exponent, typically from >>>. This means the number is // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa. @@ -522,9 +737,9 @@ static void IntegerConvert(MacroAssembler* masm, // Shift down 21 bits to get the most significant 11 bits or the low // mantissa word. __ shr(ecx, 32 - big_shift_distance); - __ or_(ecx, Operand(scratch2)); + __ or_(ecx, scratch2); // We have the answer in ecx, but we may need to negate it. - __ test(scratch, Operand(scratch)); + __ test(scratch, scratch); __ j(positive, &done, Label::kNear); __ neg(ecx); __ jmp(&done, Label::kNear); @@ -534,18 +749,18 @@ static void IntegerConvert(MacroAssembler* masm, // Exponent word in scratch, exponent part of exponent word in scratch2. // Zero in ecx. // We know the exponent is smaller than 30 (biased). If it is less than - // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie + // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, i.e. // it rounds to zero. const uint32_t zero_exponent = (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift; - __ sub(Operand(scratch2), Immediate(zero_exponent)); + __ sub(scratch2, Immediate(zero_exponent)); // ecx already has a Smi zero. __ j(less, &done, Label::kNear); // We have a shifted exponent between 0 and 30 in scratch2. __ shr(scratch2, HeapNumber::kExponentShift); __ mov(ecx, Immediate(30)); - __ sub(ecx, Operand(scratch2)); + __ sub(ecx, scratch2); __ bind(&right_exponent); // Here ecx is the shift, scratch is the exponent word. @@ -565,19 +780,19 @@ static void IntegerConvert(MacroAssembler* masm, // Shift down 22 bits to get the most significant 10 bits or the low // mantissa word. __ shr(scratch2, 32 - shift_distance); - __ or_(scratch2, Operand(scratch)); + __ or_(scratch2, scratch); // Move down according to the exponent. __ shr_cl(scratch2); // Now the unsigned answer is in scratch2. We need to move it to ecx and // we may need to fix the sign. Label negative; - __ xor_(ecx, Operand(ecx)); + __ xor_(ecx, ecx); __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset)); __ j(greater, &negative, Label::kNear); __ mov(ecx, scratch2); __ jmp(&done, Label::kNear); __ bind(&negative); - __ sub(ecx, Operand(scratch2)); + __ sub(ecx, scratch2); __ bind(&done); } } @@ -679,13 +894,13 @@ void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm, __ JumpIfNotSmi(eax, non_smi, non_smi_near); // We can't handle -0 with smis, so use a type transition for that case. - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(zero, slow, slow_near); // Try optimistic subtraction '0 - value', saving operand in eax for undo. - __ mov(edx, Operand(eax)); + __ mov(edx, eax); __ Set(eax, Immediate(0)); - __ sub(eax, Operand(edx)); + __ sub(eax, edx); __ j(overflow, undo, undo_near); __ ret(0); } @@ -706,7 +921,7 @@ void UnaryOpStub::GenerateSmiCodeBitNot( void UnaryOpStub::GenerateSmiCodeUndo(MacroAssembler* masm) { - __ mov(eax, Operand(edx)); + __ mov(eax, edx); } @@ -760,7 +975,7 @@ void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm, __ xor_(FieldOperand(eax, HeapNumber::kExponentOffset), Immediate(HeapNumber::kSignMask)); // Flip sign. } else { - __ mov(edx, Operand(eax)); + __ mov(edx, eax); // edx: operand Label slow_allocate_heapnumber, heapnumber_allocated; @@ -768,11 +983,12 @@ void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm, __ jmp(&heapnumber_allocated, Label::kNear); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(edx); - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ pop(edx); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(edx); + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ pop(edx); + } __ bind(&heapnumber_allocated); // eax: allocated 'empty' number @@ -815,15 +1031,16 @@ void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm, __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - // Push the original HeapNumber on the stack. The integer value can't - // be stored since it's untagged and not in the smi range (so we can't - // smi-tag it). We'll recalculate the value after the GC instead. - __ push(ebx); - __ CallRuntime(Runtime::kNumberAlloc, 0); - // New HeapNumber is in eax. - __ pop(edx); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Push the original HeapNumber on the stack. The integer value can't + // be stored since it's untagged and not in the smi range (so we can't + // smi-tag it). We'll recalculate the value after the GC instead. + __ push(ebx); + __ CallRuntime(Runtime::kNumberAlloc, 0); + // New HeapNumber is in eax. + __ pop(edx); + } // IntegerConvert uses ebx and edi as scratch registers. // This conversion won't go slow-case. IntegerConvert(masm, edx, CpuFeatures::IsSupported(SSE3), slow); @@ -833,7 +1050,7 @@ void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm, } if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - __ cvtsi2sd(xmm0, Operand(ecx)); + __ cvtsi2sd(xmm0, ecx); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); } else { __ push(ecx); @@ -947,6 +1164,10 @@ void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) { void BinaryOpStub::Generate(MacroAssembler* masm) { + // Explicitly allow generation of nested stubs. It is safe here because + // generation code does not use any raw pointers. + AllowStubCallsScope allow_stub_calls(masm, true); + switch (operands_type_) { case BinaryOpIC::UNINITIALIZED: GenerateTypeTransition(masm); @@ -1022,7 +1243,7 @@ void BinaryOpStub::GenerateSmiCode( // eax in case the result is not a smi. ASSERT(!left.is(ecx) && !right.is(ecx)); __ mov(ecx, right); - __ or_(right, Operand(left)); // Bitwise or is commutative. + __ or_(right, left); // Bitwise or is commutative. combined = right; break; @@ -1034,7 +1255,7 @@ void BinaryOpStub::GenerateSmiCode( case Token::DIV: case Token::MOD: __ mov(combined, right); - __ or_(combined, Operand(left)); + __ or_(combined, left); break; case Token::SHL: @@ -1044,7 +1265,7 @@ void BinaryOpStub::GenerateSmiCode( // for the smi check register. ASSERT(!left.is(ecx) && !right.is(ecx)); __ mov(ecx, right); - __ or_(right, Operand(left)); + __ or_(right, left); combined = right; break; @@ -1067,12 +1288,12 @@ void BinaryOpStub::GenerateSmiCode( case Token::BIT_XOR: ASSERT(right.is(eax)); - __ xor_(right, Operand(left)); // Bitwise xor is commutative. + __ xor_(right, left); // Bitwise xor is commutative. break; case Token::BIT_AND: ASSERT(right.is(eax)); - __ and_(right, Operand(left)); // Bitwise and is commutative. + __ and_(right, left); // Bitwise and is commutative. break; case Token::SHL: @@ -1121,12 +1342,12 @@ void BinaryOpStub::GenerateSmiCode( case Token::ADD: ASSERT(right.is(eax)); - __ add(right, Operand(left)); // Addition is commutative. + __ add(right, left); // Addition is commutative. __ j(overflow, &use_fp_on_smis); break; case Token::SUB: - __ sub(left, Operand(right)); + __ sub(left, right); __ j(overflow, &use_fp_on_smis); __ mov(eax, left); break; @@ -1140,7 +1361,7 @@ void BinaryOpStub::GenerateSmiCode( // Remove tag from one of the operands (but keep sign). __ SmiUntag(right); // Do multiplication. - __ imul(right, Operand(left)); // Multiplication is commutative. + __ imul(right, left); // Multiplication is commutative. __ j(overflow, &use_fp_on_smis); // Check for negative zero result. Use combined = left | right. __ NegativeZeroTest(right, combined, &use_fp_on_smis); @@ -1151,7 +1372,7 @@ void BinaryOpStub::GenerateSmiCode( // save the left operand. __ mov(edi, left); // Check for 0 divisor. - __ test(right, Operand(right)); + __ test(right, right); __ j(zero, &use_fp_on_smis); // Sign extend left into edx:eax. ASSERT(left.is(eax)); @@ -1167,7 +1388,7 @@ void BinaryOpStub::GenerateSmiCode( // Check for negative zero result. Use combined = left | right. __ NegativeZeroTest(eax, combined, &use_fp_on_smis); // Check that the remainder is zero. - __ test(edx, Operand(edx)); + __ test(edx, edx); __ j(not_zero, &use_fp_on_smis); // Tag the result and store it in register eax. __ SmiTag(eax); @@ -1175,7 +1396,7 @@ void BinaryOpStub::GenerateSmiCode( case Token::MOD: // Check for 0 divisor. - __ test(right, Operand(right)); + __ test(right, right); __ j(zero, ¬_smis); // Sign extend left into edx:eax. @@ -1226,11 +1447,11 @@ void BinaryOpStub::GenerateSmiCode( break; case Token::ADD: // Revert right = right + left. - __ sub(right, Operand(left)); + __ sub(right, left); break; case Token::SUB: // Revert left = left - right. - __ add(left, Operand(right)); + __ add(left, right); break; case Token::MUL: // Right was clobbered but a copy is in ebx. @@ -1268,7 +1489,7 @@ void BinaryOpStub::GenerateSmiCode( ASSERT_EQ(Token::SHL, op_); if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - __ cvtsi2sd(xmm0, Operand(left)); + __ cvtsi2sd(xmm0, left); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); } else { __ mov(Operand(esp, 1 * kPointerSize), left); @@ -1290,11 +1511,11 @@ void BinaryOpStub::GenerateSmiCode( switch (op_) { case Token::ADD: // Revert right = right + left. - __ sub(right, Operand(left)); + __ sub(right, left); break; case Token::SUB: // Revert left = left - right. - __ add(left, Operand(right)); + __ add(left, right); break; case Token::MUL: // Right was clobbered but a copy is in ebx. @@ -1486,7 +1707,7 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { // Check result type if it is currently Int32. if (result_type_ <= BinaryOpIC::INT32) { __ cvttsd2si(ecx, Operand(xmm0)); - __ cvtsi2sd(xmm2, Operand(ecx)); + __ cvtsi2sd(xmm2, ecx); __ ucomisd(xmm0, xmm2); __ j(not_zero, ¬_int32); __ j(carry, ¬_int32); @@ -1548,9 +1769,9 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { FloatingPointHelper::CheckLoadedIntegersWereInt32(masm, use_sse3_, ¬_int32); switch (op_) { - case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; - case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; - case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; + case Token::BIT_OR: __ or_(eax, ecx); break; + case Token::BIT_AND: __ and_(eax, ecx); break; + case Token::BIT_XOR: __ xor_(eax, ecx); break; case Token::SAR: __ sar_cl(eax); break; case Token::SHL: __ shl_cl(eax); break; case Token::SHR: __ shr_cl(eax); break; @@ -1574,7 +1795,7 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { if (op_ != Token::SHR) { __ bind(&non_smi_result); // Allocate a heap number if needed. - __ mov(ebx, Operand(eax)); // ebx: result + __ mov(ebx, eax); // ebx: result Label skip_allocation; switch (mode_) { case OVERWRITE_LEFT: @@ -1594,7 +1815,7 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { // Store the result in the HeapNumber and return. if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - __ cvtsi2sd(xmm0, Operand(ebx)); + __ cvtsi2sd(xmm0, ebx); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); } else { __ mov(Operand(esp, 1 * kPointerSize), ebx); @@ -1675,7 +1896,7 @@ void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { __ cmp(edx, factory->undefined_value()); __ j(not_equal, &check, Label::kNear); if (Token::IsBitOp(op_)) { - __ xor_(edx, Operand(edx)); + __ xor_(edx, edx); } else { __ mov(edx, Immediate(factory->nan_value())); } @@ -1684,7 +1905,7 @@ void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { __ cmp(eax, factory->undefined_value()); __ j(not_equal, &done, Label::kNear); if (Token::IsBitOp(op_)) { - __ xor_(eax, Operand(eax)); + __ xor_(eax, eax); } else { __ mov(eax, Immediate(factory->nan_value())); } @@ -1762,9 +1983,9 @@ void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) { use_sse3_, ¬_floats); switch (op_) { - case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; - case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; - case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; + case Token::BIT_OR: __ or_(eax, ecx); break; + case Token::BIT_AND: __ and_(eax, ecx); break; + case Token::BIT_XOR: __ xor_(eax, ecx); break; case Token::SAR: __ sar_cl(eax); break; case Token::SHL: __ shl_cl(eax); break; case Token::SHR: __ shr_cl(eax); break; @@ -1788,7 +2009,7 @@ void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) { if (op_ != Token::SHR) { __ bind(&non_smi_result); // Allocate a heap number if needed. - __ mov(ebx, Operand(eax)); // ebx: result + __ mov(ebx, eax); // ebx: result Label skip_allocation; switch (mode_) { case OVERWRITE_LEFT: @@ -1808,7 +2029,7 @@ void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) { // Store the result in the HeapNumber and return. if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - __ cvtsi2sd(xmm0, Operand(ebx)); + __ cvtsi2sd(xmm0, ebx); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); } else { __ mov(Operand(esp, 1 * kPointerSize), ebx); @@ -1961,9 +2182,9 @@ void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { use_sse3_, &call_runtime); switch (op_) { - case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; - case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; - case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; + case Token::BIT_OR: __ or_(eax, ecx); break; + case Token::BIT_AND: __ and_(eax, ecx); break; + case Token::BIT_XOR: __ xor_(eax, ecx); break; case Token::SAR: __ sar_cl(eax); break; case Token::SHL: __ shl_cl(eax); break; case Token::SHR: __ shr_cl(eax); break; @@ -1987,7 +2208,7 @@ void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { if (op_ != Token::SHR) { __ bind(&non_smi_result); // Allocate a heap number if needed. - __ mov(ebx, Operand(eax)); // ebx: result + __ mov(ebx, eax); // ebx: result Label skip_allocation; switch (mode_) { case OVERWRITE_LEFT: @@ -2007,7 +2228,7 @@ void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { // Store the result in the HeapNumber and return. if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - __ cvtsi2sd(xmm0, Operand(ebx)); + __ cvtsi2sd(xmm0, ebx); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); } else { __ mov(Operand(esp, 1 * kPointerSize), ebx); @@ -2117,10 +2338,10 @@ void BinaryOpStub::GenerateHeapResultAllocation( __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure); // Now edx can be overwritten losing one of the arguments as we are // now done and will not need it any more. - __ mov(edx, Operand(ebx)); + __ mov(edx, ebx); __ bind(&skip_allocation); // Use object in edx as a result holder - __ mov(eax, Operand(edx)); + __ mov(eax, edx); break; } case OVERWRITE_RIGHT: @@ -2178,7 +2399,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Then load the low and high words of the double into ebx, edx. STATIC_ASSERT(kSmiTagSize == 1); __ sar(eax, 1); - __ sub(Operand(esp), Immediate(2 * kPointerSize)); + __ sub(esp, Immediate(2 * kPointerSize)); __ mov(Operand(esp, 0), eax); __ fild_s(Operand(esp, 0)); __ fst_d(Operand(esp, 0)); @@ -2189,7 +2410,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Check if input is a HeapNumber. __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); Factory* factory = masm->isolate()->factory(); - __ cmp(Operand(ebx), Immediate(factory->heap_number_map())); + __ cmp(ebx, Immediate(factory->heap_number_map())); __ j(not_equal, &runtime_call); // Input is a HeapNumber. Push it on the FPU stack and load its // low and high words into ebx, edx. @@ -2201,12 +2422,12 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { } else { // UNTAGGED. if (CpuFeatures::IsSupported(SSE4_1)) { CpuFeatures::Scope sse4_scope(SSE4_1); - __ pextrd(Operand(edx), xmm1, 0x1); // copy xmm1[63..32] to edx. + __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx. } else { __ pshufd(xmm0, xmm1, 0x1); - __ movd(Operand(edx), xmm0); + __ movd(edx, xmm0); } - __ movd(Operand(ebx), xmm1); + __ movd(ebx, xmm1); } // ST[0] or xmm1 == double value @@ -2215,15 +2436,15 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Compute hash (the shifts are arithmetic): // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1); __ mov(ecx, ebx); - __ xor_(ecx, Operand(edx)); + __ xor_(ecx, edx); __ mov(eax, ecx); __ sar(eax, 16); - __ xor_(ecx, Operand(eax)); + __ xor_(ecx, eax); __ mov(eax, ecx); __ sar(eax, 8); - __ xor_(ecx, Operand(eax)); + __ xor_(ecx, eax); ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize)); - __ and_(Operand(ecx), + __ and_(ecx, Immediate(TranscendentalCache::SubCache::kCacheSize - 1)); // ST[0] or xmm1 == double value. @@ -2238,7 +2459,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ mov(eax, Operand(eax, cache_array_index)); // Eax points to the cache for the type type_. // If NULL, the cache hasn't been initialized yet, so go through runtime. - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(zero, &runtime_call_clear_stack); #ifdef DEBUG // Check that the layout of cache elements match expectations. @@ -2264,6 +2485,8 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ cmp(edx, Operand(ecx, kIntSize)); __ j(not_equal, &cache_miss, Label::kNear); // Cache hit! + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->transcendental_cache_hit(), 1); __ mov(eax, Operand(ecx, 2 * kIntSize)); if (tagged) { __ fstp(0); @@ -2274,6 +2497,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { } __ bind(&cache_miss); + __ IncrementCounter(counters->transcendental_cache_miss(), 1); // Update cache with new value. // We are short on registers, so use no_reg as scratch. // This gives slightly larger code. @@ -2281,10 +2505,10 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack); } else { // UNTAGGED. __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache); - __ sub(Operand(esp), Immediate(kDoubleSize)); + __ sub(esp, Immediate(kDoubleSize)); __ movdbl(Operand(esp, 0), xmm1); __ fld_d(Operand(esp, 0)); - __ add(Operand(esp), Immediate(kDoubleSize)); + __ add(esp, Immediate(kDoubleSize)); } GenerateOperation(masm); __ mov(Operand(ecx, 0), ebx); @@ -2299,20 +2523,21 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Skip cache and return answer directly, only in untagged case. __ bind(&skip_cache); - __ sub(Operand(esp), Immediate(kDoubleSize)); + __ sub(esp, Immediate(kDoubleSize)); __ movdbl(Operand(esp, 0), xmm1); __ fld_d(Operand(esp, 0)); GenerateOperation(masm); __ fstp_d(Operand(esp, 0)); __ movdbl(xmm1, Operand(esp, 0)); - __ add(Operand(esp), Immediate(kDoubleSize)); + __ add(esp, Immediate(kDoubleSize)); // We return the value in xmm1 without adding it to the cache, but // we cause a scavenging GC so that future allocations will succeed. - __ EnterInternalFrame(); - // Allocate an unused object bigger than a HeapNumber. - __ push(Immediate(Smi::FromInt(2 * kDoubleSize))); - __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Allocate an unused object bigger than a HeapNumber. + __ push(Immediate(Smi::FromInt(2 * kDoubleSize))); + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); + } __ Ret(); } @@ -2329,10 +2554,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ bind(&runtime_call); __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache); __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1); - __ EnterInternalFrame(); - __ push(eax); - __ CallRuntime(RuntimeFunction(), 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(eax); + __ CallRuntime(RuntimeFunction(), 1); + } __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); __ Ret(); } @@ -2343,6 +2569,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { switch (type_) { case TranscendentalCache::SIN: return Runtime::kMath_sin; case TranscendentalCache::COS: return Runtime::kMath_cos; + case TranscendentalCache::TAN: return Runtime::kMath_tan; case TranscendentalCache::LOG: return Runtime::kMath_log; default: UNIMPLEMENTED(); @@ -2356,7 +2583,9 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { // Input value is on FP stack, and also in ebx/edx. // Input value is possibly in xmm1. // Address of result (a newly allocated HeapNumber) may be in eax. - if (type_ == TranscendentalCache::SIN || type_ == TranscendentalCache::COS) { + if (type_ == TranscendentalCache::SIN || + type_ == TranscendentalCache::COS || + type_ == TranscendentalCache::TAN) { // Both fsin and fcos require arguments in the range +/-2^63 and // return NaN for infinities and NaN. They can share all code except // the actual fsin/fcos operation. @@ -2364,13 +2593,13 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { // If argument is outside the range -2^63..2^63, fsin/cos doesn't // work. We must reduce it to the appropriate range. __ mov(edi, edx); - __ and_(Operand(edi), Immediate(0x7ff00000)); // Exponent only. + __ and_(edi, Immediate(0x7ff00000)); // Exponent only. int supported_exponent_limit = (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift; - __ cmp(Operand(edi), Immediate(supported_exponent_limit)); + __ cmp(edi, Immediate(supported_exponent_limit)); __ j(below, &in_range, Label::kNear); // Check for infinity and NaN. Both return NaN for sin. - __ cmp(Operand(edi), Immediate(0x7ff00000)); + __ cmp(edi, Immediate(0x7ff00000)); Label non_nan_result; __ j(not_equal, &non_nan_result, Label::kNear); // Input is +/-Infinity or NaN. Result is NaN. @@ -2379,7 +2608,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { __ push(Immediate(0x7ff80000)); __ push(Immediate(0)); __ fld_d(Operand(esp, 0)); - __ add(Operand(esp), Immediate(2 * kPointerSize)); + __ add(esp, Immediate(2 * kPointerSize)); __ jmp(&done, Label::kNear); __ bind(&non_nan_result); @@ -2395,7 +2624,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { __ fwait(); __ fnstsw_ax(); // Clear if Illegal Operand or Zero Division exceptions are set. - __ test(Operand(eax), Immediate(5)); + __ test(eax, Immediate(5)); __ j(zero, &no_exceptions, Label::kNear); __ fnclex(); __ bind(&no_exceptions); @@ -2408,7 +2637,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { __ fprem1(); __ fwait(); __ fnstsw_ax(); - __ test(Operand(eax), Immediate(0x400 /* C2 */)); + __ test(eax, Immediate(0x400 /* C2 */)); // If C2 is set, computation only has partial result. Loop to // continue computation. __ j(not_zero, &partial_remainder_loop); @@ -2427,6 +2656,12 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { case TranscendentalCache::COS: __ fcos(); break; + case TranscendentalCache::TAN: + // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the + // FP register stack. + __ fptan(); + __ fstp(0); // Pop FP register stack. + break; default: UNREACHABLE(); } @@ -2541,13 +2776,13 @@ void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) { __ bind(&load_smi_edx); __ SmiUntag(edx); // Untag smi before converting to float. - __ cvtsi2sd(xmm0, Operand(edx)); + __ cvtsi2sd(xmm0, edx); __ SmiTag(edx); // Retag smi for heap number overwriting test. __ jmp(&load_eax); __ bind(&load_smi_eax); __ SmiUntag(eax); // Untag smi before converting to float. - __ cvtsi2sd(xmm1, Operand(eax)); + __ cvtsi2sd(xmm1, eax); __ SmiTag(eax); // Retag smi for heap number overwriting test. __ bind(&done); @@ -2571,12 +2806,12 @@ void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm, __ jmp(not_numbers); // Argument in eax is not a number. __ bind(&load_smi_edx); __ SmiUntag(edx); // Untag smi before converting to float. - __ cvtsi2sd(xmm0, Operand(edx)); + __ cvtsi2sd(xmm0, edx); __ SmiTag(edx); // Retag smi for heap number overwriting test. __ jmp(&load_eax); __ bind(&load_smi_eax); __ SmiUntag(eax); // Untag smi before converting to float. - __ cvtsi2sd(xmm1, Operand(eax)); + __ cvtsi2sd(xmm1, eax); __ SmiTag(eax); // Retag smi for heap number overwriting test. __ jmp(&done, Label::kNear); __ bind(&load_float_eax); @@ -2592,11 +2827,11 @@ void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm, __ mov(scratch, left); ASSERT(!scratch.is(right)); // We're about to clobber scratch. __ SmiUntag(scratch); - __ cvtsi2sd(xmm0, Operand(scratch)); + __ cvtsi2sd(xmm0, scratch); __ mov(scratch, right); __ SmiUntag(scratch); - __ cvtsi2sd(xmm1, Operand(scratch)); + __ cvtsi2sd(xmm1, scratch); } @@ -2604,12 +2839,12 @@ void FloatingPointHelper::CheckSSE2OperandsAreInt32(MacroAssembler* masm, Label* non_int32, Register scratch) { __ cvttsd2si(scratch, Operand(xmm0)); - __ cvtsi2sd(xmm2, Operand(scratch)); + __ cvtsi2sd(xmm2, scratch); __ ucomisd(xmm0, xmm2); __ j(not_zero, non_int32); __ j(carry, non_int32); __ cvttsd2si(scratch, Operand(xmm1)); - __ cvtsi2sd(xmm2, Operand(scratch)); + __ cvtsi2sd(xmm2, scratch); __ ucomisd(xmm1, xmm2); __ j(not_zero, non_int32); __ j(carry, non_int32); @@ -2703,157 +2938,263 @@ void FloatingPointHelper::CheckFloatOperandsAreInt32(MacroAssembler* masm, void MathPowStub::Generate(MacroAssembler* masm) { - // Registers are used as follows: - // edx = base - // eax = exponent - // ecx = temporary, result - CpuFeatures::Scope use_sse2(SSE2); - Label allocate_return, call_runtime; - - // Load input parameters. - __ mov(edx, Operand(esp, 2 * kPointerSize)); - __ mov(eax, Operand(esp, 1 * kPointerSize)); - - // Save 1 in xmm3 - we need this several times later on. - __ mov(ecx, Immediate(1)); - __ cvtsi2sd(xmm3, Operand(ecx)); - - Label exponent_nonsmi; - Label base_nonsmi; - // If the exponent is a heap number go to that specific case. - __ JumpIfNotSmi(eax, &exponent_nonsmi); - __ JumpIfNotSmi(edx, &base_nonsmi); - - // Optimized version when both exponent and base are smis. - Label powi; - __ SmiUntag(edx); - __ cvtsi2sd(xmm0, Operand(edx)); - __ jmp(&powi); - // exponent is smi and base is a heapnumber. - __ bind(&base_nonsmi); Factory* factory = masm->isolate()->factory(); - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - factory->heap_number_map()); - __ j(not_equal, &call_runtime); + const Register exponent = eax; + const Register base = edx; + const Register scratch = ecx; + const XMMRegister double_result = xmm3; + const XMMRegister double_base = xmm2; + const XMMRegister double_exponent = xmm1; + const XMMRegister double_scratch = xmm4; + + Label call_runtime, done, exponent_not_smi, int_exponent; + + // Save 1 in double_result - we need this several times later on. + __ mov(scratch, Immediate(1)); + __ cvtsi2sd(double_result, scratch); + + if (exponent_type_ == ON_STACK) { + Label base_is_smi, unpack_exponent; + // The exponent and base are supplied as arguments on the stack. + // This can only happen if the stub is called from non-optimized code. + // Load input parameters from stack. + __ mov(base, Operand(esp, 2 * kPointerSize)); + __ mov(exponent, Operand(esp, 1 * kPointerSize)); + + __ JumpIfSmi(base, &base_is_smi, Label::kNear); + __ cmp(FieldOperand(base, HeapObject::kMapOffset), + factory->heap_number_map()); + __ j(not_equal, &call_runtime); + + __ movdbl(double_base, FieldOperand(base, HeapNumber::kValueOffset)); + __ jmp(&unpack_exponent, Label::kNear); + + __ bind(&base_is_smi); + __ SmiUntag(base); + __ cvtsi2sd(double_base, base); + + __ bind(&unpack_exponent); + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear); + __ SmiUntag(exponent); + __ jmp(&int_exponent); + + __ bind(&exponent_not_smi); + __ cmp(FieldOperand(exponent, HeapObject::kMapOffset), + factory->heap_number_map()); + __ j(not_equal, &call_runtime); + __ movdbl(double_exponent, + FieldOperand(exponent, HeapNumber::kValueOffset)); + } else if (exponent_type_ == TAGGED) { + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear); + __ SmiUntag(exponent); + __ jmp(&int_exponent); + + __ bind(&exponent_not_smi); + __ movdbl(double_exponent, + FieldOperand(exponent, HeapNumber::kValueOffset)); + } - __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset)); + if (exponent_type_ != INTEGER) { + Label fast_power; + // Detect integer exponents stored as double. + __ cvttsd2si(exponent, Operand(double_exponent)); + // Skip to runtime if possibly NaN (indicated by the indefinite integer). + __ cmp(exponent, Immediate(0x80000000u)); + __ j(equal, &call_runtime); + __ cvtsi2sd(double_scratch, exponent); + // Already ruled out NaNs for exponent. + __ ucomisd(double_exponent, double_scratch); + __ j(equal, &int_exponent); + + if (exponent_type_ == ON_STACK) { + // Detect square root case. Crankshaft detects constant +/-0.5 at + // compile time and uses DoMathPowHalf instead. We then skip this check + // for non-constant cases of +/-0.5 as these hardly occur. + Label continue_sqrt, continue_rsqrt, not_plus_half; + // Test for 0.5. + // Load double_scratch with 0.5. + __ mov(scratch, Immediate(0x3F000000u)); + __ movd(double_scratch, scratch); + __ cvtss2sd(double_scratch, double_scratch); + // Already ruled out NaNs for exponent. + __ ucomisd(double_scratch, double_exponent); + __ j(not_equal, ¬_plus_half, Label::kNear); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13). + // According to IEEE-754, single-precision -Infinity has the highest + // 9 bits set and the lowest 23 bits cleared. + __ mov(scratch, 0xFF800000u); + __ movd(double_scratch, scratch); + __ cvtss2sd(double_scratch, double_scratch); + __ ucomisd(double_base, double_scratch); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &continue_sqrt, Label::kNear); + __ j(carry, &continue_sqrt, Label::kNear); + + // Set result to Infinity in the special case. + __ xorps(double_result, double_result); + __ subsd(double_result, double_scratch); + __ jmp(&done); + + __ bind(&continue_sqrt); + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorps(double_scratch, double_scratch); + __ addsd(double_scratch, double_base); // Convert -0 to +0. + __ sqrtsd(double_result, double_scratch); + __ jmp(&done); + + // Test for -0.5. + __ bind(¬_plus_half); + // Load double_exponent with -0.5 by substracting 1. + __ subsd(double_scratch, double_result); + // Already ruled out NaNs for exponent. + __ ucomisd(double_scratch, double_exponent); + __ j(not_equal, &fast_power, Label::kNear); + + // Calculates reciprocal of square root of base. Check for the special + // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13). + // According to IEEE-754, single-precision -Infinity has the highest + // 9 bits set and the lowest 23 bits cleared. + __ mov(scratch, 0xFF800000u); + __ movd(double_scratch, scratch); + __ cvtss2sd(double_scratch, double_scratch); + __ ucomisd(double_base, double_scratch); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &continue_rsqrt, Label::kNear); + __ j(carry, &continue_rsqrt, Label::kNear); + + // Set result to 0 in the special case. + __ xorps(double_result, double_result); + __ jmp(&done); + + __ bind(&continue_rsqrt); + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorps(double_exponent, double_exponent); + __ addsd(double_exponent, double_base); // Convert -0 to +0. + __ sqrtsd(double_exponent, double_exponent); + __ divsd(double_result, double_exponent); + __ jmp(&done); + } - // Optimized version of pow if exponent is a smi. - // xmm0 contains the base. - __ bind(&powi); - __ SmiUntag(eax); + // Using FPU instructions to calculate power. + Label fast_power_failed; + __ bind(&fast_power); + __ fnclex(); // Clear flags to catch exceptions later. + // Transfer (B)ase and (E)xponent onto the FPU register stack. + __ sub(esp, Immediate(kDoubleSize)); + __ movdbl(Operand(esp, 0), double_exponent); + __ fld_d(Operand(esp, 0)); // E + __ movdbl(Operand(esp, 0), double_base); + __ fld_d(Operand(esp, 0)); // B, E + + // Exponent is in st(1) and base is in st(0) + // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B) + // FYL2X calculates st(1) * log2(st(0)) + __ fyl2x(); // X + __ fld(0); // X, X + __ frndint(); // rnd(X), X + __ fsub(1); // rnd(X), X-rnd(X) + __ fxch(1); // X - rnd(X), rnd(X) + // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1 + __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X) + __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X) + __ faddp(1); // 1, 2^(X-rnd(X)), rnd(X) + // FSCALE calculates st(0) * 2^st(1) + __ fscale(); // 2^X, rnd(X) + __ fstp(1); + // Bail out to runtime in case of exceptions in the status word. + __ fnstsw_ax(); + __ test_b(eax, 0x5F); // We check for all but precision exception. + __ j(not_zero, &fast_power_failed, Label::kNear); + __ fstp_d(Operand(esp, 0)); + __ movdbl(double_result, Operand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + __ jmp(&done); + + __ bind(&fast_power_failed); + __ fninit(); + __ add(esp, Immediate(kDoubleSize)); + __ jmp(&call_runtime); + } - // Save exponent in base as we need to check if exponent is negative later. - // We know that base and exponent are in different registers. - __ mov(edx, eax); + // Calculate power with integer exponent. + __ bind(&int_exponent); + const XMMRegister double_scratch2 = double_exponent; + __ mov(scratch, exponent); // Back up exponent. + __ movsd(double_scratch, double_base); // Back up base. + __ movsd(double_scratch2, double_result); // Load double_exponent with 1. // Get absolute value of exponent. - Label no_neg; - __ cmp(eax, 0); - __ j(greater_equal, &no_neg, Label::kNear); - __ neg(eax); + Label no_neg, while_true, no_multiply; + __ test(scratch, scratch); + __ j(positive, &no_neg, Label::kNear); + __ neg(scratch); __ bind(&no_neg); - // Load xmm1 with 1. - __ movsd(xmm1, xmm3); - Label while_true; - Label no_multiply; - __ bind(&while_true); - __ shr(eax, 1); + __ shr(scratch, 1); __ j(not_carry, &no_multiply, Label::kNear); - __ mulsd(xmm1, xmm0); + __ mulsd(double_result, double_scratch); __ bind(&no_multiply); - __ mulsd(xmm0, xmm0); - __ j(not_zero, &while_true); - // base has the original value of the exponent - if the exponent is - // negative return 1/result. - __ test(edx, Operand(edx)); - __ j(positive, &allocate_return); - // Special case if xmm1 has reached infinity. - __ mov(ecx, Immediate(0x7FB00000)); - __ movd(xmm0, Operand(ecx)); - __ cvtss2sd(xmm0, xmm0); - __ ucomisd(xmm0, xmm1); - __ j(equal, &call_runtime); - __ divsd(xmm3, xmm1); - __ movsd(xmm1, xmm3); - __ jmp(&allocate_return); - - // exponent (or both) is a heapnumber - no matter what we should now work - // on doubles. - __ bind(&exponent_nonsmi); - __ cmp(FieldOperand(eax, HeapObject::kMapOffset), - factory->heap_number_map()); - __ j(not_equal, &call_runtime); - __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); - // Test if exponent is nan. - __ ucomisd(xmm1, xmm1); - __ j(parity_even, &call_runtime); + __ mulsd(double_scratch, double_scratch); + __ j(not_zero, &while_true); - Label base_not_smi; - Label handle_special_cases; - __ JumpIfNotSmi(edx, &base_not_smi, Label::kNear); - __ SmiUntag(edx); - __ cvtsi2sd(xmm0, Operand(edx)); - __ jmp(&handle_special_cases, Label::kNear); - - __ bind(&base_not_smi); - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - factory->heap_number_map()); - __ j(not_equal, &call_runtime); - __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset)); - __ and_(ecx, HeapNumber::kExponentMask); - __ cmp(Operand(ecx), Immediate(HeapNumber::kExponentMask)); - // base is NaN or +/-Infinity - __ j(greater_equal, &call_runtime); - __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset)); + // scratch has the original value of the exponent - if the exponent is + // negative, return 1/result. + __ test(exponent, exponent); + __ j(positive, &done); + __ divsd(double_scratch2, double_result); + __ movsd(double_result, double_scratch2); + // Test whether result is zero. Bail out to check for subnormal result. + // Due to subnormals, x^-y == (1/x)^y does not hold in all cases. + __ xorps(double_scratch2, double_scratch2); + __ ucomisd(double_scratch2, double_result); // Result cannot be NaN. + // double_exponent aliased as double_scratch2 has already been overwritten + // and may not have contained the exponent value in the first place when the + // exponent is a smi. We reset it with exponent value before bailing out. + __ j(not_equal, &done); + __ cvtsi2sd(double_exponent, exponent); + + // Returning or bailing out. + Counters* counters = masm->isolate()->counters(); + if (exponent_type_ == ON_STACK) { + // The arguments are still on the stack. + __ bind(&call_runtime); + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); - // base is in xmm0 and exponent is in xmm1. - __ bind(&handle_special_cases); - Label not_minus_half; - // Test for -0.5. - // Load xmm2 with -0.5. - __ mov(ecx, Immediate(0xBF000000)); - __ movd(xmm2, Operand(ecx)); - __ cvtss2sd(xmm2, xmm2); - // xmm2 now has -0.5. - __ ucomisd(xmm2, xmm1); - __ j(not_equal, ¬_minus_half, Label::kNear); - - // Calculates reciprocal of square root. - // sqrtsd returns -0 when input is -0. ECMA spec requires +0. - __ xorps(xmm1, xmm1); - __ addsd(xmm1, xmm0); - __ sqrtsd(xmm1, xmm1); - __ divsd(xmm3, xmm1); - __ movsd(xmm1, xmm3); - __ jmp(&allocate_return); - - // Test for 0.5. - __ bind(¬_minus_half); - // Load xmm2 with 0.5. - // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3. - __ addsd(xmm2, xmm3); - // xmm2 now has 0.5. - __ ucomisd(xmm2, xmm1); - __ j(not_equal, &call_runtime); - // Calculates square root. - // sqrtsd returns -0 when input is -0. ECMA spec requires +0. - __ xorps(xmm1, xmm1); - __ addsd(xmm1, xmm0); - __ sqrtsd(xmm1, xmm1); - - __ bind(&allocate_return); - __ AllocateHeapNumber(ecx, eax, edx, &call_runtime); - __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm1); - __ mov(eax, ecx); - __ ret(2 * kPointerSize); + // The stub is called from non-optimized code, which expects the result + // as heap number in exponent. + __ bind(&done); + __ AllocateHeapNumber(eax, scratch, base, &call_runtime); + __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), double_result); + __ IncrementCounter(counters->math_pow(), 1); + __ ret(2 * kPointerSize); + } else { + __ bind(&call_runtime); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(4, scratch); + __ movdbl(Operand(esp, 0 * kDoubleSize), double_base); + __ movdbl(Operand(esp, 1 * kDoubleSize), double_exponent); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), 4); + } + // Return value is in st(0) on ia32. + // Store it into the (fixed) result register. + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + __ movdbl(double_result, Operand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); - __ bind(&call_runtime); - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + __ bind(&done); + __ IncrementCounter(counters->math_pow(), 1); + __ ret(0); + } } @@ -2873,13 +3214,13 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { Label adaptor; __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset)); - __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); __ j(equal, &adaptor, Label::kNear); // Check index against formal parameters count limit passed in // through register eax. Use unsigned comparison to get negative // check for free. - __ cmp(edx, Operand(eax)); + __ cmp(edx, eax); __ j(above_equal, &slow, Label::kNear); // Read the argument from the stack and return it. @@ -2895,7 +3236,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { // comparison to get negative check for free. __ bind(&adaptor); __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ cmp(edx, Operand(ecx)); + __ cmp(edx, ecx); __ j(above_equal, &slow, Label::kNear); // Read the argument from the stack and return it. @@ -2926,7 +3267,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) { Label runtime; __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); - __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); __ j(not_equal, &runtime, Label::kNear); // Patch the arguments.length and the parameters pointer. @@ -2957,7 +3298,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Label adaptor_frame, try_allocate; __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); - __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); __ j(equal, &adaptor_frame, Label::kNear); // No adaptor, parameter count = argument count. @@ -2976,7 +3317,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { // esp[4] = parameter count (tagged) // esp[8] = address of receiver argument // Compute the mapped parameter count = min(ebx, ecx) in ebx. - __ cmp(ebx, Operand(ecx)); + __ cmp(ebx, ecx); __ j(less_equal, &try_allocate, Label::kNear); __ mov(ebx, ecx); @@ -2990,7 +3331,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { const int kParameterMapHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize; Label no_parameter_map; - __ test(ebx, Operand(ebx)); + __ test(ebx, ebx); __ j(zero, &no_parameter_map, Label::kNear); __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize)); __ bind(&no_parameter_map); @@ -2999,7 +3340,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize)); // 3. Arguments object. - __ add(Operand(ebx), Immediate(Heap::kArgumentsObjectSize)); + __ add(ebx, Immediate(Heap::kArgumentsObjectSize)); // Do the allocation of all three objects in one go. __ AllocateInNewSpace(ebx, eax, edx, edi, &runtime, TAG_OBJECT); @@ -3014,7 +3355,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); __ mov(edi, FieldOperand(edi, GlobalObject::kGlobalContextOffset)); __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ test(ebx, Operand(ebx)); + __ test(ebx, ebx); __ j(not_zero, &has_mapped_parameters, Label::kNear); __ mov(edi, Operand(edi, Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX))); @@ -3038,7 +3379,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ mov(FieldOperand(eax, i), edx); } - // Setup the callee in-object property. + // Set up the callee in-object property. STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); __ mov(edx, Operand(esp, 4 * kPointerSize)); __ mov(FieldOperand(eax, JSObject::kHeaderSize + @@ -3051,7 +3392,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Heap::kArgumentsLengthIndex * kPointerSize), ecx); - // Setup the elements pointer in the allocated arguments object. + // Set up the elements pointer in the allocated arguments object. // If we allocated a parameter map, edi will point there, otherwise to the // backing store. __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize)); @@ -3069,7 +3410,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { // Initialize parameter map. If there are no mapped arguments, we're done. Label skip_parameter_map; - __ test(ebx, Operand(ebx)); + __ test(ebx, ebx); __ j(zero, &skip_parameter_map); __ mov(FieldOperand(edi, FixedArray::kMapOffset), @@ -3093,7 +3434,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ mov(eax, Operand(esp, 2 * kPointerSize)); __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS))); __ add(ebx, Operand(esp, 4 * kPointerSize)); - __ sub(ebx, Operand(eax)); + __ sub(ebx, eax); __ mov(ecx, FACTORY->the_hole_value()); __ mov(edx, edi); __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize)); @@ -3110,12 +3451,12 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ jmp(¶meters_test, Label::kNear); __ bind(¶meters_loop); - __ sub(Operand(eax), Immediate(Smi::FromInt(1))); + __ sub(eax, Immediate(Smi::FromInt(1))); __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx); __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx); - __ add(Operand(ebx), Immediate(Smi::FromInt(1))); + __ add(ebx, Immediate(Smi::FromInt(1))); __ bind(¶meters_test); - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(not_zero, ¶meters_loop, Label::kNear); __ pop(ecx); @@ -3135,18 +3476,18 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Label arguments_loop, arguments_test; __ mov(ebx, Operand(esp, 1 * kPointerSize)); __ mov(edx, Operand(esp, 4 * kPointerSize)); - __ sub(Operand(edx), ebx); // Is there a smarter way to do negative scaling? - __ sub(Operand(edx), ebx); + __ sub(edx, ebx); // Is there a smarter way to do negative scaling? + __ sub(edx, ebx); __ jmp(&arguments_test, Label::kNear); __ bind(&arguments_loop); - __ sub(Operand(edx), Immediate(kPointerSize)); + __ sub(edx, Immediate(kPointerSize)); __ mov(eax, Operand(edx, 0)); __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax); - __ add(Operand(ebx), Immediate(Smi::FromInt(1))); + __ add(ebx, Immediate(Smi::FromInt(1))); __ bind(&arguments_test); - __ cmp(ebx, Operand(ecx)); + __ cmp(ebx, ecx); __ j(less, &arguments_loop, Label::kNear); // Restore. @@ -3174,7 +3515,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { Label adaptor_frame, try_allocate, runtime; __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); - __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); __ j(equal, &adaptor_frame, Label::kNear); // Get the length from the frame. @@ -3193,11 +3534,11 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // the arguments object and the elements array. Label add_arguments_object; __ bind(&try_allocate); - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(zero, &add_arguments_object, Label::kNear); __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize)); __ bind(&add_arguments_object); - __ add(Operand(ecx), Immediate(Heap::kArgumentsObjectSizeStrict)); + __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict)); // Do the allocation of both objects in one go. __ AllocateInNewSpace(ecx, eax, edx, ebx, &runtime, TAG_OBJECT); @@ -3224,13 +3565,13 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // If there are no actual arguments, we're done. Label done; - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(zero, &done, Label::kNear); // Get the parameters pointer from the stack. __ mov(edx, Operand(esp, 2 * kPointerSize)); - // Setup the elements pointer in the allocated arguments object and + // Set up the elements pointer in the allocated arguments object and // initialize the header in the elements fixed array. __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict)); __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi); @@ -3246,8 +3587,8 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { __ bind(&loop); __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver. __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx); - __ add(Operand(edi), Immediate(kPointerSize)); - __ sub(Operand(edx), Immediate(kPointerSize)); + __ add(edi, Immediate(kPointerSize)); + __ sub(edx, Immediate(kPointerSize)); __ dec(ecx); __ j(not_zero, &loop); @@ -3268,10 +3609,6 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { #ifdef V8_INTERPRETED_REGEXP __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); #else // V8_INTERPRETED_REGEXP - if (!FLAG_regexp_entry_native) { - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); - return; - } // Stack frame on entry. // esp[0]: return address @@ -3294,7 +3631,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { ExternalReference address_of_regexp_stack_memory_size = ExternalReference::address_of_regexp_stack_memory_size(masm->isolate()); __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size)); - __ test(ebx, Operand(ebx)); + __ test(ebx, ebx); __ j(zero, &runtime); // Check that the first argument is a JSRegExp object. @@ -3315,7 +3652,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // ecx: RegExp data (FixedArray) // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP. __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset)); - __ cmp(Operand(ebx), Immediate(Smi::FromInt(JSRegExp::IRREGEXP))); + __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP))); __ j(not_equal, &runtime); // ecx: RegExp data (FixedArray) @@ -3325,7 +3662,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // uses the asumption that smis are 2 * their untagged value. STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); - __ add(Operand(edx), Immediate(2)); // edx was a smi. + __ add(edx, Immediate(2)); // edx was a smi. // Check that the static offsets vector buffer is large enough. __ cmp(edx, OffsetsVector::kStaticOffsetsVectorSize); __ j(above, &runtime); @@ -3347,7 +3684,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // string length. A negative value will be greater (unsigned comparison). __ mov(eax, Operand(esp, kPreviousIndexOffset)); __ JumpIfNotSmi(eax, &runtime); - __ cmp(eax, Operand(ebx)); + __ cmp(eax, ebx); __ j(above_equal, &runtime); // ecx: RegExp data (FixedArray) @@ -3367,8 +3704,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // additional information. __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset)); __ SmiUntag(eax); - __ add(Operand(edx), Immediate(RegExpImpl::kLastMatchOverhead)); - __ cmp(edx, Operand(eax)); + __ add(edx, Immediate(RegExpImpl::kLastMatchOverhead)); + __ cmp(edx, eax); __ j(greater, &runtime); // Reset offset for possibly sliced string. @@ -3380,27 +3717,40 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); // First check for flat two byte string. - __ and_(ebx, - kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask); + __ and_(ebx, kIsNotStringMask | + kStringRepresentationMask | + kStringEncodingMask | + kShortExternalStringMask); STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0); __ j(zero, &seq_two_byte_string, Label::kNear); - // Any other flat string must be a flat ascii string. - __ and_(Operand(ebx), - Immediate(kIsNotStringMask | kStringRepresentationMask)); + // Any other flat string must be a flat ASCII string. None of the following + // string type tests will succeed if subject is not a string or a short + // external string. + __ and_(ebx, Immediate(kIsNotStringMask | + kStringRepresentationMask | + kShortExternalStringMask)); __ j(zero, &seq_ascii_string, Label::kNear); + // ebx: whether subject is a string and if yes, its string representation // Check for flat cons string or sliced string. // A flat cons string is a cons string where the second part is the empty // string. In that case the subject string is just the first part of the cons // string. Also in this case the first part of the cons string is known to be // a sequential string or an external string. // In the case of a sliced string its offset has to be taken into account. - Label cons_string, check_encoding; + Label cons_string, external_string, check_encoding; STATIC_ASSERT(kConsStringTag < kExternalStringTag); STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - __ cmp(Operand(ebx), Immediate(kExternalStringTag)); + STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); + STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); + __ cmp(ebx, Immediate(kExternalStringTag)); __ j(less, &cons_string); - __ j(equal, &runtime); + __ j(equal, &external_string); + + // Catch non-string subject or short external string. + STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0); + __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag)); + __ j(not_zero, &runtime); // String is sliced. __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset)); @@ -3422,16 +3772,16 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { kStringRepresentationMask | kStringEncodingMask); STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0); __ j(zero, &seq_two_byte_string, Label::kNear); - // Any other flat string must be ascii. + // Any other flat string must be sequential ASCII or external. __ test_b(FieldOperand(ebx, Map::kInstanceTypeOffset), kStringRepresentationMask); - __ j(not_zero, &runtime); + __ j(not_zero, &external_string); __ bind(&seq_ascii_string); - // eax: subject string (flat ascii) + // eax: subject string (flat ASCII) // ecx: RegExp data (FixedArray) __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset)); - __ Set(ecx, Immediate(1)); // Type is ascii. + __ Set(ecx, Immediate(1)); // Type is ASCII. __ jmp(&check_code, Label::kNear); __ bind(&seq_two_byte_string); @@ -3448,7 +3798,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // eax: subject string // edx: code - // ecx: encoding of subject string (1 if ascii, 0 if two_byte); + // ecx: encoding of subject string (1 if ASCII, 0 if two_byte); // Load used arguments before starting to push arguments for call to native // RegExp code to avoid handling changing stack height. __ mov(ebx, Operand(esp, kPreviousIndexOffset)); @@ -3457,7 +3807,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // eax: subject string // ebx: previous index // edx: code - // ecx: encoding of subject string (1 if ascii 0 if two_byte); + // ecx: encoding of subject string (1 if ASCII 0 if two_byte); // All checks done. Now push arguments for native regexp code. Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->regexp_entry_native(), 1); @@ -3497,21 +3847,21 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // esi: original subject string // eax: underlying subject string // ebx: previous index - // ecx: encoding of subject string (1 if ascii 0 if two_byte); + // ecx: encoding of subject string (1 if ASCII 0 if two_byte); // edx: code // Argument 4: End of string data // Argument 3: Start of string data // Prepare start and end index of the input. // Load the length from the original sliced string if that is the case. __ mov(esi, FieldOperand(esi, String::kLengthOffset)); - __ add(esi, Operand(edi)); // Calculate input end wrt offset. + __ add(esi, edi); // Calculate input end wrt offset. __ SmiUntag(edi); - __ add(ebx, Operand(edi)); // Calculate input start wrt offset. + __ add(ebx, edi); // Calculate input start wrt offset. // ebx: start index of the input string // esi: end index of the input string Label setup_two_byte, setup_rest; - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(zero, &setup_two_byte, Label::kNear); __ SmiUntag(esi); __ lea(ecx, FieldOperand(eax, esi, times_1, SeqAsciiString::kHeaderSize)); @@ -3531,8 +3881,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ bind(&setup_rest); // Locate the code entry and call it. - __ add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ call(Operand(edx)); + __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ call(edx); // Drop arguments and come back to JS mode. __ LeaveApiExitFrame(); @@ -3553,11 +3903,9 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // TODO(592): Rerunning the RegExp to get the stack overflow exception. ExternalReference pending_exception(Isolate::kPendingExceptionAddress, masm->isolate()); - __ mov(edx, - Operand::StaticVariable(ExternalReference::the_hole_value_location( - masm->isolate()))); + __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value())); __ mov(eax, Operand::StaticVariable(pending_exception)); - __ cmp(edx, Operand(eax)); + __ cmp(edx, eax); __ j(equal, &runtime); // For exception, throw the exception again. @@ -3578,7 +3926,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ bind(&failure); // For failure to match, return null. - __ mov(Operand(eax), factory->null_value()); + __ mov(eax, factory->null_value()); __ ret(4 * kPointerSize); // Load RegExp data. @@ -3589,7 +3937,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Calculate number of capture registers (number_of_captures + 1) * 2. STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); - __ add(Operand(edx), Immediate(2)); // edx was a smi. + __ add(edx, Immediate(2)); // edx was a smi. // edx: Number of capture registers // Load last_match_info which is still known to be a fast case JSArray. @@ -3605,12 +3953,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Store last subject and last input. __ mov(eax, Operand(esp, kSubjectOffset)); __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax); - __ mov(ecx, ebx); - __ RecordWrite(ecx, RegExpImpl::kLastSubjectOffset, eax, edi); + __ RecordWriteField(ebx, + RegExpImpl::kLastSubjectOffset, + eax, + edi, + kDontSaveFPRegs); __ mov(eax, Operand(esp, kSubjectOffset)); __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax); - __ mov(ecx, ebx); - __ RecordWrite(ecx, RegExpImpl::kLastInputOffset, eax, edi); + __ RecordWriteField(ebx, + RegExpImpl::kLastInputOffset, + eax, + edi, + kDontSaveFPRegs); // Get the static offsets vector filled by the native regexp code. ExternalReference address_of_static_offsets_vector = @@ -3624,7 +3978,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Capture register counter starts from number of capture registers and // counts down until wraping after zero. __ bind(&next_capture); - __ sub(Operand(edx), Immediate(1)); + __ sub(edx, Immediate(1)); __ j(negative, &done, Label::kNear); // Read the value from the static offsets vector buffer. __ mov(edi, Operand(ecx, edx, times_int_size, 0)); @@ -3642,6 +3996,27 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ mov(eax, Operand(esp, kLastMatchInfoOffset)); __ ret(4 * kPointerSize); + // External string. Short external strings have already been ruled out. + // eax: subject string (expected to be external) + // ebx: scratch + __ bind(&external_string); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ test_b(ebx, kIsIndirectStringMask); + __ Assert(zero, "external string expected, but not found"); + } + __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kTwoByteStringTag == 0); + __ test_b(ebx, kStringEncodingMask); + __ j(not_zero, &seq_ascii_string); + __ jmp(&seq_two_byte_string); + // Do the runtime call to execute the regexp. __ bind(&runtime); __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); @@ -3655,7 +4030,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { Label done; __ mov(ebx, Operand(esp, kPointerSize * 3)); __ JumpIfNotSmi(ebx, &slowcase); - __ cmp(Operand(ebx), Immediate(Smi::FromInt(kMaxInlineLength))); + __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength))); __ j(above, &slowcase); // Smi-tagging is equivalent to multiplying by 2. STATIC_ASSERT(kSmiTag == 0); @@ -3715,10 +4090,10 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // ebx: Start of elements in FixedArray. // edx: the hole. Label loop; - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ bind(&loop); __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero. - __ sub(Operand(ecx), Immediate(1)); + __ sub(ecx, Immediate(1)); __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx); __ jmp(&loop); @@ -3743,16 +4118,16 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, Register scratch = scratch2; // Load the number string cache. - ExternalReference roots_address = - ExternalReference::roots_address(masm->isolate()); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(masm->isolate()); __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex)); __ mov(number_string_cache, - Operand::StaticArray(scratch, times_pointer_size, roots_address)); + Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); // Make the hash mask from the length of the number string cache. It // contains two elements (number and string) for each cache entry. __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset)); __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two. - __ sub(Operand(mask), Immediate(1)); // Make mask. + __ sub(mask, Immediate(1)); // Make mask. // Calculate the entry in the number string cache. The hash value in the // number string cache for smis is just the smi value, and the hash for @@ -3778,7 +4153,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset)); __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4)); // Object is heap number and hash is now in scratch. Calculate cache index. - __ and_(scratch, Operand(mask)); + __ and_(scratch, mask); Register index = scratch; Register probe = mask; __ mov(probe, @@ -3804,7 +4179,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, __ bind(&smi_hash_calculated); // Object is smi and hash is now in scratch. Calculate cache index. - __ and_(scratch, Operand(mask)); + __ and_(scratch, mask); Register index = scratch; // Check if the entry is the smi we are looking for. __ cmp(object, @@ -3856,10 +4231,10 @@ void CompareStub::Generate(MacroAssembler* masm) { // Compare two smis if required. if (include_smi_compare_) { Label non_smi, smi_done; - __ mov(ecx, Operand(edx)); - __ or_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ or_(ecx, eax); __ JumpIfNotSmi(ecx, &non_smi, Label::kNear); - __ sub(edx, Operand(eax)); // Return on the result of the subtraction. + __ sub(edx, eax); // Return on the result of the subtraction. __ j(no_overflow, &smi_done, Label::kNear); __ not_(edx); // Correct sign in case of overflow. edx is never 0 here. __ bind(&smi_done); @@ -3867,8 +4242,8 @@ void CompareStub::Generate(MacroAssembler* masm) { __ ret(0); __ bind(&non_smi); } else if (FLAG_debug_code) { - __ mov(ecx, Operand(edx)); - __ or_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ or_(ecx, eax); __ test(ecx, Immediate(kSmiTagMask)); __ Assert(not_zero, "Unexpected smi operands."); } @@ -3880,7 +4255,7 @@ void CompareStub::Generate(MacroAssembler* masm) { // for NaN and undefined. { Label not_identical; - __ cmp(eax, Operand(edx)); + __ cmp(eax, edx); __ j(not_equal, ¬_identical); if (cc_ != equal) { @@ -3929,7 +4304,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ Set(eax, Immediate(0)); // Shift value and mask so kQuietNaNHighBitsMask applies to topmost // bits. - __ add(edx, Operand(edx)); + __ add(edx, edx); __ cmp(edx, kQuietNaNHighBitsMask << 1); if (cc_ == equal) { STATIC_ASSERT(EQUAL != 1); @@ -3963,19 +4338,19 @@ void CompareStub::Generate(MacroAssembler* masm) { STATIC_ASSERT(kSmiTag == 0); ASSERT_EQ(0, Smi::FromInt(0)); __ mov(ecx, Immediate(kSmiTagMask)); - __ and_(ecx, Operand(eax)); - __ test(ecx, Operand(edx)); + __ and_(ecx, eax); + __ test(ecx, edx); __ j(not_zero, ¬_smis, Label::kNear); // One operand is a smi. // Check whether the non-smi is a heap number. STATIC_ASSERT(kSmiTagMask == 1); // ecx still holds eax & kSmiTag, which is either zero or one. - __ sub(Operand(ecx), Immediate(0x01)); + __ sub(ecx, Immediate(0x01)); __ mov(ebx, edx); - __ xor_(ebx, Operand(eax)); - __ and_(ebx, Operand(ecx)); // ebx holds either 0 or eax ^ edx. - __ xor_(ebx, Operand(eax)); + __ xor_(ebx, eax); + __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx. + __ xor_(ebx, eax); // if eax was smi, ebx is now edx, else eax. // Check if the non-smi operand is a heap number. @@ -4037,9 +4412,9 @@ void CompareStub::Generate(MacroAssembler* masm) { // Return a result of -1, 0, or 1, based on EFLAGS. __ mov(eax, 0); // equal __ mov(ecx, Immediate(Smi::FromInt(1))); - __ cmov(above, eax, Operand(ecx)); + __ cmov(above, eax, ecx); __ mov(ecx, Immediate(Smi::FromInt(-1))); - __ cmov(below, eax, Operand(ecx)); + __ cmov(below, eax, ecx); __ ret(0); } else { FloatingPointHelper::CheckFloatOperands( @@ -4100,7 +4475,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &check_unequal_objects); - // Inline comparison of ascii strings. + // Inline comparison of ASCII strings. if (cc_ == equal) { StringCompareStub::GenerateFlatAsciiStringEquals(masm, edx, @@ -4198,43 +4573,84 @@ void StackCheckStub::Generate(MacroAssembler* masm) { } +static void GenerateRecordCallTarget(MacroAssembler* masm) { + // Cache the called function in a global property cell. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // ebx : cache cell for call target + // edi : the function to call + Isolate* isolate = masm->isolate(); + Label initialize, done; + + // Load the cache state into ecx. + __ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset)); + + // A monomorphic cache hit or an already megamorphic state: invoke the + // function without changing the state. + __ cmp(ecx, edi); + __ j(equal, &done, Label::kNear); + __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate))); + __ j(equal, &done, Label::kNear); + + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate))); + __ j(equal, &initialize, Label::kNear); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. + __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), + Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate))); + __ jmp(&done, Label::kNear); + + // An uninitialized cache is patched with the function. + __ bind(&initialize); + __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi); + // No need for a write barrier here - cells are rescanned. + + __ bind(&done); +} + + void CallFunctionStub::Generate(MacroAssembler* masm) { + // ebx : cache cell for call target + // edi : the function to call + Isolate* isolate = masm->isolate(); Label slow, non_function; // The receiver might implicitly be the global object. This is // indicated by passing the hole as the receiver to the call // function stub. if (ReceiverMightBeImplicit()) { - Label call; + Label receiver_ok; // Get the receiver from the stack. // +1 ~ return address __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize)); // Call as function is indicated with the hole. - __ cmp(eax, masm->isolate()->factory()->the_hole_value()); - __ j(not_equal, &call, Label::kNear); + __ cmp(eax, isolate->factory()->the_hole_value()); + __ j(not_equal, &receiver_ok, Label::kNear); // Patch the receiver on the stack with the global receiver object. - __ mov(ebx, GlobalObjectOperand()); - __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); - __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ebx); - __ bind(&call); + __ mov(ecx, GlobalObjectOperand()); + __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset)); + __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx); + __ bind(&receiver_ok); } - // Get the function to call from the stack. - // +2 ~ receiver, return address - __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize)); - // Check that the function really is a JavaScript function. __ JumpIfSmi(edi, &non_function); // Goto slow case if we do not have a function. __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ j(not_equal, &slow); + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + } + // Fast-case: Just invoke the function. ParameterCount actual(argc_); if (ReceiverMightBeImplicit()) { Label call_as_function; - __ cmp(eax, masm->isolate()->factory()->the_hole_value()); + __ cmp(eax, isolate->factory()->the_hole_value()); __ j(equal, &call_as_function); __ InvokeFunction(edi, actual, @@ -4251,6 +4667,13 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Slow-case: Non-function called. __ bind(&slow); + if (RecordCallTarget()) { + // If there is a call target cache, mark it megamorphic in the + // non-function case. MegamorphicSentinel is an immortal immovable + // object (undefined) so no write barrier is needed. + __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), + Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate))); + } // Check for function proxy. __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); __ j(not_equal, &non_function); @@ -4262,8 +4685,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ SetCallKind(ecx, CALL_AS_FUNCTION); __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY); { - Handle<Code> adaptor = - masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); + Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline(); __ jmp(adaptor, RelocInfo::CODE_TARGET); } @@ -4275,17 +4697,89 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ Set(ebx, Immediate(0)); __ SetCallKind(ecx, CALL_AS_METHOD); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); - Handle<Code> adaptor = - masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); + Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline(); __ jmp(adaptor, RelocInfo::CODE_TARGET); } +void CallConstructStub::Generate(MacroAssembler* masm) { + // eax : number of arguments + // ebx : cache cell for call target + // edi : constructor function + Label slow, non_function_call; + + // Check that function is not a smi. + __ JumpIfSmi(edi, &non_function_call); + // Check that function is a JSFunction. + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + } + + // Jump to the function-specific construct stub. + __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset)); + __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); + __ jmp(ebx); + + // edi: called object + // eax: number of arguments + // ecx: object map + Label do_call; + __ bind(&slow); + __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); + __ j(not_equal, &non_function_call); + __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); + __ jmp(&do_call); + + __ bind(&non_function_call); + __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); + __ bind(&do_call); + // Set expected number of arguments to zero (not changing eax). + __ Set(ebx, Immediate(0)); + Handle<Code> arguments_adaptor = + masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); + __ SetCallKind(ecx, CALL_AS_METHOD); + __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET); +} + + bool CEntryStub::NeedsImmovableCode() { return false; } +bool CEntryStub::IsPregenerated() { + return (!save_doubles_ || ISOLATE->fp_stubs_generated()) && + result_size_ == 1; +} + + +void CodeStub::GenerateStubsAheadOfTime() { + CEntryStub::GenerateAheadOfTime(); + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(); + // It is important that the store buffer overflow stubs are generated first. + RecordWriteStub::GenerateFixedRegStubsAheadOfTime(); +} + + +void CodeStub::GenerateFPStubs() { + CEntryStub save_doubles(1, kSaveFPRegs); + Handle<Code> code = save_doubles.GetCode(); + code->set_is_pregenerated(true); + code->GetIsolate()->set_fp_stubs_generated(true); +} + + +void CEntryStub::GenerateAheadOfTime() { + CEntryStub stub(1, kDontSaveFPRegs); + Handle<Code> code = stub.GetCode(); + code->set_is_pregenerated(true); +} + + void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { __ Throw(eax); } @@ -4332,7 +4826,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ mov(Operand(esp, 1 * kPointerSize), esi); // argv. __ mov(Operand(esp, 2 * kPointerSize), Immediate(ExternalReference::isolate_address())); - __ call(Operand(ebx)); + __ call(ebx); // Result is in eax or edx:eax - do not destroy these registers! if (always_allocate_scope) { @@ -4364,8 +4858,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // should have returned some failure value. if (FLAG_debug_code) { __ push(edx); - __ mov(edx, Operand::StaticVariable( - ExternalReference::the_hole_value_location(masm->isolate()))); + __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value())); Label okay; __ cmp(edx, Operand::StaticVariable(pending_exception_address)); // Cannot use check here as it attempts to generate call into runtime. @@ -4376,7 +4869,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(save_doubles_); + __ LeaveExitFrame(save_doubles_ == kSaveFPRegs); __ ret(0); // Handling of failure. @@ -4393,10 +4886,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(equal, throw_out_of_memory_exception); // Retrieve the pending exception and clear the variable. - ExternalReference the_hole_location = - ExternalReference::the_hole_value_location(masm->isolate()); __ mov(eax, Operand::StaticVariable(pending_exception_address)); - __ mov(edx, Operand::StaticVariable(the_hole_location)); + __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value())); __ mov(Operand::StaticVariable(pending_exception_address), edx); // Special handling of termination exceptions which are uncatchable @@ -4431,7 +4922,7 @@ void CEntryStub::Generate(MacroAssembler* masm) { // a garbage collection and retrying the builtin (twice). // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(save_doubles_); + __ EnterExitFrame(save_doubles_ == kSaveFPRegs); // eax: result parameter for PerformGC, if any (setup below) // ebx: pointer to builtin function (C callee-saved) @@ -4482,12 +4973,12 @@ void CEntryStub::Generate(MacroAssembler* masm) { void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { - Label invoke, exit; + Label invoke, handler_entry, exit; Label not_outermost_js, not_outermost_js_2; - // Setup frame. + // Set up frame. __ push(ebp); - __ mov(ebp, Operand(esp)); + __ mov(ebp, esp); // Push marker in two places. int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; @@ -4515,38 +5006,38 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME))); __ bind(&cont); - // Call a faked try-block that does the invoke. - __ call(&invoke); - - // Caught exception: Store result (exception) in the pending - // exception field in the JSEnv and return a failure sentinel. + // Jump to a faked try block that does the invoke, with a faked catch + // block that sets the pending exception. + __ jmp(&invoke); + __ bind(&handler_entry); + handler_offset_ = handler_entry.pos(); + // Caught exception: Store result (exception) in the pending exception + // field in the JSEnv and return a failure sentinel. ExternalReference pending_exception(Isolate::kPendingExceptionAddress, masm->isolate()); __ mov(Operand::StaticVariable(pending_exception), eax); __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception())); __ jmp(&exit); - // Invoke: Link this frame into the handler chain. + // Invoke: Link this frame into the handler chain. There's only one + // handler block in this code object, so its index is 0. __ bind(&invoke); - __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); + __ PushTryHandler(StackHandler::JS_ENTRY, 0); // Clear any pending exceptions. - ExternalReference the_hole_location = - ExternalReference::the_hole_value_location(masm->isolate()); - __ mov(edx, Operand::StaticVariable(the_hole_location)); + __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value())); __ mov(Operand::StaticVariable(pending_exception), edx); // Fake a receiver (NULL). __ push(Immediate(0)); // receiver - // Invoke the function by calling through JS entry trampoline - // builtin and pop the faked function when we return. Notice that we - // cannot store a reference to the trampoline code directly in this - // stub, because the builtin stubs may not have been generated yet. + // Invoke the function by calling through JS entry trampoline builtin and + // pop the faked function when we return. Notice that we cannot store a + // reference to the trampoline code directly in this stub, because the + // builtin stubs may not have been generated yet. if (is_construct) { - ExternalReference construct_entry( - Builtins::kJSConstructEntryTrampoline, - masm->isolate()); + ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline, + masm->isolate()); __ mov(edx, Immediate(construct_entry)); } else { ExternalReference entry(Builtins::kJSEntryTrampoline, @@ -4555,7 +5046,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { } __ mov(edx, Operand(edx, 0)); // deref address __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); - __ call(Operand(edx)); + __ call(edx); // Unlink this frame from the handler chain. __ PopTryHandler(); @@ -4563,8 +5054,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ bind(&exit); // Check if the current stack frame is marked as the outermost JS frame. __ pop(ebx); - __ cmp(Operand(ebx), - Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); + __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); __ j(not_equal, ¬_outermost_js_2); __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0)); __ bind(¬_outermost_js_2); @@ -4578,7 +5068,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ pop(ebx); __ pop(esi); __ pop(edi); - __ add(Operand(esp), Immediate(2 * kPointerSize)); // remove markers + __ add(esp, Immediate(2 * kPointerSize)); // remove markers // Restore frame pointer and return. __ pop(ebp); @@ -4617,12 +5107,12 @@ void InstanceofStub::Generate(MacroAssembler* masm) { static const int kDeltaToCmpImmediate = 2; static const int kDeltaToMov = 8; static const int kDeltaToMovImmediate = 9; - static const int8_t kCmpEdiImmediateByte1 = BitCast<int8_t, uint8_t>(0x81); - static const int8_t kCmpEdiImmediateByte2 = BitCast<int8_t, uint8_t>(0xff); + static const int8_t kCmpEdiOperandByte1 = BitCast<int8_t, uint8_t>(0x3b); + static const int8_t kCmpEdiOperandByte2 = BitCast<int8_t, uint8_t>(0x3d); static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8); - ExternalReference roots_address = - ExternalReference::roots_address(masm->isolate()); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(masm->isolate()); ASSERT_EQ(object.code(), InstanceofStub::left().code()); ASSERT_EQ(function.code(), InstanceofStub::right().code()); @@ -4644,22 +5134,23 @@ void InstanceofStub::Generate(MacroAssembler* masm) { // Look up the function and the map in the instanceof cache. Label miss; __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ cmp(function, - Operand::StaticArray(scratch, times_pointer_size, roots_address)); + __ cmp(function, Operand::StaticArray(scratch, + times_pointer_size, + roots_array_start)); __ j(not_equal, &miss, Label::kNear); __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); __ cmp(map, Operand::StaticArray( - scratch, times_pointer_size, roots_address)); + scratch, times_pointer_size, roots_array_start)); __ j(not_equal, &miss, Label::kNear); __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); __ mov(eax, Operand::StaticArray( - scratch, times_pointer_size, roots_address)); + scratch, times_pointer_size, roots_array_start)); __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize); __ bind(&miss); } // Get the prototype of the function. - __ TryGetFunctionPrototype(function, prototype, scratch, &slow); + __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(prototype, &slow); @@ -4669,9 +5160,10 @@ void InstanceofStub::Generate(MacroAssembler* masm) { // map and function. The cached answer will be set when it is known below. if (!HasCallSiteInlineCheck()) { __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); - __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), map); + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), + map); __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), + __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), function); } else { // The constants for the code patching are based on no push instructions @@ -4681,12 +5173,13 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ mov(scratch, Operand(esp, 0 * kPointerSize)); __ sub(scratch, Operand(esp, 1 * kPointerSize)); if (FLAG_debug_code) { - __ cmpb(Operand(scratch, 0), kCmpEdiImmediateByte1); + __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1); __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)"); - __ cmpb(Operand(scratch, 1), kCmpEdiImmediateByte2); + __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2); __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)"); } - __ mov(Operand(scratch, kDeltaToCmpImmediate), map); + __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate)); + __ mov(Operand(scratch, 0), map); } // Loop through the prototype chain of the object looking for the function @@ -4694,10 +5187,10 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset)); Label loop, is_instance, is_not_instance; __ bind(&loop); - __ cmp(scratch, Operand(prototype)); + __ cmp(scratch, prototype); __ j(equal, &is_instance, Label::kNear); Factory* factory = masm->isolate()->factory(); - __ cmp(Operand(scratch), Immediate(factory->null_value())); + __ cmp(scratch, Immediate(factory->null_value())); __ j(equal, &is_not_instance, Label::kNear); __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset)); @@ -4708,7 +5201,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ Set(eax, Immediate(0)); __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); __ mov(Operand::StaticArray(scratch, - times_pointer_size, roots_address), eax); + times_pointer_size, roots_array_start), eax); } else { // Get return address and delta to inlined map check. __ mov(eax, factory->true_value()); @@ -4730,7 +5223,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ Set(eax, Immediate(Smi::FromInt(1))); __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); __ mov(Operand::StaticArray( - scratch, times_pointer_size, roots_address), eax); + scratch, times_pointer_size, roots_array_start), eax); } else { // Get return address and delta to inlined map check. __ mov(eax, factory->false_value()); @@ -4788,13 +5281,14 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); } else { // Call the builtin and convert 0/1 to true/false. - __ EnterInternalFrame(); - __ push(object); - __ push(function); - __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(object); + __ push(function); + __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); + } Label true_value, done; - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(zero, &true_value, Label::kNear); __ mov(eax, factory->false_value()); __ jmp(&done, Label::kNear); @@ -4854,11 +5348,6 @@ void CompareStub::PrintName(StringStream* stream) { // StringCharCodeAtGenerator void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { - Label flat_string; - Label ascii_string; - Label got_char_code; - Label sliced_string; - // If the receiver is a smi trigger the non-string case. STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(object_, receiver_not_string_); @@ -4873,85 +5362,26 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // If the index is non-smi trigger the non-smi case. STATIC_ASSERT(kSmiTag == 0); __ JumpIfNotSmi(index_, &index_not_smi_); - - // Put smi-tagged index into scratch register. - __ mov(scratch_, index_); __ bind(&got_smi_index_); // Check for index out of range. - __ cmp(scratch_, FieldOperand(object_, String::kLengthOffset)); + __ cmp(index_, FieldOperand(object_, String::kLengthOffset)); __ j(above_equal, index_out_of_range_); - // We need special handling for non-flat strings. - STATIC_ASSERT(kSeqStringTag == 0); - __ test(result_, Immediate(kStringRepresentationMask)); - __ j(zero, &flat_string); + __ SmiUntag(index_); - // Handle non-flat strings. - __ and_(result_, kStringRepresentationMask); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - __ cmp(result_, kExternalStringTag); - __ j(greater, &sliced_string, Label::kNear); - __ j(equal, &call_runtime_); - - // ConsString. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - Label assure_seq_string; - __ cmp(FieldOperand(object_, ConsString::kSecondOffset), - Immediate(masm->isolate()->factory()->empty_string())); - __ j(not_equal, &call_runtime_); - // Get the first of the two strings and load its instance type. - __ mov(object_, FieldOperand(object_, ConsString::kFirstOffset)); - __ jmp(&assure_seq_string, Label::kNear); - - // SlicedString, unpack and add offset. - __ bind(&sliced_string); - __ add(scratch_, FieldOperand(object_, SlicedString::kOffsetOffset)); - __ mov(object_, FieldOperand(object_, SlicedString::kParentOffset)); - - // Assure that we are dealing with a sequential string. Go to runtime if not. - __ bind(&assure_seq_string); - __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); - __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kSeqStringTag == 0); - __ test(result_, Immediate(kStringRepresentationMask)); - __ j(not_zero, &call_runtime_); - __ jmp(&flat_string, Label::kNear); + Factory* factory = masm->isolate()->factory(); + StringCharLoadGenerator::Generate( + masm, factory, object_, index_, result_, &call_runtime_); - // Check for 1-byte or 2-byte string. - __ bind(&flat_string); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ test(result_, Immediate(kStringEncodingMask)); - __ j(not_zero, &ascii_string, Label::kNear); - - // 2-byte string. - // Load the 2-byte character code into the result register. - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); - __ movzx_w(result_, FieldOperand(object_, - scratch_, times_1, // Scratch is smi-tagged. - SeqTwoByteString::kHeaderSize)); - __ jmp(&got_char_code, Label::kNear); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ SmiUntag(scratch_); - __ movzx_b(result_, FieldOperand(object_, - scratch_, times_1, - SeqAsciiString::kHeaderSize)); - __ bind(&got_char_code); __ SmiTag(result_); __ bind(&exit_); } void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharCodeAt slow case"); // Index is not a smi. @@ -4963,7 +5393,6 @@ void StringCharCodeAtGenerator::GenerateSlow( DONT_DO_SMI_CHECK); call_helper.BeforeCall(masm); __ push(object_); - __ push(index_); __ push(index_); // Consumed by runtime conversion function. if (index_flags_ == STRING_INDEX_IS_NUMBER) { __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); @@ -4972,12 +5401,11 @@ void StringCharCodeAtGenerator::GenerateSlow( // NumberToSmi discards numbers that are not exact integers. __ CallRuntime(Runtime::kNumberToSmi, 1); } - if (!scratch_.is(eax)) { + if (!index_.is(eax)) { // Save the conversion result before the pop instructions below // have a chance to overwrite it. - __ mov(scratch_, eax); + __ mov(index_, eax); } - __ pop(index_); __ pop(object_); // Reload the instance type. __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); @@ -4985,7 +5413,7 @@ void StringCharCodeAtGenerator::GenerateSlow( call_helper.AfterCall(masm); // If index is still not a smi, it must be out of range. STATIC_ASSERT(kSmiTag == 0); - __ JumpIfNotSmi(scratch_, index_out_of_range_); + __ JumpIfNotSmi(index_, index_out_of_range_); // Otherwise, return to the fast path. __ jmp(&got_smi_index_); @@ -4995,6 +5423,7 @@ void StringCharCodeAtGenerator::GenerateSlow( __ bind(&call_runtime_); call_helper.BeforeCall(masm); __ push(object_); + __ SmiTag(index_); __ push(index_); __ CallRuntime(Runtime::kStringCharCodeAt, 2); if (!result_.is(eax)) { @@ -5025,7 +5454,7 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); STATIC_ASSERT(kSmiShiftSize == 0); - // At this point code register contains smi tagged ascii char code. + // At this point code register contains smi tagged ASCII char code. __ mov(result_, FieldOperand(result_, code_, times_half_pointer_size, FixedArray::kHeaderSize)); @@ -5036,7 +5465,8 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { void StringCharFromCodeGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharFromCode slow case"); __ bind(&slow_case_); @@ -5063,14 +5493,15 @@ void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) { void StringCharAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { char_code_at_generator_.GenerateSlow(masm, call_helper); char_from_code_generator_.GenerateSlow(masm, call_helper); } void StringAddStub::Generate(MacroAssembler* masm) { - Label string_add_runtime, call_builtin; + Label call_runtime, call_builtin; Builtins::JavaScript builtin_id = Builtins::ADD; // Load the two arguments. @@ -5079,14 +5510,14 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Make sure that both arguments are strings if not known in advance. if (flags_ == NO_STRING_ADD_FLAGS) { - __ JumpIfSmi(eax, &string_add_runtime); + __ JumpIfSmi(eax, &call_runtime); __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx); - __ j(above_equal, &string_add_runtime); + __ j(above_equal, &call_runtime); // First argument is a a string, test second. - __ JumpIfSmi(edx, &string_add_runtime); + __ JumpIfSmi(edx, &call_runtime); __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx); - __ j(above_equal, &string_add_runtime); + __ j(above_equal, &call_runtime); } else { // Here at least one of the arguments is definitely a string. // We convert the one that is not known to be a string. @@ -5110,7 +5541,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { Label second_not_zero_length, both_not_zero_length; __ mov(ecx, FieldOperand(edx, String::kLengthOffset)); STATIC_ASSERT(kSmiTag == 0); - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(not_zero, &second_not_zero_length, Label::kNear); // Second string is empty, result is first string which is already in eax. Counters* counters = masm->isolate()->counters(); @@ -5119,7 +5550,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ bind(&second_not_zero_length); __ mov(ebx, FieldOperand(eax, String::kLengthOffset)); STATIC_ASSERT(kSmiTag == 0); - __ test(ebx, Operand(ebx)); + __ test(ebx, ebx); __ j(not_zero, &both_not_zero_length, Label::kNear); // First string is empty, result is second string which is in edx. __ mov(eax, edx); @@ -5134,18 +5565,17 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Look at the length of the result of adding the two strings. Label string_add_flat_result, longer_than_two; __ bind(&both_not_zero_length); - __ add(ebx, Operand(ecx)); + __ add(ebx, ecx); STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength); // Handle exceptionally long strings in the runtime system. - __ j(overflow, &string_add_runtime); + __ j(overflow, &call_runtime); // Use the symbol table when adding two one character strings, as it // helps later optimizations to return a symbol here. - __ cmp(Operand(ebx), Immediate(Smi::FromInt(2))); + __ cmp(ebx, Immediate(Smi::FromInt(2))); __ j(not_equal, &longer_than_two); - // Check that both strings are non-external ascii strings. - __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, - &string_add_runtime); + // Check that both strings are non-external ASCII strings. + __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime); // Get the two characters forming the new string. __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize)); @@ -5170,14 +5600,10 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize)); __ bind(&make_two_character_string_no_reload); __ IncrementCounter(counters->string_add_make_two_char(), 1); - __ AllocateAsciiString(eax, // Result. - 2, // Length. - edi, // Scratch 1. - edx, // Scratch 2. - &string_add_runtime); + __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime); // Pack both characters in ebx. __ shl(ecx, kBitsPerByte); - __ or_(ebx, Operand(ecx)); + __ or_(ebx, ecx); // Set the characters in the new string. __ mov_w(FieldOperand(eax, SeqAsciiString::kHeaderSize), ebx); __ IncrementCounter(counters->string_add_native(), 1); @@ -5185,24 +5611,24 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ bind(&longer_than_two); // Check if resulting string will be flat. - __ cmp(Operand(ebx), Immediate(Smi::FromInt(String::kMinNonFlatLength))); + __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength))); __ j(below, &string_add_flat_result); // If result is not supposed to be flat allocate a cons string object. If both - // strings are ascii the result is an ascii cons string. + // strings are ASCII the result is an ASCII cons string. Label non_ascii, allocated, ascii_data; __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset)); __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset)); - __ and_(ecx, Operand(edi)); + __ and_(ecx, edi); STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ test(ecx, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii); __ bind(&ascii_data); - // Allocate an acsii cons string. - __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); + // Allocate an ASCII cons string. + __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime); __ bind(&allocated); // Fill the fields of the cons string. if (FLAG_debug_code) __ AbortIfNotSmi(ebx); @@ -5216,77 +5642,106 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ ret(2 * kPointerSize); __ bind(&non_ascii); // At least one of the strings is two-byte. Check whether it happens - // to contain only ascii characters. + // to contain only ASCII characters. // ecx: first instance type AND second instance type. // edi: second instance type. __ test(ecx, Immediate(kAsciiDataHintMask)); __ j(not_zero, &ascii_data); __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); - __ xor_(edi, Operand(ecx)); + __ xor_(edi, ecx); STATIC_ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0); __ and_(edi, kAsciiStringTag | kAsciiDataHintTag); __ cmp(edi, kAsciiStringTag | kAsciiDataHintTag); __ j(equal, &ascii_data); // Allocate a two byte cons string. - __ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime); + __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime); __ jmp(&allocated); - // Handle creating a flat result. First check that both strings are not - // external strings. + // We cannot encounter sliced strings or cons strings here since: + STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength); + // Handle creating a flat result from either external or sequential strings. + // Locate the first characters' locations. // eax: first string // ebx: length of resulting flat string as a smi // edx: second string + Label first_prepared, second_prepared; + Label first_is_sequential, second_is_sequential; __ bind(&string_add_flat_result); __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); - __ and_(ecx, kStringRepresentationMask); - __ cmp(ecx, kExternalStringTag); - __ j(equal, &string_add_runtime); - __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); - __ and_(ecx, kStringRepresentationMask); - __ cmp(ecx, kExternalStringTag); - __ j(equal, &string_add_runtime); - // We cannot encounter sliced strings here since: - STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength); - // Now check if both strings are ascii strings. - // eax: first string - // ebx: length of resulting flat string as a smi - // edx: second string - Label non_ascii_string_add_flat_result; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); + // ecx: instance type of first string + STATIC_ASSERT(kSeqStringTag == 0); + __ test_b(ecx, kStringRepresentationMask); + __ j(zero, &first_is_sequential, Label::kNear); + // Rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ test_b(ecx, kShortExternalStringMask); + __ j(not_zero, &call_runtime); + __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset)); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ jmp(&first_prepared, Label::kNear); + __ bind(&first_is_sequential); + __ add(eax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ bind(&first_prepared); + + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset)); + // Check whether both strings have same encoding. + // edi: instance type of second string + __ xor_(ecx, edi); + __ test_b(ecx, kStringEncodingMask); + __ j(not_zero, &call_runtime); + STATIC_ASSERT(kSeqStringTag == 0); + __ test_b(edi, kStringRepresentationMask); + __ j(zero, &second_is_sequential, Label::kNear); + // Rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ test_b(edi, kShortExternalStringMask); + __ j(not_zero, &call_runtime); + __ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset)); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ jmp(&second_prepared, Label::kNear); + __ bind(&second_is_sequential); + __ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ bind(&second_prepared); + + // Push the addresses of both strings' first characters onto the stack. + __ push(edx); + __ push(eax); + + Label non_ascii_string_add_flat_result, call_runtime_drop_two; + // edi: instance type of second string + // First string and second string have the same encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ test_b(edi, kStringEncodingMask); __ j(zero, &non_ascii_string_add_flat_result); - __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); - __ j(zero, &string_add_runtime); - // Both strings are ascii strings. As they are short they are both flat. + // Both strings are ASCII strings. // ebx: length of resulting flat string as a smi __ SmiUntag(ebx); - __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); + __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two); // eax: result string __ mov(ecx, eax); // Locate first character of result. - __ add(Operand(ecx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Load first argument and locate first character. - __ mov(edx, Operand(esp, 2 * kPointerSize)); + __ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // Load first argument's length and first character location. Account for + // values currently on the stack when fetching arguments from it. + __ mov(edx, Operand(esp, 4 * kPointerSize)); __ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ SmiUntag(edi); - __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ pop(edx); // eax: result string // ecx: first character of result // edx: first char of first argument // edi: length of first argument StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); - // Load second argument and locate first character. - __ mov(edx, Operand(esp, 1 * kPointerSize)); + // Load second argument's length and first character location. Account for + // values currently on the stack when fetching arguments from it. + __ mov(edx, Operand(esp, 2 * kPointerSize)); __ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ SmiUntag(edi); - __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ pop(edx); // eax: result string // ecx: next character of result // edx: first char of second argument @@ -5300,34 +5755,30 @@ void StringAddStub::Generate(MacroAssembler* masm) { // ebx: length of resulting flat string as a smi // edx: second string __ bind(&non_ascii_string_add_flat_result); - __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); - __ j(not_zero, &string_add_runtime); - // Both strings are two byte strings. As they are short they are both - // flat. + // Both strings are two byte strings. __ SmiUntag(ebx); - __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); + __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two); // eax: result string __ mov(ecx, eax); // Locate first character of result. - __ add(Operand(ecx), - Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Load first argument and locate first character. - __ mov(edx, Operand(esp, 2 * kPointerSize)); + __ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // Load second argument's length and first character location. Account for + // values currently on the stack when fetching arguments from it. + __ mov(edx, Operand(esp, 4 * kPointerSize)); __ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ SmiUntag(edi); - __ add(Operand(edx), - Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + __ pop(edx); // eax: result string // ecx: first character of result // edx: first char of first argument // edi: length of first argument StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); - // Load second argument and locate first character. - __ mov(edx, Operand(esp, 1 * kPointerSize)); + // Load second argument's length and first character location. Account for + // values currently on the stack when fetching arguments from it. + __ mov(edx, Operand(esp, 2 * kPointerSize)); __ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ SmiUntag(edi); - __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ pop(edx); // eax: result string // ecx: next character of result // edx: first char of second argument @@ -5336,8 +5787,11 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ IncrementCounter(counters->string_add_native(), 1); __ ret(2 * kPointerSize); + // Recover stack pointer before jumping to runtime. + __ bind(&call_runtime_drop_two); + __ Drop(2); // Just jump to runtime to add the two strings. - __ bind(&string_add_runtime); + __ bind(&call_runtime); __ TailCallRuntime(Runtime::kStringAdd, 2, 1); if (call_builtin.is_linked()) { @@ -5403,15 +5857,15 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, if (ascii) { __ mov_b(scratch, Operand(src, 0)); __ mov_b(Operand(dest, 0), scratch); - __ add(Operand(src), Immediate(1)); - __ add(Operand(dest), Immediate(1)); + __ add(src, Immediate(1)); + __ add(dest, Immediate(1)); } else { __ mov_w(scratch, Operand(src, 0)); __ mov_w(Operand(dest, 0), scratch); - __ add(Operand(src), Immediate(2)); - __ add(Operand(dest), Immediate(2)); + __ add(src, Immediate(2)); + __ add(dest, Immediate(2)); } - __ sub(Operand(count), Immediate(1)); + __ sub(count, Immediate(1)); __ j(not_zero, &loop); } @@ -5434,7 +5888,7 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm, // Nothing to do for zero characters. Label done; - __ test(count, Operand(count)); + __ test(count, count); __ j(zero, &done); // Make count the number of bytes to copy. @@ -5459,7 +5913,7 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm, // Check if there are more bytes to copy. __ bind(&last_bytes); - __ test(count, Operand(count)); + __ test(count, count); __ j(zero, &done); // Copy remaining characters. @@ -5467,9 +5921,9 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm, __ bind(&loop); __ mov_b(scratch, Operand(src, 0)); __ mov_b(Operand(dest, 0), scratch); - __ add(Operand(src), Immediate(1)); - __ add(Operand(dest), Immediate(1)); - __ sub(Operand(count), Immediate(1)); + __ add(src, Immediate(1)); + __ add(dest, Immediate(1)); + __ sub(count, Immediate(1)); __ j(not_zero, &loop); __ bind(&done); @@ -5491,12 +5945,12 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, // different hash algorithm. Don't try to look for these in the symbol table. Label not_array_index; __ mov(scratch, c1); - __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); - __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); + __ sub(scratch, Immediate(static_cast<int>('0'))); + __ cmp(scratch, Immediate(static_cast<int>('9' - '0'))); __ j(above, ¬_array_index, Label::kNear); __ mov(scratch, c2); - __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); - __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); + __ sub(scratch, Immediate(static_cast<int>('0'))); + __ cmp(scratch, Immediate(static_cast<int>('9' - '0'))); __ j(below_equal, not_probed); __ bind(¬_array_index); @@ -5509,24 +5963,24 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, // Collect the two characters in a register. Register chars = c1; __ shl(c2, kBitsPerByte); - __ or_(chars, Operand(c2)); + __ or_(chars, c2); // chars: two character string, char 1 in byte 0 and char 2 in byte 1. // hash: hash of two character string. // Load the symbol table. Register symbol_table = c2; - ExternalReference roots_address = - ExternalReference::roots_address(masm->isolate()); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(masm->isolate()); __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex)); __ mov(symbol_table, - Operand::StaticArray(scratch, times_pointer_size, roots_address)); + Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); // Calculate capacity mask from the symbol table capacity. Register mask = scratch2; __ mov(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset)); __ SmiUntag(mask); - __ sub(Operand(mask), Immediate(1)); + __ sub(mask, Immediate(1)); // Registers // chars: two character string, char 1 in byte 0 and char 2 in byte 1. @@ -5544,9 +5998,9 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, // Calculate entry in symbol table. __ mov(scratch, hash); if (i > 0) { - __ add(Operand(scratch), Immediate(SymbolTable::GetProbeOffset(i))); + __ add(scratch, Immediate(SymbolTable::GetProbeOffset(i))); } - __ and_(scratch, Operand(mask)); + __ and_(scratch, mask); // Load the entry from the symbol table. STATIC_ASSERT(SymbolTable::kEntrySize == 1); @@ -5560,7 +6014,7 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, Factory* factory = masm->isolate()->factory(); __ cmp(candidate, factory->undefined_value()); __ j(equal, not_found); - __ cmp(candidate, factory->null_value()); + __ cmp(candidate, factory->the_hole_value()); __ j(equal, &next_probe[i]); // If length is not 2 the string is not a candidate. @@ -5573,7 +6027,7 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, __ push(mask); Register temp = mask; - // Check that the candidate is a non-external ascii string. + // Check that the candidate is a non-external ASCII string. __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset)); __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset)); __ JumpIfInstanceTypeIsNotSequentialAscii( @@ -5582,7 +6036,7 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, // Check if the two characters match. __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize)); __ and_(temp, 0x0000ffff); - __ cmp(chars, Operand(temp)); + __ cmp(chars, temp); __ j(equal, &found_in_symbol_table); __ bind(&next_probe_pop_mask[i]); __ pop(mask); @@ -5608,17 +6062,17 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register scratch) { // hash = (seed + character) + ((seed + character) << 10); if (Serializer::enabled()) { - ExternalReference roots_address = - ExternalReference::roots_address(masm->isolate()); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(masm->isolate()); __ mov(scratch, Immediate(Heap::kHashSeedRootIndex)); __ mov(scratch, Operand::StaticArray(scratch, times_pointer_size, - roots_address)); + roots_array_start)); __ SmiUntag(scratch); - __ add(scratch, Operand(character)); + __ add(scratch, character); __ mov(hash, scratch); __ shl(scratch, 10); - __ add(hash, Operand(scratch)); + __ add(hash, scratch); } else { int32_t seed = masm->isolate()->heap()->HashSeed(); __ lea(scratch, Operand(character, seed)); @@ -5628,7 +6082,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, // hash ^= hash >> 6; __ mov(scratch, hash); __ shr(scratch, 6); - __ xor_(hash, Operand(scratch)); + __ xor_(hash, scratch); } @@ -5637,15 +6091,15 @@ void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm, Register character, Register scratch) { // hash += character; - __ add(hash, Operand(character)); + __ add(hash, character); // hash += hash << 10; __ mov(scratch, hash); __ shl(scratch, 10); - __ add(hash, Operand(scratch)); + __ add(hash, scratch); // hash ^= hash >> 6; __ mov(scratch, hash); __ shr(scratch, 6); - __ xor_(hash, Operand(scratch)); + __ xor_(hash, scratch); } @@ -5655,15 +6109,15 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, // hash += hash << 3; __ mov(scratch, hash); __ shl(scratch, 3); - __ add(hash, Operand(scratch)); + __ add(hash, scratch); // hash ^= hash >> 11; __ mov(scratch, hash); __ shr(scratch, 11); - __ xor_(hash, Operand(scratch)); + __ xor_(hash, scratch); // hash += hash << 15; __ mov(scratch, hash); __ shl(scratch, 15); - __ add(hash, Operand(scratch)); + __ add(hash, scratch); __ and_(hash, String::kHashBitMask); @@ -5700,22 +6154,25 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ JumpIfNotSmi(ecx, &runtime); __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index. __ JumpIfNotSmi(edx, &runtime); - __ sub(ecx, Operand(edx)); + __ sub(ecx, edx); __ cmp(ecx, FieldOperand(eax, String::kLengthOffset)); - Label return_eax; - __ j(equal, &return_eax); + Label not_original_string; + __ j(not_equal, ¬_original_string, Label::kNear); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(3 * kPointerSize); + __ bind(¬_original_string); // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character // cache). Two character strings are looked for in the symbol cache. - __ SmiUntag(ecx); // Result length is no longer smi. - __ cmp(ecx, 2); + __ cmp(ecx, Immediate(Smi::FromInt(2))); __ j(greater, &result_longer_than_two); __ j(less, &runtime); // Sub string of length 2 requested. // eax: string // ebx: instance type - // ecx: sub string length (value is 2) + // ecx: sub string length (smi, value is 2) // edx: from index (smi) __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime); @@ -5726,69 +6183,73 @@ void SubStringStub::Generate(MacroAssembler* masm) { FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1)); // Try to lookup two character string in symbol table. - Label make_two_character_string; + Label combine_two_char, save_two_char; StringHelper::GenerateTwoCharacterSymbolTableProbe( - masm, ebx, ecx, eax, edx, edi, - &make_two_character_string, &make_two_character_string); + masm, ebx, ecx, eax, edx, edi, &combine_two_char, &save_two_char); + __ IncrementCounter(counters->sub_string_native(), 1); __ ret(3 * kPointerSize); - __ bind(&make_two_character_string); - // Setup registers for allocating the two character string. - __ mov(eax, Operand(esp, 3 * kPointerSize)); - __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ bind(&combine_two_char); + __ shl(ecx, kBitsPerByte); + __ or_(ebx, ecx); + __ bind(&save_two_char); + __ AllocateAsciiString(eax, 2, ecx, edx, &runtime); + __ mov_w(FieldOperand(eax, SeqAsciiString::kHeaderSize), ebx); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(3 * kPointerSize); + + __ bind(&result_longer_than_two); + // eax: string + // ebx: instance type + // ecx: sub string length (smi) + // edx: from index (smi) + // Deal with different string types: update the index if necessary + // and put the underlying string into edi. + Label underlying_unpacked, sliced_string, seq_or_external_string; + // If the string is not indirect, it can only be sequential or external. + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ test(ebx, Immediate(kIsIndirectStringMask)); + __ j(zero, &seq_or_external_string, Label::kNear); + + Factory* factory = masm->isolate()->factory(); + __ test(ebx, Immediate(kSlicedNotConsMask)); + __ j(not_zero, &sliced_string, Label::kNear); + // Cons string. Check whether it is flat, then fetch first part. + // Flat cons strings have an empty second part. + __ cmp(FieldOperand(eax, ConsString::kSecondOffset), + factory->empty_string()); + __ j(not_equal, &runtime); + __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset)); + // Update instance type. + __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset)); __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); - __ Set(ecx, Immediate(2)); + __ jmp(&underlying_unpacked, Label::kNear); + + __ bind(&sliced_string); + // Sliced string. Fetch parent and adjust start index by offset. + __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset)); + __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset)); + // Update instance type. + __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked, Label::kNear); + + __ bind(&seq_or_external_string); + // Sequential or external string. Just move string to the expected register. + __ mov(edi, eax); + + __ bind(&underlying_unpacked); if (FLAG_string_slices) { Label copy_routine; - // If coming from the make_two_character_string path, the string - // is too short to be sliced anyways. - STATIC_ASSERT(2 < SlicedString::kMinLength); - __ jmp(©_routine); - __ bind(&result_longer_than_two); - - // eax: string - // ebx: instance type - // ecx: sub string length - // edx: from index (smi) - Label allocate_slice, sliced_string, seq_string; - __ cmp(ecx, SlicedString::kMinLength); + // edi: underlying subject string + // ebx: instance type of underlying subject string + // edx: adjusted start index (smi) + // ecx: length (smi) + __ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength))); // Short slice. Copy instead of slicing. __ j(less, ©_routine); - STATIC_ASSERT(kSeqStringTag == 0); - __ test(ebx, Immediate(kStringRepresentationMask)); - __ j(zero, &seq_string, Label::kNear); - STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); - STATIC_ASSERT(kIsIndirectStringMask != 0); - __ test(ebx, Immediate(kIsIndirectStringMask)); - // External string. Jump to runtime. - __ j(zero, &runtime); - - Factory* factory = masm->isolate()->factory(); - __ test(ebx, Immediate(kSlicedNotConsMask)); - __ j(not_zero, &sliced_string, Label::kNear); - // Cons string. Check whether it is flat, then fetch first part. - __ cmp(FieldOperand(eax, ConsString::kSecondOffset), - factory->empty_string()); - __ j(not_equal, &runtime); - __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset)); - __ jmp(&allocate_slice, Label::kNear); - - __ bind(&sliced_string); - // Sliced string. Fetch parent and correct start index by offset. - __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset)); - __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset)); - __ jmp(&allocate_slice, Label::kNear); - - __ bind(&seq_string); - // Sequential string. Just move string to the right register. - __ mov(edi, eax); - - __ bind(&allocate_slice); - // edi: underlying subject string - // ebx: instance type of original subject string - // edx: offset - // ecx: length // Allocate new sliced string. At this point we do not reload the instance // type including the string encoding because we simply rely on the info // provided by the original string. It does not matter if the original @@ -5805,40 +6266,61 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime); __ bind(&set_slice_header); __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx); - __ SmiTag(ecx); __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx); __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi); __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset), Immediate(String::kEmptyHashField)); - __ jmp(&return_eax); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(3 * kPointerSize); __ bind(©_routine); - } else { - __ bind(&result_longer_than_two); } - // eax: string - // ebx: instance type - // ecx: result string length - // Check for flat ascii string - Label non_ascii_flat; - __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat); + // edi: underlying subject string + // ebx: instance type of underlying subject string + // edx: adjusted start index (smi) + // ecx: length (smi) + // The subject string can only be external or sequential string of either + // encoding at this point. + Label two_byte_sequential, runtime_drop_two, sequential_string; + STATIC_ASSERT(kExternalStringTag != 0); + STATIC_ASSERT(kSeqStringTag == 0); + __ test_b(ebx, kExternalStringTag); + __ j(zero, &sequential_string); + + // Handle external string. + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ test_b(ebx, kShortExternalStringMask); + __ j(not_zero, &runtime); + __ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Allocate the result. - __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime); + __ bind(&sequential_string); + // Stash away (adjusted) index and (underlying) string. + __ push(edx); + __ push(edi); + __ SmiUntag(ecx); + STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0); + __ test_b(ebx, kStringEncodingMask); + __ j(zero, &two_byte_sequential); + + // Sequential ASCII string. Allocate the result. + __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime_drop_two); // eax: result string // ecx: result string length __ mov(edx, esi); // esi used by following code. // Locate first character of result. __ mov(edi, eax); - __ add(Operand(edi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ add(edi, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); // Load string argument and locate character of sub string start. - __ mov(esi, Operand(esp, 3 * kPointerSize)); - __ add(Operand(esi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from + __ pop(esi); + __ pop(ebx); __ SmiUntag(ebx); - __ add(esi, Operand(ebx)); + __ lea(esi, FieldOperand(esi, ebx, times_1, SeqAsciiString::kHeaderSize)); // eax: result string // ecx: result length @@ -5847,38 +6329,28 @@ void SubStringStub::Generate(MacroAssembler* masm) { // esi: character of sub string start StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true); __ mov(esi, edx); // Restore esi. - Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->sub_string_native(), 1); __ ret(3 * kPointerSize); - __ bind(&non_ascii_flat); - // eax: string - // ebx: instance type & kStringRepresentationMask | kStringEncodingMask - // ecx: result string length - // Check for flat two byte string - __ cmp(ebx, kSeqStringTag | kTwoByteStringTag); - __ j(not_equal, &runtime); - - // Allocate the result. - __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime); + __ bind(&two_byte_sequential); + // Sequential two-byte string. Allocate the result. + __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two); // eax: result string // ecx: result string length __ mov(edx, esi); // esi used by following code. // Locate first character of result. __ mov(edi, eax); - __ add(Operand(edi), + __ add(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); // Load string argument and locate character of sub string start. - __ mov(esi, Operand(esp, 3 * kPointerSize)); - __ add(Operand(esi), - Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from + __ pop(esi); + __ pop(ebx); // As from is a smi it is 2 times the value which matches the size of a two // byte character. STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); - __ add(esi, Operand(ebx)); + __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize)); // eax: result string // ecx: result length @@ -5887,11 +6359,13 @@ void SubStringStub::Generate(MacroAssembler* masm) { // esi: character of sub string start StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false); __ mov(esi, edx); // Restore esi. - - __ bind(&return_eax); __ IncrementCounter(counters->sub_string_native(), 1); __ ret(3 * kPointerSize); + // Drop pushed values on the stack before tail call. + __ bind(&runtime_drop_two); + __ Drop(2); + // Just jump to runtime to create the sub string. __ bind(&runtime); __ TailCallRuntime(Runtime::kSubString, 3, 1); @@ -5918,7 +6392,7 @@ void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm, Label compare_chars; __ bind(&check_zero_length); STATIC_ASSERT(kSmiTag == 0); - __ test(length, Operand(length)); + __ test(length, length); __ j(not_zero, &compare_chars, Label::kNear); __ Set(eax, Immediate(Smi::FromInt(EQUAL))); __ ret(0); @@ -5953,14 +6427,14 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, __ j(less_equal, &left_shorter, Label::kNear); // Right string is shorter. Change scratch1 to be length of right string. - __ sub(scratch1, Operand(length_delta)); + __ sub(scratch1, length_delta); __ bind(&left_shorter); Register min_length = scratch1; // If either length is zero, just compare lengths. Label compare_lengths; - __ test(min_length, Operand(min_length)); + __ test(min_length, min_length); __ j(zero, &compare_lengths, Label::kNear); // Compare characters. @@ -5970,7 +6444,7 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, // Compare lengths - strings up to min-length are equal. __ bind(&compare_lengths); - __ test(length_delta, Operand(length_delta)); + __ test(length_delta, length_delta); __ j(not_zero, &result_not_equal, Label::kNear); // Result is EQUAL. @@ -6019,7 +6493,7 @@ void StringCompareStub::GenerateAsciiCharsCompareLoop( __ mov_b(scratch, Operand(left, index, times_1, 0)); __ cmpb(scratch, Operand(right, index, times_1, 0)); __ j(not_equal, chars_not_equal, chars_not_equal_near); - __ add(Operand(index), Immediate(1)); + __ add(index, Immediate(1)); __ j(not_zero, &loop); } @@ -6036,7 +6510,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) { __ mov(eax, Operand(esp, 1 * kPointerSize)); // right Label not_same; - __ cmp(edx, Operand(eax)); + __ cmp(edx, eax); __ j(not_equal, ¬_same, Label::kNear); STATIC_ASSERT(EQUAL == 0); STATIC_ASSERT(kSmiTag == 0); @@ -6046,13 +6520,13 @@ void StringCompareStub::Generate(MacroAssembler* masm) { __ bind(¬_same); - // Check that both objects are sequential ascii strings. + // Check that both objects are sequential ASCII strings. __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime); - // Compare flat ascii strings. + // Compare flat ASCII strings. // Drop arguments from the stack. __ pop(ecx); - __ add(Operand(esp), Immediate(2 * kPointerSize)); + __ add(esp, Immediate(2 * kPointerSize)); __ push(ecx); GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi); @@ -6066,16 +6540,16 @@ void StringCompareStub::Generate(MacroAssembler* masm) { void ICCompareStub::GenerateSmis(MacroAssembler* masm) { ASSERT(state_ == CompareIC::SMIS); Label miss; - __ mov(ecx, Operand(edx)); - __ or_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ or_(ecx, eax); __ JumpIfNotSmi(ecx, &miss, Label::kNear); if (GetCondition() == equal) { // For equality we do not care about the sign of the result. - __ sub(eax, Operand(edx)); + __ sub(eax, edx); } else { Label done; - __ sub(edx, Operand(eax)); + __ sub(edx, eax); __ j(no_overflow, &done, Label::kNear); // Correct sign of result in case of overflow. __ not_(edx); @@ -6095,8 +6569,8 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) { Label generic_stub; Label unordered; Label miss; - __ mov(ecx, Operand(edx)); - __ and_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ and_(ecx, eax); __ JumpIfSmi(ecx, &generic_stub, Label::kNear); __ CmpObjectType(eax, HEAP_NUMBER_TYPE, ecx); @@ -6124,9 +6598,9 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) { // Performing mov, because xor would destroy the flag register. __ mov(eax, 0); // equal __ mov(ecx, Immediate(Smi::FromInt(1))); - __ cmov(above, eax, Operand(ecx)); + __ cmov(above, eax, ecx); __ mov(ecx, Immediate(Smi::FromInt(-1))); - __ cmov(below, eax, Operand(ecx)); + __ cmov(below, eax, ecx); __ ret(0); __ bind(&unordered); @@ -6153,9 +6627,9 @@ void ICCompareStub::GenerateSymbols(MacroAssembler* masm) { // Check that both operands are heap objects. Label miss; - __ mov(tmp1, Operand(left)); + __ mov(tmp1, left); STATIC_ASSERT(kSmiTag == 0); - __ and_(tmp1, Operand(right)); + __ and_(tmp1, right); __ JumpIfSmi(tmp1, &miss, Label::kNear); // Check that both operands are symbols. @@ -6164,13 +6638,13 @@ void ICCompareStub::GenerateSymbols(MacroAssembler* masm) { __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); STATIC_ASSERT(kSymbolTag != 0); - __ and_(tmp1, Operand(tmp2)); + __ and_(tmp1, tmp2); __ test(tmp1, Immediate(kIsSymbolMask)); __ j(zero, &miss, Label::kNear); // Symbols are compared by identity. Label done; - __ cmp(left, Operand(right)); + __ cmp(left, right); // Make sure eax is non-zero. At this point input operands are // guaranteed to be non-zero. ASSERT(right.is(eax)); @@ -6199,9 +6673,9 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) { Register tmp3 = edi; // Check that both operands are heap objects. - __ mov(tmp1, Operand(left)); + __ mov(tmp1, left); STATIC_ASSERT(kSmiTag == 0); - __ and_(tmp1, Operand(right)); + __ and_(tmp1, right); __ JumpIfSmi(tmp1, &miss); // Check that both operands are strings. This leaves the instance @@ -6212,13 +6686,13 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) { __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); __ mov(tmp3, tmp1); STATIC_ASSERT(kNotStringTag != 0); - __ or_(tmp3, Operand(tmp2)); + __ or_(tmp3, tmp2); __ test(tmp3, Immediate(kIsNotStringMask)); __ j(not_zero, &miss); // Fast check for identical strings. Label not_same; - __ cmp(left, Operand(right)); + __ cmp(left, right); __ j(not_equal, ¬_same, Label::kNear); STATIC_ASSERT(EQUAL == 0); STATIC_ASSERT(kSmiTag == 0); @@ -6232,7 +6706,7 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) { // because we already know they are not identical. Label do_compare; STATIC_ASSERT(kSymbolTag != 0); - __ and_(tmp1, Operand(tmp2)); + __ and_(tmp1, tmp2); __ test(tmp1, Immediate(kIsSymbolMask)); __ j(zero, &do_compare, Label::kNear); // Make sure eax is non-zero. At this point input operands are @@ -6265,8 +6739,8 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) { void ICCompareStub::GenerateObjects(MacroAssembler* masm) { ASSERT(state_ == CompareIC::OBJECTS); Label miss; - __ mov(ecx, Operand(edx)); - __ and_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ and_(ecx, eax); __ JumpIfSmi(ecx, &miss, Label::kNear); __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx); @@ -6275,7 +6749,7 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { __ j(not_equal, &miss, Label::kNear); ASSERT(GetCondition() == equal); - __ sub(eax, Operand(edx)); + __ sub(eax, edx); __ ret(0); __ bind(&miss); @@ -6283,34 +6757,47 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { } -void ICCompareStub::GenerateMiss(MacroAssembler* masm) { - // Save the registers. - __ pop(ecx); - __ push(edx); - __ push(eax); - __ push(ecx); +void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) { + Label miss; + __ mov(ecx, edx); + __ and_(ecx, eax); + __ JumpIfSmi(ecx, &miss, Label::kNear); - // Call the runtime system in a fresh internal frame. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss), - masm->isolate()); - __ EnterInternalFrame(); - __ push(edx); - __ push(eax); - __ push(Immediate(Smi::FromInt(op_))); - __ CallExternalReference(miss, 3); - __ LeaveInternalFrame(); + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + __ cmp(ecx, known_map_); + __ j(not_equal, &miss, Label::kNear); + __ cmp(ebx, known_map_); + __ j(not_equal, &miss, Label::kNear); - // Compute the entry point of the rewritten stub. - __ lea(edi, FieldOperand(eax, Code::kHeaderSize)); + __ sub(eax, edx); + __ ret(0); + + __ bind(&miss); + GenerateMiss(masm); +} - // Restore registers. - __ pop(ecx); - __ pop(eax); - __ pop(edx); - __ push(ecx); + +void ICCompareStub::GenerateMiss(MacroAssembler* masm) { + { + // Call the runtime system in a fresh internal frame. + ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss), + masm->isolate()); + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(edx); // Preserve edx and eax. + __ push(eax); + __ push(edx); // And also use them as the arguments. + __ push(eax); + __ push(Immediate(Smi::FromInt(op_))); + __ CallExternalReference(miss, 3); + // Compute the entry point of the rewritten stub. + __ lea(edi, FieldOperand(eax, Code::kHeaderSize)); + __ pop(eax); + __ pop(edx); + } // Do a tail call to the rewritten stub. - __ jmp(Operand(edi)); + __ jmp(edi); } @@ -6319,13 +6806,12 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) { // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - String* name, - Register r0) { +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle<String> name, + Register r0) { ASSERT(name->IsSymbol()); // If names of slots in range from 1 to kProbes - 1 for the hash value are @@ -6339,8 +6825,8 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( // Capacity is smi 2^n. __ mov(index, FieldOperand(properties, kCapacityOffset)); __ dec(index); - __ and_(Operand(index), - Immediate(Smi::FromInt(name->Hash() + + __ and_(index, + Immediate(Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i)))); // Scale the index by multiplying by the entry size. @@ -6371,12 +6857,10 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( StringDictionaryLookupStub::NEGATIVE_LOOKUP); __ push(Immediate(Handle<Object>(name))); __ push(Immediate(name->Hash())); - MaybeObject* result = masm->TryCallStub(&stub); - if (result->IsFailure()) return result; - __ test(r0, Operand(r0)); + __ CallStub(&stub); + __ test(r0, r0); __ j(not_zero, miss); __ jmp(done); - return result; } @@ -6391,6 +6875,11 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, Register name, Register r0, Register r1) { + ASSERT(!elements.is(r0)); + ASSERT(!elements.is(r1)); + ASSERT(!name.is(r0)); + ASSERT(!name.is(r1)); + // Assert that name contains a string. if (FLAG_debug_code) __ AbortIfNotString(name); @@ -6406,9 +6895,9 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, __ mov(r0, FieldOperand(name, String::kHashFieldOffset)); __ shr(r0, String::kHashShift); if (i > 0) { - __ add(Operand(r0), Immediate(StringDictionary::GetProbeOffset(i))); + __ add(r0, Immediate(StringDictionary::GetProbeOffset(i))); } - __ and_(r0, Operand(r1)); + __ and_(r0, r1); // Scale the index by multiplying by the entry size. ASSERT(StringDictionary::kEntrySize == 3); @@ -6432,13 +6921,15 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, __ push(r0); __ CallStub(&stub); - __ test(r1, Operand(r1)); + __ test(r1, r1); __ j(zero, miss); __ jmp(done); } void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. // Stack frame on entry: // esp[0 * kPointerSize]: return address. // esp[1 * kPointerSize]: key's hash. @@ -6469,8 +6960,7 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { // Compute the masked index: (hash + i + i * i) & mask. __ mov(scratch, Operand(esp, 2 * kPointerSize)); if (i > 0) { - __ add(Operand(scratch), - Immediate(StringDictionary::GetProbeOffset(i))); + __ add(scratch, Immediate(StringDictionary::GetProbeOffset(i))); } __ and_(scratch, Operand(esp, 0)); @@ -6526,6 +7016,364 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { } +struct AheadOfTimeWriteBarrierStubList { + Register object, value, address; + RememberedSetAction action; +}; + + +struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { + // Used in RegExpExecStub. + { ebx, eax, edi, EMIT_REMEMBERED_SET }, + // Used in CompileArrayPushCall. + { ebx, ecx, edx, EMIT_REMEMBERED_SET }, + { ebx, edi, edx, OMIT_REMEMBERED_SET }, + // Used in CompileStoreGlobal and CallFunctionStub. + { ebx, ecx, edx, OMIT_REMEMBERED_SET }, + // Used in StoreStubCompiler::CompileStoreField and + // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField. + { edx, ecx, ebx, EMIT_REMEMBERED_SET }, + // GenerateStoreField calls the stub with two different permutations of + // registers. This is the second. + { ebx, ecx, edx, EMIT_REMEMBERED_SET }, + // StoreIC::GenerateNormal via GenerateDictionaryStore + { ebx, edi, edx, EMIT_REMEMBERED_SET }, + // KeyedStoreIC::GenerateGeneric. + { ebx, edx, ecx, EMIT_REMEMBERED_SET}, + // KeyedStoreStubCompiler::GenerateStoreFastElement. + { edi, edx, ecx, EMIT_REMEMBERED_SET}, + // ElementsTransitionGenerator::GenerateSmiOnlyToObject + // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // and ElementsTransitionGenerator::GenerateDoubleToObject + { edx, ebx, edi, EMIT_REMEMBERED_SET}, + // ElementsTransitionGenerator::GenerateDoubleToObject + { eax, edx, esi, EMIT_REMEMBERED_SET}, + { edx, eax, edi, EMIT_REMEMBERED_SET}, + // StoreArrayLiteralElementStub::Generate + { ebx, eax, ecx, EMIT_REMEMBERED_SET}, + // Null termination. + { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET} +}; + + +bool RecordWriteStub::IsPregenerated() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + if (object_.is(entry->object) && + value_.is(entry->value) && + address_.is(entry->address) && + remembered_set_action_ == entry->action && + save_fp_regs_mode_ == kDontSaveFPRegs) { + return true; + } + } + return false; +} + + +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() { + StoreBufferOverflowStub stub1(kDontSaveFPRegs); + stub1.GetCode()->set_is_pregenerated(true); + + CpuFeatures::TryForceFeatureScope scope(SSE2); + if (CpuFeatures::IsSupported(SSE2)) { + StoreBufferOverflowStub stub2(kSaveFPRegs); + stub2.GetCode()->set_is_pregenerated(true); + } +} + + +void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + RecordWriteStub stub(entry->object, + entry->value, + entry->address, + entry->action, + kDontSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + } +} + + +// Takes the input in 3 registers: address_ value_ and object_. A pointer to +// the value has just been written into the object, now this stub makes sure +// we keep the GC informed. The word in the object where the value has been +// written is in the address register. +void RecordWriteStub::Generate(MacroAssembler* masm) { + Label skip_to_incremental_noncompacting; + Label skip_to_incremental_compacting; + + // The first two instructions are generated with labels so as to get the + // offset fixed up correctly by the bind(Label*) call. We patch it back and + // forth between a compare instructions (a nop in this position) and the + // real branch when we start and stop incremental heap marking. + __ jmp(&skip_to_incremental_noncompacting, Label::kNear); + __ jmp(&skip_to_incremental_compacting, Label::kFar); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&skip_to_incremental_noncompacting); + GenerateIncremental(masm, INCREMENTAL); + + __ bind(&skip_to_incremental_compacting); + GenerateIncremental(masm, INCREMENTAL_COMPACTION); + + // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. + // Will be checked in IncrementalMarking::ActivateGeneratedStub. + masm->set_byte_at(0, kTwoByteNopInstruction); + masm->set_byte_at(2, kFiveByteNopInstruction); +} + + +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { + regs_.Save(masm); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + Label dont_need_remembered_set; + + __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); + __ JumpIfNotInNewSpace(regs_.scratch0(), // Value. + regs_.scratch0(), + &dont_need_remembered_set); + + __ CheckPageFlag(regs_.object(), + regs_.scratch0(), + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + not_zero, + &dont_need_remembered_set); + + // First notify the incremental marker if necessary, then update the + // remembered set. + CheckNeedsToInformIncrementalMarker( + masm, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, + mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + + __ bind(&dont_need_remembered_set); + } + + CheckNeedsToInformIncrementalMarker( + masm, + kReturnOnNoNeedToInformIncrementalMarker, + mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ ret(0); +} + + +void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) { + regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_); + int argument_count = 3; + __ PrepareCallCFunction(argument_count, regs_.scratch0()); + __ mov(Operand(esp, 0 * kPointerSize), regs_.object()); + if (mode == INCREMENTAL_COMPACTION) { + __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot. + } else { + ASSERT(mode == INCREMENTAL); + __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); + __ mov(Operand(esp, 1 * kPointerSize), regs_.scratch0()); // Value. + } + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address())); + + AllowExternalCallThatCantCauseGC scope(masm); + if (mode == INCREMENTAL_COMPACTION) { + __ CallCFunction( + ExternalReference::incremental_evacuation_record_write_function( + masm->isolate()), + argument_count); + } else { + ASSERT(mode == INCREMENTAL); + __ CallCFunction( + ExternalReference::incremental_marking_record_write_function( + masm->isolate()), + argument_count); + } + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_); +} + + +void RecordWriteStub::CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode) { + Label object_is_black, need_incremental, need_incremental_pop_object; + + // Let's look at the color of the object: If it is not black we don't have + // to inform the incremental marker. + __ JumpIfBlack(regs_.object(), + regs_.scratch0(), + regs_.scratch1(), + &object_is_black, + Label::kNear); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&object_is_black); + + // Get the value from the slot. + __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); + + if (mode == INCREMENTAL_COMPACTION) { + Label ensure_not_white; + + __ CheckPageFlag(regs_.scratch0(), // Contains value. + regs_.scratch1(), // Scratch. + MemoryChunk::kEvacuationCandidateMask, + zero, + &ensure_not_white, + Label::kNear); + + __ CheckPageFlag(regs_.object(), + regs_.scratch1(), // Scratch. + MemoryChunk::kSkipEvacuationSlotsRecordingMask, + not_zero, + &ensure_not_white, + Label::kNear); + + __ jmp(&need_incremental); + + __ bind(&ensure_not_white); + } + + // We need an extra register for this, so we push the object register + // temporarily. + __ push(regs_.object()); + __ EnsureNotWhite(regs_.scratch0(), // The value. + regs_.scratch1(), // Scratch. + regs_.object(), // Scratch. + &need_incremental_pop_object, + Label::kNear); + __ pop(regs_.object()); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&need_incremental_pop_object); + __ pop(regs_.object()); + + __ bind(&need_incremental); + + // Fall through when we need to inform the incremental marker. +} + + +void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : element value to store + // -- ebx : array literal + // -- edi : map of array literal + // -- ecx : element index as smi + // -- edx : array literal index in function + // -- esp[0] : return address + // ----------------------------------- + + Label element_done; + Label double_elements; + Label smi_element; + Label slow_elements; + Label slow_elements_from_double; + Label fast_elements; + + __ CheckFastElements(edi, &double_elements); + + // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + __ JumpIfSmi(eax, &smi_element); + __ CheckFastSmiOnlyElements(edi, &fast_elements, Label::kNear); + + // Store into the array literal requires a elements transition. Call into + // the runtime. + + __ bind(&slow_elements); + __ pop(edi); // Pop return address and remember to put back later for tail + // call. + __ push(ebx); + __ push(ecx); + __ push(eax); + __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset)); + __ push(edx); + __ push(edi); // Return return address so that tail call returns to right + // place. + __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); + + __ bind(&slow_elements_from_double); + __ pop(edx); + __ jmp(&slow_elements); + + // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + __ bind(&fast_elements); + __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); + __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size, + FixedArrayBase::kHeaderSize)); + __ mov(Operand(ecx, 0), eax); + // Update the write barrier for the array store. + __ RecordWrite(ebx, ecx, eax, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ ret(0); + + // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or + // FAST_ELEMENTS, and value is Smi. + __ bind(&smi_element); + __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); + __ mov(FieldOperand(ebx, ecx, times_half_pointer_size, + FixedArrayBase::kHeaderSize), eax); + __ ret(0); + + // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + __ bind(&double_elements); + + __ push(edx); + __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset)); + __ StoreNumberToDoubleElements(eax, + edx, + ecx, + edi, + xmm0, + &slow_elements_from_double, + false); + __ pop(edx); + __ ret(0); +} + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/ia32/code-stubs-ia32.h b/deps/v8/src/ia32/code-stubs-ia32.h index fa255da1fd..4d23c3a174 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.h +++ b/deps/v8/src/ia32/code-stubs-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -60,6 +60,25 @@ class TranscendentalCacheStub: public CodeStub { }; +class StoreBufferOverflowStub: public CodeStub { + public: + explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) + : save_doubles_(save_fp) { } + + void Generate(MacroAssembler* masm); + + virtual bool IsPregenerated() { return true; } + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + private: + SaveFPRegsMode save_doubles_; + + Major MajorKey() { return StoreBufferOverflow; } + int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } +}; + + class UnaryOpStub: public CodeStub { public: UnaryOpStub(Token::Value op, @@ -128,7 +147,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_unary_op_type(operand_type_); } }; @@ -215,7 +234,7 @@ class BinaryOpStub: public CodeStub { return BinaryOpIC::ToState(operands_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } @@ -402,13 +421,12 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - String* name, - Register r0); + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle<String> name, + Register r0); static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss, @@ -418,6 +436,8 @@ class StringDictionaryLookupStub: public CodeStub { Register r0, Register r1); + virtual bool SometimesSetsUpAFrame() { return false; } + private: static const int kInlinedProbes = 4; static const int kTotalProbes = 20; @@ -430,7 +450,7 @@ class StringDictionaryLookupStub: public CodeStub { StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; - Major MajorKey() { return StringDictionaryNegativeLookup; } + Major MajorKey() { return StringDictionaryLookup; } int MinorKey() { return DictionaryBits::encode(dictionary_.code()) | @@ -451,6 +471,265 @@ class StringDictionaryLookupStub: public CodeStub { }; +class RecordWriteStub: public CodeStub { + public: + RecordWriteStub(Register object, + Register value, + Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode) + : object_(object), + value_(value), + address_(address), + remembered_set_action_(remembered_set_action), + save_fp_regs_mode_(fp_mode), + regs_(object, // An input reg. + address, // An input reg. + value) { // One scratch reg. + } + + enum Mode { + STORE_BUFFER_ONLY, + INCREMENTAL, + INCREMENTAL_COMPACTION + }; + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. + static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. + + static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. + static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. + + static Mode GetMode(Code* stub) { + byte first_instruction = stub->instruction_start()[0]; + byte second_instruction = stub->instruction_start()[2]; + + if (first_instruction == kTwoByteJumpInstruction) { + return INCREMENTAL; + } + + ASSERT(first_instruction == kTwoByteNopInstruction); + + if (second_instruction == kFiveByteJumpInstruction) { + return INCREMENTAL_COMPACTION; + } + + ASSERT(second_instruction == kFiveByteNopInstruction); + + return STORE_BUFFER_ONLY; + } + + static void Patch(Code* stub, Mode mode) { + switch (mode) { + case STORE_BUFFER_ONLY: + ASSERT(GetMode(stub) == INCREMENTAL || + GetMode(stub) == INCREMENTAL_COMPACTION); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteNopInstruction; + break; + case INCREMENTAL: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteJumpInstruction; + break; + case INCREMENTAL_COMPACTION: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteJumpInstruction; + break; + } + ASSERT(GetMode(stub) == mode); + CPU::FlushICache(stub->instruction_start(), 7); + } + + private: + // This is a helper class for freeing up 3 scratch registers, where the third + // is always ecx (needed for shift operations). The input is two registers + // that must be preserved and one scratch register provided by the caller. + class RegisterAllocation { + public: + RegisterAllocation(Register object, + Register address, + Register scratch0) + : object_orig_(object), + address_orig_(address), + scratch0_orig_(scratch0), + object_(object), + address_(address), + scratch0_(scratch0) { + ASSERT(!AreAliased(scratch0, object, address, no_reg)); + scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); + if (scratch0.is(ecx)) { + scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); + } + if (object.is(ecx)) { + object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); + } + if (address.is(ecx)) { + address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); + } + ASSERT(!AreAliased(scratch0_, object_, address_, ecx)); + } + + void Save(MacroAssembler* masm) { + ASSERT(!address_orig_.is(object_)); + ASSERT(object_.is(object_orig_) || address_.is(address_orig_)); + ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); + ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); + ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); + // We don't have to save scratch0_orig_ because it was given to us as + // a scratch register. But if we had to switch to a different reg then + // we should save the new scratch0_. + if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); + if (!ecx.is(scratch0_orig_) && + !ecx.is(object_orig_) && + !ecx.is(address_orig_)) { + masm->push(ecx); + } + masm->push(scratch1_); + if (!address_.is(address_orig_)) { + masm->push(address_); + masm->mov(address_, address_orig_); + } + if (!object_.is(object_orig_)) { + masm->push(object_); + masm->mov(object_, object_orig_); + } + } + + void Restore(MacroAssembler* masm) { + // These will have been preserved the entire time, so we just need to move + // them back. Only in one case is the orig_ reg different from the plain + // one, since only one of them can alias with ecx. + if (!object_.is(object_orig_)) { + masm->mov(object_orig_, object_); + masm->pop(object_); + } + if (!address_.is(address_orig_)) { + masm->mov(address_orig_, address_); + masm->pop(address_); + } + masm->pop(scratch1_); + if (!ecx.is(scratch0_orig_) && + !ecx.is(object_orig_) && + !ecx.is(address_orig_)) { + masm->pop(ecx); + } + if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); + } + + // If we have to call into C then we need to save and restore all caller- + // saved registers that were not already preserved. The caller saved + // registers are eax, ecx and edx. The three scratch registers (incl. ecx) + // will be restored by other means so we don't bother pushing them here. + void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { + if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax); + if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx); + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + masm->sub(esp, + Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1))); + // Save all XMM registers except XMM0. + for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) { + XMMRegister reg = XMMRegister::from_code(i); + masm->movdbl(Operand(esp, (i - 1) * kDoubleSize), reg); + } + } + } + + inline void RestoreCallerSaveRegisters(MacroAssembler*masm, + SaveFPRegsMode mode) { + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + // Restore all XMM registers except XMM0. + for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) { + XMMRegister reg = XMMRegister::from_code(i); + masm->movdbl(reg, Operand(esp, (i - 1) * kDoubleSize)); + } + masm->add(esp, + Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1))); + } + if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx); + if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax); + } + + inline Register object() { return object_; } + inline Register address() { return address_; } + inline Register scratch0() { return scratch0_; } + inline Register scratch1() { return scratch1_; } + + private: + Register object_orig_; + Register address_orig_; + Register scratch0_orig_; + Register object_; + Register address_; + Register scratch0_; + Register scratch1_; + // Third scratch register is always ecx. + + Register GetRegThatIsNotEcxOr(Register r1, + Register r2, + Register r3) { + for (int i = 0; i < Register::kNumAllocatableRegisters; i++) { + Register candidate = Register::FromAllocationIndex(i); + if (candidate.is(ecx)) continue; + if (candidate.is(r1)) continue; + if (candidate.is(r2)) continue; + if (candidate.is(r3)) continue; + return candidate; + } + UNREACHABLE(); + return no_reg; + } + friend class RecordWriteStub; + }; + + enum OnNoNeedToInformIncrementalMarker { + kReturnOnNoNeedToInformIncrementalMarker, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker + } +; + void Generate(MacroAssembler* masm); + void GenerateIncremental(MacroAssembler* masm, Mode mode); + void CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode); + void InformIncrementalMarker(MacroAssembler* masm, Mode mode); + + Major MajorKey() { return RecordWrite; } + + int MinorKey() { + return ObjectBits::encode(object_.code()) | + ValueBits::encode(value_.code()) | + AddressBits::encode(address_.code()) | + RememberedSetActionBits::encode(remembered_set_action_) | + SaveFPRegsModeBits::encode(save_fp_regs_mode_); + } + + void Activate(Code* code) { + code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); + } + + class ObjectBits: public BitField<int, 0, 3> {}; + class ValueBits: public BitField<int, 3, 3> {}; + class AddressBits: public BitField<int, 6, 3> {}; + class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {}; + class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 10, 1> {}; + + Register object_; + Register value_; + Register address_; + RememberedSetAction remembered_set_action_; + SaveFPRegsMode save_fp_regs_mode_; + RegisterAllocation regs_; +}; + + } } // namespace v8::internal #endif // V8_IA32_CODE_STUBS_IA32_H_ diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 3a657bd541..e5ca02c473 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -30,6 +30,7 @@ #if defined(V8_TARGET_ARCH_IA32) #include "codegen.h" +#include "macro-assembler.h" namespace v8 { namespace internal { @@ -39,12 +40,16 @@ namespace internal { // Platform-specific RuntimeCallHelper functions. void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterInternalFrame(); + masm->EnterFrame(StackFrame::INTERNAL); + ASSERT(!masm->has_frame()); + masm->set_has_frame(true); } void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveInternalFrame(); + masm->LeaveFrame(StackFrame::INTERNAL); + ASSERT(masm->has_frame()); + masm->set_has_frame(false); } @@ -108,14 +113,14 @@ OS::MemCopyFunction CreateMemCopyFunction() { __ mov(edx, dst); __ and_(edx, 0xF); __ neg(edx); - __ add(Operand(edx), Immediate(16)); - __ add(dst, Operand(edx)); - __ add(src, Operand(edx)); - __ sub(Operand(count), edx); + __ add(edx, Immediate(16)); + __ add(dst, edx); + __ add(src, edx); + __ sub(count, edx); // edi is now aligned. Check if esi is also aligned. Label unaligned_source; - __ test(Operand(src), Immediate(0x0F)); + __ test(src, Immediate(0x0F)); __ j(not_zero, &unaligned_source); { // Copy loop for aligned source and destination. @@ -130,11 +135,11 @@ OS::MemCopyFunction CreateMemCopyFunction() { __ prefetch(Operand(src, 0x20), 1); __ movdqa(xmm0, Operand(src, 0x00)); __ movdqa(xmm1, Operand(src, 0x10)); - __ add(Operand(src), Immediate(0x20)); + __ add(src, Immediate(0x20)); __ movdqa(Operand(dst, 0x00), xmm0); __ movdqa(Operand(dst, 0x10), xmm1); - __ add(Operand(dst), Immediate(0x20)); + __ add(dst, Immediate(0x20)); __ dec(loop_count); __ j(not_zero, &loop); @@ -142,12 +147,12 @@ OS::MemCopyFunction CreateMemCopyFunction() { // At most 31 bytes to copy. Label move_less_16; - __ test(Operand(count), Immediate(0x10)); + __ test(count, Immediate(0x10)); __ j(zero, &move_less_16); __ movdqa(xmm0, Operand(src, 0)); - __ add(Operand(src), Immediate(0x10)); + __ add(src, Immediate(0x10)); __ movdqa(Operand(dst, 0), xmm0); - __ add(Operand(dst), Immediate(0x10)); + __ add(dst, Immediate(0x10)); __ bind(&move_less_16); // At most 15 bytes to copy. Copy 16 bytes at end of string. @@ -176,11 +181,11 @@ OS::MemCopyFunction CreateMemCopyFunction() { __ prefetch(Operand(src, 0x20), 1); __ movdqu(xmm0, Operand(src, 0x00)); __ movdqu(xmm1, Operand(src, 0x10)); - __ add(Operand(src), Immediate(0x20)); + __ add(src, Immediate(0x20)); __ movdqa(Operand(dst, 0x00), xmm0); __ movdqa(Operand(dst, 0x10), xmm1); - __ add(Operand(dst), Immediate(0x20)); + __ add(dst, Immediate(0x20)); __ dec(loop_count); __ j(not_zero, &loop); @@ -188,12 +193,12 @@ OS::MemCopyFunction CreateMemCopyFunction() { // At most 31 bytes to copy. Label move_less_16; - __ test(Operand(count), Immediate(0x10)); + __ test(count, Immediate(0x10)); __ j(zero, &move_less_16); __ movdqu(xmm0, Operand(src, 0)); - __ add(Operand(src), Immediate(0x10)); + __ add(src, Immediate(0x10)); __ movdqa(Operand(dst, 0), xmm0); - __ add(Operand(dst), Immediate(0x10)); + __ add(dst, Immediate(0x10)); __ bind(&move_less_16); // At most 15 bytes to copy. Copy 16 bytes at end of string. @@ -228,10 +233,10 @@ OS::MemCopyFunction CreateMemCopyFunction() { __ mov(edx, dst); __ and_(edx, 0x03); __ neg(edx); - __ add(Operand(edx), Immediate(4)); // edx = 4 - (dst & 3) - __ add(dst, Operand(edx)); - __ add(src, Operand(edx)); - __ sub(Operand(count), edx); + __ add(edx, Immediate(4)); // edx = 4 - (dst & 3) + __ add(dst, edx); + __ add(src, edx); + __ sub(count, edx); // edi is now aligned, ecx holds number of remaning bytes to copy. __ mov(edx, count); @@ -261,6 +266,370 @@ OS::MemCopyFunction CreateMemCopyFunction() { #undef __ +// ------------------------------------------------------------------------- +// Code generators + +#define __ ACCESS_MASM(masm) + +void ElementsTransitionGenerator::GenerateSmiOnlyToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : value + // -- ebx : target map + // -- ecx : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + // Set transitioned map. + __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); + __ RecordWriteField(edx, + HeapObject::kMapOffset, + ebx, + edi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); +} + + +void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- eax : value + // -- ebx : target map + // -- ecx : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + Label loop, entry, convert_hole, gc_required; + __ push(eax); + __ push(ebx); + + __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); + __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset)); + + // Allocate new FixedDoubleArray. + // edx: receiver + // edi: length of source FixedArray (smi-tagged) + __ lea(esi, Operand(edi, times_4, FixedDoubleArray::kHeaderSize)); + __ AllocateInNewSpace(esi, eax, ebx, no_reg, &gc_required, TAG_OBJECT); + + // eax: destination FixedDoubleArray + // edi: number of elements + // edx: receiver + __ mov(FieldOperand(eax, HeapObject::kMapOffset), + Immediate(masm->isolate()->factory()->fixed_double_array_map())); + __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi); + __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset)); + // Replace receiver's backing store with newly created FixedDoubleArray. + __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax); + __ mov(ebx, eax); + __ RecordWriteField(edx, + JSObject::kElementsOffset, + ebx, + edi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + + __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset)); + + // Prepare for conversion loop. + ExternalReference canonical_the_hole_nan_reference = + ExternalReference::address_of_the_hole_nan(); + XMMRegister the_hole_nan = xmm1; + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope use_sse2(SSE2); + __ movdbl(the_hole_nan, + Operand::StaticVariable(canonical_the_hole_nan_reference)); + } + __ jmp(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + // Restore registers before jumping into runtime. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + __ pop(ebx); + __ pop(eax); + __ jmp(fail); + + // Convert and copy elements + // esi: source FixedArray + __ bind(&loop); + __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize)); + // ebx: current element from source + // edi: index of current element + __ JumpIfNotSmi(ebx, &convert_hole); + + // Normal smi, convert it to double and store. + __ SmiUntag(ebx); + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ cvtsi2sd(xmm0, ebx); + __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize), + xmm0); + } else { + __ push(ebx); + __ fild_s(Operand(esp, 0)); + __ pop(ebx); + __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize)); + } + __ jmp(&entry); + + // Found hole, store hole_nan_as_double instead. + __ bind(&convert_hole); + + if (FLAG_debug_code) { + __ cmp(ebx, masm->isolate()->factory()->the_hole_value()); + __ Assert(equal, "object found in smi-only array"); + } + + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope use_sse2(SSE2); + __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize), + the_hole_nan); + } else { + __ fld_d(Operand::StaticVariable(canonical_the_hole_nan_reference)); + __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize)); + } + + __ bind(&entry); + __ sub(edi, Immediate(Smi::FromInt(1))); + __ j(not_sign, &loop); + + __ pop(ebx); + __ pop(eax); + // eax: value + // ebx: target map + // Set transitioned map. + __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); + __ RecordWriteField(edx, + HeapObject::kMapOffset, + ebx, + edi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Restore esi. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); +} + + +void ElementsTransitionGenerator::GenerateDoubleToObject( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- eax : value + // -- ebx : target map + // -- ecx : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + Label loop, entry, convert_hole, gc_required; + __ push(eax); + __ push(edx); + __ push(ebx); + + __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); + __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset)); + + // Allocate new FixedArray. + // ebx: length of source FixedDoubleArray (smi-tagged) + __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize)); + __ AllocateInNewSpace(edi, eax, esi, no_reg, &gc_required, TAG_OBJECT); + + // eax: destination FixedArray + // ebx: number of elements + __ mov(FieldOperand(eax, HeapObject::kMapOffset), + Immediate(masm->isolate()->factory()->fixed_array_map())); + __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx); + __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); + + __ jmp(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + __ pop(ebx); + __ pop(edx); + __ pop(eax); + __ jmp(fail); + + // Box doubles into heap numbers. + // edi: source FixedDoubleArray + // eax: destination FixedArray + __ bind(&loop); + // ebx: index of current element (smi-tagged) + uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); + __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32)); + __ j(equal, &convert_hole); + + // Non-hole double, copy value into a heap number. + __ AllocateHeapNumber(edx, esi, no_reg, &gc_required); + // edx: new heap number + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ movdbl(xmm0, + FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize)); + __ movdbl(FieldOperand(edx, HeapNumber::kValueOffset), xmm0); + } else { + __ mov(esi, FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize)); + __ mov(FieldOperand(edx, HeapNumber::kValueOffset), esi); + __ mov(esi, FieldOperand(edi, ebx, times_4, offset)); + __ mov(FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize), esi); + } + __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx); + __ mov(esi, ebx); + __ RecordWriteArray(eax, + edx, + esi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ jmp(&entry, Label::kNear); + + // Replace the-hole NaN with the-hole pointer. + __ bind(&convert_hole); + __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), + masm->isolate()->factory()->the_hole_value()); + + __ bind(&entry); + __ sub(ebx, Immediate(Smi::FromInt(1))); + __ j(not_sign, &loop); + + __ pop(ebx); + __ pop(edx); + // ebx: target map + // edx: receiver + // Set transitioned map. + __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); + __ RecordWriteField(edx, + HeapObject::kMapOffset, + ebx, + edi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created and filled FixedArray. + __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax); + __ RecordWriteField(edx, + JSObject::kElementsOffset, + eax, + edi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + + // Restore registers. + __ pop(eax); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); +} + + +void StringCharLoadGenerator::Generate(MacroAssembler* masm, + Factory* factory, + Register string, + Register index, + Register result, + Label* call_runtime) { + // Fetch the instance type of the receiver into result register. + __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for indirect strings. + Label check_sequential; + __ test(result, Immediate(kIsIndirectStringMask)); + __ j(zero, &check_sequential, Label::kNear); + + // Dispatch on the indirect string shape: slice or cons. + Label cons_string; + __ test(result, Immediate(kSlicedNotConsMask)); + __ j(zero, &cons_string, Label::kNear); + + // Handle slices. + Label indirect_string_loaded; + __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset)); + __ SmiUntag(result); + __ add(index, result); + __ mov(string, FieldOperand(string, SlicedString::kParentOffset)); + __ jmp(&indirect_string_loaded, Label::kNear); + + // Handle cons strings. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ bind(&cons_string); + __ cmp(FieldOperand(string, ConsString::kSecondOffset), + Immediate(factory->empty_string())); + __ j(not_equal, call_runtime); + __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); + + __ bind(&indirect_string_loaded); + __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // Distinguish sequential and external strings. Only these two string + // representations can reach here (slices and flat cons strings have been + // reduced to the underlying sequential or external string). + Label seq_string; + __ bind(&check_sequential); + STATIC_ASSERT(kSeqStringTag == 0); + __ test(result, Immediate(kStringRepresentationMask)); + __ j(zero, &seq_string, Label::kNear); + + // Handle external strings. + Label ascii_external, done; + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ test(result, Immediate(kIsIndirectStringMask)); + __ Assert(zero, "external string expected, but not found"); + } + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ test_b(result, kShortExternalStringMask); + __ j(not_zero, call_runtime); + // Check encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ test_b(result, kStringEncodingMask); + __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset)); + __ j(not_equal, &ascii_external, Label::kNear); + // Two-byte string. + __ movzx_w(result, Operand(result, index, times_2, 0)); + __ jmp(&done, Label::kNear); + __ bind(&ascii_external); + // Ascii string. + __ movzx_b(result, Operand(result, index, times_1, 0)); + __ jmp(&done, Label::kNear); + + // Dispatch on the encoding: ASCII or two-byte. + Label ascii; + __ bind(&seq_string); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ test(result, Immediate(kStringEncodingMask)); + __ j(not_zero, &ascii, Label::kNear); + + // Two-byte string. + // Load the two-byte character code into the result register. + __ movzx_w(result, FieldOperand(string, + index, + times_2, + SeqTwoByteString::kHeaderSize)); + __ jmp(&done, Label::kNear); + + // Ascii string. + // Load the byte into the result register. + __ bind(&ascii); + __ movzx_b(result, FieldOperand(string, + index, + times_1, + SeqAsciiString::kHeaderSize)); + __ bind(&done); +} + +#undef __ + } } // namespace v8::internal #endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index c85fa83e9e..f4ab0b50f6 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -72,6 +72,22 @@ class CodeGenerator { }; +class StringCharLoadGenerator : public AllStatic { + public: + // Generates the code for handling different string types and loading the + // indexed character into |result|. We expect |index| as untagged input and + // |result| as untagged output. + static void Generate(MacroAssembler* masm, + Factory* factory, + Register string, + Register index, + Register result, + Label* call_runtime); + + private: + DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); +}; + } } // namespace v8::internal #endif // V8_IA32_CODEGEN_IA32_H_ diff --git a/deps/v8/src/ia32/cpu-ia32.cc b/deps/v8/src/ia32/cpu-ia32.cc index 57e66df9e3..9eabb2a969 100644 --- a/deps/v8/src/ia32/cpu-ia32.cc +++ b/deps/v8/src/ia32/cpu-ia32.cc @@ -41,7 +41,7 @@ namespace v8 { namespace internal { -void CPU::Setup() { +void CPU::SetUp() { CpuFeatures::Probe(); } diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc index 2389948866..d13fa759ca 100644 --- a/deps/v8/src/ia32/debug-ia32.cc +++ b/deps/v8/src/ia32/debug-ia32.cc @@ -100,63 +100,64 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, RegList non_object_regs, bool convert_call_to_jmp) { // Enter an internal frame. - __ EnterInternalFrame(); - - // Store the registers containing live values on the expression stack to - // make sure that these are correctly updated during GC. Non object values - // are stored as a smi causing it to be untouched by GC. - ASSERT((object_regs & ~kJSCallerSaved) == 0); - ASSERT((non_object_regs & ~kJSCallerSaved) == 0); - ASSERT((object_regs & non_object_regs) == 0); - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if ((object_regs & (1 << r)) != 0) { - __ push(reg); - } - if ((non_object_regs & (1 << r)) != 0) { - if (FLAG_debug_code) { - __ test(reg, Immediate(0xc0000000)); - __ Assert(zero, "Unable to encode value as smi"); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Store the registers containing live values on the expression stack to + // make sure that these are correctly updated during GC. Non object values + // are stored as a smi causing it to be untouched by GC. + ASSERT((object_regs & ~kJSCallerSaved) == 0); + ASSERT((non_object_regs & ~kJSCallerSaved) == 0); + ASSERT((object_regs & non_object_regs) == 0); + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if ((object_regs & (1 << r)) != 0) { + __ push(reg); + } + if ((non_object_regs & (1 << r)) != 0) { + if (FLAG_debug_code) { + __ test(reg, Immediate(0xc0000000)); + __ Assert(zero, "Unable to encode value as smi"); + } + __ SmiTag(reg); + __ push(reg); } - __ SmiTag(reg); - __ push(reg); } - } #ifdef DEBUG - __ RecordComment("// Calling from debug break to runtime - come in - over"); + __ RecordComment("// Calling from debug break to runtime - come in - over"); #endif - __ Set(eax, Immediate(0)); // No arguments. - __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate()))); - - CEntryStub ceb(1); - __ CallStub(&ceb); - - // Restore the register values containing object pointers from the expression - // stack. - for (int i = kNumJSCallerSaved; --i >= 0;) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if (FLAG_debug_code) { - __ Set(reg, Immediate(kDebugZapValue)); - } - if ((object_regs & (1 << r)) != 0) { - __ pop(reg); - } - if ((non_object_regs & (1 << r)) != 0) { - __ pop(reg); - __ SmiUntag(reg); + __ Set(eax, Immediate(0)); // No arguments. + __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate()))); + + CEntryStub ceb(1); + __ CallStub(&ceb); + + // Restore the register values containing object pointers from the + // expression stack. + for (int i = kNumJSCallerSaved; --i >= 0;) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if (FLAG_debug_code) { + __ Set(reg, Immediate(kDebugZapValue)); + } + if ((object_regs & (1 << r)) != 0) { + __ pop(reg); + } + if ((non_object_regs & (1 << r)) != 0) { + __ pop(reg); + __ SmiUntag(reg); + } } - } - // Get rid of the internal frame. - __ LeaveInternalFrame(); + // Get rid of the internal frame. + } // If this call did not replace a call but patched other code then there will // be an unwanted return address left on the stack. Here we get rid of that. if (convert_call_to_jmp) { - __ add(Operand(esp), Immediate(kPointerSize)); + __ add(esp, Immediate(kPointerSize)); } // Now that the break point has been handled, resume normal execution by @@ -221,8 +222,36 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { +void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // Register state just before return from JS function (from codegen-ia32.cc). + // ----------- S t a t e ------------- + // -- eax: return value + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true); +} + + +void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-ia32.cc). + // ----------- S t a t e ------------- + // -- edi: function + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false); +} + + +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-ia32.cc). + // ----------- S t a t e ------------- + // -- ebx: cache cell for call target + // -- edi: function + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), 0, false); +} + + +void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { + // Register state for CallConstructStub (from code-stubs-ia32.cc). // eax is the actual number of arguments not encoded as a smi see comment // above IC call. // ----------- S t a t e ------------- @@ -234,21 +263,17 @@ void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { - // Register state just before return from JS function (from codegen-ia32.cc). - // ----------- S t a t e ------------- - // -- eax: return value - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true); -} - - -void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { - // Register state for stub CallFunction (from CallFunctionStub in ic-ia32.cc). +void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallConstructStub (from code-stubs-ia32.cc). + // eax is the actual number of arguments not encoded as a smi see comment + // above IC call. // ----------- S t a t e ------------- - // No registers used on entry. + // -- eax: number of arguments (not smi) + // -- ebx: cache cell for call target + // -- edi: constructor function // ----------------------------------- - Generate_DebugBreakCallHelper(masm, 0, 0, false); + // The number of arguments in eax is not smi encoded. + Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), eax.bit(), false); } @@ -257,9 +282,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) { Label check_codesize; __ bind(&check_codesize); __ RecordDebugBreakSlot(); - for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) { - __ nop(); - } + __ Nop(Assembler::kDebugBreakSlotLength); ASSERT_EQ(Assembler::kDebugBreakSlotLength, masm->SizeOfCodeGeneratedSince(&check_codesize)); } @@ -298,7 +321,7 @@ void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); // Re-run JSFunction, edi is function, esi is context. - __ jmp(Operand(edx)); + __ jmp(edx); } const bool Debug::kFrameDropperSupported = true; diff --git a/deps/v8/src/ia32/deoptimizer-ia32.cc b/deps/v8/src/ia32/deoptimizer-ia32.cc index 080ad644dc..14f26757e9 100644 --- a/deps/v8/src/ia32/deoptimizer-ia32.cc +++ b/deps/v8/src/ia32/deoptimizer-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -99,7 +99,7 @@ void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) { new_reloc->GetDataStartAddress() + padding, 0); intptr_t comment_string = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString); - RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string); + RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL); for (int i = 0; i < additional_comments; ++i) { #ifdef DEBUG byte* pos_before = reloc_info_writer.pos(); @@ -156,7 +156,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { // We use RUNTIME_ENTRY for deoptimization bailouts. RelocInfo rinfo(call_address + 1, // 1 after the call opcode. RelocInfo::RUNTIME_ENTRY, - reinterpret_cast<intptr_t>(deopt_entry)); + reinterpret_cast<intptr_t>(deopt_entry), + NULL); reloc_info_writer.Write(&rinfo); ASSERT_GE(reloc_info_writer.pos(), reloc_info->address() + ByteArray::kHeaderSize); @@ -188,6 +189,11 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { node->set_next(data->deoptimizing_code_list_); data->deoptimizing_code_list_ = node; + // We might be in the middle of incremental marking with compaction. + // Tell collector to treat this code object in a special way and + // ignore all slots that might have been recorded on it. + isolate->heap()->mark_compact_collector()->InvalidateCode(code); + // Set the code for the function to non-optimized version. function->ReplaceCode(function->shared()->code()); @@ -199,7 +205,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { } -void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, +void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { Address call_target_address = pc_after - kIntSize; @@ -224,14 +231,18 @@ void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, ASSERT(*(call_target_address - 3) == 0x73 && // jae *(call_target_address - 2) == 0x07 && // offset *(call_target_address - 1) == 0xe8); // call - *(call_target_address - 3) = 0x90; // nop - *(call_target_address - 2) = 0x90; // nop + *(call_target_address - 3) = 0x66; // 2 byte nop part 1 + *(call_target_address - 2) = 0x90; // 2 byte nop part 2 Assembler::set_target_address_at(call_target_address, replacement_code->entry()); + + unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, call_target_address, replacement_code); } -void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, +void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { Address call_target_address = pc_after - kIntSize; @@ -239,13 +250,16 @@ void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, Assembler::target_address_at(call_target_address)); // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to // restore the conditional branch. - ASSERT(*(call_target_address - 3) == 0x90 && // nop - *(call_target_address - 2) == 0x90 && // nop + ASSERT(*(call_target_address - 3) == 0x66 && // 2 byte nop part 1 + *(call_target_address - 2) == 0x90 && // 2 byte nop part 2 *(call_target_address - 1) == 0xe8); // call *(call_target_address - 3) = 0x73; // jae *(call_target_address - 2) = 0x07; // offset Assembler::set_target_address_at(call_target_address, check_code->entry()); + + check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, call_target_address, check_code); } @@ -285,12 +299,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() { ASSERT(Translation::BEGIN == opcode); USE(opcode); int count = iterator.Next(); + iterator.Next(); // Drop JS frames count. ASSERT(count == 1); USE(count); opcode = static_cast<Translation::Opcode>(iterator.Next()); USE(opcode); - ASSERT(Translation::FRAME == opcode); + ASSERT(Translation::JS_FRAME == opcode); unsigned node_id = iterator.Next(); USE(node_id); ASSERT(node_id == ast_id); @@ -326,9 +341,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_ = new FrameDescription*[1]; output_[0] = new(output_frame_size) FrameDescription( output_frame_size, function_); -#ifdef DEBUG - output_[0]->SetKind(Code::OPTIMIZED_FUNCTION); -#endif + output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); // Clear the incoming parameters in the optimized frame to avoid // confusing the garbage collector. @@ -392,8 +405,15 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_[0] = input_; output_[0]->SetPc(reinterpret_cast<uint32_t>(from_)); } else { - // Setup the frame pointer and the context pointer. - output_[0]->SetRegister(ebp.code(), input_->GetRegister(ebp.code())); + // Set up the frame pointer and the context pointer. + // All OSR stack frames are dynamically aligned to an 8-byte boundary. + int frame_pointer = input_->GetRegister(ebp.code()); + if ((frame_pointer & 0x4) == 0) { + // Return address at FP + 4 should be aligned, so FP mod 8 should be 4. + frame_pointer -= kPointerSize; + has_alignment_padding_ = 1; + } + output_[0]->SetRegister(ebp.code(), frame_pointer); output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code())); unsigned pc_offset = data->OsrPcOffset()->value(); @@ -416,13 +436,112 @@ void Deoptimizer::DoComputeOsrOutputFrame() { } -void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, - int frame_index) { - // Read the ast node id, function, and frame height for this output frame. - Translation::Opcode opcode = - static_cast<Translation::Opcode>(iterator->Next()); - USE(opcode); - ASSERT(Translation::FRAME == opcode); +void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; + unsigned input_frame_size = input_->GetFrameSize(); + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, function); + output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR); + + // Arguments adaptor can not be topmost or bottommost. + ASSERT(frame_index > 0 && frame_index < output_count_ - 1); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address of the frame is computed from the previous + // frame's top and this frame's size. + uint32_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + unsigned input_offset = input_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + input_offset -= (parameter_count * kPointerSize); + + // Read caller's PC from the previous frame. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t callers_pc = output_[frame_index - 1]->GetPc(); + output_frame->SetFrameSlot(output_offset, callers_pc); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n", + top_address + output_offset, output_offset, callers_pc); + } + + // Read caller's FP from the previous frame, and set this frame's FP. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t value = output_[frame_index - 1]->GetFp(); + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + output_frame->SetFp(fp_value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n", + fp_value, output_offset, value); + } + + // A marker value is used in place of the context. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t context = reinterpret_cast<intptr_t>( + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); + output_frame->SetFrameSlot(output_offset, context); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n", + top_address + output_offset, output_offset, context); + } + + // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(function); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* adaptor_trampoline = + builtins->builtin(Builtins::kArgumentsAdaptorTrampoline); + uint32_t pc = reinterpret_cast<uint32_t>( + adaptor_trampoline->instruction_start() + + isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value()); + output_frame->SetPc(pc); +} + + +void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, + int frame_index) { int node_id = iterator->Next(); JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); unsigned height = iterator->Next(); @@ -442,9 +561,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, // Allocate and store the output frame description. FrameDescription* output_frame = new(output_frame_size) FrameDescription(output_frame_size, function); -#ifdef DEBUG - output_frame->SetKind(Code::FUNCTION); -#endif + output_frame->SetFrameType(StackFrame::JAVA_SCRIPT); bool is_bottommost = (0 == frame_index); bool is_topmost = (output_count_ - 1 == frame_index); @@ -458,9 +575,11 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, // top address and the current frame's size. uint32_t top_address; if (is_bottommost) { - // 2 = context and function in the frame. - top_address = - input_->GetRegister(ebp.code()) - (2 * kPointerSize) - height_in_bytes; + // If the optimized frame had alignment padding, adjust the frame pointer + // to point to the new position of the old frame pointer after padding + // is removed. Subtract 2 * kPointerSize for the context and function slots. + top_address = input_->GetRegister(ebp.code()) - (2 * kPointerSize) - + height_in_bytes + has_alignment_padding_ * kPointerSize; } else { top_address = output_[frame_index - 1]->GetTop() - output_frame_size; } @@ -511,7 +630,9 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, } output_frame->SetFrameSlot(output_offset, value); intptr_t fp_value = top_address + output_offset; - ASSERT(!is_bottommost || input_->GetRegister(ebp.code()) == fp_value); + ASSERT(!is_bottommost || + input_->GetRegister(ebp.code()) + has_alignment_padding_ * kPointerSize + == fp_value); output_frame->SetFp(fp_value); if (is_topmost) output_frame->SetRegister(ebp.code(), fp_value); if (FLAG_trace_deopt) { @@ -616,7 +737,7 @@ void Deoptimizer::EntryGenerator::Generate() { const int kDoubleRegsSize = kDoubleSize * XMMRegister::kNumAllocatableRegisters; - __ sub(Operand(esp), Immediate(kDoubleRegsSize)); + __ sub(esp, Immediate(kDoubleRegsSize)); for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) { XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i); int offset = i * kDoubleSize; @@ -640,7 +761,7 @@ void Deoptimizer::EntryGenerator::Generate() { __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize)); __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize)); } - __ sub(edx, Operand(ebp)); + __ sub(edx, ebp); __ neg(edx); // Allocate a new deoptimizer object. @@ -653,7 +774,10 @@ void Deoptimizer::EntryGenerator::Generate() { __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta. __ mov(Operand(esp, 5 * kPointerSize), Immediate(ExternalReference::isolate_address())); - __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + } // Preserve deoptimizer object in register eax and get the input // frame descriptor pointer. @@ -676,15 +800,15 @@ void Deoptimizer::EntryGenerator::Generate() { // Remove the bailout id and the double registers from the stack. if (type() == EAGER) { - __ add(Operand(esp), Immediate(kDoubleRegsSize + kPointerSize)); + __ add(esp, Immediate(kDoubleRegsSize + kPointerSize)); } else { - __ add(Operand(esp), Immediate(kDoubleRegsSize + 2 * kPointerSize)); + __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize)); } // Compute a pointer to the unwinding limit in register ecx; that is // the first stack slot not part of the input frame. __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); - __ add(ecx, Operand(esp)); + __ add(ecx, esp); // Unwind the stack down to - but not including - the unwinding // limit and copy the contents of the activation frame to the input @@ -693,18 +817,43 @@ void Deoptimizer::EntryGenerator::Generate() { Label pop_loop; __ bind(&pop_loop); __ pop(Operand(edx, 0)); - __ add(Operand(edx), Immediate(sizeof(uint32_t))); - __ cmp(ecx, Operand(esp)); + __ add(edx, Immediate(sizeof(uint32_t))); + __ cmp(ecx, esp); __ j(not_equal, &pop_loop); + // If frame was dynamically aligned, pop padding. + Label sentinel, sentinel_done; + __ pop(ecx); + __ cmp(ecx, Operand(eax, Deoptimizer::frame_alignment_marker_offset())); + __ j(equal, &sentinel); + __ push(ecx); + __ jmp(&sentinel_done); + __ bind(&sentinel); + __ mov(Operand(eax, Deoptimizer::has_alignment_padding_offset()), + Immediate(1)); + __ bind(&sentinel_done); // Compute the output frame in the deoptimizer. __ push(eax); __ PrepareCallCFunction(1, ebx); __ mov(Operand(esp, 0 * kPointerSize), eax); - __ CallCFunction( - ExternalReference::compute_output_frames_function(isolate), 1); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction( + ExternalReference::compute_output_frames_function(isolate), 1); + } __ pop(eax); + if (type() == OSR) { + // If alignment padding is added, push the sentinel. + Label no_osr_padding; + __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()), + Immediate(0)); + __ j(equal, &no_osr_padding, Label::kNear); + __ push(Operand(eax, Deoptimizer::frame_alignment_marker_offset())); + __ bind(&no_osr_padding); + } + + // Replace the current frame with the output frames. Label outer_push_loop, inner_push_loop; // Outer loop state: eax = current FrameDescription**, edx = one past the @@ -717,12 +866,12 @@ void Deoptimizer::EntryGenerator::Generate() { __ mov(ebx, Operand(eax, 0)); __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); __ bind(&inner_push_loop); - __ sub(Operand(ecx), Immediate(sizeof(uint32_t))); + __ sub(ecx, Immediate(sizeof(uint32_t))); __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset())); - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(not_zero, &inner_push_loop); - __ add(Operand(eax), Immediate(kPointerSize)); - __ cmp(eax, Operand(edx)); + __ add(eax, Immediate(kPointerSize)); + __ cmp(eax, edx); __ j(below, &outer_push_loop); // In case of OSR, we have to restore the XMM registers. diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc index a936277b2f..b5ddcca192 100644 --- a/deps/v8/src/ia32/disasm-ia32.cc +++ b/deps/v8/src/ia32/disasm-ia32.cc @@ -55,6 +55,7 @@ struct ByteMnemonic { static const ByteMnemonic two_operands_instr[] = { + {0x01, "add", OPER_REG_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER}, {0x09, "or", OPER_REG_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER}, @@ -117,6 +118,19 @@ static const ByteMnemonic short_immediate_instr[] = { }; +// Generally we don't want to generate these because they are subject to partial +// register stalls. They are included for completeness and because the cmp +// variant is used by the RecordWrite stub. Because it does not update the +// register it is not subject to partial register stalls. +static ByteMnemonic byte_immediate_instr[] = { + {0x0c, "or", UNSET_OP_ORDER}, + {0x24, "and", UNSET_OP_ORDER}, + {0x34, "xor", UNSET_OP_ORDER}, + {0x3c, "cmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + static const char* const jump_conditional_mnem[] = { /*0*/ "jo", "jno", "jc", "jnc", /*4*/ "jz", "jnz", "jna", "ja", @@ -149,7 +163,8 @@ enum InstructionType { REGISTER_INSTR, MOVE_REG_INSTR, CALL_JUMP_INSTR, - SHORT_IMMEDIATE_INSTR + SHORT_IMMEDIATE_INSTR, + BYTE_IMMEDIATE_INSTR }; @@ -164,6 +179,10 @@ class InstructionTable { public: InstructionTable(); const InstructionDesc& Get(byte x) const { return instructions_[x]; } + static InstructionTable* get_instance() { + static InstructionTable table; + return &table; + } private: InstructionDesc instructions_[256]; @@ -198,6 +217,7 @@ void InstructionTable::Init() { CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); CopyTable(call_jump_instr, CALL_JUMP_INSTR); CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); + CopyTable(byte_immediate_instr, BYTE_IMMEDIATE_INSTR); AddJumpConditionalShort(); SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc"); SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec"); @@ -243,15 +263,13 @@ void InstructionTable::AddJumpConditionalShort() { } -static InstructionTable instruction_table; - - // The IA32 disassembler implementation. class DisassemblerIA32 { public: DisassemblerIA32(const NameConverter& converter, bool abort_on_unimplemented = true) : converter_(converter), + instruction_table_(InstructionTable::get_instance()), tmp_buffer_pos_(0), abort_on_unimplemented_(abort_on_unimplemented) { tmp_buffer_[0] = '\0'; @@ -265,11 +283,11 @@ class DisassemblerIA32 { private: const NameConverter& converter_; + InstructionTable* instruction_table_; v8::internal::EmbeddedVector<char, 128> tmp_buffer_; unsigned int tmp_buffer_pos_; bool abort_on_unimplemented_; - enum { eax = 0, ecx = 1, @@ -745,10 +763,13 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode, case 0xEB: mnem = "fldpi"; break; case 0xED: mnem = "fldln2"; break; case 0xEE: mnem = "fldz"; break; + case 0xF0: mnem = "f2xm1"; break; case 0xF1: mnem = "fyl2x"; break; case 0xF5: mnem = "fprem1"; break; case 0xF7: mnem = "fincstp"; break; case 0xF8: mnem = "fprem"; break; + case 0xFC: mnem = "frndint"; break; + case 0xFD: mnem = "fscale"; break; case 0xFE: mnem = "fsin"; break; case 0xFF: mnem = "fcos"; break; default: UnimplementedInstruction(); @@ -770,6 +791,8 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode, has_register = true; } else if (modrm_byte == 0xE2) { mnem = "fclex"; + } else if (modrm_byte == 0xE3) { + mnem = "fninit"; } else { UnimplementedInstruction(); } @@ -868,7 +891,7 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, } bool processed = true; // Will be set to false if the current instruction // is not in 'instructions' table. - const InstructionDesc& idesc = instruction_table.Get(*data); + const InstructionDesc& idesc = instruction_table_->Get(*data); switch (idesc.type) { case ZERO_OPERANDS_INSTR: AppendToBuffer(idesc.mnem); @@ -912,6 +935,12 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, break; } + case BYTE_IMMEDIATE_INSTR: { + AppendToBuffer("%s al, 0x%x", idesc.mnem, data[1]); + data += 2; + break; + } + case NO_INSTR: processed = false; break; @@ -963,7 +992,7 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, break; case 0x0F: - { byte f0byte = *(data+1); + { byte f0byte = data[1]; const char* f0mnem = F0Mnem(f0byte); if (f0byte == 0x18) { int mod, regop, rm; @@ -971,6 +1000,25 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, const char* suffix[] = {"nta", "1", "2", "3"}; AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]); data += PrintRightOperand(data); + } else if (f0byte == 0x1F && data[2] == 0) { + AppendToBuffer("nop"); // 3 byte nop. + data += 3; + } else if (f0byte == 0x1F && data[2] == 0x40 && data[3] == 0) { + AppendToBuffer("nop"); // 4 byte nop. + data += 4; + } else if (f0byte == 0x1F && data[2] == 0x44 && data[3] == 0 && + data[4] == 0) { + AppendToBuffer("nop"); // 5 byte nop. + data += 5; + } else if (f0byte == 0x1F && data[2] == 0x80 && data[3] == 0 && + data[4] == 0 && data[5] == 0 && data[6] == 0) { + AppendToBuffer("nop"); // 7 byte nop. + data += 7; + } else if (f0byte == 0x1F && data[2] == 0x84 && data[3] == 0 && + data[4] == 0 && data[5] == 0 && data[6] == 0 && + data[7] == 0) { + AppendToBuffer("nop"); // 8 byte nop. + data += 8; } else if (f0byte == 0xA2 || f0byte == 0x31) { AppendToBuffer("%s", f0mnem); data += 2; @@ -1106,8 +1154,12 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, break; case 0x66: // prefix - data++; - if (*data == 0x8B) { + while (*data == 0x66) data++; + if (*data == 0xf && data[1] == 0x1f) { + AppendToBuffer("nop"); // 0x66 prefix + } else if (*data == 0x90) { + AppendToBuffer("nop"); // 0x66 prefix + } else if (*data == 0x8B) { data++; data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data); } else if (*data == 0x89) { @@ -1161,6 +1213,16 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, NameOfXMMRegister(rm), static_cast<int>(imm8)); data += 2; + } else if (*data == 0x17) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast<int8_t>(data[1]); + AppendToBuffer("extractps %s,%s,%d", + NameOfCPURegister(regop), + NameOfXMMRegister(rm), + static_cast<int>(imm8)); + data += 2; } else if (*data == 0x22) { data++; int mod, regop, rm; @@ -1234,6 +1296,9 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, NameOfXMMRegister(rm), static_cast<int>(imm8)); data += 2; + } else if (*data == 0x90) { + data++; + AppendToBuffer("nop"); // 2 byte nop. } else if (*data == 0xF3) { data++; int mod, regop, rm; @@ -1346,11 +1411,6 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, data += 2; break; - case 0x2C: - AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1)); - data += 2; - break; - case 0xA9: AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1)); data += 5; diff --git a/deps/v8/src/ia32/frames-ia32.h b/deps/v8/src/ia32/frames-ia32.h index 2f1b2a96d3..9e51857bdb 100644 --- a/deps/v8/src/ia32/frames-ia32.h +++ b/deps/v8/src/ia32/frames-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,37 +34,37 @@ namespace internal { // Register lists // Note that the bit values must match those used in actual instruction encoding -static const int kNumRegs = 8; +const int kNumRegs = 8; // Caller-saved registers -static const RegList kJSCallerSaved = +const RegList kJSCallerSaved = 1 << 0 | // eax 1 << 1 | // ecx 1 << 2 | // edx 1 << 3 | // ebx - used as a caller-saved register in JavaScript code 1 << 7; // edi - callee function -static const int kNumJSCallerSaved = 5; +const int kNumJSCallerSaved = 5; typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; // Number of registers for which space is reserved in safepoints. -static const int kNumSafepointRegisters = 8; +const int kNumSafepointRegisters = 8; // ---------------------------------------------------- class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kContextOffset = 1 * kPointerSize; - static const int kFPOffset = 2 * kPointerSize; - static const int kStateOffset = 3 * kPointerSize; - static const int kPCOffset = 4 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kCodeOffset = 1 * kPointerSize; + static const int kStateOffset = 2 * kPointerSize; + static const int kContextOffset = 3 * kPointerSize; + static const int kFPOffset = 4 * kPointerSize; - static const int kSize = kPCOffset + kPointerSize; + static const int kSize = kFPOffset + kPointerSize; }; @@ -95,9 +95,11 @@ class ExitFrameConstants : public AllStatic { class StandardFrameConstants : public AllStatic { public: + // Fixed part of the frame consists of return address, caller fp, + // context and function. // StandardFrame::IterateExpressions assumes that kContextOffset is the last // object pointer. - static const int kFixedFrameSize = 4; // Currently unused. + static const int kFixedFrameSize = 4 * kPointerSize; static const int kExpressionsOffset = -3 * kPointerSize; static const int kMarkerOffset = -2 * kPointerSize; static const int kContextOffset = -1 * kPointerSize; @@ -123,6 +125,8 @@ class JavaScriptFrameConstants : public AllStatic { class ArgumentsAdaptorFrameConstants : public AllStatic { public: static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset; + static const int kFrameSize = + StandardFrameConstants::kFixedFrameSize + kPointerSize; }; diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index ca6ce6e31a..7bb4cffad0 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -44,11 +44,6 @@ namespace internal { #define __ ACCESS_MASM(masm_) -static unsigned GetPropertyId(Property* property) { - return property->id(); -} - - class JumpPatchSite BASE_EMBEDDED { public: explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { @@ -111,7 +106,7 @@ class JumpPatchSite BASE_EMBEDDED { // formal parameter count expected by the function. // // The live registers are: -// o edi: the JS function object being called (ie, ourselves) +// o edi: the JS function object being called (i.e. ourselves) // o esi: our context // o ebp: our caller's frame pointer // o esp: stack pointer (pointing to return address) @@ -122,6 +117,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ASSERT(info_ == NULL); info_ = info; scope_ = info->scope(); + handler_table_ = + isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); @@ -132,21 +129,50 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif + // We can optionally optimize based on counters rather than statistical + // sampling. + if (info->ShouldSelfOptimize()) { + if (FLAG_trace_opt) { + PrintF("[adding self-optimization header to %s]\n", + *info->function()->debug_name()->ToCString()); + } + MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell( + Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt)); + JSGlobalPropertyCell* cell; + if (maybe_cell->To(&cell)) { + __ sub(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)), + Immediate(Smi::FromInt(1))); + Handle<Code> compile_stub( + isolate()->builtins()->builtin(Builtins::kLazyRecompile)); + STATIC_ASSERT(kSmiTag == 0); + __ j(zero, compile_stub); + } + } + // Strict mode functions and builtins need to replace the receiver // with undefined when called as functions (without an explicit // receiver object). ecx is zero for method calls and non-zero for // function calls. - if (info->is_strict_mode() || info->is_native()) { + if (!info->is_classic_mode() || info->is_native()) { Label ok; - __ test(ecx, Operand(ecx)); + __ test(ecx, ecx); __ j(zero, &ok, Label::kNear); // +1 for return address. int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize; + __ mov(ecx, Operand(esp, receiver_offset)); + __ JumpIfSmi(ecx, &ok); + __ CmpObjectType(ecx, JS_GLOBAL_PROXY_TYPE, ecx); + __ j(not_equal, &ok, Label::kNear); __ mov(Operand(esp, receiver_offset), Immediate(isolate()->factory()->undefined_value())); __ bind(&ok); } + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + __ push(ebp); // Caller's frame pointer. __ mov(ebp, esp); __ push(esi); // Callee's context. @@ -164,11 +190,6 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } } - set_stack_height(2 + scope()->num_stack_slots()); - if (FLAG_verify_stack_height) { - verify_stack_height(); - } - bool function_in_register = true; // Possibly allocate a local context. @@ -200,11 +221,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Store it in the context. int context_offset = Context::SlotOffset(var->index()); __ mov(Operand(esi, context_offset), eax); - // Update the write barrier. This clobbers all involved - // registers, so we have use a third register to avoid - // clobbering esi. - __ mov(ecx, esi); - __ RecordWrite(ecx, context_offset, eax, ebx); + // Update the write barrier. This clobbers eax and ebx. + __ RecordWriteContextSlot(esi, + context_offset, + eax, + ebx, + kDontSaveFPRegs); } } } @@ -225,12 +247,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { Operand(ebp, StandardFrameConstants::kCallerSPOffset + offset)); __ push(edx); __ SafePush(Immediate(Smi::FromInt(num_parameters))); - // Arguments to ArgumentsAccessStub and/or New...: + // Arguments to ArgumentsAccessStub: // function, receiver address, parameter count. // The stub will rewrite receiver and parameter count if the previous // stack frame was an arguments adapter frame. ArgumentsAccessStub::Type type; - if (is_strict_mode()) { + if (!is_classic_mode()) { type = ArgumentsAccessStub::NEW_STRICT; } else if (function()->has_duplicate_parameters()) { type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; @@ -259,8 +281,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - int ignored = 0; - EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); + VariableProxy* proxy = scope()->function(); + ASSERT(proxy->var()->mode() == CONST || + proxy->var()->mode() == CONST_HARMONY); + ASSERT(proxy->var()->location() != Variable::UNALLOCATED); + EmitDeclaration(proxy, proxy->var()->mode(), NULL); } VisitDeclarations(scope()->declarations()); } @@ -363,15 +388,6 @@ void FullCodeGenerator::EmitReturnSequence() { } -void FullCodeGenerator::verify_stack_height() { - ASSERT(FLAG_verify_stack_height); - __ sub(Operand(ebp), Immediate(kPointerSize * stack_height())); - __ cmp(ebp, Operand(esp)); - __ Assert(equal, "Full codegen stack height not as expected."); - __ add(Operand(ebp), Immediate(kPointerSize * stack_height())); -} - - void FullCodeGenerator::EffectContext::Plug(Variable* var) const { ASSERT(var->IsStackAllocated() || var->IsContextSlot()); } @@ -388,14 +404,13 @@ void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { MemOperand operand = codegen()->VarOperand(var, result_register()); // Memory operands can be pushed directly. __ push(operand); - codegen()->increment_stack_height(); } void FullCodeGenerator::TestContext::Plug(Variable* var) const { // For simplicity we always test the accumulator register. codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -442,12 +457,11 @@ void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { } else { __ push(Immediate(lit)); } - codegen()->increment_stack_height(); } void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -480,7 +494,6 @@ void FullCodeGenerator::EffectContext::DropAndPlug(int count, Register reg) const { ASSERT(count > 0); __ Drop(count); - codegen()->decrement_stack_height(count); } @@ -490,7 +503,6 @@ void FullCodeGenerator::AccumulatorValueContext::DropAndPlug( ASSERT(count > 0); __ Drop(count); __ Move(result_register(), reg); - codegen()->decrement_stack_height(count); } @@ -499,7 +511,6 @@ void FullCodeGenerator::StackValueContext::DropAndPlug(int count, ASSERT(count > 0); if (count > 1) __ Drop(count - 1); __ mov(Operand(esp, 0), reg); - codegen()->decrement_stack_height(count - 1); } @@ -509,9 +520,8 @@ void FullCodeGenerator::TestContext::DropAndPlug(int count, // For simplicity we always test the accumulator register. __ Drop(count); __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); - codegen()->decrement_stack_height(count); } @@ -545,7 +555,6 @@ void FullCodeGenerator::StackValueContext::Plug( __ bind(materialize_false); __ push(Immediate(isolate()->factory()->false_value())); __ bind(&done); - codegen()->increment_stack_height(); } @@ -573,12 +582,11 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const { ? isolate()->factory()->true_value() : isolate()->factory()->false_value(); __ push(Immediate(value)); - codegen()->increment_stack_height(); } void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -597,7 +605,7 @@ void FullCodeGenerator::DoTest(Expression* condition, ToBooleanStub stub(result_register()); __ push(result_register()); __ CallStub(&stub, condition->test_id()); - __ test(result_register(), Operand(result_register())); + __ test(result_register(), result_register()); // The stub returns nonzero for true. Split(not_zero, if_true, if_false, fall_through); } @@ -661,16 +669,17 @@ void FullCodeGenerator::SetVar(Variable* var, ASSERT(!scratch1.is(src)); MemOperand location = VarOperand(var, scratch0); __ mov(location, src); + // Emit the write barrier code if the location is in the heap. if (var->IsContextSlot()) { int offset = Context::SlotOffset(var->index()); ASSERT(!scratch0.is(esi) && !src.is(esi) && !scratch1.is(esi)); - __ RecordWrite(scratch0, offset, src, scratch1); + __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); } } -void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, +void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false) { @@ -681,13 +690,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, Label skip; if (should_normalize) __ jmp(&skip, Label::kNear); - - ForwardBailoutStack* current = forward_bailout_stack_; - while (current != NULL) { - PrepareForBailout(current->expr(), state); - current = current->parent(); - } - + PrepareForBailout(expr, TOS_REG); if (should_normalize) { __ cmp(eax, isolate()->factory()->true_value()); Split(equal, if_true, if_false, NULL); @@ -697,16 +700,17 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function, - int* global_count) { + VariableMode mode, + FunctionLiteral* function) { // If it was not possible to allocate the variable at compile time, we // need to "declare" it at runtime to make sure it actually exists in the // local context. Variable* variable = proxy->var(); + bool binding_needs_init = (function == NULL) && + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (variable->location()) { case Variable::UNALLOCATED: - ++(*global_count); + ++global_count_; break; case Variable::PARAMETER: @@ -715,7 +719,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ mov(StackOperand(variable), result_register()); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ mov(StackOperand(variable), Immediate(isolate()->factory()->the_hole_value())); @@ -738,11 +742,16 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ mov(ContextOperand(esi, variable->index()), result_register()); - int offset = Context::SlotOffset(variable->index()); - __ mov(ebx, esi); - __ RecordWrite(ebx, offset, result_register(), ecx); + // We know that we have written a function, which is not a smi. + __ RecordWriteContextSlot(esi, + Context::SlotOffset(variable->index()), + result_register(), + ecx, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ mov(ContextOperand(esi, variable->index()), Immediate(isolate()->factory()->the_hole_value())); @@ -755,37 +764,32 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); __ push(esi); __ push(Immediate(variable->name())); - // Declaration nodes are always introduced in one of three modes. - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; + // Declaration nodes are always introduced in one of four modes. + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); + PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY) + ? READ_ONLY : NONE; __ push(Immediate(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as // 'undefined') because we may have a (legal) redeclaration and we // must not destroy the current value. - increment_stack_height(3); if (function != NULL) { VisitForStackValue(function); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { __ push(Immediate(isolate()->factory()->the_hole_value())); - increment_stack_height(); } else { __ push(Immediate(Smi::FromInt(0))); // Indicates no initial value. - increment_stack_height(); } __ CallRuntime(Runtime::kDeclareContextSlot, 4); - decrement_stack_height(4); break; } } } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } - - void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. __ push(esi); // The context is the first argument. @@ -801,7 +805,6 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { Breakable nested_statement(this, stmt); SetStatementPosition(stmt); - int switch_clause_stack_height = stack_height(); // Keep the switch value on the stack until a case matches. VisitForStackValue(stmt->tag()); PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); @@ -835,10 +838,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { if (inline_smi_code) { Label slow_case; __ mov(ecx, edx); - __ or_(ecx, Operand(eax)); + __ or_(ecx, eax); patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); - __ cmp(edx, Operand(eax)); + __ cmp(edx, eax); __ j(not_equal, &next_test); __ Drop(1); // Switch value is no longer needed. __ jmp(clause->body_target()); @@ -850,7 +853,7 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT); __ call(ic, RelocInfo::CODE_TARGET, clause->CompareId()); patch_site.EmitPatchInfo(); - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(not_equal, &next_test); __ Drop(1); // Switch value is no longer needed. __ jmp(clause->body_target()); @@ -866,7 +869,6 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { __ jmp(default_clause->body_target()); } - set_stack_height(switch_clause_stack_height); // Compile all the case bodies. for (int i = 0; i < clauses->length(); i++) { Comment cmnt(masm_, "[ Case body"); @@ -908,13 +910,18 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ bind(&done_convert); __ push(eax); - increment_stack_height(); + + // Check for proxies. + Label call_runtime; + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CmpObjectType(eax, LAST_JS_PROXY_TYPE, ecx); + __ j(below_equal, &call_runtime); // Check cache validity in generated code. This is a fast case for // the JSObject::IsSimpleEnum cache validity checks. If we cannot // guarantee cache validity, call the runtime system to check cache // validity or get the property names in a fixed array. - Label next, call_runtime; + Label next; __ mov(ecx, eax); __ bind(&next); @@ -939,7 +946,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // For all objects but the receiver, check that the cache is empty. Label check_prototype; - __ cmp(ecx, Operand(eax)); + __ cmp(ecx, eax); __ j(equal, &check_prototype, Label::kNear); __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset)); __ cmp(edx, isolate()->factory()->empty_fixed_array()); @@ -976,7 +983,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset)); __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - // Setup the four remaining stack slots. + // Set up the four remaining stack slots. __ push(eax); // Map. __ push(edx); // Enumeration cache. __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset)); @@ -985,15 +992,21 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ jmp(&loop); // We got a fixed array in register eax. Iterate through that. + Label non_proxy; __ bind(&fixed_array); - __ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check. - __ push(eax); + __ mov(ebx, Immediate(Smi::FromInt(1))); // Smi indicates slow check + __ mov(ecx, Operand(esp, 0 * kPointerSize)); // Get enumerated object + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CmpObjectType(ecx, LAST_JS_PROXY_TYPE, ecx); + __ j(above, &non_proxy); + __ mov(ebx, Immediate(Smi::FromInt(0))); // Zero indicates proxy + __ bind(&non_proxy); + __ push(ebx); // Smi + __ push(eax); // Array __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset)); __ push(eax); // Fixed array length (as smi). __ push(Immediate(Smi::FromInt(0))); // Initial index. - // 1 ~ The object has already been pushed. - increment_stack_height(ForIn::kElementCount - 1); // Generate code for doing the condition check. __ bind(&loop); __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index. @@ -1004,26 +1017,32 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ mov(ebx, Operand(esp, 2 * kPointerSize)); __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize)); - // Get the expected map from the stack or a zero map in the + // Get the expected map from the stack or a smi in the // permanent slow case into register edx. __ mov(edx, Operand(esp, 3 * kPointerSize)); // Check if the expected map still matches that of the enumerable. - // If not, we have to filter the key. + // If not, we may have to filter the key. Label update_each; __ mov(ecx, Operand(esp, 4 * kPointerSize)); __ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset)); __ j(equal, &update_each, Label::kNear); + // For proxies, no filtering is done. + // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. + ASSERT(Smi::FromInt(0) == 0); + __ test(edx, edx); + __ j(zero, &update_each); + // Convert the entry to a string or null if it isn't a property // anymore. If the property has been removed while iterating, we // just skip it. __ push(ecx); // Enumerable. __ push(ebx); // Current entry. __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(equal, loop_statement.continue_label()); - __ mov(ebx, Operand(eax)); + __ mov(ebx, eax); // Update the 'each' property or variable from the possibly filtered // entry in register ebx. @@ -1047,9 +1066,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // Remove the pointers stored on the stack. __ bind(loop_statement.break_label()); - __ add(Operand(esp), Immediate(5 * kPointerSize)); + __ add(esp, Immediate(5 * kPointerSize)); - decrement_stack_height(ForIn::kElementCount); // Exit and decrement the loop depth. __ bind(&exit); decrement_loop_depth(); @@ -1069,7 +1087,7 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, !pretenure && scope()->is_function_scope() && info->num_literals() == 0) { - FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(info->language_mode()); __ push(Immediate(info)); __ CallStub(&stub); } else { @@ -1099,7 +1117,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); @@ -1113,7 +1131,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, // If no outer scope calls eval, we do not need to check more // context extensions. If we have reached an eval scope, we check // all extensions from this point. - if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; + if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; s = s->outer_scope(); } @@ -1158,7 +1176,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); @@ -1189,16 +1207,23 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (var->mode() == Variable::DYNAMIC_GLOBAL) { + if (var->mode() == DYNAMIC_GLOBAL) { EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + } else if (var->mode() == DYNAMIC_LOCAL) { Variable* local = var->local_if_not_shadowed(); __ mov(eax, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == Variable::CONST) { + if (local->mode() == CONST || + local->mode() == CONST_HARMONY || + local->mode() == LET) { __ cmp(eax, isolate()->factory()->the_hole_value()); __ j(not_equal, done); - __ mov(eax, isolate()->factory()->undefined_value()); + if (local->mode() == CONST) { + __ mov(eax, isolate()->factory()->undefined_value()); + } else { // LET || CONST_HARMONY + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } } __ jmp(done); } @@ -1231,23 +1256,63 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { Comment cmnt(masm_, var->IsContextSlot() ? "Context variable" : "Stack variable"); - if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { - context()->Plug(var); - } else { - // Let and const need a read barrier. - Label done; - GetVar(eax, var); - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, &done, Label::kNear); - if (var->mode() == Variable::LET) { - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - } else { // Variable::CONST - __ mov(eax, isolate()->factory()->undefined_value()); + if (var->binding_needs_init()) { + // var->scope() may be NULL when the proxy is located in eval code and + // refers to a potential outside binding. Currently those bindings are + // always looked up dynamically, i.e. in that case + // var->location() == LOOKUP. + // always holds. + ASSERT(var->scope() != NULL); + + // Check if the binding really needs an initialization check. The check + // can be skipped in the following situation: we have a LET or CONST + // binding in harmony mode, both the Variable and the VariableProxy have + // the same declaration scope (i.e. they are both in global code, in the + // same function or in the same eval code) and the VariableProxy is in + // the source physically located after the initializer of the variable. + // + // We cannot skip any initialization checks for CONST in non-harmony + // mode because const variables may be declared but never initialized: + // if (false) { const x; }; var y = x; + // + // The condition on the declaration scopes is a conservative check for + // nested functions that access a binding and are called before the + // binding is initialized: + // function() { f(); let x = 1; function f() { x = 2; } } + // + bool skip_init_check; + if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { + skip_init_check = false; + } else { + // Check that we always have valid source position. + ASSERT(var->initializer_position() != RelocInfo::kNoPosition); + ASSERT(proxy->position() != RelocInfo::kNoPosition); + skip_init_check = var->mode() != CONST && + var->initializer_position() < proxy->position(); + } + + if (!skip_init_check) { + // Let and const need a read barrier. + Label done; + GetVar(eax, var); + __ cmp(eax, isolate()->factory()->the_hole_value()); + __ j(not_equal, &done, Label::kNear); + if (var->mode() == LET || var->mode() == CONST_HARMONY) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } else { + // Uninitalized const bindings outside of harmony mode are unholed. + ASSERT(var->mode() == CONST); + __ mov(eax, isolate()->factory()->undefined_value()); + } + __ bind(&done); + context()->Plug(eax); + break; } - __ bind(&done); - context()->Plug(eax); } + context()->Plug(var); break; } @@ -1325,10 +1390,11 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Comment cmnt(masm_, "[ ObjectLiteral"); + Handle<FixedArray> constant_properties = expr->constant_properties(); __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(edi, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(expr->literal_index()))); - __ push(Immediate(expr->constant_properties())); + __ push(Immediate(constant_properties)); int flags = expr->fast_elements() ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags; @@ -1336,10 +1402,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ? ObjectLiteral::kHasFunction : ObjectLiteral::kNoFlags; __ push(Immediate(Smi::FromInt(flags))); + int properties_count = constant_properties->length() / 2; if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); + } else { + FastCloneShallowObjectStub stub(properties_count); + __ CallStub(&stub); } // If result_saved is true the result is on top of the stack. If @@ -1360,7 +1431,6 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { if (!result_saved) { __ push(eax); // Save result on the stack result_saved = true; - increment_stack_height(); } switch (property->kind()) { case ObjectLiteral::Property::MATERIALIZED_LITERAL: @@ -1372,9 +1442,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(ecx, Immediate(key->handle())); __ mov(edx, Operand(esp, 0)); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, key->id()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1385,7 +1455,6 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // Fall through. case ObjectLiteral::Property::PROTOTYPE: __ push(Operand(esp, 0)); // Duplicate receiver. - increment_stack_height(); VisitForStackValue(key); VisitForStackValue(value); if (property->emit_store()) { @@ -1394,20 +1463,16 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } else { __ Drop(3); } - decrement_stack_height(3); break; case ObjectLiteral::Property::SETTER: case ObjectLiteral::Property::GETTER: __ push(Operand(esp, 0)); // Duplicate receiver. - increment_stack_height(); VisitForStackValue(key); __ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ? Smi::FromInt(1) : Smi::FromInt(0))); - increment_stack_height(); VisitForStackValue(value); __ CallRuntime(Runtime::kDefineAccessor, 4); - decrement_stack_height(4); break; default: UNREACHABLE(); } @@ -1432,25 +1497,42 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); int length = subexprs->length(); + Handle<FixedArray> constant_elements = expr->constant_elements(); + ASSERT_EQ(2, constant_elements->length()); + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); + bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(constant_elements->get(1))); __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(expr->literal_index()))); - __ push(Immediate(expr->constant_elements())); - if (expr->constant_elements()->map() == - isolate()->heap()->fixed_cow_array_map()) { - ASSERT(expr->depth() == 1); + __ push(Immediate(constant_elements)); + Heap* heap = isolate()->heap(); + if (has_constant_fast_elements && + constant_elements_values->map() == heap->fixed_cow_array_map()) { + // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // change, so it's possible to specialize the stub in advance. + __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); + FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, + length); __ CallStub(&stub); - __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); } else if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::CLONE_ELEMENTS, length); + ASSERT(constant_elements_kind == FAST_ELEMENTS || + constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + FLAG_smi_only_arrays); + // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // change, so it's possible to specialize the stub in advance. + FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements + ? FastCloneShallowArrayStub::CLONE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); __ CallStub(&stub); } @@ -1470,18 +1552,31 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { if (!result_saved) { __ push(eax); result_saved = true; - increment_stack_height(); } VisitForAccumulatorValue(subexpr); - // Store the subexpression value in the array's elements. - __ mov(ebx, Operand(esp, 0)); // Copy of array literal. - __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); - int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ mov(FieldOperand(ebx, offset), result_register()); - - // Update the write barrier for the array store. - __ RecordWrite(ebx, offset, result_register(), ecx); + if (constant_elements_kind == FAST_ELEMENTS) { + // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot + // transition and don't need to call the runtime stub. + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ mov(ebx, Operand(esp, 0)); // Copy of array literal. + __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset)); + // Store the subexpression value in the array's elements. + __ mov(FieldOperand(ebx, offset), result_register()); + // Update the write barrier for the array store. + __ RecordWriteField(ebx, offset, result_register(), ecx, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + INLINE_SMI_CHECK); + } else { + // Store the subexpression value in the array's elements. + __ mov(ebx, Operand(esp, 0)); // Copy of array literal. + __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset)); + __ mov(ecx, Immediate(Smi::FromInt(i))); + __ mov(edx, Immediate(Smi::FromInt(expr->literal_index()))); + StoreArrayLiteralElementStub stub; + __ CallStub(&stub); + } PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } @@ -1499,9 +1594,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' // on the left-hand side. if (!expr->target()->IsValidLeftHandSide()) { - ASSERT(expr->target()->AsThrow() != NULL); - VisitInCurrentContext(expr->target()); // Throw does not plug the context - context()->Plug(eax); + VisitForEffect(expr->target()); return; } @@ -1526,7 +1619,6 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { // We need the receiver both on the stack and in the accumulator. VisitForAccumulatorValue(property->obj()); __ push(result_register()); - increment_stack_height(); } else { VisitForStackValue(property->obj()); } @@ -1537,7 +1629,6 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { VisitForAccumulatorValue(property->key()); __ mov(edx, Operand(esp, 0)); __ push(eax); - increment_stack_height(); } else { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); @@ -1569,7 +1660,6 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { Token::Value op = expr->binary_op(); __ push(eax); // Left operand goes on the stack. - increment_stack_height(); VisitForAccumulatorValue(expr->value()); OverwriteMode mode = expr->value()->ResultOverwriteAllowed() @@ -1619,14 +1709,14 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { ASSERT(!key->handle()->IsSmi()); __ mov(ecx, Immediate(key->handle())); Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ call(ic, RelocInfo::CODE_TARGET, prop->id()); } void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1639,9 +1729,8 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, // stack. Right operand is in eax. Label smi_case, done, stub_call; __ pop(edx); - decrement_stack_height(); __ mov(ecx, eax); - __ or_(eax, Operand(edx)); + __ or_(eax, edx); JumpPatchSite patch_site(masm_); patch_site.EmitJumpIfSmi(eax, &smi_case, Label::kNear); @@ -1691,32 +1780,32 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, break; } case Token::ADD: - __ add(eax, Operand(ecx)); + __ add(eax, ecx); __ j(overflow, &stub_call); break; case Token::SUB: - __ sub(eax, Operand(ecx)); + __ sub(eax, ecx); __ j(overflow, &stub_call); break; case Token::MUL: { __ SmiUntag(eax); - __ imul(eax, Operand(ecx)); + __ imul(eax, ecx); __ j(overflow, &stub_call); - __ test(eax, Operand(eax)); + __ test(eax, eax); __ j(not_zero, &done, Label::kNear); __ mov(ebx, edx); - __ or_(ebx, Operand(ecx)); + __ or_(ebx, ecx); __ j(negative, &stub_call); break; } case Token::BIT_OR: - __ or_(eax, Operand(ecx)); + __ or_(eax, ecx); break; case Token::BIT_AND: - __ and_(eax, Operand(ecx)); + __ and_(eax, ecx); break; case Token::BIT_XOR: - __ xor_(eax, Operand(ecx)); + __ xor_(eax, ecx); break; default: UNREACHABLE(); @@ -1731,7 +1820,6 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode) { __ pop(edx); - decrement_stack_height(); BinaryOpStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. __ call(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); @@ -1744,9 +1832,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { // Invalid left-hand sides are rewritten to have a 'throw // ReferenceError' on the left-hand side. if (!expr->IsValidLeftHandSide()) { - ASSERT(expr->AsThrow() != NULL); - VisitInCurrentContext(expr); // Throw does not plug the context - context()->Plug(eax); + VisitForEffect(expr); return; } @@ -1770,31 +1856,26 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { } case NAMED_PROPERTY: { __ push(eax); // Preserve value. - increment_stack_height(); VisitForAccumulatorValue(prop->obj()); __ mov(edx, eax); __ pop(eax); // Restore value. - decrement_stack_height(); __ mov(ecx, prop->key()->AsLiteral()->handle()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic); break; } case KEYED_PROPERTY: { __ push(eax); // Preserve value. - increment_stack_height(); VisitForStackValue(prop->obj()); VisitForAccumulatorValue(prop->key()); __ mov(ecx, eax); __ pop(edx); - decrement_stack_height(); __ pop(eax); // Restore value. - decrement_stack_height(); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic); break; } @@ -1810,9 +1891,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // Global var, const, or let. __ mov(ecx, var->name()); __ mov(edx, GlobalObjectOperand()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { @@ -1838,13 +1919,13 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { __ push(eax); // Value. __ push(esi); // Context. __ push(Immediate(var->name())); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ push(Immediate(Smi::FromInt(language_mode()))); __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { ASSERT(var->IsStackAllocated() || var->IsContextSlot()); @@ -1859,12 +1940,14 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ mov(location, eax); if (var->IsContextSlot()) { __ mov(edx, eax); - __ RecordWrite(ecx, Context::SlotOffset(var->index()), edx, ebx); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs); } } - } else if (var->mode() != Variable::CONST) { - // Assignment to var or initializing assignment to let. + } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { + // Assignment to var or initializing assignment to let/const + // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, ecx); if (FLAG_debug_code && op == Token::INIT_LET) { @@ -1877,14 +1960,15 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ mov(location, eax); if (var->IsContextSlot()) { __ mov(edx, eax); - __ RecordWrite(ecx, Context::SlotOffset(var->index()), edx, ebx); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs); } } else { ASSERT(var->IsLookupSlot()); __ push(eax); // Value. __ push(esi); // Context. __ push(Immediate(var->name())); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ push(Immediate(Smi::FromInt(language_mode()))); __ CallRuntime(Runtime::kStoreContextSlot, 4); } } @@ -1915,11 +1999,10 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ mov(edx, Operand(esp, 0)); } else { __ pop(edx); - decrement_stack_height(); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1929,7 +2012,6 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(eax); __ Drop(1); - decrement_stack_height(); } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(eax); @@ -1951,18 +2033,16 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { } __ pop(ecx); - decrement_stack_height(); if (expr->ends_initialization_block()) { __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later. } else { __ pop(edx); - decrement_stack_height(); } // Record source code position before IC call. SetSourcePosition(expr->position()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1972,7 +2052,6 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ push(edx); __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(eax); - decrement_stack_height(); } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); @@ -1992,7 +2071,6 @@ void FullCodeGenerator::VisitProperty(Property* expr) { VisitForStackValue(expr->obj()); VisitForAccumulatorValue(expr->key()); __ pop(edx); - decrement_stack_height(); EmitKeyedPropertyLoad(expr); context()->Plug(eax); } @@ -2019,7 +2097,6 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, RecordJSReturnSite(expr); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - decrement_stack_height(arg_count + 1); context()->Plug(eax); } @@ -2034,7 +2111,6 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, __ pop(ecx); __ push(eax); __ push(ecx); - increment_stack_height(); // Load the arguments. ZoneList<Expression*>* args = expr->arguments(); @@ -2053,7 +2129,6 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, RecordJSReturnSite(expr); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - decrement_stack_height(arg_count + 1); context()->DropAndPlug(1, eax); // Drop the key still on the stack. } @@ -2069,19 +2144,30 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } // Record source position for debugger. SetSourcePosition(expr->position()); + + // Record call targets in unoptimized code, but not in the snapshot. + if (!Serializer::enabled()) { + flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET); + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->id(), cell); + __ mov(ebx, cell); + } + CallFunctionStub stub(arg_count, flags); - __ CallStub(&stub); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub, expr->id()); + RecordJSReturnSite(expr); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - - decrement_stack_height(arg_count + 1); context()->DropAndPlug(1, eax); } -void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, - int arg_count) { +void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { __ push(Operand(esp, arg_count * kPointerSize)); @@ -2091,18 +2177,14 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, // Push the receiver of the enclosing function. __ push(Operand(ebp, (2 + info_->scope()->num_parameters()) * kPointerSize)); + // Push the language mode. + __ push(Immediate(Smi::FromInt(language_mode()))); - // Push the strict mode flag. In harmony mode every eval call - // is a strict mode eval call. - StrictModeFlag strict_mode = strict_mode_flag(); - if (FLAG_harmony_block_scoping) { - strict_mode = kStrictMode; - } - __ push(Immediate(Smi::FromInt(strict_mode))); + // Push the start position of the scope the calls resides in. + __ push(Immediate(Smi::FromInt(scope()->start_position()))); - __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP - ? Runtime::kResolvePossiblyDirectEvalNoLookup - : Runtime::kResolvePossiblyDirectEval, 4); + // Do the runtime call. + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); } @@ -2128,33 +2210,15 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(callee); // Reserved receiver slot. __ push(Immediate(isolate()->factory()->undefined_value())); - increment_stack_height(); // Push the arguments. for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); } - // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly in - // generated code. If we succeed, there is no need to perform a - // context lookup in the runtime system. - Label done; - Variable* var = proxy->var(); - if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { - Label slow; - EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); - // Push the function and resolve eval. - __ push(eax); - EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); - __ jmp(&done); - __ bind(&slow); - } - // Push a copy of the function (found below the arguments) and // resolve eval. __ push(Operand(esp, (arg_count + 1) * kPointerSize)); - EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - __ bind(&done); + EmitResolvePossiblyDirectEval(arg_count); // The runtime call returns a pair of values in eax (function) and // edx (receiver). Touch up the stack with the right values. @@ -2164,17 +2228,16 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - decrement_stack_height(arg_count + 1); // Function is left on the stack. context()->DropAndPlug(1, eax); } else if (proxy != NULL && proxy->var()->IsUnallocated()) { // Push global object as receiver for the call IC. __ push(GlobalObjectOperand()); - increment_stack_height(); EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { @@ -2193,7 +2256,6 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ CallRuntime(Runtime::kLoadContextSlot, 2); __ push(eax); // Function. __ push(edx); // Receiver. - increment_stack_height(2); // If fast case code has been generated, emit code to push the function // and receiver and have the slow path jump around this code. @@ -2201,8 +2263,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { Label call; __ jmp(&call, Label::kNear); __ bind(&done); - // Push function. Stack height already incremented in slow case - // above. + // Push function. __ push(eax); // The receiver is implicitly the global receiver. Indicate this by // passing the hole to the call function stub. @@ -2235,7 +2296,6 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Load global receiver object. __ mov(ebx, GlobalObjectOperand()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); - increment_stack_height(); // Emit function call. EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS); } @@ -2273,16 +2333,28 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ SafeSet(eax, Immediate(arg_count)); __ mov(edi, Operand(esp, arg_count * kPointerSize)); - Handle<Code> construct_builtin = - isolate()->builtins()->JSConstructCall(); - __ call(construct_builtin, RelocInfo::CONSTRUCT_CALL); + // Record call targets in unoptimized code, but not in the snapshot. + CallFunctionFlags flags; + if (!Serializer::enabled()) { + flags = RECORD_CALL_TARGET; + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->id(), cell); + __ mov(ebx, cell); + } else { + flags = NO_CALL_FUNCTION_FLAGS; + } - decrement_stack_height(arg_count + 1); + CallConstructStub stub(flags); + __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); context()->Plug(eax); } -void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2294,7 +2366,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ test(eax, Immediate(kSmiTagMask)); Split(zero, if_true, if_false, fall_through); @@ -2302,7 +2374,8 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2314,7 +2387,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ test(eax, Immediate(kSmiTagMask | 0x80000000)); Split(zero, if_true, if_false, fall_through); @@ -2322,7 +2395,8 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2346,14 +2420,15 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ cmp(ecx, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); __ j(below, if_false); __ cmp(ecx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(below_equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2367,14 +2442,15 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { __ JumpIfSmi(eax, if_false); __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ebx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(above_equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2390,7 +2466,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); __ test(ebx, Immediate(1 << Map::kIsUndetectable)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(not_zero, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2398,7 +2474,8 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - ZoneList<Expression*>* args) { + CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2438,9 +2515,9 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( STATIC_ASSERT(kPointerSize == 4); __ lea(ecx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize)); // Calculate location of the first key name. - __ add(Operand(ebx), - Immediate(FixedArray::kHeaderSize + - DescriptorArray::kFirstIndex * kPointerSize)); + __ add(ebx, + Immediate(FixedArray::kHeaderSize + + DescriptorArray::kFirstIndex * kPointerSize)); // Loop through all the keys in the descriptor array. If one of these is the // symbol valueOf the result is false. Label entry, loop; @@ -2449,9 +2526,9 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ mov(edx, FieldOperand(ebx, 0)); __ cmp(edx, FACTORY->value_of_symbol()); __ j(equal, if_false); - __ add(Operand(ebx), Immediate(kPointerSize)); + __ add(ebx, Immediate(kPointerSize)); __ bind(&entry); - __ cmp(ebx, Operand(ecx)); + __ cmp(ebx, ecx); __ j(not_equal, &loop); // Reload map as register ebx was used as temporary above. @@ -2475,12 +2552,13 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf)); __ jmp(if_true); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2494,14 +2572,15 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { __ JumpIfSmi(eax, if_false); __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2515,14 +2594,15 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { __ JumpIfSmi(eax, if_false); __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2536,7 +2616,7 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { __ JumpIfSmi(eax, if_false); __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2544,8 +2624,8 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { -void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label materialize_true, materialize_false; Label* if_true = NULL; @@ -2568,14 +2648,15 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { __ bind(&check_frame_marker); __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset), Immediate(Smi::FromInt(StackFrame::CONSTRUCT))); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -2590,16 +2671,16 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { &if_true, &if_false, &fall_through); __ pop(ebx); - decrement_stack_height(); - __ cmp(eax, Operand(ebx)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + __ cmp(eax, ebx); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitArguments(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); // ArgumentsAccessStub expects the key in edx and the formal @@ -2613,8 +2694,8 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label exit; // Get the number of formal parameters. @@ -2636,7 +2717,8 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); Label done, null, function, non_function_constructor; @@ -2647,20 +2729,24 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { // Check that the object is a JS object but take special care of JS // functions to make sure they have 'Function' as their class. + // Assume that there are only two callable types, and one of them is at + // either end of the type range for JS object types. Saves extra comparisons. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, eax); // Map is now in eax. __ j(below, &null); - - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - __ CmpInstanceType(eax, FIRST_CALLABLE_SPEC_OBJECT_TYPE); - __ j(above_equal, &function); - - // Check if the constructor in the map is a function. + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + __ j(equal, &function); + + __ CmpInstanceType(eax, LAST_SPEC_OBJECT_TYPE); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + __ j(equal, &function); + // Assume that there is no larger type. + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); + + // Check if the constructor in the map is a JS function. __ mov(eax, FieldOperand(eax, Map::kConstructorOffset)); __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); __ j(not_equal, &non_function_constructor); @@ -2692,7 +2778,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitLog(CallRuntime* expr) { // Conditionally generate a log call. // Args: // 0 (literal string): The type of logging (corresponds to the flags). @@ -2700,12 +2786,12 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { // 1 (string): Format string. Access the string at argument index 2 // with '%2s' (see Logger::LogRuntime for all the formats). // 2 (array): Arguments to the format string. + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 3); if (CodeGenerator::ShouldGenerateLog(args->at(0))) { VisitForStackValue(args->at(1)); VisitForStackValue(args->at(2)); __ CallRuntime(Runtime::kLog, 2); - decrement_stack_height(2); } // Finally, we're expected to leave a value on the top of the stack. __ mov(eax, isolate()->factory()->undefined_value()); @@ -2713,8 +2799,8 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label slow_allocate_heapnumber; Label heapnumber_allocated; @@ -2730,9 +2816,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { __ bind(&heapnumber_allocated); __ PrepareCallCFunction(1, ebx); - __ mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address())); - __ CallCFunction(ExternalReference::random_uint32_function(isolate()), - 1); + __ mov(eax, ContextOperand(context_register(), Context::GLOBAL_INDEX)); + __ mov(eax, FieldOperand(eax, GlobalObject::kGlobalContextOffset)); + __ mov(Operand(esp, 0), eax); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); // Convert 32 random bits in eax to 0.(32 random bits) in a double // by computing: @@ -2741,8 +2828,8 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope fscope(SSE2); __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single. - __ movd(xmm1, Operand(ebx)); - __ movd(xmm0, Operand(eax)); + __ movd(xmm1, ebx); + __ movd(xmm0, eax); __ cvtss2sd(xmm1, xmm1); __ xorps(xmm0, xmm1); __ subsd(xmm0, xmm1); @@ -2763,34 +2850,35 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); VisitForStackValue(args->at(2)); __ CallStub(&stub); - decrement_stack_height(3); context()->Plug(eax); } -void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpExecStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 4); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); VisitForStackValue(args->at(2)); VisitForStackValue(args->at(3)); __ CallStub(&stub); - decrement_stack_height(4); context()->Plug(eax); } -void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); // Load the object. @@ -2808,30 +2896,30 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { // Load the arguments on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); if (CpuFeatures::IsSupported(SSE2)) { - MathPowStub stub; + MathPowStub stub(MathPowStub::ON_STACK); __ CallStub(&stub); } else { __ CallRuntime(Runtime::kMath_pow, 2); } - decrement_stack_height(2); context()->Plug(eax); } -void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); // Load the object. VisitForAccumulatorValue(args->at(1)); // Load the value. __ pop(ebx); // eax = value. ebx = object. - decrement_stack_height(); Label done; // If the object is a smi, return the value. @@ -2843,17 +2931,19 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { // Store the value. __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax); + // Update the write barrier. Save the value as it will be // overwritten by the write barrier code and is needed afterward. __ mov(edx, eax); - __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx); + __ RecordWriteField(ebx, JSValue::kValueOffset, edx, ecx, kDontSaveFPRegs); __ bind(&done); context()->Plug(eax); } -void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 1); // Load the argument on the stack and call the stub. @@ -2861,12 +2951,12 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { NumberToStringStub stub; __ CallStub(&stub); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2884,7 +2974,8 @@ void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2892,18 +2983,15 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Register object = ebx; Register index = eax; - Register scratch = ecx; Register result = edx; __ pop(object); - decrement_stack_height(); Label need_conversion; Label index_out_of_range; Label done; StringCharCodeAtGenerator generator(object, index, - scratch, result, &need_conversion, &need_conversion, @@ -2932,7 +3020,8 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2940,20 +3029,17 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Register object = ebx; Register index = eax; - Register scratch1 = ecx; - Register scratch2 = edx; + Register scratch = edx; Register result = eax; __ pop(object); - decrement_stack_height(); Label need_conversion; Label index_out_of_range; Label done; StringCharAtGenerator generator(object, index, - scratch1, - scratch2, + scratch, result, &need_conversion, &need_conversion, @@ -2982,7 +3068,8 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -2990,12 +3077,12 @@ void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { StringAddStub stub(NO_STRING_ADD_FLAGS); __ CallStub(&stub); - decrement_stack_height(2); context()->Plug(eax); } -void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -3003,58 +3090,70 @@ void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { StringCompareStub stub; __ CallStub(&stub); - decrement_stack_height(2); context()->Plug(eax); } -void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); + ASSERT(args->length() == 1); + VisitForStackValue(args->at(0)); + __ CallStub(&stub); + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::LOG, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { // Load the argument on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallRuntime(Runtime::kMath_sqrt, 1); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() >= 2); int arg_count = args->length() - 2; // 2 ~ receiver and function. @@ -3063,31 +3162,43 @@ void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { } VisitForAccumulatorValue(args->last()); // Function. + // Check for proxy. + Label proxy, done; + __ CmpObjectType(eax, JS_FUNCTION_PROXY_TYPE, ebx); + __ j(equal, &proxy); + // InvokeFunction requires the function in edi. Move it in there. __ mov(edi, result_register()); ParameterCount count(arg_count); __ InvokeFunction(edi, count, CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - decrement_stack_height(arg_count + 1); + __ jmp(&done); + + __ bind(&proxy); + __ push(eax); + __ CallRuntime(Runtime::kCall, args->length()); + __ bind(&done); + context()->Plug(eax); } -void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpConstructResultStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); VisitForStackValue(args->at(2)); __ CallStub(&stub); - decrement_stack_height(3); context()->Plug(eax); } -void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSwapElements(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3119,14 +3230,14 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ mov(index_1, Operand(esp, 1 * kPointerSize)); __ mov(index_2, Operand(esp, 0)); __ mov(temp, index_1); - __ or_(temp, Operand(index_2)); + __ or_(temp, index_2); __ JumpIfNotSmi(temp, &slow_case); // Check that both indices are valid. __ mov(temp, FieldOperand(object, JSArray::kLengthOffset)); - __ cmp(temp, Operand(index_1)); + __ cmp(temp, index_1); __ j(below_equal, &slow_case); - __ cmp(temp, Operand(index_2)); + __ cmp(temp, index_2); __ j(below_equal, &slow_case); // Bring addresses into index1 and index2. @@ -3139,16 +3250,35 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ mov(Operand(index_2, 0), object); __ mov(Operand(index_1, 0), temp); - Label new_space; - __ InNewSpace(elements, temp, equal, &new_space); + Label no_remembered_set; + __ CheckPageFlag(elements, + temp, + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + not_zero, + &no_remembered_set, + Label::kNear); + // Possible optimization: do a check that both values are Smis + // (or them and test against Smi mask.) + + // We are swapping two objects in an array and the incremental marker never + // pauses in the middle of scanning a single object. Therefore the + // incremental marker is not disturbed, so we don't need to call the + // RecordWrite stub that notifies the incremental marker. + __ RememberedSetHelper(elements, + index_1, + temp, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + __ RememberedSetHelper(elements, + index_2, + temp, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + + __ bind(&no_remembered_set); - __ mov(object, elements); - __ RecordWriteHelper(object, index_1, temp); - __ RecordWriteHelper(elements, index_2, temp); - - __ bind(&new_space); // We are done. Drop elements from the stack, and return undefined. - __ add(Operand(esp), Immediate(3 * kPointerSize)); + __ add(esp, Immediate(3 * kPointerSize)); __ mov(eax, isolate()->factory()->undefined_value()); __ jmp(&done); @@ -3156,12 +3286,12 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ CallRuntime(Runtime::kSwapElements, 3); __ bind(&done); - decrement_stack_height(3); context()->Plug(eax); } -void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); ASSERT_NE(NULL, args->at(0)->AsLiteral()); @@ -3209,7 +3339,8 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); Register right = eax; @@ -3221,11 +3352,11 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { __ pop(left); Label done, fail, ok; - __ cmp(left, Operand(right)); + __ cmp(left, right); __ j(equal, &ok); // Fail if either is a non-HeapObject. __ mov(tmp, left); - __ and_(Operand(tmp), right); + __ and_(tmp, right); __ JumpIfSmi(tmp, &fail); __ mov(tmp, FieldOperand(left, HeapObject::kMapOffset)); __ CmpInstanceType(tmp, JS_REGEXP_TYPE); @@ -3242,12 +3373,12 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { __ mov(eax, Immediate(isolate()->factory()->true_value())); __ bind(&done); - decrement_stack_height(); context()->Plug(eax); } -void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3265,14 +3396,15 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { __ test(FieldOperand(eax, String::kHashFieldOffset), Immediate(String::kContainsCachedArrayIndexMask)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(zero, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3287,11 +3419,12 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { Label bailout, done, one_char_separator, long_separator, non_trivial_array, not_size_one_array, loop, loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // We will leave the separator on the stack until the end of the function. VisitForStackValue(args->at(1)); @@ -3316,7 +3449,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { Operand separator_operand = Operand(esp, 2 * kPointerSize); Operand result_operand = Operand(esp, 1 * kPointerSize); Operand array_length_operand = Operand(esp, 0); - __ sub(Operand(esp), Immediate(2 * kPointerSize)); + __ sub(esp, Immediate(2 * kPointerSize)); __ cld(); // Check that the array is a JSArray __ JumpIfSmi(array, &bailout); @@ -3352,7 +3485,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // Live loop registers: index, array_length, string, // scratch, string_length, elements. if (FLAG_debug_code) { - __ cmp(index, Operand(array_length)); + __ cmp(index, array_length); __ Assert(less, "No empty arrays here in EmitFastAsciiArrayJoin"); } __ bind(&loop); @@ -3370,8 +3503,8 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { __ add(string_length, FieldOperand(string, SeqAsciiString::kLengthOffset)); __ j(overflow, &bailout); - __ add(Operand(index), Immediate(1)); - __ cmp(index, Operand(array_length)); + __ add(index, Immediate(1)); + __ cmp(index, array_length); __ j(less, &loop); // If array_length is 1, return elements[0], a string. @@ -3405,10 +3538,10 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // to string_length. __ mov(scratch, separator_operand); __ mov(scratch, FieldOperand(scratch, SeqAsciiString::kLengthOffset)); - __ sub(string_length, Operand(scratch)); // May be negative, temporarily. + __ sub(string_length, scratch); // May be negative, temporarily. __ imul(scratch, array_length_operand); __ j(overflow, &bailout); - __ add(string_length, Operand(scratch)); + __ add(string_length, scratch); __ j(overflow, &bailout); __ shr(string_length, 1); @@ -3449,7 +3582,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { __ lea(string, FieldOperand(string, SeqAsciiString::kHeaderSize)); __ CopyBytes(string, result_pos, string_length, scratch); - __ add(Operand(index), Immediate(1)); + __ add(index, Immediate(1)); __ bind(&loop_1_condition); __ cmp(index, array_length_operand); __ j(less, &loop_1); // End while (index < length). @@ -3459,7 +3592,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // One-character separator case __ bind(&one_char_separator); - // Replace separator with its ascii character value. + // Replace separator with its ASCII character value. __ mov_b(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize)); __ mov_b(separator_operand, scratch); @@ -3490,7 +3623,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { __ lea(string, FieldOperand(string, SeqAsciiString::kHeaderSize)); __ CopyBytes(string, result_pos, string_length, scratch); - __ add(Operand(index), Immediate(1)); + __ add(index, Immediate(1)); __ cmp(index, array_length_operand); __ j(less, &loop_2); // End while (index < length). @@ -3531,7 +3664,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { __ lea(string, FieldOperand(string, SeqAsciiString::kHeaderSize)); __ CopyBytes(string, result_pos, string_length, scratch); - __ add(Operand(index), Immediate(1)); + __ add(index, Immediate(1)); __ cmp(index, array_length_operand); __ j(less, &loop_3); // End while (index < length). @@ -3543,10 +3676,9 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { __ bind(&done); __ mov(eax, result_operand); // Drop temp values from the stack, and restore context register. - __ add(Operand(esp), Immediate(3 * kPointerSize)); + __ add(esp, Immediate(3 * kPointerSize)); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - decrement_stack_height(); context()->Plug(eax); } @@ -3566,7 +3698,6 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Prepare for calling JS runtime function. __ mov(eax, GlobalObjectOperand()); __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset)); - increment_stack_height(); } // Push the arguments ("left-to-right"). @@ -3588,11 +3719,6 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Call the C runtime function. __ CallRuntime(expr->function(), arg_count); } - decrement_stack_height(arg_count); - if (expr->is_jsruntime()) { - decrement_stack_height(); - } - context()->Plug(eax); } @@ -3607,15 +3733,16 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { if (property != NULL) { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ push(Immediate(Smi::FromInt(strict_mode_flag))); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); - decrement_stack_height(2); context()->Plug(eax); } else if (proxy != NULL) { Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode // but "delete this" is allowed. - ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); + ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); if (var->IsUnallocated()) { __ push(GlobalObjectOperand()); __ push(Immediate(var->name())); @@ -3657,18 +3784,41 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { // Unary NOT has no side effects so it's only necessary to visit the // subexpression. Match the optimizing compiler by not branching. VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); } else { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - - // Notice that the labels are swapped. - context()->PrepareTest(&materialize_true, &materialize_false, - &if_false, &if_true, &fall_through); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitForControl(expr->expression(), if_true, if_false, fall_through); - context()->Plug(if_false, if_true); // Labels swapped. + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + __ bind(&materialize_true); + PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); + if (context()->IsAccumulatorValue()) { + __ mov(eax, isolate()->factory()->true_value()); + } else { + __ Push(isolate()->factory()->true_value()); + } + __ jmp(&done, Label::kNear); + __ bind(&materialize_false); + PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); + if (context()->IsAccumulatorValue()) { + __ mov(eax, isolate()->factory()->false_value()); + } else { + __ Push(isolate()->factory()->false_value()); + } + __ bind(&done); } break; } @@ -3679,7 +3829,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForTypeofValue(expr->expression()); } __ CallRuntime(Runtime::kTypeof, 1); - decrement_stack_height(); context()->Plug(eax); break; } @@ -3733,10 +3882,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' // as the left-hand side. if (!expr->expression()->IsValidLeftHandSide()) { - ASSERT(expr->expression()->AsThrow() != NULL); - VisitInCurrentContext(expr->expression()); - // Visiting Throw does not plug the context. - context()->Plug(eax); + VisitForEffect(expr->expression()); return; } @@ -3761,20 +3907,17 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { // Reserve space for result of postfix operation. if (expr->is_postfix() && !context()->IsEffect()) { __ push(Immediate(Smi::FromInt(0))); - increment_stack_height(); } if (assign_type == NAMED_PROPERTY) { // Put the object both on the stack and in the accumulator. VisitForAccumulatorValue(prop->obj()); __ push(eax); - increment_stack_height(); EmitNamedPropertyLoad(prop); } else { VisitForStackValue(prop->obj()); VisitForAccumulatorValue(prop->key()); __ mov(edx, Operand(esp, 0)); __ push(eax); - increment_stack_height(); EmitKeyedPropertyLoad(prop); } } @@ -3805,7 +3948,6 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { switch (assign_type) { case VARIABLE: __ push(eax); - increment_stack_height(); break; case NAMED_PROPERTY: __ mov(Operand(esp, kPointerSize), eax); @@ -3823,9 +3965,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { if (ShouldInlineSmiCase(expr->op())) { if (expr->op() == Token::INC) { - __ add(Operand(eax), Immediate(Smi::FromInt(1))); + __ add(eax, Immediate(Smi::FromInt(1))); } else { - __ sub(Operand(eax), Immediate(Smi::FromInt(1))); + __ sub(eax, Immediate(Smi::FromInt(1))); } __ j(overflow, &stub_call, Label::kNear); // We could eliminate this smi check if we split the code at @@ -3835,9 +3977,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ bind(&stub_call); // Call stub. Undo operation first. if (expr->op() == Token::INC) { - __ sub(Operand(eax), Immediate(Smi::FromInt(1))); + __ sub(eax, Immediate(Smi::FromInt(1))); } else { - __ add(Operand(eax), Immediate(Smi::FromInt(1))); + __ add(eax, Immediate(Smi::FromInt(1))); } } @@ -3879,10 +4021,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ mov(ecx, prop->key()->AsLiteral()->handle()); __ pop(edx); - decrement_stack_height(); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3897,11 +4038,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(ecx); __ pop(edx); - decrement_stack_height(); - decrement_stack_height(); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3950,20 +4089,25 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { context()->Plug(eax); } else { // This expression cannot throw a reference error at the top level. - VisitInCurrentContext(expr); + VisitInDuplicateContext(expr); } } void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through) { + Expression* sub_expr, + Handle<String> check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + { AccumulatorValueContext context(this); - VisitForTypeofValue(expr); + VisitForTypeofValue(sub_expr); } - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); if (check->Equals(isolate()->heap()->number_symbol())) { __ JumpIfSmi(eax, if_true); @@ -3998,8 +4142,11 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(not_zero, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->function_symbol())) { __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, FIRST_CALLABLE_SPEC_OBJECT_TYPE, edx); - Split(above_equal, if_true, if_false, fall_through); + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ CmpObjectType(eax, JS_FUNCTION_TYPE, edx); + __ j(equal, if_true); + __ CmpInstanceType(edx, JS_FUNCTION_PROXY_TYPE); + Split(equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(eax, if_false); if (!FLAG_harmony_typeof) { @@ -4017,18 +4164,7 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else { if (if_false != fall_through) __ jmp(if_false); } -} - - -void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through) { - VisitForAccumulatorValue(expr); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ cmp(eax, isolate()->factory()->undefined_value()); - Split(equal, if_true, if_false, fall_through); + context()->Plug(if_true, if_false); } @@ -4036,9 +4172,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); SetSourcePosition(expr->position()); + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4046,21 +4185,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { - context()->Plug(if_true, if_false); - return; - } - Token::Value op = expr->op(); VisitForStackValue(expr->left()); - switch (expr->op()) { + switch (op) { case Token::IN: VisitForStackValue(expr->right()); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - decrement_stack_height(2); - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); __ cmp(eax, isolate()->factory()->true_value()); Split(equal, if_true, if_false, fall_through); break; @@ -4069,9 +4200,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForStackValue(expr->right()); InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); - decrement_stack_height(2); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - __ test(eax, Operand(eax)); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + __ test(eax, eax); // The stub returns 0 for true. Split(zero, if_true, if_false, fall_through); break; @@ -4084,43 +4214,34 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: case Token::EQ: cc = equal; - __ pop(edx); break; case Token::LT: cc = less; - __ pop(edx); break; case Token::GT: - // Reverse left and right sizes to obtain ECMA-262 conversion order. - cc = less; - __ mov(edx, result_register()); - __ pop(eax); + cc = greater; break; case Token::LTE: - // Reverse left and right sizes to obtain ECMA-262 conversion order. - cc = greater_equal; - __ mov(edx, result_register()); - __ pop(eax); + cc = less_equal; break; case Token::GTE: cc = greater_equal; - __ pop(edx); break; case Token::IN: case Token::INSTANCEOF: default: UNREACHABLE(); } - decrement_stack_height(); + __ pop(edx); bool inline_smi_code = ShouldInlineSmiCase(op); JumpPatchSite patch_site(masm_); if (inline_smi_code) { Label slow_case; - __ mov(ecx, Operand(edx)); - __ or_(ecx, Operand(eax)); + __ mov(ecx, edx); + __ or_(ecx, eax); patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); - __ cmp(edx, Operand(eax)); + __ cmp(edx, eax); Split(cc, if_true, if_false, NULL); __ bind(&slow_case); } @@ -4131,8 +4252,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { __ call(ic, RelocInfo::CODE_TARGET, expr->id()); patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - __ test(eax, Operand(eax)); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + __ test(eax, eax); Split(cc, if_true, if_false, fall_through); } } @@ -4143,7 +4264,9 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } -void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4151,15 +4274,20 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - VisitForAccumulatorValue(expr->expression()); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ cmp(eax, isolate()->factory()->null_value()); - if (expr->is_strict()) { + VisitForAccumulatorValue(sub_expr); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + Handle<Object> nil_value = nil == kNullValue ? + isolate()->factory()->null_value() : + isolate()->factory()->undefined_value(); + __ cmp(eax, nil_value); + if (expr->op() == Token::EQ_STRICT) { Split(equal, if_true, if_false, fall_through); } else { + Handle<Object> other_nil_value = nil == kNullValue ? + isolate()->factory()->undefined_value() : + isolate()->factory()->null_value(); __ j(equal, if_true); - __ cmp(eax, isolate()->factory()->undefined_value()); + __ cmp(eax, other_nil_value); __ j(equal, if_true); __ JumpIfSmi(eax, if_false); // It can be an undetectable object. @@ -4226,7 +4354,7 @@ void FullCodeGenerator::EnterFinallyBlock() { // Cook return address on top of stack (smi encoded Code* delta) ASSERT(!result_register().is(edx)); __ pop(edx); - __ sub(Operand(edx), Immediate(masm_->CodeObject())); + __ sub(edx, Immediate(masm_->CodeObject())); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); STATIC_ASSERT(kSmiTag == 0); __ SmiTag(edx); @@ -4242,8 +4370,8 @@ void FullCodeGenerator::ExitFinallyBlock() { // Uncook return address. __ pop(edx); __ SmiUntag(edx); - __ add(Operand(edx), Immediate(masm_->CodeObject())); - __ jmp(Operand(edx)); + __ add(edx, Immediate(masm_->CodeObject())); + __ jmp(edx); } diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index 9b5cc56401..dbb0554ac0 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -212,7 +212,7 @@ static void GenerateDictionaryStore(MacroAssembler* masm, // Update write barrier. Make sure not to clobber the value. __ mov(r1, value); - __ RecordWrite(elements, r0, r1); + __ RecordWrite(elements, r0, r1, kDontSaveFPRegs); } @@ -326,7 +326,7 @@ static void GenerateFastArrayLoad(MacroAssembler* masm, // Fast case: Do the load. STATIC_ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0)); __ mov(scratch, FieldOperand(scratch, key, times_2, FixedArray::kHeaderSize)); - __ cmp(Operand(scratch), Immediate(FACTORY->the_hole_value())); + __ cmp(scratch, Immediate(FACTORY->the_hole_value())); // In case the loaded value is the_hole we have to consult GetProperty // to ensure the prototype chain is searched. __ j(equal, out_of_range); @@ -394,8 +394,8 @@ static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm, // Check if element is in the range of mapped arguments. If not, jump // to the unmapped lookup with the parameter map in scratch1. __ mov(scratch2, FieldOperand(scratch1, FixedArray::kLengthOffset)); - __ sub(Operand(scratch2), Immediate(Smi::FromInt(2))); - __ cmp(key, Operand(scratch2)); + __ sub(scratch2, Immediate(Smi::FromInt(2))); + __ cmp(key, scratch2); __ j(greater_equal, unmapped_case); // Load element index and check whether it is the hole. @@ -432,7 +432,7 @@ static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm, Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map()); __ CheckMap(backing_store, fixed_array_map, slow_case, DONT_DO_SMI_CHECK); __ mov(scratch, FieldOperand(backing_store, FixedArray::kLengthOffset)); - __ cmp(key, Operand(scratch)); + __ cmp(key, scratch); __ j(greater_equal, slow_case); return FieldOperand(backing_store, key, @@ -473,7 +473,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { Counters* counters = isolate->counters(); __ IncrementCounter(counters->keyed_load_generic_smi(), 1); __ ret(0); - __ bind(&check_number_dictionary); __ mov(ebx, eax); __ SmiUntag(ebx); @@ -534,18 +533,38 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ shr(ecx, KeyedLookupCache::kMapHashShift); __ mov(edi, FieldOperand(eax, String::kHashFieldOffset)); __ shr(edi, String::kHashShift); - __ xor_(ecx, Operand(edi)); - __ and_(ecx, KeyedLookupCache::kCapacityMask); + __ xor_(ecx, edi); + __ and_(ecx, KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask); // Load the key (consisting of map and symbol) from the cache and // check for match. + Label load_in_object_property; + static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; + Label hit_on_nth_entry[kEntriesPerBucket]; ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys(masm->isolate()); - __ mov(edi, ecx); + + for (int i = 0; i < kEntriesPerBucket - 1; i++) { + Label try_next_entry; + __ mov(edi, ecx); + __ shl(edi, kPointerSizeLog2 + 1); + if (i != 0) { + __ add(edi, Immediate(kPointerSize * i * 2)); + } + __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys)); + __ j(not_equal, &try_next_entry); + __ add(edi, Immediate(kPointerSize)); + __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys)); + __ j(equal, &hit_on_nth_entry[i]); + __ bind(&try_next_entry); + } + + __ lea(edi, Operand(ecx, 1)); __ shl(edi, kPointerSizeLog2 + 1); + __ add(edi, Immediate(kPointerSize * (kEntriesPerBucket - 1) * 2)); __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys)); __ j(not_equal, &slow); - __ add(Operand(edi), Immediate(kPointerSize)); + __ add(edi, Immediate(kPointerSize)); __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys)); __ j(not_equal, &slow); @@ -556,15 +575,27 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // ecx : lookup cache index ExternalReference cache_field_offsets = ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate()); - __ mov(edi, - Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets)); - __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset)); - __ sub(edi, Operand(ecx)); - __ j(above_equal, &property_array_property); + + // Hit on nth entry. + for (int i = kEntriesPerBucket - 1; i >= 0; i--) { + __ bind(&hit_on_nth_entry[i]); + if (i != 0) { + __ add(ecx, Immediate(i)); + } + __ mov(edi, + Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets)); + __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset)); + __ sub(edi, ecx); + __ j(above_equal, &property_array_property); + if (i != 0) { + __ jmp(&load_in_object_property); + } + } // Load in-object property. + __ bind(&load_in_object_property); __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); - __ add(ecx, Operand(edi)); + __ add(ecx, edi); __ mov(eax, FieldOperand(edx, ecx, times_pointer_size, 0)); __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1); __ ret(0); @@ -606,14 +637,12 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { Register receiver = edx; Register index = eax; - Register scratch1 = ebx; - Register scratch2 = ecx; + Register scratch = ecx; Register result = eax; StringCharAtGenerator char_at_generator(receiver, index, - scratch1, - scratch2, + scratch, result, &miss, // When not a string. &miss, // When not a number. @@ -651,8 +680,8 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { // Check that it has indexed interceptor and access checks // are not enabled for this object. __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset)); - __ and_(Operand(ecx), Immediate(kSlowCaseBitFieldMask)); - __ cmp(Operand(ecx), Immediate(1 << Map::kHasIndexedInterceptor)); + __ and_(ecx, Immediate(kSlowCaseBitFieldMask)); + __ cmp(ecx, Immediate(1 << Map::kHasIndexedInterceptor)); __ j(not_zero, &slow); // Everything is fine, call runtime. @@ -710,7 +739,7 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { __ mov(mapped_location, eax); __ lea(ecx, mapped_location); __ mov(edx, eax); - __ RecordWrite(ebx, ecx, edx); + __ RecordWrite(ebx, ecx, edx, kDontSaveFPRegs); __ Ret(); __ bind(¬in); // The unmapped lookup expects that the parameter map is in ebx. @@ -719,7 +748,7 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { __ mov(unmapped_location, eax); __ lea(edi, unmapped_location); __ mov(edx, eax); - __ RecordWrite(ebx, edi, edx); + __ RecordWrite(ebx, edi, edx, kDontSaveFPRegs); __ Ret(); __ bind(&slow); GenerateMiss(masm, false); @@ -734,7 +763,10 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // -- edx : receiver // -- esp[0] : return address // ----------------------------------- - Label slow, fast, array, extra; + Label slow, fast_object_with_map_check, fast_object_without_map_check; + Label fast_double_with_map_check, fast_double_without_map_check; + Label check_if_double_array, array, extra, transition_smi_elements; + Label finish_object_store, non_double_value, transition_double_elements; // Check that the object isn't a smi. __ JumpIfSmi(edx, &slow); @@ -750,22 +782,18 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ CmpInstanceType(edi, JS_ARRAY_TYPE); __ j(equal, &array); // Check that the object is some kind of JSObject. - __ CmpInstanceType(edi, FIRST_JS_RECEIVER_TYPE); + __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE); __ j(below, &slow); - __ CmpInstanceType(edi, JS_PROXY_TYPE); - __ j(equal, &slow); - __ CmpInstanceType(edi, JS_FUNCTION_PROXY_TYPE); - __ j(equal, &slow); // Object case: Check key against length in the elements array. // eax: value // edx: JSObject // ecx: key (a smi) - __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); - // Check that the object is in fast mode and writable. - __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK); - __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); - __ j(below, &fast); + // edi: receiver map + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); + // Check array bounds. Both the key and the length of FixedArray are smis. + __ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); + __ j(below, &fast_object_with_map_check); // Slow case: call runtime. __ bind(&slow); @@ -778,16 +806,28 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // eax: value // edx: receiver, a JSArray // ecx: key, a smi. - // edi: receiver->elements, a FixedArray + // ebx: receiver->elements, a FixedArray + // edi: receiver map // flags: compare (ecx, edx.length()) // do not leave holes in the array: __ j(not_equal, &slow); - __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); + __ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); __ j(above_equal, &slow); - // Add 1 to receiver->length, and go to fast array write. + __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset)); + __ cmp(edi, masm->isolate()->factory()->fixed_array_map()); + __ j(not_equal, &check_if_double_array); + // Add 1 to receiver->length, and go to common element store code for Objects. + __ add(FieldOperand(edx, JSArray::kLengthOffset), + Immediate(Smi::FromInt(1))); + __ jmp(&fast_object_without_map_check); + + __ bind(&check_if_double_array); + __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map()); + __ j(not_equal, &slow); + // Add 1 to receiver->length, and go to common element store code for doubles. __ add(FieldOperand(edx, JSArray::kLengthOffset), Immediate(Smi::FromInt(1))); - __ jmp(&fast); + __ jmp(&fast_double_without_map_check); // Array case: Get the length and the elements array from the JS // array. Check that the array is in fast mode (and writable); if it @@ -796,34 +836,111 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // eax: value // edx: receiver, a JSArray // ecx: key, a smi. - __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); - __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK); + // edi: receiver map + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); - // Check the key against the length in the array, compute the - // address to store into and fall through to fast case. + // Check the key against the length in the array and fall through to the + // common store code. __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis. __ j(above_equal, &extra); - // Fast case: Do the store. - __ bind(&fast); + // Fast case: Do the store, could either Object or double. + __ bind(&fast_object_with_map_check); // eax: value // ecx: key (a smi) // edx: receiver - // edi: FixedArray receiver->elements - __ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax); + // ebx: FixedArray receiver->elements + // edi: receiver map + __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset)); + __ cmp(edi, masm->isolate()->factory()->fixed_array_map()); + __ j(not_equal, &fast_double_with_map_check); + __ bind(&fast_object_without_map_check); + // Smi stores don't require further checks. + Label non_smi_value; + __ JumpIfNotSmi(eax, &non_smi_value); + // It's irrelevant whether array is smi-only or not when writing a smi. + __ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax); + __ ret(0); + + __ bind(&non_smi_value); + // Escape to elements kind transition case. + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); + __ CheckFastObjectElements(edi, &transition_smi_elements); + + // Fast elements array, store the value to the elements backing store. + __ bind(&finish_object_store); + __ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax); // Update write barrier for the elements array address. - __ mov(edx, Operand(eax)); - __ RecordWrite(edi, 0, edx, ecx); + __ mov(edx, eax); // Preserve the value which is returned. + __ RecordWriteArray( + ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ ret(0); + + __ bind(&fast_double_with_map_check); + // Check for fast double array case. If this fails, call through to the + // runtime. + __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map()); + __ j(not_equal, &slow); + __ bind(&fast_double_without_map_check); + // If the value is a number, store it as a double in the FastDoubleElements + // array. + __ StoreNumberToDoubleElements(eax, ebx, ecx, edx, xmm0, + &transition_double_elements, false); __ ret(0); + + __ bind(&transition_smi_elements); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + + // Transition the array appropriately depending on the value type. + __ CheckMap(eax, + masm->isolate()->factory()->heap_number_map(), + &non_double_value, + DONT_DO_SMI_CHECK); + + // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // FAST_DOUBLE_ELEMENTS and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_DOUBLE_ELEMENTS, + ebx, + edi, + &slow); + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); + __ jmp(&fast_double_without_map_check); + + __ bind(&non_double_value); + // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + ebx, + edi, + &slow); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); + + __ bind(&transition_double_elements); + // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a + // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and + // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, + FAST_ELEMENTS, + ebx, + edi, + &slow); + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); } // The generated code does not accept smi keys. // The generated code falls through if both probes miss. -static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, + int argc, + Code::Kind kind, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- ecx : name // -- edx : receiver @@ -833,11 +950,11 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, - extra_ic_state, + extra_state, NORMAL, argc); - Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, - eax); + Isolate* isolate = masm->isolate(); + isolate->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, eax); // If the stub cache probing failed, the receiver might be a value. // For value objects, we use the map of the prototype objects for @@ -863,9 +980,9 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Check for boolean. __ bind(&non_string); - __ cmp(edx, FACTORY->true_value()); + __ cmp(edx, isolate->factory()->true_value()); __ j(equal, &boolean); - __ cmp(edx, FACTORY->false_value()); + __ cmp(edx, isolate->factory()->false_value()); __ j(not_equal, &miss); __ bind(&boolean); StubCompiler::GenerateLoadGlobalFunctionPrototype( @@ -873,8 +990,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache for the value object. __ bind(&probe); - Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, - no_reg); + isolate->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, no_reg); __ bind(&miss); } @@ -904,8 +1020,9 @@ static void GenerateFunctionTailCall(MacroAssembler* masm, NullCallWrapper(), CALL_AS_METHOD); } + // The generated code falls through if the call should be handled by runtime. -static void GenerateCallNormal(MacroAssembler* masm, int argc) { +void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -929,10 +1046,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { } -static void GenerateCallMiss(MacroAssembler* masm, - int argc, - IC::UtilityId id, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -951,22 +1068,22 @@ static void GenerateCallMiss(MacroAssembler* masm, // Get the receiver of the function from the stack; 1 ~ return address. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); - // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push the receiver and the name of the function. - __ push(edx); - __ push(ecx); + // Push the receiver and the name of the function. + __ push(edx); + __ push(ecx); - // Call the entry. - CEntryStub stub(1); - __ mov(eax, Immediate(2)); - __ mov(ebx, Immediate(ExternalReference(IC_Utility(id), masm->isolate()))); - __ CallStub(&stub); + // Call the entry. + CEntryStub stub(1); + __ mov(eax, Immediate(2)); + __ mov(ebx, Immediate(ExternalReference(IC_Utility(id), masm->isolate()))); + __ CallStub(&stub); - // Move result to edi and exit the internal frame. - __ mov(edi, eax); - __ LeaveInternalFrame(); + // Move result to edi and exit the internal frame. + __ mov(edi, eax); + } // Check if the receiver is a global object of some sort. // This can happen only for regular CallIC but not KeyedCallIC. @@ -989,7 +1106,7 @@ static void GenerateCallMiss(MacroAssembler* masm, } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state) + CallKind call_kind = CallICBase::Contextual::decode(extra_state) ? CALL_AS_FUNCTION : CALL_AS_METHOD; ParameterCount actual(argc); @@ -1003,7 +1120,7 @@ static void GenerateCallMiss(MacroAssembler* masm, void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc, - Code::ExtraICState extra_ic_state) { + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1014,38 +1131,10 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, // Get the receiver of the function from the stack; 1 ~ return address. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); - GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state); - - GenerateMiss(masm, argc, extra_ic_state); -} - - -void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- ecx : name - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- ... - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - GenerateCallNormal(masm, argc); - GenerateMiss(masm, argc, Code::kNoExtraICState); -} - + CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, + extra_state); -void CallIC::GenerateMiss(MacroAssembler* masm, - int argc, - Code::ExtraICState extra_ic_state) { - // ----------- S t a t e ------------- - // -- ecx : name - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- ... - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state); + GenerateMiss(masm, argc, extra_state); } @@ -1111,13 +1200,17 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // This branch is taken when calling KeyedCallIC_Miss is neither required // nor beneficial. __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1); - __ EnterInternalFrame(); - __ push(ecx); // save the key - __ push(edx); // pass the receiver - __ push(ecx); // pass the key - __ CallRuntime(Runtime::kKeyedGetProperty, 2); - __ pop(ecx); // restore the key - __ LeaveInternalFrame(); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(ecx); // save the key + __ push(edx); // pass the receiver + __ push(ecx); // pass the key + __ CallRuntime(Runtime::kKeyedGetProperty, 2); + __ pop(ecx); // restore the key + // Leave the internal frame. + } + __ mov(edi, eax); __ jmp(&do_call); @@ -1143,10 +1236,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { __ bind(&lookup_monomorphic_cache); __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1); - GenerateMonomorphicCacheProbe(masm, - argc, - Code::KEYED_CALL_IC, - Code::kNoExtraICState); + CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, + Code::kNoExtraICState); // Fall through on miss. __ bind(&slow_call); @@ -1209,25 +1300,12 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ JumpIfSmi(ecx, &miss); Condition cond = masm->IsObjectStringType(ecx, eax, eax); __ j(NegateCondition(cond), &miss); - GenerateCallNormal(masm, argc); + CallICBase::GenerateNormal(masm, argc); __ bind(&miss); GenerateMiss(masm, argc); } -void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- ecx : name - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- ... - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState); -} - - void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : receiver @@ -1375,10 +1453,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { // -- esp[0] : return address // ----------------------------------- // - // This accepts as a receiver anything JSObject::SetElementsLength accepts + // This accepts as a receiver anything JSArray::SetElementsLength accepts // (currently anything except for external arrays which means anything with - // elements of FixedArray type.), but currently is restricted to JSArray. - // Value must be a number, but only smis are accepted as the most common case. + // elements of FixedArray type). Value must be a number, but only smis are + // accepted as the most common case. Label miss; @@ -1400,6 +1478,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch); __ j(not_equal, &miss); + // Check that the array has fast properties, otherwise the length + // property might have been redefined. + __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset)); + __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset), + Heap::kHashTableMapRootIndex); + __ j(equal, &miss); + // Check that value is a smi. __ JumpIfNotSmi(value, &miss); @@ -1536,6 +1621,51 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- ebx : target map + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + // Must return the modified receiver in eax. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + __ mov(eax, edx); + __ Ret(); + __ bind(&fail); + } + + __ pop(ebx); + __ push(edx); + __ push(ebx); // return address + __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1); +} + + +void KeyedStoreIC::GenerateTransitionElementsDoubleToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- ebx : target map + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + // Must return the modified receiver in eax. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); + __ mov(eax, edx); + __ Ret(); + __ bind(&fail); + } + + __ pop(ebx); + __ push(edx); + __ push(ebx); // return address + __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1); +} + + #undef __ @@ -1547,11 +1677,9 @@ Condition CompareIC::ComputeCondition(Token::Value op) { case Token::LT: return less; case Token::GT: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return less; + return greater; case Token::LTE: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return greater_equal; + return less_equal; case Token::GTE: return greater_equal; default: @@ -1583,6 +1711,9 @@ void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { rewritten = stub.GetCode(); } else { ICCompareStub stub(op_, state); + if (state == KNOWN_OBJECTS) { + stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map())); + } rewritten = stub.GetCode(); } set_target(*rewritten); diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index d5a4fe6610..5a276f4527 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -33,6 +33,7 @@ #include "code-stubs.h" #include "deoptimizer.h" #include "stub-cache.h" +#include "codegen.h" namespace v8 { namespace internal { @@ -70,6 +71,17 @@ bool LCodeGen::GenerateCode() { ASSERT(is_unused()); status_ = GENERATING; CpuFeatures::Scope scope(SSE2); + + CodeStub::GenerateFPStubs(); + + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done in GeneratePrologue). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + + dynamic_frame_alignment_ = chunk()->num_double_slots() > 2 || + info()->osr_ast_id() != AstNode::kNoNumber; + return GeneratePrologue() && GenerateBody() && GenerateDeferredCode() && @@ -133,7 +145,7 @@ bool LCodeGen::GeneratePrologue() { // with undefined when called as functions (without an explicit // receiver object). ecx is zero for method calls and non-zero for // function calls. - if (info_->is_strict_mode() || info_->is_native()) { + if (!info_->is_classic_mode() || info_->is_native()) { Label ok; __ test(ecx, Operand(ecx)); __ j(zero, &ok, Label::kNear); @@ -144,6 +156,29 @@ bool LCodeGen::GeneratePrologue() { __ bind(&ok); } + if (dynamic_frame_alignment_) { + Label do_not_pad, align_loop; + STATIC_ASSERT(kDoubleSize == 2 * kPointerSize); + // Align esp to a multiple of 2 * kPointerSize. + __ test(esp, Immediate(kPointerSize)); + __ j(zero, &do_not_pad, Label::kNear); + __ push(Immediate(0)); + __ mov(ebx, esp); + // Copy arguments, receiver, and return address. + __ mov(ecx, Immediate(scope()->num_parameters() + 2)); + + __ bind(&align_loop); + __ mov(eax, Operand(ebx, 1 * kPointerSize)); + __ mov(Operand(ebx, 0), eax); + __ add(Operand(ebx), Immediate(kPointerSize)); + __ dec(ecx); + __ j(not_zero, &align_loop, Label::kNear); + __ mov(Operand(ebx, 0), + Immediate(isolate()->factory()->frame_alignment_marker())); + + __ bind(&do_not_pad); + } + __ push(ebp); // Caller's frame pointer. __ mov(ebp, esp); __ push(esi); // Callee's context. @@ -204,11 +239,12 @@ bool LCodeGen::GeneratePrologue() { // Store it in the context. int context_offset = Context::SlotOffset(var->index()); __ mov(Operand(esi, context_offset), eax); - // Update the write barrier. This clobbers all involved - // registers, so we have to use a third register to avoid - // clobbering esi. - __ mov(ecx, esi); - __ RecordWrite(ecx, context_offset, eax, ebx); + // Update the write barrier. This clobbers eax and ebx. + __ RecordWriteContextSlot(esi, + context_offset, + eax, + ebx, + kDontSaveFPRegs); } } Comment(";;; End allocate local context"); @@ -252,6 +288,9 @@ bool LCodeGen::GenerateDeferredCode() { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; __ bind(code->entry()); + Comment(";;; Deferred code @%d: %s.", + code->instruction_index(), + code->instr()->Mnemonic()); code->Generate(); __ jmp(code->exit()); } @@ -302,18 +341,21 @@ int LCodeGen::ToInteger32(LConstantOperand* op) const { } -Immediate LCodeGen::ToImmediate(LOperand* op) { - LConstantOperand* const_op = LConstantOperand::cast(op); - Handle<Object> literal = chunk_->LookupLiteral(const_op); - Representation r = chunk_->LookupLiteralRepresentation(const_op); - if (r.IsInteger32()) { - ASSERT(literal->IsNumber()); - return Immediate(static_cast<int32_t>(literal->Number())); - } else if (r.IsDouble()) { - Abort("unsupported double immediate"); - } - ASSERT(r.IsTagged()); - return Immediate(literal); +Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const { + Handle<Object> literal = chunk_->LookupLiteral(op); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + return literal; +} + + +double LCodeGen::ToDouble(LConstantOperand* op) const { + Handle<Object> value = chunk_->LookupLiteral(op); + return value->Number(); +} + + +bool LCodeGen::IsInteger32(LConstantOperand* op) const { + return chunk_->LookupLiteralRepresentation(op).IsInteger32(); } @@ -352,7 +394,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - translation->BeginFrame(environment->ast_id(), closure_id, height); + if (environment->is_arguments_adaptor()) { + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + } else { + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); // spilled_registers_ and spilled_double_registers_ are either @@ -464,14 +510,18 @@ void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, int argc, LInstruction* instr, LOperand* context) { - ASSERT(context->IsRegister() || context->IsStackSlot()); if (context->IsRegister()) { if (!ToRegister(context).is(esi)) { __ mov(esi, ToRegister(context)); } - } else { - // Context is stack slot. + } else if (context->IsStackSlot()) { __ mov(esi, ToOperand(context)); + } else if (context->IsConstantOperand()) { + Handle<Object> literal = + chunk_->LookupLiteral(LConstantOperand::cast(context)); + __ LoadHeapObject(esi, Handle<Context>::cast(literal)); + } else { + UNREACHABLE(); } __ CallRuntimeSaveDoubles(id); @@ -497,10 +547,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization( // |>------------ translation_size ------------<| int frame_count = 0; + int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; + if (!e->is_arguments_adaptor()) { + ++jsframe_count; + } } - Translation translation(&translations_, frame_count); + Translation translation(&translations_, frame_count, jsframe_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); int pc_offset = masm()->pc_offset(); @@ -568,7 +622,6 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) { int length = deoptimizations_.length(); if (length == 0) return; - ASSERT(FLAG_deopt); Handle<DeoptimizationInputData> data = factory()->NewDeoptimizationInputData(length, TENURED); @@ -643,7 +696,7 @@ void LCodeGen::RecordSafepoint( int arguments, Safepoint::DeoptMode deopt_mode) { ASSERT(kind == expected_safepoint_kind_); - const ZoneList<LOperand*>* operands = pointers->operands(); + const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands(); Safepoint safepoint = safepoints_.DefineSafepoint(masm(), kind, arguments, deopt_mode); for (int i = 0; i < operands->length(); i++) { @@ -1111,7 +1164,7 @@ void LCodeGen::DoSubI(LSubI* instr) { ASSERT(left->Equals(instr->result())); if (right->IsConstantOperand()) { - __ sub(ToOperand(left), ToImmediate(right)); + __ sub(ToOperand(left), ToInteger32Immediate(right)); } else { __ sub(ToRegister(left), ToOperand(right)); } @@ -1167,8 +1220,13 @@ void LCodeGen::DoConstantD(LConstantD* instr) { void LCodeGen::DoConstantT(LConstantT* instr) { - ASSERT(instr->result()->IsRegister()); - __ Set(ToRegister(instr->result()), Immediate(instr->value())); + Register reg = ToRegister(instr->result()); + Handle<Object> handle = instr->value(); + if (handle->IsHeapObject()) { + __ LoadHeapObject(reg, Handle<HeapObject>::cast(handle)); + } else { + __ Set(reg, Immediate(handle)); + } } @@ -1245,7 +1303,7 @@ void LCodeGen::DoAddI(LAddI* instr) { ASSERT(left->Equals(instr->result())); if (right->IsConstantOperand()) { - __ add(ToOperand(left), ToImmediate(right)); + __ add(ToOperand(left), ToInteger32Immediate(right)); } else { __ add(ToRegister(left), ToOperand(right)); } @@ -1494,32 +1552,40 @@ Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { } -void LCodeGen::EmitCmpI(LOperand* left, LOperand* right) { - if (right->IsConstantOperand()) { - __ cmp(ToOperand(left), ToImmediate(right)); - } else { - __ cmp(ToRegister(left), ToOperand(right)); - } -} - - void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { LOperand* left = instr->InputAt(0); LOperand* right = instr->InputAt(1); int false_block = chunk_->LookupDestination(instr->false_block_id()); int true_block = chunk_->LookupDestination(instr->true_block_id()); + Condition cc = TokenToCondition(instr->op(), instr->is_double()); - if (instr->is_double()) { - // Don't base result on EFLAGS when a NaN is involved. Instead - // jump to the false block. - __ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right)); - __ j(parity_even, chunk_->GetAssemblyLabel(false_block)); + if (left->IsConstantOperand() && right->IsConstantOperand()) { + // We can statically evaluate the comparison. + double left_val = ToDouble(LConstantOperand::cast(left)); + double right_val = ToDouble(LConstantOperand::cast(right)); + int next_block = + EvalComparison(instr->op(), left_val, right_val) ? true_block + : false_block; + EmitGoto(next_block); } else { - EmitCmpI(left, right); + if (instr->is_double()) { + // Don't base result on EFLAGS when a NaN is involved. Instead + // jump to the false block. + __ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right)); + __ j(parity_even, chunk_->GetAssemblyLabel(false_block)); + } else { + if (right->IsConstantOperand()) { + __ cmp(ToRegister(left), ToInteger32Immediate(right)); + } else if (left->IsConstantOperand()) { + __ cmp(ToOperand(right), ToInteger32Immediate(left)); + // We transposed the operands. Reverse the condition. + cc = ReverseCondition(cc); + } else { + __ cmp(ToRegister(left), ToOperand(right)); + } + } + EmitBranch(true_block, false_block, cc); } - - Condition cc = TokenToCondition(instr->op(), instr->is_double()); - EmitBranch(true_block, false_block, cc); } @@ -1544,23 +1610,33 @@ void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { } -void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) { +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { Register reg = ToRegister(instr->InputAt(0)); + int false_block = chunk_->LookupDestination(instr->false_block_id()); - // TODO(fsc): If the expression is known to be a smi, then it's - // definitely not null. Jump to the false block. + // If the expression is known to be untagged or a smi, then it's definitely + // not null, and it can't be a an undetectable object. + if (instr->hydrogen()->representation().IsSpecialization() || + instr->hydrogen()->type().IsSmi()) { + EmitGoto(false_block); + return; + } int true_block = chunk_->LookupDestination(instr->true_block_id()); - int false_block = chunk_->LookupDestination(instr->false_block_id()); - - __ cmp(reg, factory()->null_value()); - if (instr->is_strict()) { + Handle<Object> nil_value = instr->nil() == kNullValue ? + factory()->null_value() : + factory()->undefined_value(); + __ cmp(reg, nil_value); + if (instr->kind() == kStrictEquality) { EmitBranch(true_block, false_block, equal); } else { + Handle<Object> other_nil_value = instr->nil() == kNullValue ? + factory()->undefined_value() : + factory()->null_value(); Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); __ j(equal, true_label); - __ cmp(reg, factory()->undefined_value()); + __ cmp(reg, other_nil_value); __ j(equal, true_label); __ JumpIfSmi(reg, false_label); // Check for undetectable objects by looking in the bit field in @@ -1612,6 +1688,31 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { } +Condition LCodeGen::EmitIsString(Register input, + Register temp1, + Label* is_not_string) { + __ JumpIfSmi(input, is_not_string); + + Condition cond = masm_->IsObjectStringType(input, temp1, temp1); + + return cond; +} + + +void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp = ToRegister(instr->TempAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = EmitIsString(reg, temp, false_label); + + EmitBranch(true_block, false_block, true_cond); +} + + void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { Operand input = ToOperand(instr->InputAt(0)); @@ -1639,6 +1740,41 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { } +static Condition ComputeCompareCondition(Token::Value op) { + switch (op) { + case Token::EQ_STRICT: + case Token::EQ: + return equal; + case Token::LT: + return less; + case Token::GT: + return greater; + case Token::LTE: + return less_equal; + case Token::GTE: + return greater_equal; + default: + UNREACHABLE(); + return no_condition; + } +} + + +void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) { + Token::Value op = instr->op(); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Handle<Code> ic = CompareIC::GetUninitialized(op); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + + Condition condition = ComputeCompareCondition(op); + __ test(eax, Operand(eax)); + + EmitBranch(true_block, false_block, condition); +} + + static InstanceType TestType(HHasInstanceTypeAndBranch* instr) { InstanceType from = instr->from(); InstanceType to = instr->to(); @@ -1702,7 +1838,7 @@ void LCodeGen::DoHasCachedArrayIndexAndBranch( // Branches to a label or falls through with the answer in the z flag. Trashes -// the temp registers, but not the input. Only input and temp2 may alias. +// the temp registers, but not the input. void LCodeGen::EmitClassOfTest(Label* is_true, Label* is_false, Handle<String>class_name, @@ -1710,30 +1846,39 @@ void LCodeGen::EmitClassOfTest(Label* is_true, Register temp, Register temp2) { ASSERT(!input.is(temp)); - ASSERT(!temp.is(temp2)); // But input and temp2 may be the same register. + ASSERT(!input.is(temp2)); + ASSERT(!temp.is(temp2)); __ JumpIfSmi(input, is_false); - __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp); - __ j(below, is_false); - // Map is now in temp. - // Functions have class 'Function'. - __ CmpInstanceType(temp, FIRST_CALLABLE_SPEC_OBJECT_TYPE); if (class_name->IsEqualTo(CStrVector("Function"))) { - __ j(above_equal, is_true); + // Assuming the following assertions, we can use the same compares to test + // for both being a function type and being in the object type range. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp); + __ j(below, is_false); + __ j(equal, is_true); + __ CmpInstanceType(temp, LAST_SPEC_OBJECT_TYPE); + __ j(equal, is_true); } else { - __ j(above_equal, is_false); + // Faster code path to avoid two compares: subtract lower bound from the + // actual type and do a signed compare with the width of the type range. + __ mov(temp, FieldOperand(input, HeapObject::kMapOffset)); + __ mov(temp2, FieldOperand(temp, Map::kInstanceTypeOffset)); + __ sub(Operand(temp2), Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ cmpb(Operand(temp2), + static_cast<int8_t>(LAST_NONCALLABLE_SPEC_OBJECT_TYPE - + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ j(above, is_false); } + // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range. // Check if the constructor in the map is a function. __ mov(temp, FieldOperand(temp, Map::kConstructorOffset)); - - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - // Objects with a non-function constructor have class 'Object'. __ CmpObjectType(temp, JS_FUNCTION_TYPE, temp2); if (class_name->IsEqualTo(CStrVector("Object"))) { @@ -1762,12 +1907,7 @@ void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { Register input = ToRegister(instr->InputAt(0)); Register temp = ToRegister(instr->TempAt(0)); Register temp2 = ToRegister(instr->TempAt(1)); - if (input.is(temp)) { - // Swap. - Register swapper = temp; - temp = temp2; - temp2 = swapper; - } + Handle<String> class_name = instr->hydrogen()->class_name(); int true_block = chunk_->LookupDestination(instr->true_block_id()); @@ -1818,9 +1958,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { virtual void Generate() { codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } - + virtual LInstruction* instr() { return instr_; } Label* map_check() { return &map_check_; } - private: LInstanceOfKnownGlobal* instr_; Label map_check_; @@ -1843,7 +1982,9 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { Register map = ToRegister(instr->TempAt(0)); __ mov(map, FieldOperand(object, HeapObject::kMapOffset)); __ bind(deferred->map_check()); // Label for calculating code patching. - __ cmp(map, factory()->the_hole_value()); // Patched to cached map. + Handle<JSGlobalPropertyCell> cache_cell = + factory()->NewJSGlobalPropertyCell(factory()->the_hole_value()); + __ cmp(map, Operand::Cell(cache_cell)); // Patched to cached map. __ j(not_equal, &cache_miss, Label::kNear); __ mov(eax, factory()->the_hole_value()); // Patched to either true or false. __ jmp(&done); @@ -1891,7 +2032,7 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, // the stub. Register temp = ToRegister(instr->TempAt(0)); ASSERT(MacroAssembler::SafepointRegisterStackIndex(temp) == 0); - __ mov(InstanceofStub::right(), Immediate(instr->function())); + __ LoadHeapObject(InstanceofStub::right(), instr->function()); static const int kAdditionalDelta = 13; int delta = masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta; __ mov(temp, Immediate(delta)); @@ -1909,26 +2050,6 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, } -static Condition ComputeCompareCondition(Token::Value op) { - switch (op) { - case Token::EQ_STRICT: - case Token::EQ: - return equal; - case Token::LT: - return less; - case Token::GT: - return greater; - case Token::LTE: - return less_equal; - case Token::GTE: - return greater_equal; - default: - UNREACHABLE(); - return no_condition; - } -} - - void LCodeGen::DoCmpT(LCmpT* instr) { Token::Value op = instr->op(); @@ -1936,9 +2057,6 @@ void LCodeGen::DoCmpT(LCmpT* instr) { CallCode(ic, RelocInfo::CODE_TARGET, instr); Condition condition = ComputeCompareCondition(op); - if (op == Token::GT || op == Token::LTE) { - condition = ReverseCondition(condition); - } Label true_value, done; __ test(eax, Operand(eax)); __ j(condition, &true_value, Label::kNear); @@ -1962,6 +2080,17 @@ void LCodeGen::DoReturn(LReturn* instr) { } __ mov(esp, ebp); __ pop(ebp); + if (dynamic_frame_alignment_) { + Label aligned; + // Frame alignment marker (padding) is below arguments, + // and receiver, so its return-address-relative offset is + // (num_arguments + 2) words. + __ cmp(Operand(esp, (GetParameterCount() + 2) * kPointerSize), + Immediate(factory()->frame_alignment_marker())); + __ j(not_equal, &aligned); + __ Ret((GetParameterCount() + 2) * kPointerSize, ecx); + __ bind(&aligned); + } __ Ret((GetParameterCount() + 1) * kPointerSize, ecx); } @@ -1969,7 +2098,7 @@ void LCodeGen::DoReturn(LReturn* instr) { void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { Register result = ToRegister(instr->result()); __ mov(result, Operand::Cell(instr->hydrogen()->cell())); - if (instr->hydrogen()->check_hole_value()) { + if (instr->hydrogen()->RequiresHoleCheck()) { __ cmp(result, factory()->the_hole_value()); DeoptimizeIf(equal, instr->environment()); } @@ -1990,20 +2119,21 @@ void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) { void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { - Register value = ToRegister(instr->InputAt(0)); - Operand cell_operand = Operand::Cell(instr->hydrogen()->cell()); + Register value = ToRegister(instr->value()); + Handle<JSGlobalPropertyCell> cell_handle = instr->hydrogen()->cell(); // If the cell we are storing to contains the hole it could have // been deleted from the property dictionary. In that case, we need // to update the property details in the property dictionary to mark // it as no longer deleted. We deoptimize in that case. - if (instr->hydrogen()->check_hole_value()) { - __ cmp(cell_operand, factory()->the_hole_value()); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ cmp(Operand::Cell(cell_handle), factory()->the_hole_value()); DeoptimizeIf(equal, instr->environment()); } // Store the value. - __ mov(cell_operand, value); + __ mov(Operand::Cell(cell_handle), value); + // Cells are always rescanned, so no write barrier here. } @@ -2013,7 +2143,7 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) { ASSERT(ToRegister(instr->value()).is(eax)); __ mov(ecx, instr->name()); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr); @@ -2024,18 +2154,54 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { Register context = ToRegister(instr->context()); Register result = ToRegister(instr->result()); __ mov(result, ContextOperand(context, instr->slot_index())); + + if (instr->hydrogen()->RequiresHoleCheck()) { + __ cmp(result, factory()->the_hole_value()); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(equal, instr->environment()); + } else { + Label is_not_hole; + __ j(not_equal, &is_not_hole, Label::kNear); + __ mov(result, factory()->undefined_value()); + __ bind(&is_not_hole); + } + } } void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { Register context = ToRegister(instr->context()); Register value = ToRegister(instr->value()); - __ mov(ContextOperand(context, instr->slot_index()), value); - if (instr->needs_write_barrier()) { + + Label skip_assignment; + + Operand target = ContextOperand(context, instr->slot_index()); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ cmp(target, factory()->the_hole_value()); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(equal, instr->environment()); + } else { + __ j(not_equal, &skip_assignment, Label::kNear); + } + } + + __ mov(target, value); + if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; Register temp = ToRegister(instr->TempAt(0)); int offset = Context::SlotOffset(instr->slot_index()); - __ RecordWrite(context, offset, value, temp); + __ RecordWriteContextSlot(context, + offset, + value, + temp, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } + + __ bind(&skip_assignment); } @@ -2055,9 +2221,9 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, Register object, Handle<Map> type, Handle<String> name) { - LookupResult lookup; + LookupResult lookup(isolate()); type->LookupInDescriptors(NULL, *name, &lookup); - ASSERT(lookup.IsProperty() && + ASSERT(lookup.IsFound() && (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION)); if (lookup.type() == FIELD) { int index = lookup.GetLocalFieldIndexFromMap(*type); @@ -2073,7 +2239,24 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, } } else { Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type)); - LoadHeapObject(result, Handle<HeapObject>::cast(function)); + __ LoadHeapObject(result, function); + } +} + + +void LCodeGen::EmitPushTaggedOperand(LOperand* operand) { + ASSERT(!operand->IsDoubleRegister()); + if (operand->IsConstantOperand()) { + Handle<Object> object = ToHandle(LConstantOperand::cast(operand)); + if (object->IsSmi()) { + __ Push(Handle<Smi>::cast(object)); + } else { + __ PushHeapObject(Handle<HeapObject>::cast(object)); + } + } else if (operand->IsRegister()) { + __ push(ToRegister(operand)); + } else { + __ push(ToOperand(operand)); } } @@ -2251,16 +2434,14 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( LLoadKeyedFastDoubleElement* instr) { XMMRegister result = ToDoubleRegister(instr->result()); - if (instr->hydrogen()->RequiresHoleCheck()) { - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + - sizeof(kHoleNanLower32); - Operand hole_check_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), - FAST_DOUBLE_ELEMENTS, - offset); - __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); - DeoptimizeIf(equal, instr->environment()); - } + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + + sizeof(kHoleNanLower32); + Operand hole_check_operand = BuildFastArrayOperand( + instr->elements(), instr->key(), + FAST_DOUBLE_ELEMENTS, + offset); + __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); + DeoptimizeIf(equal, instr->environment()); Operand double_load_operand = BuildFastArrayOperand( instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, @@ -2330,6 +2511,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -2484,17 +2666,13 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { void LCodeGen::DoPushArgument(LPushArgument* instr) { LOperand* argument = instr->InputAt(0); - if (argument->IsConstantOperand()) { - __ push(ToImmediate(argument)); - } else { - __ push(ToOperand(argument)); - } + EmitPushTaggedOperand(argument); } void LCodeGen::DoThisFunction(LThisFunction* instr) { Register result = ToRegister(instr->result()); - __ mov(result, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ LoadHeapObject(result, instr->hydrogen()->closure()); } @@ -2530,41 +2708,53 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, int arity, LInstruction* instr, CallKind call_kind) { - // Change context if needed. - bool change_context = - (info()->closure()->context() != function->context()) || - scope()->contains_with() || - (scope()->num_heap_slots() > 0); - if (change_context) { - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - } else { - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - } - - // Set eax to arguments count if adaption is not needed. Assumes that eax - // is available to write to at this point. - if (!function->NeedsArgumentsAdaption()) { - __ mov(eax, arity); - } + bool can_invoke_directly = !function->NeedsArgumentsAdaption() || + function->shared()->formal_parameter_count() == arity; LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); - // Invoke function. - __ SetCallKind(ecx, call_kind); - if (*function == *info()->closure()) { - __ CallSelf(); + if (can_invoke_directly) { + __ LoadHeapObject(edi, function); + + // Change context if needed. + bool change_context = + (info()->closure()->context() != function->context()) || + scope()->contains_with() || + (scope()->num_heap_slots() > 0); + + if (change_context) { + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + } else { + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + } + + // Set eax to arguments count if adaption is not needed. Assumes that eax + // is available to write to at this point. + if (!function->NeedsArgumentsAdaption()) { + __ mov(eax, arity); + } + + // Invoke function directly. + __ SetCallKind(ecx, call_kind); + if (*function == *info()->closure()) { + __ CallSelf(); + } else { + __ call(FieldOperand(edi, JSFunction::kCodeEntryOffset)); + } + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); } else { - __ call(FieldOperand(edi, JSFunction::kCodeEntryOffset)); + // We need to adapt arguments. + SafepointGenerator generator( + this, pointers, Safepoint::kLazyDeopt); + ParameterCount count(arity); + __ InvokeFunction(function, count, CALL_FUNCTION, generator, call_kind); } - - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); } void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) { ASSERT(ToRegister(instr->result()).is(eax)); - __ mov(edi, instr->function()); CallKnownFunction(instr->function(), instr->arity(), instr, @@ -2647,6 +2837,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { virtual void Generate() { codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_); } + virtual LInstruction* instr() { return instr_; } private: LUnaryMathOperation* instr_; }; @@ -2778,72 +2969,90 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { } -void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { +void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) { XMMRegister xmm_scratch = xmm0; XMMRegister input_reg = ToDoubleRegister(instr->value()); + Register scratch = ToRegister(instr->temp()); ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); + + // Note that according to ECMA-262 15.8.2.13: + // Math.pow(-Infinity, 0.5) == Infinity + // Math.sqrt(-Infinity) == NaN + Label done, sqrt; + // Check base for -Infinity. According to IEEE-754, single-precision + // -Infinity has the highest 9 bits set and the lowest 23 bits cleared. + __ mov(scratch, 0xFF800000); + __ movd(xmm_scratch, scratch); + __ cvtss2sd(xmm_scratch, xmm_scratch); + __ ucomisd(input_reg, xmm_scratch); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &sqrt, Label::kNear); + __ j(carry, &sqrt, Label::kNear); + // If input is -Infinity, return Infinity. + __ xorps(input_reg, input_reg); + __ subsd(input_reg, xmm_scratch); + __ jmp(&done, Label::kNear); + + // Square root. + __ bind(&sqrt); __ xorps(xmm_scratch, xmm_scratch); __ addsd(input_reg, xmm_scratch); // Convert -0 to +0. __ sqrtsd(input_reg, input_reg); + __ bind(&done); } void LCodeGen::DoPower(LPower* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); - DoubleRegister result_reg = ToDoubleRegister(instr->result()); Representation exponent_type = instr->hydrogen()->right()->representation(); - - if (exponent_type.IsDouble()) { - // It is safe to use ebx directly since the instruction is marked - // as a call. - __ PrepareCallCFunction(4, ebx); - __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); - __ movdbl(Operand(esp, 1 * kDoubleSize), ToDoubleRegister(right)); - __ CallCFunction(ExternalReference::power_double_double_function(isolate()), - 4); + // Having marked this as a call, we can use any registers. + // Just make sure that the input/output registers are the expected ones. + ASSERT(!instr->InputAt(1)->IsDoubleRegister() || + ToDoubleRegister(instr->InputAt(1)).is(xmm1)); + ASSERT(!instr->InputAt(1)->IsRegister() || + ToRegister(instr->InputAt(1)).is(eax)); + ASSERT(ToDoubleRegister(instr->InputAt(0)).is(xmm2)); + ASSERT(ToDoubleRegister(instr->result()).is(xmm3)); + + if (exponent_type.IsTagged()) { + Label no_deopt; + __ JumpIfSmi(eax, &no_deopt); + __ CmpObjectType(eax, HEAP_NUMBER_TYPE, ecx); + DeoptimizeIf(not_equal, instr->environment()); + __ bind(&no_deopt); + MathPowStub stub(MathPowStub::TAGGED); + __ CallStub(&stub); } else if (exponent_type.IsInteger32()) { - // It is safe to use ebx directly since the instruction is marked - // as a call. - ASSERT(!ToRegister(right).is(ebx)); - __ PrepareCallCFunction(4, ebx); - __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); - __ mov(Operand(esp, 1 * kDoubleSize), ToRegister(right)); - __ CallCFunction(ExternalReference::power_double_int_function(isolate()), - 4); + MathPowStub stub(MathPowStub::INTEGER); + __ CallStub(&stub); } else { - ASSERT(exponent_type.IsTagged()); - CpuFeatures::Scope scope(SSE2); - Register right_reg = ToRegister(right); - - Label non_smi, call; - __ JumpIfNotSmi(right_reg, &non_smi); - __ SmiUntag(right_reg); - __ cvtsi2sd(result_reg, Operand(right_reg)); - __ jmp(&call); - - __ bind(&non_smi); - // It is safe to use ebx directly since the instruction is marked - // as a call. - ASSERT(!right_reg.is(ebx)); - __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , ebx); - DeoptimizeIf(not_equal, instr->environment()); - __ movdbl(result_reg, FieldOperand(right_reg, HeapNumber::kValueOffset)); - - __ bind(&call); - __ PrepareCallCFunction(4, ebx); - __ movdbl(Operand(esp, 0 * kDoubleSize), ToDoubleRegister(left)); - __ movdbl(Operand(esp, 1 * kDoubleSize), result_reg); - __ CallCFunction(ExternalReference::power_double_double_function(isolate()), - 4); + ASSERT(exponent_type.IsDouble()); + MathPowStub stub(MathPowStub::DOUBLE); + __ CallStub(&stub); } +} - // Return value is in st(0) on ia32. - // Store it into the (fixed) result register. - __ sub(Operand(esp), Immediate(kDoubleSize)); - __ fstp_d(Operand(esp, 0)); - __ movdbl(result_reg, Operand(esp, 0)); - __ add(Operand(esp), Immediate(kDoubleSize)); + +void LCodeGen::DoRandom(LRandom* instr) { + // Having marked this instruction as a call we can use any + // registers. + ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + ASSERT(ToRegister(instr->InputAt(0)).is(eax)); + + __ PrepareCallCFunction(1, ebx); + __ mov(eax, FieldOperand(eax, GlobalObject::kGlobalContextOffset)); + __ mov(Operand(esp, 0), eax); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // Convert 32 random bits in eax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm2, ebx); + __ movd(xmm1, eax); + __ cvtss2sd(xmm2, xmm2); + __ xorps(xmm1, xmm2); + __ subsd(xmm1, xmm2); } @@ -2878,6 +3087,14 @@ void LCodeGen::DoMathLog(LUnaryMathOperation* instr) { } +void LCodeGen::DoMathTan(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + void LCodeGen::DoMathCos(LUnaryMathOperation* instr) { ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); TranscendentalCacheStub stub(TranscendentalCache::COS, @@ -2908,15 +3125,15 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { case kMathSqrt: DoMathSqrt(instr); break; - case kMathPowHalf: - DoMathPowHalf(instr); - break; case kMathCos: DoMathCos(instr); break; case kMathSin: DoMathSin(instr); break; + case kMathTan: + DoMathTan(instr); + break; case kMathLog: DoMathLog(instr); break; @@ -2968,12 +3185,12 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { void LCodeGen::DoCallFunction(LCallFunction* instr) { ASSERT(ToRegister(instr->context()).is(esi)); + ASSERT(ToRegister(instr->function()).is(edi)); ASSERT(ToRegister(instr->result()).is(eax)); int arity = instr->arity(); - CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT); + CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); - __ Drop(1); } @@ -2992,7 +3209,6 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) { ASSERT(ToRegister(instr->result()).is(eax)); - __ mov(edi, instr->target()); CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION); } @@ -3002,9 +3218,9 @@ void LCodeGen::DoCallNew(LCallNew* instr) { ASSERT(ToRegister(instr->constructor()).is(edi)); ASSERT(ToRegister(instr->result()).is(eax)); - Handle<Code> builtin = isolate()->builtins()->JSConstructCall(); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ Set(eax, Immediate(instr->arity())); - CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr); + CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr); } @@ -3023,21 +3239,36 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { } // Do the store. + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; if (instr->is_in_object()) { __ mov(FieldOperand(object, offset), value); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { Register temp = ToRegister(instr->TempAt(0)); // Update the write barrier for the object for in-object properties. - __ RecordWrite(object, offset, value, temp); + __ RecordWriteField(object, + offset, + value, + temp, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } else { Register temp = ToRegister(instr->TempAt(0)); __ mov(temp, FieldOperand(object, JSObject::kPropertiesOffset)); __ mov(FieldOperand(temp, offset), value); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { // Update the write barrier for the properties array. // object is used as a scratch register. - __ RecordWrite(temp, offset, value, object); + __ RecordWriteField(temp, + offset, + value, + object, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } } @@ -3049,7 +3280,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { ASSERT(ToRegister(instr->value()).is(eax)); __ mov(ecx, instr->name()); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); @@ -3059,7 +3290,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { if (instr->index()->IsConstantOperand()) { __ cmp(ToOperand(instr->length()), - ToImmediate(LConstantOperand::cast(instr->index()))); + Immediate(ToInteger32(LConstantOperand::cast(instr->index())))); DeoptimizeIf(below_equal, instr->environment()); } else { __ cmp(ToRegister(instr->index()), ToOperand(instr->length())); @@ -3096,6 +3327,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -3128,13 +3360,21 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { } if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; // Compute address of modified element and store it into key register. __ lea(key, FieldOperand(elements, key, times_pointer_size, FixedArray::kHeaderSize)); - __ RecordWrite(elements, key, value); + __ RecordWrite(elements, + key, + value, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } @@ -3165,99 +3405,75 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(ecx)); ASSERT(ToRegister(instr->value()).is(eax)); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() : isolate()->builtins()->KeyedStoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); } +void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { + Register object_reg = ToRegister(instr->object()); + Register new_map_reg = ToRegister(instr->new_map_reg()); + + Handle<Map> from_map = instr->original_map(); + Handle<Map> to_map = instr->transitioned_map(); + ElementsKind from_kind = from_map->elements_kind(); + ElementsKind to_kind = to_map->elements_kind(); + + Label not_applicable; + __ cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map); + __ j(not_equal, ¬_applicable); + __ mov(new_map_reg, to_map); + if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + Register object_reg = ToRegister(instr->object()); + __ mov(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg); + // Write barrier. + ASSERT_NE(instr->temp_reg(), NULL); + __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, + ToRegister(instr->temp_reg()), kDontSaveFPRegs); + } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && + to_kind == FAST_DOUBLE_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(edx)); + ASSERT(new_map_reg.is(ebx)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), + RelocInfo::CODE_TARGET, instr); + } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(edx)); + ASSERT(new_map_reg.is(ebx)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(), + RelocInfo::CODE_TARGET, instr); + } else { + UNREACHABLE(); + } + __ bind(¬_applicable); +} + + void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { class DeferredStringCharCodeAt: public LDeferredCode { public: DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharCodeAt* instr_; }; - Register string = ToRegister(instr->string()); - Register index = ToRegister(instr->index()); - Register result = ToRegister(instr->result()); - DeferredStringCharCodeAt* deferred = new DeferredStringCharCodeAt(this, instr); - // Fetch the instance type of the receiver into result register. - __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); - __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); - - // We need special handling for indirect strings. - Label check_sequential; - __ test(result, Immediate(kIsIndirectStringMask)); - __ j(zero, &check_sequential, Label::kNear); - - // Dispatch on the indirect string shape: slice or cons. - Label cons_string; - __ test(result, Immediate(kSlicedNotConsMask)); - __ j(zero, &cons_string, Label::kNear); - - // Handle slices. - Label indirect_string_loaded; - __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset)); - __ SmiUntag(result); - __ add(index, Operand(result)); - __ mov(string, FieldOperand(string, SlicedString::kParentOffset)); - __ jmp(&indirect_string_loaded, Label::kNear); - - // Handle conses. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - __ bind(&cons_string); - __ cmp(FieldOperand(string, ConsString::kSecondOffset), - Immediate(factory()->empty_string())); - __ j(not_equal, deferred->entry()); - __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); - - __ bind(&indirect_string_loaded); - __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); - __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); - - // Check whether the string is sequential. The only non-sequential - // shapes we support have just been unwrapped above. - __ bind(&check_sequential); - STATIC_ASSERT(kSeqStringTag == 0); - __ test(result, Immediate(kStringRepresentationMask)); - __ j(not_zero, deferred->entry()); - - // Dispatch on the encoding: ASCII or two-byte. - Label ascii_string; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ test(result, Immediate(kStringEncodingMask)); - __ j(not_zero, &ascii_string, Label::kNear); - - // Two-byte string. - // Load the two-byte character code into the result register. - Label done; - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); - __ movzx_w(result, FieldOperand(string, - index, - times_2, - SeqTwoByteString::kHeaderSize)); - __ jmp(&done, Label::kNear); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ movzx_b(result, FieldOperand(string, - index, - times_1, - SeqAsciiString::kHeaderSize)); - __ bind(&done); + StringCharLoadGenerator::Generate(masm(), + factory(), + ToRegister(instr->string()), + ToRegister(instr->index()), + ToRegister(instr->result()), + deferred->entry()); __ bind(deferred->exit()); } @@ -3300,6 +3516,7 @@ void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharFromCode* instr_; }; @@ -3349,16 +3566,8 @@ void LCodeGen::DoStringLength(LStringLength* instr) { void LCodeGen::DoStringAdd(LStringAdd* instr) { - if (instr->left()->IsConstantOperand()) { - __ push(ToImmediate(instr->left())); - } else { - __ push(ToOperand(instr->left())); - } - if (instr->right()->IsConstantOperand()) { - __ push(ToImmediate(instr->right())); - } else { - __ push(ToOperand(instr->right())); - } + EmitPushTaggedOperand(instr->left()); + EmitPushTaggedOperand(instr->right()); StringAddStub stub(NO_STRING_CHECK_IN_STUB); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } @@ -3379,6 +3588,7 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) { DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); } + virtual LInstruction* instr() { return instr_; } private: LNumberTagI* instr_; }; @@ -3446,6 +3656,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } + virtual LInstruction* instr() { return instr_; } private: LNumberTagD* instr_; }; @@ -3506,8 +3717,10 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagD(Register input_reg, + Register temp_reg, XMMRegister result_reg, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env) { Label load_smi, done; @@ -3536,6 +3749,15 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, } // Heap number to XMM conversion. __ movdbl(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset)); + if (deoptimize_on_minus_zero) { + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ ucomisd(result_reg, xmm_scratch); + __ j(not_zero, &done, Label::kNear); + __ movmskpd(temp_reg, result_reg); + __ test_b(temp_reg, 1); + DeoptimizeIf(not_zero, env); + } __ jmp(&done, Label::kNear); // Smi to XMM conversion @@ -3547,16 +3769,6 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, } -class DeferredTaggedToI: public LDeferredCode { - public: - DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } - private: - LTaggedToI* instr_; -}; - - void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { Label done, heap_number; Register input_reg = ToRegister(instr->InputAt(0)); @@ -3589,8 +3801,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { __ cmp(Operand(input_reg), Immediate(kTooBigExponent)); __ j(less, &convert, Label::kNear); // Pop FPU stack before deoptimizing. - __ ffree(0); - __ fincstp(); + __ fstp(0); DeoptimizeIf(no_condition, instr->environment()); // Reserve space for 64 bit answer. @@ -3638,6 +3849,16 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { void LCodeGen::DoTaggedToI(LTaggedToI* instr) { + class DeferredTaggedToI: public LDeferredCode { + public: + DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LTaggedToI* instr_; + }; + LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); ASSERT(input->Equals(instr->result())); @@ -3659,14 +3880,23 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) { void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); + LOperand* temp = instr->TempAt(0); + ASSERT(temp == NULL || temp->IsRegister()); LOperand* result = instr->result(); ASSERT(result->IsDoubleRegister()); Register input_reg = ToRegister(input); XMMRegister result_reg = ToDoubleRegister(result); - EmitNumberUntagD(input_reg, result_reg, + bool deoptimize_on_minus_zero = + instr->hydrogen()->deoptimize_on_minus_zero(); + Register temp_reg = deoptimize_on_minus_zero ? ToRegister(temp) : no_reg; + + EmitNumberUntagD(input_reg, + temp_reg, + result_reg, instr->hydrogen()->deoptimize_on_undefined(), + deoptimize_on_minus_zero, instr->environment()); } @@ -3848,20 +4078,37 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckFunction(LCheckFunction* instr) { - ASSERT(instr->InputAt(0)->IsRegister()); - Operand operand = ToOperand(instr->InputAt(0)); - __ cmp(operand, instr->hydrogen()->target()); + Handle<JSFunction> target = instr->hydrogen()->target(); + if (isolate()->heap()->InNewSpace(*target)) { + Register reg = ToRegister(instr->value()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(target); + __ cmp(reg, Operand::Cell(cell)); + } else { + Operand operand = ToOperand(instr->value()); + __ cmp(operand, target); + } DeoptimizeIf(not_equal, instr->environment()); } +void LCodeGen::DoCheckMapCommon(Register reg, + Handle<Map> map, + CompareMapMode mode, + LEnvironment* env) { + Label success; + __ CompareMap(reg, map, &success, mode); + DeoptimizeIf(not_equal, env); + __ bind(&success); +} + + void LCodeGen::DoCheckMap(LCheckMap* instr) { LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); Register reg = ToRegister(input); - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - instr->hydrogen()->map()); - DeoptimizeIf(not_equal, instr->environment()); + Handle<Map> map = instr->hydrogen()->map(); + DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment()); } @@ -3913,17 +4160,6 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { } -void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) { - if (isolate()->heap()->InNewSpace(*object)) { - Handle<JSGlobalPropertyCell> cell = - isolate()->factory()->NewJSGlobalPropertyCell(object); - __ mov(result, Operand::Cell(cell)); - } else { - __ mov(result, object); - } -} - - void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Register reg = ToRegister(instr->TempAt(0)); @@ -3931,33 +4167,53 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Handle<JSObject> current_prototype = instr->prototype(); // Load prototype object. - LoadHeapObject(reg, current_prototype); + __ LoadHeapObject(reg, current_prototype); // Check prototype maps up to the holder. while (!current_prototype.is_identical_to(holder)) { - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle<Map>(current_prototype->map())); - DeoptimizeIf(not_equal, instr->environment()); + DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); + current_prototype = Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype())); // Load next prototype object. - LoadHeapObject(reg, current_prototype); + __ LoadHeapObject(reg, current_prototype); } // Check the holder map. - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle<Map>(current_prototype->map())); - DeoptimizeIf(not_equal, instr->environment()); + DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); } void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { ASSERT(ToRegister(instr->context()).is(esi)); - // Setup the parameters to the stub/runtime call. + Heap* heap = isolate()->heap(); + ElementsKind boilerplate_elements_kind = + instr->hydrogen()->boilerplate_elements_kind(); + + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to FAST_ELEMENTS. + if (boilerplate_elements_kind != FAST_ELEMENTS) { + __ LoadHeapObject(eax, instr->hydrogen()->boilerplate_object()); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + // Load the map's "bit field 2". We only need the first byte, + // but the following masking takes care of that anyway. + __ mov(ebx, FieldOperand(ebx, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ and_(ebx, Map::kElementsKindMask); + __ cmp(ebx, boilerplate_elements_kind << Map::kElementsKindShift); + DeoptimizeIf(not_equal, instr->environment()); + } + + // Set up the parameters to the stub/runtime call. __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(eax, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index()))); - __ push(Immediate(instr->hydrogen()->constant_elements())); + // Boilerplate already exists, constant elements are never accessed. + // Pass an empty fixed array. + __ push(Immediate(Handle<FixedArray>(heap->empty_fixed_array()))); // Pick the right runtime function or stub to call. int length = instr->hydrogen()->length(); @@ -3973,20 +4229,97 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr); } else { FastCloneShallowArrayStub::Mode mode = - FastCloneShallowArrayStub::CLONE_ELEMENTS; + boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS + ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ELEMENTS; FastCloneShallowArrayStub stub(mode, length); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } -void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { +void LCodeGen::EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset) { + ASSERT(!source.is(ecx)); + ASSERT(!result.is(ecx)); + + if (FLAG_debug_code) { + __ LoadHeapObject(ecx, object); + __ cmp(source, ecx); + __ Assert(equal, "Unexpected object literal boilerplate"); + } + + // Increase the offset so that subsequent objects end up right after + // this one. + int current_offset = *offset; + int size = object->map()->instance_size(); + *offset += size; + + // Copy object header. + ASSERT(object->properties()->length() == 0); + ASSERT(object->elements()->length() == 0 || + object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); + int inobject_properties = object->map()->inobject_properties(); + int header_size = size - inobject_properties * kPointerSize; + for (int i = 0; i < header_size; i += kPointerSize) { + __ mov(ecx, FieldOperand(source, i)); + __ mov(FieldOperand(result, current_offset + i), ecx); + } + + // Copy in-object properties. + for (int i = 0; i < inobject_properties; i++) { + int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + __ lea(ecx, Operand(result, *offset)); + __ mov(FieldOperand(result, total_offset), ecx); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(ecx, Handle<HeapObject>::cast(value)); + __ mov(FieldOperand(result, total_offset), ecx); + } else { + __ mov(FieldOperand(result, total_offset), Immediate(value)); + } + } +} + + +void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { ASSERT(ToRegister(instr->context()).is(esi)); - // Setup the parameters to the stub/runtime call. + int size = instr->hydrogen()->total_size(); + + // Allocate all objects that are part of the literal in one big + // allocation. This avoids multiple limit checks. + Label allocated, runtime_allocate; + __ AllocateInNewSpace(size, eax, ecx, edx, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ push(Immediate(Smi::FromInt(size))); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + + __ bind(&allocated); + int offset = 0; + __ LoadHeapObject(ebx, instr->hydrogen()->boilerplate()); + EmitDeepCopy(instr->hydrogen()->boilerplate(), eax, ebx, &offset); + ASSERT_EQ(size, offset); +} + + +void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { + ASSERT(ToRegister(instr->context()).is(esi)); + Handle<FixedArray> constant_properties = + instr->hydrogen()->constant_properties(); + + // Set up the parameters to the stub/runtime call. __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(eax, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index()))); - __ push(Immediate(instr->hydrogen()->constant_properties())); + __ push(Immediate(constant_properties)); int flags = instr->hydrogen()->fast_elements() ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags; @@ -3995,11 +4328,16 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { : ObjectLiteral::kNoFlags; __ push(Immediate(Smi::FromInt(flags))); - // Pick the right runtime function to call. + // Pick the right runtime function or stub to call. + int properties_count = constant_properties->length() / 2; if (instr->hydrogen()->depth() > 1) { CallRuntime(Runtime::kCreateObjectLiteral, 4, instr); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr); + } else { + FastCloneShallowObjectStub stub(properties_count); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } @@ -4072,8 +4410,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { Handle<SharedFunctionInfo> shared_info = instr->shared_info(); bool pretenure = instr->hydrogen()->pretenure(); if (!pretenure && shared_info->num_literals() == 0) { - FastNewClosureStub stub( - shared_info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(shared_info->language_mode()); __ push(Immediate(shared_info)); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } else { @@ -4089,11 +4426,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { void LCodeGen::DoTypeof(LTypeof* instr) { LOperand* input = instr->InputAt(1); - if (input->IsConstantOperand()) { - __ push(ToImmediate(input)); - } else { - __ push(ToOperand(input)); - } + EmitPushTaggedOperand(input); CallRuntime(Runtime::kTypeof, 1, instr); } @@ -4105,12 +4438,11 @@ void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); - Condition final_branch_condition = EmitTypeofIs(true_label, - false_label, - input, - instr->type_literal()); - - EmitBranch(true_block, false_block, final_branch_condition); + Condition final_branch_condition = + EmitTypeofIs(true_label, false_label, input, instr->type_literal()); + if (final_branch_condition != no_condition) { + EmitBranch(true_block, false_block, final_branch_condition); + } } @@ -4154,10 +4486,12 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = not_zero; } else if (type_name->Equals(heap()->function_symbol())) { - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ JumpIfSmi(input, false_label); - __ CmpObjectType(input, FIRST_CALLABLE_SPEC_OBJECT_TYPE, input); - final_branch_condition = above_equal; + __ CmpObjectType(input, JS_FUNCTION_TYPE, input); + __ j(equal, true_label); + __ CmpInstanceType(input, JS_FUNCTION_PROXY_TYPE); + final_branch_condition = equal; } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); @@ -4175,11 +4509,8 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = zero; } else { - final_branch_condition = not_equal; __ jmp(false_label); - // A dead branch instruction will be generated after this point. } - return final_branch_condition; } @@ -4219,9 +4550,7 @@ void LCodeGen::EnsureSpaceForLazyDeopt() { int patch_size = Deoptimizer::patch_size(); if (current_pc < last_lazy_deopt_pc_ + patch_size) { int padding_size = last_lazy_deopt_pc_ + patch_size - current_pc; - while (padding_size-- > 0) { - __ nop(); - } + __ Nop(padding_size); } last_lazy_deopt_pc_ = masm()->pc_offset(); } @@ -4245,11 +4574,7 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) { LOperand* obj = instr->object(); LOperand* key = instr->key(); __ push(ToOperand(obj)); - if (key->IsConstantOperand()) { - __ push(ToImmediate(key)); - } else { - __ push(ToOperand(key)); - } + EmitPushTaggedOperand(key); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); @@ -4281,6 +4606,7 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStackCheck* instr_; }; @@ -4345,16 +4671,8 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { void LCodeGen::DoIn(LIn* instr) { LOperand* obj = instr->object(); LOperand* key = instr->key(); - if (key->IsConstantOperand()) { - __ push(ToImmediate(key)); - } else { - __ push(ToOperand(key)); - } - if (obj->IsConstantOperand()) { - __ push(ToImmediate(obj)); - } else { - __ push(ToOperand(obj)); - } + EmitPushTaggedOperand(key); + EmitPushTaggedOperand(obj); ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h index d9554501dc..d86d48cd8c 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -58,6 +58,7 @@ class LCodeGen BASE_EMBEDDED { inlined_function_count_(0), scope_(info->scope()), status_(UNUSED), + dynamic_frame_alignment_(false), deferred_(8), osr_pc_offset_(-1), last_lazy_deopt_pc_(0), @@ -77,7 +78,13 @@ class LCodeGen BASE_EMBEDDED { Operand ToOperand(LOperand* op) const; Register ToRegister(LOperand* op) const; XMMRegister ToDoubleRegister(LOperand* op) const; - Immediate ToImmediate(LOperand* op); + + bool IsInteger32(LConstantOperand* op) const; + Immediate ToInteger32Immediate(LOperand* op) const { + return Immediate(ToInteger32(LConstantOperand::cast(op))); + } + + Handle<Object> ToHandle(LConstantOperand* op) const; // The operand denoting the second word (the one with a higher address) of // a double stack slot. @@ -103,6 +110,9 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); + void DoCheckMapCommon(Register reg, Handle<Map> map, + CompareMapMode mode, LEnvironment* env); + // Parallel move support. void DoParallelMove(LParallelMove* move); void DoGap(LGap* instr); @@ -130,8 +140,12 @@ class LCodeGen BASE_EMBEDDED { bool is_done() const { return status_ == DONE; } bool is_aborted() const { return status_ == ABORTED; } - int strict_mode_flag() const { - return info()->is_strict_mode() ? kStrictMode : kNonStrictMode; + StrictModeFlag strict_mode_flag() const { + return info()->is_classic_mode() ? kNonStrictMode : kStrictMode; + } + bool dynamic_frame_alignment() const { return dynamic_frame_alignment_; } + void set_dynamic_frame_alignment(bool value) { + dynamic_frame_alignment_ = value; } LChunk* chunk() const { return chunk_; } @@ -202,8 +216,6 @@ class LCodeGen BASE_EMBEDDED { LInstruction* instr, CallKind call_kind); - void LoadHeapObject(Register result, Handle<HeapObject> object); - void RecordSafepointWithLazyDeopt(LInstruction* instr, SafepointMode safepoint_mode); @@ -222,6 +234,8 @@ class LCodeGen BASE_EMBEDDED { Register ToRegister(int index) const; XMMRegister ToDoubleRegister(int index) const; int ToInteger32(LConstantOperand* op) const; + + double ToDouble(LConstantOperand* op) const; Operand BuildFastArrayOperand(LOperand* elements_pointer, LOperand* key, ElementsKind elements_kind, @@ -233,8 +247,8 @@ class LCodeGen BASE_EMBEDDED { void DoMathFloor(LUnaryMathOperation* instr); void DoMathRound(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr); - void DoMathPowHalf(LUnaryMathOperation* instr); void DoMathLog(LUnaryMathOperation* instr); + void DoMathTan(LUnaryMathOperation* instr); void DoMathCos(LUnaryMathOperation* instr); void DoMathSin(LUnaryMathOperation* instr); @@ -253,17 +267,20 @@ class LCodeGen BASE_EMBEDDED { static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); void EmitBranch(int left_block, int right_block, Condition cc); - void EmitCmpI(LOperand* left, LOperand* right); void EmitNumberUntagD(Register input, + Register temp, XMMRegister result, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env); // Emits optimized code for typeof x == "y". Modifies input register. // Returns the condition on which a final split to // true and false label should be made, to optimize fallthrough. - Condition EmitTypeofIs(Label* true_label, Label* false_label, - Register input, Handle<String> type_name); + Condition EmitTypeofIs(Label* true_label, + Label* false_label, + Register input, + Handle<String> type_name); // Emits optimized code for %_IsObject(x). Preserves input register. // Returns the condition on which a final split to @@ -273,6 +290,13 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsString(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsString(Register input, + Register temp1, + Label* is_not_string); + // Emits optimized code for %_IsConstructCall(). // Caller should branch on equal condition. void EmitIsConstructCall(Register temp); @@ -281,8 +305,20 @@ class LCodeGen BASE_EMBEDDED { Register object, Handle<Map> type, Handle<String> name); + + // Emits optimized code to deep-copy the contents of statically known + // object graphs (e.g. object literal boilerplate). + void EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset); + void EnsureSpaceForLazyDeopt(); + // Emits code for pushing either a tagged constant, a (non-double) + // register, or a stack slot operand. + void EmitPushTaggedOperand(LOperand* operand); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; @@ -295,6 +331,7 @@ class LCodeGen BASE_EMBEDDED { int inlined_function_count_; Scope* const scope_; Status status_; + bool dynamic_frame_alignment_; TranslationBuffer translations_; ZoneList<LDeferredCode*> deferred_; int osr_pc_offset_; @@ -338,16 +375,20 @@ class LCodeGen BASE_EMBEDDED { class LDeferredCode: public ZoneObject { public: explicit LDeferredCode(LCodeGen* codegen) - : codegen_(codegen), external_exit_(NULL) { + : codegen_(codegen), + external_exit_(NULL), + instruction_index_(codegen->current_instruction_) { codegen->AddDeferredCode(this); } virtual ~LDeferredCode() { } virtual void Generate() = 0; + virtual LInstruction* instr() = 0; - void SetExit(Label *exit) { external_exit_ = exit; } + void SetExit(Label* exit) { external_exit_ = exit; } Label* entry() { return &entry_; } Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; } + int instruction_index() const { return instruction_index_; } protected: LCodeGen* codegen() const { return codegen_; } @@ -358,6 +399,7 @@ class LDeferredCode: public ZoneObject { Label entry_; Label exit_; Label* external_exit_; + int instruction_index_; }; } } // namespace v8::internal diff --git a/deps/v8/src/ia32/lithium-gap-resolver-ia32.cc b/deps/v8/src/ia32/lithium-gap-resolver-ia32.cc index fcf1f91378..510d9f1dc6 100644 --- a/deps/v8/src/ia32/lithium-gap-resolver-ia32.cc +++ b/deps/v8/src/ia32/lithium-gap-resolver-ia32.cc @@ -303,14 +303,24 @@ void LGapResolver::EmitMove(int index) { } } else if (source->IsConstantOperand()) { - ASSERT(destination->IsRegister() || destination->IsStackSlot()); - Immediate src = cgen_->ToImmediate(source); + LConstantOperand* constant_source = LConstantOperand::cast(source); if (destination->IsRegister()) { Register dst = cgen_->ToRegister(destination); - __ Set(dst, src); + if (cgen_->IsInteger32(constant_source)) { + __ Set(dst, cgen_->ToInteger32Immediate(constant_source)); + } else { + __ LoadObject(dst, cgen_->ToHandle(constant_source)); + } } else { + ASSERT(destination->IsStackSlot()); Operand dst = cgen_->ToOperand(destination); - __ Set(dst, src); + if (cgen_->IsInteger32(constant_source)) { + __ Set(dst, cgen_->ToInteger32Immediate(constant_source)); + } else { + Register tmp = EnsureTempRegister(); + __ LoadObject(tmp, cgen_->ToHandle(constant_source)); + __ mov(dst, tmp); + } } } else if (source->IsDoubleRegister()) { diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index 3dc220d3d9..60f105014b 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -110,22 +110,17 @@ void LInstruction::PrintTo(StringStream* stream) { } -template<int R, int I, int T> -void LTemplateInstruction<R, I, T>::PrintDataTo(StringStream* stream) { +void LInstruction::PrintDataTo(StringStream* stream) { stream->Add("= "); - for (int i = 0; i < inputs_.length(); i++) { + for (int i = 0; i < InputCount(); i++) { if (i > 0) stream->Add(" "); - inputs_[i]->PrintTo(stream); + InputAt(i)->PrintTo(stream); } } -template<int R, int I, int T> -void LTemplateInstruction<R, I, T>::PrintOutputOperandTo(StringStream* stream) { - for (int i = 0; i < results_.length(); i++) { - if (i > 0) stream->Add(" "); - results_[i]->PrintTo(stream); - } +void LInstruction::PrintOutputOperandTo(StringStream* stream) { + if (HasResult()) result()->PrintTo(stream); } @@ -214,10 +209,11 @@ void LCmpIDAndBranch::PrintDataTo(StringStream* stream) { } -void LIsNullAndBranch::PrintDataTo(StringStream* stream) { +void LIsNilAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if "); InputAt(0)->PrintTo(stream); - stream->Add(is_strict() ? " === null" : " == null"); + stream->Add(kind() == kStrictEquality ? " === " : " == "); + stream->Add(nil() == kNullValue ? "null" : "undefined"); stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); } @@ -229,6 +225,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { } +void LIsStringAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_string("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LIsSmiAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_smi("); InputAt(0)->PrintTo(stream); @@ -243,6 +246,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { } +void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if string_compare("); + InputAt(1)->PrintTo(stream); + InputAt(2)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if has_instance_type("); InputAt(0)->PrintTo(stream); @@ -287,6 +298,12 @@ void LUnaryMathOperation::PrintDataTo(StringStream* stream) { } +void LMathPowHalf::PrintDataTo(StringStream* stream) { + stream->Add("/pow_half "); + InputAt(0)->PrintTo(stream); +} + + void LLoadContextSlot::PrintDataTo(StringStream* stream) { InputAt(0)->PrintTo(stream); stream->Add("[%d]", slot_index()); @@ -351,7 +368,11 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) { int LChunk::GetNextSpillIndex(bool is_double) { // Skip a slot if for a double-width slot. - if (is_double) spill_slot_count_++; + if (is_double) { + spill_slot_count_ |= 1; // Make it odd, so incrementing makes it even. + spill_slot_count_++; + num_double_slots_++; + } return spill_slot_count_++; } @@ -447,8 +468,14 @@ void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) { } +void LTransitionElementsKind::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add(" %p -> %p", *original_map(), *transitioned_map()); +} + + void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) { - LInstructionGap* gap = new LInstructionGap(block); + LInstructionGap* gap = new(graph_->zone()) LInstructionGap(block); int index = -1; if (instr->IsControl()) { instructions_.Add(gap); @@ -523,7 +550,7 @@ Representation LChunk::LookupLiteralRepresentation( LChunk* LChunkBuilder::Build() { ASSERT(is_unused()); - chunk_ = new LChunk(info(), graph()); + chunk_ = new(zone()) LChunk(info(), graph()); HPhase phase("Building chunk", chunk_); status_ = BUILDING; const ZoneList<HBasicBlock*>* blocks = graph()->blocks(); @@ -553,20 +580,15 @@ void LChunkBuilder::Abort(const char* format, ...) { } -LRegister* LChunkBuilder::ToOperand(Register reg) { - return LRegister::Create(Register::ToAllocationIndex(reg)); -} - - LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { - return new LUnallocated(LUnallocated::FIXED_REGISTER, - Register::ToAllocationIndex(reg)); + return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER, + Register::ToAllocationIndex(reg)); } LUnallocated* LChunkBuilder::ToUnallocated(XMMRegister reg) { - return new LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER, - XMMRegister::ToAllocationIndex(reg)); + return new(zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER, + XMMRegister::ToAllocationIndex(reg)); } @@ -581,30 +603,30 @@ LOperand* LChunkBuilder::UseFixedDouble(HValue* value, XMMRegister reg) { LOperand* LChunkBuilder::UseRegister(HValue* value) { - return Use(value, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); + return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); } LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) { return Use(value, - new LUnallocated(LUnallocated::MUST_HAVE_REGISTER, - LUnallocated::USED_AT_START)); + new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER, + LUnallocated::USED_AT_START)); } LOperand* LChunkBuilder::UseTempRegister(HValue* value) { - return Use(value, new LUnallocated(LUnallocated::WRITABLE_REGISTER)); + return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER)); } LOperand* LChunkBuilder::Use(HValue* value) { - return Use(value, new LUnallocated(LUnallocated::NONE)); + return Use(value, new(zone()) LUnallocated(LUnallocated::NONE)); } LOperand* LChunkBuilder::UseAtStart(HValue* value) { - return Use(value, new LUnallocated(LUnallocated::NONE, - LUnallocated::USED_AT_START)); + return Use(value, new(zone()) LUnallocated(LUnallocated::NONE, + LUnallocated::USED_AT_START)); } @@ -639,7 +661,7 @@ LOperand* LChunkBuilder::UseRegisterOrConstantAtStart(HValue* value) { LOperand* LChunkBuilder::UseAny(HValue* value) { return value->IsConstant() ? chunk_->DefineConstantOperand(HConstant::cast(value)) - : Use(value, new LUnallocated(LUnallocated::ANY)); + : Use(value, new(zone()) LUnallocated(LUnallocated::ANY)); } @@ -648,7 +670,7 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { HInstruction* instr = HInstruction::cast(value); VisitInstruction(instr); } - allocator_->RecordUse(value, operand); + operand->set_virtual_register(value->id()); return operand; } @@ -656,22 +678,17 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { template<int I, int T> LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result) { - allocator_->RecordDefinition(current_instruction_, result); + result->set_virtual_register(current_instruction_->id()); instr->set_result(result); return instr; } template<int I, int T> -LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr) { - return Define(instr, new LUnallocated(LUnallocated::NONE)); -} - - -template<int I, int T> LInstruction* LChunkBuilder::DefineAsRegister( LTemplateInstruction<1, I, T>* instr) { - return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); + return Define(instr, + new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); } @@ -679,14 +696,16 @@ template<int I, int T> LInstruction* LChunkBuilder::DefineAsSpilled( LTemplateInstruction<1, I, T>* instr, int index) { - return Define(instr, new LUnallocated(LUnallocated::FIXED_SLOT, index)); + return Define(instr, + new(zone()) LUnallocated(LUnallocated::FIXED_SLOT, index)); } template<int I, int T> LInstruction* LChunkBuilder::DefineSameAsFirst( LTemplateInstruction<1, I, T>* instr) { - return Define(instr, new LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT)); + return Define(instr, + new(zone()) LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT)); } @@ -707,7 +726,9 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - instr->set_environment(CreateEnvironment(hydrogen_env)); + int argument_index_accumulator = 0; + instr->set_environment(CreateEnvironment(hydrogen_env, + &argument_index_accumulator)); return instr; } @@ -737,7 +758,7 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, instr->MarkAsCall(); instr = AssignPointerMap(instr); - if (hinstr->HasSideEffects()) { + if (hinstr->HasObservableSideEffects()) { ASSERT(hinstr->next()->IsSimulate()); HSimulate* sim = HSimulate::cast(hinstr->next()); instr = SetInstructionPendingDeoptimizationEnvironment( @@ -749,7 +770,8 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, // Thus we still need to attach environment to this call even if // call sequence can not deoptimize eagerly. bool needs_environment = - (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || !hinstr->HasSideEffects(); + (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || + !hinstr->HasObservableSideEffects(); if (needs_environment && !instr->HasEnvironment()) { instr = AssignEnvironment(instr); } @@ -766,67 +788,48 @@ LInstruction* LChunkBuilder::MarkAsSaveDoubles(LInstruction* instr) { LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { ASSERT(!instr->HasPointerMap()); - instr->set_pointer_map(new LPointerMap(position_)); + instr->set_pointer_map(new(zone()) LPointerMap(position_)); return instr; } LUnallocated* LChunkBuilder::TempRegister() { - LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER); - allocator_->RecordTemporary(operand); + LUnallocated* operand = + new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER); + operand->set_virtual_register(allocator_->GetVirtualRegister()); + if (!allocator_->AllocationOk()) { + Abort("Not enough virtual registers (temps)."); + } return operand; } LOperand* LChunkBuilder::FixedTemp(Register reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { - return new LLabel(instr->block()); + return new(zone()) LLabel(instr->block()); } LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) { - return AssignEnvironment(new LDeoptimize); + return AssignEnvironment(new(zone()) LDeoptimize); } LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { - return AssignEnvironment(new LDeoptimize); -} - - -LInstruction* LChunkBuilder::DoBit(Token::Value op, - HBitwiseBinaryOperation* instr) { - if (instr->representation().IsInteger32()) { - ASSERT(instr->left()->representation().IsInteger32()); - ASSERT(instr->right()->representation().IsInteger32()); - - LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); - LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); - return DefineSameAsFirst(new LBitI(op, left, right)); - } else { - ASSERT(instr->representation().IsTagged()); - ASSERT(instr->left()->representation().IsTagged()); - ASSERT(instr->right()->representation().IsTagged()); - - LOperand* context = UseFixed(instr->context(), esi); - LOperand* left = UseFixed(instr->left(), edx); - LOperand* right = UseFixed(instr->right(), eax); - LArithmeticT* result = new LArithmeticT(op, context, left, right); - return MarkAsCall(DefineFixed(result, eax), instr); - } + return AssignEnvironment(new(zone()) LDeoptimize); } @@ -839,7 +842,7 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op, LOperand* context = UseFixed(instr->context(), esi); LOperand* left = UseFixed(instr->left(), edx); LOperand* right = UseFixed(instr->right(), eax); - LArithmeticT* result = new LArithmeticT(op, context, left, right); + LArithmeticT* result = new(zone()) LArithmeticT(op, context, left, right); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -873,7 +876,7 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op, } LInstruction* result = - DefineSameAsFirst(new LShiftI(op, left, right, does_deopt)); + DefineSameAsFirst(new(zone()) LShiftI(op, left, right, does_deopt)); return does_deopt ? AssignEnvironment(result) : result; } @@ -886,7 +889,7 @@ LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op, ASSERT(op != Token::MOD); LOperand* left = UseRegisterAtStart(instr->left()); LOperand* right = UseRegisterAtStart(instr->right()); - LArithmeticD* result = new LArithmeticD(op, left, right); + LArithmeticD* result = new(zone()) LArithmeticD(op, left, right); return DefineSameAsFirst(result); } @@ -906,7 +909,7 @@ LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op, LOperand* left_operand = UseFixed(left, edx); LOperand* right_operand = UseFixed(right, eax); LArithmeticT* result = - new LArithmeticT(op, context, left_operand, right_operand); + new(zone()) LArithmeticT(op, context, left_operand, right_operand); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -994,20 +997,25 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { +LEnvironment* LChunkBuilder::CreateEnvironment( + HEnvironment* hydrogen_env, + int* argument_index_accumulator) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); + LEnvironment* outer = + CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber); + ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); int value_count = hydrogen_env->length(); - LEnvironment* result = new LEnvironment(hydrogen_env->closure(), - ast_id, - hydrogen_env->parameter_count(), - argument_count_, - value_count, - outer); - int argument_index = 0; + LEnvironment* result = + new(zone()) LEnvironment(hydrogen_env->closure(), + hydrogen_env->is_arguments_adaptor(), + ast_id, + hydrogen_env->parameter_count(), + argument_count_, + value_count, + outer); + int argument_index = *argument_index_accumulator; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1016,56 +1024,69 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { if (value->IsArgumentsObject()) { op = NULL; } else if (value->IsPushArgument()) { - op = new LArgument(argument_index++); + op = new(zone()) LArgument(argument_index++); } else { op = UseAny(value); } result->AddValue(op, value->representation()); } + if (!hydrogen_env->is_arguments_adaptor()) { + *argument_index_accumulator = argument_index; + } + return result; } LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { - return new LGoto(instr->FirstSuccessor()->block_id()); + return new(zone()) LGoto(instr->FirstSuccessor()->block_id()); } LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { - HValue* v = instr->value(); - if (v->EmitAtUses()) { - ASSERT(v->IsConstant()); - ASSERT(!v->representation().IsDouble()); - HBasicBlock* successor = HConstant::cast(v)->ToBoolean() + HValue* value = instr->value(); + if (value->EmitAtUses()) { + ASSERT(value->IsConstant()); + ASSERT(!value->representation().IsDouble()); + HBasicBlock* successor = HConstant::cast(value)->ToBoolean() ? instr->FirstSuccessor() : instr->SecondSuccessor(); - return new LGoto(successor->block_id()); + return new(zone()) LGoto(successor->block_id()); + } + + // Untagged integers or doubles, smis and booleans don't require a + // deoptimization environment nor a temp register. + Representation rep = value->representation(); + HType type = value->type(); + if (!rep.IsTagged() || type.IsSmi() || type.IsBoolean()) { + return new(zone()) LBranch(UseRegister(value), NULL); } + ToBooleanStub::Types expected = instr->expected_input_types(); // We need a temporary register when we have to access the map *or* we have // no type info yet, in which case we handle all cases (including the ones // involving maps). bool needs_temp = expected.NeedsMap() || expected.IsEmpty(); LOperand* temp = needs_temp ? TempRegister() : NULL; - return AssignEnvironment(new LBranch(UseRegister(v), temp)); + return AssignEnvironment(new(zone()) LBranch(UseRegister(value), temp)); } LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) { ASSERT(instr->value()->representation().IsTagged()); LOperand* value = UseRegisterAtStart(instr->value()); - return new LCmpMapAndBranch(value); + return new(zone()) LCmpMapAndBranch(value); } LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) { - return DefineAsRegister(new LArgumentsLength(Use(length->value()))); + return DefineAsRegister(new(zone()) LArgumentsLength(Use(length->value()))); } LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) { - return DefineAsRegister(new LArgumentsElements); + return DefineAsRegister(new(zone()) LArgumentsElements); } @@ -1073,7 +1094,7 @@ LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LOperand* left = UseFixed(instr->left(), InstanceofStub::left()); LOperand* right = UseFixed(instr->right(), InstanceofStub::right()); LOperand* context = UseFixed(instr->context(), esi); - LInstanceOf* result = new LInstanceOf(context, left, right); + LInstanceOf* result = new(zone()) LInstanceOf(context, left, right); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1081,7 +1102,7 @@ LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( HInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* result = - new LInstanceOfKnownGlobal( + new(zone()) LInstanceOfKnownGlobal( UseFixed(instr->context(), esi), UseFixed(instr->left(), InstanceofStub::left()), FixedTemp(edi)); @@ -1095,11 +1116,11 @@ LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LOperand* length = UseFixed(instr->length(), ebx); LOperand* elements = UseFixed(instr->elements(), ecx); LOperand* temp = FixedTemp(edx); - LApplyArguments* result = new LApplyArguments(function, - receiver, - length, - elements, - temp); + LApplyArguments* result = new(zone()) LApplyArguments(function, + receiver, + length, + elements, + temp); return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY); } @@ -1107,42 +1128,44 @@ LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) { ++argument_count_; LOperand* argument = UseAny(instr->argument()); - return new LPushArgument(argument); + return new(zone()) LPushArgument(argument); } LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) { - return instr->HasNoUses() ? NULL : DefineAsRegister(new LThisFunction); + return instr->HasNoUses() + ? NULL + : DefineAsRegister(new(zone()) LThisFunction); } LInstruction* LChunkBuilder::DoContext(HContext* instr) { - return instr->HasNoUses() ? NULL : DefineAsRegister(new LContext); + return instr->HasNoUses() ? NULL : DefineAsRegister(new(zone()) LContext); } LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { LOperand* context = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LOuterContext(context)); + return DefineAsRegister(new(zone()) LOuterContext(context)); } LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) { LOperand* context = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LGlobalObject(context)); + return DefineAsRegister(new(zone()) LGlobalObject(context)); } LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) { LOperand* global_object = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LGlobalReceiver(global_object)); + return DefineAsRegister(new(zone()) LGlobalReceiver(global_object)); } LInstruction* LChunkBuilder::DoCallConstantFunction( HCallConstantFunction* instr) { argument_count_ -= instr->argument_count(); - return MarkAsCall(DefineFixed(new LCallConstantFunction, eax), instr); + return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, eax), instr); } @@ -1150,7 +1173,7 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* function = UseFixed(instr->function(), edi); argument_count_ -= instr->argument_count(); - LInvokeFunction* result = new LInvokeFunction(context, function); + LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY); } @@ -1162,17 +1185,25 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { ASSERT(instr->value()->representation().IsDouble()); LOperand* context = UseAny(instr->context()); // Not actually used. LOperand* input = UseRegisterAtStart(instr->value()); - LUnaryMathOperation* result = new LUnaryMathOperation(context, input); + LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(context, + input); return DefineSameAsFirst(result); } else if (op == kMathSin || op == kMathCos) { LOperand* context = UseFixed(instr->context(), esi); LOperand* input = UseFixedDouble(instr->value(), xmm1); - LUnaryMathOperation* result = new LUnaryMathOperation(context, input); + LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(context, + input); return MarkAsCall(DefineFixedDouble(result, xmm1), instr); } else { LOperand* input = UseRegisterAtStart(instr->value()); LOperand* context = UseAny(instr->context()); // Deferred use by MathAbs. - LUnaryMathOperation* result = new LUnaryMathOperation(context, input); + if (op == kMathPowHalf) { + LOperand* temp = TempRegister(); + LMathPowHalf* result = new(zone()) LMathPowHalf(context, input, temp); + return DefineSameAsFirst(result); + } + LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(context, + input); switch (op) { case kMathAbs: return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); @@ -1182,8 +1213,6 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { return AssignEnvironment(DefineAsRegister(result)); case kMathSqrt: return DefineSameAsFirst(result); - case kMathPowHalf: - return DefineSameAsFirst(result); default: UNREACHABLE(); return NULL; @@ -1197,7 +1226,7 @@ LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* key = UseFixed(instr->key(), ecx); argument_count_ -= instr->argument_count(); - LCallKeyed* result = new LCallKeyed(context, key); + LCallKeyed* result = new(zone()) LCallKeyed(context, key); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1205,7 +1234,7 @@ LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) { LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) { LOperand* context = UseFixed(instr->context(), esi); argument_count_ -= instr->argument_count(); - LCallNamed* result = new LCallNamed(context); + LCallNamed* result = new(zone()) LCallNamed(context); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1213,14 +1242,14 @@ LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) { LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) { LOperand* context = UseFixed(instr->context(), esi); argument_count_ -= instr->argument_count(); - LCallGlobal* result = new LCallGlobal(context); + LCallGlobal* result = new(zone()) LCallGlobal(context); return MarkAsCall(DefineFixed(result, eax), instr); } LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) { argument_count_ -= instr->argument_count(); - return MarkAsCall(DefineFixed(new LCallKnownGlobal, eax), instr); + return MarkAsCall(DefineFixed(new(zone()) LCallKnownGlobal, eax), instr); } @@ -1228,15 +1257,16 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* constructor = UseFixed(instr->constructor(), edi); argument_count_ -= instr->argument_count(); - LCallNew* result = new LCallNew(context, constructor); + LCallNew* result = new(zone()) LCallNew(context, constructor); return MarkAsCall(DefineFixed(result, eax), instr); } LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { LOperand* context = UseFixed(instr->context(), esi); + LOperand* function = UseFixed(instr->function(), edi); argument_count_ -= instr->argument_count(); - LCallFunction* result = new LCallFunction(context); + LCallFunction* result = new(zone()) LCallFunction(context, function); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1244,7 +1274,7 @@ LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { argument_count_ -= instr->argument_count(); LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(DefineFixed(new LCallRuntime(context), eax), instr); + return MarkAsCall(DefineFixed(new(zone()) LCallRuntime(context), eax), instr); } @@ -1263,8 +1293,26 @@ LInstruction* LChunkBuilder::DoShl(HShl* instr) { } -LInstruction* LChunkBuilder::DoBitAnd(HBitAnd* instr) { - return DoBit(Token::BIT_AND, instr); +LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + + LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); + LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); + return DefineSameAsFirst(new(zone()) LBitI(left, right)); + } else { + ASSERT(instr->representation().IsTagged()); + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + + LOperand* context = UseFixed(instr->context(), esi); + LOperand* left = UseFixed(instr->left(), edx); + LOperand* right = UseFixed(instr->right(), eax); + LArithmeticT* result = + new(zone()) LArithmeticT(instr->op(), context, left, right); + return MarkAsCall(DefineFixed(result, eax), instr); + } } @@ -1272,21 +1320,11 @@ LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) { ASSERT(instr->value()->representation().IsInteger32()); ASSERT(instr->representation().IsInteger32()); LOperand* input = UseRegisterAtStart(instr->value()); - LBitNotI* result = new LBitNotI(input); + LBitNotI* result = new(zone()) LBitNotI(input); return DefineSameAsFirst(result); } -LInstruction* LChunkBuilder::DoBitOr(HBitOr* instr) { - return DoBit(Token::BIT_OR, instr); -} - - -LInstruction* LChunkBuilder::DoBitXor(HBitXor* instr) { - return DoBit(Token::BIT_XOR, instr); -} - - LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { if (instr->representation().IsDouble()) { return DoArithmeticD(Token::DIV, instr); @@ -1296,7 +1334,7 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { LOperand* temp = FixedTemp(edx); LOperand* dividend = UseFixed(instr->left(), eax); LOperand* divisor = UseRegister(instr->right()); - LDivI* result = new LDivI(dividend, divisor, temp); + LDivI* result = new(zone()) LDivI(dividend, divisor, temp); return AssignEnvironment(DefineFixed(result, eax)); } else { ASSERT(instr->representation().IsTagged()); @@ -1314,7 +1352,8 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { if (instr->HasPowerOf2Divisor()) { ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); LOperand* value = UseRegisterAtStart(instr->left()); - LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL); + LModI* mod = + new(zone()) LModI(value, UseOrConstant(instr->right()), NULL); result = DefineSameAsFirst(mod); } else { // The temporary operand is necessary to ensure that right is @@ -1322,7 +1361,7 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { LOperand* temp = FixedTemp(edx); LOperand* value = UseFixed(instr->left(), eax); LOperand* divisor = UseRegister(instr->right()); - LModI* mod = new LModI(value, divisor, temp); + LModI* mod = new(zone()) LModI(value, divisor, temp); result = DefineFixed(mod, edx); } @@ -1339,7 +1378,7 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { // TODO(fschneider): Allow any register as input registers. LOperand* left = UseFixedDouble(instr->left(), xmm2); LOperand* right = UseFixedDouble(instr->right(), xmm1); - LArithmeticD* result = new LArithmeticD(Token::MOD, left, right); + LArithmeticD* result = new(zone()) LArithmeticD(Token::MOD, left, right); return MarkAsCall(DefineFixedDouble(result, xmm1), instr); } } @@ -1355,8 +1394,12 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) { if (instr->CheckFlag(HValue::kBailoutOnMinusZero)) { temp = TempRegister(); } - LMulI* mul = new LMulI(left, right, temp); - return AssignEnvironment(DefineSameAsFirst(mul)); + LMulI* mul = new(zone()) LMulI(left, right, temp); + if (instr->CheckFlag(HValue::kCanOverflow) || + instr->CheckFlag(HValue::kBailoutOnMinusZero)) { + AssignEnvironment(mul); + } + return DefineSameAsFirst(mul); } else if (instr->representation().IsDouble()) { return DoArithmeticD(Token::MUL, instr); } else { @@ -1372,7 +1415,7 @@ LInstruction* LChunkBuilder::DoSub(HSub* instr) { ASSERT(instr->right()->representation().IsInteger32()); LOperand* left = UseRegisterAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); - LSubI* sub = new LSubI(left, right); + LSubI* sub = new(zone()) LSubI(left, right); LInstruction* result = DefineSameAsFirst(sub); if (instr->CheckFlag(HValue::kCanOverflow)) { result = AssignEnvironment(result); @@ -1393,7 +1436,7 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { ASSERT(instr->right()->representation().IsInteger32()); LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); - LAddI* add = new LAddI(left, right); + LAddI* add = new(zone()) LAddI(left, right); LInstruction* result = DefineSameAsFirst(add); if (instr->CheckFlag(HValue::kCanOverflow)) { result = AssignEnvironment(result); @@ -1414,25 +1457,32 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { // We need to use fixed result register for the call. Representation exponent_type = instr->right()->representation(); ASSERT(instr->left()->representation().IsDouble()); - LOperand* left = UseFixedDouble(instr->left(), xmm1); + LOperand* left = UseFixedDouble(instr->left(), xmm2); LOperand* right = exponent_type.IsDouble() ? - UseFixedDouble(instr->right(), xmm2) : + UseFixedDouble(instr->right(), xmm1) : UseFixed(instr->right(), eax); - LPower* result = new LPower(left, right); + LPower* result = new(zone()) LPower(left, right); return MarkAsCall(DefineFixedDouble(result, xmm3), instr, CAN_DEOPTIMIZE_EAGERLY); } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseFixed(instr->global_object(), eax); + LRandom* result = new(zone()) LRandom(global_object); + return MarkAsCall(DefineFixedDouble(result, xmm1), instr); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { - Token::Value op = instr->token(); ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); - bool reversed = (op == Token::GT || op == Token::LTE); LOperand* context = UseFixed(instr->context(), esi); - LOperand* left = UseFixed(instr->left(), reversed ? eax : edx); - LOperand* right = UseFixed(instr->right(), reversed ? edx : eax); - LCmpT* result = new LCmpT(context, left, right); + LOperand* left = UseFixed(instr->left(), edx); + LOperand* right = UseFixed(instr->right(), eax); + LCmpT* result = new(zone()) LCmpT(context, left, right); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1443,16 +1493,23 @@ LInstruction* LChunkBuilder::DoCompareIDAndBranch( if (r.IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); - return new LCmpIDAndBranch(left, right); + return new(zone()) LCmpIDAndBranch(left, right); } else { ASSERT(r.IsDouble()); ASSERT(instr->left()->representation().IsDouble()); ASSERT(instr->right()->representation().IsDouble()); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); - return new LCmpIDAndBranch(left, right); + LOperand* left; + LOperand* right; + if (instr->left()->IsConstant() && instr->right()->IsConstant()) { + left = UseRegisterOrConstantAtStart(instr->left()); + right = UseRegisterOrConstantAtStart(instr->right()); + } else { + left = UseRegisterAtStart(instr->left()); + right = UseRegisterAtStart(instr->right()); + } + return new(zone()) LCmpIDAndBranch(left, right); } } @@ -1461,49 +1518,73 @@ LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( HCompareObjectEqAndBranch* instr) { LOperand* left = UseRegisterAtStart(instr->left()); LOperand* right = UseAtStart(instr->right()); - return new LCmpObjectEqAndBranch(left, right); + return new(zone()) LCmpObjectEqAndBranch(left, right); } LInstruction* LChunkBuilder::DoCompareConstantEqAndBranch( HCompareConstantEqAndBranch* instr) { - return new LCmpConstantEqAndBranch(UseRegisterAtStart(instr->value())); + return new(zone()) LCmpConstantEqAndBranch( + UseRegisterAtStart(instr->value())); } -LInstruction* LChunkBuilder::DoIsNullAndBranch(HIsNullAndBranch* instr) { +LInstruction* LChunkBuilder::DoIsNilAndBranch(HIsNilAndBranch* instr) { // We only need a temp register for non-strict compare. - LOperand* temp = instr->is_strict() ? NULL : TempRegister(); - return new LIsNullAndBranch(UseRegisterAtStart(instr->value()), temp); + LOperand* temp = instr->kind() == kStrictEquality ? NULL : TempRegister(); + return new(zone()) LIsNilAndBranch(UseRegisterAtStart(instr->value()), temp); } LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); LOperand* temp = TempRegister(); - return new LIsObjectAndBranch(UseRegister(instr->value()), temp); + return new(zone()) LIsObjectAndBranch(UseRegister(instr->value()), temp); +} + + +LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* temp = TempRegister(); + return new LIsStringAndBranch(UseRegister(instr->value()), temp); } LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LIsSmiAndBranch(Use(instr->value())); + return new(zone()) LIsSmiAndBranch(Use(instr->value())); } LInstruction* LChunkBuilder::DoIsUndetectableAndBranch( HIsUndetectableAndBranch* instr) { ASSERT(instr ->value()->representation().IsTagged()); - return new LIsUndetectableAndBranch(UseRegisterAtStart(instr->value()), - TempRegister()); + return new(zone()) LIsUndetectableAndBranch( + UseRegisterAtStart(instr->value()), TempRegister()); +} + + +LInstruction* LChunkBuilder::DoStringCompareAndBranch( + HStringCompareAndBranch* instr) { + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + LOperand* context = UseFixed(instr->context(), esi); + LOperand* left = UseFixed(instr->left(), edx); + LOperand* right = UseFixed(instr->right(), eax); + + LStringCompareAndBranch* result = new + LStringCompareAndBranch(context, left, right); + + return MarkAsCall(result, instr); } LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch( HHasInstanceTypeAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LHasInstanceTypeAndBranch(UseRegisterAtStart(instr->value()), - TempRegister()); + return new(zone()) LHasInstanceTypeAndBranch( + UseRegisterAtStart(instr->value()), + TempRegister()); } @@ -1512,14 +1593,14 @@ LInstruction* LChunkBuilder::DoGetCachedArrayIndex( ASSERT(instr->value()->representation().IsTagged()); LOperand* value = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LGetCachedArrayIndex(value)); + return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value)); } LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( HHasCachedArrayIndexAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LHasCachedArrayIndexAndBranch( + return new(zone()) LHasCachedArrayIndexAndBranch( UseRegisterAtStart(instr->value())); } @@ -1527,40 +1608,40 @@ LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( LInstruction* LChunkBuilder::DoClassOfTestAndBranch( HClassOfTestAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - return new LClassOfTestAndBranch(UseTempRegister(instr->value()), - TempRegister(), - TempRegister()); + return new(zone()) LClassOfTestAndBranch(UseRegister(instr->value()), + TempRegister(), + TempRegister()); } LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) { LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LJSArrayLength(array)); + return DefineAsRegister(new(zone()) LJSArrayLength(array)); } LInstruction* LChunkBuilder::DoFixedArrayBaseLength( HFixedArrayBaseLength* instr) { LOperand* array = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LFixedArrayBaseLength(array)); + return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array)); } LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LOperand* object = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LElementsKind(object)); + return DefineAsRegister(new(zone()) LElementsKind(object)); } LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LOperand* object = UseRegister(instr->value()); - LValueOf* result = new LValueOf(object, TempRegister()); - return AssignEnvironment(DefineSameAsFirst(result)); + LValueOf* result = new(zone()) LValueOf(object, TempRegister()); + return DefineSameAsFirst(result); } LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { - return AssignEnvironment(new LBoundsCheck( + return AssignEnvironment(new(zone()) LBoundsCheck( UseRegisterOrConstantAtStart(instr->index()), UseAtStart(instr->length()))); } @@ -1576,7 +1657,7 @@ LInstruction* LChunkBuilder::DoAbnormalExit(HAbnormalExit* instr) { LInstruction* LChunkBuilder::DoThrow(HThrow* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* value = UseFixed(instr->value(), eax); - return MarkAsCall(new LThrow(context, value), instr); + return MarkAsCall(new(zone()) LThrow(context, value), instr); } @@ -1599,7 +1680,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { if (from.IsTagged()) { if (to.IsDouble()) { LOperand* value = UseRegister(instr->value()); - LNumberUntagD* res = new LNumberUntagD(value); + // Temp register only necessary for minus zero check. + LOperand* temp = instr->deoptimize_on_minus_zero() + ? TempRegister() + : NULL; + LNumberUntagD* res = new(zone()) LNumberUntagD(value, temp); return AssignEnvironment(DefineAsRegister(res)); } else { ASSERT(to.IsInteger32()); @@ -1611,10 +1696,10 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { (truncating && CpuFeatures::IsSupported(SSE3)) ? NULL : FixedTemp(xmm1); - LTaggedToI* res = new LTaggedToI(value, xmm_temp); + LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp); return AssignEnvironment(DefineSameAsFirst(res)); } else { - return DefineSameAsFirst(new LSmiUntag(value, needs_check)); + return DefineSameAsFirst(new(zone()) LSmiUntag(value, needs_check)); } } } else if (from.IsDouble()) { @@ -1624,7 +1709,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { // Make sure that temp and result_temp are different registers. LUnallocated* result_temp = TempRegister(); - LNumberTagD* result = new LNumberTagD(value, temp); + LNumberTagD* result = new(zone()) LNumberTagD(value, temp); return AssignPointerMap(Define(result, result_temp)); } else { ASSERT(to.IsInteger32()); @@ -1633,21 +1718,23 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LOperand* value = needs_temp ? UseTempRegister(instr->value()) : UseRegister(instr->value()); LOperand* temp = needs_temp ? TempRegister() : NULL; - return AssignEnvironment(DefineAsRegister(new LDoubleToI(value, temp))); + return AssignEnvironment( + DefineAsRegister(new(zone()) LDoubleToI(value, temp))); } } else if (from.IsInteger32()) { if (to.IsTagged()) { HValue* val = instr->value(); LOperand* value = UseRegister(val); if (val->HasRange() && val->range()->IsInSmiRange()) { - return DefineSameAsFirst(new LSmiTag(value)); + return DefineSameAsFirst(new(zone()) LSmiTag(value)); } else { - LNumberTagI* result = new LNumberTagI(value); + LNumberTagI* result = new(zone()) LNumberTagI(value); return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); } } else { ASSERT(to.IsDouble()); - return DefineAsRegister(new LInteger32ToDouble(Use(instr->value()))); + return DefineAsRegister( + new(zone()) LInteger32ToDouble(Use(instr->value()))); } } UNREACHABLE(); @@ -1657,40 +1744,46 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) { LOperand* value = UseAtStart(instr->value()); - return AssignEnvironment(new LCheckNonSmi(value)); + return AssignEnvironment(new(zone()) LCheckNonSmi(value)); } LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) { LOperand* value = UseRegisterAtStart(instr->value()); LOperand* temp = TempRegister(); - LCheckInstanceType* result = new LCheckInstanceType(value, temp); + LCheckInstanceType* result = new(zone()) LCheckInstanceType(value, temp); return AssignEnvironment(result); } LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) { LOperand* temp = TempRegister(); - LCheckPrototypeMaps* result = new LCheckPrototypeMaps(temp); + LCheckPrototypeMaps* result = new(zone()) LCheckPrototypeMaps(temp); return AssignEnvironment(result); } LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) { LOperand* value = UseAtStart(instr->value()); - return AssignEnvironment(new LCheckSmi(value)); + return AssignEnvironment(new(zone()) LCheckSmi(value)); } LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) { - LOperand* value = UseAtStart(instr->value()); - return AssignEnvironment(new LCheckFunction(value)); + // If the target is in new space, we'll emit a global cell compare and so + // want the value in a register. If the target gets promoted before we + // emit code, we will still get the register but will do an immediate + // compare instead of the cell compare. This is safe. + LOperand* value = Isolate::Current()->heap()->InNewSpace(*instr->target()) + ? UseRegisterAtStart(instr->value()) + : UseAtStart(instr->value()); + return AssignEnvironment(new(zone()) LCheckFunction(value)); } LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) { LOperand* value = UseRegisterAtStart(instr->value()); - LCheckMap* result = new LCheckMap(value); + LCheckMap* result = new(zone()) LCheckMap(value); return AssignEnvironment(result); } @@ -1700,17 +1793,17 @@ LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { Representation input_rep = value->representation(); if (input_rep.IsDouble()) { LOperand* reg = UseRegister(value); - return DefineAsRegister(new LClampDToUint8(reg)); + return DefineAsRegister(new(zone()) LClampDToUint8(reg)); } else if (input_rep.IsInteger32()) { LOperand* reg = UseFixed(value, eax); - return DefineFixed(new LClampIToUint8(reg), eax); + return DefineFixed(new(zone()) LClampIToUint8(reg), eax); } else { ASSERT(input_rep.IsTagged()); LOperand* reg = UseFixed(value, eax); // Register allocator doesn't (yet) support allocation of double // temps. Reserve xmm1 explicitly. LOperand* temp = FixedTemp(xmm1); - LClampTToUint8* result = new LClampTToUint8(reg, temp); + LClampTToUint8* result = new(zone()) LClampTToUint8(reg, temp); return AssignEnvironment(DefineFixed(result, eax)); } } @@ -1725,7 +1818,7 @@ LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) { LOperand* reg = UseRegister(value); LOperand* temp_reg = CpuFeatures::IsSupported(SSE3) ? NULL : TempRegister(); - result = DefineAsRegister(new LDoubleToI(reg, temp_reg)); + result = DefineAsRegister(new(zone()) LDoubleToI(reg, temp_reg)); } else if (input_rep.IsInteger32()) { // Canonicalization should already have removed the hydrogen instruction in // this case, since it is a noop. @@ -1738,29 +1831,29 @@ LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) { // temps. Reserve xmm1 explicitly. LOperand* xmm_temp = CpuFeatures::IsSupported(SSE3) ? NULL : FixedTemp(xmm1); - result = DefineSameAsFirst(new LTaggedToI(reg, xmm_temp)); + result = DefineSameAsFirst(new(zone()) LTaggedToI(reg, xmm_temp)); } return AssignEnvironment(result); } LInstruction* LChunkBuilder::DoReturn(HReturn* instr) { - return new LReturn(UseFixed(instr->value(), eax)); + return new(zone()) LReturn(UseFixed(instr->value(), eax)); } LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { Representation r = instr->representation(); if (r.IsInteger32()) { - return DefineAsRegister(new LConstantI); + return DefineAsRegister(new(zone()) LConstantI); } else if (r.IsDouble()) { double value = instr->DoubleValue(); LOperand* temp = (BitCast<uint64_t, double>(value) != 0) ? TempRegister() : NULL; - return DefineAsRegister(new LConstantD(temp)); + return DefineAsRegister(new(zone()) LConstantD(temp)); } else if (r.IsTagged()) { - return DefineAsRegister(new LConstantT); + return DefineAsRegister(new(zone()) LConstantT); } else { UNREACHABLE(); return NULL; @@ -1769,8 +1862,8 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { - LLoadGlobalCell* result = new LLoadGlobalCell; - return instr->check_hole_value() + LLoadGlobalCell* result = new(zone()) LLoadGlobalCell; + return instr->RequiresHoleCheck() ? AssignEnvironment(DefineAsRegister(result)) : DefineAsRegister(result); } @@ -1779,15 +1872,16 @@ LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* global_object = UseFixed(instr->global_object(), eax); - LLoadGlobalGeneric* result = new LLoadGlobalGeneric(context, global_object); + LLoadGlobalGeneric* result = + new(zone()) LLoadGlobalGeneric(context, global_object); return MarkAsCall(DefineFixed(result, eax), instr); } LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) { LStoreGlobalCell* result = - new LStoreGlobalCell(UseRegisterAtStart(instr->value())); - return instr->check_hole_value() ? AssignEnvironment(result) : result; + new(zone()) LStoreGlobalCell(UseRegister(instr->value())); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } @@ -1796,38 +1890,39 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) { LOperand* global_object = UseFixed(instr->global_object(), edx); LOperand* value = UseFixed(instr->value(), eax); LStoreGlobalGeneric* result = - new LStoreGlobalGeneric(context, global_object, value); + new(zone()) LStoreGlobalGeneric(context, global_object, value); return MarkAsCall(result, instr); } LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { LOperand* context = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LLoadContextSlot(context)); + LInstruction* result = + DefineAsRegister(new(zone()) LLoadContextSlot(context)); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { - LOperand* context; LOperand* value; LOperand* temp; + LOperand* context = UseRegister(instr->context()); if (instr->NeedsWriteBarrier()) { - context = UseTempRegister(instr->context()); value = UseTempRegister(instr->value()); temp = TempRegister(); } else { - context = UseRegister(instr->context()); value = UseRegister(instr->value()); temp = NULL; } - return new LStoreContextSlot(context, value, temp); + LInstruction* result = new(zone()) LStoreContextSlot(context, value, temp); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { ASSERT(instr->representation().IsTagged()); LOperand* obj = UseRegisterAtStart(instr->object()); - return DefineAsRegister(new LLoadNamedField(obj)); + return DefineAsRegister(new(zone()) LLoadNamedField(obj)); } @@ -1838,12 +1933,12 @@ LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( if (instr->need_generic()) { LOperand* obj = UseFixed(instr->object(), eax); LLoadNamedFieldPolymorphic* result = - new LLoadNamedFieldPolymorphic(context, obj); + new(zone()) LLoadNamedFieldPolymorphic(context, obj); return MarkAsCall(DefineFixed(result, eax), instr); } else { LOperand* obj = UseRegisterAtStart(instr->object()); LLoadNamedFieldPolymorphic* result = - new LLoadNamedFieldPolymorphic(context, obj); + new(zone()) LLoadNamedFieldPolymorphic(context, obj); return AssignEnvironment(DefineAsRegister(result)); } } @@ -1852,7 +1947,7 @@ LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* object = UseFixed(instr->object(), eax); - LLoadNamedGeneric* result = new LLoadNamedGeneric(context, object); + LLoadNamedGeneric* result = new(zone()) LLoadNamedGeneric(context, object); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1860,21 +1955,21 @@ LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LInstruction* LChunkBuilder::DoLoadFunctionPrototype( HLoadFunctionPrototype* instr) { return AssignEnvironment(DefineAsRegister( - new LLoadFunctionPrototype(UseRegister(instr->function()), - TempRegister()))); + new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()), + TempRegister()))); } LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) { LOperand* input = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LLoadElements(input)); + return DefineAsRegister(new(zone()) LLoadElements(input)); } LInstruction* LChunkBuilder::DoLoadExternalArrayPointer( HLoadExternalArrayPointer* instr) { LOperand* input = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LLoadExternalArrayPointer(input)); + return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input)); } @@ -1884,8 +1979,9 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastElement( ASSERT(instr->key()->representation().IsInteger32()); LOperand* obj = UseRegisterAtStart(instr->object()); LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); - return AssignEnvironment(DefineAsRegister(result)); + LLoadKeyedFastElement* result = new(zone()) LLoadKeyedFastElement(obj, key); + if (instr->RequiresHoleCheck()) AssignEnvironment(result); + return DefineAsRegister(result); } @@ -1896,7 +1992,7 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LOperand* elements = UseRegisterAtStart(instr->elements()); LOperand* key = UseRegisterOrConstantAtStart(instr->key()); LLoadKeyedFastDoubleElement* result = - new LLoadKeyedFastDoubleElement(elements, key); + new(zone()) LLoadKeyedFastDoubleElement(elements, key); return AssignEnvironment(DefineAsRegister(result)); } @@ -1904,19 +2000,18 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ElementsKind elements_kind = instr->elements_kind(); - Representation representation(instr->representation()); ASSERT( - (representation.IsInteger32() && + (instr->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegisterOrConstant(instr->key()); LLoadKeyedSpecializedArrayElement* result = - new LLoadKeyedSpecializedArrayElement(external_pointer, + new(zone()) LLoadKeyedSpecializedArrayElement(external_pointer, key); LInstruction* load_instr = DefineAsRegister(result); // An unsigned int array load might overflow and cause a deopt, make sure it @@ -1932,7 +2027,8 @@ LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { LOperand* object = UseFixed(instr->object(), edx); LOperand* key = UseFixed(instr->key(), eax); - LLoadKeyedGeneric* result = new LLoadKeyedGeneric(context, object, key); + LLoadKeyedGeneric* result = + new(zone()) LLoadKeyedGeneric(context, object, key); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -1944,15 +2040,14 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( ASSERT(instr->object()->representation().IsTagged()); ASSERT(instr->key()->representation().IsInteger32()); - LOperand* obj = UseTempRegister(instr->object()); + LOperand* obj = UseRegister(instr->object()); LOperand* val = needs_write_barrier ? UseTempRegister(instr->value()) : UseRegisterAtStart(instr->value()); LOperand* key = needs_write_barrier ? UseTempRegister(instr->key()) : UseRegisterOrConstantAtStart(instr->key()); - - return AssignEnvironment(new LStoreKeyedFastElement(obj, key, val)); + return new(zone()) LStoreKeyedFastElement(obj, key, val); } @@ -1966,19 +2061,18 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LOperand* val = UseTempRegister(instr->value()); LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - return new LStoreKeyedFastDoubleElement(elements, key, val); + return new(zone()) LStoreKeyedFastDoubleElement(elements, key, val); } LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { - Representation representation(instr->value()->representation()); ElementsKind elements_kind = instr->elements_kind(); ASSERT( - (representation.IsInteger32() && + (instr->value()->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->value()->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); @@ -1996,9 +2090,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( val = UseRegister(instr->value()); } - return new LStoreKeyedSpecializedArrayElement(external_pointer, - key, - val); + return new(zone()) LStoreKeyedSpecializedArrayElement(external_pointer, + key, + val); } @@ -2013,17 +2107,45 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { ASSERT(instr->value()->representation().IsTagged()); LStoreKeyedGeneric* result = - new LStoreKeyedGeneric(context, object, key, value); + new(zone()) LStoreKeyedGeneric(context, object, key, value); return MarkAsCall(result, instr); } +LInstruction* LChunkBuilder::DoTransitionElementsKind( + HTransitionElementsKind* instr) { + if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + LOperand* object = UseRegister(instr->object()); + LOperand* new_map_reg = TempRegister(); + LOperand* temp_reg = TempRegister(); + LTransitionElementsKind* result = + new(zone()) LTransitionElementsKind(object, new_map_reg, temp_reg); + return DefineSameAsFirst(result); + } else { + LOperand* object = UseFixed(instr->object(), eax); + LOperand* fixed_object_reg = FixedTemp(edx); + LOperand* new_map_reg = FixedTemp(ebx); + LTransitionElementsKind* result = + new(zone()) LTransitionElementsKind(object, + new_map_reg, + fixed_object_reg); + return MarkAsCall(DefineFixed(result, eax), instr); + } +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool needs_write_barrier = instr->NeedsWriteBarrier(); - LOperand* obj = needs_write_barrier - ? UseTempRegister(instr->object()) - : UseRegisterAtStart(instr->object()); + LOperand* obj; + if (needs_write_barrier) { + obj = instr->is_in_object() + ? UseRegister(instr->object()) + : UseTempRegister(instr->object()); + } else { + obj = UseRegisterAtStart(instr->object()); + } LOperand* val = needs_write_barrier ? UseTempRegister(instr->value()) @@ -2035,7 +2157,7 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { ? TempRegister() : NULL; - return new LStoreNamedField(obj, val, temp); + return new(zone()) LStoreNamedField(obj, val, temp); } @@ -2044,7 +2166,8 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LOperand* object = UseFixed(instr->object(), edx); LOperand* value = UseFixed(instr->value(), eax); - LStoreNamedGeneric* result = new LStoreNamedGeneric(context, object, value); + LStoreNamedGeneric* result = + new(zone()) LStoreNamedGeneric(context, object, value); return MarkAsCall(result, instr); } @@ -2053,7 +2176,7 @@ LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* left = UseOrConstantAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); - LStringAdd* string_add = new LStringAdd(context, left, right); + LStringAdd* string_add = new(zone()) LStringAdd(context, left, right); return MarkAsCall(DefineFixed(string_add, eax), instr); } @@ -2062,7 +2185,8 @@ LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { LOperand* string = UseTempRegister(instr->string()); LOperand* index = UseTempRegister(instr->index()); LOperand* context = UseAny(instr->context()); - LStringCharCodeAt* result = new LStringCharCodeAt(context, string, index); + LStringCharCodeAt* result = + new(zone()) LStringCharCodeAt(context, string, index); return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); } @@ -2070,38 +2194,51 @@ LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) { LOperand* char_code = UseRegister(instr->value()); LOperand* context = UseAny(instr->context()); - LStringCharFromCode* result = new LStringCharFromCode(context, char_code); + LStringCharFromCode* result = + new(zone()) LStringCharFromCode(context, char_code); return AssignPointerMap(DefineAsRegister(result)); } LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { LOperand* string = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LStringLength(string)); + return DefineAsRegister(new(zone()) LStringLength(string)); } LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(DefineFixed(new LArrayLiteral(context), eax), instr); + return MarkAsCall( + DefineFixed(new(zone()) LArrayLiteral(context), eax), instr); +} + + +LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { + LOperand* context = UseFixed(instr->context(), esi); + return MarkAsCall( + DefineFixed(new(zone()) LObjectLiteralFast(context), eax), instr); } -LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { +LInstruction* LChunkBuilder::DoObjectLiteralGeneric( + HObjectLiteralGeneric* instr) { LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(DefineFixed(new LObjectLiteral(context), eax), instr); + return MarkAsCall( + DefineFixed(new(zone()) LObjectLiteralGeneric(context), eax), instr); } LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(DefineFixed(new LRegExpLiteral(context), eax), instr); + return MarkAsCall( + DefineFixed(new(zone()) LRegExpLiteral(context), eax), instr); } LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(DefineFixed(new LFunctionLiteral(context), eax), instr); + return MarkAsCall( + DefineFixed(new(zone()) LFunctionLiteral(context), eax), instr); } @@ -2109,7 +2246,7 @@ LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* object = UseAtStart(instr->object()); LOperand* key = UseOrConstantAtStart(instr->key()); - LDeleteProperty* result = new LDeleteProperty(context, object, key); + LDeleteProperty* result = new(zone()) LDeleteProperty(context, object, key); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -2117,13 +2254,13 @@ LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) { allocator_->MarkAsOsrEntry(); current_block_->last_environment()->set_ast_id(instr->ast_id()); - return AssignEnvironment(new LOsrEntry); + return AssignEnvironment(new(zone()) LOsrEntry); } LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { int spill_index = chunk()->GetParameterStackSlot(instr->index()); - return DefineAsSpilled(new LParameter, spill_index); + return DefineAsSpilled(new(zone()) LParameter, spill_index); } @@ -2133,14 +2270,14 @@ LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { Abort("Too many spill slots needed for OSR"); spill_index = 0; } - return DefineAsSpilled(new LUnknownOSRValue, spill_index); + return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); } LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) { LOperand* context = UseFixed(instr->context(), esi); argument_count_ -= instr->argument_count(); - LCallStub* result = new LCallStub(context); + LCallStub* result = new(zone()) LCallStub(context); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -2158,14 +2295,15 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { LOperand* arguments = UseRegister(instr->arguments()); LOperand* length = UseTempRegister(instr->length()); LOperand* index = Use(instr->index()); - LAccessArgumentsAt* result = new LAccessArgumentsAt(arguments, length, index); + LAccessArgumentsAt* result = + new(zone()) LAccessArgumentsAt(arguments, length, index); return AssignEnvironment(DefineAsRegister(result)); } LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { LOperand* object = UseFixed(instr->value(), eax); - LToFastProperties* result = new LToFastProperties(object); + LToFastProperties* result = new(zone()) LToFastProperties(object); return MarkAsCall(DefineFixed(result, eax), instr); } @@ -2173,19 +2311,19 @@ LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* value = UseAtStart(instr->value()); - LTypeof* result = new LTypeof(context, value); + LTypeof* result = new(zone()) LTypeof(context, value); return MarkAsCall(DefineFixed(result, eax), instr); } LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) { - return new LTypeofIsAndBranch(UseTempRegister(instr->value())); + return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value())); } LInstruction* LChunkBuilder::DoIsConstructCallAndBranch( HIsConstructCallAndBranch* instr) { - return new LIsConstructCallAndBranch(TempRegister()); + return new(zone()) LIsConstructCallAndBranch(TempRegister()); } @@ -2209,7 +2347,7 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { // lazy bailout instruction to capture the environment. if (pending_deoptimization_ast_id_ != AstNode::kNoNumber) { ASSERT(pending_deoptimization_ast_id_ == instr->ast_id()); - LLazyBailout* lazy_bailout = new LLazyBailout; + LLazyBailout* lazy_bailout = new(zone()) LLazyBailout; LInstruction* result = AssignEnvironment(lazy_bailout); instruction_pending_deoptimization_environment_-> set_deoptimization_environment(result->environment()); @@ -2224,11 +2362,12 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) { if (instr->is_function_entry()) { LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall(new LStackCheck(context), instr); + return MarkAsCall(new(zone()) LStackCheck(context), instr); } else { ASSERT(instr->is_backwards_branch()); LOperand* context = UseAny(instr->context()); - return AssignEnvironment(AssignPointerMap(new LStackCheck(context))); + return AssignEnvironment( + AssignPointerMap(new(zone()) LStackCheck(context))); } } @@ -2237,6 +2376,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { HEnvironment* outer = current_block_->last_environment(); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner = outer->CopyForInlining(instr->closure(), + instr->arguments_count(), instr->function(), undefined, instr->call_kind()); @@ -2247,7 +2387,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { - HEnvironment* outer = current_block_->last_environment()->outer(); + HEnvironment* outer = current_block_->last_environment()-> + DiscardInlined(false); current_block_->UpdateEnvironment(outer); return NULL; } @@ -2257,7 +2398,7 @@ LInstruction* LChunkBuilder::DoIn(HIn* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* key = UseOrConstantAtStart(instr->key()); LOperand* object = UseOrConstantAtStart(instr->object()); - LIn* result = new LIn(context, key, object); + LIn* result = new(zone()) LIn(context, key, object); return MarkAsCall(DefineFixed(result, eax), instr); } diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index b0ab6b416e..825aad18e5 100644 --- a/deps/v8/src/ia32/lithium-ia32.h +++ b/deps/v8/src/ia32/lithium-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -101,10 +101,12 @@ class LCodeGen; V(Integer32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ - V(IsNullAndBranch) \ + V(IsNilAndBranch) \ V(IsObjectAndBranch) \ + V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ + V(StringCompareAndBranch) \ V(JSArrayLength) \ V(Label) \ V(LazyBailout) \ @@ -121,16 +123,19 @@ class LCodeGen; V(LoadNamedField) \ V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ + V(MathPowHalf) \ V(ModI) \ V(MulI) \ V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteral) \ + V(ObjectLiteralFast) \ + V(ObjectLiteralGeneric) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ V(Power) \ + V(Random) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -156,6 +161,7 @@ class LCodeGen; V(ThisFunction) \ V(Throw) \ V(ToFastProperties) \ + V(TransitionElementsKind) \ V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ @@ -191,8 +197,8 @@ class LInstruction: public ZoneObject { virtual void CompileToNative(LCodeGen* generator) = 0; virtual const char* Mnemonic() const = 0; virtual void PrintTo(StringStream* stream); - virtual void PrintDataTo(StringStream* stream) = 0; - virtual void PrintOutputOperandTo(StringStream* stream) = 0; + virtual void PrintDataTo(StringStream* stream); + virtual void PrintOutputOperandTo(StringStream* stream); enum Opcode { // Declare a unique enum value for each instruction. @@ -288,9 +294,6 @@ class LTemplateInstruction: public LInstruction { int TempCount() { return T; } LOperand* TempAt(int i) { return temps_[i]; } - virtual void PrintDataTo(StringStream* stream); - virtual void PrintOutputOperandTo(StringStream* stream); - protected: EmbeddedContainer<LOperand*, R> results_; EmbeddedContainer<LOperand*, I> inputs_; @@ -581,6 +584,24 @@ class LUnaryMathOperation: public LTemplateInstruction<1, 2, 0> { }; +class LMathPowHalf: public LTemplateInstruction<1, 2, 1> { + public: + LMathPowHalf(LOperand* context, LOperand* value, LOperand* temp) { + inputs_[1] = context; + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* context() { return inputs_[1]; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half") + + virtual void PrintDataTo(StringStream* stream); +}; + + class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { public: LCmpObjectEqAndBranch(LOperand* left, LOperand* right) { @@ -605,17 +626,18 @@ class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> { }; -class LIsNullAndBranch: public LControlInstruction<1, 1> { +class LIsNilAndBranch: public LControlInstruction<1, 1> { public: - LIsNullAndBranch(LOperand* value, LOperand* temp) { + LIsNilAndBranch(LOperand* value, LOperand* temp) { inputs_[0] = value; temps_[0] = temp; } - DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch, "is-null-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsNullAndBranch) + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch) - bool is_strict() const { return hydrogen()->is_strict(); } + EqualityKind kind() const { return hydrogen()->kind(); } + NilValue nil() const { return hydrogen()->nil(); } virtual void PrintDataTo(StringStream* stream); }; @@ -634,6 +656,19 @@ class LIsObjectAndBranch: public LControlInstruction<1, 1> { }; +class LIsStringAndBranch: public LControlInstruction<1, 1> { + public: + LIsStringAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") + + virtual void PrintDataTo(StringStream* stream); +}; + + class LIsSmiAndBranch: public LControlInstruction<1, 0> { public: explicit LIsSmiAndBranch(LOperand* value) { @@ -661,6 +696,24 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> { }; +class LStringCompareAndBranch: public LControlInstruction<3, 0> { + public: + LStringCompareAndBranch(LOperand* context, LOperand* left, LOperand* right) { + inputs_[0] = context; + inputs_[1] = left; + inputs_[2] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, + "string-compare-and-branch") + DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) + + virtual void PrintDataTo(StringStream* stream); + + Token::Value op() const { return hydrogen()->token(); } +}; + + class LHasInstanceTypeAndBranch: public LControlInstruction<1, 1> { public: LHasInstanceTypeAndBranch(LOperand* value, LOperand* temp) { @@ -787,18 +840,15 @@ class LBoundsCheck: public LTemplateInstruction<0, 2, 0> { class LBitI: public LTemplateInstruction<1, 2, 0> { public: - LBitI(Token::Value op, LOperand* left, LOperand* right) - : op_(op) { + LBitI(LOperand* left, LOperand* right) { inputs_[0] = left; inputs_[1] = right; } - Token::Value op() const { return op_; } + Token::Value op() const { return hydrogen()->op(); } DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") - - private: - Token::Value op_; + DECLARE_HYDROGEN_ACCESSOR(Bitwise) }; @@ -994,6 +1044,17 @@ class LPower: public LTemplateInstruction<1, 2, 0> { }; +class LRandom: public LTemplateInstruction<1, 1, 0> { + public: + explicit LRandom(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD: public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) @@ -1228,6 +1289,8 @@ class LStoreGlobalCell: public LTemplateInstruction<0, 1, 0> { DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) + + LOperand* value() { return inputs_[0]; } }; @@ -1248,7 +1311,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 3, 0> { LOperand* global_object() { return InputAt(1); } Handle<Object> name() const { return hydrogen()->name(); } LOperand* value() { return InputAt(2); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1282,7 +1345,6 @@ class LStoreContextSlot: public LTemplateInstruction<0, 2, 1> { LOperand* context() { return InputAt(0); } LOperand* value() { return InputAt(1); } int slot_index() { return hydrogen()->slot_index(); } - int needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } virtual void PrintDataTo(StringStream* stream); }; @@ -1299,7 +1361,9 @@ class LPushArgument: public LTemplateInstruction<0, 1, 0> { class LThisFunction: public LTemplateInstruction<1, 0, 0> { + public: DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function") + DECLARE_HYDROGEN_ACCESSOR(ThisFunction) }; @@ -1412,17 +1476,19 @@ class LCallNamed: public LTemplateInstruction<1, 1, 0> { }; -class LCallFunction: public LTemplateInstruction<1, 1, 0> { +class LCallFunction: public LTemplateInstruction<1, 2, 0> { public: - explicit LCallFunction(LOperand* context) { + explicit LCallFunction(LOperand* context, LOperand* function) { inputs_[0] = context; + inputs_[1] = function; } DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") DECLARE_HYDROGEN_ACCESSOR(CallFunction) LOperand* context() { return inputs_[0]; } - int arity() const { return hydrogen()->argument_count() - 2; } + LOperand* function() { return inputs_[1]; } + int arity() const { return hydrogen()->argument_count() - 1; } }; @@ -1558,10 +1624,11 @@ class LSmiTag: public LTemplateInstruction<1, 1, 0> { }; -class LNumberUntagD: public LTemplateInstruction<1, 1, 0> { +class LNumberUntagD: public LTemplateInstruction<1, 1, 1> { public: - explicit LNumberUntagD(LOperand* value) { + explicit LNumberUntagD(LOperand* value, LOperand* temp) { inputs_[0] = value; + temps_[0] = temp; } DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag") @@ -1604,7 +1671,6 @@ class LStoreNamedField: public LTemplateInstruction<0, 2, 1> { Handle<Object> name() const { return hydrogen()->name(); } bool is_in_object() { return hydrogen()->is_in_object(); } int offset() { return hydrogen()->offset(); } - bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } Handle<Map> transition() const { return hydrogen()->transition(); } }; @@ -1626,7 +1692,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 3, 0> { LOperand* object() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } Handle<Object> name() const { return hydrogen()->name(); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1716,7 +1782,31 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> { LOperand* object() { return inputs_[1]; } LOperand* key() { return inputs_[2]; } LOperand* value() { return inputs_[3]; } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } +}; + + +class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> { + public: + LTransitionElementsKind(LOperand* object, + LOperand* new_map_temp, + LOperand* temp_reg) { + inputs_[0] = object; + temps_[0] = new_map_temp; + temps_[1] = temp_reg; + } + + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, + "transition-elements-kind") + DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* new_map_reg() { return temps_[0]; } + LOperand* temp_reg() { return temps_[1]; } + Handle<Map> original_map() { return hydrogen()->original_map(); } + Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); } }; @@ -1788,6 +1878,8 @@ class LCheckFunction: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function") DECLARE_HYDROGEN_ACCESSOR(CheckFunction) }; @@ -1900,16 +1992,29 @@ class LArrayLiteral: public LTemplateInstruction<1, 1, 0> { }; -class LObjectLiteral: public LTemplateInstruction<1, 1, 0> { +class LObjectLiteralFast: public LTemplateInstruction<1, 1, 0> { public: - explicit LObjectLiteral(LOperand* context) { + explicit LObjectLiteralFast(LOperand* context) { inputs_[0] = context; } LOperand* context() { return inputs_[0]; } - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) +}; + + +class LObjectLiteralGeneric: public LTemplateInstruction<1, 1, 0> { + public: + explicit LObjectLiteralGeneric(LOperand* context) { + inputs_[0] = context; + } + + LOperand* context() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) }; @@ -2060,6 +2165,7 @@ class LChunk: public ZoneObject { graph_(graph), instructions_(32), pointer_maps_(8), + num_double_slots_(0), inlined_closures_(1) { } void AddInstruction(LInstruction* instruction, HBasicBlock* block); @@ -2073,6 +2179,8 @@ class LChunk: public ZoneObject { int ParameterAt(int index); int GetParameterStackSlot(int index) const; int spill_slot_count() const { return spill_slot_count_; } + int num_double_slots() const { return num_double_slots_; } + CompilationInfo* info() const { return info_; } HGraph* graph() const { return graph_; } const ZoneList<LInstruction*>* instructions() const { return &instructions_; } @@ -2114,6 +2222,7 @@ class LChunk: public ZoneObject { HGraph* const graph_; ZoneList<LInstruction*> instructions_; ZoneList<LPointerMap*> pointer_maps_; + int num_double_slots_; ZoneList<Handle<JSFunction> > inlined_closures_; }; @@ -2124,6 +2233,7 @@ class LChunkBuilder BASE_EMBEDDED { : chunk_(NULL), info_(info), graph_(graph), + isolate_(graph->isolate()), status_(UNUSED), current_instruction_(NULL), current_block_(NULL), @@ -2153,6 +2263,7 @@ class LChunkBuilder BASE_EMBEDDED { LChunk* chunk() const { return chunk_; } CompilationInfo* info() const { return info_; } HGraph* graph() const { return graph_; } + Zone* zone() { return isolate_->zone(); } bool is_unused() const { return status_ == UNUSED; } bool is_building() const { return status_ == BUILDING; } @@ -2162,7 +2273,6 @@ class LChunkBuilder BASE_EMBEDDED { void Abort(const char* format, ...); // Methods for getting operands for Use / Define / Temp. - LRegister* ToOperand(Register reg); LUnallocated* ToUnallocated(Register reg); LUnallocated* ToUnallocated(XMMRegister reg); @@ -2213,8 +2323,6 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result); template<int I, int T> - LInstruction* Define(LTemplateInstruction<1, I, T>* instr); - template<int I, int T> LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr); template<int I, int T> LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr, @@ -2249,12 +2357,12 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, + int* argument_index_accumulator); void VisitInstruction(HInstruction* current); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); - LInstruction* DoBit(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoArithmeticD(Token::Value op, HArithmeticBinaryOperation* instr); @@ -2264,6 +2372,7 @@ class LChunkBuilder BASE_EMBEDDED { LChunk* chunk_; CompilationInfo* info_; HGraph* const graph_; + Isolate* isolate_; Status status_; HInstruction* current_instruction_; HBasicBlock* current_block_; diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index ce6d6a6ea0..9986c3ed86 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -44,7 +44,8 @@ namespace internal { MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) : Assembler(arg_isolate, buffer, size), generating_stub_(false), - allow_stub_calls_(true) { + allow_stub_calls_(true), + has_frame_(false) { if (isolate() != NULL) { code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), isolate()); @@ -52,33 +53,75 @@ MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) } -void MacroAssembler::RecordWriteHelper(Register object, - Register addr, - Register scratch) { - if (emit_debug_code()) { - // Check that the object is not in new space. - Label not_in_new_space; - InNewSpace(object, scratch, not_equal, ¬_in_new_space); - Abort("new-space object passed to RecordWriteHelper"); - bind(¬_in_new_space); +void MacroAssembler::InNewSpace( + Register object, + Register scratch, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance) { + ASSERT(cc == equal || cc == not_equal); + if (scratch.is(object)) { + and_(scratch, Immediate(~Page::kPageAlignmentMask)); + } else { + mov(scratch, Immediate(~Page::kPageAlignmentMask)); + and_(scratch, object); } + // Check that we can use a test_b. + ASSERT(MemoryChunk::IN_FROM_SPACE < 8); + ASSERT(MemoryChunk::IN_TO_SPACE < 8); + int mask = (1 << MemoryChunk::IN_FROM_SPACE) + | (1 << MemoryChunk::IN_TO_SPACE); + // If non-zero, the page belongs to new-space. + test_b(Operand(scratch, MemoryChunk::kFlagsOffset), + static_cast<uint8_t>(mask)); + j(cc, condition_met, condition_met_distance); +} - // Compute the page start address from the heap object pointer, and reuse - // the 'object' register for it. - and_(object, ~Page::kPageAlignmentMask); - - // Compute number of region covering addr. See Page::GetRegionNumberForAddress - // method for more details. - shr(addr, Page::kRegionSizeLog2); - and_(addr, Page::kPageAlignmentMask >> Page::kRegionSizeLog2); - // Set dirty mark for region. - // Bit tests with a memory operand should be avoided on Intel processors, - // as they usually have long latency and multiple uops. We load the bit base - // operand to a register at first and store it back after bit set. - mov(scratch, Operand(object, Page::kDirtyFlagOffset)); - bts(Operand(scratch), addr); - mov(Operand(object, Page::kDirtyFlagOffset), scratch); +void MacroAssembler::RememberedSetHelper( + Register object, // Only used for debug checks. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + MacroAssembler::RememberedSetFinalAction and_then) { + Label done; + if (FLAG_debug_code) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear); + int3(); + bind(&ok); + } + // Load store buffer top. + ExternalReference store_buffer = + ExternalReference::store_buffer_top(isolate()); + mov(scratch, Operand::StaticVariable(store_buffer)); + // Store pointer to buffer. + mov(Operand(scratch, 0), addr); + // Increment buffer top. + add(scratch, Immediate(kPointerSize)); + // Write back new top of buffer. + mov(Operand::StaticVariable(store_buffer), scratch); + // Call stub on end of buffer. + // Check for end of buffer. + test(scratch, Immediate(StoreBuffer::kStoreBufferOverflowBit)); + if (and_then == kReturnAtEnd) { + Label buffer_overflowed; + j(not_equal, &buffer_overflowed, Label::kNear); + ret(0); + bind(&buffer_overflowed); + } else { + ASSERT(and_then == kFallThroughAtEnd); + j(equal, &done, Label::kNear); + } + StoreBufferOverflowStub store_buffer_overflow = + StoreBufferOverflowStub(save_fp); + CallStub(&store_buffer_overflow); + if (and_then == kReturnAtEnd) { + ret(0); + } else { + ASSERT(and_then == kFallThroughAtEnd); + bind(&done); + } } @@ -112,100 +155,144 @@ void MacroAssembler::ClampUint8(Register reg) { } -void MacroAssembler::InNewSpace(Register object, - Register scratch, - Condition cc, - Label* branch, - Label::Distance branch_near) { - ASSERT(cc == equal || cc == not_equal); - if (Serializer::enabled()) { - // Can't do arithmetic on external references if it might get serialized. - mov(scratch, Operand(object)); - // The mask isn't really an address. We load it as an external reference in - // case the size of the new space is different between the snapshot maker - // and the running system. - and_(Operand(scratch), - Immediate(ExternalReference::new_space_mask(isolate()))); - cmp(Operand(scratch), - Immediate(ExternalReference::new_space_start(isolate()))); - j(cc, branch, branch_near); - } else { - int32_t new_space_start = reinterpret_cast<int32_t>( - ExternalReference::new_space_start(isolate()).address()); - lea(scratch, Operand(object, -new_space_start)); - and_(scratch, isolate()->heap()->NewSpaceMask()); - j(cc, branch, branch_near); +void MacroAssembler::RecordWriteArray(Register object, + Register value, + Register index, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis. + Label done; + + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + ASSERT_EQ(0, kSmiTag); + test(value, Immediate(kSmiTagMask)); + j(zero, &done); + } + + // Array access: calculate the destination address in the same manner as + // KeyedStoreIC::GenerateGeneric. Multiply a smi by 2 to get an offset + // into an array of words. + Register dst = index; + lea(dst, Operand(object, index, times_half_pointer_size, + FixedArray::kHeaderSize - kHeapObjectTag)); + + RecordWrite( + object, dst, value, save_fp, remembered_set_action, OMIT_SMI_CHECK); + + bind(&done); + + // Clobber clobbered input registers when running with the debug-code flag + // turned on to provoke errors. + if (emit_debug_code()) { + mov(value, Immediate(BitCast<int32_t>(kZapValue))); + mov(index, Immediate(BitCast<int32_t>(kZapValue))); } } -void MacroAssembler::RecordWrite(Register object, - int offset, - Register value, - Register scratch) { +void MacroAssembler::RecordWriteField( + Register object, + int offset, + Register value, + Register dst, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { // First, check if a write barrier is even needed. The tests below - // catch stores of Smis and stores into young gen. + // catch stores of Smis. Label done; // Skip barrier if writing a smi. - STATIC_ASSERT(kSmiTag == 0); - JumpIfSmi(value, &done, Label::kNear); - - InNewSpace(object, value, equal, &done, Label::kNear); + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done, Label::kNear); + } - // The offset is relative to a tagged or untagged HeapObject pointer, - // so either offset or offset + kHeapObjectTag must be a - // multiple of kPointerSize. - ASSERT(IsAligned(offset, kPointerSize) || - IsAligned(offset + kHeapObjectTag, kPointerSize)); + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + ASSERT(IsAligned(offset, kPointerSize)); - Register dst = scratch; - if (offset != 0) { - lea(dst, Operand(object, offset)); - } else { - // Array access: calculate the destination address in the same manner as - // KeyedStoreIC::GenerateGeneric. Multiply a smi by 2 to get an offset - // into an array of words. - STATIC_ASSERT(kSmiTagSize == 1); - STATIC_ASSERT(kSmiTag == 0); - lea(dst, Operand(object, dst, times_half_pointer_size, - FixedArray::kHeaderSize - kHeapObjectTag)); + lea(dst, FieldOperand(object, offset)); + if (emit_debug_code()) { + Label ok; + test_b(dst, (1 << kPointerSizeLog2) - 1); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); } - RecordWriteHelper(object, dst, value); + + RecordWrite( + object, dst, value, save_fp, remembered_set_action, OMIT_SMI_CHECK); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered input registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - mov(object, Immediate(BitCast<int32_t>(kZapValue))); mov(value, Immediate(BitCast<int32_t>(kZapValue))); - mov(scratch, Immediate(BitCast<int32_t>(kZapValue))); + mov(dst, Immediate(BitCast<int32_t>(kZapValue))); } } void MacroAssembler::RecordWrite(Register object, Register address, - Register value) { + Register value, + SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + ASSERT(!object.is(value)); + ASSERT(!object.is(address)); + ASSERT(!value.is(address)); + if (emit_debug_code()) { + AbortIfSmi(object); + } + + if (remembered_set_action == OMIT_REMEMBERED_SET && + !FLAG_incremental_marking) { + return; + } + + if (FLAG_debug_code) { + Label ok; + cmp(value, Operand(address, 0)); + j(equal, &ok, Label::kNear); + int3(); + bind(&ok); + } + // First, check if a write barrier is even needed. The tests below // catch stores of Smis and stores into young gen. Label done; - // Skip barrier if writing a smi. - STATIC_ASSERT(kSmiTag == 0); - JumpIfSmi(value, &done, Label::kNear); - - InNewSpace(object, value, equal, &done); - - RecordWriteHelper(object, address, value); + if (smi_check == INLINE_SMI_CHECK) { + // Skip barrier if writing a smi. + JumpIfSmi(value, &done, Label::kNear); + } + + CheckPageFlag(value, + value, // Used as scratch. + MemoryChunk::kPointersToHereAreInterestingMask, + zero, + &done, + Label::kNear); + CheckPageFlag(object, + value, // Used as scratch. + MemoryChunk::kPointersFromHereAreInterestingMask, + zero, + &done, + Label::kNear); + + RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); + CallStub(&stub); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - mov(object, Immediate(BitCast<int32_t>(kZapValue))); mov(address, Immediate(BitCast<int32_t>(kZapValue))); mov(value, Immediate(BitCast<int32_t>(kZapValue))); } @@ -224,7 +311,7 @@ void MacroAssembler::DebugBreak() { void MacroAssembler::Set(Register dst, const Immediate& x) { if (x.is_zero()) { - xor_(dst, Operand(dst)); // Shorter than mov. + xor_(dst, dst); // Shorter than mov. } else { mov(dst, x); } @@ -265,7 +352,15 @@ void MacroAssembler::SafePush(const Immediate& x) { void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { // see ROOT_ACCESSOR macro in factory.h - Handle<Object> value(&isolate()->heap()->roots_address()[index]); + Handle<Object> value(&isolate()->heap()->roots_array_start()[index]); + cmp(with, value); +} + + +void MacroAssembler::CompareRoot(const Operand& with, + Heap::RootListIndex index) { + // see ROOT_ACCESSOR macro in factory.h + Handle<Object> value(&isolate()->heap()->roots_array_start()[index]); cmp(with, value); } @@ -287,22 +382,153 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastElementValue); + j(above, fail, distance); +} + + +void MacroAssembler::CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastSmiOnlyElementValue); + j(below_equal, fail, distance); cmpb(FieldOperand(map, Map::kBitField2Offset), Map::kMaximumBitField2FastElementValue); j(above, fail, distance); } +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Map::kMaximumBitField2FastSmiOnlyElementValue); + j(above, fail, distance); +} + + +void MacroAssembler::StoreNumberToDoubleElements( + Register maybe_number, + Register elements, + Register key, + Register scratch1, + XMMRegister scratch2, + Label* fail, + bool specialize_for_processor) { + Label smi_value, done, maybe_nan, not_nan, is_nan, have_double_value; + JumpIfSmi(maybe_number, &smi_value, Label::kNear); + + CheckMap(maybe_number, + isolate()->factory()->heap_number_map(), + fail, + DONT_DO_SMI_CHECK); + + // Double value, canonicalize NaN. + uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32); + cmp(FieldOperand(maybe_number, offset), + Immediate(kNaNOrInfinityLowerBoundUpper32)); + j(greater_equal, &maybe_nan, Label::kNear); + + bind(¬_nan); + ExternalReference canonical_nan_reference = + ExternalReference::address_of_canonical_non_hole_nan(); + if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) { + CpuFeatures::Scope use_sse2(SSE2); + movdbl(scratch2, FieldOperand(maybe_number, HeapNumber::kValueOffset)); + bind(&have_double_value); + movdbl(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize), + scratch2); + } else { + fld_d(FieldOperand(maybe_number, HeapNumber::kValueOffset)); + bind(&have_double_value); + fstp_d(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize)); + } + jmp(&done); + + bind(&maybe_nan); + // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise + // it's an Infinity, and the non-NaN code path applies. + j(greater, &is_nan, Label::kNear); + cmp(FieldOperand(maybe_number, HeapNumber::kValueOffset), Immediate(0)); + j(zero, ¬_nan); + bind(&is_nan); + if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) { + CpuFeatures::Scope use_sse2(SSE2); + movdbl(scratch2, Operand::StaticVariable(canonical_nan_reference)); + } else { + fld_d(Operand::StaticVariable(canonical_nan_reference)); + } + jmp(&have_double_value, Label::kNear); + + bind(&smi_value); + // Value is a smi. Convert to a double and store. + // Preserve original value. + mov(scratch1, maybe_number); + SmiUntag(scratch1); + if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) { + CpuFeatures::Scope fscope(SSE2); + cvtsi2sd(scratch2, scratch1); + movdbl(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize), + scratch2); + } else { + push(scratch1); + fild_s(Operand(esp, 0)); + pop(scratch1); + fstp_d(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize)); + } + bind(&done); +} + + +void MacroAssembler::CompareMap(Register obj, + Handle<Map> map, + Label* early_success, + CompareMapMode mode) { + cmp(FieldOperand(obj, HeapObject::kMapOffset), map); + if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { + Map* transitioned_fast_element_map( + map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); + ASSERT(transitioned_fast_element_map == NULL || + map->elements_kind() != FAST_ELEMENTS); + if (transitioned_fast_element_map != NULL) { + j(equal, early_success, Label::kNear); + cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle<Map>(transitioned_fast_element_map)); + } + + Map* transitioned_double_map( + map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); + ASSERT(transitioned_double_map == NULL || + map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); + if (transitioned_double_map != NULL) { + j(equal, early_success, Label::kNear); + cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle<Map>(transitioned_double_map)); + } + } +} + + void MacroAssembler::CheckMap(Register obj, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type) { + SmiCheckType smi_check_type, + CompareMapMode mode) { if (smi_check_type == DO_SMI_CHECK) { JumpIfSmi(obj, fail); } - cmp(FieldOperand(obj, HeapObject::kMapOffset), Immediate(map)); + + Label success; + CompareMap(obj, map, &success, mode); j(not_equal, fail); + bind(&success); } @@ -345,7 +571,7 @@ void MacroAssembler::IsInstanceJSObjectType(Register map, Register scratch, Label* fail) { movzx_b(scratch, FieldOperand(map, Map::kInstanceTypeOffset)); - sub(Operand(scratch), Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + sub(scratch, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); cmp(scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE - FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); j(above, fail); @@ -355,8 +581,7 @@ void MacroAssembler::IsInstanceJSObjectType(Register map, void MacroAssembler::FCmp() { if (CpuFeatures::IsSupported(CMOV)) { fucomip(); - ffree(0); - fincstp(); + fstp(0); } else { fucompp(); push(eax); @@ -402,7 +627,7 @@ void MacroAssembler::AbortIfSmi(Register object) { void MacroAssembler::EnterFrame(StackFrame::Type type) { push(ebp); - mov(ebp, Operand(esp)); + mov(ebp, esp); push(esi); push(Immediate(Smi::FromInt(type))); push(Immediate(CodeObject())); @@ -424,12 +649,12 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { void MacroAssembler::EnterExitFramePrologue() { - // Setup the frame structure on the stack. + // Set up the frame structure on the stack. ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize); push(ebp); - mov(ebp, Operand(esp)); + mov(ebp, esp); // Reserve room for entry stack pointer and push the code object. ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); @@ -451,14 +676,14 @@ void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) { if (save_doubles) { CpuFeatures::Scope scope(SSE2); int space = XMMRegister::kNumRegisters * kDoubleSize + argc * kPointerSize; - sub(Operand(esp), Immediate(space)); + sub(esp, Immediate(space)); const int offset = -2 * kPointerSize; for (int i = 0; i < XMMRegister::kNumRegisters; i++) { XMMRegister reg = XMMRegister::from_code(i); movdbl(Operand(ebp, offset - ((i + 1) * kDoubleSize)), reg); } } else { - sub(Operand(esp), Immediate(argc * kPointerSize)); + sub(esp, Immediate(argc * kPointerSize)); } // Get the required frame alignment for the OS. @@ -476,9 +701,9 @@ void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) { void MacroAssembler::EnterExitFrame(bool save_doubles) { EnterExitFramePrologue(); - // Setup argc and argv in callee-saved registers. + // Set up argc and argv in callee-saved registers. int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - mov(edi, Operand(eax)); + mov(edi, eax); lea(esi, Operand(ebp, eax, times_4, offset)); // Reserve space for argc, argv and isolate. @@ -532,55 +757,68 @@ void MacroAssembler::LeaveExitFrameEpilogue() { void MacroAssembler::LeaveApiExitFrame() { - mov(esp, Operand(ebp)); + mov(esp, ebp); pop(ebp); LeaveExitFrameEpilogue(); } -void MacroAssembler::PushTryHandler(CodeLocation try_location, - HandlerType type) { +void MacroAssembler::PushTryHandler(StackHandler::Kind kind, + int handler_index) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // The pc (return address) is already on TOS. - if (try_location == IN_JAVASCRIPT) { - if (type == TRY_CATCH_HANDLER) { - push(Immediate(StackHandler::TRY_CATCH)); - } else { - push(Immediate(StackHandler::TRY_FINALLY)); - } - push(ebp); - push(esi); - } else { - ASSERT(try_location == IN_JS_ENTRY); - // The frame pointer does not point to a JS frame so we save NULL - // for ebp. We expect the code throwing an exception to check ebp - // before dereferencing it to restore the context. - push(Immediate(StackHandler::ENTRY)); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // We will build up the handler from the bottom by pushing on the stack. + // First push the frame pointer and context. + if (kind == StackHandler::JS_ENTRY) { + // The frame pointer does not point to a JS frame so we save NULL for + // ebp. We expect the code throwing an exception to check ebp before + // dereferencing it to restore the context. push(Immediate(0)); // NULL frame pointer. push(Immediate(Smi::FromInt(0))); // No context. + } else { + push(ebp); + push(esi); } - // Save the current handler as the next handler. - push(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, - isolate()))); - // Link this handler as the new current one. - mov(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, - isolate())), - esp); + // Push the state and the code object. + unsigned state = + StackHandler::IndexField::encode(handler_index) | + StackHandler::KindField::encode(kind); + push(Immediate(state)); + Push(CodeObject()); + + // Link the current handler as the next handler. + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + push(Operand::StaticVariable(handler_address)); + // Set this new handler as the current one. + mov(Operand::StaticVariable(handler_address), esp); } void MacroAssembler::PopTryHandler() { STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - pop(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, - isolate()))); - add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize)); + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + pop(Operand::StaticVariable(handler_address)); + add(esp, Immediate(StackHandlerConstants::kSize - kPointerSize)); +} + + +void MacroAssembler::JumpToHandlerEntry() { + // Compute the handler entry address and jump to it. The handler table is + // a fixed array of (smi-tagged) code offsets. + // eax = exception, edi = code object, edx = state. + mov(ebx, FieldOperand(edi, Code::kHandlerTableOffset)); + shr(edx, StackHandler::kKindWidth); + mov(edx, FieldOperand(ebx, edx, times_4, FixedArray::kHeaderSize)); + SmiUntag(edx); + lea(edi, FieldOperand(edi, edx, times_1, Code::kHeaderSize)); + jmp(edi); } @@ -588,36 +826,39 @@ void MacroAssembler::Throw(Register value) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // eax must hold the exception. + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // The exception is expected in eax. if (!value.is(eax)) { mov(eax, value); } - - // Drop the sp to the top of the handler. - ExternalReference handler_address(Isolate::kHandlerAddress, - isolate()); + // Drop the stack pointer to the top of the top handler. + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); mov(esp, Operand::StaticVariable(handler_address)); - - // Restore next handler, context, and frame pointer; discard handler state. + // Restore the next handler. pop(Operand::StaticVariable(handler_address)); + + // Remove the code object and state, compute the handler address in edi. + pop(edi); // Code object. + pop(edx); // Index and state. + + // Restore the context and frame pointer. pop(esi); // Context. pop(ebp); // Frame pointer. - pop(edx); // State. // If the handler is a JS frame, restore the context to the frame. - // (edx == ENTRY) == (ebp == 0) == (esi == 0), so we could test any - // of them. + // (kind == ENTRY) == (ebp == 0) == (esi == 0), so we could test either + // ebp or esi. Label skip; - cmp(Operand(edx), Immediate(StackHandler::ENTRY)); - j(equal, &skip, Label::kNear); + test(esi, esi); + j(zero, &skip, Label::kNear); mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi); bind(&skip); - ret(0); + JumpToHandlerEntry(); } @@ -626,61 +867,55 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - - // eax must hold the exception. - if (!value.is(eax)) { - mov(eax, value); - } - - // Drop sp to the top stack handler. - ExternalReference handler_address(Isolate::kHandlerAddress, - isolate()); - mov(esp, Operand::StaticVariable(handler_address)); - - // Unwind the handlers until the ENTRY handler is found. - Label loop, done; - bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY)); - j(equal, &done, Label::kNear); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - mov(esp, Operand(esp, kNextOffset)); - jmp(&loop); - bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - pop(Operand::StaticVariable(handler_address)); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + // The exception is expected in eax. if (type == OUT_OF_MEMORY) { // Set external caught exception to false. - ExternalReference external_caught( - Isolate::kExternalCaughtExceptionAddress, - isolate()); - mov(eax, false); - mov(Operand::StaticVariable(external_caught), eax); + ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress, + isolate()); + mov(Operand::StaticVariable(external_caught), Immediate(false)); // Set pending exception and eax to out of memory exception. ExternalReference pending_exception(Isolate::kPendingExceptionAddress, isolate()); mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException())); mov(Operand::StaticVariable(pending_exception), eax); + } else if (!value.is(eax)) { + mov(eax, value); } - // Discard the context saved in the handler and clear the context pointer. - pop(edx); - Set(esi, Immediate(0)); + // Drop the stack pointer to the top of the top stack handler. + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + mov(esp, Operand::StaticVariable(handler_address)); + + // Unwind the handlers until the top ENTRY handler is found. + Label fetch_next, check_kind; + jmp(&check_kind, Label::kNear); + bind(&fetch_next); + mov(esp, Operand(esp, StackHandlerConstants::kNextOffset)); + + bind(&check_kind); + STATIC_ASSERT(StackHandler::JS_ENTRY == 0); + test(Operand(esp, StackHandlerConstants::kStateOffset), + Immediate(StackHandler::KindField::kMask)); + j(not_zero, &fetch_next); - // Restore fp from handler and discard handler state. + // Set the top handler address to next handler past the top ENTRY handler. + pop(Operand::StaticVariable(handler_address)); + + // Remove the code object and state, compute the handler address in edi. + pop(edi); // Code object. + pop(edx); // Index and state. + + // Clear the context pointer and frame pointer (0 was saved in the handler). + pop(esi); pop(ebp); - pop(edx); // State. - ret(0); + JumpToHandlerEntry(); } @@ -696,7 +931,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, // When generating debug code, make sure the lexical context is set. if (emit_debug_code()) { - cmp(Operand(scratch), Immediate(0)); + cmp(scratch, Immediate(0)); Check(not_equal, "we should not have an empty lexical context"); } // Load the global context of the current context. @@ -759,40 +994,39 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, void MacroAssembler::GetNumberHash(Register r0, Register scratch) { // Xor original key with a seed. if (Serializer::enabled()) { - ExternalReference roots_address = - ExternalReference::roots_address(isolate()); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); mov(scratch, Immediate(Heap::kHashSeedRootIndex)); - mov(scratch, Operand::StaticArray(scratch, - times_pointer_size, - roots_address)); + mov(scratch, + Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); SmiUntag(scratch); - xor_(r0, Operand(scratch)); + xor_(r0, scratch); } else { int32_t seed = isolate()->heap()->HashSeed(); - xor_(r0, seed); + xor_(r0, Immediate(seed)); } // hash = ~hash + (hash << 15); mov(scratch, r0); not_(r0); shl(scratch, 15); - add(r0, Operand(scratch)); + add(r0, scratch); // hash = hash ^ (hash >> 12); mov(scratch, r0); shr(scratch, 12); - xor_(r0, Operand(scratch)); + xor_(r0, scratch); // hash = hash + (hash << 2); lea(r0, Operand(r0, r0, times_4, 0)); // hash = hash ^ (hash >> 4); mov(scratch, r0); shr(scratch, 4); - xor_(r0, Operand(scratch)); + xor_(r0, scratch); // hash = hash * 2057; imul(r0, r0, 2057); // hash = hash ^ (hash >> 16); mov(scratch, r0); shr(scratch, 16); - xor_(r0, Operand(scratch)); + xor_(r0, scratch); } @@ -836,9 +1070,9 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(r2, r0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - add(Operand(r2), Immediate(SeededNumberDictionary::GetProbeOffset(i))); + add(r2, Immediate(SeededNumberDictionary::GetProbeOffset(i))); } - and_(r2, Operand(r1)); + and_(r2, r1); // Scale the index by multiplying by the entry size. ASSERT(SeededNumberDictionary::kEntrySize == 3); @@ -894,7 +1128,7 @@ void MacroAssembler::LoadAllocationTopHelper(Register result, if (scratch.is(no_reg)) { mov(result, Operand::StaticVariable(new_space_allocation_top)); } else { - mov(Operand(scratch), Immediate(new_space_allocation_top)); + mov(scratch, Immediate(new_space_allocation_top)); mov(result, Operand(scratch, 0)); } } @@ -953,7 +1187,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size, if (!top_reg.is(result)) { mov(top_reg, result); } - add(Operand(top_reg), Immediate(object_size)); + add(top_reg, Immediate(object_size)); j(carry, gc_required); cmp(top_reg, Operand::StaticVariable(new_space_allocation_limit)); j(above, gc_required); @@ -964,12 +1198,12 @@ void MacroAssembler::AllocateInNewSpace(int object_size, // Tag result if requested. if (top_reg.is(result)) { if ((flags & TAG_OBJECT) != 0) { - sub(Operand(result), Immediate(object_size - kHeapObjectTag)); + sub(result, Immediate(object_size - kHeapObjectTag)); } else { - sub(Operand(result), Immediate(object_size)); + sub(result, Immediate(object_size)); } } else if ((flags & TAG_OBJECT) != 0) { - add(Operand(result), Immediate(kHeapObjectTag)); + add(result, Immediate(kHeapObjectTag)); } } @@ -1007,7 +1241,7 @@ void MacroAssembler::AllocateInNewSpace(int header_size, // We assume that element_count*element_size + header_size does not // overflow. lea(result_end, Operand(element_count, element_size, header_size)); - add(result_end, Operand(result)); + add(result_end, result); j(carry, gc_required); cmp(result_end, Operand::StaticVariable(new_space_allocation_limit)); j(above, gc_required); @@ -1052,7 +1286,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, if (!object_size.is(result_end)) { mov(result_end, object_size); } - add(result_end, Operand(result)); + add(result_end, result); j(carry, gc_required); cmp(result_end, Operand::StaticVariable(new_space_allocation_limit)); j(above, gc_required); @@ -1072,7 +1306,7 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) { ExternalReference::new_space_allocation_top_address(isolate()); // Make sure the object has no tag before resetting top. - and_(Operand(object), Immediate(~kHeapObjectTagMask)); + and_(object, Immediate(~kHeapObjectTagMask)); #ifdef DEBUG cmp(object, Operand::StaticVariable(new_space_allocation_top)); Check(below, "Undo allocation of non allocated memory"); @@ -1111,7 +1345,7 @@ void MacroAssembler::AllocateTwoByteString(Register result, ASSERT(kShortSize == 2); // scratch1 = length * 2 + kObjectAlignmentMask. lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask)); - and_(Operand(scratch1), Immediate(~kObjectAlignmentMask)); + and_(scratch1, Immediate(~kObjectAlignmentMask)); // Allocate two byte string in new space. AllocateInNewSpace(SeqTwoByteString::kHeaderSize, @@ -1145,10 +1379,10 @@ void MacroAssembler::AllocateAsciiString(Register result, ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0); mov(scratch1, length); ASSERT(kCharSize == 1); - add(Operand(scratch1), Immediate(kObjectAlignmentMask)); - and_(Operand(scratch1), Immediate(~kObjectAlignmentMask)); + add(scratch1, Immediate(kObjectAlignmentMask)); + and_(scratch1, Immediate(~kObjectAlignmentMask)); - // Allocate ascii string in new space. + // Allocate ASCII string in new space. AllocateInNewSpace(SeqAsciiString::kHeaderSize, times_1, scratch1, @@ -1176,7 +1410,7 @@ void MacroAssembler::AllocateAsciiString(Register result, Label* gc_required) { ASSERT(length > 0); - // Allocate ascii string in new space. + // Allocate ASCII string in new space. AllocateInNewSpace(SeqAsciiString::SizeFor(length), result, scratch1, @@ -1280,7 +1514,7 @@ void MacroAssembler::CopyBytes(Register source, Register scratch) { Label loop, done, short_string, short_loop; // Experimentation shows that the short string loop is faster if length < 10. - cmp(Operand(length), Immediate(10)); + cmp(length, Immediate(10)); j(less_equal, &short_string); ASSERT(source.is(esi)); @@ -1295,12 +1529,12 @@ void MacroAssembler::CopyBytes(Register source, mov(scratch, ecx); shr(ecx, 2); rep_movs(); - and_(Operand(scratch), Immediate(0x3)); - add(destination, Operand(scratch)); + and_(scratch, Immediate(0x3)); + add(destination, scratch); jmp(&done); bind(&short_string); - test(length, Operand(length)); + test(length, length); j(zero, &done); bind(&short_loop); @@ -1315,13 +1549,40 @@ void MacroAssembler::CopyBytes(Register source, } +void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler) { + Label loop, entry; + jmp(&entry); + bind(&loop); + mov(Operand(start_offset, 0), filler); + add(start_offset, Immediate(kPointerSize)); + bind(&entry); + cmp(start_offset, end_offset); + j(less, &loop); +} + + +void MacroAssembler::BooleanBitTest(Register object, + int field_offset, + int bit_index) { + bit_index += kSmiTagSize + kSmiShiftSize; + ASSERT(IsPowerOf2(kBitsPerByte)); + int byte_index = bit_index / kBitsPerByte; + int byte_bit_index = bit_index & (kBitsPerByte - 1); + test_b(FieldOperand(object, field_offset + byte_index), + static_cast<byte>(1 << byte_bit_index)); +} + + + void MacroAssembler::NegativeZeroTest(Register result, Register op, Label* then_label) { Label ok; - test(result, Operand(result)); + test(result, result); j(not_zero, &ok); - test(op, Operand(op)); + test(op, op); j(sign, then_label); bind(&ok); } @@ -1333,10 +1594,10 @@ void MacroAssembler::NegativeZeroTest(Register result, Register scratch, Label* then_label) { Label ok; - test(result, Operand(result)); + test(result, result); j(not_zero, &ok); - mov(scratch, Operand(op1)); - or_(scratch, Operand(op2)); + mov(scratch, op1); + or_(scratch, op2); j(sign, then_label); bind(&ok); } @@ -1345,7 +1606,8 @@ void MacroAssembler::NegativeZeroTest(Register result, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. JumpIfSmi(function, miss); @@ -1353,6 +1615,15 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CmpObjectType(function, JS_FUNCTION_TYPE, result); j(not_equal, miss); + if (miss_on_bound_function) { + // If a bound function, go to miss label. + mov(scratch, + FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); + BooleanBitTest(scratch, SharedFunctionInfo::kCompilerHintsOffset, + SharedFunctionInfo::kBoundFunction); + j(not_zero, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset)); @@ -1366,7 +1637,7 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, // If the prototype or initial map is the hole, don't return it and // simply miss the cache instead. This will allow us to allocate a // prototype object on-demand in the runtime system. - cmp(Operand(result), Immediate(isolate()->factory()->the_hole_value())); + cmp(result, Immediate(isolate()->factory()->the_hole_value())); j(equal, miss); // If the function does not have an initial map, we're done. @@ -1389,48 +1660,32 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, void MacroAssembler::CallStub(CodeStub* stub, unsigned ast_id) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. + ASSERT(AllowThisStubCall(stub)); // Calls are not allowed in some stubs. call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id); } -MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - call(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET); - return result; -} - - void MacroAssembler::TailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. + ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe()); jmp(stub->GetCode(), RelocInfo::CODE_TARGET); } -MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - jmp(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET); - return result; -} - - void MacroAssembler::StubReturn(int argc) { ASSERT(argc >= 1 && generating_stub()); ret((argc - 1) * kPointerSize); } +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { + if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false; + return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(); +} + + void MacroAssembler::IllegalOperation(int num_arguments) { if (num_arguments > 0) { - add(Operand(esp), Immediate(num_arguments * kPointerSize)); + add(esp, Immediate(num_arguments * kPointerSize)); } mov(eax, Immediate(isolate()->factory()->undefined_value())); } @@ -1464,18 +1719,11 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { const Runtime::Function* function = Runtime::FunctionForId(id); Set(eax, Immediate(function->nargs)); mov(ebx, Immediate(ExternalReference(function, isolate()))); - CEntryStub ces(1); - ces.SaveDoubles(); + CEntryStub ces(1, kSaveFPRegs); CallStub(&ces); } -MaybeObject* MacroAssembler::TryCallRuntime(Runtime::FunctionId id, - int num_arguments) { - return TryCallRuntime(Runtime::FunctionForId(id), num_arguments); -} - - void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments) { // If the expected number of arguments of the runtime function is @@ -1497,26 +1745,6 @@ void MacroAssembler::CallRuntime(const Runtime::Function* f, } -MaybeObject* MacroAssembler::TryCallRuntime(const Runtime::Function* f, - int num_arguments) { - if (f->nargs >= 0 && f->nargs != num_arguments) { - IllegalOperation(num_arguments); - // Since we did not call the stub, there was no allocation failure. - // Return some non-failure object. - return isolate()->heap()->undefined_value(); - } - - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - Set(eax, Immediate(num_arguments)); - mov(ebx, Immediate(ExternalReference(f, isolate()))); - CEntryStub ces(1); - return TryCallStub(&ces); -} - - void MacroAssembler::CallExternalReference(ExternalReference ref, int num_arguments) { mov(eax, Immediate(num_arguments)); @@ -1539,17 +1767,6 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } -MaybeObject* MacroAssembler::TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size) { - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - Set(eax, Immediate(num_arguments)); - return TryJumpToExternalReference(ext); -} - - void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -1559,14 +1776,6 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } -MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size) { - return TryTailCallExternalReference( - ExternalReference(fid, isolate()), num_arguments, result_size); -} - - // If true, a Handle<T> returned by value from a function with cdecl calling // convention will be returned directly as a value of location_ field in a // register eax. @@ -1615,8 +1824,8 @@ void MacroAssembler::PrepareCallApiFunction(int argc) { } -MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, - int stack_space) { +void MacroAssembler::CallApiFunctionAndReturn(Address function_address, + int stack_space) { ExternalReference next_address = ExternalReference::handle_scope_next_address(); ExternalReference limit_address = @@ -1629,8 +1838,8 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, mov(edi, Operand::StaticVariable(limit_address)); add(Operand::StaticVariable(level_address), Immediate(1)); - // Call the api function! - call(function->address(), RelocInfo::RUNTIME_ENTRY); + // Call the api function. + call(function_address, RelocInfo::RUNTIME_ENTRY); if (!kReturnHandlesDirectly) { // PrepareCallApiFunction saved pointer to the output slot into @@ -1645,7 +1854,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, Label leave_exit_frame; // Check if the result handle holds 0. - test(eax, Operand(eax)); + test(eax, eax); j(zero, &empty_handle); // It was non-zero. Dereference to get the result value. mov(eax, Operand(eax, 0)); @@ -1668,11 +1877,8 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, LeaveApiExitFrame(); ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - MaybeObject* result = - TryTailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); - if (result->IsFailure()) { - return result; - } + TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + bind(&empty_handle); // It was zero; the result is undefined. mov(eax, isolate()->factory()->undefined_value()); @@ -1686,11 +1892,9 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, mov(edi, eax); mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address())); mov(eax, Immediate(delete_extensions)); - call(Operand(eax)); + call(eax); mov(eax, edi); jmp(&leave_exit_frame); - - return result; } @@ -1702,15 +1906,6 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) { } -MaybeObject* MacroAssembler::TryJumpToExternalReference( - const ExternalReference& ext) { - // Set the entry point and jump to the C entry runtime stub. - mov(ebx, Immediate(ext)); - CEntryStub ces(1); - return TryTailCallStub(&ces); -} - - void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) { // This macro takes the dst register to make the code more readable // at the call sites. However, the dst register has to be ecx to @@ -1720,10 +1915,10 @@ void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) { if (call_kind == CALL_AS_FUNCTION) { // Set to some non-zero smi by updating the least significant // byte. - mov_b(Operand(dst), 1 << kSmiTagSize); + mov_b(dst, 1 << kSmiTagSize); } else { // Set to smi zero by clearing the register. - xor_(dst, Operand(dst)); + xor_(dst, dst); } } @@ -1733,11 +1928,13 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, Handle<Code> code_constant, const Operand& code_operand, Label* done, + bool* definitely_mismatches, InvokeFlag flag, Label::Distance done_near, const CallWrapper& call_wrapper, CallKind call_kind) { bool definitely_matches = false; + *definitely_mismatches = false; Label invoke; if (expected.is_immediate()) { ASSERT(actual.is_immediate()); @@ -1753,6 +1950,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, // arguments. definitely_matches = true; } else { + *definitely_mismatches = true; mov(ebx, expected.immediate()); } } @@ -1768,7 +1966,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, } else if (!expected.reg().is(actual.reg())) { // Both expected and actual are in (different) registers. This // is the case when we invoke functions using call and apply. - cmp(expected.reg(), Operand(actual.reg())); + cmp(expected.reg(), actual.reg()); j(equal, &invoke); ASSERT(actual.reg().is(eax)); ASSERT(expected.reg().is(ebx)); @@ -1780,7 +1978,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, isolate()->builtins()->ArgumentsAdaptorTrampoline(); if (!code_constant.is_null()) { mov(edx, Immediate(code_constant)); - add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag)); + add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag)); } else if (!code_operand.is_reg(edx)) { mov(edx, code_operand); } @@ -1790,7 +1988,9 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, SetCallKind(ecx, call_kind); call(adaptor, RelocInfo::CODE_TARGET); call_wrapper.AfterCall(); - jmp(done, done_near); + if (!*definitely_mismatches) { + jmp(done, done_near); + } } else { SetCallKind(ecx, call_kind); jmp(adaptor, RelocInfo::CODE_TARGET); @@ -1806,21 +2006,27 @@ void MacroAssembler::InvokeCode(const Operand& code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; + bool definitely_mismatches = false; InvokePrologue(expected, actual, Handle<Code>::null(), code, - &done, flag, Label::kNear, call_wrapper, - call_kind); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - SetCallKind(ecx, call_kind); - call(code); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(ecx, call_kind); - jmp(code); + &done, &definitely_mismatches, flag, Label::kNear, + call_wrapper, call_kind); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + SetCallKind(ecx, call_kind); + call(code); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(ecx, call_kind); + jmp(code); + } + bind(&done); } - bind(&done); } @@ -1831,21 +2037,27 @@ void MacroAssembler::InvokeCode(Handle<Code> code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; - Operand dummy(eax); - InvokePrologue(expected, actual, code, dummy, &done, flag, Label::kNear, - call_wrapper, call_kind); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code, rmode)); - SetCallKind(ecx, call_kind); - call(code, rmode); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(ecx, call_kind); - jmp(code, rmode); + Operand dummy(eax, 0); + bool definitely_mismatches = false; + InvokePrologue(expected, actual, code, dummy, &done, &definitely_mismatches, + flag, Label::kNear, call_wrapper, call_kind); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code, rmode)); + SetCallKind(ecx, call_kind); + call(code, rmode); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(ecx, call_kind); + jmp(code, rmode); + } + bind(&done); } - bind(&done); } @@ -1854,6 +2066,9 @@ void MacroAssembler::InvokeFunction(Register fun, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + ASSERT(fun.is(edi)); mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); @@ -1866,36 +2081,32 @@ void MacroAssembler::InvokeFunction(Register fun, } -void MacroAssembler::InvokeFunction(JSFunction* function, +void MacroAssembler::InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { - ASSERT(function->is_compiled()); + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + // Get the function and setup the context. - mov(edi, Immediate(Handle<JSFunction>(function))); + LoadHeapObject(edi, function); mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); ParameterCount expected(function->shared()->formal_parameter_count()); - if (V8::UseCrankshaft()) { - // TODO(kasperl): For now, we always call indirectly through the - // code field in the function to allow recompilation to take effect - // without changing any of the call sites. - InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), - expected, actual, flag, call_wrapper, call_kind); - } else { - Handle<Code> code(function->code()); - InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, - flag, call_wrapper, call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), + expected, actual, flag, call_wrapper, call_kind); } void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, const CallWrapper& call_wrapper) { - // Calls are not allowed in some stubs. - ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); + // You can't call a builtin without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); // Rely on the assertion to check that the number of provided // arguments match the expected number of arguments. Fake a @@ -1906,6 +2117,7 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, expected, expected, flag, call_wrapper, CALL_AS_METHOD); } + void MacroAssembler::GetBuiltinFunction(Register target, Builtins::JavaScript id) { // Load the JavaScript builtin function from the builtins object. @@ -1915,6 +2127,7 @@ void MacroAssembler::GetBuiltinFunction(Register target, JSBuiltinsObject::OffsetOfFunctionWithId(id))); } + void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { ASSERT(!target.is(edi)); // Load the JavaScript builtin function from the builtins object. @@ -1950,6 +2163,46 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match) { + // Load the global or builtins object from the current context. + mov(scratch, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); + mov(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); + + // Check that the function's map is the same as the expected cached map. + int expected_index = + Context::GetContextMapIndexFromElementsKind(expected_kind); + cmp(map_in_out, Operand(scratch, Context::SlotOffset(expected_index))); + j(not_equal, no_map_match); + + // Use the transitioned cached map. + int trans_index = + Context::GetContextMapIndexFromElementsKind(transitioned_kind); + mov(map_in_out, Operand(scratch, Context::SlotOffset(trans_index))); +} + + +void MacroAssembler::LoadInitialArrayMap( + Register function_in, Register scratch, Register map_out) { + ASSERT(!function_in.is(map_out)); + Label done; + mov(map_out, FieldOperand(function_in, + JSFunction::kPrototypeOrInitialMapOffset)); + if (!FLAG_smi_only_arrays) { + LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + map_out, + scratch, + &done); + } + bind(&done); +} + + void MacroAssembler::LoadGlobalFunction(int index, Register function) { // Load the global or builtins object from the current context. mov(function, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); @@ -2006,6 +2259,29 @@ int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { } +void MacroAssembler::LoadHeapObject(Register result, + Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + mov(result, Operand::Cell(cell)); + } else { + mov(result, object); + } +} + + +void MacroAssembler::PushHeapObject(Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + push(Operand::Cell(cell)); + } else { + Push(object); + } +} + + void MacroAssembler::Ret() { ret(0); } @@ -2016,7 +2292,7 @@ void MacroAssembler::Ret(int bytes_dropped, Register scratch) { ret(bytes_dropped); } else { pop(scratch); - add(Operand(esp), Immediate(bytes_dropped)); + add(esp, Immediate(bytes_dropped)); push(scratch); ret(0); } @@ -2025,7 +2301,7 @@ void MacroAssembler::Ret(int bytes_dropped, Register scratch) { void MacroAssembler::Drop(int stack_elements) { if (stack_elements > 0) { - add(Operand(esp), Immediate(stack_elements * kPointerSize)); + add(esp, Immediate(stack_elements * kPointerSize)); } } @@ -2037,11 +2313,6 @@ void MacroAssembler::Move(Register dst, Register src) { } -void MacroAssembler::Move(Register dst, Handle<Object> value) { - mov(dst, value); -} - - void MacroAssembler::SetCounter(StatsCounter* counter, int value) { if (FLAG_native_code_counters && counter->Enabled()) { mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value)); @@ -2168,13 +2439,19 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif - // Disable stub call restrictions to always allow calls to abort. - AllowStubCallsScope allow_scope(this, true); push(eax); push(Immediate(p0)); push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)))); - CallRuntime(Runtime::kAbort, 2); + // Disable stub call restrictions to always allow calls to abort. + if (!has_frame_) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + CallRuntime(Runtime::kAbort, 2); + } else { + CallRuntime(Runtime::kAbort, 2); + } // will not return here int3(); } @@ -2197,7 +2474,7 @@ void MacroAssembler::LoadPowerOf2(XMMRegister dst, ASSERT(is_uintn(power + HeapNumber::kExponentBias, HeapNumber::kExponentBits)); mov(scratch, Immediate(power + HeapNumber::kExponentBias)); - movd(dst, Operand(scratch)); + movd(dst, scratch); psllq(dst, HeapNumber::kMantissaBits); } @@ -2223,8 +2500,8 @@ void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register object1, Label* failure) { // Check that both objects are not smis. STATIC_ASSERT(kSmiTag == 0); - mov(scratch1, Operand(object1)); - and_(scratch1, Operand(object2)); + mov(scratch1, object1); + and_(scratch1, object2); JumpIfSmi(scratch1, failure); // Load instance type for both strings. @@ -2233,7 +2510,7 @@ void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register object1, movzx_b(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset)); movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset)); - // Check that both are flat ascii strings. + // Check that both are flat ASCII strings. const int kFlatAsciiStringMask = kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; const int kFlatAsciiStringTag = ASCII_STRING_TYPE; @@ -2253,12 +2530,12 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { // Make stack end at alignment and make room for num_arguments words // and the original value of esp. mov(scratch, esp); - sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize)); + sub(esp, Immediate((num_arguments + 1) * kPointerSize)); ASSERT(IsPowerOf2(frame_alignment)); and_(esp, -frame_alignment); mov(Operand(esp, num_arguments * kPointerSize), scratch); } else { - sub(Operand(esp), Immediate(num_arguments * kPointerSize)); + sub(esp, Immediate(num_arguments * kPointerSize)); } } @@ -2266,27 +2543,39 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { void MacroAssembler::CallCFunction(ExternalReference function, int num_arguments) { // Trashing eax is ok as it will be the return value. - mov(Operand(eax), Immediate(function)); + mov(eax, Immediate(function)); CallCFunction(eax, num_arguments); } void MacroAssembler::CallCFunction(Register function, int num_arguments) { + ASSERT(has_frame()); // Check stack alignment. if (emit_debug_code()) { CheckStackAlignment(); } - call(Operand(function)); + call(function); if (OS::ActivationFrameAlignment() != 0) { mov(esp, Operand(esp, num_arguments * kPointerSize)); } else { - add(Operand(esp), Immediate(num_arguments * kPointerSize)); + add(esp, Immediate(num_arguments * kPointerSize)); } } +bool AreAliased(Register r1, Register r2, Register r3, Register r4) { + if (r1.is(r2)) return true; + if (r1.is(r3)) return true; + if (r1.is(r4)) return true; + if (r2.is(r3)) return true; + if (r2.is(r4)) return true; + if (r3.is(r4)) return true; + return false; +} + + CodePatcher::CodePatcher(byte* address, int size) : address_(address), size_(size), @@ -2308,6 +2597,198 @@ CodePatcher::~CodePatcher() { } +void MacroAssembler::CheckPageFlag( + Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance) { + ASSERT(cc == zero || cc == not_zero); + if (scratch.is(object)) { + and_(scratch, Immediate(~Page::kPageAlignmentMask)); + } else { + mov(scratch, Immediate(~Page::kPageAlignmentMask)); + and_(scratch, object); + } + if (mask < (1 << kBitsPerByte)) { + test_b(Operand(scratch, MemoryChunk::kFlagsOffset), + static_cast<uint8_t>(mask)); + } else { + test(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); + } + j(cc, condition_met, condition_met_distance); +} + + +void MacroAssembler::JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black, + Label::Distance on_black_near) { + HasColor(object, scratch0, scratch1, + on_black, on_black_near, + 1, 0); // kBlackBitPattern. + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); +} + + +void MacroAssembler::HasColor(Register object, + Register bitmap_scratch, + Register mask_scratch, + Label* has_color, + Label::Distance has_color_distance, + int first_bit, + int second_bit) { + ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, ecx)); + + GetMarkBits(object, bitmap_scratch, mask_scratch); + + Label other_color, word_boundary; + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(first_bit == 1 ? zero : not_zero, &other_color, Label::kNear); + add(mask_scratch, mask_scratch); // Shift left 1 by adding. + j(zero, &word_boundary, Label::kNear); + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); + jmp(&other_color, Label::kNear); + + bind(&word_boundary); + test_b(Operand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize), 1); + + j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); + bind(&other_color); +} + + +void MacroAssembler::GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg) { + ASSERT(!AreAliased(addr_reg, mask_reg, bitmap_reg, ecx)); + mov(bitmap_reg, Immediate(~Page::kPageAlignmentMask)); + and_(bitmap_reg, addr_reg); + mov(ecx, addr_reg); + int shift = + Bitmap::kBitsPerCellLog2 + kPointerSizeLog2 - Bitmap::kBytesPerCellLog2; + shr(ecx, shift); + and_(ecx, + (Page::kPageAlignmentMask >> shift) & ~(Bitmap::kBytesPerCell - 1)); + + add(bitmap_reg, ecx); + mov(ecx, addr_reg); + shr(ecx, kPointerSizeLog2); + and_(ecx, (1 << Bitmap::kBitsPerCellLog2) - 1); + mov(mask_reg, Immediate(1)); + shl_cl(mask_reg); +} + + +void MacroAssembler::EnsureNotWhite( + Register value, + Register bitmap_scratch, + Register mask_scratch, + Label* value_is_white_and_not_data, + Label::Distance distance) { + ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, ecx)); + GetMarkBits(value, bitmap_scratch, mask_scratch); + + // If the value is black or grey we don't need to do anything. + ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); + ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + Label done; + + // Since both black and grey have a 1 in the first position and white does + // not have a 1 there we only need to check one bit. + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(not_zero, &done, Label::kNear); + + if (FLAG_debug_code) { + // Check for impossible bit pattern. + Label ok; + push(mask_scratch); + // shl. May overflow making the check conservative. + add(mask_scratch, mask_scratch); + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); + pop(mask_scratch); + } + + // Value is white. We check whether it is data that doesn't need scanning. + // Currently only checks for HeapNumber and non-cons strings. + Register map = ecx; // Holds map while checking type. + Register length = ecx; // Holds length of object after checking type. + Label not_heap_number; + Label is_data_object; + + // Check for heap-number + mov(map, FieldOperand(value, HeapObject::kMapOffset)); + cmp(map, FACTORY->heap_number_map()); + j(not_equal, ¬_heap_number, Label::kNear); + mov(length, Immediate(HeapNumber::kSize)); + jmp(&is_data_object, Label::kNear); + + bind(¬_heap_number); + // Check for strings. + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + Register instance_type = ecx; + movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset)); + test_b(instance_type, kIsIndirectStringMask | kIsNotStringMask); + j(not_zero, value_is_white_and_not_data); + // It's a non-indirect (non-cons and non-slice) string. + // If it's external, the length is just ExternalString::kSize. + // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). + Label not_external; + // External strings are the only ones with the kExternalStringTag bit + // set. + ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); + ASSERT_EQ(0, kConsStringTag & kExternalStringTag); + test_b(instance_type, kExternalStringTag); + j(zero, ¬_external, Label::kNear); + mov(length, Immediate(ExternalString::kSize)); + jmp(&is_data_object, Label::kNear); + + bind(¬_external); + // Sequential string, either ASCII or UC16. + ASSERT(kAsciiStringTag == 0x04); + and_(length, Immediate(kStringEncodingMask)); + xor_(length, Immediate(kStringEncodingMask)); + add(length, Immediate(0x04)); + // Value now either 4 (if ASCII) or 8 (if UC16), i.e., char-size shifted + // by 2. If we multiply the string length as smi by this, it still + // won't overflow a 32-bit value. + ASSERT_EQ(SeqAsciiString::kMaxSize, SeqTwoByteString::kMaxSize); + ASSERT(SeqAsciiString::kMaxSize <= + static_cast<int>(0xffffffffu >> (2 + kSmiTagSize))); + imul(length, FieldOperand(value, String::kLengthOffset)); + shr(length, 2 + kSmiTagSize + kSmiShiftSize); + add(length, Immediate(SeqString::kHeaderSize + kObjectAlignmentMask)); + and_(length, Immediate(~kObjectAlignmentMask)); + + bind(&is_data_object); + // Value is a data object, and it is white. Mark it black. Since we know + // that the object is white we can make it black by flipping one bit. + or_(Operand(bitmap_scratch, MemoryChunk::kHeaderSize), mask_scratch); + + and_(bitmap_scratch, Immediate(~Page::kPageAlignmentMask)); + add(Operand(bitmap_scratch, MemoryChunk::kLiveBytesOffset), + length); + if (FLAG_debug_code) { + mov(length, Operand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + cmp(length, Operand(bitmap_scratch, MemoryChunk::kSizeOffset)); + Check(less_equal, "Live Bytes Count overflow chunk size"); + } + + bind(&done); +} + } } // namespace v8::internal #endif // V8_TARGET_ARCH_IA32 diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 8c5f5e9421..b06d801b64 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,7 @@ #define V8_IA32_MACRO_ASSEMBLER_IA32_H_ #include "assembler.h" +#include "frames.h" #include "v8globals.h" namespace v8 { @@ -50,6 +51,13 @@ enum AllocationFlags { // distinguish memory operands from other operands on ia32. typedef Operand MemOperand; +enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; + + +bool AreAliased(Register r1, Register r2, Register r3, Register r4); + + // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: @@ -61,42 +69,130 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // GC Support + enum RememberedSetFinalAction { + kReturnAtEnd, + kFallThroughAtEnd + }; + + // Record in the remembered set the fact that we have a pointer to new space + // at the address pointed to by the addr register. Only works if addr is not + // in new space. + void RememberedSetHelper(Register object, // Used for debug code. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then); + + void CheckPageFlag(Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Check if object is in new space. Jumps if the object is not in new space. + // The register scratch can be object itself, but scratch will be clobbered. + void JumpIfNotInNewSpace(Register object, + Register scratch, + Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, zero, branch, distance); + } - // For page containing |object| mark region covering |addr| dirty. - // RecordWriteHelper only works if the object is not in new - // space. - void RecordWriteHelper(Register object, - Register addr, - Register scratch); + // Check if object is in new space. Jumps if the object is in new space. + // The register scratch can be object itself, but it will be clobbered. + void JumpIfInNewSpace(Register object, + Register scratch, + Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, not_zero, branch, distance); + } - // Check if object is in new space. - // scratch can be object itself, but it will be clobbered. - void InNewSpace(Register object, - Register scratch, - Condition cc, // equal for new space, not_equal otherwise. - Label* branch, - Label::Distance branch_near = Label::kFar); + // Check if an object has a given incremental marking color. Also uses ecx! + void HasColor(Register object, + Register scratch0, + Register scratch1, + Label* has_color, + Label::Distance has_color_distance, + int first_bit, + int second_bit); + + void JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black, + Label::Distance on_black_distance = Label::kFar); + + // Checks the color of an object. If the object is already grey or black + // then we just fall through, since it is already live. If it is white and + // we can determine that it doesn't need to be scanned, then we just mark it + // black and fall through. For the rest we jump to the label so the + // incremental marker can fix its assumptions. + void EnsureNotWhite(Register object, + Register scratch1, + Register scratch2, + Label* object_is_white_and_not_data, + Label::Distance distance); + + // Notify the garbage collector that we wrote a pointer into an object. + // |object| is the object being stored into, |value| is the object being + // stored. value and scratch registers are clobbered by the operation. + // The offset is the offset from the start of the object, not the offset from + // the tagged HeapObject pointer. For use with FieldOperand(reg, off). + void RecordWriteField( + Register object, + int offset, + Register value, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // As above, but the offset has the tag presubtracted. For use with + // Operand(reg, off). + void RecordWriteContextSlot( + Register context, + int offset, + Register value, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK) { + RecordWriteField(context, + offset + kHeapObjectTag, + value, + scratch, + save_fp, + remembered_set_action, + smi_check); + } - // For page containing |object| mark region covering [object+offset] - // dirty. |object| is the object being stored into, |value| is the - // object being stored. If offset is zero, then the scratch register - // contains the array index into the elements array represented as a - // Smi. All registers are clobbered by the operation. RecordWrite + // Notify the garbage collector that we wrote a pointer into a fixed array. + // |array| is the array being stored into, |value| is the + // object being stored. |index| is the array index represented as a + // Smi. All registers are clobbered by the operation RecordWriteArray // filters out smis so it does not update the write barrier if the // value is a smi. - void RecordWrite(Register object, - int offset, - Register value, - Register scratch); + void RecordWriteArray( + Register array, + Register value, + Register index, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); // For page containing |object| mark region covering |address| // dirty. |object| is the object being stored into, |value| is the - // object being stored. All registers are clobbered by the + // object being stored. The address and value registers are clobbered by the // operation. RecordWrite filters out smis so it does not update the // write barrier if the value is a smi. - void RecordWrite(Register object, - Register address, - Register value); + void RecordWrite( + Register object, + Register address, + Register value, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); #ifdef ENABLE_DEBUGGER_SUPPORT // --------------------------------------------------------------------------- @@ -105,15 +201,6 @@ class MacroAssembler: public Assembler { void DebugBreak(); #endif - // --------------------------------------------------------------------------- - // Activation frames - - void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } - void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); } - - void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } - void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter specific kind of exit frame. Expects the number of // arguments in register eax and sets up the number of arguments in // register edi and the pointer to the first argument in register @@ -134,6 +221,22 @@ class MacroAssembler: public Assembler { // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); + // Conditionally load the cached Array transitioned map of type + // transitioned_kind from the global context if the map in register + // map_in_out is the cached Array map in the global context of + // expected_kind. + void LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match); + + // Load the initial map for new Arrays from a JSFunction. + void LoadInitialArrayMap(Register function_in, + Register scratch, + Register map_out); + // Load the global function with the given index. void LoadGlobalFunction(int index, Register function); @@ -150,15 +253,35 @@ class MacroAssembler: public Assembler { void StoreToSafepointRegisterSlot(Register dst, Immediate src); void LoadFromSafepointRegisterSlot(Register dst, Register src); + void LoadHeapObject(Register result, Handle<HeapObject> object); + void PushHeapObject(Handle<HeapObject> object); + + void LoadObject(Register result, Handle<Object> object) { + if (object->IsHeapObject()) { + LoadHeapObject(result, Handle<HeapObject>::cast(object)); + } else { + Set(result, Immediate(object)); + } + } + // --------------------------------------------------------------------------- // JavaScript invokes - // Setup call kind marking in ecx. The method takes ecx as an + // Set up call kind marking in ecx. The method takes ecx as an // explicit first parameter to make the code more readable at the // call sites. void SetCallKind(Register dst, CallKind kind); // Invoke the JavaScript function code by either calling or jumping. + void InvokeCode(Register code, + const ParameterCount& expected, + const ParameterCount& actual, + InvokeFlag flag, + const CallWrapper& call_wrapper, + CallKind call_kind) { + InvokeCode(Operand(code), expected, actual, flag, call_wrapper, call_kind); + } + void InvokeCode(const Operand& code, const ParameterCount& expected, const ParameterCount& actual, @@ -182,7 +305,7 @@ class MacroAssembler: public Assembler { const CallWrapper& call_wrapper, CallKind call_kind); - void InvokeFunction(JSFunction* function, + void InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper, @@ -209,8 +332,9 @@ class MacroAssembler: public Assembler { void SafeSet(Register dst, const Immediate& x); void SafePush(const Immediate& x); - // Compare a register against a known root, e.g. undefined, null, true, ... + // Compare against a known root, e.g. undefined, null, true, ... void CompareRoot(Register with, Heap::RootListIndex index); + void CompareRoot(const Operand& with, Heap::RootListIndex index); // Compare object type for heap object. // Incoming register is heap_object and outgoing register is map. @@ -225,13 +349,47 @@ class MacroAssembler: public Assembler { Label* fail, Label::Distance distance = Label::kFar); + // Check if a map for a JSObject indicates that the object can have both smi + // and HeapObject elements. Jump to the specified label if it does not. + void CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + + // Check if a map for a JSObject indicates that the object has fast smi only + // elements. Jump to the specified label if it does not. + void CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + + // Check to see if maybe_number can be stored as a double in + // FastDoubleElements. If it can, store it at the index specified by key in + // the FastDoubleElements array elements, otherwise jump to fail. + void StoreNumberToDoubleElements(Register maybe_number, + Register elements, + Register key, + Register scratch1, + XMMRegister scratch2, + Label* fail, + bool specialize_for_processor); + + // Compare an object's map with the specified map and its transitioned + // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. FLAGS are set with + // result of map compare. If multiple map compares are required, the compare + // sequences branches to early_success. + void CompareMap(Register obj, + Handle<Map> map, + Label* early_success, + CompareMapMode mode = REQUIRE_EXACT_MAP); + // Check if the map of an object is equal to a specified map and branch to // label if not. Skip the smi check if not required (object is known to be a - // heap object) + // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match + // against maps that are ElementsKind transition maps of the specified map. void CheckMap(Register obj, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type); + SmiCheckType smi_check_type, + CompareMapMode mode = REQUIRE_EXACT_MAP); // Check if the map of an object is equal to a specified map and branch to a // specified target if equal. Skip the smi check if not required (object is @@ -277,7 +435,7 @@ class MacroAssembler: public Assembler { void SmiTag(Register reg) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); - add(reg, Operand(reg)); + add(reg, reg); } void SmiUntag(Register reg) { sar(reg, kSmiTagSize); @@ -332,9 +490,8 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Exception handling - // Push a new try handler and link into try handler chain. The return - // address must be pushed before calling this helper. - void PushTryHandler(CodeLocation try_location, HandlerType type); + // Push a new try handler and link it into try handler chain. + void PushTryHandler(StackHandler::Kind kind, int handler_index); // Unlink the stack handler on top of the stack from the try handler chain. void PopTryHandler(); @@ -466,9 +623,19 @@ class MacroAssembler: public Assembler { Register length, Register scratch); + // Initialize fields with filler values. Fields starting at |start_offset| + // not including end_offset are overwritten with the value in |filler|. At + // the end the loop, |start_offset| takes the value of |end_offset|. + void InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler); + // --------------------------------------------------------------------------- // Support functions. + // Check a boolean-bit of a Smi field. + void BooleanBitTest(Register object, int field_offset, int bit_index); + // Check if result is zero and op is negative. void NegativeZeroTest(Register result, Register op, Label* then_label); @@ -485,7 +652,8 @@ class MacroAssembler: public Assembler { void TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Generates code for reporting that an illegal operation has // occurred. @@ -503,19 +671,9 @@ class MacroAssembler: public Assembler { // Call a code stub. Generate the code if necessary. void CallStub(CodeStub* stub, unsigned ast_id = kNoASTId); - // Call a code stub and return the code object called. Try to generate - // the code if necessary. Do not perform a GC but instead return a retry - // after GC failure. - MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub); - // Tail call a code stub (jump). Generate the code if necessary. void TailCallStub(CodeStub* stub); - // Tail call a code stub (jump) and return the code object called. Try to - // generate the code if necessary. Do not perform a GC but instead return - // a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallStub(CodeStub* stub); - // Return from a code stub after popping its arguments. void StubReturn(int argc); @@ -523,19 +681,9 @@ class MacroAssembler: public Assembler { void CallRuntime(const Runtime::Function* f, int num_arguments); void CallRuntimeSaveDoubles(Runtime::FunctionId id); - // Call a runtime function, returning the CodeStub object called. - // Try to generate the stub code if necessary. Do not perform a GC - // but instead return a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryCallRuntime(const Runtime::Function* f, - int num_arguments); - // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); - // Convenience function: Same as above, but takes the fid instead. - MUST_USE_RESULT MaybeObject* TryCallRuntime(Runtime::FunctionId id, - int num_arguments); - // Convenience function: call an external reference. void CallExternalReference(ExternalReference ref, int num_arguments); @@ -546,23 +694,11 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); - // Tail call of a runtime routine (jump). Try to generate the code if - // necessary. Do not perform a GC but instead return a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size); - // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); - // Convenience function: tail call a runtime routine (jump). Try to generate - // the code if necessary. Do not perform a GC but instead return a retry after - // GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size); - // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], // etc., not pushed. The argument count assumes all arguments are word sized. @@ -587,19 +723,15 @@ class MacroAssembler: public Assembler { // stores the pointer to the reserved slot into esi. void PrepareCallApiFunction(int argc); - // Calls an API function. Allocates HandleScope, extracts - // returned value from handle and propagates exceptions. - // Clobbers ebx, edi and caller-save registers. Restores context. - // On return removes stack_space * kPointerSize (GCed). - MaybeObject* TryCallApiFunctionAndReturn(ApiFunction* function, - int stack_space); + // Calls an API function. Allocates HandleScope, extracts returned value + // from handle and propagates exceptions. Clobbers ebx, edi and + // caller-save registers. Restores context. On return removes + // stack_space * kPointerSize (GCed). + void CallApiFunctionAndReturn(Address function_address, int stack_space); // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext); - MaybeObject* TryJumpToExternalReference(const ExternalReference& ext); - - // --------------------------------------------------------------------------- // Utilities @@ -624,10 +756,8 @@ class MacroAssembler: public Assembler { // Move if the registers are not identical. void Move(Register target, Register source); - void Move(Register target, Handle<Object> value); - // Push a handle value. - void Push(Handle<Object> handle) { push(handle); } + void Push(Handle<Object> handle) { push(Immediate(handle)); } Handle<Object> CodeObject() { ASSERT(!code_object_.is_null()); @@ -668,11 +798,14 @@ class MacroAssembler: public Assembler { bool generating_stub() { return generating_stub_; } void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } bool allow_stub_calls() { return allow_stub_calls_; } + void set_has_frame(bool value) { has_frame_ = value; } + bool has_frame() { return has_frame_; } + inline bool AllowThisStubCall(CodeStub* stub); // --------------------------------------------------------------------------- // String utilities. - // Check whether the instance type represents a flat ascii string. Jump to the + // Check whether the instance type represents a flat ASCII string. Jump to the // label if not. If the instance type can be scratched specify same register // for both instance type and scratch. void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type, @@ -691,9 +824,14 @@ class MacroAssembler: public Assembler { return SafepointRegisterStackIndex(reg.code()); } + // Activation support. + void EnterFrame(StackFrame::Type type); + void LeaveFrame(StackFrame::Type type); + private: bool generating_stub_; bool allow_stub_calls_; + bool has_frame_; // This handle will be patched with the code object on installation. Handle<Object> code_object_; @@ -703,15 +841,12 @@ class MacroAssembler: public Assembler { Handle<Code> code_constant, const Operand& code_operand, Label* done, + bool* definitely_mismatches, InvokeFlag flag, - Label::Distance done_near = Label::kFar, + Label::Distance done_distance, const CallWrapper& call_wrapper = NullCallWrapper(), CallKind call_kind = CALL_AS_METHOD); - // Activation support. - void EnterFrame(StackFrame::Type type); - void LeaveFrame(StackFrame::Type type); - void EnterExitFramePrologue(); void EnterExitFrameEpilogue(int argc, bool save_doubles); @@ -730,6 +865,24 @@ class MacroAssembler: public Assembler { Register scratch, bool gc_allowed); + // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. + void InNewSpace(Register object, + Register scratch, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Helper for finding the mark bits for an address. Afterwards, the + // bitmap register points at the word with the mark bits and the mask + // the position of the first bit. Uses ecx as scratch and leaves addr_reg + // unchanged. + inline void GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg); + + // Helper for throwing exceptions. Compute a handler address and jump to + // it. See the implementation for register usage. + void JumpToHandlerEntry(); // Compute memory operands for safepoint stack slots. Operand SafepointRegisterSlot(Register reg); @@ -765,26 +918,26 @@ class CodePatcher { // Static helper functions. // Generate an Operand for loading a field from an object. -static inline Operand FieldOperand(Register object, int offset) { +inline Operand FieldOperand(Register object, int offset) { return Operand(object, offset - kHeapObjectTag); } // Generate an Operand for loading an indexed field from an object. -static inline Operand FieldOperand(Register object, - Register index, - ScaleFactor scale, - int offset) { +inline Operand FieldOperand(Register object, + Register index, + ScaleFactor scale, + int offset) { return Operand(object, index, scale, offset - kHeapObjectTag); } -static inline Operand ContextOperand(Register context, int index) { +inline Operand ContextOperand(Register context, int index) { return Operand(context, Context::SlotOffset(index)); } -static inline Operand GlobalObjectOperand() { +inline Operand GlobalObjectOperand() { return ContextOperand(esi, Context::GLOBAL_INDEX); } diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc index d175d9e036..e613a06c3c 100644 --- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2008-2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -134,7 +134,7 @@ int RegExpMacroAssemblerIA32::stack_limit_slack() { void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) { if (by != 0) { - __ add(Operand(edi), Immediate(by * char_size())); + __ add(edi, Immediate(by * char_size())); } } @@ -152,8 +152,8 @@ void RegExpMacroAssemblerIA32::Backtrack() { CheckPreemption(); // Pop Code* offset from backtrack stack, add Code* and jump to location. Pop(ebx); - __ add(Operand(ebx), Immediate(masm_->CodeObject())); - __ jmp(Operand(ebx)); + __ add(ebx, Immediate(masm_->CodeObject())); + __ jmp(ebx); } @@ -210,7 +210,7 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str, bool check_end_of_string) { #ifdef DEBUG // If input is ASCII, don't even bother calling here if the string to - // match contains a non-ascii character. + // match contains a non-ASCII character. if (mode_ == ASCII) { ASSERT(String::IsAscii(str.start(), str.length())); } @@ -219,7 +219,7 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str, int byte_offset = cp_offset * char_size(); if (check_end_of_string) { // Check that there are at least str.length() characters left in the input. - __ cmp(Operand(edi), Immediate(-(byte_offset + byte_length))); + __ cmp(edi, Immediate(-(byte_offset + byte_length))); BranchOrBacktrack(greater, on_failure); } @@ -288,7 +288,7 @@ void RegExpMacroAssemblerIA32::CheckGreedyLoop(Label* on_equal) { Label fallthrough; __ cmp(edi, Operand(backtrack_stackpointer(), 0)); __ j(not_equal, &fallthrough); - __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); // Pop. + __ add(backtrack_stackpointer(), Immediate(kPointerSize)); // Pop. BranchOrBacktrack(no_condition, on_equal); __ bind(&fallthrough); } @@ -300,7 +300,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( Label fallthrough; __ mov(edx, register_location(start_reg)); // Index of start of capture __ mov(ebx, register_location(start_reg + 1)); // Index of end of capture - __ sub(ebx, Operand(edx)); // Length of capture. + __ sub(ebx, edx); // Length of capture. // The length of a capture should not be negative. This can only happen // if the end of the capture is unrecorded, or at a point earlier than @@ -320,9 +320,9 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( __ push(backtrack_stackpointer()); // After this, the eax, ecx, and edi registers are available. - __ add(edx, Operand(esi)); // Start of capture - __ add(edi, Operand(esi)); // Start of text to match against capture. - __ add(ebx, Operand(edi)); // End of text to match against capture. + __ add(edx, esi); // Start of capture + __ add(edi, esi); // Start of text to match against capture. + __ add(ebx, edi); // End of text to match against capture. Label loop; __ bind(&loop); @@ -339,15 +339,15 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( __ movzx_b(ecx, Operand(edx, 0)); __ or_(ecx, 0x20); - __ cmp(eax, Operand(ecx)); + __ cmp(eax, ecx); __ j(not_equal, &fail); __ bind(&loop_increment); // Increment pointers into match and capture strings. - __ add(Operand(edx), Immediate(1)); - __ add(Operand(edi), Immediate(1)); + __ add(edx, Immediate(1)); + __ add(edi, Immediate(1)); // Compare to end of match, and loop if not done. - __ cmp(edi, Operand(ebx)); + __ cmp(edi, ebx); __ j(below, &loop); __ jmp(&success); @@ -361,9 +361,9 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( // Restore original value before continuing. __ pop(backtrack_stackpointer()); // Drop original value of character position. - __ add(Operand(esp), Immediate(kPointerSize)); + __ add(esp, Immediate(kPointerSize)); // Compute new value of character position after the matched part. - __ sub(edi, Operand(esi)); + __ sub(edi, esi); } else { ASSERT(mode_ == UC16); // Save registers before calling C function. @@ -389,16 +389,19 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( // Set byte_offset2. // Found by adding negative string-end offset of current position (edi) // to end of string. - __ add(edi, Operand(esi)); + __ add(edi, esi); __ mov(Operand(esp, 1 * kPointerSize), edi); // Set byte_offset1. // Start of capture, where edx already holds string-end negative offset. - __ add(edx, Operand(esi)); + __ add(edx, esi); __ mov(Operand(esp, 0 * kPointerSize), edx); - ExternalReference compare = - ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); - __ CallCFunction(compare, argument_count); + { + AllowExternalCallThatCantCauseGC scope(masm_); + ExternalReference compare = + ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); + __ CallCFunction(compare, argument_count); + } // Pop original values before reacting on result value. __ pop(ebx); __ pop(backtrack_stackpointer()); @@ -406,10 +409,10 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( __ pop(esi); // Check if function returned non-zero for success or zero for failure. - __ or_(eax, Operand(eax)); + __ or_(eax, eax); BranchOrBacktrack(zero, on_no_match); // On success, increment position by length of capture. - __ add(edi, Operand(ebx)); + __ add(edi, ebx); } __ bind(&fallthrough); } @@ -425,7 +428,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( // Find length of back-referenced capture. __ mov(edx, register_location(start_reg)); __ mov(eax, register_location(start_reg + 1)); - __ sub(eax, Operand(edx)); // Length to check. + __ sub(eax, edx); // Length to check. // Fail on partial or illegal capture (start of capture after end of capture). BranchOrBacktrack(less, on_no_match); // Succeed on empty capture (including no capture) @@ -433,7 +436,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( // Check that there are sufficient characters left in the input. __ mov(ebx, edi); - __ add(ebx, Operand(eax)); + __ add(ebx, eax); BranchOrBacktrack(greater, on_no_match); // Save register to make it available below. @@ -441,7 +444,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( // Compute pointers to match string and capture string __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match. - __ add(edx, Operand(esi)); // Start of capture. + __ add(edx, esi); // Start of capture. __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match Label loop; @@ -456,10 +459,10 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( } __ j(not_equal, &fail); // Increment pointers into capture and match string. - __ add(Operand(edx), Immediate(char_size())); - __ add(Operand(ebx), Immediate(char_size())); + __ add(edx, Immediate(char_size())); + __ add(ebx, Immediate(char_size())); // Check if we have reached end of match area. - __ cmp(ebx, Operand(ecx)); + __ cmp(ebx, ecx); __ j(below, &loop); __ jmp(&success); @@ -471,7 +474,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( __ bind(&success); // Move current character position to position after match. __ mov(edi, ecx); - __ sub(Operand(edi), esi); + __ sub(edi, esi); // Restore backtrack stackpointer. __ pop(backtrack_stackpointer()); @@ -574,17 +577,17 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, return true; case '.': { // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) - __ mov(Operand(eax), current_character()); - __ xor_(Operand(eax), Immediate(0x01)); + __ mov(eax, current_character()); + __ xor_(eax, Immediate(0x01)); // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c - __ sub(Operand(eax), Immediate(0x0b)); + __ sub(eax, Immediate(0x0b)); __ cmp(eax, 0x0c - 0x0b); BranchOrBacktrack(below_equal, on_no_match); if (mode_ == UC16) { // Compare original value to 0x2028 and 0x2029, using the already // computed (current_char ^ 0x01 - 0x0b). I.e., check for // 0x201d (0x2028 - 0x0b) or 0x201e. - __ sub(Operand(eax), Immediate(0x2028 - 0x0b)); + __ sub(eax, Immediate(0x2028 - 0x0b)); __ cmp(eax, 0x2029 - 0x2028); BranchOrBacktrack(below_equal, on_no_match); } @@ -593,7 +596,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, case 'w': { if (mode_ != ASCII) { // Table is 128 entries, so all ASCII characters can be tested. - __ cmp(Operand(current_character()), Immediate('z')); + __ cmp(current_character(), Immediate('z')); BranchOrBacktrack(above, on_no_match); } ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char. @@ -607,7 +610,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, Label done; if (mode_ != ASCII) { // Table is 128 entries, so all ASCII characters can be tested. - __ cmp(Operand(current_character()), Immediate('z')); + __ cmp(current_character(), Immediate('z')); __ j(above, &done); } ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char. @@ -627,10 +630,10 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, case 'n': { // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029). // The opposite of '.'. - __ mov(Operand(eax), current_character()); - __ xor_(Operand(eax), Immediate(0x01)); + __ mov(eax, current_character()); + __ xor_(eax, Immediate(0x01)); // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c - __ sub(Operand(eax), Immediate(0x0b)); + __ sub(eax, Immediate(0x0b)); __ cmp(eax, 0x0c - 0x0b); if (mode_ == ASCII) { BranchOrBacktrack(above, on_no_match); @@ -641,7 +644,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, // Compare original value to 0x2028 and 0x2029, using the already // computed (current_char ^ 0x01 - 0x0b). I.e., check for // 0x201d (0x2028 - 0x0b) or 0x201e. - __ sub(Operand(eax), Immediate(0x2028 - 0x0b)); + __ sub(eax, Immediate(0x2028 - 0x0b)); __ cmp(eax, 1); BranchOrBacktrack(above, on_no_match); __ bind(&done); @@ -668,7 +671,12 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { // Entry code: __ bind(&entry_label_); - // Start new stack frame. + + // Tell the system that we have a stack frame. Because the type is MANUAL, no + // code is generated. + FrameScope scope(masm_, StackFrame::MANUAL); + + // Actually emit code to start a new stack frame. __ push(ebp); __ mov(ebp, esp); // Save callee-save registers. Order here should correspond to order of @@ -699,7 +707,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ bind(&stack_limit_hit); CallCheckStackGuardState(ebx); - __ or_(eax, Operand(eax)); + __ or_(eax, eax); // If returned value is non-zero, we exit with the returned value as result. __ j(not_zero, &exit_label_); @@ -708,13 +716,13 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ mov(ebx, Operand(ebp, kStartIndex)); // Allocate space on stack for registers. - __ sub(Operand(esp), Immediate(num_registers_ * kPointerSize)); + __ sub(esp, Immediate(num_registers_ * kPointerSize)); // Load string length. __ mov(esi, Operand(ebp, kInputEnd)); // Load input position. __ mov(edi, Operand(ebp, kInputStart)); // Set up edi to be negative offset from string end. - __ sub(edi, Operand(esi)); + __ sub(edi, esi); // Set eax to address of char before start of the string. // (effectively string position -1). @@ -736,7 +744,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { Label init_loop; __ bind(&init_loop); __ mov(Operand(ebp, ecx, times_1, +0), eax); - __ sub(Operand(ecx), Immediate(kPointerSize)); + __ sub(ecx, Immediate(kPointerSize)); __ cmp(ecx, kRegisterZero - num_saved_registers_ * kPointerSize); __ j(greater, &init_loop); } @@ -777,12 +785,12 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { if (mode_ == UC16) { __ lea(ecx, Operand(ecx, edx, times_2, 0)); } else { - __ add(ecx, Operand(edx)); + __ add(ecx, edx); } for (int i = 0; i < num_saved_registers_; i++) { __ mov(eax, register_location(i)); // Convert to index from start of string, not end. - __ add(eax, Operand(ecx)); + __ add(eax, ecx); if (mode_ == UC16) { __ sar(eax, 1); // Convert byte index to character index. } @@ -819,7 +827,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ push(edi); CallCheckStackGuardState(ebx); - __ or_(eax, Operand(eax)); + __ or_(eax, eax); // If returning non-zero, we should end execution with the given // result as return value. __ j(not_zero, &exit_label_); @@ -854,7 +862,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) { __ CallCFunction(grow_stack, num_arguments); // If return NULL, we have failed to grow the stack, and // must exit with a stack-overflow exception. - __ or_(eax, Operand(eax)); + __ or_(eax, eax); __ j(equal, &exit_with_exception); // Otherwise use return value as new stack pointer. __ mov(backtrack_stackpointer(), eax); @@ -1133,6 +1141,11 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address* return_address, frame_entry<const String*>(re_frame, kInputString) = *subject; frame_entry<const byte*>(re_frame, kInputStart) = new_address; frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length; + } else if (frame_entry<const String*>(re_frame, kInputString) != *subject) { + // Subject string might have been a ConsString that underwent + // short-circuiting during GC. That will not change start_address but + // will change pointer inside the subject handle. + frame_entry<const String*>(re_frame, kInputString) = *subject; } return 0; @@ -1183,8 +1196,8 @@ void RegExpMacroAssemblerIA32::SafeCall(Label* to) { void RegExpMacroAssemblerIA32::SafeReturn() { __ pop(ebx); - __ add(Operand(ebx), Immediate(masm_->CodeObject())); - __ jmp(Operand(ebx)); + __ add(ebx, Immediate(masm_->CodeObject())); + __ jmp(ebx); } @@ -1196,14 +1209,14 @@ void RegExpMacroAssemblerIA32::SafeCallTarget(Label* name) { void RegExpMacroAssemblerIA32::Push(Register source) { ASSERT(!source.is(backtrack_stackpointer())); // Notice: This updates flags, unlike normal Push. - __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); + __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); __ mov(Operand(backtrack_stackpointer(), 0), source); } void RegExpMacroAssemblerIA32::Push(Immediate value) { // Notice: This updates flags, unlike normal Push. - __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); + __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); __ mov(Operand(backtrack_stackpointer(), 0), value); } @@ -1212,7 +1225,7 @@ void RegExpMacroAssemblerIA32::Pop(Register target) { ASSERT(!target.is(backtrack_stackpointer())); __ mov(target, Operand(backtrack_stackpointer(), 0)); // Notice: This updates flags, unlike normal Pop. - __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); + __ add(backtrack_stackpointer(), Immediate(kPointerSize)); } diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index ab62764e64..9717869b5e 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -66,8 +66,8 @@ static void ProbeTable(Isolate* isolate, __ j(not_equal, &miss); // Jump to the first instruction in the code stub. - __ add(Operand(extra), Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ jmp(Operand(extra)); + __ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ jmp(extra); __ bind(&miss); } else { @@ -92,8 +92,8 @@ static void ProbeTable(Isolate* isolate, __ mov(offset, Operand::StaticArray(offset, times_2, value_offset)); // Jump to the first instruction in the code stub. - __ add(Operand(offset), Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ jmp(Operand(offset)); + __ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ jmp(offset); // Pop at miss. __ bind(&miss); @@ -107,12 +107,12 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -static MaybeObject* GenerateDictionaryNegativeLookup(MacroAssembler* masm, - Label* miss_label, - Register receiver, - String* name, - Register r0, - Register r1) { +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle<String> name, + Register r0, + Register r1) { ASSERT(name->IsSymbol()); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->negative_lookups(), 1); @@ -142,19 +142,14 @@ static MaybeObject* GenerateDictionaryNegativeLookup(MacroAssembler* masm, __ j(not_equal, miss_label); Label done; - MaybeObject* result = - StringDictionaryLookupStub::GenerateNegativeLookup(masm, - miss_label, - &done, - properties, - name, - r1); - if (result->IsFailure()) return result; - + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + properties, + name, + r1); __ bind(&done); __ DecrementCounter(counters->negative_lookups_miss(), 1); - - return result; } @@ -165,25 +160,23 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Register scratch, Register extra, Register extra2) { - Isolate* isolate = Isolate::Current(); Label miss; - USE(extra2); // The register extra2 is not used on the ia32 platform. - // Make sure that code is valid. The shifting code relies on the - // entry size being 8. + // Assert that code is valid. The shifting code relies on the entry size + // being 8. ASSERT(sizeof(Entry) == 8); - // Make sure the flags does not name a specific type. + // Assert the flags do not name a specific type. ASSERT(Code::ExtractTypeFromFlags(flags) == 0); - // Make sure that there are no register conflicts. + // Assert that there are no register conflicts. ASSERT(!scratch.is(receiver)); ASSERT(!scratch.is(name)); ASSERT(!extra.is(receiver)); ASSERT(!extra.is(name)); ASSERT(!extra.is(scratch)); - // Check scratch and extra registers are valid, and extra2 is unused. + // Assert scratch and extra registers are valid, and extra2 is unused. ASSERT(!scratch.is(no_reg)); ASSERT(extra2.is(no_reg)); @@ -197,19 +190,19 @@ void StubCache::GenerateProbe(MacroAssembler* masm, __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); // Probe the primary table. - ProbeTable(isolate, masm, flags, kPrimary, name, scratch, extra); + ProbeTable(isolate(), masm, flags, kPrimary, name, scratch, extra); // Primary miss: Compute hash for secondary probe. __ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); __ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ xor_(scratch, flags); __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); - __ sub(scratch, Operand(name)); - __ add(Operand(scratch), Immediate(flags)); + __ sub(scratch, name); + __ add(scratch, Immediate(flags)); __ and_(scratch, (kSecondaryTableSize - 1) << kHeapObjectTagSize); // Probe the secondary table. - ProbeTable(isolate, masm, flags, kSecondary, name, scratch, extra); + ProbeTable(isolate(), masm, flags, kSecondary, name, scratch, extra); // Cache miss: Fall-through and let caller handle the miss by // entering the runtime system. @@ -228,14 +221,17 @@ void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, int index, Register prototype, Label* miss) { + MacroAssembler* masm, + int index, + Register prototype, + Label* miss) { // Check we're still in the same context. __ cmp(Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)), masm->isolate()->global()); __ j(not_equal, miss); // Get the global function with the given index. - JSFunction* function = - JSFunction::cast(masm->isolate()->global_context()->get(index)); + Handle<JSFunction> function( + JSFunction::cast(masm->isolate()->global_context()->get(index))); // Load its initial map. The global functions all have initial maps. __ Set(prototype, Immediate(Handle<Map>(function->initial_map()))); // Load the prototype from the initial map. @@ -318,7 +314,7 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, Register scratch2, Label* miss_label) { __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); - __ mov(eax, Operand(scratch1)); + __ mov(eax, scratch1); __ ret(0); } @@ -327,8 +323,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle<JSObject> holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -348,12 +346,12 @@ static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, Register name, - JSObject* holder_obj) { + Handle<JSObject> holder_obj) { __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!masm->isolate()->heap()->InNewSpace(interceptor)); + Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); + ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); Register scratch = name; - __ mov(scratch, Immediate(Handle<Object>(interceptor))); + __ mov(scratch, Immediate(interceptor)); __ push(scratch); __ push(receiver); __ push(holder); @@ -361,11 +359,12 @@ static void PushInterceptorArguments(MacroAssembler* masm, } -static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { +static void CompileCallLoadPropertyWithInterceptor( + MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + Handle<JSObject> holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); __ CallExternalReference( ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly), @@ -406,15 +405,15 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // frame. // ----------------------------------- __ pop(scratch); - __ add(Operand(esp), Immediate(kPointerSize * kFastApiCallArguments)); + __ add(esp, Immediate(kPointerSize * kFastApiCallArguments)); __ push(scratch); } // Generates call to API function. -static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, - const CallOptimization& optimization, - int argc) { +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + int argc) { // ----------- S t a t e ------------- // -- esp[0] : return address // -- esp[4] : object passing the type check @@ -429,30 +428,25 @@ static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, // -- esp[(argc + 4) * 4] : receiver // ----------------------------------- // Get the function and setup the context. - JSFunction* function = optimization.constant_function(); - __ mov(edi, Immediate(Handle<JSFunction>(function))); + Handle<JSFunction> function = optimization.constant_function(); + __ LoadHeapObject(edi, function); __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); // Pass the additional arguments. __ mov(Operand(esp, 2 * kPointerSize), edi); - Object* call_data = optimization.api_call_info()->data(); - Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); - if (masm->isolate()->heap()->InNewSpace(call_data)) { - __ mov(ecx, api_call_info_handle); + Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); + Handle<Object> call_data(api_call_info->data()); + if (masm->isolate()->heap()->InNewSpace(*call_data)) { + __ mov(ecx, api_call_info); __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset)); __ mov(Operand(esp, 3 * kPointerSize), ebx); } else { - __ mov(Operand(esp, 3 * kPointerSize), - Immediate(Handle<Object>(call_data))); + __ mov(Operand(esp, 3 * kPointerSize), Immediate(call_data)); } // Prepare arguments. __ lea(eax, Operand(esp, 3 * kPointerSize)); - Object* callback = optimization.api_call_info()->callback(); - Address api_function_address = v8::ToCData<Address>(callback); - ApiFunction fun(api_function_address); - const int kApiArgc = 1; // API function gets reference to the v8::Arguments. // Allocate the v8::Arguments structure in the arguments' space since @@ -462,7 +456,7 @@ static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, __ PrepareCallApiFunction(kApiArgc + kApiStackSpace); __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_. - __ add(Operand(eax), Immediate(argc * kPointerSize)); + __ add(eax, Immediate(argc * kPointerSize)); __ mov(ApiParameterOperand(2), eax); // v8::Arguments::values_. __ Set(ApiParameterOperand(3), Immediate(argc)); // v8::Arguments::length_. // v8::Arguments::is_construct_call_. @@ -472,12 +466,10 @@ static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, __ lea(eax, ApiParameterOperand(1)); __ mov(ApiParameterOperand(0), eax); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. - return masm->TryCallApiFunctionAndReturn(&fun, - argc + kFastApiCallArguments + 1); + // Function address is a foreign pointer outside V8's heap. + Address function_address = v8::ToCData<Address>(api_call_info->callback()); + __ CallApiFunctionAndReturn(function_address, + argc + kFastApiCallArguments + 1); } @@ -486,22 +478,22 @@ class CallInterceptorCompiler BASE_EMBEDDED { CallInterceptorCompiler(StubCompiler* stub_compiler, const ParameterCount& arguments, Register name, - Code::ExtraICState extra_ic_state) + Code::ExtraICState extra_state) : stub_compiler_(stub_compiler), arguments_(arguments), name_(name), - extra_ic_state_(extra_ic_state) {} - - MaybeObject* Compile(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - LookupResult* lookup, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Label* miss) { + extra_state_(extra_state) {} + + void Compile(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Label* miss) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -509,45 +501,27 @@ class CallInterceptorCompiler BASE_EMBEDDED { __ JumpIfSmi(receiver, miss); CallOptimization optimization(lookup); - if (optimization.is_constant_call()) { - return CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, + holder, lookup, name, optimization, miss); } else { - CompileRegular(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - name, - holder, - miss); - return masm->isolate()->heap()->undefined_value(); // Success. + CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, + name, holder, miss); } } private: - MaybeObject* CompileCacheable(MacroAssembler* masm, - JSObject* object, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - JSObject* interceptor_holder, - LookupResult* lookup, - String* name, - const CallOptimization& optimization, - Label* miss_label) { + void CompileCacheable(MacroAssembler* masm, + Handle<JSObject> object, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<JSObject> interceptor_holder, + LookupResult* lookup, + Handle<String> name, + const CallOptimization& optimization, + Label* miss_label) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); @@ -556,16 +530,14 @@ class CallInterceptorCompiler BASE_EMBEDDED { bool can_do_fast_api_call = false; if (optimization.is_simple_api_call() && !lookup->holder()->IsGlobalObject()) { - depth1 = - optimization.GetPrototypeDepthOfExpectedType(object, - interceptor_holder); + depth1 = optimization.GetPrototypeDepthOfExpectedType( + object, interceptor_holder); if (depth1 == kInvalidProtoDepth) { - depth2 = - optimization.GetPrototypeDepthOfExpectedType(interceptor_holder, - lookup->holder()); + depth2 = optimization.GetPrototypeDepthOfExpectedType( + interceptor_holder, Handle<JSObject>(lookup->holder())); } - can_do_fast_api_call = (depth1 != kInvalidProtoDepth) || - (depth2 != kInvalidProtoDepth); + can_do_fast_api_call = + depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth; } Counters* counters = masm->isolate()->counters(); @@ -581,9 +553,9 @@ class CallInterceptorCompiler BASE_EMBEDDED { Label miss_cleanup; Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; Register holder = - stub_compiler_->CheckPrototypes(object, receiver, - interceptor_holder, scratch1, - scratch2, scratch3, name, depth1, miss); + stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, depth1, miss); // Invoke an interceptor and if it provides a value, // branch to |regular_invoke|. @@ -596,10 +568,11 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Check that the maps from interceptor's holder to constant function's // holder haven't changed and thus we can use cached constant function. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { stub_compiler_->CheckPrototypes(interceptor_holder, receiver, - lookup->holder(), scratch1, - scratch2, scratch3, name, depth2, miss); + Handle<JSObject>(lookup->holder()), + scratch1, scratch2, scratch3, + name, depth2, miss); } else { // CheckPrototypes has a side effect of fetching a 'holder' // for API (object which is instanceof for the signature). It's @@ -610,11 +583,9 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - MaybeObject* result = - GenerateFastApiCall(masm, optimization, arguments_.immediate()); - if (result->IsFailure()) return result; + GenerateFastApiCall(masm, optimization, arguments_.immediate()); } else { - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(optimization.constant_function(), arguments_, @@ -633,33 +604,27 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } - - return masm->isolate()->heap()->undefined_value(); // Success. } void CompileRegular(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, Register receiver, Register scratch1, Register scratch2, Register scratch3, - String* name, - JSObject* interceptor_holder, + Handle<String> name, + Handle<JSObject> interceptor_holder, Label* miss_label) { Register holder = stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, name, - miss_label); + scratch1, scratch2, scratch3, + name, miss_label); - __ EnterInternalFrame(); + FrameScope scope(masm, StackFrame::INTERNAL); // Save the name_ register across the call. __ push(name_); - PushInterceptorArguments(masm, - receiver, - holder, - name_, - interceptor_holder); + PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder); __ CallExternalReference( ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall), @@ -668,27 +633,30 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Restore the name_ register. __ pop(name_); - __ LeaveInternalFrame(); + + // Leave the internal frame. } void LoadWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, - JSObject* holder_obj, + Handle<JSObject> holder_obj, Label* interceptor_succeeded) { - __ EnterInternalFrame(); - __ push(holder); // Save the holder. - __ push(name_); // Save the name. - - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); - - __ pop(name_); // Restore the name. - __ pop(receiver); // Restore the holder. - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(holder); // Save the holder. + __ push(name_); // Save the name. + + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); + + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. + // Leave the internal frame. + } __ cmp(eax, masm->isolate()->factory()->no_interceptor_result_sentinel()); __ j(not_equal, interceptor_succeeded); @@ -697,49 +665,39 @@ class CallInterceptorCompiler BASE_EMBEDDED { StubCompiler* stub_compiler_; const ParameterCount& arguments_; Register name_; - Code::ExtraICState extra_ic_state_; + Code::ExtraICState extra_state_; }; void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); - Code* code = NULL; - if (kind == Code::LOAD_IC) { - code = masm->isolate()->builtins()->builtin(Builtins::kLoadIC_Miss); - } else { - code = masm->isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Miss); - } - - Handle<Code> ic(code); - __ jmp(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = (kind == Code::LOAD_IC) + ? masm->isolate()->builtins()->LoadIC_Miss() + : masm->isolate()->builtins()->KeyedLoadIC_Miss(); + __ jmp(code, RelocInfo::CODE_TARGET); } void StubCompiler::GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm) { - Code* code = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - Handle<Code> ic(code); - __ jmp(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ jmp(code, RelocInfo::CODE_TARGET); } // Both name_reg and receiver_reg are preserved on jumps to miss_label, // but may be destroyed if store is successful. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, int index, - Map* transition, + Handle<Map> transition, Register receiver_reg, Register name_reg, Register scratch, Label* miss_label) { - // Check that the object isn't a smi. - __ JumpIfSmi(receiver_reg, miss_label); - // Check that the map of the object hasn't changed. - __ cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Immediate(Handle<Map>(object->map()))); - __ j(not_equal, miss_label); + __ CheckMap(receiver_reg, Handle<Map>(object->map()), + miss_label, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -751,12 +709,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); // Perform map transition for the receiver if necessary. - if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { + if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. __ pop(scratch); // Return address. __ push(receiver_reg); - __ push(Immediate(Handle<Map>(transition))); + __ push(Immediate(transition)); __ push(eax); __ push(scratch); __ TailCallExternalReference( @@ -767,11 +725,11 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, return; } - if (transition != NULL) { + if (!transition.is_null()) { // Update the map of the object; no write barrier updating is // needed because the map is never in new space. __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Immediate(Handle<Map>(transition))); + Immediate(transition)); } // Adjust for the number of properties stored in the object. Even in the @@ -786,8 +744,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the value being stored in the now unused name_reg. - __ mov(name_reg, Operand(eax)); - __ RecordWrite(receiver_reg, offset, name_reg, scratch); + __ mov(name_reg, eax); + __ RecordWriteField(receiver_reg, + offset, + name_reg, + scratch, + kDontSaveFPRegs); } else { // Write to the properties array. int offset = index * kPointerSize + FixedArray::kHeaderSize; @@ -797,8 +759,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the value being stored in the now unused name_reg. - __ mov(name_reg, Operand(eax)); - __ RecordWrite(scratch, offset, name_reg, receiver_reg); + __ mov(name_reg, eax); + __ RecordWriteField(scratch, + offset, + name_reg, + receiver_reg, + kDontSaveFPRegs); } // Return the value (register eax). @@ -809,70 +775,58 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( - MacroAssembler* masm, - GlobalObject* global, - String* name, - Register scratch, - Label* miss) { - Object* probe; - { MaybeObject* maybe_probe = global->EnsurePropertyCell(name); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle<GlobalObject> global, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSGlobalPropertyCell> cell = + GlobalObject::EnsurePropertyCell(global, name); ASSERT(cell->value()->IsTheHole()); + Handle<Oddball> the_hole = masm->isolate()->factory()->the_hole_value(); if (Serializer::enabled()) { - __ mov(scratch, Immediate(Handle<Object>(cell))); + __ mov(scratch, Immediate(cell)); __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), - Immediate(masm->isolate()->factory()->the_hole_value())); + Immediate(the_hole)); } else { - __ cmp(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)), - Immediate(masm->isolate()->factory()->the_hole_value())); + __ cmp(Operand::Cell(cell), Immediate(the_hole)); } __ j(not_equal, miss); - return cell; } // Calls GenerateCheckPropertyCell for each global object in the prototype chain // from object to (but not including) holder. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( - MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - Register scratch, - Label* miss) { - JSObject* current = object; - while (current != holder) { +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { if (current->IsGlobalObject()) { - // Returns a cell or a failure. - MaybeObject* result = GenerateCheckPropertyCell( - masm, - GlobalObject::cast(current), - name, - scratch, - miss); - if (result->IsFailure()) return result; + GenerateCheckPropertyCell(masm, + Handle<GlobalObject>::cast(current), + name, + scratch, + miss); } - ASSERT(current->IsJSObject()); - current = JSObject::cast(current->GetPrototype()); + current = Handle<JSObject>(JSObject::cast(current->GetPrototype())); } - return NULL; } - #undef __ #define __ ACCESS_MASM(masm()) -Register StubCompiler::CheckPrototypes(JSObject* object, +Register StubCompiler::CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, int save_at_depth, Label* miss) { // Make sure there's no overlap between holder and object registers. @@ -882,7 +836,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Keep track of the current object in register reg. Register reg = object_reg; - JSObject* current = object; + Handle<JSObject> current = object; int depth = 0; if (save_at_depth == depth) { @@ -891,79 +845,55 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Traverse the prototype chain and check the maps in the prototype chain for // fast and global objects or do negative lookup for normal objects. - while (current != holder) { - depth++; + while (!current.is_identical_to(holder)) { + ++depth; // Only global objects and objects that do not require access // checks are allowed in stubs. ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - ASSERT(current->GetPrototype()->IsJSObject()); - JSObject* prototype = JSObject::cast(current->GetPrototype()); + Handle<JSObject> prototype(JSObject::cast(current->GetPrototype())); if (!current->HasFastProperties() && !current->IsJSGlobalObject() && !current->IsJSGlobalProxy()) { if (!name->IsSymbol()) { - MaybeObject* maybe_lookup_result = heap()->LookupSymbol(name); - Object* lookup_result = NULL; // Initialization to please compiler. - if (!maybe_lookup_result->ToObject(&lookup_result)) { - set_failure(Failure::cast(maybe_lookup_result)); - return reg; - } - name = String::cast(lookup_result); + name = factory()->LookupSymbol(name); } - ASSERT(current->property_dictionary()->FindEntry(name) == + ASSERT(current->property_dictionary()->FindEntry(*name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); - if (negative_lookup->IsFailure()) { - set_failure(Failure::cast(negative_lookup)); - return reg; - } + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // from now the object is in holder_reg + reg = holder_reg; // From now on the object will be in holder_reg. __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); - } else if (heap()->InNewSpace(prototype)) { - // Get the map of the current object. - __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - __ cmp(Operand(scratch1), Immediate(Handle<Map>(current->map()))); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - - // Restore scratch register to be the map of the object. - // We load the prototype from the map in the scratch register. + } else { + bool in_new_space = heap()->InNewSpace(*prototype); + Handle<Map> current_map(current->map()); + if (in_new_space) { + // Save the map in scratch1 for later. __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); } - // The prototype is in new space; we cannot store a reference - // to it in the code. Load it from the map. - reg = holder_reg; // from now the object is in holder_reg - __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); - } else { - // Check the map of the current object. - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - Immediate(Handle<Map>(current->map()))); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. + __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK, + ALLOW_ELEMENT_TRANSITION_MAPS); + + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); + __ CheckAccessGlobalProxy(reg, scratch2, miss); + } + reg = holder_reg; // From now on the object will be in holder_reg. + + if (in_new_space) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ mov(reg, prototype); } - // The prototype is in old space; load it directly. - reg = holder_reg; // from now the object is in holder_reg - __ mov(reg, Handle<JSObject>(prototype)); } if (save_at_depth == depth) { @@ -973,54 +903,46 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Go to the next object in the prototype chain. current = prototype; } - ASSERT(current == holder); + ASSERT(current.is_identical_to(holder)); // Log the check depth. LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); // Check the holder map. - __ cmp(FieldOperand(reg, HeapObject::kMapOffset), - Immediate(Handle<Map>(holder->map()))); - __ j(not_equal, miss); + __ CheckMap(reg, Handle<Map>(holder->map()), + miss, DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform security check for access to the global object. ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); if (holder->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); - }; - - // If we've skipped any global objects, it's not enough to verify - // that their maps haven't changed. We also need to check that the - // property cell for the property is still empty. - MaybeObject* result = GenerateCheckPropertyCells(masm(), - object, - holder, - name, - scratch1, - miss); - if (result->IsFailure()) set_failure(Failure::cast(result)); + } + + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); // Return the register containing the holder. return reg; } -void StubCompiler::GenerateLoadField(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadField(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, int index, - String* name, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check the prototype chain. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + Register reg = CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); // Get the value from the properties. GenerateFastPropertyLoad(masm(), eax, reg, holder, index); @@ -1028,40 +950,37 @@ void StubCompiler::GenerateLoadField(JSObject* object, } -MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss) { +void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, + Handle<JSObject> holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, - scratch2, scratch3, name, miss); - - Handle<AccessorInfo> callback_handle(callback); + Register reg = CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); // Insert additional parameters into the stack frame above return address. ASSERT(!scratch3.is(reg)); __ pop(scratch3); // Get return address to place it below. __ push(receiver); // receiver - __ mov(scratch2, Operand(esp)); + __ mov(scratch2, esp); ASSERT(!scratch2.is(reg)); __ push(reg); // holder // Push data from AccessorInfo. - if (isolate()->heap()->InNewSpace(callback_handle->data())) { - __ mov(scratch1, Immediate(callback_handle)); + if (isolate()->heap()->InNewSpace(callback->data())) { + __ mov(scratch1, Immediate(callback)); __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); } else { - __ push(Immediate(Handle<Object>(callback_handle->data()))); + __ push(Immediate(Handle<Object>(callback->data()))); } // Save a pointer to where we pushed the arguments pointer. @@ -1073,59 +992,56 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, __ push(scratch3); // Restore return address. - // Do call through the api. - Address getter_address = v8::ToCData<Address>(callback->getter()); - ApiFunction fun(getter_address); - - // 3 elements array for v8::Agruments::values_, handler for name and pointer + // 3 elements array for v8::Arguments::values_, handler for name and pointer // to the values (it considered as smi in GC). const int kStackSpace = 5; const int kApiArgc = 2; __ PrepareCallApiFunction(kApiArgc); __ mov(ApiParameterOperand(0), ebx); // name. - __ add(Operand(ebx), Immediate(kPointerSize)); + __ add(ebx, Immediate(kPointerSize)); __ mov(ApiParameterOperand(1), ebx); // arguments pointer. // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - return masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); + Address getter_address = v8::ToCData<Address>(callback->getter()); + __ CallApiFunctionAndReturn(getter_address, kStackSpace); } -void StubCompiler::GenerateLoadConstant(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, - Object* value, - String* name, + Handle<JSFunction> value, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); // Return the constant value. - __ mov(eax, Handle<Object>(value)); + __ LoadHeapObject(eax, value); __ ret(0); } -void StubCompiler::GenerateLoadInterceptor(JSObject* object, - JSObject* interceptor_holder, +void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> interceptor_holder, LookupResult* lookup, Register receiver, Register name_reg, Register scratch1, Register scratch2, Register scratch3, - String* name, + Handle<String> name, Label* miss) { ASSERT(interceptor_holder->HasNamedInterceptor()); ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -1137,13 +1053,13 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // and CALLBACKS, so inline only them, other cases may be added // later. bool compile_followup_inline = false; - if (lookup->IsProperty() && lookup->IsCacheable()) { + if (lookup->IsFound() && lookup->IsCacheable()) { if (lookup->type() == FIELD) { compile_followup_inline = true; } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsAccessorInfo() && - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { - compile_followup_inline = true; + lookup->GetCallbackObject()->IsAccessorInfo()) { + compile_followup_inline = + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL; } } @@ -1158,47 +1074,49 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // Save necessary data before invoking an interceptor. // Requires a frame to make GC aware of pushed pointers. - __ EnterInternalFrame(); + { + FrameScope frame_scope(masm(), StackFrame::INTERNAL); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - // CALLBACKS case needs a receiver to be passed into C++ callback. - __ push(receiver); - } - __ push(holder_reg); - __ push(name_reg); - - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method.) - CompileCallLoadPropertyWithInterceptor(masm(), - receiver, - holder_reg, - name_reg, - interceptor_holder); - - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ cmp(eax, factory()->no_interceptor_result_sentinel()); - __ j(equal, &interceptor_failed); - __ LeaveInternalFrame(); - __ ret(0); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + // CALLBACKS case needs a receiver to be passed into C++ callback. + __ push(receiver); + } + __ push(holder_reg); + __ push(name_reg); - __ bind(&interceptor_failed); - __ pop(name_reg); - __ pop(holder_reg); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - __ pop(receiver); - } + // Invoke an interceptor. Note: map checks from receiver to + // interceptor's holder has been compiled before (see a caller + // of this method.) + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + interceptor_holder); + + // Check if interceptor provided a value for property. If it's + // the case, return immediately. + Label interceptor_failed; + __ cmp(eax, factory()->no_interceptor_result_sentinel()); + __ j(equal, &interceptor_failed); + frame_scope.GenerateLeaveFrame(); + __ ret(0); - __ LeaveInternalFrame(); + __ bind(&interceptor_failed); + __ pop(name_reg); + __ pop(holder_reg); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + __ pop(receiver); + } + + // Leave the internal frame. + } // Check that the maps from interceptor's holder to lookup's holder // haven't changed. And load lookup's holder into holder_reg. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { holder_reg = CheckPrototypes(interceptor_holder, holder_reg, - lookup->holder(), + Handle<JSObject>(lookup->holder()), scratch1, scratch2, scratch3, @@ -1210,15 +1128,15 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), eax, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle<JSObject>(lookup->holder()), + lookup->GetFieldIndex()); __ ret(0); } else { // We found CALLBACKS property in prototype chain of interceptor's // holder. ASSERT(lookup->type() == CALLBACKS); - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); - ASSERT(callback != NULL); + Handle<AccessorInfo> callback( + AccessorInfo::cast(lookup->GetCallbackObject())); ASSERT(callback->getter() != NULL); // Tail call to runtime. @@ -1227,7 +1145,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ pop(scratch2); // return address __ push(receiver); __ push(holder_reg); - __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback))); + __ mov(holder_reg, Immediate(callback)); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); __ push(holder_reg); __ push(name_reg); @@ -1257,17 +1175,17 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ cmp(Operand(ecx), Immediate(Handle<String>(name))); + __ cmp(ecx, Immediate(name)); __ j(not_equal, miss); } } -void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, +void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, Label* miss) { ASSERT(holder->IsGlobalObject()); @@ -1280,7 +1198,7 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(edx, miss); } @@ -1289,19 +1207,20 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, } -void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, - Label* miss) { +void CallStubCompiler::GenerateLoadFunctionFromCell( + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Label* miss) { // Get the value from the cell. if (Serializer::enabled()) { - __ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell))); + __ mov(edi, Immediate(cell)); __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset)); } else { - __ mov(edi, Operand::Cell(Handle<JSGlobalPropertyCell>(cell))); + __ mov(edi, Operand::Cell(cell)); } // Check that the cell contains the same function. - if (isolate()->heap()->InNewSpace(function)) { + if (isolate()->heap()->InNewSpace(*function)) { // We can't embed a pointer to a function in new space so we have // to verify that the shared function info is unchanged. This has // the nice side effect that multiple closures based on the same @@ -1314,31 +1233,26 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, // Check the shared function info. Make sure it hasn't changed. __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset), Immediate(Handle<SharedFunctionInfo>(function->shared()))); - __ j(not_equal, miss); } else { - __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function))); - __ j(not_equal, miss); + __ cmp(edi, Immediate(function)); } + __ j(not_equal, miss); } -MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = +void CallStubCompiler::GenerateMissBranch() { + Handle<Code> code = isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), kind_, - extra_ic_state_); - Object* obj; - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - __ jmp(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET); - return obj; + extra_state_); + __ jmp(code, RelocInfo::CODE_TARGET); } -MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField( - JSObject* object, - JSObject* holder, - int index, - String* name) { +Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, + Handle<JSObject> holder, + int index, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1376,7 +1290,7 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField( } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(edi, arguments(), JUMP_FUNCTION, @@ -1384,19 +1298,19 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField( // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); } -MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPushCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1406,8 +1320,8 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) { - return isolate()->heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) { + return Handle<Code>::null(); } Label miss; @@ -1421,9 +1335,8 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // Check that the receiver isn't a smi. __ JumpIfSmi(edx, &miss); - CheckPrototypes(JSObject::cast(object), edx, - holder, ebx, - eax, edi, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, &miss); if (argc == 0) { // Noop, return the length. @@ -1432,51 +1345,85 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } else { Label call_builtin; - // Get the elements array of the object. - __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset)); + if (argc == 1) { // Otherwise fall through to call builtin. + Label attempt_to_grow_elements, with_write_barrier; - // Check that the elements are in fast mode and writable. - __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), - Immediate(factory()->fixed_array_map())); - __ j(not_equal, &call_builtin); + // Get the elements array of the object. + __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset)); - if (argc == 1) { // Otherwise fall through to call builtin. - Label exit, with_write_barrier, attempt_to_grow_elements; + // Check that the elements are in fast mode and writable. + __ cmp(FieldOperand(edi, HeapObject::kMapOffset), + Immediate(factory()->fixed_array_map())); + __ j(not_equal, &call_builtin); // Get the array's length into eax and calculate new length. __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset)); STATIC_ASSERT(kSmiTagSize == 1); STATIC_ASSERT(kSmiTag == 0); - __ add(Operand(eax), Immediate(Smi::FromInt(argc))); + __ add(eax, Immediate(Smi::FromInt(argc))); - // Get the element's length into ecx. - __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); + // Get the elements' length into ecx. + __ mov(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // Check if we could survive without allocation. - __ cmp(eax, Operand(ecx)); + __ cmp(eax, ecx); __ j(greater, &attempt_to_grow_elements); + // Check if value is a smi. + __ mov(ecx, Operand(esp, argc * kPointerSize)); + __ JumpIfNotSmi(ecx, &with_write_barrier); + // Save new length. __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); - // Push the element. - __ lea(edx, FieldOperand(ebx, - eax, times_half_pointer_size, - FixedArray::kHeaderSize - argc * kPointerSize)); - __ mov(ecx, Operand(esp, argc * kPointerSize)); - __ mov(Operand(edx, 0), ecx); - - // Check if value is a smi. - __ JumpIfNotSmi(ecx, &with_write_barrier); + // Store the value. + __ mov(FieldOperand(edi, + eax, + times_half_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize), + ecx); - __ bind(&exit); __ ret((argc + 1) * kPointerSize); __ bind(&with_write_barrier); - __ InNewSpace(ebx, ecx, equal, &exit); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + + if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) { + Label fast_object, not_fast_object; + __ CheckFastObjectElements(ebx, ¬_fast_object, Label::kNear); + __ jmp(&fast_object); + // In case of fast smi-only, convert to fast object, otherwise bail out. + __ bind(¬_fast_object); + __ CheckFastSmiOnlyElements(ebx, &call_builtin); + // edi: elements array + // edx: receiver + // ebx: map + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + ebx, + edi, + &call_builtin); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + // Restore edi. + __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset)); + __ bind(&fast_object); + } else { + __ CheckFastObjectElements(ebx, &call_builtin); + } + + // Save new length. + __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); + + // Store the value. + __ lea(edx, FieldOperand(edi, + eax, times_half_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize)); + __ mov(Operand(edx, 0), ecx); + + __ RecordWrite(edi, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); - __ RecordWriteHelper(ebx, edx, ecx); __ ret((argc + 1) * kPointerSize); __ bind(&attempt_to_grow_elements); @@ -1484,6 +1431,19 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ jmp(&call_builtin); } + __ mov(ebx, Operand(esp, argc * kPointerSize)); + // Growing elements that are SMI-only requires special handling in case + // the new element is non-Smi. For now, delegate to the builtin. + Label no_fast_elements_check; + __ JumpIfSmi(ebx, &no_fast_elements_check); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ CheckFastObjectElements(ecx, &call_builtin, Label::kFar); + __ bind(&no_fast_elements_check); + + // We could be lucky and the elements array could be at the top of + // new-space. In this case we can just grow it in place by moving the + // allocation pointer up. + ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address(isolate()); ExternalReference new_space_allocation_limit = @@ -1494,36 +1454,46 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ mov(ecx, Operand::StaticVariable(new_space_allocation_top)); // Check if it's the end of elements. - __ lea(edx, FieldOperand(ebx, + __ lea(edx, FieldOperand(edi, eax, times_half_pointer_size, FixedArray::kHeaderSize - argc * kPointerSize)); - __ cmp(edx, Operand(ecx)); + __ cmp(edx, ecx); __ j(not_equal, &call_builtin); - __ add(Operand(ecx), Immediate(kAllocationDelta * kPointerSize)); + __ add(ecx, Immediate(kAllocationDelta * kPointerSize)); __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit)); __ j(above, &call_builtin); // We fit and could grow elements. __ mov(Operand::StaticVariable(new_space_allocation_top), ecx); - __ mov(ecx, Operand(esp, argc * kPointerSize)); // Push the argument... - __ mov(Operand(edx, 0), ecx); + __ mov(Operand(edx, 0), ebx); // ... and fill the rest with holes. for (int i = 1; i < kAllocationDelta; i++) { __ mov(Operand(edx, i * kPointerSize), Immediate(factory()->the_hole_value())); } + // We know the elements array is in new space so we don't need the + // remembered set, but we just pushed a value onto it so we may have to + // tell the incremental marker to rescan the object that we just grew. We + // don't need to worry about the holes because they are in old space and + // already marked black. + __ RecordWrite(edi, edx, ebx, kDontSaveFPRegs, OMIT_REMEMBERED_SET); + // Restore receiver to edx as finish sequence assumes it's here. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // Increment element's and array's sizes. - __ add(FieldOperand(ebx, FixedArray::kLengthOffset), + __ add(FieldOperand(edi, FixedArray::kLengthOffset), Immediate(Smi::FromInt(kAllocationDelta))); + + // NOTE: This only happen in new-space, where we don't + // care about the black-byte-count on pages. Otherwise we should + // update that too if the object is black. + __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); - // Elements are in new space, so write barrier is not required. __ ret((argc + 1) * kPointerSize); } @@ -1535,19 +1505,19 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPopCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1557,8 +1527,8 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) { - return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) { + return Handle<Code>::null(); } Label miss, return_undefined, call_builtin; @@ -1571,9 +1541,8 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Check that the receiver isn't a smi. __ JumpIfSmi(edx, &miss); - CheckPrototypes(JSObject::cast(object), edx, - holder, ebx, - eax, edi, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, &miss); // Get the elements array of the object. __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset)); @@ -1585,7 +1554,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Get the array's length into ecx and calculate new length. __ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset)); - __ sub(Operand(ecx), Immediate(Smi::FromInt(1))); + __ sub(ecx, Immediate(Smi::FromInt(1))); __ j(negative, &return_undefined); // Get the last element. @@ -1594,7 +1563,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, __ mov(eax, FieldOperand(ebx, ecx, times_half_pointer_size, FixedArray::kHeaderSize)); - __ cmp(Operand(eax), Immediate(factory()->the_hole_value())); + __ cmp(eax, Immediate(factory()->the_hole_value())); __ j(equal, &call_builtin); // Set the array's length. @@ -1618,20 +1587,19 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, 1); __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : function name // -- esp[0] : return address @@ -1641,8 +1609,8 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) { - return isolate()->heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) { + return Handle<Code>::null(); } const int argc = arguments().immediate(); @@ -1653,7 +1621,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Label* index_out_of_range_label = &index_out_of_range; if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } @@ -1665,13 +1633,12 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Context::STRING_FUNCTION_INDEX, eax, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, - ebx, edx, edi, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + eax, holder, ebx, edx, edi, name, &miss); Register receiver = ebx; Register index = edi; - Register scratch = edx; Register result = eax; __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize)); if (argc > 0) { @@ -1680,19 +1647,18 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ Set(index, Immediate(factory()->undefined_value())); } - StringCharCodeAtGenerator char_code_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_code_at_generator.GenerateFast(masm()); + StringCharCodeAtGenerator generator(receiver, + index, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); StubRuntimeCallHelper call_helper; - char_code_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1702,22 +1668,21 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ bind(&miss); // Restore function name in ecx. - __ Set(ecx, Immediate(Handle<String>(name))); + __ Set(ecx, Immediate(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : function name // -- esp[0] : return address @@ -1727,8 +1692,8 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) { - return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) { + return Handle<Code>::null(); } const int argc = arguments().immediate(); @@ -1739,7 +1704,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( Label* index_out_of_range_label = &index_out_of_range; if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } @@ -1751,14 +1716,13 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( Context::STRING_FUNCTION_INDEX, eax, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, - ebx, edx, edi, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + eax, holder, ebx, edx, edi, name, &miss); Register receiver = eax; Register index = edi; - Register scratch1 = ebx; - Register scratch2 = edx; + Register scratch = edx; Register result = eax; __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize)); if (argc > 0) { @@ -1767,20 +1731,19 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ Set(index, Immediate(factory()->undefined_value())); } - StringCharAtGenerator char_at_generator(receiver, - index, - scratch1, - scratch2, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_at_generator.GenerateFast(masm()); + StringCharAtGenerator generator(receiver, + index, + scratch, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1790,22 +1753,21 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ bind(&miss); // Restore function name in ecx. - __ Set(ecx, Immediate(Handle<String>(name))); + __ Set(ecx, Immediate(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : function name // -- esp[0] : return address @@ -1819,23 +1781,22 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. if (!object->IsJSObject() || argc != 1) { - return isolate()->heap()->undefined_value(); + return Handle<Code>::null(); } Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ mov(edx, Operand(esp, 2 * kPointerSize)); - STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(edx, &miss); - - CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -1851,17 +1812,17 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // Convert the smi code to uint16. __ and_(code, Immediate(Smi::FromInt(0xffff))); - StringCharFromCodeGenerator char_from_code_generator(code, eax); - char_from_code_generator.GenerateFast(masm()); + StringCharFromCodeGenerator generator(code, eax); + generator.GenerateFast(masm()); __ ret(2 * kPointerSize); StubRuntimeCallHelper call_helper; - char_from_code_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(function, arguments(), JUMP_FUNCTION, @@ -1869,19 +1830,19 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathFloorCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1891,7 +1852,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, // ----------------------------------- if (!CpuFeatures::IsSupported(SSE2)) { - return isolate()->heap()->undefined_value(); + return Handle<Code>::null(); } CpuFeatures::Scope use_sse2(SSE2); @@ -1901,23 +1862,24 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. if (!object->IsJSObject() || argc != 1) { - return isolate()->heap()->undefined_value(); + return Handle<Code>::null(); } Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ mov(edx, Operand(esp, 2 * kPointerSize)); STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(edx, &miss); - CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -1998,19 +1960,19 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathAbsCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -2024,23 +1986,24 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. if (!object->IsJSObject() || argc != 1) { - return isolate()->heap()->undefined_value(); + return Handle<Code>::null(); } Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ mov(edx, Operand(esp, 2 * kPointerSize)); STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(edx, &miss); - CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2058,10 +2021,10 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ sar(ebx, kBitsPerInt - 1); // Do bitwise not or do nothing depending on ebx. - __ xor_(eax, Operand(ebx)); + __ xor_(eax, ebx); // Add 1 or do nothing depending on ebx. - __ sub(eax, Operand(ebx)); + __ sub(eax, ebx); // If the result is still negative, go to the slow case. // This only happens for the most negative smi. @@ -2102,30 +2065,29 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileFastApiCall( +Handle<Code> CallStubCompiler::CompileFastApiCall( const CallOptimization& optimization, - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { ASSERT(optimization.is_simple_api_call()); // Bail out if object is a global object as we don't want to // repatch it to global receiver. - if (object->IsGlobalObject()) return heap()->undefined_value(); - if (cell != NULL) return heap()->undefined_value(); - if (!object->IsJSObject()) return heap()->undefined_value(); + if (object->IsGlobalObject()) return Handle<Code>::null(); + if (!cell.is_null()) return Handle<Code>::null(); + if (!object->IsJSObject()) return Handle<Code>::null(); int depth = optimization.GetPrototypeDepthOfExpectedType( - JSObject::cast(object), holder); - if (depth == kInvalidProtoDepth) return heap()->undefined_value(); + Handle<JSObject>::cast(object), holder); + if (depth == kInvalidProtoDepth) return Handle<Code>::null(); Label miss, miss_before_stack_reserved; @@ -2144,11 +2106,11 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( // Allocate space for v8::Arguments implicit values. Must be initialized // before calling any runtime function. - __ sub(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); + __ sub(esp, Immediate(kFastApiCallArguments * kPointerSize)); // Check that the maps haven't changed and find a Holder as a side effect. - CheckPrototypes(JSObject::cast(object), edx, holder, - ebx, eax, edi, name, depth, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi, + name, depth, &miss); // Move the return address on top of the stack. __ mov(eax, Operand(esp, 3 * kPointerSize)); @@ -2156,27 +2118,24 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( // esp[2 * kPointerSize] is uninitialized, esp[3 * kPointerSize] contains // duplicate of return address and will be overwritten. - MaybeObject* result = GenerateFastApiCall(masm(), optimization, argc); - if (result->IsFailure()) return result; + GenerateFastApiCall(masm(), optimization, argc); __ bind(&miss); - __ add(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); + __ add(esp, Immediate(kFastApiCallArguments * kPointerSize)); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallConstant( - Object* object, - JSObject* holder, - JSFunction* function, - String* name, - CheckType check) { +Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function, + Handle<String> name, + CheckType check) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -2186,16 +2145,14 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, NULL, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, + Handle<JSGlobalPropertyCell>::null(), + function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the receiver from the stack. @@ -2210,15 +2167,13 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Make sure that it's okay not to patch the on stack receiver // unless we're doing a receiver map check. ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); - - SharedFunctionInfo* function_info = function->shared(); switch (check) { case RECEIVER_MAP_CHECK: __ IncrementCounter(isolate()->counters()->call_const(), 1); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), edx, holder, - ebx, eax, edi, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, + edi, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -2229,28 +2184,25 @@ MaybeObject* CallStubCompiler::CompileCallConstant( break; case STRING_CHECK: - if (!function->IsBuiltin() && !function_info->strict_mode()) { - // Calling non-strict non-builtins with a value as the receiver - // requires boxing. - __ jmp(&miss); - } else { + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { // Check that the object is a string or a symbol. __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, eax); __ j(above_equal, &miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::STRING_FUNCTION_INDEX, eax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, - ebx, edx, edi, name, &miss); - } - break; - - case NUMBER_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + eax, holder, ebx, edx, edi, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case NUMBER_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a smi or a heap number. __ JumpIfSmi(edx, &fast); @@ -2260,18 +2212,18 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::NUMBER_FUNCTION_INDEX, eax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, - ebx, edx, edi, name, &miss); - } - break; - } - - case BOOLEAN_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + eax, holder, ebx, edx, edi, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case BOOLEAN_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a boolean. __ cmp(edx, factory()->true_value()); @@ -2282,17 +2234,18 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::BOOLEAN_FUNCTION_INDEX, eax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, - ebx, edx, edi, name, &miss); + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + eax, holder, ebx, edx, edi, name, &miss); + } else { + // Calling non-strict non-builtins with a value as the receiver + // requires boxing. + __ jmp(&miss); } break; - } - - default: - UNREACHABLE(); } - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(function, arguments(), JUMP_FUNCTION, @@ -2300,17 +2253,16 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -2325,24 +2277,15 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Get the number of arguments. const int argc = arguments().immediate(); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // Get the receiver from the stack. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); - CallInterceptorCompiler compiler(this, arguments(), ecx, extra_ic_state_); - MaybeObject* result = compiler.Compile(masm(), - object, - holder, - name, - &lookup, - edx, - ebx, - edi, - eax, - &miss); - if (result->IsFailure()) return result; + CallInterceptorCompiler compiler(this, arguments(), ecx, extra_state_); + compiler.Compile(masm(), object, holder, name, &lookup, edx, ebx, edi, eax, + &miss); // Restore receiver. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); @@ -2361,7 +2304,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Invoke the function. __ mov(edi, eax); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(edi, arguments(), JUMP_FUNCTION, @@ -2369,20 +2312,19 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Handle load cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(INTERCEPTOR, name); } -MaybeObject* CallStubCompiler::CompileCallGlobal( - JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileCallGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -2392,23 +2334,17 @@ MaybeObject* CallStubCompiler::CompileCallGlobal( // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, cell, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, cell, function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - GenerateGlobalReceiverCheck(object, holder, name, &miss); - GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy. @@ -2417,46 +2353,37 @@ MaybeObject* CallStubCompiler::CompileCallGlobal( __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); } - // Setup the context (function already in edi). + // Set up the context (function already in edi). __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); // Jump to the cached code (tail call). Counters* counters = isolate()->counters(); __ IncrementCounter(counters->call_global_inline(), 1); - ASSERT(function->is_compiled()); ParameterCount expected(function->shared()->formal_parameter_count()); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - if (V8::UseCrankshaft()) { - // TODO(kasperl): For now, we always call indirectly through the - // code field in the function to allow recompilation to take effect - // without changing any of the call sites. - __ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), - expected, arguments(), JUMP_FUNCTION, - NullCallWrapper(), call_kind); - } else { - Handle<Code> code(function->code()); - __ InvokeCode(code, expected, arguments(), - RelocInfo::CODE_TARGET, JUMP_FUNCTION, - NullCallWrapper(), call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + __ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), + expected, arguments(), JUMP_FUNCTION, + NullCallWrapper(), call_kind); // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); } -MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -2466,27 +2393,23 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, Label miss; // Generate store field code. Trashes the name register. - GenerateStoreField(masm(), - object, - index, - transition, - edx, ecx, ebx, - &miss); + GenerateStoreField(masm(), object, index, transition, edx, ecx, ebx, &miss); // Handle store cache miss. __ bind(&miss); - __ mov(ecx, Immediate(Handle<String>(name))); // restore name + __ mov(ecx, Immediate(name)); // restore name Handle<Code> ic = isolate()->builtins()->StoreIC_Miss(); __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, - AccessorInfo* callback, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreCallback( + Handle<JSObject> object, + Handle<AccessorInfo> callback, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -2495,13 +2418,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(edx, &miss); - // Check that the map of the object hasn't changed. - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - Immediate(Handle<Map>(object->map()))); - __ j(not_equal, &miss); + __ CheckMap(edx, Handle<Map>(object->map()), + &miss, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -2514,7 +2433,7 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, __ pop(ebx); // remove the return address __ push(edx); // receiver - __ push(Immediate(Handle<AccessorInfo>(callback))); // callback info + __ push(Immediate(callback)); // callback info __ push(ecx); // name __ push(eax); // value __ push(ebx); // restore return address @@ -2534,8 +2453,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, } -MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreInterceptor( + Handle<JSObject> receiver, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -2544,13 +2464,9 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(edx, &miss); - // Check that the map of the object hasn't changed. - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - Immediate(Handle<Map>(receiver->map()))); - __ j(not_equal, &miss); + __ CheckMap(edx, Handle<Map>(receiver->map()), + &miss, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (receiver->IsJSGlobalProxy()) { @@ -2583,9 +2499,10 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, } -MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, - JSGlobalPropertyCell* cell, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreGlobal( + Handle<GlobalObject> object, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -2599,13 +2516,9 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, Immediate(Handle<Map>(object->map()))); __ j(not_equal, &miss); - // Compute the cell operand to use. - Operand cell_operand = Operand::Cell(Handle<JSGlobalPropertyCell>(cell)); - if (Serializer::enabled()) { - __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell))); - cell_operand = FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset); - } + __ mov(ebx, Immediate(cell)); + Operand cell_operand = FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset); // Check that the value in the cell is not the hole. If it is, this // cell could have been deleted and reintroducing the global needs @@ -2616,6 +2529,7 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, // Store the value in the cell. __ mov(cell_operand, eax); + // No write barrier here, because cells are always rescanned. // Return the value (register eax). Counters* counters = isolate()->counters(); @@ -2633,10 +2547,10 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, } -MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key @@ -2649,16 +2563,11 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ IncrementCounter(counters->keyed_store_field(), 1); // Check that the name has not changed. - __ cmp(Operand(ecx), Immediate(Handle<String>(name))); + __ cmp(ecx, Immediate(name)); __ j(not_equal, &miss); // Generate store field code. Trashes the name register. - GenerateStoreField(masm(), - object, - index, - transition, - edx, ecx, ebx, - &miss); + GenerateStoreField(masm(), object, index, transition, edx, ecx, ebx, &miss); // Handle store cache miss. __ bind(&miss); @@ -2667,39 +2576,37 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { +Handle<Code> KeyedStoreStubCompiler::CompileStoreElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key // -- edx : receiver // -- esp[0] : return address // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE; - MaybeObject* maybe_stub = - KeyedStoreElementStub(is_jsarray, elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(edx, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = + KeyedStoreElementStub(is_jsarray, elements_kind).GetCode(); + + __ DispatchMap(edx, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss(); __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_stubs, + MapHandleList* transitioned_maps) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key @@ -2707,28 +2614,33 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( // -- esp[0] : return address // ----------------------------------- Label miss; - __ JumpIfSmi(edx, &miss); - - Register map_reg = ebx; - __ mov(map_reg, FieldOperand(edx, HeapObject::kMapOffset)); - int receiver_count = receiver_maps->length(); - for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - __ cmp(map_reg, map); - __ j(equal, Handle<Code>(handler_ics->at(current))); + __ JumpIfSmi(edx, &miss, Label::kNear); + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); + // ebx: receiver->map(). + for (int i = 0; i < receiver_maps->length(); ++i) { + __ cmp(edi, receiver_maps->at(i)); + if (transitioned_maps->at(i).is_null()) { + __ j(equal, handler_stubs->at(i)); + } else { + Label next_map; + __ j(not_equal, &next_map, Label::kNear); + __ mov(ebx, Immediate(transitioned_maps->at(i))); + __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET); + __ bind(&next_map); + } } __ bind(&miss); Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss(); __ jmp(miss_ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last) { +Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> last) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2749,15 +2661,8 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - edx, - &miss); - if (cell->IsFailure()) { - miss.Unuse(); - return cell; - } + GenerateCheckPropertyCell( + masm(), Handle<GlobalObject>::cast(last), name, edx, &miss); } // Return undefined if maps of the full prototype chain are still the @@ -2769,14 +2674,14 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NONEXISTENT, isolate()->heap()->empty_string()); + return GetCode(NONEXISTENT, factory()->empty_string()); } -MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, - JSObject* holder, +Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2793,10 +2698,11 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> LoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2804,13 +2710,8 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, // ----------------------------------- Label miss; - MaybeObject* result = GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, - edi, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, edi, callback, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2819,10 +2720,10 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, } -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2839,9 +2740,9 @@ MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2849,21 +2750,13 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, // ----------------------------------- Label miss; - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // TODO(368): Compile in the whole chain: all the interceptors in // prototypes and ultimate answer. - GenerateLoadInterceptor(receiver, - holder, - &lookup, - eax, - ecx, - edx, - ebx, - edi, - name, - &miss); + GenerateLoadInterceptor(receiver, holder, &lookup, eax, ecx, edx, ebx, edi, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2873,11 +2766,12 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete) { +Handle<Code> LoadStubCompiler::CompileLoadGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name, + bool is_dont_delete) { // ----------- S t a t e ------------- // -- eax : receiver // -- ecx : name @@ -2888,7 +2782,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual loads. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(eax, &miss); } @@ -2897,10 +2791,10 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // Get the value from the cell. if (Serializer::enabled()) { - __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell))); + __ mov(ebx, Immediate(cell)); __ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset)); } else { - __ mov(ebx, Operand::Cell(Handle<JSGlobalPropertyCell>(cell))); + __ mov(ebx, Operand::Cell(cell)); } // Check for deleted property if property can actually be deleted. @@ -2926,9 +2820,9 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int index) { // ----------- S t a t e ------------- // -- eax : key @@ -2941,7 +2835,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, __ IncrementCounter(counters->keyed_load_field(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); GenerateLoadField(receiver, holder, edx, ebx, ecx, edi, index, name, &miss); @@ -2955,11 +2849,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -2971,18 +2865,13 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( __ IncrementCounter(counters->keyed_load_callback(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); - MaybeObject* result = GenerateLoadCallback(receiver, holder, edx, eax, ebx, - ecx, edi, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } + GenerateLoadCallback(receiver, holder, edx, eax, ebx, ecx, edi, callback, + name, &miss); __ bind(&miss); - __ DecrementCounter(counters->keyed_load_callback(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2991,10 +2880,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( } -MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3006,11 +2896,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, __ IncrementCounter(counters->keyed_load_constant_function(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); - GenerateLoadConstant(receiver, holder, edx, ebx, ecx, edi, - value, name, &miss); + GenerateLoadConstant( + receiver, holder, edx, ebx, ecx, edi, value, name, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_load_constant_function(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -3020,9 +2910,10 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor( + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3034,21 +2925,13 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ IncrementCounter(counters->keyed_load_interceptor(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(receiver, - holder, - &lookup, - edx, - eax, - ecx, - ebx, - edi, - name, - &miss); + GenerateLoadInterceptor(receiver, holder, &lookup, edx, eax, ecx, ebx, edi, + name, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_load_interceptor(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -3058,7 +2941,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3070,7 +2954,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { __ IncrementCounter(counters->keyed_load_array_length(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); GenerateLoadArrayLength(masm(), edx, ecx, &miss); @@ -3083,7 +2967,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3095,7 +2980,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ IncrementCounter(counters->keyed_load_string_length(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss, true); @@ -3108,7 +2993,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype( + Handle<String> name) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3120,7 +3006,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { __ IncrementCounter(counters->keyed_load_function_prototype(), 1); // Check that the name has not changed. - __ cmp(Operand(eax), Immediate(Handle<String>(name))); + __ cmp(eax, Immediate(name)); __ j(not_equal, &miss); GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss); @@ -3133,31 +3019,29 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver // -- esp[0] : return address // ----------------------------------- - Code* stub; + ElementsKind elements_kind = receiver_map->elements_kind(); - MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(edx, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode(); + + __ DispatchMap(edx, receiver_map, stub, DO_SMI_CHECK); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_ics) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3170,22 +3054,22 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( __ mov(map_reg, FieldOperand(edx, HeapObject::kMapOffset)); int receiver_count = receiver_maps->length(); for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - __ cmp(map_reg, map); - __ j(equal, Handle<Code>(handler_ics->at(current))); + __ cmp(map_reg, receiver_maps->at(current)); + __ j(equal, handler_ics->at(current)); } __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } // Specialized stub for constructing objects from functions which only have only // simple assignments of the form this.x = ...; in their body. -MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { +Handle<Code> ConstructStubCompiler::CompileConstructStub( + Handle<JSFunction> function) { // ----------- S t a t e ------------- // -- eax : argc // -- edi : constructor @@ -3224,12 +3108,8 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // ebx: initial map __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); __ shl(ecx, kPointerSizeLog2); - __ AllocateInNewSpace(ecx, - edx, - ecx, - no_reg, - &generic_stub_call, - NO_ALLOCATION_FLAGS); + __ AllocateInNewSpace(ecx, edx, ecx, no_reg, + &generic_stub_call, NO_ALLOCATION_FLAGS); // Allocated the JSObject, now initialize the fields and add the heap tag. // ebx: initial map @@ -3260,7 +3140,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // edi: undefined // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. - SharedFunctionInfo* shared = function->shared(); + Handle<SharedFunctionInfo> shared(function->shared()); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { // Check if the argument assigned to the property is actually passed. @@ -3298,7 +3178,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // Move argc to ebx and retrieve and tag the JSObject to return. __ mov(ebx, eax); __ pop(eax); - __ or_(Operand(eax), Immediate(kHeapObjectTag)); + __ or_(eax, Immediate(kHeapObjectTag)); // Remove caller arguments and receiver from the stack and return. __ pop(ecx); @@ -3312,9 +3192,8 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // Jump to the generic stub in case the specialized code cannot handle the // construction. __ bind(&generic_stub_call); - Handle<Code> generic_construct_stub = - isolate()->builtins()->JSConstructStubGeneric(); - __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET); + Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric(); + __ jmp(code, RelocInfo::CODE_TARGET); // Return the generated code. return GetCode(); @@ -3506,8 +3385,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // If we fail allocation of the HeapNumber, we still have a value on // top of the FPU stack. Remove it. __ bind(&failed_allocation); - __ ffree(); - __ fincstp(); + __ fstp(0); // Fall through to slow case. // Slow case: Jump to runtime. @@ -3679,10 +3557,10 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // If the value is NaN or +/-infinity, the result is 0x80000000, // which is automatically zero when taken mod 2^n, n < 32. __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); - __ sub(Operand(esp), Immediate(2 * kPointerSize)); + __ sub(esp, Immediate(2 * kPointerSize)); __ fisttp_d(Operand(esp, 0)); __ pop(ebx); - __ add(Operand(esp), Immediate(kPointerSize)); + __ add(esp, Immediate(kPointerSize)); } else { ASSERT(CpuFeatures::IsSupported(SSE2)); CpuFeatures::Scope scope(SSE2); @@ -3824,8 +3702,7 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( // A value was pushed on the floating point stack before the allocation, if // the allocation fails it needs to be removed. if (!CpuFeatures::IsSupported(SSE2)) { - __ ffree(); - __ fincstp(); + __ fstp(0); } Handle<Code> slow_ic = masm->isolate()->builtins()->KeyedLoadIC_Slow(); @@ -3838,15 +3715,17 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key // -- edx : receiver // -- esp[0] : return address // ----------------------------------- - Label miss_force_generic; + Label miss_force_generic, transition_elements_kind; // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. @@ -3870,11 +3749,28 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ j(above_equal, &miss_force_generic); } - // Do the store and update the write barrier. Make sure to preserve - // the value in register eax. - __ mov(edx, Operand(eax)); - __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax); - __ RecordWrite(edi, 0, edx, ecx); + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + __ JumpIfNotSmi(eax, &transition_elements_kind); + // ecx is a smi, use times_half_pointer_size instead of + // times_pointer_size + __ mov(FieldOperand(edi, + ecx, + times_half_pointer_size, + FixedArray::kHeaderSize), eax); + } else { + ASSERT(elements_kind == FAST_ELEMENTS); + // Do the store and update the write barrier. + // ecx is a smi, use times_half_pointer_size instead of + // times_pointer_size + __ lea(ecx, FieldOperand(edi, + ecx, + times_half_pointer_size, + FixedArray::kHeaderSize)); + __ mov(Operand(ecx, 0), eax); + // Make sure to preserve the value in register eax. + __ mov(edx, eax); + __ RecordWrite(edi, ecx, edx, kDontSaveFPRegs); + } // Done. __ ret(0); @@ -3884,6 +3780,11 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, Handle<Code> ic_force_generic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ jmp(ic_force_generic, RelocInfo::CODE_TARGET); + + // Handle transition to other elements kinds without using the generic stub. + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ jmp(ic_miss, RelocInfo::CODE_TARGET); } @@ -3896,8 +3797,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // -- edx : receiver // -- esp[0] : return address // ----------------------------------- - Label miss_force_generic, smi_value, is_nan, maybe_nan; - Label have_double_value, not_nan; + Label miss_force_generic, transition_elements_kind; // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. @@ -3918,59 +3818,13 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( } __ j(above_equal, &miss_force_generic); - __ JumpIfSmi(eax, &smi_value, Label::kNear); - - __ CheckMap(eax, - masm->isolate()->factory()->heap_number_map(), - &miss_force_generic, - DONT_DO_SMI_CHECK); - - // Double value, canonicalize NaN. - uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32); - __ cmp(FieldOperand(eax, offset), Immediate(kNaNOrInfinityLowerBoundUpper32)); - __ j(greater_equal, &maybe_nan, Label::kNear); - - __ bind(¬_nan); - ExternalReference canonical_nan_reference = - ExternalReference::address_of_canonical_non_hole_nan(); - if (CpuFeatures::IsSupported(SSE2)) { - CpuFeatures::Scope use_sse2(SSE2); - __ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset)); - __ bind(&have_double_value); - __ movdbl(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize), - xmm0); - __ ret(0); - } else { - __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); - __ bind(&have_double_value); - __ fstp_d(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize)); - __ ret(0); - } - - __ bind(&maybe_nan); - // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise - // it's an Infinity, and the non-NaN code path applies. - __ j(greater, &is_nan, Label::kNear); - __ cmp(FieldOperand(eax, HeapNumber::kValueOffset), Immediate(0)); - __ j(zero, ¬_nan); - __ bind(&is_nan); - if (CpuFeatures::IsSupported(SSE2)) { - CpuFeatures::Scope use_sse2(SSE2); - __ movdbl(xmm0, Operand::StaticVariable(canonical_nan_reference)); - } else { - __ fld_d(Operand::StaticVariable(canonical_nan_reference)); - } - __ jmp(&have_double_value, Label::kNear); - - __ bind(&smi_value); - // Value is a smi. convert to a double and store. - // Preserve original value. - __ mov(edx, eax); - __ SmiUntag(edx); - __ push(edx); - __ fild_s(Operand(esp, 0)); - __ pop(edx); - __ fstp_d(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize)); + __ StoreNumberToDoubleElements(eax, + edi, + ecx, + edx, + xmm0, + &transition_elements_kind, + true); __ ret(0); // Handle store cache miss, replacing the ic with the generic stub. @@ -3978,6 +3832,11 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( Handle<Code> ic_force_generic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ jmp(ic_force_generic, RelocInfo::CODE_TARGET); + + // Handle transition to other elements kinds without using the generic stub. + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ jmp(ic_miss, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/ic-inl.h b/deps/v8/src/ic-inl.h index b4f789cb44..4daf944434 100644 --- a/deps/v8/src/ic-inl.h +++ b/deps/v8/src/ic-inl.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,8 @@ #define V8_IC_INL_H_ #include "ic.h" + +#include "compiler.h" #include "debug.h" #include "macro-assembler.h" @@ -36,7 +38,7 @@ namespace v8 { namespace internal { -Address IC::address() { +Address IC::address() const { // Get the address of the call. Address result = pc() - Assembler::kCallTargetAddressOffset; @@ -87,6 +89,9 @@ void IC::SetTargetAtAddress(Address address, Code* target) { } #endif Assembler::set_target_address_at(address, target->instruction_start()); + target->GetHeap()->incremental_marking()->RecordCodeTargetPatch(address, + target); + PostPatching(); } diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index 0f76a9a06c..9846984dd3 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -40,13 +40,13 @@ namespace v8 { namespace internal { #ifdef DEBUG -static char TransitionMarkFromState(IC::State state) { +char IC::TransitionMarkFromState(IC::State state) { switch (state) { case UNINITIALIZED: return '0'; case PREMONOMORPHIC: return 'P'; case MONOMORPHIC: return '1'; case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; - case MEGAMORPHIC: return 'N'; + case MEGAMORPHIC: return IsGeneric() ? 'G' : 'N'; // We never see the debugger states here, because the state is // computed from the original code - not the patched code. Let @@ -80,19 +80,7 @@ void IC::TraceIC(const char* type, raw_frame = it.frame(); } } - if (raw_frame->is_java_script()) { - JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame); - Code* js_code = frame->unchecked_code(); - // Find the function on the stack and both the active code for the - // function and the original code. - JSFunction* function = JSFunction::cast(frame->function()); - function->PrintName(); - int code_offset = - static_cast<int>(address() - js_code->instruction_start()); - PrintF("+%d", code_offset); - } else { - PrintF("<unknown>"); - } + JavaScriptFrame::PrintTop(stdout, false, true); PrintF(" (%c->%c)", TransitionMarkFromState(old_state), TransitionMarkFromState(new_state)); @@ -100,8 +88,22 @@ void IC::TraceIC(const char* type, PrintF("]\n"); } } -#endif +#define TRACE_GENERIC_IC(type, reason) \ + do { \ + if (FLAG_trace_ic) { \ + PrintF("[%s patching generic stub in ", type); \ + JavaScriptFrame::PrintTop(stdout, false, true); \ + PrintF(" (%s)]\n", reason); \ + } \ + } while (false) + +#else +#define TRACE_GENERIC_IC(type, reason) +#endif // DEBUG + +#define TRACE_IC(type, name, old_state, new_target) \ + ASSERT((TraceIC(type, name, old_state, new_target), true)) IC::IC(FrameDepth depth, Isolate* isolate) : isolate_(isolate) { ASSERT(isolate == Isolate::Current()); @@ -133,7 +135,7 @@ IC::IC(FrameDepth depth, Isolate* isolate) : isolate_(isolate) { #ifdef ENABLE_DEBUGGER_SUPPORT -Address IC::OriginalCodeAddress() { +Address IC::OriginalCodeAddress() const { HandleScope scope; // Compute the JavaScript frame for the frame pointer of this IC // structure. We need this to be able to find the function @@ -167,7 +169,7 @@ static bool HasNormalObjectsInPrototypeChain(Isolate* isolate, LookupResult* lookup, Object* receiver) { Object* end = lookup->IsProperty() - ? lookup->holder() : isolate->heap()->null_value(); + ? lookup->holder() : Object::cast(isolate->heap()->null_value()); for (Object* current = receiver; current != end; current = current->GetPrototype()) { @@ -290,6 +292,31 @@ Failure* IC::ReferenceError(const char* type, Handle<String> name) { } +void IC::PostPatching() { + if (FLAG_watch_ic_patching) { + Isolate::Current()->runtime_profiler()->NotifyICChanged(); + // We do not want to optimize until the ICs have settled down, + // so when they are patched, we postpone optimization for the + // current function and the functions above it on the stack that + // might want to inline this one. + StackFrameIterator it; + if (it.done()) return; + it.Advance(); + static const int kStackFramesToMark = Compiler::kMaxInliningLevels - 1; + for (int i = 0; i < kStackFramesToMark; ++i) { + if (it.done()) return; + StackFrame* raw_frame = it.frame(); + if (raw_frame->is_java_script()) { + JSFunction* function = + JSFunction::cast(JavaScriptFrame::cast(raw_frame)->function()); + function->shared()->set_profiler_ticks(0); + } + it.Advance(); + } + } +} + + void IC::Clear(Address address) { Code* target = GetTargetAtAddress(address); @@ -368,15 +395,13 @@ static bool HasInterceptorGetter(JSObject* object) { } -static void LookupForRead(Object* object, - String* name, +static void LookupForRead(Handle<Object> object, + Handle<String> name, LookupResult* lookup) { - AssertNoAllocation no_gc; // pointers must stay valid - // Skip all the objects with named interceptors, but // without actual getter. while (true) { - object->Lookup(name, lookup); + object->Lookup(*name, lookup); // Besides normal conditions (property not found or it's not // an interceptor), bail out if lookup is not cacheable: we won't // be able to IC it anyway and regular lookup should work fine. @@ -386,18 +411,18 @@ static void LookupForRead(Object* object, return; } - JSObject* holder = lookup->holder(); - if (HasInterceptorGetter(holder)) { + Handle<JSObject> holder(lookup->holder()); + if (HasInterceptorGetter(*holder)) { return; } - holder->LocalLookupRealNamedProperty(name, lookup); + holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsProperty()) { ASSERT(lookup->type() != INTERCEPTOR); return; } - Object* proto = holder->GetPrototype(); + Handle<Object> proto(holder->GetPrototype()); if (proto->IsNull()) { lookup->NotFound(); return; @@ -408,31 +433,32 @@ static void LookupForRead(Object* object, } -Object* CallICBase::TryCallAsFunction(Object* object) { - HandleScope scope(isolate()); - Handle<Object> target(object, isolate()); - Handle<Object> delegate = Execution::GetFunctionDelegate(target); +Handle<Object> CallICBase::TryCallAsFunction(Handle<Object> object) { + Handle<Object> delegate = Execution::GetFunctionDelegate(object); - if (delegate->IsJSFunction()) { + if (delegate->IsJSFunction() && !object->IsJSFunctionProxy()) { // Patch the receiver and use the delegate as the function to - // invoke. This is used for invoking objects as if they were - // functions. - const int argc = this->target()->arguments_count(); + // invoke. This is used for invoking objects as if they were functions. + const int argc = target()->arguments_count(); StackFrameLocator locator; JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); int index = frame->ComputeExpressionsCount() - (argc + 1); - frame->SetExpression(index, *target); + frame->SetExpression(index, *object); } - return *delegate; + return delegate; } void CallICBase::ReceiverToObjectIfRequired(Handle<Object> callee, Handle<Object> object) { + while (callee->IsJSFunctionProxy()) { + callee = Handle<Object>(JSFunctionProxy::cast(*callee)->call_trap()); + } + if (callee->IsJSFunction()) { Handle<JSFunction> function = Handle<JSFunction>::cast(callee); - if (function->shared()->strict_mode() || function->IsBuiltin()) { + if (!function->shared()->is_classic_mode() || function->IsBuiltin()) { // Do not wrap receiver for strict mode functions or for builtins. return; } @@ -464,31 +490,27 @@ MaybeObject* CallICBase::LoadFunction(State state, // the element if so. uint32_t index; if (name->AsArrayIndex(&index)) { - Object* result; - { MaybeObject* maybe_result = object->GetElement(index); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - - if (result->IsJSFunction()) return result; + Handle<Object> result = Object::GetElement(object, index); + RETURN_IF_EMPTY_HANDLE(isolate(), result); + if (result->IsJSFunction()) return *result; // Try to find a suitable function delegate for the object at hand. result = TryCallAsFunction(result); - if (result->IsJSFunction()) return result; + if (result->IsJSFunction()) return *result; // Otherwise, it will fail in the lookup step. } // Lookup the property in the object. - LookupResult lookup; - LookupForRead(*object, *name, &lookup); + LookupResult lookup(isolate()); + LookupForRead(object, name, &lookup); if (!lookup.IsProperty()) { // If the object does not have the requested property, check which // exception we need to throw. - if (IsContextual(object)) { - return ReferenceError("not_defined", name); - } - return TypeError("undefined_method", object, name); + return IsContextual(object) + ? ReferenceError("not_defined", name) + : TypeError("undefined_method", object, name); } // Lookup is valid: Update inline cache and stub cache. @@ -498,53 +520,42 @@ MaybeObject* CallICBase::LoadFunction(State state, // Get the property. PropertyAttributes attr; - Object* result; - { MaybeObject* maybe_result = - object->GetProperty(*object, &lookup, *name, &attr); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Object> result = + Object::GetProperty(object, object, &lookup, name, &attr); + RETURN_IF_EMPTY_HANDLE(isolate(), result); - if (lookup.type() == INTERCEPTOR) { + if (lookup.type() == INTERCEPTOR && attr == ABSENT) { // If the object does not have the requested property, check which // exception we need to throw. - if (attr == ABSENT) { - if (IsContextual(object)) { - return ReferenceError("not_defined", name); - } - return TypeError("undefined_method", object, name); - } + return IsContextual(object) + ? ReferenceError("not_defined", name) + : TypeError("undefined_method", object, name); } ASSERT(!result->IsTheHole()); - HandleScope scope(isolate()); - // Wrap result in a handle because ReceiverToObjectIfRequired may allocate - // new object and cause GC. - Handle<Object> result_handle(result); // Make receiver an object if the callee requires it. Strict mode or builtin // functions do not wrap the receiver, non-strict functions and objects // called as functions do. - ReceiverToObjectIfRequired(result_handle, object); + ReceiverToObjectIfRequired(result, object); - if (result_handle->IsJSFunction()) { + if (result->IsJSFunction()) { + Handle<JSFunction> function = Handle<JSFunction>::cast(result); #ifdef ENABLE_DEBUGGER_SUPPORT // Handle stepping into a function if step into is active. Debug* debug = isolate()->debug(); if (debug->StepInActive()) { // Protect the result in a handle as the debugger can allocate and might // cause GC. - Handle<JSFunction> function(JSFunction::cast(*result_handle), isolate()); debug->HandleStepIn(function, object, fp(), false); - return *function; } #endif - - return *result_handle; + return *function; } // Try to find a suitable function delegate for the object at hand. - result_handle = Handle<Object>(TryCallAsFunction(*result_handle)); - if (result_handle->IsJSFunction()) return *result_handle; + result = TryCallAsFunction(result); + if (result->IsJSFunction()) return *result; return TypeError("property_not_function", object, name); } @@ -594,89 +605,57 @@ bool CallICBase::TryUpdateExtraICState(LookupResult* lookup, } -MaybeObject* CallICBase::ComputeMonomorphicStub( - LookupResult* lookup, - State state, - Code::ExtraICState extra_ic_state, - Handle<Object> object, - Handle<String> name) { +Handle<Code> CallICBase::ComputeMonomorphicStub(LookupResult* lookup, + State state, + Code::ExtraICState extra_state, + Handle<Object> object, + Handle<String> name) { int argc = target()->arguments_count(); - MaybeObject* maybe_code = NULL; + Handle<JSObject> holder(lookup->holder()); switch (lookup->type()) { case FIELD: { int index = lookup->GetFieldIndex(); - maybe_code = isolate()->stub_cache()->ComputeCallField(argc, - kind_, - extra_ic_state, - *name, - *object, - lookup->holder(), - index); - break; + return isolate()->stub_cache()->ComputeCallField( + argc, kind_, extra_state, name, object, holder, index); } case CONSTANT_FUNCTION: { // Get the constant function and compute the code stub for this // call; used for rewriting to monomorphic state and making sure // that the code stub is in the stub cache. - JSFunction* function = lookup->GetConstantFunction(); - maybe_code = - isolate()->stub_cache()->ComputeCallConstant(argc, - kind_, - extra_ic_state, - *name, - *object, - lookup->holder(), - function); - break; + Handle<JSFunction> function(lookup->GetConstantFunction()); + return isolate()->stub_cache()->ComputeCallConstant( + argc, kind_, extra_state, name, object, holder, function); } case NORMAL: { - if (!object->IsJSObject()) return NULL; + // If we return a null handle, the IC will not be patched. + if (!object->IsJSObject()) return Handle<Code>::null(); Handle<JSObject> receiver = Handle<JSObject>::cast(object); - if (lookup->holder()->IsGlobalObject()) { - GlobalObject* global = GlobalObject::cast(lookup->holder()); - JSGlobalPropertyCell* cell = - JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); - if (!cell->value()->IsJSFunction()) return NULL; - JSFunction* function = JSFunction::cast(cell->value()); - maybe_code = isolate()->stub_cache()->ComputeCallGlobal(argc, - kind_, - extra_ic_state, - *name, - *receiver, - global, - cell, - function); + if (holder->IsGlobalObject()) { + Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder); + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(lookup)); + if (!cell->value()->IsJSFunction()) return Handle<Code>::null(); + Handle<JSFunction> function(JSFunction::cast(cell->value())); + return isolate()->stub_cache()->ComputeCallGlobal( + argc, kind_, extra_state, name, receiver, global, cell, function); } else { // There is only one shared stub for calling normalized // properties. It does not traverse the prototype chain, so the // property must be found in the receiver for the stub to be // applicable. - if (lookup->holder() != *receiver) return NULL; - maybe_code = isolate()->stub_cache()->ComputeCallNormal(argc, - kind_, - extra_ic_state, - *name, - *receiver); + if (!holder.is_identical_to(receiver)) return Handle<Code>::null(); + return isolate()->stub_cache()->ComputeCallNormal( + argc, kind_, extra_state); } break; } - case INTERCEPTOR: { - ASSERT(HasInterceptorGetter(lookup->holder())); - maybe_code = isolate()->stub_cache()->ComputeCallInterceptor( - argc, - kind_, - extra_ic_state, - *name, - *object, - lookup->holder()); - break; - } + case INTERCEPTOR: + ASSERT(HasInterceptorGetter(*holder)); + return isolate()->stub_cache()->ComputeCallInterceptor( + argc, kind_, extra_state, name, object, holder); default: - maybe_code = NULL; - break; + return Handle<Code>::null(); } - return maybe_code; } @@ -698,75 +677,57 @@ void CallICBase::UpdateCaches(LookupResult* lookup, // Compute the number of arguments. int argc = target()->arguments_count(); - MaybeObject* maybe_code = NULL; bool had_proto_failure = false; + Handle<Code> code; if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay // setting the monomorphic state. - maybe_code = - isolate()->stub_cache()->ComputeCallPreMonomorphic(argc, - kind_, - extra_ic_state); + code = isolate()->stub_cache()->ComputeCallPreMonomorphic( + argc, kind_, extra_ic_state); } else if (state == MONOMORPHIC) { if (kind_ == Code::CALL_IC && TryUpdateExtraICState(lookup, object, &extra_ic_state)) { - maybe_code = ComputeMonomorphicStub(lookup, - state, - extra_ic_state, - object, - name); + code = ComputeMonomorphicStub(lookup, state, extra_ic_state, + object, name); } else if (kind_ == Code::CALL_IC && TryRemoveInvalidPrototypeDependentStub(target(), *object, *name)) { had_proto_failure = true; - maybe_code = ComputeMonomorphicStub(lookup, - state, - extra_ic_state, - object, - name); + code = ComputeMonomorphicStub(lookup, state, extra_ic_state, + object, name); } else { - maybe_code = - isolate()->stub_cache()->ComputeCallMegamorphic(argc, - kind_, - extra_ic_state); + code = isolate()->stub_cache()->ComputeCallMegamorphic( + argc, kind_, extra_ic_state); } } else { - maybe_code = ComputeMonomorphicStub(lookup, - state, - extra_ic_state, - object, - name); + code = ComputeMonomorphicStub(lookup, state, extra_ic_state, + object, name); } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - Object* code; - if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; + // If there's no appropriate stub we simply avoid updating the caches. + if (code.is_null()) return; // Patch the call site depending on the state of the cache. if (state == UNINITIALIZED || state == PREMONOMORPHIC || state == MONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) { - set_target(Code::cast(code)); + set_target(*code); } else if (state == MEGAMORPHIC) { // Cache code holding map should be consistent with // GenerateMonomorphicCacheProbe. It is not the map which holds the stub. - Map* map = JSObject::cast(object->IsJSObject() ? *object : - object->GetPrototype())->map(); - + Handle<JSObject> cache_object = object->IsJSObject() + ? Handle<JSObject>::cast(object) + : Handle<JSObject>(JSObject::cast(object->GetPrototype())); // Update the stub cache. - isolate()->stub_cache()->Set(*name, map, Code::cast(code)); + isolate()->stub_cache()->Set(*name, cache_object->map(), *code); } - USE(had_proto_failure); -#ifdef DEBUG if (had_proto_failure) state = MONOMORPHIC_PROTOTYPE_FAILURE; - TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC", - name, state, target()); -#endif + TRACE_IC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC", + name, state, target()); } @@ -786,34 +747,22 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, if (FLAG_use_ic && state != MEGAMORPHIC && object->IsHeapObject()) { int argc = target()->arguments_count(); - Heap* heap = Handle<HeapObject>::cast(object)->GetHeap(); - Map* map = heap->non_strict_arguments_elements_map(); + Handle<Map> map = + isolate()->factory()->non_strict_arguments_elements_map(); if (object->IsJSObject() && - Handle<JSObject>::cast(object)->elements()->map() == map) { - MaybeObject* maybe_code = isolate()->stub_cache()->ComputeCallArguments( + Handle<JSObject>::cast(object)->elements()->map() == *map) { + Handle<Code> code = isolate()->stub_cache()->ComputeCallArguments( argc, Code::KEYED_CALL_IC); - Object* code; - if (maybe_code->ToObject(&code)) { - set_target(Code::cast(code)); -#ifdef DEBUG - TraceIC("KeyedCallIC", key, state, target()); -#endif - } - } else if (FLAG_use_ic && state != MEGAMORPHIC && - !object->IsAccessCheckNeeded()) { - MaybeObject* maybe_code = isolate()->stub_cache()->ComputeCallMegamorphic( + set_target(*code); + TRACE_IC("KeyedCallIC", key, state, target()); + } else if (!object->IsAccessCheckNeeded()) { + Handle<Code> code = isolate()->stub_cache()->ComputeCallMegamorphic( argc, Code::KEYED_CALL_IC, Code::kNoExtraICState); - Object* code; - if (maybe_code->ToObject(&code)) { - set_target(Code::cast(code)); -#ifdef DEBUG - TraceIC("KeyedCallIC", key, state, target()); -#endif - } + set_target(*code); + TRACE_IC("KeyedCallIC", key, state, target()); } } - HandleScope scope(isolate()); Handle<Object> result = GetProperty(object, key); RETURN_IF_EMPTY_HANDLE(isolate(), result); @@ -821,9 +770,9 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, // functions do not wrap the receiver, non-strict functions and objects // called as functions do. ReceiverToObjectIfRequired(result, object); - if (result->IsJSFunction()) return *result; - result = Handle<Object>(TryCallAsFunction(*result)); + + result = TryCallAsFunction(result); if (result->IsJSFunction()) return *result; return TypeError("property_not_function", object, key); @@ -846,53 +795,44 @@ MaybeObject* LoadIC::Load(State state, // the underlying string value. See ECMA-262 15.5.5.1. if ((object->IsString() || object->IsStringWrapper()) && name->Equals(isolate()->heap()->length_symbol())) { - AssertNoAllocation no_allocation; - Code* stub = NULL; + Handle<Code> stub; if (state == UNINITIALIZED) { stub = pre_monomorphic_stub(); } else if (state == PREMONOMORPHIC) { - if (object->IsString()) { - stub = isolate()->builtins()->builtin( - Builtins::kLoadIC_StringLength); - } else { - stub = isolate()->builtins()->builtin( - Builtins::kLoadIC_StringWrapperLength); - } + stub = object->IsString() + ? isolate()->builtins()->LoadIC_StringLength() + : isolate()->builtins()->LoadIC_StringWrapperLength(); } else if (state == MONOMORPHIC && object->IsStringWrapper()) { - stub = isolate()->builtins()->builtin( - Builtins::kLoadIC_StringWrapperLength); + stub = isolate()->builtins()->LoadIC_StringWrapperLength(); } else if (state != MEGAMORPHIC) { stub = megamorphic_stub(); } - if (stub != NULL) { - set_target(stub); + if (!stub.is_null()) { + set_target(*stub); #ifdef DEBUG if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n"); #endif } // Get the string if we have a string wrapper object. - if (object->IsJSValue()) { - return Smi::FromInt( - String::cast(Handle<JSValue>::cast(object)->value())->length()); - } - return Smi::FromInt(String::cast(*object)->length()); + Handle<Object> string = object->IsJSValue() + ? Handle<Object>(Handle<JSValue>::cast(object)->value()) + : object; + return Smi::FromInt(String::cast(*string)->length()); } // Use specialized code for getting the length of arrays. if (object->IsJSArray() && name->Equals(isolate()->heap()->length_symbol())) { - AssertNoAllocation no_allocation; - Code* stub = NULL; + Handle<Code> stub; if (state == UNINITIALIZED) { stub = pre_monomorphic_stub(); } else if (state == PREMONOMORPHIC) { - stub = isolate()->builtins()->builtin( - Builtins::kLoadIC_ArrayLength); + stub = isolate()->builtins()->LoadIC_ArrayLength(); } else if (state != MEGAMORPHIC) { stub = megamorphic_stub(); } - if (stub != NULL) { - set_target(stub); + if (!stub.is_null()) { + set_target(*stub); #ifdef DEBUG if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n"); #endif @@ -903,23 +843,20 @@ MaybeObject* LoadIC::Load(State state, // Use specialized code for getting prototype of functions. if (object->IsJSFunction() && name->Equals(isolate()->heap()->prototype_symbol()) && - JSFunction::cast(*object)->should_have_prototype()) { - { AssertNoAllocation no_allocation; - Code* stub = NULL; - if (state == UNINITIALIZED) { - stub = pre_monomorphic_stub(); - } else if (state == PREMONOMORPHIC) { - stub = isolate()->builtins()->builtin( - Builtins::kLoadIC_FunctionPrototype); - } else if (state != MEGAMORPHIC) { - stub = megamorphic_stub(); - } - if (stub != NULL) { - set_target(stub); + Handle<JSFunction>::cast(object)->should_have_prototype()) { + Handle<Code> stub; + if (state == UNINITIALIZED) { + stub = pre_monomorphic_stub(); + } else if (state == PREMONOMORPHIC) { + stub = isolate()->builtins()->LoadIC_FunctionPrototype(); + } else if (state != MEGAMORPHIC) { + stub = megamorphic_stub(); + } + if (!stub.is_null()) { + set_target(*stub); #ifdef DEBUG - if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n"); + if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n"); #endif - } } return Accessors::FunctionGetPrototype(*object, 0); } @@ -931,8 +868,8 @@ MaybeObject* LoadIC::Load(State state, if (name->AsArrayIndex(&index)) return object->GetElement(index); // Named lookup in the object. - LookupResult lookup; - LookupForRead(*object, *name, &lookup); + LookupResult lookup(isolate()); + LookupForRead(object, name, &lookup); // If we did not find a property, check if we need to throw an exception. if (!lookup.IsProperty()) { @@ -948,20 +885,18 @@ MaybeObject* LoadIC::Load(State state, } PropertyAttributes attr; - if (lookup.IsProperty() && + if (lookup.IsFound() && (lookup.type() == INTERCEPTOR || lookup.type() == HANDLER)) { // Get the property. - Object* result; - { MaybeObject* maybe_result = - object->GetProperty(*object, &lookup, *name, &attr); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Object> result = + Object::GetProperty(object, object, &lookup, name, &attr); + RETURN_IF_EMPTY_HANDLE(isolate(), result); // If the property is not present, check if we need to throw an // exception. if (attr == ABSENT && IsContextual(object)) { return ReferenceError("not_defined", name); } - return result; + return *result; } // Get the property. @@ -984,120 +919,105 @@ void LoadIC::UpdateCaches(LookupResult* lookup, if (HasNormalObjectsInPrototypeChain(isolate(), lookup, *object)) return; // Compute the code stub for this load. - MaybeObject* maybe_code = NULL; - Object* code; + Handle<Code> code; if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay // setting the monomorphic state. - maybe_code = pre_monomorphic_stub(); + code = pre_monomorphic_stub(); } else if (!lookup->IsProperty()) { // Nonexistent property. The result is undefined. - maybe_code = isolate()->stub_cache()->ComputeLoadNonexistent(*name, - *receiver); + code = isolate()->stub_cache()->ComputeLoadNonexistent(name, receiver); } else { // Compute monomorphic stub. + Handle<JSObject> holder(lookup->holder()); switch (lookup->type()) { - case FIELD: { - maybe_code = isolate()->stub_cache()->ComputeLoadField( - *name, - *receiver, - lookup->holder(), - lookup->GetFieldIndex()); + case FIELD: + code = isolate()->stub_cache()->ComputeLoadField( + name, receiver, holder, lookup->GetFieldIndex()); break; - } case CONSTANT_FUNCTION: { - Object* constant = lookup->GetConstantFunction(); - maybe_code = isolate()->stub_cache()->ComputeLoadConstant( - *name, *receiver, lookup->holder(), constant); + Handle<JSFunction> constant(lookup->GetConstantFunction()); + code = isolate()->stub_cache()->ComputeLoadConstant( + name, receiver, holder, constant); break; } - case NORMAL: { - if (lookup->holder()->IsGlobalObject()) { - GlobalObject* global = GlobalObject::cast(lookup->holder()); - JSGlobalPropertyCell* cell = - JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); - maybe_code = isolate()->stub_cache()->ComputeLoadGlobal(*name, - *receiver, - global, - cell, - lookup->IsDontDelete()); + case NORMAL: + if (holder->IsGlobalObject()) { + Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder); + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(lookup)); + code = isolate()->stub_cache()->ComputeLoadGlobal( + name, receiver, global, cell, lookup->IsDontDelete()); } else { // There is only one shared stub for loading normalized // properties. It does not traverse the prototype chain, so the // property must be found in the receiver for the stub to be // applicable. - if (lookup->holder() != *receiver) return; - maybe_code = isolate()->stub_cache()->ComputeLoadNormal(); + if (!holder.is_identical_to(receiver)) return; + code = isolate()->stub_cache()->ComputeLoadNormal(); } break; - } case CALLBACKS: { - if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; - AccessorInfo* callback = - AccessorInfo::cast(lookup->GetCallbackObject()); + Handle<Object> callback_object(lookup->GetCallbackObject()); + if (!callback_object->IsAccessorInfo()) return; + Handle<AccessorInfo> callback = + Handle<AccessorInfo>::cast(callback_object); if (v8::ToCData<Address>(callback->getter()) == 0) return; - maybe_code = isolate()->stub_cache()->ComputeLoadCallback( - *name, *receiver, lookup->holder(), callback); + code = isolate()->stub_cache()->ComputeLoadCallback( + name, receiver, holder, callback); break; } - case INTERCEPTOR: { - ASSERT(HasInterceptorGetter(lookup->holder())); - maybe_code = isolate()->stub_cache()->ComputeLoadInterceptor( - *name, *receiver, lookup->holder()); + case INTERCEPTOR: + ASSERT(HasInterceptorGetter(*holder)); + code = isolate()->stub_cache()->ComputeLoadInterceptor( + name, receiver, holder); break; - } default: return; } } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; - // Patch the call site depending on the state of the cache. - if (state == UNINITIALIZED || state == PREMONOMORPHIC || + if (state == UNINITIALIZED || + state == PREMONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) { - set_target(Code::cast(code)); + set_target(*code); } else if (state == MONOMORPHIC) { - set_target(megamorphic_stub()); + set_target(*megamorphic_stub()); } else if (state == MEGAMORPHIC) { // Cache code holding map should be consistent with // GenerateMonomorphicCacheProbe. - Map* map = JSObject::cast(object->IsJSObject() ? *object : - object->GetPrototype())->map(); - - isolate()->stub_cache()->Set(*name, map, Code::cast(code)); + isolate()->stub_cache()->Set(*name, receiver->map(), *code); } -#ifdef DEBUG - TraceIC("LoadIC", name, state, target()); -#endif + TRACE_IC("LoadIC", name, state, target()); } -MaybeObject* KeyedLoadIC::GetElementStubWithoutMapCheck( +Handle<Code> KeyedLoadIC::GetElementStubWithoutMapCheck( bool is_js_array, ElementsKind elements_kind) { - return KeyedLoadElementStub(elements_kind).TryGetCode(); + return KeyedLoadElementStub(elements_kind).GetCode(); } -MaybeObject* KeyedLoadIC::ConstructMegamorphicStub( - MapList* receiver_maps, - CodeList* targets, +Handle<Code> KeyedLoadIC::ComputePolymorphicStub( + MapHandleList* receiver_maps, StrictModeFlag strict_mode) { - Object* object; - KeyedLoadStubCompiler compiler; - MaybeObject* maybe_code = compiler.CompileLoadMegamorphic(receiver_maps, - targets); - if (!maybe_code->ToObject(&object)) return maybe_code; + CodeHandleList handler_ics(receiver_maps->length()); + for (int i = 0; i < receiver_maps->length(); ++i) { + Handle<Map> receiver_map = receiver_maps->at(i); + Handle<Code> cached_stub = ComputeMonomorphicStubWithoutMapCheck( + receiver_map, strict_mode); + handler_ics.Add(cached_stub); + } + KeyedLoadStubCompiler compiler(isolate()); + Handle<Code> code = compiler.CompileLoadPolymorphic( + receiver_maps, &handler_ics); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); - PROFILE(isolate(), CodeCreateEvent( - Logger::KEYED_LOAD_MEGAMORPHIC_IC_TAG, - Code::cast(object), 0)); - return object; + PROFILE(isolate(), + CodeCreateEvent(Logger::KEYED_LOAD_MEGAMORPHIC_IC_TAG, *code, 0)); + return code; } @@ -1107,9 +1027,8 @@ MaybeObject* KeyedLoadIC::Load(State state, bool force_generic_stub) { // Check for values that can be converted into a symbol. // TODO(1295): Remove this code. - HandleScope scope(isolate()); if (key->IsHeapNumber() && - isnan(HeapNumber::cast(*key)->value())) { + isnan(Handle<HeapNumber>::cast(key)->value())) { key = isolate()->factory()->nan_symbol(); } else if (key->IsUndefined()) { key = isolate()->factory()->undefined_symbol(); @@ -1131,16 +1050,11 @@ MaybeObject* KeyedLoadIC::Load(State state, if (object->IsString() && name->Equals(isolate()->heap()->length_symbol())) { Handle<String> string = Handle<String>::cast(object); - Object* code = NULL; - { MaybeObject* maybe_code = - isolate()->stub_cache()->ComputeKeyedLoadStringLength(*name, - *string); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - set_target(Code::cast(code)); -#ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); -#endif // DEBUG + Handle<Code> code = + isolate()->stub_cache()->ComputeKeyedLoadStringLength(name, string); + ASSERT(!code.is_null()); + set_target(*code); + TRACE_IC("KeyedLoadIC", name, state, target()); return Smi::FromInt(string->length()); } @@ -1148,34 +1062,25 @@ MaybeObject* KeyedLoadIC::Load(State state, if (object->IsJSArray() && name->Equals(isolate()->heap()->length_symbol())) { Handle<JSArray> array = Handle<JSArray>::cast(object); - Object* code; - { MaybeObject* maybe_code = - isolate()->stub_cache()->ComputeKeyedLoadArrayLength(*name, - *array); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - set_target(Code::cast(code)); -#ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); -#endif // DEBUG - return JSArray::cast(*object)->length(); + Handle<Code> code = + isolate()->stub_cache()->ComputeKeyedLoadArrayLength(name, array); + ASSERT(!code.is_null()); + set_target(*code); + TRACE_IC("KeyedLoadIC", name, state, target()); + return array->length(); } // Use specialized code for getting prototype of functions. if (object->IsJSFunction() && name->Equals(isolate()->heap()->prototype_symbol()) && - JSFunction::cast(*object)->should_have_prototype()) { + Handle<JSFunction>::cast(object)->should_have_prototype()) { Handle<JSFunction> function = Handle<JSFunction>::cast(object); - Object* code; - { MaybeObject* maybe_code = - isolate()->stub_cache()->ComputeKeyedLoadFunctionPrototype( - *name, *function); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - set_target(Code::cast(code)); -#ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); -#endif // DEBUG + Handle<Code> code = + isolate()->stub_cache()->ComputeKeyedLoadFunctionPrototype( + name, function); + ASSERT(!code.is_null()); + set_target(*code); + TRACE_IC("KeyedLoadIC", name, state, target()); return Accessors::FunctionGetPrototype(*object, 0); } } @@ -1184,15 +1089,14 @@ MaybeObject* KeyedLoadIC::Load(State state, // the element or char if so. uint32_t index = 0; if (name->AsArrayIndex(&index)) { - HandleScope scope(isolate()); // Rewrite to the generic keyed load stub. - if (FLAG_use_ic) set_target(generic_stub()); + if (FLAG_use_ic) set_target(*generic_stub()); return Runtime::GetElementOrCharAt(isolate(), object, index); } // Named lookup. - LookupResult lookup; - LookupForRead(*object, *name, &lookup); + LookupResult lookup(isolate()); + LookupForRead(object, name, &lookup); // If we did not find a property, check if we need to throw an exception. if (!lookup.IsProperty() && IsContextual(object)) { @@ -1204,19 +1108,17 @@ MaybeObject* KeyedLoadIC::Load(State state, } PropertyAttributes attr; - if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { + if (lookup.IsFound() && lookup.type() == INTERCEPTOR) { // Get the property. - Object* result; - { MaybeObject* maybe_result = - object->GetProperty(*object, &lookup, *name, &attr); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Object> result = + Object::GetProperty(object, object, &lookup, name, &attr); + RETURN_IF_EMPTY_HANDLE(isolate(), result); // If the property is not present, check if we need to throw an // exception. if (attr == ABSENT && IsContextual(object)) { return ReferenceError("not_defined", name); } - return result; + return *result; } return object->GetProperty(*object, &lookup, *name, &attr); @@ -1227,44 +1129,40 @@ MaybeObject* KeyedLoadIC::Load(State state, bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded(); if (use_ic) { - Code* stub = generic_stub(); + Handle<Code> stub = generic_stub(); if (!force_generic_stub) { if (object->IsString() && key->IsNumber()) { if (state == UNINITIALIZED) { stub = string_stub(); } } else if (object->IsJSObject()) { - JSObject* receiver = JSObject::cast(*object); - Heap* heap = Handle<JSObject>::cast(object)->GetHeap(); - Map* elements_map = Handle<JSObject>::cast(object)->elements()->map(); - if (elements_map == heap->non_strict_arguments_elements_map()) { + Handle<JSObject> receiver = Handle<JSObject>::cast(object); + if (receiver->elements()->map() == + isolate()->heap()->non_strict_arguments_elements_map()) { stub = non_strict_arguments_stub(); } else if (receiver->HasIndexedInterceptor()) { stub = indexed_interceptor_stub(); - } else if (key->IsSmi() && (target() != non_strict_arguments_stub())) { - MaybeObject* maybe_stub = ComputeStub(receiver, - false, - kNonStrictMode, - stub); - stub = maybe_stub->IsFailure() ? - NULL : Code::cast(maybe_stub->ToObjectUnchecked()); + } else if (key->IsSmi() && (target() != *non_strict_arguments_stub())) { + stub = ComputeStub(receiver, LOAD, kNonStrictMode, stub); } } + } else { + TRACE_GENERIC_IC("KeyedLoadIC", "force generic"); } - if (stub != NULL) set_target(stub); + if (!stub.is_null()) set_target(*stub); } -#ifdef DEBUG - TraceIC("KeyedLoadIC", key, state, target()); -#endif // DEBUG + TRACE_IC("KeyedLoadIC", key, state, target()); // Get the property. return Runtime::GetObjectProperty(isolate(), object, key); } -void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, - Handle<Object> object, Handle<String> name) { +void KeyedLoadIC::UpdateCaches(LookupResult* lookup, + State state, + Handle<Object> object, + Handle<String> name) { // Bail out if we didn't find a result. if (!lookup->IsProperty() || !lookup->IsCacheable()) return; @@ -1274,97 +1172,89 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, if (HasNormalObjectsInPrototypeChain(isolate(), lookup, *object)) return; // Compute the code stub for this load. - MaybeObject* maybe_code = NULL; - Object* code; + Handle<Code> code; if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay // setting the monomorphic state. - maybe_code = pre_monomorphic_stub(); + code = pre_monomorphic_stub(); } else { // Compute a monomorphic stub. + Handle<JSObject> holder(lookup->holder()); switch (lookup->type()) { - case FIELD: { - maybe_code = isolate()->stub_cache()->ComputeKeyedLoadField( - *name, *receiver, lookup->holder(), lookup->GetFieldIndex()); + case FIELD: + code = isolate()->stub_cache()->ComputeKeyedLoadField( + name, receiver, holder, lookup->GetFieldIndex()); break; - } case CONSTANT_FUNCTION: { - Object* constant = lookup->GetConstantFunction(); - maybe_code = isolate()->stub_cache()->ComputeKeyedLoadConstant( - *name, *receiver, lookup->holder(), constant); + Handle<JSFunction> constant(lookup->GetConstantFunction()); + code = isolate()->stub_cache()->ComputeKeyedLoadConstant( + name, receiver, holder, constant); break; } case CALLBACKS: { - if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; - AccessorInfo* callback = - AccessorInfo::cast(lookup->GetCallbackObject()); + Handle<Object> callback_object(lookup->GetCallbackObject()); + if (!callback_object->IsAccessorInfo()) return; + Handle<AccessorInfo> callback = + Handle<AccessorInfo>::cast(callback_object); if (v8::ToCData<Address>(callback->getter()) == 0) return; - maybe_code = isolate()->stub_cache()->ComputeKeyedLoadCallback( - *name, *receiver, lookup->holder(), callback); + code = isolate()->stub_cache()->ComputeKeyedLoadCallback( + name, receiver, holder, callback); break; } - case INTERCEPTOR: { + case INTERCEPTOR: ASSERT(HasInterceptorGetter(lookup->holder())); - maybe_code = isolate()->stub_cache()->ComputeKeyedLoadInterceptor( - *name, *receiver, lookup->holder()); + code = isolate()->stub_cache()->ComputeKeyedLoadInterceptor( + name, receiver, holder); break; - } - default: { + default: // Always rewrite to the generic case so that we do not // repeatedly try to rewrite. - maybe_code = generic_stub(); + code = generic_stub(); break; - } } } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; - // Patch the call site depending on the state of the cache. Make // sure to always rewrite from monomorphic to megamorphic. ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE); if (state == UNINITIALIZED || state == PREMONOMORPHIC) { - set_target(Code::cast(code)); + set_target(*code); } else if (state == MONOMORPHIC) { - set_target(megamorphic_stub()); + set_target(*megamorphic_stub()); } -#ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); -#endif + TRACE_IC("KeyedLoadIC", name, state, target()); } static bool StoreICableLookup(LookupResult* lookup) { // Bail out if we didn't find a result. - if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false; + if (!lookup->IsFound() || lookup->type() == NULL_DESCRIPTOR) return false; + + // Bail out if inline caching is not allowed. + if (!lookup->IsCacheable()) return false; - // If the property is read-only, we leave the IC in its current - // state. + // If the property is read-only, we leave the IC in its current state. if (lookup->IsReadOnly()) return false; return true; } -static bool LookupForWrite(JSReceiver* receiver, - String* name, +static bool LookupForWrite(Handle<JSObject> receiver, + Handle<String> name, LookupResult* lookup) { - receiver->LocalLookup(name, lookup); + receiver->LocalLookup(*name, lookup); if (!StoreICableLookup(lookup)) { return false; } - if (lookup->type() == INTERCEPTOR) { - JSObject* object = JSObject::cast(receiver); - if (object->GetNamedInterceptor()->setter()->IsUndefined()) { - object->LocalLookupRealNamedProperty(name, lookup); - return StoreICableLookup(lookup); - } + if (lookup->type() == INTERCEPTOR && + receiver->GetNamedInterceptor()->setter()->IsUndefined()) { + receiver->LocalLookupRealNamedProperty(*name, lookup); + return StoreICableLookup(lookup); } return true; @@ -1376,58 +1266,62 @@ MaybeObject* StoreIC::Store(State state, Handle<Object> object, Handle<String> name, Handle<Object> value) { - // If the object is undefined or null it's illegal to try to set any - // properties on it; throw a TypeError in that case. - if (object->IsUndefined() || object->IsNull()) { - return TypeError("non_object_property_store", object, name); - } + if (!object->IsJSObject()) { + // Handle proxies. + if (object->IsJSProxy()) { + return JSProxy::cast(*object)-> + SetProperty(*name, *value, NONE, strict_mode); + } + + // If the object is undefined or null it's illegal to try to set any + // properties on it; throw a TypeError in that case. + if (object->IsUndefined() || object->IsNull()) { + return TypeError("non_object_property_store", object, name); + } - if (!object->IsJSReceiver()) { // The length property of string values is read-only. Throw in strict mode. if (strict_mode == kStrictMode && object->IsString() && name->Equals(isolate()->heap()->length_symbol())) { return TypeError("strict_read_only_property", object, name); } - // Ignore stores where the receiver is not a JSObject. + // Ignore other stores where the receiver is not a JSObject. + // TODO(1475): Must check prototype chains of object wrappers. return *value; } - // Handle proxies. - if (object->IsJSProxy()) { - return JSReceiver::cast(*object)-> - SetProperty(*name, *value, NONE, strict_mode); - } - Handle<JSObject> receiver = Handle<JSObject>::cast(object); // Check if the given name is an array index. uint32_t index; if (name->AsArrayIndex(&index)) { - HandleScope scope(isolate()); - Handle<Object> result = SetElement(receiver, index, value, strict_mode); - if (result.is_null()) return Failure::Exception(); + Handle<Object> result = + JSObject::SetElement(receiver, index, value, strict_mode); + RETURN_IF_EMPTY_HANDLE(isolate(), result); return *value; } - // Use specialized code for setting the length of arrays. - if (receiver->IsJSArray() - && name->Equals(isolate()->heap()->length_symbol()) - && JSArray::cast(*receiver)->AllowsSetElementsLength()) { + // Use specialized code for setting the length of arrays with fast + // properties. Slow properties might indicate redefinition of the + // length property. + if (receiver->IsJSArray() && + name->Equals(isolate()->heap()->length_symbol()) && + Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() && + receiver->HasFastProperties()) { #ifdef DEBUG if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n"); #endif - Builtins::Name target = (strict_mode == kStrictMode) - ? Builtins::kStoreIC_ArrayLength_Strict - : Builtins::kStoreIC_ArrayLength; - set_target(isolate()->builtins()->builtin(target)); + Handle<Code> stub = (strict_mode == kStrictMode) + ? isolate()->builtins()->StoreIC_ArrayLength_Strict() + : isolate()->builtins()->StoreIC_ArrayLength(); + set_target(*stub); return receiver->SetProperty(*name, *value, NONE, strict_mode); } // Lookup the property locally in the receiver. if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) { - LookupResult lookup; + LookupResult lookup(isolate()); - if (LookupForWrite(*receiver, *name, &lookup)) { + if (LookupForWrite(receiver, name, &lookup)) { // Generate a stub for this store. UpdateCaches(&lookup, state, strict_mode, receiver, name, value); } else { @@ -1444,16 +1338,15 @@ MaybeObject* StoreIC::Store(State state, } if (receiver->IsJSGlobalProxy()) { + // TODO(ulan): find out why we patch this site even with --no-use-ic // Generate a generic stub that goes to the runtime when we see a global // proxy as receiver. - Code* stub = (strict_mode == kStrictMode) + Handle<Code> stub = (strict_mode == kStrictMode) ? global_proxy_stub_strict() : global_proxy_stub(); - if (target() != stub) { - set_target(stub); -#ifdef DEBUG - TraceIC("StoreIC", name, state, target()); -#endif + if (target() != *stub) { + set_target(*stub); + TRACE_IC("StoreIC", name, state, target()); } } @@ -1468,10 +1361,12 @@ void StoreIC::UpdateCaches(LookupResult* lookup, Handle<JSObject> receiver, Handle<String> name, Handle<Object> value) { - // Skip JSGlobalProxy. ASSERT(!receiver->IsJSGlobalProxy()); - ASSERT(StoreICableLookup(lookup)); + // These are not cacheable, so we never see such LookupResults here. + ASSERT(lookup->type() != HANDLER); + // We get only called for properties or transitions, see StoreICableLookup. + ASSERT(lookup->type() != NULL_DESCRIPTOR); // If the property has a non-field type allowing map transitions // where there is extra room in the object, we leave the IC in its @@ -1481,89 +1376,87 @@ void StoreIC::UpdateCaches(LookupResult* lookup, // Compute the code stub for this store; used for rewriting to // monomorphic state and making sure that the code stub is in the // stub cache. - MaybeObject* maybe_code = NULL; - Object* code = NULL; + Handle<Code> code; switch (type) { - case FIELD: { - maybe_code = isolate()->stub_cache()->ComputeStoreField( - *name, *receiver, lookup->GetFieldIndex(), NULL, strict_mode); + case FIELD: + code = isolate()->stub_cache()->ComputeStoreField(name, + receiver, + lookup->GetFieldIndex(), + Handle<Map>::null(), + strict_mode); break; - } case MAP_TRANSITION: { if (lookup->GetAttributes() != NONE) return; - HandleScope scope(isolate()); - ASSERT(type == MAP_TRANSITION); Handle<Map> transition(lookup->GetTransitionMap()); int index = transition->PropertyIndexFor(*name); - maybe_code = isolate()->stub_cache()->ComputeStoreField( - *name, *receiver, index, *transition, strict_mode); + code = isolate()->stub_cache()->ComputeStoreField( + name, receiver, index, transition, strict_mode); break; } - case NORMAL: { + case NORMAL: if (receiver->IsGlobalObject()) { // The stub generated for the global object picks the value directly // from the property cell. So the property must be directly on the // global object. Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver); - JSGlobalPropertyCell* cell = - JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); - maybe_code = isolate()->stub_cache()->ComputeStoreGlobal( - *name, *global, cell, strict_mode); + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(lookup)); + code = isolate()->stub_cache()->ComputeStoreGlobal( + name, global, cell, strict_mode); } else { if (lookup->holder() != *receiver) return; - maybe_code = isolate()->stub_cache()->ComputeStoreNormal(strict_mode); + code = isolate()->stub_cache()->ComputeStoreNormal(strict_mode); } break; - } case CALLBACKS: { - if (!lookup->GetCallbackObject()->IsAccessorInfo()) return; - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); + Handle<Object> callback_object(lookup->GetCallbackObject()); + if (!callback_object->IsAccessorInfo()) return; + Handle<AccessorInfo> callback = + Handle<AccessorInfo>::cast(callback_object); if (v8::ToCData<Address>(callback->setter()) == 0) return; - maybe_code = isolate()->stub_cache()->ComputeStoreCallback( - *name, *receiver, callback, strict_mode); + code = isolate()->stub_cache()->ComputeStoreCallback( + name, receiver, callback, strict_mode); break; } - case INTERCEPTOR: { + case INTERCEPTOR: ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined()); - maybe_code = isolate()->stub_cache()->ComputeStoreInterceptor( - *name, *receiver, strict_mode); + code = isolate()->stub_cache()->ComputeStoreInterceptor( + name, receiver, strict_mode); break; - } - default: + case CONSTANT_FUNCTION: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: + return; + case HANDLER: + case NULL_DESCRIPTOR: + UNREACHABLE(); return; } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; - // Patch the call site depending on the state of the cache. if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) { - set_target(Code::cast(code)); + set_target(*code); } else if (state == MONOMORPHIC) { // Only move to megamorphic if the target changes. - if (target() != Code::cast(code)) { + if (target() != *code) { set_target((strict_mode == kStrictMode) ? megamorphic_stub_strict() : megamorphic_stub()); } } else if (state == MEGAMORPHIC) { // Update the stub cache. - isolate()->stub_cache()->Set(*name, - receiver->map(), - Code::cast(code)); + isolate()->stub_cache()->Set(*name, receiver->map(), *code); } -#ifdef DEBUG - TraceIC("StoreIC", name, state, target()); -#endif + TRACE_IC("StoreIC", name, state, target()); } -static bool AddOneReceiverMapIfMissing(MapList* receiver_maps, - Map* new_receiver_map) { +static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, + Handle<Map> new_receiver_map) { + ASSERT(!new_receiver_map.is_null()); for (int current = 0; current < receiver_maps->length(); ++current) { - if (receiver_maps->at(current) == new_receiver_map) { + if (!receiver_maps->at(current).is_null() && + receiver_maps->at(current).is_identical_to(new_receiver_map)) { return false; } } @@ -1572,109 +1465,101 @@ static bool AddOneReceiverMapIfMissing(MapList* receiver_maps, } -void KeyedIC::GetReceiverMapsForStub(Code* stub, MapList* result) { +void KeyedIC::GetReceiverMapsForStub(Handle<Code> stub, + MapHandleList* result) { ASSERT(stub->is_inline_cache_stub()); - if (stub == string_stub()) { - return result->Add(isolate()->heap()->string_map()); + if (!string_stub().is_null() && stub.is_identical_to(string_stub())) { + return result->Add(isolate()->factory()->string_map()); } else if (stub->is_keyed_load_stub() || stub->is_keyed_store_stub()) { if (stub->ic_state() == MONOMORPHIC) { - result->Add(Map::cast(stub->FindFirstMap())); + result->Add(Handle<Map>(stub->FindFirstMap())); } else { ASSERT(stub->ic_state() == MEGAMORPHIC); AssertNoAllocation no_allocation; int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - for (RelocIterator it(stub, mask); !it.done(); it.next()) { + for (RelocIterator it(*stub, mask); !it.done(); it.next()) { RelocInfo* info = it.rinfo(); - Object* object = info->target_object(); + Handle<Object> object(info->target_object()); ASSERT(object->IsMap()); - result->Add(Map::cast(object)); + AddOneReceiverMapIfMissing(result, Handle<Map>::cast(object)); } } } } -MaybeObject* KeyedIC::ComputeStub(JSObject* receiver, - bool is_store, +Handle<Code> KeyedIC::ComputeStub(Handle<JSObject> receiver, + StubKind stub_kind, StrictModeFlag strict_mode, - Code* generic_stub) { + Handle<Code> generic_stub) { State ic_state = target()->ic_state(); - if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) { - Code* monomorphic_stub; - MaybeObject* maybe_stub = ComputeMonomorphicStub(receiver, - is_store, - strict_mode, - generic_stub); - if (!maybe_stub->To(&monomorphic_stub)) return maybe_stub; - - return monomorphic_stub; + if ((ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) && + !IsTransitionStubKind(stub_kind)) { + return ComputeMonomorphicStub( + receiver, stub_kind, strict_mode, generic_stub); } - ASSERT(target() != generic_stub); + ASSERT(target() != *generic_stub); // Don't handle megamorphic property accesses for INTERCEPTORS or CALLBACKS // via megamorphic stubs, since they don't have a map in their relocation info // and so the stubs can't be harvested for the object needed for a map check. if (target()->type() != NORMAL) { + TRACE_GENERIC_IC("KeyedIC", "non-NORMAL target type"); return generic_stub; } // Determine the list of receiver maps that this call site has seen, // adding the map that was just encountered. - MapList target_receiver_maps; - GetReceiverMapsForStub(target(), &target_receiver_maps); - if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver->map())) { - // If the miss wasn't due to an unseen map, a MEGAMORPHIC stub + MapHandleList target_receiver_maps; + Handle<Map> receiver_map(receiver->map()); + if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) { + target_receiver_maps.Add(receiver_map); + } else { + GetReceiverMapsForStub(Handle<Code>(target()), &target_receiver_maps); + } + bool map_added = + AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map); + if (IsTransitionStubKind(stub_kind)) { + Handle<Map> new_map = ComputeTransitionedMap(receiver, stub_kind); + map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps, new_map); + } + if (!map_added) { + // If the miss wasn't due to an unseen map, a polymorphic stub // won't help, use the generic stub. + TRACE_GENERIC_IC("KeyedIC", "same map added twice"); return generic_stub; } // If the maximum number of receiver maps has been exceeded, use the generic // version of the IC. if (target_receiver_maps.length() > kMaxKeyedPolymorphism) { + TRACE_GENERIC_IC("KeyedIC", "max polymorph exceeded"); return generic_stub; } - PolymorphicCodeCache* cache = isolate()->heap()->polymorphic_code_cache(); - Code::Flags flags = Code::ComputeFlags(this->kind(), - MEGAMORPHIC, - strict_mode); - Object* maybe_cached_stub = cache->Lookup(&target_receiver_maps, flags); - // If there is a cached stub, use it. - if (!maybe_cached_stub->IsUndefined()) { - ASSERT(maybe_cached_stub->IsCode()); - return Code::cast(maybe_cached_stub); - } - // Collect MONOMORPHIC stubs for all target_receiver_maps. - CodeList handler_ics(target_receiver_maps.length()); - for (int i = 0; i < target_receiver_maps.length(); ++i) { - Map* receiver_map(target_receiver_maps.at(i)); - MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck( - receiver_map, strict_mode); - Code* cached_stub; - if (!maybe_cached_stub->To(&cached_stub)) return maybe_cached_stub; - handler_ics.Add(cached_stub); - } - // Build the MEGAMORPHIC stub. - Code* stub; - MaybeObject* maybe_stub = ConstructMegamorphicStub(&target_receiver_maps, - &handler_ics, - strict_mode); - if (!maybe_stub->To(&stub)) return maybe_stub; - MaybeObject* maybe_update = cache->Update(&target_receiver_maps, flags, stub); - if (maybe_update->IsFailure()) return maybe_update; + Handle<PolymorphicCodeCache> cache = + isolate()->factory()->polymorphic_code_cache(); + Code::Flags flags = Code::ComputeFlags(kind(), MEGAMORPHIC, strict_mode); + Handle<Object> probe = cache->Lookup(&target_receiver_maps, flags); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + Handle<Code> stub = + ComputePolymorphicStub(&target_receiver_maps, strict_mode); + PolymorphicCodeCache::Update(cache, &target_receiver_maps, flags, stub); return stub; } -MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck( - Map* receiver_map, +Handle<Code> KeyedIC::ComputeMonomorphicStubWithoutMapCheck( + Handle<Map> receiver_map, StrictModeFlag strict_mode) { if ((receiver_map->instance_type() & kNotStringTag) == 0) { - ASSERT(string_stub() != NULL); + ASSERT(!string_stub().is_null()); return string_stub(); } else { ASSERT(receiver_map->has_dictionary_elements() || receiver_map->has_fast_elements() || + receiver_map->has_fast_smi_only_elements() || receiver_map->has_fast_double_elements() || receiver_map->has_external_array_elements()); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; @@ -1684,47 +1569,78 @@ MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck( } -MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver, - bool is_store, +Handle<Code> KeyedIC::ComputeMonomorphicStub(Handle<JSObject> receiver, + StubKind stub_kind, StrictModeFlag strict_mode, - Code* generic_stub) { - Code* result = NULL; + Handle<Code> generic_stub) { if (receiver->HasFastElements() || + receiver->HasFastSmiOnlyElements() || receiver->HasExternalArrayElements() || receiver->HasFastDoubleElements() || receiver->HasDictionaryElements()) { - MaybeObject* maybe_stub = - isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement( - receiver, is_store, strict_mode); - if (!maybe_stub->To(&result)) return maybe_stub; + return isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement( + receiver, stub_kind, strict_mode); } else { - result = generic_stub; + return generic_stub; + } +} + + +Handle<Map> KeyedIC::ComputeTransitionedMap(Handle<JSObject> receiver, + StubKind stub_kind) { + switch (stub_kind) { + case KeyedIC::STORE_TRANSITION_SMI_TO_OBJECT: + case KeyedIC::STORE_TRANSITION_DOUBLE_TO_OBJECT: + return JSObject::GetElementsTransitionMap(receiver, FAST_ELEMENTS); + break; + case KeyedIC::STORE_TRANSITION_SMI_TO_DOUBLE: + return JSObject::GetElementsTransitionMap(receiver, FAST_DOUBLE_ELEMENTS); + break; + default: + UNREACHABLE(); + return Handle<Map>::null(); } - return result; } -MaybeObject* KeyedStoreIC::GetElementStubWithoutMapCheck( +Handle<Code> KeyedStoreIC::GetElementStubWithoutMapCheck( bool is_js_array, ElementsKind elements_kind) { - return KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); + return KeyedStoreElementStub(is_js_array, elements_kind).GetCode(); } -MaybeObject* KeyedStoreIC::ConstructMegamorphicStub( - MapList* receiver_maps, - CodeList* targets, - StrictModeFlag strict_mode) { - Object* object; - KeyedStoreStubCompiler compiler(strict_mode); - MaybeObject* maybe_code = compiler.CompileStoreMegamorphic(receiver_maps, - targets); - if (!maybe_code->ToObject(&object)) return maybe_code; +Handle<Code> KeyedStoreIC::ComputePolymorphicStub(MapHandleList* receiver_maps, + StrictModeFlag strict_mode) { + // Collect MONOMORPHIC stubs for all target_receiver_maps. + CodeHandleList handler_ics(receiver_maps->length()); + MapHandleList transitioned_maps(receiver_maps->length()); + for (int i = 0; i < receiver_maps->length(); ++i) { + Handle<Map> receiver_map(receiver_maps->at(i)); + Handle<Code> cached_stub; + Handle<Map> transitioned_map = + receiver_map->FindTransitionedMap(receiver_maps); + if (!transitioned_map.is_null()) { + cached_stub = ElementsTransitionAndStoreStub( + receiver_map->elements_kind(), // original elements_kind + transitioned_map->elements_kind(), + receiver_map->instance_type() == JS_ARRAY_TYPE, // is_js_array + strict_mode).GetCode(); + } else { + cached_stub = ComputeMonomorphicStubWithoutMapCheck(receiver_map, + strict_mode); + } + ASSERT(!cached_stub.is_null()); + handler_ics.Add(cached_stub); + transitioned_maps.Add(transitioned_map); + } + KeyedStoreStubCompiler compiler(isolate(), strict_mode); + Handle<Code> code = compiler.CompileStorePolymorphic( + receiver_maps, &handler_ics, &transitioned_maps); isolate()->counters()->keyed_store_polymorphic_stubs()->Increment(); - PROFILE(isolate(), CodeCreateEvent( - Logger::KEYED_STORE_MEGAMORPHIC_IC_TAG, - Code::cast(object), 0)); - return object; + PROFILE(isolate(), + CodeCreateEvent(Logger::KEYED_STORE_MEGAMORPHIC_IC_TAG, *code, 0)); + return code; } @@ -1737,6 +1653,12 @@ MaybeObject* KeyedStoreIC::Store(State state, if (key->IsSymbol()) { Handle<String> name = Handle<String>::cast(key); + // Handle proxies. + if (object->IsJSProxy()) { + return JSProxy::cast(*object)->SetProperty( + *name, *value, NONE, strict_mode); + } + // If the object is undefined or null it's illegal to try to set any // properties on it; throw a TypeError in that case. if (object->IsUndefined() || object->IsNull()) { @@ -1750,19 +1672,18 @@ MaybeObject* KeyedStoreIC::Store(State state, // Check if the given name is an array index. uint32_t index; if (name->AsArrayIndex(&index)) { - HandleScope scope(isolate()); - Handle<Object> result = SetElement(receiver, index, value, strict_mode); - if (result.is_null()) return Failure::Exception(); + Handle<Object> result = + JSObject::SetElement(receiver, index, value, strict_mode); + RETURN_IF_EMPTY_HANDLE(isolate(), result); return *value; } - // Lookup the property locally in the receiver. - LookupResult lookup; - receiver->LocalLookup(*name, &lookup); - // Update inline cache and stub cache. - if (FLAG_use_ic) { - UpdateCaches(&lookup, state, strict_mode, receiver, name, value); + if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) { + LookupResult lookup(isolate()); + if (LookupForWrite(receiver, name, &lookup)) { + UpdateCaches(&lookup, state, strict_mode, receiver, name, value); + } } // Set the property. @@ -1775,33 +1696,38 @@ MaybeObject* KeyedStoreIC::Store(State state, ASSERT(!(use_ic && object->IsJSGlobalProxy())); if (use_ic) { - Code* stub = (strict_mode == kStrictMode) + Handle<Code> stub = (strict_mode == kStrictMode) ? generic_stub_strict() : generic_stub(); if (object->IsJSObject()) { - JSObject* receiver = JSObject::cast(*object); - Heap* heap = Handle<JSObject>::cast(object)->GetHeap(); - Map* elements_map = Handle<JSObject>::cast(object)->elements()->map(); - if (elements_map == heap->non_strict_arguments_elements_map()) { + Handle<JSObject> receiver = Handle<JSObject>::cast(object); + if (receiver->elements()->map() == + isolate()->heap()->non_strict_arguments_elements_map()) { stub = non_strict_arguments_stub(); } else if (!force_generic) { - if (key->IsSmi() && (target() != non_strict_arguments_stub())) { - HandleScope scope(isolate()); - MaybeObject* maybe_stub = ComputeStub(receiver, - true, - strict_mode, - stub); - stub = maybe_stub->IsFailure() ? - NULL : Code::cast(maybe_stub->ToObjectUnchecked()); + if (key->IsSmi() && (target() != *non_strict_arguments_stub())) { + StubKind stub_kind = STORE_NO_TRANSITION; + if (receiver->GetElementsKind() == FAST_SMI_ONLY_ELEMENTS) { + if (value->IsHeapNumber()) { + stub_kind = STORE_TRANSITION_SMI_TO_DOUBLE; + } else if (value->IsHeapObject()) { + stub_kind = STORE_TRANSITION_SMI_TO_OBJECT; + } + } else if (receiver->GetElementsKind() == FAST_DOUBLE_ELEMENTS) { + if (!value->IsSmi() && !value->IsHeapNumber()) { + stub_kind = STORE_TRANSITION_DOUBLE_TO_OBJECT; + } + } + stub = ComputeStub(receiver, stub_kind, strict_mode, stub); } + } else { + TRACE_GENERIC_IC("KeyedStoreIC", "force generic"); } } - if (stub != NULL) set_target(stub); + if (!stub.is_null()) set_target(*stub); } -#ifdef DEBUG - TraceIC("KeyedStoreIC", key, state, target()); -#endif + TRACE_IC("KeyedStoreIC", key, state, target()); // Set the property. return Runtime::SetObjectProperty( @@ -1815,15 +1741,12 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, Handle<JSObject> receiver, Handle<String> name, Handle<Object> value) { - // Skip JSGlobalProxy. - if (receiver->IsJSGlobalProxy()) return; - - // Bail out if we didn't find a result. - if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return; - - // If the property is read-only, we leave the IC in its current - // state. - if (lookup->IsReadOnly()) return; + ASSERT(!receiver->IsJSGlobalProxy()); + ASSERT(StoreICableLookup(lookup)); + // These are not cacheable, so we never see such LookupResults here. + ASSERT(lookup->type() != HANDLER); + // We get only called for properties or transitions, see StoreICableLookup. + ASSERT(lookup->type() != NULL_DESCRIPTOR); // If the property has a non-field type allowing map transitions // where there is extra room in the object, we leave the IC in its @@ -1833,75 +1756,68 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, // Compute the code stub for this store; used for rewriting to // monomorphic state and making sure that the code stub is in the // stub cache. - MaybeObject* maybe_code = NULL; - Object* code = NULL; + Handle<Code> code; switch (type) { - case FIELD: { - maybe_code = isolate()->stub_cache()->ComputeKeyedStoreField( - *name, *receiver, lookup->GetFieldIndex(), NULL, strict_mode); + case FIELD: + code = isolate()->stub_cache()->ComputeKeyedStoreField( + name, receiver, lookup->GetFieldIndex(), + Handle<Map>::null(), strict_mode); break; - } - case MAP_TRANSITION: { + case MAP_TRANSITION: if (lookup->GetAttributes() == NONE) { - HandleScope scope(isolate()); - ASSERT(type == MAP_TRANSITION); Handle<Map> transition(lookup->GetTransitionMap()); int index = transition->PropertyIndexFor(*name); - maybe_code = isolate()->stub_cache()->ComputeKeyedStoreField( - *name, *receiver, index, *transition, strict_mode); + code = isolate()->stub_cache()->ComputeKeyedStoreField( + name, receiver, index, transition, strict_mode); break; } // fall through. - } - default: { + case NORMAL: + case CONSTANT_FUNCTION: + case CALLBACKS: + case INTERCEPTOR: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: // Always rewrite to the generic case so that we do not // repeatedly try to rewrite. - maybe_code = (strict_mode == kStrictMode) + code = (strict_mode == kStrictMode) ? generic_stub_strict() : generic_stub(); break; - } + case HANDLER: + case NULL_DESCRIPTOR: + UNREACHABLE(); + return; } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; + ASSERT(!code.is_null()); // Patch the call site depending on the state of the cache. Make // sure to always rewrite from monomorphic to megamorphic. ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE); if (state == UNINITIALIZED || state == PREMONOMORPHIC) { - set_target(Code::cast(code)); + set_target(*code); } else if (state == MONOMORPHIC) { set_target((strict_mode == kStrictMode) - ? megamorphic_stub_strict() - : megamorphic_stub()); + ? *megamorphic_stub_strict() + : *megamorphic_stub()); } -#ifdef DEBUG - TraceIC("KeyedStoreIC", name, state, target()); -#endif + TRACE_IC("KeyedStoreIC", name, state, target()); } +#undef TRACE_IC + + // ---------------------------------------------------------------------------- // Static IC stub generators. // -static JSFunction* CompileFunction(Isolate* isolate, - JSFunction* function) { - // Compile now with optimization. - HandleScope scope(isolate); - Handle<JSFunction> function_handle(function, isolate); - CompileLazy(function_handle, CLEAR_EXCEPTION); - return *function_handle; -} - - // Used from ic-<arch>.cc. RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 2); CallIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -1910,45 +1826,46 @@ RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) { extra_ic_state, args.at<Object>(0), args.at<String>(1)); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; + // Result could be a function or a failure. + JSFunction* raw_function = NULL; + if (!maybe_result->To(&raw_function)) return maybe_result; // The first time the inline cache is updated may be the first time the - // function it references gets called. If the function was lazily compiled + // function it references gets called. If the function is lazily compiled // then the first call will trigger a compilation. We check for this case // and we do the compilation immediately, instead of waiting for the stub - // currently attached to the JSFunction object to trigger compilation. We - // do this in the case where we know that the inline cache is inside a loop, - // because then we know that we want to optimize the function. - if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { - return result; - } - return CompileFunction(isolate, JSFunction::cast(result)); + // currently attached to the JSFunction object to trigger compilation. + if (raw_function->is_compiled()) return raw_function; + + Handle<JSFunction> function(raw_function); + JSFunction::CompileLazy(function, CLEAR_EXCEPTION); + return *function; } // Used from ic-<arch>.cc. RUNTIME_FUNCTION(MaybeObject*, KeyedCallIC_Miss) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 2); KeyedCallIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); - Object* result; - { MaybeObject* maybe_result = + MaybeObject* maybe_result = ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + // Result could be a function or a failure. + JSFunction* raw_function = NULL; + if (!maybe_result->To(&raw_function)) return maybe_result; - if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { - return result; - } - return CompileFunction(isolate, JSFunction::cast(result)); + if (raw_function->is_compiled()) return raw_function; + + Handle<JSFunction> function(raw_function); + JSFunction::CompileLazy(function, CLEAR_EXCEPTION); + return *function; } // Used from ic-<arch>.cc. RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 2); LoadIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -1958,7 +1875,7 @@ RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) { // Used from ic-<arch>.cc RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 2); KeyedLoadIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -1967,7 +1884,7 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) { RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissForceGeneric) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 2); KeyedLoadIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -1977,7 +1894,7 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissForceGeneric) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(MaybeObject*, StoreIC_Miss) { - NoHandleAllocation na; + HandleScope scope; ASSERT(args.length() == 3); StoreIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -1994,12 +1911,19 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) { NoHandleAllocation nha; ASSERT(args.length() == 2); - JSObject* receiver = JSObject::cast(args[0]); + JSArray* receiver = JSArray::cast(args[0]); Object* len = args[1]; // The generated code should filter out non-Smis before we get here. ASSERT(len->IsSmi()); +#ifdef DEBUG + // The length property has to be a writable callback property. + LookupResult debug_lookup(isolate); + receiver->LocalLookup(isolate->heap()->length_symbol(), &debug_lookup); + ASSERT(debug_lookup.type() == CALLBACKS && !debug_lookup.IsReadOnly()); +#endif + Object* result; { MaybeObject* maybe_result = receiver->SetElementsLength(len); if (!maybe_result->ToObject(&result)) return maybe_result; @@ -2046,7 +1970,7 @@ RUNTIME_FUNCTION(MaybeObject*, SharedStoreIC_ExtendStorage) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 3); KeyedStoreIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -2080,7 +2004,7 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Slow) { RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissForceGeneric) { - NoHandleAllocation na; + HandleScope scope(isolate); ASSERT(args.length() == 3); KeyedStoreIC ic(isolate); IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); @@ -2402,7 +2326,7 @@ RUNTIME_FUNCTION(MaybeObject*, BinaryOp_Patch) { Handle<JSFunction> builtin_function(JSFunction::cast(builtin), isolate); bool caught_exception; - Object** builtin_args[] = { right.location() }; + Handle<Object> builtin_args[] = { right }; Handle<Object> result = Execution::Call(builtin_function, left, ARRAY_SIZE(builtin_args), @@ -2435,6 +2359,7 @@ const char* CompareIC::GetStateName(State state) { case SMIS: return "SMIS"; case HEAP_NUMBERS: return "HEAP_NUMBERS"; case OBJECTS: return "OBJECTS"; + case KNOWN_OBJECTS: return "OBJECTS"; case SYMBOLS: return "SYMBOLS"; case STRINGS: return "STRINGS"; case GENERIC: return "GENERIC"; @@ -2449,19 +2374,38 @@ CompareIC::State CompareIC::TargetState(State state, bool has_inlined_smi_code, Handle<Object> x, Handle<Object> y) { - if (!has_inlined_smi_code && state != UNINITIALIZED && state != SYMBOLS) { - return GENERIC; + switch (state) { + case UNINITIALIZED: + if (x->IsSmi() && y->IsSmi()) return SMIS; + if (x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS; + if (!Token::IsEqualityOp(op_)) return GENERIC; + if (x->IsSymbol() && y->IsSymbol()) return SYMBOLS; + if (x->IsString() && y->IsString()) return STRINGS; + if (x->IsJSObject() && y->IsJSObject()) { + if (Handle<JSObject>::cast(x)->map() == + Handle<JSObject>::cast(y)->map() && + Token::IsEqualityOp(op_)) { + return KNOWN_OBJECTS; + } else { + return OBJECTS; + } + } + return GENERIC; + case SMIS: + return has_inlined_smi_code && x->IsNumber() && y->IsNumber() + ? HEAP_NUMBERS + : GENERIC; + case SYMBOLS: + ASSERT(Token::IsEqualityOp(op_)); + return x->IsString() && y->IsString() ? STRINGS : GENERIC; + case HEAP_NUMBERS: + case STRINGS: + case OBJECTS: + case KNOWN_OBJECTS: + case GENERIC: + return GENERIC; } - if (state == UNINITIALIZED && x->IsSmi() && y->IsSmi()) return SMIS; - if ((state == UNINITIALIZED || (state == SMIS && has_inlined_smi_code)) && - x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS; - if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return GENERIC; - if (state == UNINITIALIZED && - x->IsSymbol() && y->IsSymbol()) return SYMBOLS; - if ((state == UNINITIALIZED || state == SYMBOLS) && - x->IsString() && y->IsString()) return STRINGS; - if (state == UNINITIALIZED && - x->IsJSObject() && y->IsJSObject()) return OBJECTS; + UNREACHABLE(); return GENERIC; } diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index ece5be9f05..d2c98c0869 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -91,10 +91,13 @@ class IC { // Construct the IC structure with the given number of extra // JavaScript frames on the stack. IC(FrameDepth depth, Isolate* isolate); + virtual ~IC() {} // Get the call-site target; used for determining the state. - Code* target() { return GetTargetAtAddress(address()); } - inline Address address(); + Code* target() const { return GetTargetAtAddress(address()); } + inline Address address() const; + + virtual bool IsGeneric() const { return false; } // Compute the current IC state based on the target stub, receiver and name. static State StateFrom(Code* target, Object* receiver, Object* name); @@ -139,13 +142,15 @@ class IC { #ifdef ENABLE_DEBUGGER_SUPPORT // Computes the address in the original code when the code running is // containing break points (calls to DebugBreakXXX builtins). - Address OriginalCodeAddress(); + Address OriginalCodeAddress() const; #endif // Set the call-site target. void set_target(Code* code) { SetTargetAtAddress(address(), code); } #ifdef DEBUG + char TransitionMarkFromState(IC::State state); + void TraceIC(const char* type, Handle<Object> name, State old_state, @@ -160,6 +165,7 @@ class IC { // Access the target code for the given IC address. static inline Code* GetTargetAtAddress(Address address); static inline void SetTargetAtAddress(Address address, Code* target); + static void PostPatching(); private: // Frame pointer for the frame that uses (calls) the IC. @@ -198,47 +204,60 @@ class CallICBase: public IC { class Contextual: public BitField<bool, 0, 1> {}; class StringStubState: public BitField<StringStubFeedback, 1, 1> {}; - protected: - CallICBase(Code::Kind kind, Isolate* isolate) - : IC(EXTRA_CALL_FRAME, isolate), kind_(kind) {} - - public: + // Returns a JSFunction or a Failure. MUST_USE_RESULT MaybeObject* LoadFunction(State state, Code::ExtraICState extra_ic_state, Handle<Object> object, Handle<String> name); protected: - Code::Kind kind_; + CallICBase(Code::Kind kind, Isolate* isolate) + : IC(EXTRA_CALL_FRAME, isolate), kind_(kind) {} bool TryUpdateExtraICState(LookupResult* lookup, Handle<Object> object, Code::ExtraICState* extra_ic_state); - MUST_USE_RESULT MaybeObject* ComputeMonomorphicStub( - LookupResult* lookup, - State state, - Code::ExtraICState extra_ic_state, - Handle<Object> object, - Handle<String> name); + // Compute a monomorphic stub if possible, otherwise return a null handle. + Handle<Code> ComputeMonomorphicStub(LookupResult* lookup, + State state, + Code::ExtraICState extra_state, + Handle<Object> object, + Handle<String> name); - // Update the inline cache and the global stub cache based on the - // lookup result. + // Update the inline cache and the global stub cache based on the lookup + // result. void UpdateCaches(LookupResult* lookup, State state, Code::ExtraICState extra_ic_state, Handle<Object> object, Handle<String> name); - // Returns a JSFunction if the object can be called as a function, - // and patches the stack to be ready for the call. - // Otherwise, it returns the undefined value. - Object* TryCallAsFunction(Object* object); + // Returns a JSFunction if the object can be called as a function, and + // patches the stack to be ready for the call. Otherwise, it returns the + // undefined value. + Handle<Object> TryCallAsFunction(Handle<Object> object); void ReceiverToObjectIfRequired(Handle<Object> callee, Handle<Object> object); static void Clear(Address address, Code* target); + // Platform-specific code generation functions used by both call and + // keyed call. + static void GenerateMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id, + Code::ExtraICState extra_state); + + static void GenerateNormal(MacroAssembler* masm, int argc); + + static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, + int argc, + Code::Kind kind, + Code::ExtraICState extra_state); + + Code::Kind kind_; + friend class IC; }; @@ -252,16 +271,24 @@ class CallIC: public CallICBase { // Code generator routines. static void GenerateInitialize(MacroAssembler* masm, int argc, - Code::ExtraICState extra_ic_state) { - GenerateMiss(masm, argc, extra_ic_state); + Code::ExtraICState extra_state) { + GenerateMiss(masm, argc, extra_state); } + static void GenerateMiss(MacroAssembler* masm, int argc, - Code::ExtraICState extra_ic_state); + Code::ExtraICState extra_state) { + CallICBase::GenerateMiss(masm, argc, IC::kCallIC_Miss, extra_state); + } + static void GenerateMegamorphic(MacroAssembler* masm, int argc, Code::ExtraICState extra_ic_state); - static void GenerateNormal(MacroAssembler* masm, int argc); + + static void GenerateNormal(MacroAssembler* masm, int argc) { + CallICBase::GenerateNormal(masm, argc); + GenerateMiss(masm, argc, Code::kNoExtraICState); + } }; @@ -280,7 +307,12 @@ class KeyedCallIC: public CallICBase { static void GenerateInitialize(MacroAssembler* masm, int argc) { GenerateMiss(masm, argc); } - static void GenerateMiss(MacroAssembler* masm, int argc); + + static void GenerateMiss(MacroAssembler* masm, int argc) { + CallICBase::GenerateMiss(masm, argc, IC::kKeyedCallIC_Miss, + Code::kNoExtraICState); + } + static void GenerateMegamorphic(MacroAssembler* masm, int argc); static void GenerateNormal(MacroAssembler* masm, int argc); static void GenerateNonStrictArguments(MacroAssembler* masm, int argc); @@ -321,17 +353,15 @@ class LoadIC: public IC { Handle<String> name); // Stub accessors. - Code* megamorphic_stub() { - return isolate()->builtins()->builtin( - Builtins::kLoadIC_Megamorphic); + Handle<Code> megamorphic_stub() { + return isolate()->builtins()->LoadIC_Megamorphic(); } static Code* initialize_stub() { return Isolate::Current()->builtins()->builtin( Builtins::kLoadIC_Initialize); } - Code* pre_monomorphic_stub() { - return isolate()->builtins()->builtin( - Builtins::kLoadIC_PreMonomorphic); + Handle<Code> pre_monomorphic_stub() { + return isolate()->builtins()->LoadIC_PreMonomorphic(); } static void Clear(Address address, Code* target); @@ -342,41 +372,53 @@ class LoadIC: public IC { class KeyedIC: public IC { public: + enum StubKind { + LOAD, + STORE_NO_TRANSITION, + STORE_TRANSITION_SMI_TO_OBJECT, + STORE_TRANSITION_SMI_TO_DOUBLE, + STORE_TRANSITION_DOUBLE_TO_OBJECT + }; explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {} virtual ~KeyedIC() {} - virtual MaybeObject* GetElementStubWithoutMapCheck( + virtual Handle<Code> GetElementStubWithoutMapCheck( bool is_js_array, ElementsKind elements_kind) = 0; protected: - virtual Code* string_stub() { - return NULL; + virtual Handle<Code> string_stub() { + return Handle<Code>::null(); } virtual Code::Kind kind() const = 0; - MaybeObject* ComputeStub(JSObject* receiver, - bool is_store, + Handle<Code> ComputeStub(Handle<JSObject> receiver, + StubKind stub_kind, StrictModeFlag strict_mode, - Code* default_stub); - - virtual MaybeObject* ConstructMegamorphicStub( - MapList* receiver_maps, - CodeList* targets, - StrictModeFlag strict_mode) = 0; + Handle<Code> default_stub); - private: - void GetReceiverMapsForStub(Code* stub, MapList* result); + virtual Handle<Code> ComputePolymorphicStub(MapHandleList* receiver_maps, + StrictModeFlag strict_mode) = 0; - MaybeObject* ComputeMonomorphicStubWithoutMapCheck( - Map* receiver_map, + Handle<Code> ComputeMonomorphicStubWithoutMapCheck( + Handle<Map> receiver_map, StrictModeFlag strict_mode); - MaybeObject* ComputeMonomorphicStub(JSObject* receiver, - bool is_store, + private: + void GetReceiverMapsForStub(Handle<Code> stub, MapHandleList* result); + + Handle<Code> ComputeMonomorphicStub(Handle<JSObject> receiver, + StubKind stub_kind, StrictModeFlag strict_mode, - Code* default_stub); + Handle<Code> default_stub); + + Handle<Map> ComputeTransitionedMap(Handle<JSObject> receiver, + StubKind stub_kind); + + static bool IsTransitionStubKind(StubKind stub_kind) { + return stub_kind > STORE_NO_TRANSITION; + } }; @@ -412,21 +454,22 @@ class KeyedLoadIC: public KeyedIC { static const int kSlowCaseBitFieldMask = (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor); - virtual MaybeObject* GetElementStubWithoutMapCheck( + virtual Handle<Code> GetElementStubWithoutMapCheck( bool is_js_array, ElementsKind elements_kind); + virtual bool IsGeneric() const { + return target() == *generic_stub(); + } + protected: virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; } - virtual MaybeObject* ConstructMegamorphicStub( - MapList* receiver_maps, - CodeList* targets, - StrictModeFlag strict_mode); + virtual Handle<Code> ComputePolymorphicStub(MapHandleList* receiver_maps, + StrictModeFlag strict_mode); - virtual Code* string_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_String); + virtual Handle<Code> string_stub() { + return isolate()->builtins()->KeyedLoadIC_String(); } private: @@ -441,25 +484,20 @@ class KeyedLoadIC: public KeyedIC { return Isolate::Current()->builtins()->builtin( Builtins::kKeyedLoadIC_Initialize); } - Code* megamorphic_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_Generic); + Handle<Code> megamorphic_stub() { + return isolate()->builtins()->KeyedLoadIC_Generic(); } - Code* generic_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_Generic); + Handle<Code> generic_stub() const { + return isolate()->builtins()->KeyedLoadIC_Generic(); } - Code* pre_monomorphic_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_PreMonomorphic); + Handle<Code> pre_monomorphic_stub() { + return isolate()->builtins()->KeyedLoadIC_PreMonomorphic(); } - Code* indexed_interceptor_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_IndexedInterceptor); + Handle<Code> indexed_interceptor_stub() { + return isolate()->builtins()->KeyedLoadIC_IndexedInterceptor(); } - Code* non_strict_arguments_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_NonStrictArguments); + Handle<Code> non_strict_arguments_stub() { + return isolate()->builtins()->KeyedLoadIC_NonStrictArguments(); } static void Clear(Address address, Code* target); @@ -524,13 +562,11 @@ class StoreIC: public IC { return Isolate::Current()->builtins()->builtin( Builtins::kStoreIC_Initialize_Strict); } - Code* global_proxy_stub() { - return isolate()->builtins()->builtin( - Builtins::kStoreIC_GlobalProxy); + Handle<Code> global_proxy_stub() { + return isolate()->builtins()->StoreIC_GlobalProxy(); } - Code* global_proxy_stub_strict() { - return isolate()->builtins()->builtin( - Builtins::kStoreIC_GlobalProxy_Strict); + Handle<Code> global_proxy_stub_strict() { + return isolate()->builtins()->StoreIC_GlobalProxy_Strict(); } static void Clear(Address address, Code* target); @@ -562,18 +598,23 @@ class KeyedStoreIC: public KeyedIC { StrictModeFlag strict_mode); static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode); static void GenerateNonStrictArguments(MacroAssembler* masm); + static void GenerateTransitionElementsSmiToDouble(MacroAssembler* masm); + static void GenerateTransitionElementsDoubleToObject(MacroAssembler* masm); - virtual MaybeObject* GetElementStubWithoutMapCheck( + virtual Handle<Code> GetElementStubWithoutMapCheck( bool is_js_array, ElementsKind elements_kind); + virtual bool IsGeneric() const { + return target() == *generic_stub() || + target() == *generic_stub_strict(); + } + protected: virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; } - virtual MaybeObject* ConstructMegamorphicStub( - MapList* receiver_maps, - CodeList* targets, - StrictModeFlag strict_mode); + virtual Handle<Code> ComputePolymorphicStub(MapHandleList* receiver_maps, + StrictModeFlag strict_mode); private: // Update the inline cache. @@ -596,29 +637,24 @@ class KeyedStoreIC: public KeyedIC { return Isolate::Current()->builtins()->builtin( Builtins::kKeyedStoreIC_Initialize); } - Code* megamorphic_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedStoreIC_Generic); - } static Code* initialize_stub_strict() { return Isolate::Current()->builtins()->builtin( Builtins::kKeyedStoreIC_Initialize_Strict); } - Code* megamorphic_stub_strict() { - return isolate()->builtins()->builtin( - Builtins::kKeyedStoreIC_Generic_Strict); + Handle<Code> megamorphic_stub() { + return isolate()->builtins()->KeyedStoreIC_Generic(); } - Code* generic_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedStoreIC_Generic); + Handle<Code> megamorphic_stub_strict() { + return isolate()->builtins()->KeyedStoreIC_Generic_Strict(); } - Code* generic_stub_strict() { - return isolate()->builtins()->builtin( - Builtins::kKeyedStoreIC_Generic_Strict); + Handle<Code> generic_stub() const { + return isolate()->builtins()->KeyedStoreIC_Generic(); } - Code* non_strict_arguments_stub() { - return isolate()->builtins()->builtin( - Builtins::kKeyedStoreIC_NonStrictArguments); + Handle<Code> generic_stub_strict() const { + return isolate()->builtins()->KeyedStoreIC_Generic_Strict(); + } + Handle<Code> non_strict_arguments_stub() { + return isolate()->builtins()->KeyedStoreIC_NonStrictArguments(); } static void Clear(Address address, Code* target); @@ -689,6 +725,7 @@ class CompareIC: public IC { SYMBOLS, STRINGS, OBJECTS, + KNOWN_OBJECTS, GENERIC }; diff --git a/deps/v8/src/incremental-marking-inl.h b/deps/v8/src/incremental-marking-inl.h new file mode 100644 index 0000000000..3e3d6c43fd --- /dev/null +++ b/deps/v8/src/incremental-marking-inl.h @@ -0,0 +1,133 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_INCREMENTAL_MARKING_INL_H_ +#define V8_INCREMENTAL_MARKING_INL_H_ + +#include "incremental-marking.h" + +namespace v8 { +namespace internal { + + +bool IncrementalMarking::BaseRecordWrite(HeapObject* obj, + Object** slot, + Object* value) { + MarkBit value_bit = Marking::MarkBitFrom(HeapObject::cast(value)); + if (Marking::IsWhite(value_bit)) { + MarkBit obj_bit = Marking::MarkBitFrom(obj); + if (Marking::IsBlack(obj_bit)) { + BlackToGreyAndUnshift(obj, obj_bit); + RestartIfNotMarking(); + } + + // Object is either grey or white. It will be scanned if survives. + return false; + } + return true; +} + + +void IncrementalMarking::RecordWrite(HeapObject* obj, + Object** slot, + Object* value) { + if (IsMarking() && value->NonFailureIsHeapObject()) { + RecordWriteSlow(obj, slot, value); + } +} + + +void IncrementalMarking::RecordWriteOfCodeEntry(JSFunction* host, + Object** slot, + Code* value) { + if (IsMarking()) RecordWriteOfCodeEntrySlow(host, slot, value); +} + + +void IncrementalMarking::RecordWriteIntoCode(HeapObject* obj, + RelocInfo* rinfo, + Object* value) { + if (IsMarking() && value->NonFailureIsHeapObject()) { + RecordWriteIntoCodeSlow(obj, rinfo, value); + } +} + + +void IncrementalMarking::RecordWrites(HeapObject* obj) { + if (IsMarking()) { + MarkBit obj_bit = Marking::MarkBitFrom(obj); + if (Marking::IsBlack(obj_bit)) { + BlackToGreyAndUnshift(obj, obj_bit); + RestartIfNotMarking(); + } + } +} + + +void IncrementalMarking::BlackToGreyAndUnshift(HeapObject* obj, + MarkBit mark_bit) { + ASSERT(Marking::MarkBitFrom(obj) == mark_bit); + ASSERT(obj->Size() >= 2*kPointerSize); + ASSERT(IsMarking()); + Marking::BlackToGrey(mark_bit); + int obj_size = obj->Size(); + MemoryChunk::IncrementLiveBytesFromGC(obj->address(), -obj_size); + bytes_scanned_ -= obj_size; + int64_t old_bytes_rescanned = bytes_rescanned_; + bytes_rescanned_ = old_bytes_rescanned + obj_size; + if ((bytes_rescanned_ >> 20) != (old_bytes_rescanned >> 20)) { + if (bytes_rescanned_ > 2 * heap_->PromotedSpaceSize()) { + // If we have queued twice the heap size for rescanning then we are + // going around in circles, scanning the same objects again and again + // as the program mutates the heap faster than we can incrementally + // trace it. In this case we switch to non-incremental marking in + // order to finish off this marking phase. + if (FLAG_trace_gc) { + PrintF("Hurrying incremental marking because of lack of progress\n"); + } + allocation_marking_factor_ = kMaxAllocationMarkingFactor; + } + } + + marking_deque_.UnshiftGrey(obj); +} + + +void IncrementalMarking::WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit) { + WhiteToGrey(obj, mark_bit); + marking_deque_.PushGrey(obj); +} + + +void IncrementalMarking::WhiteToGrey(HeapObject* obj, MarkBit mark_bit) { + Marking::WhiteToGrey(mark_bit); +} + + +} } // namespace v8::internal + +#endif // V8_INCREMENTAL_MARKING_INL_H_ diff --git a/deps/v8/src/incremental-marking.cc b/deps/v8/src/incremental-marking.cc new file mode 100644 index 0000000000..d0346171d3 --- /dev/null +++ b/deps/v8/src/incremental-marking.cc @@ -0,0 +1,926 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "incremental-marking.h" + +#include "code-stubs.h" +#include "compilation-cache.h" +#include "v8conversions.h" + +namespace v8 { +namespace internal { + + +IncrementalMarking::IncrementalMarking(Heap* heap) + : heap_(heap), + state_(STOPPED), + marking_deque_memory_(NULL), + marking_deque_memory_committed_(false), + steps_count_(0), + steps_took_(0), + longest_step_(0.0), + old_generation_space_available_at_start_of_incremental_(0), + old_generation_space_used_at_start_of_incremental_(0), + steps_count_since_last_gc_(0), + steps_took_since_last_gc_(0), + should_hurry_(false), + allocation_marking_factor_(0), + allocated_(0), + no_marking_scope_depth_(0) { +} + + +void IncrementalMarking::TearDown() { + delete marking_deque_memory_; +} + + +void IncrementalMarking::RecordWriteSlow(HeapObject* obj, + Object** slot, + Object* value) { + if (BaseRecordWrite(obj, slot, value) && is_compacting_ && slot != NULL) { + MarkBit obj_bit = Marking::MarkBitFrom(obj); + if (Marking::IsBlack(obj_bit)) { + // Object is not going to be rescanned we need to record the slot. + heap_->mark_compact_collector()->RecordSlot( + HeapObject::RawField(obj, 0), slot, value); + } + } +} + + +void IncrementalMarking::RecordWriteFromCode(HeapObject* obj, + Object* value, + Isolate* isolate) { + ASSERT(obj->IsHeapObject()); + + // Fast cases should already be covered by RecordWriteStub. + ASSERT(value->IsHeapObject()); + ASSERT(!value->IsHeapNumber()); + ASSERT(!value->IsString() || + value->IsConsString() || + value->IsSlicedString()); + ASSERT(Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(value)))); + + IncrementalMarking* marking = isolate->heap()->incremental_marking(); + ASSERT(!marking->is_compacting_); + marking->RecordWrite(obj, NULL, value); +} + + +void IncrementalMarking::RecordWriteForEvacuationFromCode(HeapObject* obj, + Object** slot, + Isolate* isolate) { + IncrementalMarking* marking = isolate->heap()->incremental_marking(); + ASSERT(marking->is_compacting_); + marking->RecordWrite(obj, slot, *slot); +} + + +void IncrementalMarking::RecordCodeTargetPatch(Code* host, + Address pc, + HeapObject* value) { + if (IsMarking()) { + RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); + RecordWriteIntoCode(host, &rinfo, value); + } +} + + +void IncrementalMarking::RecordCodeTargetPatch(Address pc, HeapObject* value) { + if (IsMarking()) { + Code* host = heap_->isolate()->inner_pointer_to_code_cache()-> + GcSafeFindCodeForInnerPointer(pc); + RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); + RecordWriteIntoCode(host, &rinfo, value); + } +} + + +void IncrementalMarking::RecordWriteOfCodeEntrySlow(JSFunction* host, + Object** slot, + Code* value) { + if (BaseRecordWrite(host, slot, value) && is_compacting_) { + ASSERT(slot != NULL); + heap_->mark_compact_collector()-> + RecordCodeEntrySlot(reinterpret_cast<Address>(slot), value); + } +} + + +void IncrementalMarking::RecordWriteIntoCodeSlow(HeapObject* obj, + RelocInfo* rinfo, + Object* value) { + MarkBit value_bit = Marking::MarkBitFrom(HeapObject::cast(value)); + if (Marking::IsWhite(value_bit)) { + MarkBit obj_bit = Marking::MarkBitFrom(obj); + if (Marking::IsBlack(obj_bit)) { + BlackToGreyAndUnshift(obj, obj_bit); + RestartIfNotMarking(); + } + // Object is either grey or white. It will be scanned if survives. + return; + } + + if (is_compacting_) { + MarkBit obj_bit = Marking::MarkBitFrom(obj); + if (Marking::IsBlack(obj_bit)) { + // Object is not going to be rescanned. We need to record the slot. + heap_->mark_compact_collector()->RecordRelocSlot(rinfo, + Code::cast(value)); + } + } +} + + +class IncrementalMarkingMarkingVisitor : public ObjectVisitor { + public: + IncrementalMarkingMarkingVisitor(Heap* heap, + IncrementalMarking* incremental_marking) + : heap_(heap), + incremental_marking_(incremental_marking) { + } + + void VisitEmbeddedPointer(RelocInfo* rinfo) { + ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); + Object* target = rinfo->target_object(); + if (target->NonFailureIsHeapObject()) { + heap_->mark_compact_collector()->RecordRelocSlot(rinfo, target); + MarkObject(target); + } + } + + void VisitCodeTarget(RelocInfo* rinfo) { + ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); + Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); + heap_->mark_compact_collector()->RecordRelocSlot(rinfo, Code::cast(target)); + MarkObject(target); + } + + void VisitDebugTarget(RelocInfo* rinfo) { + ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) && + rinfo->IsPatchedReturnSequence()) || + (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && + rinfo->IsPatchedDebugBreakSlotSequence())); + Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); + heap_->mark_compact_collector()->RecordRelocSlot(rinfo, Code::cast(target)); + MarkObject(target); + } + + void VisitCodeEntry(Address entry_address) { + Object* target = Code::GetObjectFromEntryAddress(entry_address); + heap_->mark_compact_collector()-> + RecordCodeEntrySlot(entry_address, Code::cast(target)); + MarkObject(target); + } + + void VisitPointer(Object** p) { + Object* obj = *p; + if (obj->NonFailureIsHeapObject()) { + heap_->mark_compact_collector()->RecordSlot(p, p, obj); + MarkObject(obj); + } + } + + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) { + Object* obj = *p; + if (obj->NonFailureIsHeapObject()) { + heap_->mark_compact_collector()->RecordSlot(start, p, obj); + MarkObject(obj); + } + } + } + + private: + // Mark object pointed to by p. + INLINE(void MarkObject(Object* obj)) { + HeapObject* heap_object = HeapObject::cast(obj); + MarkBit mark_bit = Marking::MarkBitFrom(heap_object); + if (mark_bit.data_only()) { + if (incremental_marking_->MarkBlackOrKeepGrey(mark_bit)) { + MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), + heap_object->Size()); + } + } else if (Marking::IsWhite(mark_bit)) { + incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit); + } + } + + Heap* heap_; + IncrementalMarking* incremental_marking_; +}; + + +class IncrementalMarkingRootMarkingVisitor : public ObjectVisitor { + public: + IncrementalMarkingRootMarkingVisitor(Heap* heap, + IncrementalMarking* incremental_marking) + : heap_(heap), + incremental_marking_(incremental_marking) { + } + + void VisitPointer(Object** p) { + MarkObjectByPointer(p); + } + + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) MarkObjectByPointer(p); + } + + private: + void MarkObjectByPointer(Object** p) { + Object* obj = *p; + if (!obj->IsHeapObject()) return; + + HeapObject* heap_object = HeapObject::cast(obj); + MarkBit mark_bit = Marking::MarkBitFrom(heap_object); + if (mark_bit.data_only()) { + if (incremental_marking_->MarkBlackOrKeepGrey(mark_bit)) { + MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), + heap_object->Size()); + } + } else { + if (Marking::IsWhite(mark_bit)) { + incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit); + } + } + } + + Heap* heap_; + IncrementalMarking* incremental_marking_; +}; + + +void IncrementalMarking::SetOldSpacePageFlags(MemoryChunk* chunk, + bool is_marking, + bool is_compacting) { + if (is_marking) { + chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); + chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); + + // It's difficult to filter out slots recorded for large objects. + if (chunk->owner()->identity() == LO_SPACE && + chunk->size() > static_cast<size_t>(Page::kPageSize) && + is_compacting) { + chunk->SetFlag(MemoryChunk::RESCAN_ON_EVACUATION); + } + } else if (chunk->owner()->identity() == CELL_SPACE || + chunk->scan_on_scavenge()) { + chunk->ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); + chunk->ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); + } else { + chunk->ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); + chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); + } +} + + +void IncrementalMarking::SetNewSpacePageFlags(NewSpacePage* chunk, + bool is_marking) { + chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); + if (is_marking) { + chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); + } else { + chunk->ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); + } + chunk->SetFlag(MemoryChunk::SCAN_ON_SCAVENGE); +} + + +void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace( + PagedSpace* space) { + PageIterator it(space); + while (it.has_next()) { + Page* p = it.next(); + SetOldSpacePageFlags(p, false, false); + } +} + + +void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace( + NewSpace* space) { + NewSpacePageIterator it(space); + while (it.has_next()) { + NewSpacePage* p = it.next(); + SetNewSpacePageFlags(p, false); + } +} + + +void IncrementalMarking::DeactivateIncrementalWriteBarrier() { + DeactivateIncrementalWriteBarrierForSpace(heap_->old_pointer_space()); + DeactivateIncrementalWriteBarrierForSpace(heap_->old_data_space()); + DeactivateIncrementalWriteBarrierForSpace(heap_->cell_space()); + DeactivateIncrementalWriteBarrierForSpace(heap_->map_space()); + DeactivateIncrementalWriteBarrierForSpace(heap_->code_space()); + DeactivateIncrementalWriteBarrierForSpace(heap_->new_space()); + + LargePage* lop = heap_->lo_space()->first_page(); + while (lop->is_valid()) { + SetOldSpacePageFlags(lop, false, false); + lop = lop->next_page(); + } +} + + +void IncrementalMarking::ActivateIncrementalWriteBarrier(PagedSpace* space) { + PageIterator it(space); + while (it.has_next()) { + Page* p = it.next(); + SetOldSpacePageFlags(p, true, is_compacting_); + } +} + + +void IncrementalMarking::ActivateIncrementalWriteBarrier(NewSpace* space) { + NewSpacePageIterator it(space->ToSpaceStart(), space->ToSpaceEnd()); + while (it.has_next()) { + NewSpacePage* p = it.next(); + SetNewSpacePageFlags(p, true); + } +} + + +void IncrementalMarking::ActivateIncrementalWriteBarrier() { + ActivateIncrementalWriteBarrier(heap_->old_pointer_space()); + ActivateIncrementalWriteBarrier(heap_->old_data_space()); + ActivateIncrementalWriteBarrier(heap_->cell_space()); + ActivateIncrementalWriteBarrier(heap_->map_space()); + ActivateIncrementalWriteBarrier(heap_->code_space()); + ActivateIncrementalWriteBarrier(heap_->new_space()); + + LargePage* lop = heap_->lo_space()->first_page(); + while (lop->is_valid()) { + SetOldSpacePageFlags(lop, true, is_compacting_); + lop = lop->next_page(); + } +} + + +bool IncrementalMarking::WorthActivating() { +#ifndef DEBUG + static const intptr_t kActivationThreshold = 8 * MB; +#else + // TODO(gc) consider setting this to some low level so that some + // debug tests run with incremental marking and some without. + static const intptr_t kActivationThreshold = 0; +#endif + + return !FLAG_expose_gc && + FLAG_incremental_marking && + !Serializer::enabled() && + heap_->PromotedSpaceSize() > kActivationThreshold; +} + + +void IncrementalMarking::ActivateGeneratedStub(Code* stub) { + ASSERT(RecordWriteStub::GetMode(stub) == + RecordWriteStub::STORE_BUFFER_ONLY); + + if (!IsMarking()) { + // Initially stub is generated in STORE_BUFFER_ONLY mode thus + // we don't need to do anything if incremental marking is + // not active. + } else if (IsCompacting()) { + RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL_COMPACTION); + } else { + RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL); + } +} + + +static void PatchIncrementalMarkingRecordWriteStubs( + Heap* heap, RecordWriteStub::Mode mode) { + UnseededNumberDictionary* stubs = heap->code_stubs(); + + int capacity = stubs->Capacity(); + for (int i = 0; i < capacity; i++) { + Object* k = stubs->KeyAt(i); + if (stubs->IsKey(k)) { + uint32_t key = NumberToUint32(k); + + if (CodeStub::MajorKeyFromKey(key) == + CodeStub::RecordWrite) { + Object* e = stubs->ValueAt(i); + if (e->IsCode()) { + RecordWriteStub::Patch(Code::cast(e), mode); + } + } + } + } +} + + +void IncrementalMarking::EnsureMarkingDequeIsCommitted() { + if (marking_deque_memory_ == NULL) { + marking_deque_memory_ = new VirtualMemory(4 * MB); + } + if (!marking_deque_memory_committed_) { + bool success = marking_deque_memory_->Commit( + reinterpret_cast<Address>(marking_deque_memory_->address()), + marking_deque_memory_->size(), + false); // Not executable. + CHECK(success); + marking_deque_memory_committed_ = true; + } +} + +void IncrementalMarking::UncommitMarkingDeque() { + if (state_ == STOPPED && marking_deque_memory_committed_) { + bool success = marking_deque_memory_->Uncommit( + reinterpret_cast<Address>(marking_deque_memory_->address()), + marking_deque_memory_->size()); + CHECK(success); + marking_deque_memory_committed_ = false; + } +} + + +void IncrementalMarking::Start() { + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Start\n"); + } + ASSERT(FLAG_incremental_marking); + ASSERT(state_ == STOPPED); + + ResetStepCounters(); + + if (heap_->old_pointer_space()->IsSweepingComplete() && + heap_->old_data_space()->IsSweepingComplete()) { + StartMarking(ALLOW_COMPACTION); + } else { + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Start sweeping.\n"); + } + state_ = SWEEPING; + } + + heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold); +} + + +static void MarkObjectGreyDoNotEnqueue(Object* obj) { + if (obj->IsHeapObject()) { + HeapObject* heap_obj = HeapObject::cast(obj); + MarkBit mark_bit = Marking::MarkBitFrom(HeapObject::cast(obj)); + if (Marking::IsBlack(mark_bit)) { + MemoryChunk::IncrementLiveBytesFromGC(heap_obj->address(), + -heap_obj->Size()); + } + Marking::AnyToGrey(mark_bit); + } +} + + +void IncrementalMarking::StartMarking(CompactionFlag flag) { + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Start marking\n"); + } + + is_compacting_ = !FLAG_never_compact && (flag == ALLOW_COMPACTION) && + heap_->mark_compact_collector()->StartCompaction( + MarkCompactCollector::INCREMENTAL_COMPACTION); + + state_ = MARKING; + + RecordWriteStub::Mode mode = is_compacting_ ? + RecordWriteStub::INCREMENTAL_COMPACTION : RecordWriteStub::INCREMENTAL; + + PatchIncrementalMarkingRecordWriteStubs(heap_, mode); + + EnsureMarkingDequeIsCommitted(); + + // Initialize marking stack. + Address addr = static_cast<Address>(marking_deque_memory_->address()); + size_t size = marking_deque_memory_->size(); + if (FLAG_force_marking_deque_overflows) size = 64 * kPointerSize; + marking_deque_.Initialize(addr, addr + size); + + ActivateIncrementalWriteBarrier(); + +#ifdef DEBUG + // Marking bits are cleared by the sweeper. + if (FLAG_verify_heap) { + heap_->mark_compact_collector()->VerifyMarkbitsAreClean(); + } +#endif + + heap_->CompletelyClearInstanceofCache(); + heap_->isolate()->compilation_cache()->MarkCompactPrologue(); + + if (FLAG_cleanup_code_caches_at_gc) { + // We will mark cache black with a separate pass + // when we finish marking. + MarkObjectGreyDoNotEnqueue(heap_->polymorphic_code_cache()); + } + + // Mark strong roots grey. + IncrementalMarkingRootMarkingVisitor visitor(heap_, this); + heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG); + + // Ready to start incremental marking. + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Running\n"); + } +} + + +void IncrementalMarking::PrepareForScavenge() { + if (!IsMarking()) return; + NewSpacePageIterator it(heap_->new_space()->FromSpaceStart(), + heap_->new_space()->FromSpaceEnd()); + while (it.has_next()) { + Bitmap::Clear(it.next()); + } +} + + +void IncrementalMarking::UpdateMarkingDequeAfterScavenge() { + if (!IsMarking()) return; + + int current = marking_deque_.bottom(); + int mask = marking_deque_.mask(); + int limit = marking_deque_.top(); + HeapObject** array = marking_deque_.array(); + int new_top = current; + + Map* filler_map = heap_->one_pointer_filler_map(); + + while (current != limit) { + HeapObject* obj = array[current]; + ASSERT(obj->IsHeapObject()); + current = ((current + 1) & mask); + if (heap_->InNewSpace(obj)) { + MapWord map_word = obj->map_word(); + if (map_word.IsForwardingAddress()) { + HeapObject* dest = map_word.ToForwardingAddress(); + array[new_top] = dest; + new_top = ((new_top + 1) & mask); + ASSERT(new_top != marking_deque_.bottom()); +#ifdef DEBUG + MarkBit mark_bit = Marking::MarkBitFrom(obj); + ASSERT(Marking::IsGrey(mark_bit) || + (obj->IsFiller() && Marking::IsWhite(mark_bit))); +#endif + } + } else if (obj->map() != filler_map) { + // Skip one word filler objects that appear on the + // stack when we perform in place array shift. + array[new_top] = obj; + new_top = ((new_top + 1) & mask); + ASSERT(new_top != marking_deque_.bottom()); +#ifdef DEBUG + MarkBit mark_bit = Marking::MarkBitFrom(obj); + ASSERT(Marking::IsGrey(mark_bit) || + (obj->IsFiller() && Marking::IsWhite(mark_bit))); +#endif + } + } + marking_deque_.set_top(new_top); + + steps_took_since_last_gc_ = 0; + steps_count_since_last_gc_ = 0; + longest_step_ = 0.0; +} + + +void IncrementalMarking::VisitGlobalContext(Context* ctx, ObjectVisitor* v) { + v->VisitPointers( + HeapObject::RawField( + ctx, Context::MarkCompactBodyDescriptor::kStartOffset), + HeapObject::RawField( + ctx, Context::MarkCompactBodyDescriptor::kEndOffset)); + + MarkCompactCollector* collector = heap_->mark_compact_collector(); + for (int idx = Context::FIRST_WEAK_SLOT; + idx < Context::GLOBAL_CONTEXT_SLOTS; + ++idx) { + Object** slot = + HeapObject::RawField(ctx, FixedArray::OffsetOfElementAt(idx)); + collector->RecordSlot(slot, slot, *slot); + } +} + + +void IncrementalMarking::Hurry() { + if (state() == MARKING) { + double start = 0.0; + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Hurry\n"); + start = OS::TimeCurrentMillis(); + } + // TODO(gc) hurry can mark objects it encounters black as mutator + // was stopped. + Map* filler_map = heap_->one_pointer_filler_map(); + Map* global_context_map = heap_->global_context_map(); + IncrementalMarkingMarkingVisitor marking_visitor(heap_, this); + while (!marking_deque_.IsEmpty()) { + HeapObject* obj = marking_deque_.Pop(); + + // Explicitly skip one word fillers. Incremental markbit patterns are + // correct only for objects that occupy at least two words. + Map* map = obj->map(); + if (map == filler_map) { + continue; + } else if (map == global_context_map) { + // Global contexts have weak fields. + VisitGlobalContext(Context::cast(obj), &marking_visitor); + } else { + obj->Iterate(&marking_visitor); + } + + MarkBit mark_bit = Marking::MarkBitFrom(obj); + ASSERT(!Marking::IsBlack(mark_bit)); + Marking::MarkBlack(mark_bit); + MemoryChunk::IncrementLiveBytesFromGC(obj->address(), obj->Size()); + } + state_ = COMPLETE; + if (FLAG_trace_incremental_marking) { + double end = OS::TimeCurrentMillis(); + PrintF("[IncrementalMarking] Complete (hurry), spent %d ms.\n", + static_cast<int>(end - start)); + } + } + + if (FLAG_cleanup_code_caches_at_gc) { + PolymorphicCodeCache* poly_cache = heap_->polymorphic_code_cache(); + Marking::GreyToBlack(Marking::MarkBitFrom(poly_cache)); + MemoryChunk::IncrementLiveBytesFromGC(poly_cache->address(), + PolymorphicCodeCache::kSize); + } + + Object* context = heap_->global_contexts_list(); + while (!context->IsUndefined()) { + // GC can happen when the context is not fully initialized, + // so the cache can be undefined. + HeapObject* cache = HeapObject::cast( + Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX)); + if (!cache->IsUndefined()) { + MarkBit mark_bit = Marking::MarkBitFrom(cache); + if (Marking::IsGrey(mark_bit)) { + Marking::GreyToBlack(mark_bit); + MemoryChunk::IncrementLiveBytesFromGC(cache->address(), cache->Size()); + } + } + context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); + } +} + + +void IncrementalMarking::Abort() { + if (IsStopped()) return; + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Aborting.\n"); + } + heap_->new_space()->LowerInlineAllocationLimit(0); + IncrementalMarking::set_should_hurry(false); + ResetStepCounters(); + if (IsMarking()) { + PatchIncrementalMarkingRecordWriteStubs(heap_, + RecordWriteStub::STORE_BUFFER_ONLY); + DeactivateIncrementalWriteBarrier(); + + if (is_compacting_) { + LargeObjectIterator it(heap_->lo_space()); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + Page* p = Page::FromAddress(obj->address()); + if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { + p->ClearFlag(Page::RESCAN_ON_EVACUATION); + } + } + } + } + heap_->isolate()->stack_guard()->Continue(GC_REQUEST); + state_ = STOPPED; + is_compacting_ = false; +} + + +void IncrementalMarking::Finalize() { + Hurry(); + state_ = STOPPED; + is_compacting_ = false; + heap_->new_space()->LowerInlineAllocationLimit(0); + IncrementalMarking::set_should_hurry(false); + ResetStepCounters(); + PatchIncrementalMarkingRecordWriteStubs(heap_, + RecordWriteStub::STORE_BUFFER_ONLY); + DeactivateIncrementalWriteBarrier(); + ASSERT(marking_deque_.IsEmpty()); + heap_->isolate()->stack_guard()->Continue(GC_REQUEST); +} + + +void IncrementalMarking::MarkingComplete() { + state_ = COMPLETE; + // We will set the stack guard to request a GC now. This will mean the rest + // of the GC gets performed as soon as possible (we can't do a GC here in a + // record-write context). If a few things get allocated between now and then + // that shouldn't make us do a scavenge and keep being incremental, so we set + // the should-hurry flag to indicate that there can't be much work left to do. + set_should_hurry(true); + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Complete (normal).\n"); + } + if (!heap_->idle_notification_will_schedule_next_gc()) { + heap_->isolate()->stack_guard()->RequestGC(); + } +} + + +void IncrementalMarking::Step(intptr_t allocated_bytes) { + if (heap_->gc_state() != Heap::NOT_IN_GC || + !FLAG_incremental_marking || + !FLAG_incremental_marking_steps || + (state_ != SWEEPING && state_ != MARKING)) { + return; + } + + allocated_ += allocated_bytes; + + if (allocated_ < kAllocatedThreshold) return; + + if (state_ == MARKING && no_marking_scope_depth_ > 0) return; + + intptr_t bytes_to_process = allocated_ * allocation_marking_factor_; + bytes_scanned_ += bytes_to_process; + + double start = 0; + + if (FLAG_trace_incremental_marking || FLAG_trace_gc) { + start = OS::TimeCurrentMillis(); + } + + if (state_ == SWEEPING) { + if (heap_->AdvanceSweepers(static_cast<int>(bytes_to_process))) { + bytes_scanned_ = 0; + StartMarking(PREVENT_COMPACTION); + } + } else if (state_ == MARKING) { + Map* filler_map = heap_->one_pointer_filler_map(); + Map* global_context_map = heap_->global_context_map(); + IncrementalMarkingMarkingVisitor marking_visitor(heap_, this); + while (!marking_deque_.IsEmpty() && bytes_to_process > 0) { + HeapObject* obj = marking_deque_.Pop(); + + // Explicitly skip one word fillers. Incremental markbit patterns are + // correct only for objects that occupy at least two words. + Map* map = obj->map(); + if (map == filler_map) continue; + + int size = obj->SizeFromMap(map); + bytes_to_process -= size; + MarkBit map_mark_bit = Marking::MarkBitFrom(map); + if (Marking::IsWhite(map_mark_bit)) { + WhiteToGreyAndPush(map, map_mark_bit); + } + + // TODO(gc) switch to static visitor instead of normal visitor. + if (map == global_context_map) { + // Global contexts have weak fields. + Context* ctx = Context::cast(obj); + + // We will mark cache black with a separate pass + // when we finish marking. + MarkObjectGreyDoNotEnqueue(ctx->normalized_map_cache()); + + VisitGlobalContext(ctx, &marking_visitor); + } else { + obj->IterateBody(map->instance_type(), size, &marking_visitor); + } + + MarkBit obj_mark_bit = Marking::MarkBitFrom(obj); + SLOW_ASSERT(Marking::IsGrey(obj_mark_bit) || + (obj->IsFiller() && Marking::IsWhite(obj_mark_bit))); + Marking::MarkBlack(obj_mark_bit); + MemoryChunk::IncrementLiveBytesFromGC(obj->address(), size); + } + if (marking_deque_.IsEmpty()) MarkingComplete(); + } + + allocated_ = 0; + + steps_count_++; + steps_count_since_last_gc_++; + + bool speed_up = false; + + if ((steps_count_ % kAllocationMarkingFactorSpeedupInterval) == 0) { + if (FLAG_trace_gc) { + PrintF("Speed up marking after %d steps\n", + static_cast<int>(kAllocationMarkingFactorSpeedupInterval)); + } + speed_up = true; + } + + bool space_left_is_very_small = + (old_generation_space_available_at_start_of_incremental_ < 10 * MB); + + bool only_1_nth_of_space_that_was_available_still_left = + (SpaceLeftInOldSpace() * (allocation_marking_factor_ + 1) < + old_generation_space_available_at_start_of_incremental_); + + if (space_left_is_very_small || + only_1_nth_of_space_that_was_available_still_left) { + if (FLAG_trace_gc) PrintF("Speed up marking because of low space left\n"); + speed_up = true; + } + + bool size_of_old_space_multiplied_by_n_during_marking = + (heap_->PromotedTotalSize() > + (allocation_marking_factor_ + 1) * + old_generation_space_used_at_start_of_incremental_); + if (size_of_old_space_multiplied_by_n_during_marking) { + speed_up = true; + if (FLAG_trace_gc) { + PrintF("Speed up marking because of heap size increase\n"); + } + } + + int64_t promoted_during_marking = heap_->PromotedTotalSize() + - old_generation_space_used_at_start_of_incremental_; + intptr_t delay = allocation_marking_factor_ * MB; + intptr_t scavenge_slack = heap_->MaxSemiSpaceSize(); + + // We try to scan at at least twice the speed that we are allocating. + if (promoted_during_marking > bytes_scanned_ / 2 + scavenge_slack + delay) { + if (FLAG_trace_gc) { + PrintF("Speed up marking because marker was not keeping up\n"); + } + speed_up = true; + } + + if (speed_up) { + if (state_ != MARKING) { + if (FLAG_trace_gc) { + PrintF("Postponing speeding up marking until marking starts\n"); + } + } else { + allocation_marking_factor_ += kAllocationMarkingFactorSpeedup; + allocation_marking_factor_ = static_cast<int>( + Min(kMaxAllocationMarkingFactor, + static_cast<intptr_t>(allocation_marking_factor_ * 1.3))); + if (FLAG_trace_gc) { + PrintF("Marking speed increased to %d\n", allocation_marking_factor_); + } + } + } + + if (FLAG_trace_incremental_marking || FLAG_trace_gc) { + double end = OS::TimeCurrentMillis(); + double delta = (end - start); + longest_step_ = Max(longest_step_, delta); + steps_took_ += delta; + steps_took_since_last_gc_ += delta; + } +} + + +void IncrementalMarking::ResetStepCounters() { + steps_count_ = 0; + steps_took_ = 0; + longest_step_ = 0.0; + old_generation_space_available_at_start_of_incremental_ = + SpaceLeftInOldSpace(); + old_generation_space_used_at_start_of_incremental_ = + heap_->PromotedTotalSize(); + steps_count_since_last_gc_ = 0; + steps_took_since_last_gc_ = 0; + bytes_rescanned_ = 0; + allocation_marking_factor_ = kInitialAllocationMarkingFactor; + bytes_scanned_ = 0; +} + + +int64_t IncrementalMarking::SpaceLeftInOldSpace() { + return heap_->MaxOldGenerationSize() - heap_->PromotedSpaceSize(); +} + +} } // namespace v8::internal diff --git a/deps/v8/src/incremental-marking.h b/deps/v8/src/incremental-marking.h new file mode 100644 index 0000000000..4f8fa6b127 --- /dev/null +++ b/deps/v8/src/incremental-marking.h @@ -0,0 +1,278 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_INCREMENTAL_MARKING_H_ +#define V8_INCREMENTAL_MARKING_H_ + + +#include "execution.h" +#include "mark-compact.h" +#include "objects.h" + +namespace v8 { +namespace internal { + + +class IncrementalMarking { + public: + enum State { + STOPPED, + SWEEPING, + MARKING, + COMPLETE + }; + + explicit IncrementalMarking(Heap* heap); + + void TearDown(); + + State state() { + ASSERT(state_ == STOPPED || FLAG_incremental_marking); + return state_; + } + + bool should_hurry() { return should_hurry_; } + void set_should_hurry(bool val) { should_hurry_ = val; } + + inline bool IsStopped() { return state() == STOPPED; } + + INLINE(bool IsMarking()) { return state() >= MARKING; } + + inline bool IsMarkingIncomplete() { return state() == MARKING; } + + inline bool IsComplete() { return state() == COMPLETE; } + + bool WorthActivating(); + + void Start(); + + void Stop(); + + void PrepareForScavenge(); + + void UpdateMarkingDequeAfterScavenge(); + + void Hurry(); + + void Finalize(); + + void Abort(); + + void MarkingComplete(); + + // It's hard to know how much work the incremental marker should do to make + // progress in the face of the mutator creating new work for it. We start + // of at a moderate rate of work and gradually increase the speed of the + // incremental marker until it completes. + // Do some marking every time this much memory has been allocated. + static const intptr_t kAllocatedThreshold = 65536; + // Start off by marking this many times more memory than has been allocated. + static const intptr_t kInitialAllocationMarkingFactor = 1; + // But if we are promoting a lot of data we need to mark faster to keep up + // with the data that is entering the old space through promotion. + static const intptr_t kFastMarking = 3; + // After this many steps we increase the marking/allocating factor. + static const intptr_t kAllocationMarkingFactorSpeedupInterval = 1024; + // This is how much we increase the marking/allocating factor by. + static const intptr_t kAllocationMarkingFactorSpeedup = 2; + static const intptr_t kMaxAllocationMarkingFactor = 1000; + + void OldSpaceStep(intptr_t allocated) { + Step(allocated * kFastMarking / kInitialAllocationMarkingFactor); + } + + void Step(intptr_t allocated); + + inline void RestartIfNotMarking() { + if (state_ == COMPLETE) { + state_ = MARKING; + if (FLAG_trace_incremental_marking) { + PrintF("[IncrementalMarking] Restarting (new grey objects)\n"); + } + } + } + + static void RecordWriteFromCode(HeapObject* obj, + Object* value, + Isolate* isolate); + + static void RecordWriteForEvacuationFromCode(HeapObject* obj, + Object** slot, + Isolate* isolate); + + INLINE(bool BaseRecordWrite(HeapObject* obj, Object** slot, Object* value)); + INLINE(void RecordWrite(HeapObject* obj, Object** slot, Object* value)); + INLINE(void RecordWriteIntoCode(HeapObject* obj, + RelocInfo* rinfo, + Object* value)); + INLINE(void RecordWriteOfCodeEntry(JSFunction* host, + Object** slot, + Code* value)); + + + void RecordWriteSlow(HeapObject* obj, Object** slot, Object* value); + void RecordWriteIntoCodeSlow(HeapObject* obj, + RelocInfo* rinfo, + Object* value); + void RecordWriteOfCodeEntrySlow(JSFunction* host, Object** slot, Code* value); + void RecordCodeTargetPatch(Code* host, Address pc, HeapObject* value); + void RecordCodeTargetPatch(Address pc, HeapObject* value); + + inline void RecordWrites(HeapObject* obj); + + inline void BlackToGreyAndUnshift(HeapObject* obj, MarkBit mark_bit); + + inline void WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit); + + inline void WhiteToGrey(HeapObject* obj, MarkBit mark_bit); + + // Does white->black or keeps gray or black color. Returns true if converting + // white to black. + inline bool MarkBlackOrKeepGrey(MarkBit mark_bit) { + ASSERT(!Marking::IsImpossible(mark_bit)); + if (mark_bit.Get()) { + // Grey or black: Keep the color. + return false; + } + mark_bit.Set(); + ASSERT(Marking::IsBlack(mark_bit)); + return true; + } + + inline int steps_count() { + return steps_count_; + } + + inline double steps_took() { + return steps_took_; + } + + inline double longest_step() { + return longest_step_; + } + + inline int steps_count_since_last_gc() { + return steps_count_since_last_gc_; + } + + inline double steps_took_since_last_gc() { + return steps_took_since_last_gc_; + } + + inline void SetOldSpacePageFlags(MemoryChunk* chunk) { + SetOldSpacePageFlags(chunk, IsMarking(), IsCompacting()); + } + + inline void SetNewSpacePageFlags(NewSpacePage* chunk) { + SetNewSpacePageFlags(chunk, IsMarking()); + } + + MarkingDeque* marking_deque() { return &marking_deque_; } + + bool IsCompacting() { return IsMarking() && is_compacting_; } + + void ActivateGeneratedStub(Code* stub); + + void NotifyOfHighPromotionRate() { + if (IsMarking()) { + if (allocation_marking_factor_ < kFastMarking) { + if (FLAG_trace_gc) { + PrintF("Increasing marking speed to %d due to high promotion rate\n", + static_cast<int>(kFastMarking)); + } + allocation_marking_factor_ = kFastMarking; + } + } + } + + void EnterNoMarkingScope() { + no_marking_scope_depth_++; + } + + void LeaveNoMarkingScope() { + no_marking_scope_depth_--; + } + + void UncommitMarkingDeque(); + + private: + int64_t SpaceLeftInOldSpace(); + + void ResetStepCounters(); + + enum CompactionFlag { ALLOW_COMPACTION, PREVENT_COMPACTION }; + + void StartMarking(CompactionFlag flag); + + void ActivateIncrementalWriteBarrier(PagedSpace* space); + static void ActivateIncrementalWriteBarrier(NewSpace* space); + void ActivateIncrementalWriteBarrier(); + + static void DeactivateIncrementalWriteBarrierForSpace(PagedSpace* space); + static void DeactivateIncrementalWriteBarrierForSpace(NewSpace* space); + void DeactivateIncrementalWriteBarrier(); + + static void SetOldSpacePageFlags(MemoryChunk* chunk, + bool is_marking, + bool is_compacting); + + static void SetNewSpacePageFlags(NewSpacePage* chunk, bool is_marking); + + void EnsureMarkingDequeIsCommitted(); + + void VisitGlobalContext(Context* ctx, ObjectVisitor* v); + + Heap* heap_; + + State state_; + bool is_compacting_; + + VirtualMemory* marking_deque_memory_; + bool marking_deque_memory_committed_; + MarkingDeque marking_deque_; + + int steps_count_; + double steps_took_; + double longest_step_; + int64_t old_generation_space_available_at_start_of_incremental_; + int64_t old_generation_space_used_at_start_of_incremental_; + int steps_count_since_last_gc_; + double steps_took_since_last_gc_; + int64_t bytes_rescanned_; + bool should_hurry_; + int allocation_marking_factor_; + intptr_t bytes_scanned_; + intptr_t allocated_; + + int no_marking_scope_depth_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalMarking); +}; + +} } // namespace v8::internal + +#endif // V8_INCREMENTAL_MARKING_H_ diff --git a/deps/v8/src/inspector.cc b/deps/v8/src/inspector.cc index 8fb80f1a22..833d338439 100644 --- a/deps/v8/src/inspector.cc +++ b/deps/v8/src/inspector.cc @@ -38,11 +38,11 @@ namespace internal { //============================================================================ // The Inspector. -void Inspector::DumpObjectType(FILE* out, Object *obj, bool print_more) { +void Inspector::DumpObjectType(FILE* out, Object* obj, bool print_more) { // Dump the object pointer. OS::FPrint(out, "%p:", reinterpret_cast<void*>(obj)); if (obj->IsHeapObject()) { - HeapObject *hobj = HeapObject::cast(obj); + HeapObject* hobj = HeapObject::cast(obj); OS::FPrint(out, " size %d :", hobj->Size()); } diff --git a/deps/v8/src/inspector.h b/deps/v8/src/inspector.h index e328bcdfa5..6962e21f4f 100644 --- a/deps/v8/src/inspector.h +++ b/deps/v8/src/inspector.h @@ -41,14 +41,14 @@ namespace internal { class Inspector { public: - static void DumpObjectType(FILE* out, Object *obj, bool print_more); - static void DumpObjectType(FILE* out, Object *obj) { + static void DumpObjectType(FILE* out, Object* obj, bool print_more); + static void DumpObjectType(FILE* out, Object* obj) { DumpObjectType(out, obj, false); } - static void DumpObjectType(Object *obj, bool print_more) { + static void DumpObjectType(Object* obj, bool print_more) { DumpObjectType(stdout, obj, print_more); } - static void DumpObjectType(Object *obj) { + static void DumpObjectType(Object* obj) { DumpObjectType(stdout, obj, false); } }; diff --git a/deps/v8/src/interpreter-irregexp.cc b/deps/v8/src/interpreter-irregexp.cc index 796a447e2f..b337e88452 100644 --- a/deps/v8/src/interpreter-irregexp.cc +++ b/deps/v8/src/interpreter-irregexp.cc @@ -33,9 +33,9 @@ #include "utils.h" #include "ast.h" #include "bytecodes-irregexp.h" +#include "jsregexp.h" #include "interpreter-irregexp.h" - namespace v8 { namespace internal { @@ -187,12 +187,12 @@ class BacktrackStack { template <typename Char> -static bool RawMatch(Isolate* isolate, - const byte* code_base, - Vector<const Char> subject, - int* registers, - int current, - uint32_t current_char) { +static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate, + const byte* code_base, + Vector<const Char> subject, + int* registers, + int current, + uint32_t current_char) { const byte* pc = code_base; // BacktrackStack ensures that the memory allocated for the backtracking stack // is returned to the system or cached if there is no stack being cached at @@ -211,24 +211,24 @@ static bool RawMatch(Isolate* isolate, switch (insn & BYTECODE_MASK) { BYTECODE(BREAK) UNREACHABLE(); - return false; + return RegExpImpl::RE_FAILURE; BYTECODE(PUSH_CP) if (--backtrack_stack_space < 0) { - return false; // No match on backtrack stack overflow. + return RegExpImpl::RE_EXCEPTION; } *backtrack_sp++ = current; pc += BC_PUSH_CP_LENGTH; break; BYTECODE(PUSH_BT) if (--backtrack_stack_space < 0) { - return false; // No match on backtrack stack overflow. + return RegExpImpl::RE_EXCEPTION; } *backtrack_sp++ = Load32Aligned(pc + 4); pc += BC_PUSH_BT_LENGTH; break; BYTECODE(PUSH_REGISTER) if (--backtrack_stack_space < 0) { - return false; // No match on backtrack stack overflow. + return RegExpImpl::RE_EXCEPTION; } *backtrack_sp++ = registers[insn >> BYTECODE_SHIFT]; pc += BC_PUSH_REGISTER_LENGTH; @@ -278,9 +278,9 @@ static bool RawMatch(Isolate* isolate, pc += BC_POP_REGISTER_LENGTH; break; BYTECODE(FAIL) - return false; + return RegExpImpl::RE_FAILURE; BYTECODE(SUCCEED) - return true; + return RegExpImpl::RE_SUCCESS; BYTECODE(ADVANCE_CP) current += insn >> BYTECODE_SHIFT; pc += BC_ADVANCE_CP_LENGTH; @@ -625,11 +625,12 @@ static bool RawMatch(Isolate* isolate, } -bool IrregexpInterpreter::Match(Isolate* isolate, - Handle<ByteArray> code_array, - Handle<String> subject, - int* registers, - int start_position) { +RegExpImpl::IrregexpResult IrregexpInterpreter::Match( + Isolate* isolate, + Handle<ByteArray> code_array, + Handle<String> subject, + int* registers, + int start_position) { ASSERT(subject->IsFlat()); AssertNoAllocation a; diff --git a/deps/v8/src/interpreter-irregexp.h b/deps/v8/src/interpreter-irregexp.h index 076f0c5081..0f45d98207 100644 --- a/deps/v8/src/interpreter-irregexp.h +++ b/deps/v8/src/interpreter-irregexp.h @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,11 +36,11 @@ namespace internal { class IrregexpInterpreter { public: - static bool Match(Isolate* isolate, - Handle<ByteArray> code, - Handle<String> subject, - int* captures, - int start_position); + static RegExpImpl::IrregexpResult Match(Isolate* isolate, + Handle<ByteArray> code, + Handle<String> subject, + int* captures, + int start_position); }; diff --git a/deps/v8/src/isolate-inl.h b/deps/v8/src/isolate-inl.h index aa6b5372ca..0a2c17404e 100644 --- a/deps/v8/src/isolate-inl.h +++ b/deps/v8/src/isolate-inl.h @@ -36,6 +36,19 @@ namespace v8 { namespace internal { +SaveContext::SaveContext(Isolate* isolate) : prev_(isolate->save_context()) { + if (isolate->context() != NULL) { + context_ = Handle<Context>(isolate->context()); +#if __GNUC_VERSION__ >= 40100 && __GNUC_VERSION__ < 40300 + dummy_ = Handle<Context>(isolate->context()); +#endif + } + isolate->set_save_context(this); + + c_entry_fp_ = isolate->c_entry_fp(isolate->thread_local_top()); +} + + bool Isolate::DebuggerHasBreakPoints() { #ifdef ENABLE_DEBUGGER_SUPPORT return debug()->has_break_points(); diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index fd0f673e7e..96c45b1360 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -98,6 +98,15 @@ void ThreadLocalTop::InitializeInternal() { failed_access_check_callback_ = NULL; save_context_ = NULL; catcher_ = NULL; + top_lookup_result_ = NULL; + + // These members are re-initialized later after deserialization + // is complete. + pending_exception_ = NULL; + has_pending_message_ = false; + pending_message_obj_ = NULL; + pending_message_script_ = NULL; + scheduled_exception_ = NULL; } @@ -472,6 +481,9 @@ void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) { for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) { it.frame()->Iterate(v); } + + // Iterate pointers in live lookup results. + thread->top_lookup_result_->Iterate(v); } @@ -530,6 +542,18 @@ Handle<String> Isolate::StackTraceString() { } +void Isolate::CaptureAndSetCurrentStackTraceFor(Handle<JSObject> error_object) { + if (capture_stack_trace_for_uncaught_exceptions_) { + // Capture stack trace for a detailed exception message. + Handle<String> key = factory()->hidden_stack_trace_symbol(); + Handle<JSArray> stack_trace = CaptureCurrentStackTrace( + stack_trace_for_uncaught_exceptions_frame_limit_, + stack_trace_for_uncaught_exceptions_options_); + JSObject::SetHiddenProperty(error_object, key, stack_trace); + } +} + + Handle<JSArray> Isolate::CaptureCurrentStackTrace( int frame_limit, StackTrace::StackTraceOptions options) { // Ensure no negative values. @@ -558,7 +582,7 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace( frame->Summarize(&frames); for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) { // Create a JSObject to hold the information for the StackFrame. - Handle<JSObject> stackFrame = factory()->NewJSObject(object_function()); + Handle<JSObject> stack_frame = factory()->NewJSObject(object_function()); Handle<JSFunction> fun = frames[i].function(); Handle<Script> script(Script::cast(fun->shared()->script())); @@ -579,16 +603,24 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace( // tag. column_offset += script->column_offset()->value(); } - SetLocalPropertyNoThrow(stackFrame, column_key, - Handle<Smi>(Smi::FromInt(column_offset + 1))); + CHECK_NOT_EMPTY_HANDLE( + this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, column_key, + Handle<Smi>(Smi::FromInt(column_offset + 1)), NONE)); } - SetLocalPropertyNoThrow(stackFrame, line_key, - Handle<Smi>(Smi::FromInt(line_number + 1))); + CHECK_NOT_EMPTY_HANDLE( + this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, line_key, + Handle<Smi>(Smi::FromInt(line_number + 1)), NONE)); } if (options & StackTrace::kScriptName) { Handle<Object> script_name(script->name(), this); - SetLocalPropertyNoThrow(stackFrame, script_key, script_name); + CHECK_NOT_EMPTY_HANDLE(this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, script_key, script_name, NONE)); } if (options & StackTrace::kScriptNameOrSourceURL) { @@ -604,8 +636,10 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace( if (caught_exception) { result = factory()->undefined_value(); } - SetLocalPropertyNoThrow(stackFrame, script_name_or_source_url_key, - result); + CHECK_NOT_EMPTY_HANDLE(this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, script_name_or_source_url_key, + result, NONE)); } if (options & StackTrace::kFunctionName) { @@ -613,23 +647,30 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace( if (fun_name->ToBoolean()->IsFalse()) { fun_name = Handle<Object>(fun->shared()->inferred_name(), this); } - SetLocalPropertyNoThrow(stackFrame, function_key, fun_name); + CHECK_NOT_EMPTY_HANDLE(this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, function_key, fun_name, NONE)); } if (options & StackTrace::kIsEval) { int type = Smi::cast(script->compilation_type())->value(); Handle<Object> is_eval = (type == Script::COMPILATION_TYPE_EVAL) ? factory()->true_value() : factory()->false_value(); - SetLocalPropertyNoThrow(stackFrame, eval_key, is_eval); + CHECK_NOT_EMPTY_HANDLE(this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, eval_key, is_eval, NONE)); } if (options & StackTrace::kIsConstructor) { Handle<Object> is_constructor = (frames[i].is_constructor()) ? factory()->true_value() : factory()->false_value(); - SetLocalPropertyNoThrow(stackFrame, constructor_key, is_constructor); + CHECK_NOT_EMPTY_HANDLE(this, + JSObject::SetLocalPropertyIgnoreAttributes( + stack_frame, constructor_key, + is_constructor, NONE)); } - FixedArray::cast(stack_trace->elements())->set(frames_seen, *stackFrame); + FixedArray::cast(stack_trace->elements())->set(frames_seen, *stack_frame); frames_seen++; } it.Advance(); @@ -982,7 +1023,7 @@ bool Isolate::ShouldReportException(bool* can_be_caught_externally, // Find the top-most try-catch handler. StackHandler* handler = StackHandler::FromAddress(Isolate::handler(thread_local_top())); - while (handler != NULL && !handler->is_try_catch()) { + while (handler != NULL && !handler->is_catch()) { handler = handler->next(); } @@ -1008,22 +1049,39 @@ bool Isolate::ShouldReportException(bool* can_be_caught_externally, } -void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) { +bool Isolate::IsErrorObject(Handle<Object> obj) { + if (!obj->IsJSObject()) return false; + + String* error_key = *(factory()->LookupAsciiSymbol("$Error")); + Object* error_constructor = + js_builtins_object()->GetPropertyNoExceptionThrown(error_key); + + for (Object* prototype = *obj; !prototype->IsNull(); + prototype = prototype->GetPrototype()) { + if (!prototype->IsJSObject()) return false; + if (JSObject::cast(prototype)->map()->constructor() == error_constructor) { + return true; + } + } + return false; +} + + +void Isolate::DoThrow(Object* exception, MessageLocation* location) { ASSERT(!has_pending_exception()); HandleScope scope; - Object* exception_object = Smi::FromInt(0); - bool is_object = exception->ToObject(&exception_object); - Handle<Object> exception_handle(exception_object); + Handle<Object> exception_handle(exception); // Determine reporting and whether the exception is caught externally. bool catchable_by_javascript = is_catchable_by_javascript(exception); - // Only real objects can be caught by JS. - ASSERT(!catchable_by_javascript || is_object); bool can_be_caught_externally = false; bool should_report_exception = ShouldReportException(&can_be_caught_externally, catchable_by_javascript); bool report_exception = catchable_by_javascript && should_report_exception; + bool try_catch_needs_message = + can_be_caught_externally && try_catch_handler()->capture_message_; + bool bootstrapping = bootstrapper()->IsActive(); #ifdef ENABLE_DEBUGGER_SUPPORT // Notify debugger of exception. @@ -1032,63 +1090,74 @@ void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) { } #endif - // Generate the message. - Handle<Object> message_obj; - MessageLocation potential_computed_location; - bool try_catch_needs_message = - can_be_caught_externally && - try_catch_handler()->capture_message_; + // Generate the message if required. if (report_exception || try_catch_needs_message) { + MessageLocation potential_computed_location; if (location == NULL) { - // If no location was specified we use a computed one instead + // If no location was specified we use a computed one instead. ComputeLocation(&potential_computed_location); location = &potential_computed_location; } - if (!bootstrapper()->IsActive()) { - // It's not safe to try to make message objects or collect stack - // traces while the bootstrapper is active since the infrastructure - // may not have been properly initialized. + // It's not safe to try to make message objects or collect stack traces + // while the bootstrapper is active since the infrastructure may not have + // been properly initialized. + if (!bootstrapping) { Handle<String> stack_trace; if (FLAG_trace_exception) stack_trace = StackTraceString(); Handle<JSArray> stack_trace_object; - if (report_exception && capture_stack_trace_for_uncaught_exceptions_) { + if (capture_stack_trace_for_uncaught_exceptions_) { + if (IsErrorObject(exception_handle)) { + // We fetch the stack trace that corresponds to this error object. + String* key = heap()->hidden_stack_trace_symbol(); + Object* stack_property = + JSObject::cast(*exception_handle)->GetHiddenProperty(key); + // Property lookup may have failed. In this case it's probably not + // a valid Error object. + if (stack_property->IsJSArray()) { + stack_trace_object = Handle<JSArray>(JSArray::cast(stack_property)); + } + } + if (stack_trace_object.is_null()) { + // Not an error object, we capture at throw site. stack_trace_object = CaptureCurrentStackTrace( stack_trace_for_uncaught_exceptions_frame_limit_, stack_trace_for_uncaught_exceptions_options_); + } } - ASSERT(is_object); // Can't use the handle unless there's a real object. - message_obj = MessageHandler::MakeMessageObject("uncaught_exception", - location, HandleVector<Object>(&exception_handle, 1), stack_trace, + Handle<Object> message_obj = MessageHandler::MakeMessageObject( + "uncaught_exception", + location, + HandleVector<Object>(&exception_handle, 1), + stack_trace, stack_trace_object); + thread_local_top()->pending_message_obj_ = *message_obj; + if (location != NULL) { + thread_local_top()->pending_message_script_ = *location->script(); + thread_local_top()->pending_message_start_pos_ = location->start_pos(); + thread_local_top()->pending_message_end_pos_ = location->end_pos(); + } + } else if (location != NULL && !location->script().is_null()) { + // We are bootstrapping and caught an error where the location is set + // and we have a script for the location. + // In this case we could have an extension (or an internal error + // somewhere) and we print out the line number at which the error occured + // to the console for easier debugging. + int line_number = GetScriptLineNumberSafe(location->script(), + location->start_pos()); + OS::PrintError("Extension or internal compilation error at line %d.\n", + line_number); } } // Save the message for reporting if the the exception remains uncaught. thread_local_top()->has_pending_message_ = report_exception; - if (!message_obj.is_null()) { - thread_local_top()->pending_message_obj_ = *message_obj; - if (location != NULL) { - thread_local_top()->pending_message_script_ = *location->script(); - thread_local_top()->pending_message_start_pos_ = location->start_pos(); - thread_local_top()->pending_message_end_pos_ = location->end_pos(); - } - } // Do not forget to clean catcher_ if currently thrown exception cannot // be caught. If necessary, ReThrow will update the catcher. thread_local_top()->catcher_ = can_be_caught_externally ? try_catch_handler() : NULL; - // NOTE: Notifying the debugger or generating the message - // may have caused new exceptions. For now, we just ignore - // that and set the pending exception to the original one. - if (is_object) { - set_pending_exception(*exception_handle); - } else { - // Failures are not on the heap so they neither need nor work with handles. - ASSERT(exception_handle->IsFailure()); - set_pending_exception(exception); - } + set_pending_exception(*exception_handle); } @@ -1124,8 +1193,8 @@ bool Isolate::IsExternallyCaught() { StackHandler* handler = StackHandler::FromAddress(Isolate::handler(thread_local_top())); while (handler != NULL && handler->address() < external_handler_address) { - ASSERT(!handler->is_try_catch()); - if (handler->is_try_finally()) return false; + ASSERT(!handler->is_catch()); + if (handler->is_finally()) return false; handler = handler->next(); } @@ -1180,7 +1249,7 @@ bool Isolate::OptionalRescheduleException(bool is_bottom_call) { ASSERT(has_pending_exception()); PropagatePendingExceptionToExternalTryCatch(); - // Allways reschedule out of memory exceptions. + // Always reschedule out of memory exceptions. if (!is_out_of_memory()) { bool is_termination_exception = pending_exception() == heap_.termination_exception(); @@ -1284,6 +1353,9 @@ char* Isolate::ArchiveThread(char* to) { memcpy(to, reinterpret_cast<char*>(thread_local_top()), sizeof(ThreadLocalTop)); InitializeThreadLocal(); + clear_pending_exception(); + clear_pending_message(); + clear_scheduled_exception(); return to + sizeof(ThreadLocalTop); } @@ -1403,14 +1475,17 @@ Isolate::Isolate() in_use_list_(0), free_list_(0), preallocated_storage_preallocated_(false), - pc_to_code_cache_(NULL), + inner_pointer_to_code_cache_(NULL), write_input_buffer_(NULL), global_handles_(NULL), context_switcher_(NULL), thread_manager_(NULL), + fp_stubs_generated_(false), + has_installed_extensions_(false), string_tracker_(NULL), regexp_stack_(NULL), - embedder_data_(NULL) { + embedder_data_(NULL), + context_exit_happened_(false) { TRACE_ISOLATE(constructor); memset(isolate_addresses_, 0, @@ -1575,8 +1650,8 @@ Isolate::~Isolate() { compilation_cache_ = NULL; delete bootstrapper_; bootstrapper_ = NULL; - delete pc_to_code_cache_; - pc_to_code_cache_ = NULL; + delete inner_pointer_to_code_cache_; + inner_pointer_to_code_cache_ = NULL; delete write_input_buffer_; write_input_buffer_ = NULL; @@ -1610,9 +1685,6 @@ Isolate::~Isolate() { void Isolate::InitializeThreadLocal() { thread_local_top_.isolate_ = this; thread_local_top_.Initialize(); - clear_pending_exception(); - clear_pending_message(); - clear_scheduled_exception(); } @@ -1700,7 +1772,7 @@ bool Isolate::Init(Deserializer* des) { context_slot_cache_ = new ContextSlotCache(); descriptor_lookup_cache_ = new DescriptorLookupCache(); unicode_cache_ = new UnicodeCache(); - pc_to_code_cache_ = new PcToCodeCache(this); + inner_pointer_to_code_cache_ = new InnerPointerToCodeCache(this); write_input_buffer_ = new StringInputBuffer(); global_handles_ = new GlobalHandles(this); bootstrapper_ = new Bootstrapper(); @@ -1710,10 +1782,10 @@ bool Isolate::Init(Deserializer* des) { regexp_stack_->isolate_ = this; // Enable logging before setting up the heap - logger_->Setup(); + logger_->SetUp(); - CpuProfiler::Setup(); - HeapProfiler::Setup(); + CpuProfiler::SetUp(); + HeapProfiler::SetUp(); // Initialize other runtime facilities #if defined(USE_SIMULATOR) @@ -1730,10 +1802,10 @@ bool Isolate::Init(Deserializer* des) { stack_guard_.InitThread(lock); } - // Setup the object heap. + // SetUp the object heap. const bool create_heap_objects = (des == NULL); - ASSERT(!heap_.HasBeenSetup()); - if (!heap_.Setup(create_heap_objects)) { + ASSERT(!heap_.HasBeenSetUp()); + if (!heap_.SetUp(create_heap_objects)) { V8::SetFatalError(); return false; } @@ -1741,7 +1813,7 @@ bool Isolate::Init(Deserializer* des) { InitializeThreadLocal(); bootstrapper_->Initialize(create_heap_objects); - builtins_.Setup(create_heap_objects); + builtins_.SetUp(create_heap_objects); // Only preallocate on the first initialization. if (FLAG_preallocate_message_memory && preallocated_message_space_ == NULL) { @@ -1760,23 +1832,28 @@ bool Isolate::Init(Deserializer* des) { } #ifdef ENABLE_DEBUGGER_SUPPORT - debug_->Setup(create_heap_objects); + debug_->SetUp(create_heap_objects); #endif stub_cache_->Initialize(create_heap_objects); // If we are deserializing, read the state into the now-empty heap. if (des != NULL) { des->Deserialize(); - stub_cache_->Clear(); + stub_cache_->Initialize(true); } + // Finish initialization of ThreadLocal after deserialization is done. + clear_pending_exception(); + clear_pending_message(); + clear_scheduled_exception(); + // Deserializing may put strange things in the root array's copy of the // stack guard. heap_.SetStackLimits(); deoptimizer_data_ = new DeoptimizerData; runtime_profiler_ = new RuntimeProfiler(this); - runtime_profiler_->Setup(); + runtime_profiler_->SetUp(); // If we are deserializing, log non-function code objects and compiled // functions found in the snapshot. @@ -1787,6 +1864,7 @@ bool Isolate::Init(Deserializer* des) { } state_ = INITIALIZED; + time_millis_at_init_ = OS::TimeCurrentMillis(); return true; } diff --git a/deps/v8/src/isolate.h b/deps/v8/src/isolate.h index 2582da644a..5612630c04 100644 --- a/deps/v8/src/isolate.h +++ b/deps/v8/src/isolate.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -66,7 +66,7 @@ class HandleScopeImplementer; class HeapProfiler; class InlineRuntimeFunctionsTable; class NoAllocationStringAllocator; -class PcToCodeCache; +class InnerPointerToCodeCache; class PreallocatedMemoryThread; class RegExpStack; class SaveContext; @@ -106,15 +106,28 @@ class Simulator; // of handles to the actual constants. typedef ZoneList<Handle<Object> > ZoneObjectList; -#define RETURN_IF_SCHEDULED_EXCEPTION(isolate) \ - if (isolate->has_scheduled_exception()) \ - return isolate->PromoteScheduledException() +#define RETURN_IF_SCHEDULED_EXCEPTION(isolate) \ + do { \ + Isolate* __isolate__ = (isolate); \ + if (__isolate__->has_scheduled_exception()) { \ + return __isolate__->PromoteScheduledException(); \ + } \ + } while (false) #define RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, value) \ - if (call.is_null()) { \ - ASSERT(isolate->has_pending_exception()); \ - return value; \ - } + do { \ + if ((call).is_null()) { \ + ASSERT((isolate)->has_pending_exception()); \ + return (value); \ + } \ + } while (false) + +#define CHECK_NOT_EMPTY_HANDLE(isolate, call) \ + do { \ + ASSERT(!(isolate)->has_pending_exception()); \ + CHECK(!(call).is_null()); \ + CHECK(!(isolate)->has_pending_exception()); \ + } while (false) #define RETURN_IF_EMPTY_HANDLE(isolate, call) \ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, Failure::Exception()) @@ -245,7 +258,7 @@ class ThreadLocalTop BASE_EMBEDDED { #endif #endif // USE_SIMULATOR - Address js_entry_sp_; // the stack pointer of the bottom js entry frame + Address js_entry_sp_; // the stack pointer of the bottom JS entry frame Address external_callback_; // the external callback we're currently in StateTag current_vm_state_; @@ -255,6 +268,9 @@ class ThreadLocalTop BASE_EMBEDDED { // Call back function to report unsafe JS accesses. v8::FailedAccessCheckCallback failed_access_check_callback_; + // Head of the list of live LookupResults. + LookupResult* top_lookup_result_; + // Whether out of memory exceptions should be ignored. bool ignore_out_of_memory_; @@ -311,7 +327,6 @@ class HashMap; V(int, bad_char_shift_table, kUC16AlphabetSize) \ V(int, good_suffix_shift_table, (kBMMaxShift + 1)) \ V(int, suffix_table, (kBMMaxShift + 1)) \ - V(uint32_t, random_seed, 2) \ V(uint32_t, private_random_seed, 2) \ ISOLATE_INIT_DEBUG_ARRAY_LIST(V) @@ -347,7 +362,7 @@ typedef List<HeapObject*, PreallocatedStorage> DebugObjectCache; /* Serializer state. */ \ V(ExternalReferenceTable*, external_reference_table, NULL) \ /* AstNode state. */ \ - V(unsigned, ast_node_id, 0) \ + V(int, ast_node_id, 0) \ V(unsigned, ast_node_count, 0) \ /* SafeStackFrameIterator activations count. */ \ V(int, safe_stack_iterator_counter, 0) \ @@ -470,7 +485,7 @@ class Isolate { bool IsDefaultIsolate() const { return this == default_isolate_; } // Ensures that process-wide resources and the default isolate have been - // allocated. It is only necessary to call this method in rare casses, for + // allocated. It is only necessary to call this method in rare cases, for // example if you are using V8 from within the body of a static initializer. // Safe to call multiple times. static void EnsureDefaultIsolate(); @@ -620,7 +635,7 @@ class Isolate { void* formal_count_address() { return &thread_local_top_.formal_count_; } // Returns the global object of the current context. It could be - // a builtin object, or a js global object. + // a builtin object, or a JS global object. Handle<GlobalObject> global() { return Handle<GlobalObject>(context()->global()); } @@ -688,6 +703,8 @@ class Isolate { int frame_limit, StackTrace::StackTraceOptions options); + void CaptureAndSetCurrentStackTraceFor(Handle<JSObject> error_object); + // Returns if the top context may access the given global object. If // the result is false, the pending exception is guaranteed to be // set. @@ -714,7 +731,7 @@ class Isolate { // Promote a scheduled exception to pending. Asserts has_scheduled_exception. Failure* PromoteScheduledException(); - void DoThrow(MaybeObject* exception, MessageLocation* location); + void DoThrow(Object* exception, MessageLocation* location); // Checks if exception should be reported and finds out if it's // caught externally. bool ShouldReportException(bool* can_be_caught_externally, @@ -841,7 +858,9 @@ class Isolate { return unicode_cache_; } - PcToCodeCache* pc_to_code_cache() { return pc_to_code_cache_; } + InnerPointerToCodeCache* inner_pointer_to_code_cache() { + return inner_pointer_to_code_cache_; + } StringInputBuffer* write_input_buffer() { return write_input_buffer_; } @@ -879,12 +898,24 @@ class Isolate { RuntimeState* runtime_state() { return &runtime_state_; } + void set_fp_stubs_generated(bool value) { + fp_stubs_generated_ = value; + } + + bool fp_stubs_generated() { return fp_stubs_generated_; } + StaticResource<SafeStringInputBuffer>* compiler_safe_string_input_buffer() { return &compiler_safe_string_input_buffer_; } Builtins* builtins() { return &builtins_; } + void NotifyExtensionInstalled() { + has_installed_extensions_ = true; + } + + bool has_installed_extensions() { return has_installed_extensions_; } + unibrow::Mapping<unibrow::Ecma262Canonicalize>* regexp_macro_assembler_canonicalize() { return ®exp_macro_assembler_canonicalize_; @@ -987,6 +1018,24 @@ class Isolate { void SetData(void* data) { embedder_data_ = data; } void* GetData() { return embedder_data_; } + LookupResult* top_lookup_result() { + return thread_local_top_.top_lookup_result_; + } + void SetTopLookupResult(LookupResult* top) { + thread_local_top_.top_lookup_result_ = top; + } + + bool context_exit_happened() { + return context_exit_happened_; + } + void set_context_exit_happened(bool context_exit_happened) { + context_exit_happened_ = context_exit_happened; + } + + double time_millis_since_init() { + return OS::TimeCurrentMillis() - time_millis_at_init_; + } + private: Isolate(); @@ -1028,6 +1077,7 @@ class Isolate { Isolate* previous_isolate; EntryStackItem* previous_item; + private: DISALLOW_COPY_AND_ASSIGN(EntryStackItem); }; @@ -1093,6 +1143,10 @@ class Isolate { void InitializeDebugger(); + // Traverse prototype chain to find out whether the object is derived from + // the Error object. + bool IsErrorObject(Handle<Object> obj); + int stack_trace_nesting_level_; StringStream* incomplete_message_; // The preallocated memory thread singleton. @@ -1130,14 +1184,16 @@ class Isolate { PreallocatedStorage in_use_list_; PreallocatedStorage free_list_; bool preallocated_storage_preallocated_; - PcToCodeCache* pc_to_code_cache_; + InnerPointerToCodeCache* inner_pointer_to_code_cache_; StringInputBuffer* write_input_buffer_; GlobalHandles* global_handles_; ContextSwitcher* context_switcher_; ThreadManager* thread_manager_; RuntimeState runtime_state_; + bool fp_stubs_generated_; StaticResource<SafeStringInputBuffer> compiler_safe_string_input_buffer_; Builtins builtins_; + bool has_installed_extensions_; StringTracker* string_tracker_; unibrow::Mapping<unibrow::Ecma262UnCanonicalize> jsregexp_uncanonicalize_; unibrow::Mapping<unibrow::CanonicalizationRange> jsregexp_canonrange_; @@ -1150,6 +1206,13 @@ class Isolate { unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_; void* embedder_data_; + // The garbage collector should be a little more aggressive when it knows + // that a context was recently exited. + bool context_exit_happened_; + + // Time stamp at initialization. + double time_millis_at_init_; + #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \ defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__) bool simulator_initialized_; @@ -1210,19 +1273,7 @@ class Isolate { // versions of GCC. See V8 issue 122 for details. class SaveContext BASE_EMBEDDED { public: - explicit SaveContext(Isolate* isolate) : prev_(isolate->save_context()) { - if (isolate->context() != NULL) { - context_ = Handle<Context>(isolate->context()); -#if __GNUC_VERSION__ >= 40100 && __GNUC_VERSION__ < 40300 - dummy_ = Handle<Context>(isolate->context()); -#endif - } - isolate->set_save_context(this); - - // If there is no JS frame under the current C frame, use the value 0. - JavaScriptFrameIterator it(isolate); - js_sp_ = it.done() ? 0 : it.frame()->sp(); - } + inline explicit SaveContext(Isolate* isolate); ~SaveContext() { if (context_.is_null()) { @@ -1240,8 +1291,8 @@ class SaveContext BASE_EMBEDDED { SaveContext* prev() { return prev_; } // Returns true if this save context is below a given JavaScript frame. - bool below(JavaScriptFrame* frame) { - return (js_sp_ == 0) || (frame->sp() < js_sp_); + bool IsBelowFrame(JavaScriptFrame* frame) { + return (c_entry_fp_ == 0) || (c_entry_fp_ > frame->sp()); } private: @@ -1250,7 +1301,7 @@ class SaveContext BASE_EMBEDDED { Handle<Context> dummy_; #endif SaveContext* prev_; - Address js_sp_; // The top JS frame's sp when saving context. + Address c_entry_fp_; }; diff --git a/deps/v8/src/json-parser.h b/deps/v8/src/json-parser.h index 68eab65fd5..d22cd0da3a 100644 --- a/deps/v8/src/json-parser.h +++ b/deps/v8/src/json-parser.h @@ -130,7 +130,7 @@ class JsonParser BASE_EMBEDDED { // An object literal is a squiggly-braced and comma separated sequence // (possibly empty) of key/value pairs, where the key is a JSON string // literal, the value is a JSON value, and the two are separated by a colon. - // A JSON array dosn't allow numbers and identifiers as keys, like a + // A JSON array doesn't allow numbers and identifiers as keys, like a // JavaScript array. Handle<Object> ParseJsonObject(); @@ -165,7 +165,7 @@ class JsonParser BASE_EMBEDDED { template <bool seq_ascii> Handle<Object> JsonParser<seq_ascii>::ParseJson(Handle<String> source) { - isolate_ = source->map()->isolate(); + isolate_ = source->map()->GetHeap()->isolate(); FlattenString(source); source_ = source; source_length_ = source_->length(); @@ -177,7 +177,7 @@ Handle<Object> JsonParser<seq_ascii>::ParseJson(Handle<String> source) { // Set initial position right before the string. position_ = -1; - // Advance to the first character (posibly EOS) + // Advance to the first character (possibly EOS) AdvanceSkipWhitespace(); Handle<Object> result = ParseJsonValue(); if (result.is_null() || c0_ != kEndOfString) { @@ -303,11 +303,12 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() { uint32_t index; if (key->AsArrayIndex(&index)) { - SetOwnElement(json_object, index, value, kNonStrictMode); + JSObject::SetOwnElement(json_object, index, value, kNonStrictMode); } else if (key->Equals(isolate()->heap()->Proto_symbol())) { SetPrototype(json_object, value); } else { - SetLocalPropertyIgnoreAttributes(json_object, key, value, NONE); + JSObject::SetLocalPropertyIgnoreAttributes( + json_object, key, value, NONE); } } while (MatchSkipWhiteSpace(',')); if (c0_ != '}') { diff --git a/deps/v8/src/json.js b/deps/v8/src/json.js index deba126212..ccef4456d6 100644 --- a/deps/v8/src/json.js +++ b/deps/v8/src/json.js @@ -345,4 +345,4 @@ function SetUpJSON() { )); } -SetUpJSON() +SetUpJSON(); diff --git a/deps/v8/src/jsregexp.cc b/deps/v8/src/jsregexp.cc index 3ebfbdfc98..18b86bafee 100644 --- a/deps/v8/src/jsregexp.cc +++ b/deps/v8/src/jsregexp.cc @@ -68,9 +68,9 @@ Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor, Handle<String> flags, bool* has_pending_exception) { // Call the construct code with 2 arguments. - Object** argv[2] = { Handle<Object>::cast(pattern).location(), - Handle<Object>::cast(flags).location() }; - return Execution::New(constructor, 2, argv, has_pending_exception); + Handle<Object> argv[] = { pattern, flags }; + return Execution::New(constructor, ARRAY_SIZE(argv), argv, + has_pending_exception); } @@ -509,14 +509,16 @@ RegExpImpl::IrregexpResult RegExpImpl::IrregexpExecOnce( } Handle<ByteArray> byte_codes(IrregexpByteCode(*irregexp, is_ascii), isolate); - if (IrregexpInterpreter::Match(isolate, - byte_codes, - subject, - register_vector, - index)) { - return RE_SUCCESS; + IrregexpResult result = IrregexpInterpreter::Match(isolate, + byte_codes, + subject, + register_vector, + index); + if (result == RE_EXCEPTION) { + ASSERT(!isolate->has_pending_exception()); + isolate->StackOverflow(); } - return RE_FAILURE; + return result; #endif // V8_INTERPRETED_REGEXP } @@ -702,7 +704,7 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp, // the virtualized backtrack stack and some register changes. When a node is // to be emitted it can flush the Trace or update it. Flushing the Trace // will emit code to bring the actual state into line with the virtual state. -// Avoiding flushing the state can postpone some work (eg updates of capture +// Avoiding flushing the state can postpone some work (e.g. updates of capture // registers). Postponing work can save time when executing the regular // expression since it may be found that the work never has to be done as a // failure to match can occur. In addition it is much faster to jump to a @@ -2634,7 +2636,7 @@ void TextNode::MakeCaseIndependent(bool is_ascii) { TextElement elm = elms_->at(i); if (elm.type == TextElement::CHAR_CLASS) { RegExpCharacterClass* cc = elm.data.u_char_class; - // None of the standard character classses is different in the case + // None of the standard character classes is different in the case // independent case and it slows us down if we don't know that. if (cc->is_standard()) continue; ZoneList<CharacterRange>* ranges = cc->ranges(); @@ -4723,7 +4725,6 @@ bool OutSet::Get(unsigned value) { const uc16 DispatchTable::Config::kNoKey = unibrow::Utf8::kBadChar; -const DispatchTable::Entry DispatchTable::Config::kNoValue; void DispatchTable::AddRange(CharacterRange full_range, int value) { diff --git a/deps/v8/src/jsregexp.h b/deps/v8/src/jsregexp.h index 54297a49ab..42c76fbd67 100644 --- a/deps/v8/src/jsregexp.h +++ b/deps/v8/src/jsregexp.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,14 +29,17 @@ #define V8_JSREGEXP_H_ #include "allocation.h" +#include "assembler.h" #include "zone-inl.h" namespace v8 { namespace internal { - +class NodeVisitor; +class RegExpCompiler; class RegExpMacroAssembler; - +class RegExpNode; +class RegExpTree; class RegExpImpl { public: @@ -388,7 +391,7 @@ class DispatchTable : public ZoneObject { typedef uc16 Key; typedef Entry Value; static const uc16 kNoKey; - static const Entry kNoValue; + static const Entry NoValue() { return Value(); } static inline int Compare(uc16 a, uc16 b) { if (a == b) return 0; @@ -633,7 +636,7 @@ class RegExpNode: public ZoneObject { static const int kNodeIsTooComplexForGreedyLoops = -1; virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; } Label* label() { return &label_; } - // If non-generic code is generated for a node (ie the node is not at the + // If non-generic code is generated for a node (i.e. the node is not at the // start of the trace) then it cannot be reused. This variable sets a limit // on how often we allow that to happen before we insist on starting a new // trace and generating generic code for a node that can be reused by flushing diff --git a/deps/v8/src/list-inl.h b/deps/v8/src/list-inl.h index 80bccc9bc3..7c2c83f0f7 100644 --- a/deps/v8/src/list-inl.h +++ b/deps/v8/src/list-inl.h @@ -72,9 +72,9 @@ void List<T, P>::ResizeAdd(const T& element) { template<typename T, class P> void List<T, P>::ResizeAddInternal(const T& element) { ASSERT(length_ >= capacity_); - // Grow the list capacity by 50%, but make sure to let it grow + // Grow the list capacity by 100%, but make sure to let it grow // even when the capacity is zero (possible initial case). - int new_capacity = 1 + capacity_ + (capacity_ >> 1); + int new_capacity = 1 + 2 * capacity_; // Since the element reference could be an element of the list, copy // it out of the old backing storage before resizing. T temp = element; @@ -216,11 +216,11 @@ int SortedListBSearch( int mid = (low + high) / 2; T mid_elem = list[mid]; - if (mid_elem > elem) { + if (cmp(&mid_elem, &elem) > 0) { high = mid - 1; continue; } - if (mid_elem < elem) { + if (cmp(&mid_elem, &elem) < 0) { low = mid + 1; continue; } @@ -236,6 +236,7 @@ int SortedListBSearch(const List<T>& list, T elem) { return SortedListBSearch<T>(list, elem, PointerValueCompare<T>); } + } } // namespace v8::internal #endif // V8_LIST_INL_H_ diff --git a/deps/v8/src/list.h b/deps/v8/src/list.h index 055870904e..adddea41f0 100644 --- a/deps/v8/src/list.h +++ b/deps/v8/src/list.h @@ -67,7 +67,7 @@ class List { // Returns a reference to the element at index i. This reference is // not safe to use after operations that can change the list's - // backing store (eg, Add). + // backing store (e.g. Add). inline T& operator[](int i) const { ASSERT(0 <= i); ASSERT(i < length_); @@ -165,8 +165,11 @@ class List { class Map; class Code; +template<typename T> class Handle; typedef List<Map*> MapList; typedef List<Code*> CodeList; +typedef List<Handle<Map> > MapHandleList; +typedef List<Handle<Code> > CodeHandleList; // Perform binary search for an element in an already sorted // list. Returns the index of the element of -1 if it was not found. @@ -176,6 +179,7 @@ int SortedListBSearch( template <typename T> int SortedListBSearch(const List<T>& list, T elem); + } } // namespace v8::internal diff --git a/deps/v8/src/lithium-allocator.cc b/deps/v8/src/lithium-allocator.cc index 466110678a..2cab9a7665 100644 --- a/deps/v8/src/lithium-allocator.cc +++ b/deps/v8/src/lithium-allocator.cc @@ -49,13 +49,13 @@ namespace internal { #define DEFINE_OPERAND_CACHE(name, type) \ name name::cache[name::kNumCachedOperands]; \ - void name::SetupCache() { \ + void name::SetUpCache() { \ for (int i = 0; i < kNumCachedOperands; i++) { \ cache[i].ConvertTo(type, i); \ } \ } \ static bool name##_initialize() { \ - name::SetupCache(); \ + name::SetUpCache(); \ return true; \ } \ static bool name##_cache_initialized = name##_initialize(); @@ -152,8 +152,8 @@ bool LiveRange::HasOverlap(UseInterval* target) const { LiveRange::LiveRange(int id) : id_(id), spilled_(false), + is_double_(false), assigned_register_(kInvalidAssignment), - assigned_register_kind_(NONE), last_interval_(NULL), first_interval_(NULL), first_pos_(NULL), @@ -161,15 +161,14 @@ LiveRange::LiveRange(int id) next_(NULL), current_interval_(NULL), last_processed_use_(NULL), - spill_start_index_(kMaxInt) { - spill_operand_ = new LUnallocated(LUnallocated::IGNORE); -} + spill_operand_(new LOperand()), + spill_start_index_(kMaxInt) { } void LiveRange::set_assigned_register(int reg, RegisterKind register_kind) { ASSERT(!HasRegisterAssigned() && !IsSpilled()); assigned_register_ = reg; - assigned_register_kind_ = register_kind; + is_double_ = (register_kind == DOUBLE_REGISTERS); ConvertOperands(); } @@ -184,14 +183,15 @@ void LiveRange::MakeSpilled() { bool LiveRange::HasAllocatedSpillOperand() const { - return spill_operand_ != NULL && !spill_operand_->IsUnallocated(); + ASSERT(spill_operand_ != NULL); + return !spill_operand_->IsIgnored(); } void LiveRange::SetSpillOperand(LOperand* operand) { ASSERT(!operand->IsUnallocated()); ASSERT(spill_operand_ != NULL); - ASSERT(spill_operand_->IsUnallocated()); + ASSERT(spill_operand_->IsIgnored()); spill_operand_->ConvertTo(operand->kind(), operand->index()); } @@ -234,7 +234,8 @@ bool LiveRange::CanBeSpilled(LifetimePosition pos) { // at the current or the immediate next position. UsePosition* use_pos = NextRegisterPosition(pos); if (use_pos == NULL) return true; - return use_pos->pos().Value() > pos.NextInstruction().Value(); + return + use_pos->pos().Value() > pos.NextInstruction().InstructionEnd().Value(); } @@ -545,6 +546,7 @@ LifetimePosition LiveRange::FirstIntersection(LiveRange* other) { LAllocator::LAllocator(int num_values, HGraph* graph) : chunk_(NULL), + allocation_ok_(true), live_in_sets_(graph->blocks()->length()), live_ranges_(num_values * 2), fixed_live_ranges_(NULL), @@ -555,7 +557,7 @@ LAllocator::LAllocator(int num_values, HGraph* graph) reusable_slots_(8), next_virtual_register_(num_values), first_artificial_register_(num_values), - mode_(NONE), + mode_(GENERAL_REGISTERS), num_registers_(-1), graph_(graph), has_osr_entry_(false) {} @@ -696,7 +698,7 @@ LGap* LAllocator::GetLastGap(HBasicBlock* block) { HPhi* LAllocator::LookupPhi(LOperand* operand) const { if (!operand->IsUnallocated()) return NULL; - int index = operand->VirtualRegister(); + int index = LUnallocated::cast(operand)->virtual_register(); HValue* instr = graph_->LookupValue(index); if (instr != NULL && instr->IsPhi()) { return HPhi::cast(instr); @@ -764,7 +766,8 @@ void LAllocator::AddConstraintsGapMove(int index, LMoveOperands cur = move_operands->at(i); LOperand* cur_to = cur.destination(); if (cur_to->IsUnallocated()) { - if (cur_to->VirtualRegister() == from->VirtualRegister()) { + if (LUnallocated::cast(cur_to)->virtual_register() == + LUnallocated::cast(from)->virtual_register()) { move->AddMove(cur.source(), to); return; } @@ -785,6 +788,7 @@ void LAllocator::MeetRegisterConstraints(HBasicBlock* block) { if (i < end) instr = InstructionAt(i + 1); if (i > start) prev_instr = InstructionAt(i - 1); MeetConstraintsBetween(prev_instr, instr, i); + if (!AllocationOk()) return; } } } @@ -806,11 +810,11 @@ void LAllocator::MeetConstraintsBetween(LInstruction* first, // Handle fixed output operand. if (first != NULL && first->Output() != NULL) { LUnallocated* first_output = LUnallocated::cast(first->Output()); - LiveRange* range = LiveRangeFor(first_output->VirtualRegister()); + LiveRange* range = LiveRangeFor(first_output->virtual_register()); bool assigned = false; if (first_output->HasFixedPolicy()) { LUnallocated* output_copy = first_output->CopyUnconstrained(); - bool is_tagged = HasTaggedValue(first_output->VirtualRegister()); + bool is_tagged = HasTaggedValue(first_output->virtual_register()); AllocateFixed(first_output, gap_index, is_tagged); // This value is produced on the stack, we never need to spill it. @@ -841,7 +845,7 @@ void LAllocator::MeetConstraintsBetween(LInstruction* first, LUnallocated* cur_input = LUnallocated::cast(it.Current()); if (cur_input->HasFixedPolicy()) { LUnallocated* input_copy = cur_input->CopyUnconstrained(); - bool is_tagged = HasTaggedValue(cur_input->VirtualRegister()); + bool is_tagged = HasTaggedValue(cur_input->virtual_register()); AllocateFixed(cur_input, gap_index + 1, is_tagged); AddConstraintsGapMove(gap_index, input_copy, cur_input); } else if (cur_input->policy() == LUnallocated::WRITABLE_REGISTER) { @@ -850,7 +854,8 @@ void LAllocator::MeetConstraintsBetween(LInstruction* first, ASSERT(!cur_input->IsUsedAtStart()); LUnallocated* input_copy = cur_input->CopyUnconstrained(); - cur_input->set_virtual_register(next_virtual_register_++); + cur_input->set_virtual_register(GetVirtualRegister()); + if (!AllocationOk()) return; if (RequiredRegisterKind(input_copy->virtual_register()) == DOUBLE_REGISTERS) { @@ -868,8 +873,8 @@ void LAllocator::MeetConstraintsBetween(LInstruction* first, LUnallocated* second_output = LUnallocated::cast(second->Output()); if (second_output->HasSameAsInputPolicy()) { LUnallocated* cur_input = LUnallocated::cast(second->FirstInput()); - int output_vreg = second_output->VirtualRegister(); - int input_vreg = cur_input->VirtualRegister(); + int output_vreg = second_output->virtual_register(); + int input_vreg = cur_input->virtual_register(); LUnallocated* input_copy = cur_input->CopyUnconstrained(); cur_input->set_virtual_register(second_output->virtual_register()); @@ -924,9 +929,9 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) { } } else { if (to->IsUnallocated()) { - if (live->Contains(to->VirtualRegister())) { + if (live->Contains(LUnallocated::cast(to)->virtual_register())) { Define(curr_position, to, from); - live->Remove(to->VirtualRegister()); + live->Remove(LUnallocated::cast(to)->virtual_register()); } else { cur->Eliminate(); continue; @@ -937,7 +942,7 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) { } Use(block_start_position, curr_position, from, hint); if (from->IsUnallocated()) { - live->Add(from->VirtualRegister()); + live->Add(LUnallocated::cast(from)->virtual_register()); } } } else { @@ -947,7 +952,9 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) { if (instr != NULL) { LOperand* output = instr->Output(); if (output != NULL) { - if (output->IsUnallocated()) live->Remove(output->VirtualRegister()); + if (output->IsUnallocated()) { + live->Remove(LUnallocated::cast(output)->virtual_register()); + } Define(curr_position, output, NULL); } @@ -985,7 +992,9 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) { } Use(block_start_position, use_pos, input, NULL); - if (input->IsUnallocated()) live->Add(input->VirtualRegister()); + if (input->IsUnallocated()) { + live->Add(LUnallocated::cast(input)->virtual_register()); + } } for (TempIterator it(instr); !it.Done(); it.Advance()) { @@ -1043,11 +1052,13 @@ void LAllocator::ResolvePhis(HBasicBlock* block) { // it into a location different from the operand of a live range // covering a branch instruction. // Thus we need to manually record a pointer. - if (phi->representation().IsTagged()) { - LInstruction* branch = - InstructionAt(cur_block->last_instruction_index()); - if (branch->HasPointerMap()) { + LInstruction* branch = + InstructionAt(cur_block->last_instruction_index()); + if (branch->HasPointerMap()) { + if (phi->representation().IsTagged()) { branch->pointer_map()->RecordPointer(phi_operand); + } else if (!phi->representation().IsDouble()) { + branch->pointer_map()->RecordUntagged(phi_operand); } } } @@ -1061,18 +1072,22 @@ void LAllocator::ResolvePhis(HBasicBlock* block) { } -void LAllocator::Allocate(LChunk* chunk) { +bool LAllocator::Allocate(LChunk* chunk) { ASSERT(chunk_ == NULL); chunk_ = chunk; MeetRegisterConstraints(); + if (!AllocationOk()) return false; ResolvePhis(); BuildLiveRanges(); AllocateGeneralRegisters(); + if (!AllocationOk()) return false; AllocateDoubleRegisters(); + if (!AllocationOk()) return false; PopulatePointerMaps(); if (has_osr_entry_) ProcessOsrEntry(); ConnectRanges(); ResolveControlFlow(); + return true; } @@ -1083,6 +1098,7 @@ void LAllocator::MeetRegisterConstraints() { for (int i = 0; i < blocks->length(); ++i) { HBasicBlock* block = blocks->at(i); MeetRegisterConstraints(block); + if (!AllocationOk()) return; } } @@ -1142,10 +1158,13 @@ void LAllocator::ResolveControlFlow(LiveRange* range, // it into a location different from the operand of a live range // covering a branch instruction. // Thus we need to manually record a pointer. - if (HasTaggedValue(range->id())) { - LInstruction* branch = InstructionAt(pred->last_instruction_index()); - if (branch->HasPointerMap()) { + LInstruction* branch = InstructionAt(pred->last_instruction_index()); + if (branch->HasPointerMap()) { + if (HasTaggedValue(range->id())) { branch->pointer_map()->RecordPointer(cur_op); + } else if (!cur_op->IsDoubleStackSlot() && + !cur_op->IsDoubleRegister()) { + branch->pointer_map()->RemovePointer(cur_op); } } } @@ -1264,7 +1283,8 @@ void LAllocator::BuildLiveRanges() { LParallelMove* move = gap->GetOrCreateParallelMove(LGap::START); for (int j = 0; j < move->move_operands()->length(); ++j) { LOperand* to = move->move_operands()->at(j).destination(); - if (to->IsUnallocated() && to->VirtualRegister() == phi->id()) { + if (to->IsUnallocated() && + LUnallocated::cast(to)->virtual_register() == phi->id()) { hint = move->move_operands()->at(j).source(); phi_operand = to; break; @@ -1461,7 +1481,6 @@ void LAllocator::ProcessOsrEntry() { void LAllocator::AllocateGeneralRegisters() { HPhase phase("Allocate general registers", this); num_registers_ = Register::kNumAllocatableRegisters; - mode_ = GENERAL_REGISTERS; AllocateRegisters(); } @@ -1475,7 +1494,6 @@ void LAllocator::AllocateDoubleRegisters() { void LAllocator::AllocateRegisters() { - ASSERT(mode_ != NONE); ASSERT(unhandled_live_ranges_.is_empty()); for (int i = 0; i < live_ranges_.length(); ++i) { @@ -1534,6 +1552,7 @@ void LAllocator::AllocateRegisters() { // Do not spill live range eagerly if use position that can benefit from // the register is too close to the start of live range. SpillBetween(current, current->Start(), pos->pos()); + if (!AllocationOk()) return; ASSERT(UnhandledIsSorted()); continue; } @@ -1564,9 +1583,10 @@ void LAllocator::AllocateRegisters() { ASSERT(!current->HasRegisterAssigned() && !current->IsSpilled()); bool result = TryAllocateFreeReg(current); - if (!result) { - AllocateBlockedReg(current); - } + if (!AllocationOk()) return; + + if (!result) AllocateBlockedReg(current); + if (!AllocationOk()) return; if (current->HasRegisterAssigned()) { AddToActive(current); @@ -1580,7 +1600,6 @@ void LAllocator::AllocateRegisters() { const char* LAllocator::RegisterName(int allocation_index) { - ASSERT(mode_ != NONE); if (mode_ == GENERAL_REGISTERS) { return Register::AllocationIndexToString(allocation_index); } else { @@ -1621,29 +1640,6 @@ RegisterKind LAllocator::RequiredRegisterKind(int virtual_register) const { } -void LAllocator::RecordDefinition(HInstruction* instr, LUnallocated* operand) { - operand->set_virtual_register(instr->id()); -} - - -void LAllocator::RecordTemporary(LUnallocated* operand) { - ASSERT(next_virtual_register_ < LUnallocated::kMaxVirtualRegisters); - if (!operand->HasFixedPolicy()) { - operand->set_virtual_register(next_virtual_register_++); - } -} - - -void LAllocator::RecordUse(HValue* value, LUnallocated* operand) { - operand->set_virtual_register(value->id()); -} - - -int LAllocator::max_initial_value_ids() { - return LUnallocated::kMaxVirtualRegisters / 32; -} - - void LAllocator::AddToActive(LiveRange* range) { TraceAlloc("Add live range %d to active\n", range->id()); active_live_ranges_.Add(range); @@ -1838,7 +1834,8 @@ bool LAllocator::TryAllocateFreeReg(LiveRange* current) { if (pos.Value() < current->End().Value()) { // Register reg is available at the range start but becomes blocked before // the range end. Split current at position where it becomes blocked. - LiveRange* tail = SplitAt(current, pos); + LiveRange* tail = SplitRangeAt(current, pos); + if (!AllocationOk()) return false; AddToUnhandledSorted(tail); } @@ -1993,7 +1990,7 @@ bool LAllocator::IsBlockBoundary(LifetimePosition pos) { } -LiveRange* LAllocator::SplitAt(LiveRange* range, LifetimePosition pos) { +LiveRange* LAllocator::SplitRangeAt(LiveRange* range, LifetimePosition pos) { ASSERT(!range->IsFixed()); TraceAlloc("Splitting live range %d at %d\n", range->id(), pos.Value()); @@ -2004,7 +2001,8 @@ LiveRange* LAllocator::SplitAt(LiveRange* range, LifetimePosition pos) { ASSERT(pos.IsInstructionStart() || !chunk_->instructions()->at(pos.InstructionIndex())->IsControl()); - LiveRange* result = LiveRangeFor(next_virtual_register_++); + LiveRange* result = LiveRangeFor(GetVirtualRegister()); + if (!AllocationOk()) return NULL; range->SplitAt(pos, result); return result; } @@ -2021,7 +2019,7 @@ LiveRange* LAllocator::SplitBetween(LiveRange* range, LifetimePosition split_pos = FindOptimalSplitPos(start, end); ASSERT(split_pos.Value() >= start.Value()); - return SplitAt(range, split_pos); + return SplitRangeAt(range, split_pos); } @@ -2060,7 +2058,8 @@ LifetimePosition LAllocator::FindOptimalSplitPos(LifetimePosition start, void LAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { - LiveRange* second_part = SplitAt(range, pos); + LiveRange* second_part = SplitRangeAt(range, pos); + if (!AllocationOk()) return; Spill(second_part); } @@ -2069,7 +2068,8 @@ void LAllocator::SpillBetween(LiveRange* range, LifetimePosition start, LifetimePosition end) { ASSERT(start.Value() < end.Value()); - LiveRange* second_part = SplitAt(range, start); + LiveRange* second_part = SplitRangeAt(range, start); + if (!AllocationOk()) return; if (second_part->Start().Value() < end.Value()) { // The split result intersects with [start, end[. diff --git a/deps/v8/src/lithium-allocator.h b/deps/v8/src/lithium-allocator.h index e4e64974b5..43a48cc61b 100644 --- a/deps/v8/src/lithium-allocator.h +++ b/deps/v8/src/lithium-allocator.h @@ -146,7 +146,6 @@ class LifetimePosition { enum RegisterKind { - NONE, GENERAL_REGISTERS, DOUBLE_REGISTERS }; @@ -319,7 +318,7 @@ class LiveRange: public ZoneObject { // live range to the result live range. void SplitAt(LifetimePosition position, LiveRange* result); - bool IsDouble() const { return assigned_register_kind_ == DOUBLE_REGISTERS; } + bool IsDouble() const { return is_double_; } bool HasRegisterAssigned() const { return assigned_register_ != kInvalidAssignment; } @@ -377,8 +376,8 @@ class LiveRange: public ZoneObject { int id_; bool spilled_; + bool is_double_; int assigned_register_; - RegisterKind assigned_register_kind_; UseInterval* last_interval_; UseInterval* first_interval_; UsePosition* first_pos_; @@ -432,24 +431,13 @@ class LAllocator BASE_EMBEDDED { static void TraceAlloc(const char* msg, ...); - // Lithium translation support. - // Record a use of an input operand in the current instruction. - void RecordUse(HValue* value, LUnallocated* operand); - // Record the definition of the output operand. - void RecordDefinition(HInstruction* instr, LUnallocated* operand); - // Record a temporary operand. - void RecordTemporary(LUnallocated* operand); - // Checks whether the value of a given virtual register is tagged. bool HasTaggedValue(int virtual_register) const; // Returns the register kind required by the given virtual register. RegisterKind RequiredRegisterKind(int virtual_register) const; - // Control max function size. - static int max_initial_value_ids(); - - void Allocate(LChunk* chunk); + bool Allocate(LChunk* chunk); const ZoneList<LiveRange*>* live_ranges() const { return &live_ranges_; } const Vector<LiveRange*>* fixed_live_ranges() const { @@ -462,6 +450,15 @@ class LAllocator BASE_EMBEDDED { LChunk* chunk() const { return chunk_; } HGraph* graph() const { return graph_; } + int GetVirtualRegister() { + if (next_virtual_register_ > LUnallocated::kMaxVirtualRegisters) { + allocation_ok_ = false; + } + return next_virtual_register_++; + } + + bool AllocationOk() { return allocation_ok_; } + void MarkAsOsrEntry() { // There can be only one. ASSERT(!has_osr_entry_); @@ -534,7 +531,7 @@ class LAllocator BASE_EMBEDDED { // Otherwise returns the live range that starts at pos and contains // all uses from the original range that follow pos. Uses at pos will // still be owned by the original range after splitting. - LiveRange* SplitAt(LiveRange* range, LifetimePosition pos); + LiveRange* SplitRangeAt(LiveRange* range, LifetimePosition pos); // Split the given range in a position from the interval [start, end]. LiveRange* SplitBetween(LiveRange* range, @@ -592,6 +589,9 @@ class LAllocator BASE_EMBEDDED { LChunk* chunk_; + // Indicates success or failure during register allocation. + bool allocation_ok_; + // During liveness analysis keep a mapping from block id to live_in sets // for blocks already analyzed. ZoneList<BitVector*> live_in_sets_; diff --git a/deps/v8/src/lithium.cc b/deps/v8/src/lithium.cc index 5410f6f058..5a44fcec99 100644 --- a/deps/v8/src/lithium.cc +++ b/deps/v8/src/lithium.cc @@ -36,6 +36,7 @@ void LOperand::PrintTo(StringStream* stream) { LUnallocated* unalloc = NULL; switch (kind()) { case INVALID: + stream->Add("(0)"); break; case UNALLOCATED: unalloc = LUnallocated::cast(this); @@ -70,9 +71,6 @@ void LOperand::PrintTo(StringStream* stream) { case LUnallocated::ANY: stream->Add("(-)"); break; - case LUnallocated::IGNORE: - stream->Add("(0)"); - break; } break; case CONSTANT_OPERAND: @@ -97,12 +95,6 @@ void LOperand::PrintTo(StringStream* stream) { } -int LOperand::VirtualRegister() { - LUnallocated* unalloc = LUnallocated::cast(this); - return unalloc->virtual_register(); -} - - bool LParallelMove::IsRedundant() const { for (int i = 0; i < move_operands_.length(); ++i) { if (!move_operands_[i].IsRedundant()) return false; @@ -156,6 +148,27 @@ void LPointerMap::RecordPointer(LOperand* op) { } +void LPointerMap::RemovePointer(LOperand* op) { + // Do not record arguments as pointers. + if (op->IsStackSlot() && op->index() < 0) return; + ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); + for (int i = 0; i < pointer_operands_.length(); ++i) { + if (pointer_operands_[i]->Equals(op)) { + pointer_operands_.Remove(i); + --i; + } + } +} + + +void LPointerMap::RecordUntagged(LOperand* op) { + // Do not record arguments as pointers. + if (op->IsStackSlot() && op->index() < 0) return; + ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); + untagged_operands_.Add(op); +} + + void LPointerMap::PrintTo(StringStream* stream) { stream->Add("{"); for (int i = 0; i < pointer_operands_.length(); ++i) { @@ -182,6 +195,7 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) { case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: return 3; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: diff --git a/deps/v8/src/lithium.h b/deps/v8/src/lithium.h index a933f72aef..4987b5a4cd 100644 --- a/deps/v8/src/lithium.h +++ b/deps/v8/src/lithium.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -59,8 +59,8 @@ class LOperand: public ZoneObject { bool IsDoubleRegister() const { return kind() == DOUBLE_REGISTER; } bool IsArgument() const { return kind() == ARGUMENT; } bool IsUnallocated() const { return kind() == UNALLOCATED; } + bool IsIgnored() const { return kind() == INVALID; } bool Equals(LOperand* other) const { return value_ == other->value_; } - int VirtualRegister(); void PrintTo(StringStream* stream); void ConvertTo(Kind kind, int index) { @@ -89,8 +89,7 @@ class LUnallocated: public LOperand { FIXED_SLOT, MUST_HAVE_REGISTER, WRITABLE_REGISTER, - SAME_AS_FIRST_INPUT, - IGNORE + SAME_AS_FIRST_INPUT }; // Lifetime of operand inside the instruction. @@ -121,9 +120,9 @@ class LUnallocated: public LOperand { // The superclass has a KindField. Some policies have a signed fixed // index in the upper bits. - static const int kPolicyWidth = 4; + static const int kPolicyWidth = 3; static const int kLifetimeWidth = 1; - static const int kVirtualRegisterWidth = 17; + static const int kVirtualRegisterWidth = 18; static const int kPolicyShift = kKindFieldWidth; static const int kLifetimeShift = kPolicyShift + kPolicyWidth; @@ -143,12 +142,10 @@ class LUnallocated: public LOperand { kVirtualRegisterWidth> { }; - static const int kMaxVirtualRegisters = 1 << (kVirtualRegisterWidth + 1); + static const int kMaxVirtualRegisters = 1 << kVirtualRegisterWidth; static const int kMaxFixedIndex = 63; static const int kMinFixedIndex = -64; - bool HasIgnorePolicy() const { return policy() == IGNORE; } - bool HasNoPolicy() const { return policy() == NONE; } bool HasAnyPolicy() const { return policy() == ANY; } @@ -171,7 +168,7 @@ class LUnallocated: public LOperand { return static_cast<int>(value_) >> kFixedIndexShift; } - unsigned virtual_register() const { + int virtual_register() const { return VirtualRegisterField::decode(value_); } @@ -234,9 +231,7 @@ class LMoveOperands BASE_EMBEDDED { } bool IsIgnored() const { - return destination_ != NULL && - destination_->IsUnallocated() && - LUnallocated::cast(destination_)->HasIgnorePolicy(); + return destination_ != NULL && destination_->IsIgnored(); } // We clear both operands to indicate move that's been eliminated. @@ -265,7 +260,7 @@ class LConstantOperand: public LOperand { return reinterpret_cast<LConstantOperand*>(op); } - static void SetupCache(); + static void SetUpCache(); private: static const int kNumCachedOperands = 128; @@ -300,7 +295,7 @@ class LStackSlot: public LOperand { return reinterpret_cast<LStackSlot*>(op); } - static void SetupCache(); + static void SetUpCache(); private: static const int kNumCachedOperands = 128; @@ -324,7 +319,7 @@ class LDoubleStackSlot: public LOperand { return reinterpret_cast<LDoubleStackSlot*>(op); } - static void SetupCache(); + static void SetUpCache(); private: static const int kNumCachedOperands = 128; @@ -348,7 +343,7 @@ class LRegister: public LOperand { return reinterpret_cast<LRegister*>(op); } - static void SetupCache(); + static void SetUpCache(); private: static const int kNumCachedOperands = 16; @@ -372,7 +367,7 @@ class LDoubleRegister: public LOperand { return reinterpret_cast<LDoubleRegister*>(op); } - static void SetupCache(); + static void SetUpCache(); private: static const int kNumCachedOperands = 16; @@ -407,9 +402,18 @@ class LParallelMove : public ZoneObject { class LPointerMap: public ZoneObject { public: explicit LPointerMap(int position) - : pointer_operands_(8), position_(position), lithium_position_(-1) { } - - const ZoneList<LOperand*>* operands() const { return &pointer_operands_; } + : pointer_operands_(8), + untagged_operands_(0), + position_(position), + lithium_position_(-1) { } + + const ZoneList<LOperand*>* GetNormalizedOperands() { + for (int i = 0; i < untagged_operands_.length(); ++i) { + RemovePointer(untagged_operands_[i]); + } + untagged_operands_.Clear(); + return &pointer_operands_; + } int position() const { return position_; } int lithium_position() const { return lithium_position_; } @@ -419,10 +423,13 @@ class LPointerMap: public ZoneObject { } void RecordPointer(LOperand* op); + void RemovePointer(LOperand* op); + void RecordUntagged(LOperand* op); void PrintTo(StringStream* stream); private: ZoneList<LOperand*> pointer_operands_; + ZoneList<LOperand*> untagged_operands_; int position_; int lithium_position_; }; @@ -431,12 +438,14 @@ class LPointerMap: public ZoneObject { class LEnvironment: public ZoneObject { public: LEnvironment(Handle<JSFunction> closure, + bool is_arguments_adaptor, int ast_id, int parameter_count, int argument_count, int value_count, LEnvironment* outer) : closure_(closure), + is_arguments_adaptor_(is_arguments_adaptor), arguments_stack_height_(argument_count), deoptimization_index_(Safepoint::kNoDeoptimizationIndex), translation_index_(-1), @@ -444,7 +453,7 @@ class LEnvironment: public ZoneObject { parameter_count_(parameter_count), pc_offset_(-1), values_(value_count), - representations_(value_count), + is_tagged_(value_count), spilled_registers_(NULL), spilled_double_registers_(NULL), outer_(outer) { @@ -466,11 +475,13 @@ class LEnvironment: public ZoneObject { void AddValue(LOperand* operand, Representation representation) { values_.Add(operand); - representations_.Add(representation); + if (representation.IsTagged()) { + is_tagged_.Add(values_.length() - 1); + } } bool HasTaggedValueAt(int index) const { - return representations_[index].IsTagged(); + return is_tagged_.Contains(index); } void Register(int deoptimization_index, @@ -493,8 +504,11 @@ class LEnvironment: public ZoneObject { void PrintTo(StringStream* stream); + bool is_arguments_adaptor() const { return is_arguments_adaptor_; } + private: Handle<JSFunction> closure_; + bool is_arguments_adaptor_; int arguments_stack_height_; int deoptimization_index_; int translation_index_; @@ -502,7 +516,7 @@ class LEnvironment: public ZoneObject { int parameter_count_; int pc_offset_; ZoneList<LOperand*> values_; - ZoneList<Representation> representations_; + BitVector is_tagged_; // Allocation index indexed arrays of spill slot operands for registers // that are also in spill slots at an OSR entry. NULL for environments @@ -511,8 +525,6 @@ class LEnvironment: public ZoneObject { LOperand** spilled_double_registers_; LEnvironment* outer_; - - friend class LCodegen; }; diff --git a/deps/v8/src/liveedit-debugger.js b/deps/v8/src/liveedit-debugger.js index e05c53ce18..abfb0f69c6 100644 --- a/deps/v8/src/liveedit-debugger.js +++ b/deps/v8/src/liveedit-debugger.js @@ -325,9 +325,10 @@ Debug.LiveEdit = new function() { if (old_node.children[i].live_shared_function_infos) { old_node.children[i].live_shared_function_infos. forEach(function (old_child_info) { - %LiveEditReplaceRefToNestedFunction(old_info.info, - corresponding_child_info, - old_child_info.info); + %LiveEditReplaceRefToNestedFunction( + old_info.info, + corresponding_child_info, + old_child_info.info); }); } } @@ -381,7 +382,7 @@ Debug.LiveEdit = new function() { position: break_point_position, line: break_point.line(), column: break_point.column() - } + }; break_point_old_positions.push(old_position_description); } @@ -418,7 +419,7 @@ Debug.LiveEdit = new function() { position: updated_position, line: new_location.line, column: new_location.column - } + }; break_point.set(original_script); @@ -428,7 +429,7 @@ Debug.LiveEdit = new function() { new_positions: new_position_description } ); } - } + }; } @@ -465,7 +466,7 @@ Debug.LiveEdit = new function() { } PosTranslator.prototype.GetChunks = function() { return this.chunks; - } + }; PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) { var array = this.chunks; @@ -492,18 +493,18 @@ Debug.LiveEdit = new function() { inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler; } return inside_chunk_handler(pos, chunk); - } + }; PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) { Assert(false, "Cannot translate position in changed area"); - } + }; PosTranslator.ShiftWithTopInsideChunkHandler = function(pos, diff_chunk) { // We carelessly do not check whether we stay inside the chunk after // translation. return pos - diff_chunk.pos1 + diff_chunk.pos2; - } + }; var FunctionStatus = { // No change to function or its inner functions; however its positions @@ -517,7 +518,7 @@ Debug.LiveEdit = new function() { CHANGED: "changed", // Function is changed but cannot be patched. DAMAGED: "damaged" - } + }; function CodeInfoTreeNode(code_info, children, array_index) { this.info = code_info; @@ -580,19 +581,19 @@ Debug.LiveEdit = new function() { // children of unchanged functions are ignored. function MarkChangedFunctions(code_info_tree, chunks) { - // A convenient interator over diff chunks that also translates + // A convenient iterator over diff chunks that also translates // positions from old to new in a current non-changed part of script. var chunk_it = new function() { var chunk_index = 0; var pos_diff = 0; - this.current = function() { return chunks[chunk_index]; } + this.current = function() { return chunks[chunk_index]; }; this.next = function() { var chunk = chunks[chunk_index]; pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1); chunk_index++; - } - this.done = function() { return chunk_index >= chunks.length; } - this.TranslatePos = function(pos) { return pos + pos_diff; } + }; + this.done = function() { return chunk_index >= chunks.length; }; + this.TranslatePos = function(pos) { return pos + pos_diff; }; }; // A recursive function that processes internals of a function and all its @@ -946,16 +947,16 @@ Debug.LiveEdit = new function() { BLOCKED_ON_OTHER_STACK: 3, BLOCKED_UNDER_NATIVE_CODE: 4, REPLACED_ON_ACTIVE_STACK: 5 - } + }; FunctionPatchabilityStatus.SymbolName = function(code) { - var enum = FunctionPatchabilityStatus; - for (name in enum) { - if (enum[name] == code) { + var enumeration = FunctionPatchabilityStatus; + for (name in enumeration) { + if (enumeration[name] == code) { return name; } } - } + }; // A logical failure in liveedit process. This means that change_log @@ -968,7 +969,7 @@ Debug.LiveEdit = new function() { Failure.prototype.toString = function() { return "LiveEdit Failure: " + this.message; - } + }; // A testing entry. function GetPcFromSourcePos(func, source_pos) { @@ -1078,5 +1079,5 @@ Debug.LiveEdit = new function() { PosTranslator: PosTranslator, CompareStrings: CompareStrings, ApplySingleChunkPatch: ApplySingleChunkPatch - } -} + }; +}; diff --git a/deps/v8/src/liveedit.cc b/deps/v8/src/liveedit.cc index d44c2fc1cd..5ff8ff9d3b 100644 --- a/deps/v8/src/liveedit.cc +++ b/deps/v8/src/liveedit.cc @@ -54,7 +54,7 @@ void SetElementNonStrict(Handle<JSObject> object, // are element setters causing exceptions and the debugger context has none // of these. Handle<Object> no_failure; - no_failure = SetElement(object, index, value, kNonStrictMode); + no_failure = JSObject::SetElement(object, index, value, kNonStrictMode); ASSERT(!no_failure.is_null()); USE(no_failure); } @@ -602,7 +602,8 @@ static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) { // Build AST. CompilationInfo info(script); info.MarkAsGlobal(); - if (ParserApi::Parse(&info)) { + // Parse and don't allow skipping lazy functions. + if (ParserApi::Parse(&info, kNoParsingFlags)) { // Compile the code. LiveEditFunctionTracker tracker(info.isolate(), info.function()); if (Compiler::MakeCodeForLiveEdit(&info)) { @@ -797,7 +798,7 @@ class FunctionInfoListener { HandleScope scope; FunctionInfoWrapper info = FunctionInfoWrapper::Create(); info.SetInitialProperties(fun->name(), fun->start_position(), - fun->end_position(), fun->num_parameters(), + fun->end_position(), fun->parameter_count(), current_parent_index_); current_parent_index_ = len_; SetElementNonStrict(result_, len_, info.GetJSArray()); @@ -855,38 +856,20 @@ class FunctionInfoListener { return HEAP->undefined_value(); } do { - ZoneList<Variable*> list(10); - outer_scope->CollectUsedVariables(&list); - int j = 0; - for (int i = 0; i < list.length(); i++) { - Variable* var1 = list[i]; - if (var1->IsContextSlot()) { - if (j != i) { - list[j] = var1; - } - j++; - } - } + ZoneList<Variable*> stack_list(outer_scope->StackLocalCount()); + ZoneList<Variable*> context_list(outer_scope->ContextLocalCount()); + outer_scope->CollectStackAndContextLocals(&stack_list, &context_list); + context_list.Sort(&Variable::CompareIndex); - // Sort it. - for (int k = 1; k < j; k++) { - int l = k; - for (int m = k + 1; m < j; m++) { - if (list[l]->index() > list[m]->index()) { - l = m; - } - } - list[k] = list[l]; - } - for (int i = 0; i < j; i++) { + for (int i = 0; i < context_list.length(); i++) { SetElementNonStrict(scope_info_list, scope_info_length, - list[i]->name()); + context_list[i]->name()); scope_info_length++; SetElementNonStrict( scope_info_list, scope_info_length, - Handle<Smi>(Smi::FromInt(list[i]->index()))); + Handle<Smi>(Smi::FromInt(context_list[i]->index()))); scope_info_length++; } SetElementNonStrict(scope_info_list, @@ -1000,6 +983,7 @@ class ReferenceCollectorVisitor : public ObjectVisitor { static void ReplaceCodeObject(Code* original, Code* substitution) { ASSERT(!HEAP->InNewSpace(substitution)); + HeapIterator iterator; AssertNoAllocation no_allocations_please; // A zone scope for ReferenceCollectorVisitor. @@ -1016,7 +1000,6 @@ static void ReplaceCodeObject(Code* original, Code* substitution) { // Now iterate over all pointers of all objects, including code_target // implicit pointers. - HeapIterator iterator; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { obj->Iterate(&visitor); } @@ -1101,12 +1084,14 @@ MaybeObject* LiveEdit::ReplaceFunctionCode( Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo(); + HEAP->EnsureHeapIsIterable(); + if (IsJSFunctionCode(shared_info->code())) { Handle<Code> code = compile_info_wrapper.GetFunctionCode(); ReplaceCodeObject(shared_info->code(), *code); Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo(); if (code_scope_info->IsFixedArray()) { - shared_info->set_scope_info(SerializedScopeInfo::cast(*code_scope_info)); + shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info)); } } @@ -1243,7 +1228,7 @@ class RelocInfoBuffer { V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer"); } - // Setup new buffer. + // Set up new buffer. byte* new_buffer = NewArray<byte>(new_buffer_size); // Copy the data. @@ -1271,7 +1256,8 @@ class RelocInfoBuffer { // Patch positions in code (changes relocation info section) and possibly // returns new instance of code. -static Handle<Code> PatchPositionsInCode(Handle<Code> code, +static Handle<Code> PatchPositionsInCode( + Handle<Code> code, Handle<JSArray> position_change_array) { RelocInfoBuffer buffer_writer(code->relocation_size(), @@ -1286,7 +1272,7 @@ static Handle<Code> PatchPositionsInCode(Handle<Code> code, int new_position = TranslatePosition(position, position_change_array); if (position != new_position) { - RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position); + RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL); buffer_writer.Write(&info_copy); continue; } @@ -1333,6 +1319,8 @@ MaybeObject* LiveEdit::PatchFunctionPositions( info->set_end_position(new_function_end); info->set_function_token_position(new_function_token_pos); + HEAP->EnsureHeapIsIterable(); + if (IsJSFunctionCode(info->code())) { // Patch relocation info section of the code. Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()), diff --git a/deps/v8/src/liveobjectlist-inl.h b/deps/v8/src/liveobjectlist-inl.h index f742de3a03..2bc2296e29 100644 --- a/deps/v8/src/liveobjectlist-inl.h +++ b/deps/v8/src/liveobjectlist-inl.h @@ -59,7 +59,7 @@ void LiveObjectList::IterateElements(ObjectVisitor* v) { } -void LiveObjectList::ProcessNonLive(HeapObject *obj) { +void LiveObjectList::ProcessNonLive(HeapObject* obj) { // Only do work if we have at least one list to process. if (last()) DoProcessNonLive(obj); } @@ -93,7 +93,7 @@ LiveObjectList* LiveObjectList::FindLolForId(int id, template <typename T> inline LiveObjectList::Element* LiveObjectList::FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key) { - LiveObjectList *lol = last(); + LiveObjectList* lol = last(); while (lol != NULL) { Element* elements = lol->elements_; for (int i = 0; i < lol->obj_count_; i++) { diff --git a/deps/v8/src/liveobjectlist.cc b/deps/v8/src/liveobjectlist.cc index 957c0515d6..1aabc59814 100644 --- a/deps/v8/src/liveobjectlist.cc +++ b/deps/v8/src/liveobjectlist.cc @@ -165,7 +165,7 @@ const char* GetObjectTypeDesc(HeapObject* heap_obj) { } -bool IsOfType(LiveObjectType type, HeapObject *obj) { +bool IsOfType(LiveObjectType type, HeapObject* obj) { // Note: there are types that are more general (e.g. JSObject) that would // have passed the Is##type_() test for more specialized types (e.g. // JSFunction). If we find a more specialized match but we're looking for @@ -211,7 +211,7 @@ static AllocationSpace FindSpaceFor(String* space_str) { } -static bool InSpace(AllocationSpace space, HeapObject *heap_obj) { +static bool InSpace(AllocationSpace space, HeapObject* heap_obj) { Heap* heap = ISOLATE->heap(); if (space != LO_SPACE) { return heap->InSpace(heap_obj, space); @@ -462,7 +462,7 @@ static int CompactString(char* str) { char prev_ch = 0; while (*dst != '\0') { char ch = *src++; - // We will treat non-ascii chars as '?'. + // We will treat non-ASCII chars as '?'. if ((ch & 0x80) != 0) { ch = '?'; } @@ -498,7 +498,7 @@ static void GenerateObjectDesc(HeapObject* obj, length); } else if (obj->IsString()) { - String *str = String::cast(obj); + String* str = String::cast(obj); // Only grab up to 160 chars in case they are double byte. // We'll only dump 80 of them after we compact them. const int kMaxCharToDump = 80; @@ -842,7 +842,7 @@ class LiveObjectSummary { bool found_root_; bool found_weak_root_; - LolFilter *filter_; + LolFilter* filter_; }; @@ -857,8 +857,8 @@ class SummaryWriter { // A summary writer for filling in a summary of lol lists and diffs. class LolSummaryWriter: public SummaryWriter { public: - LolSummaryWriter(LiveObjectList *older_lol, - LiveObjectList *newer_lol) + LolSummaryWriter(LiveObjectList* older_lol, + LiveObjectList* newer_lol) : older_(older_lol), newer_(newer_lol) { } @@ -944,7 +944,7 @@ LiveObjectList::~LiveObjectList() { int LiveObjectList::GetTotalObjCountAndSize(int* size_p) { int size = 0; int count = 0; - LiveObjectList *lol = this; + LiveObjectList* lol = this; do { // Only compute total size if requested i.e. when size_p is not null. if (size_p != NULL) { @@ -1085,7 +1085,7 @@ void LiveObjectList::SortAll() { static int CountHeapObjects() { int count = 0; // Iterate over all the heap spaces and count the number of objects. - HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapIterator iterator; HeapObject* heap_obj = NULL; while ((heap_obj = iterator.next()) != NULL) { count++; @@ -1122,7 +1122,7 @@ MaybeObject* LiveObjectList::Capture() { // allocation, and we need allocate below. { // Iterate over all the heap spaces and add the objects. - HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapIterator iterator; HeapObject* heap_obj = NULL; bool failed = false; while (!failed && (heap_obj = iterator.next()) != NULL) { @@ -1183,7 +1183,7 @@ MaybeObject* LiveObjectList::Capture() { // only time we'll actually delete the lol is when we Reset() or if the lol is // invisible, and its element count reaches 0. bool LiveObjectList::Delete(int id) { - LiveObjectList *lol = last(); + LiveObjectList* lol = last(); while (lol != NULL) { if (lol->id() == id) { break; @@ -1246,8 +1246,8 @@ MaybeObject* LiveObjectList::Dump(int older_id, newer_id = temp; } - LiveObjectList *newer_lol = FindLolForId(newer_id, last()); - LiveObjectList *older_lol = FindLolForId(older_id, newer_lol); + LiveObjectList* newer_lol = FindLolForId(newer_id, last()); + LiveObjectList* older_lol = FindLolForId(older_id, newer_lol); // If the id is defined, and we can't find a LOL for it, then we have an // invalid id. @@ -1336,7 +1336,9 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, // Allocate the JSArray of the elements. Handle<JSObject> elements = factory->NewJSObject(isolate->array_function()); if (elements->IsFailure()) return Object::cast(*elements); - Handle<JSArray>::cast(elements)->SetContent(*elements_arr); + + maybe_result = Handle<JSArray>::cast(elements)->SetContent(*elements_arr); + if (maybe_result->IsFailure()) return maybe_result; // Set body.elements. Handle<String> elements_sym = factory->LookupAsciiSymbol("elements"); @@ -1363,8 +1365,8 @@ MaybeObject* LiveObjectList::Summarize(int older_id, newer_id = temp; } - LiveObjectList *newer_lol = FindLolForId(newer_id, last()); - LiveObjectList *older_lol = FindLolForId(older_id, newer_lol); + LiveObjectList* newer_lol = FindLolForId(newer_id, last()); + LiveObjectList* older_lol = FindLolForId(older_id, newer_lol); // If the id is defined, and we can't find a LOL for it, then we have an // invalid id. @@ -1462,7 +1464,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, Handle<JSObject> summary_obj = factory->NewJSObject(isolate->array_function()); if (summary_obj->IsFailure()) return Object::cast(*summary_obj); - Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr); + + maybe_result = Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr); + if (maybe_result->IsFailure()) return maybe_result; // Create the body object. Handle<JSObject> body = factory->NewJSObject(isolate->object_function()); @@ -1589,7 +1593,9 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { // Return the result as a JS array. Handle<JSObject> lols = factory->NewJSObject(isolate->array_function()); - Handle<JSArray>::cast(lols)->SetContent(*list); + + maybe_result = Handle<JSArray>::cast(lols)->SetContent(*list); + if (maybe_result->IsFailure()) return maybe_result; Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); if (result->IsFailure()) return Object::cast(*result); @@ -1620,7 +1626,7 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { // Deletes all captured lols. void LiveObjectList::Reset() { - LiveObjectList *lol = last(); + LiveObjectList* lol = last(); // Just delete the last. Each lol will delete it's prev automatically. delete lol; @@ -1709,8 +1715,8 @@ class LolVisitor: public ObjectVisitor { inline bool AddRootRetainerIfFound(const LolVisitor& visitor, LolFilter* filter, - LiveObjectSummary *summary, - void (*SetRootFound)(LiveObjectSummary *s), + LiveObjectSummary* summary, + void (*SetRootFound)(LiveObjectSummary* s), int start, int dump_limit, int* total_count, @@ -1756,12 +1762,12 @@ inline bool AddRootRetainerIfFound(const LolVisitor& visitor, } -inline void SetFoundRoot(LiveObjectSummary *summary) { +inline void SetFoundRoot(LiveObjectSummary* summary) { summary->set_found_root(); } -inline void SetFoundWeakRoot(LiveObjectSummary *summary) { +inline void SetFoundWeakRoot(LiveObjectSummary* summary) { summary->set_found_weak_root(); } @@ -1773,7 +1779,7 @@ int LiveObjectList::GetRetainers(Handle<HeapObject> target, int dump_limit, int* total_count, LolFilter* filter, - LiveObjectSummary *summary, + LiveObjectSummary* summary, JSFunction* arguments_function, Handle<Object> error) { HandleScope scope; @@ -2261,7 +2267,7 @@ Object* LiveObjectList::GetPath(int obj_id1, } -void LiveObjectList::DoProcessNonLive(HeapObject *obj) { +void LiveObjectList::DoProcessNonLive(HeapObject* obj) { // We should only be called if we have at least one lol to search. ASSERT(last() != NULL); Element* element = last()->Find(obj); @@ -2278,7 +2284,7 @@ void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) { int count = lol->obj_count_; for (int i = 0; i < count; i++) { HeapObject** p = &elements[i].obj_; - v->VisitPointer(reinterpret_cast<Object **>(p)); + v->VisitPointer(reinterpret_cast<Object** >(p)); } lol = lol->prev_; } @@ -2383,11 +2389,11 @@ void LiveObjectList::GCEpiloguePrivate() { PurgeDuplicates(); // After the GC, sweep away all free'd Elements and compact. - LiveObjectList *prev = NULL; - LiveObjectList *next = NULL; + LiveObjectList* prev = NULL; + LiveObjectList* next = NULL; // Iterating from the youngest lol to the oldest lol. - for (LiveObjectList *lol = last(); lol; lol = prev) { + for (LiveObjectList* lol = last(); lol; lol = prev) { Element* elements = lol->elements_; prev = lol->prev(); // Save the prev. @@ -2440,7 +2446,7 @@ void LiveObjectList::GCEpiloguePrivate() { const int kMaxUnusedSpace = 64; if (diff > kMaxUnusedSpace) { // Threshold for shrinking. // Shrink the list. - Element *new_elements = NewArray<Element>(new_count); + Element* new_elements = NewArray<Element>(new_count); memcpy(new_elements, elements, new_count * sizeof(Element)); DeleteArray<Element>(elements); @@ -2507,7 +2513,7 @@ void LiveObjectList::Verify(bool match_heap_exactly) { OS::Print(" Start verify ...\n"); OS::Print(" Verifying ..."); Flush(); - HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapIterator iterator; HeapObject* heap_obj = NULL; while ((heap_obj = iterator.next()) != NULL) { number_of_heap_objects++; @@ -2613,7 +2619,7 @@ void LiveObjectList::VerifyNotInFromSpace() { HeapObject* heap_obj = it.Obj(); if (heap->InFromSpace(heap_obj)) { OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n", - i++, heap_obj, heap->new_space()->FromSpaceLow()); + i++, heap_obj, Heap::new_space()->FromSpaceStart()); } } } diff --git a/deps/v8/src/liveobjectlist.h b/deps/v8/src/liveobjectlist.h index 65470d7ad9..1aa9196051 100644 --- a/deps/v8/src/liveobjectlist.h +++ b/deps/v8/src/liveobjectlist.h @@ -77,7 +77,7 @@ class LiveObjectList { inline static void GCEpilogue(); inline static void GCPrologue(); inline static void IterateElements(ObjectVisitor* v); - inline static void ProcessNonLive(HeapObject *obj); + inline static void ProcessNonLive(HeapObject* obj); inline static void UpdateReferencesForScavengeGC(); // Note: LOLs can be listed by calling Dump(0, <lol id>), and 2 LOLs can be @@ -125,7 +125,7 @@ class LiveObjectList { static void GCEpiloguePrivate(); static void IterateElementsPrivate(ObjectVisitor* v); - static void DoProcessNonLive(HeapObject *obj); + static void DoProcessNonLive(HeapObject* obj); static int CompareElement(const Element* a, const Element* b); @@ -138,7 +138,7 @@ class LiveObjectList { int dump_limit, int* total_count, LolFilter* filter, - LiveObjectSummary *summary, + LiveObjectSummary* summary, JSFunction* arguments_function, Handle<Object> error); @@ -151,7 +151,7 @@ class LiveObjectList { bool is_tracking_roots); static bool NeedLOLProcessing() { return (last() != NULL); } - static void NullifyNonLivePointer(HeapObject **p) { + static void NullifyNonLivePointer(HeapObject** p) { // Mask out the low bit that marks this as a heap object. We'll use this // cleared bit as an indicator that this pointer needs to be collected. // @@ -202,7 +202,7 @@ class LiveObjectList { int id_; int capacity_; int obj_count_; - Element *elements_; + Element* elements_; // Statics for managing all the lists. static uint32_t next_element_id_; diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index 3d66b5fb10..3979719071 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -1356,12 +1356,12 @@ class EnumerateOptimizedFunctionsVisitor: public OptimizedFunctionVisitor { static int EnumerateCompiledFunctions(Handle<SharedFunctionInfo>* sfis, Handle<Code>* code_objects) { + HeapIterator iterator; AssertNoAllocation no_alloc; int compiled_funcs_count = 0; // Iterate the heap to find shared function info objects and record // the unoptimized code for them. - HeapIterator iterator; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { if (!obj->IsSharedFunctionInfo()) continue; SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); @@ -1450,6 +1450,8 @@ void Logger::LogCodeInfo() { const char arch[] = "x64"; #elif V8_TARGET_ARCH_ARM const char arch[] = "arm"; +#elif V8_TARGET_ARCH_MIPS + const char arch[] = "mips"; #else const char arch[] = "unknown"; #endif @@ -1519,8 +1521,10 @@ void Logger::LowLevelLogWriteBytes(const char* bytes, int size) { void Logger::LogCodeObjects() { - AssertNoAllocation no_alloc; + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "Logger::LogCodeObjects"); HeapIterator iterator; + AssertNoAllocation no_alloc; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { if (obj->IsCode()) LogCodeObject(obj); } @@ -1573,6 +1577,8 @@ void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared, void Logger::LogCompiledFunctions() { + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "Logger::LogCompiledFunctions"); HandleScope scope; const int compiled_funcs_count = EnumerateCompiledFunctions(NULL, NULL); ScopedVector< Handle<SharedFunctionInfo> > sfis(compiled_funcs_count); @@ -1591,9 +1597,10 @@ void Logger::LogCompiledFunctions() { void Logger::LogAccessorCallbacks() { - AssertNoAllocation no_alloc; + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "Logger::LogAccessorCallbacks"); HeapIterator iterator; - i::Isolate* isolate = ISOLATE; + AssertNoAllocation no_alloc; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { if (!obj->IsAccessorInfo()) continue; AccessorInfo* ai = AccessorInfo::cast(obj); @@ -1601,17 +1608,17 @@ void Logger::LogAccessorCallbacks() { String* name = String::cast(ai->name()); Address getter_entry = v8::ToCData<Address>(ai->getter()); if (getter_entry != 0) { - PROFILE(isolate, GetterCallbackEvent(name, getter_entry)); + PROFILE(ISOLATE, GetterCallbackEvent(name, getter_entry)); } Address setter_entry = v8::ToCData<Address>(ai->setter()); if (setter_entry != 0) { - PROFILE(isolate, SetterCallbackEvent(name, setter_entry)); + PROFILE(ISOLATE, SetterCallbackEvent(name, setter_entry)); } } } -bool Logger::Setup() { +bool Logger::SetUp() { // Tests and EnsureInitialize() can call this twice in a row. It's harmless. if (is_initialized_) return true; is_initialized_ = true; @@ -1704,9 +1711,9 @@ FILE* Logger::TearDown() { void Logger::EnableSlidingStateWindow() { - // If the ticker is NULL, Logger::Setup has not been called yet. In + // If the ticker is NULL, Logger::SetUp has not been called yet. In // that case, we set the sliding_state_window flag so that the - // sliding window computation will be started when Logger::Setup is + // sliding window computation will be started when Logger::SetUp is // called. if (ticker_ == NULL) { FLAG_sliding_state_window = true; diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index 50358ce56f..86bcad69aa 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -29,6 +29,7 @@ #define V8_LOG_H_ #include "allocation.h" +#include "objects.h" #include "platform.h" #include "log-utils.h" @@ -149,14 +150,14 @@ class Logger { #undef DECLARE_ENUM // Acquires resources for logging if the right flags are set. - bool Setup(); + bool SetUp(); void EnsureTickerStarted(); void EnsureTickerStopped(); Sampler* sampler(); - // Frees resources acquired in Setup. + // Frees resources acquired in SetUp. // When a temporary file is used for the log, returns its stream descriptor, // leaving the file open. FILE* TearDown(); @@ -410,7 +411,7 @@ class Logger { NameMap* address_to_name_map_; // Guards against multiple calls to TearDown() that can happen in some tests. - // 'true' between Setup() and TearDown(). + // 'true' between SetUp() and TearDown(). bool is_initialized_; // Support for 'incremental addresses' in compressed logs: diff --git a/deps/v8/src/macro-assembler.h b/deps/v8/src/macro-assembler.h index 30838bd761..7d4bbbc0ca 100644 --- a/deps/v8/src/macro-assembler.h +++ b/deps/v8/src/macro-assembler.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -36,20 +36,6 @@ enum InvokeFlag { }; -enum CodeLocation { - IN_JAVASCRIPT, - IN_JS_ENTRY, - IN_C_ENTRY -}; - - -enum HandlerType { - TRY_CATCH_HANDLER, - TRY_FINALLY_HANDLER, - JS_ENTRY_HANDLER -}; - - // Types of uncatchable exceptions. enum UncatchableExceptionType { OUT_OF_MEMORY, @@ -93,6 +79,63 @@ const int kInvalidProtoDepth = -1; namespace v8 { namespace internal { +class FrameScope { + public: + explicit FrameScope(MacroAssembler* masm, StackFrame::Type type) + : masm_(masm), type_(type), old_has_frame_(masm->has_frame()) { + masm->set_has_frame(true); + if (type != StackFrame::MANUAL && type_ != StackFrame::NONE) { + masm->EnterFrame(type); + } + } + + ~FrameScope() { + if (type_ != StackFrame::MANUAL && type_ != StackFrame::NONE) { + masm_->LeaveFrame(type_); + } + masm_->set_has_frame(old_has_frame_); + } + + // Normally we generate the leave-frame code when this object goes + // out of scope. Sometimes we may need to generate the code somewhere else + // in addition. Calling this will achieve that, but the object stays in + // scope, the MacroAssembler is still marked as being in a frame scope, and + // the code will be generated again when it goes out of scope. + void GenerateLeaveFrame() { + masm_->LeaveFrame(type_); + } + + private: + MacroAssembler* masm_; + StackFrame::Type type_; + bool old_has_frame_; +}; + + +class AllowExternalCallThatCantCauseGC: public FrameScope { + public: + explicit AllowExternalCallThatCantCauseGC(MacroAssembler* masm) + : FrameScope(masm, StackFrame::NONE) { } +}; + + +class NoCurrentFrameScope { + public: + explicit NoCurrentFrameScope(MacroAssembler* masm) + : masm_(masm), saved_(masm->has_frame()) { + masm->set_has_frame(false); + } + + ~NoCurrentFrameScope() { + masm_->set_has_frame(saved_); + } + + private: + MacroAssembler* masm_; + bool saved_; +}; + + // Support for "structured" code comments. #ifdef DEBUG diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py index 7a493ca70f..8e9c62dbe5 100644 --- a/deps/v8/src/macros.py +++ b/deps/v8/src/macros.py @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Dictionary that is passed as defines for js2c.py. -# Used for defines that must be defined for all native js files. +# Used for defines that must be defined for all native JS files. const NONE = 0; const READ_ONLY = 1; @@ -82,8 +82,6 @@ const kMinYear = -1000000; const kMaxYear = 1000000; const kMinMonth = -10000000; const kMaxMonth = 10000000; -const kMinDate = -100000000; -const kMaxDate = 100000000; # Native cache ids. const STRING_TO_REGEXP_CACHE_ID = 0; @@ -103,6 +101,9 @@ macro IS_OBJECT(arg) = (%_IsObject(arg)); macro IS_ARRAY(arg) = (%_IsArray(arg)); macro IS_FUNCTION(arg) = (%_IsFunction(arg)); macro IS_REGEXP(arg) = (%_IsRegExp(arg)); +macro IS_SET(arg) = (%_ClassOf(arg) === 'Set'); +macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map'); +macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap'); macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date'); macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); @@ -128,6 +129,11 @@ macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg)); # we cannot handle those anyway. macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function'); +# Indices in bound function info retrieved by %BoundFunctionGetBindings(...). +const kBoundFunctionIndex = 0; +const kBoundThisIndex = 1; +const kBoundArgumentsStartIndex = 2; + # Inline macros. Use %IS_VAR to make sure arg is evaluated only once. macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg)); macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0))); diff --git a/deps/v8/src/mark-compact-inl.h b/deps/v8/src/mark-compact-inl.h new file mode 100644 index 0000000000..fd25a403ca --- /dev/null +++ b/deps/v8/src/mark-compact-inl.h @@ -0,0 +1,119 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_MARK_COMPACT_INL_H_ +#define V8_MARK_COMPACT_INL_H_ + +#include "isolate.h" +#include "memory.h" +#include "mark-compact.h" + + +namespace v8 { +namespace internal { + + +MarkBit Marking::MarkBitFrom(Address addr) { + MemoryChunk* p = MemoryChunk::FromAddress(addr); + return p->markbits()->MarkBitFromIndex(p->AddressToMarkbitIndex(addr), + p->ContainsOnlyData()); +} + + +void MarkCompactCollector::SetFlags(int flags) { + sweep_precisely_ = ((flags & Heap::kMakeHeapIterableMask) != 0); + reduce_memory_footprint_ = ((flags & Heap::kReduceMemoryFootprintMask) != 0); +} + + +void MarkCompactCollector::ClearCacheOnMap(Map* map) { + if (FLAG_cleanup_code_caches_at_gc) { + map->ClearCodeCache(heap()); + } +} + + +void MarkCompactCollector::MarkObject(HeapObject* obj, MarkBit mark_bit) { + ASSERT(Marking::MarkBitFrom(obj) == mark_bit); + if (!mark_bit.Get()) { + mark_bit.Set(); + MemoryChunk::IncrementLiveBytesFromGC(obj->address(), obj->Size()); + ProcessNewlyMarkedObject(obj); + } +} + + +bool MarkCompactCollector::MarkObjectWithoutPush(HeapObject* object) { + MarkBit mark = Marking::MarkBitFrom(object); + bool old_mark = mark.Get(); + if (!old_mark) SetMark(object, mark); + return old_mark; +} + + +void MarkCompactCollector::MarkObjectAndPush(HeapObject* object) { + if (!MarkObjectWithoutPush(object)) marking_deque_.PushBlack(object); +} + + +void MarkCompactCollector::SetMark(HeapObject* obj, MarkBit mark_bit) { + ASSERT(!mark_bit.Get()); + ASSERT(Marking::MarkBitFrom(obj) == mark_bit); + mark_bit.Set(); + MemoryChunk::IncrementLiveBytesFromGC(obj->address(), obj->Size()); + if (obj->IsMap()) { + ClearCacheOnMap(Map::cast(obj)); + } +} + + +bool MarkCompactCollector::IsMarked(Object* obj) { + ASSERT(obj->IsHeapObject()); + HeapObject* heap_object = HeapObject::cast(obj); + return Marking::MarkBitFrom(heap_object).Get(); +} + + +void MarkCompactCollector::RecordSlot(Object** anchor_slot, + Object** slot, + Object* object) { + Page* object_page = Page::FromAddress(reinterpret_cast<Address>(object)); + if (object_page->IsEvacuationCandidate() && + !ShouldSkipEvacuationSlotRecording(anchor_slot)) { + if (!SlotsBuffer::AddTo(&slots_buffer_allocator_, + object_page->slots_buffer_address(), + slot, + SlotsBuffer::FAIL_ON_OVERFLOW)) { + EvictEvacuationCandidate(object_page); + } + } +} + + +} } // namespace v8::internal + +#endif // V8_MARK_COMPACT_INL_H_ diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index 9b0d5fca5a..1adb74745f 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,20 +27,31 @@ #include "v8.h" +#include "code-stubs.h" #include "compilation-cache.h" +#include "deoptimizer.h" #include "execution.h" -#include "heap-profiler.h" #include "gdb-jit.h" #include "global-handles.h" +#include "heap-profiler.h" #include "ic-inl.h" +#include "incremental-marking.h" #include "liveobjectlist-inl.h" #include "mark-compact.h" #include "objects-visiting.h" +#include "objects-visiting-inl.h" #include "stub-cache.h" namespace v8 { namespace internal { + +const char* Marking::kWhiteBitPattern = "00"; +const char* Marking::kBlackBitPattern = "10"; +const char* Marking::kGreyBitPattern = "11"; +const char* Marking::kImpossibleBitPattern = "01"; + + // ------------------------------------------------------------------------- // MarkCompactCollector @@ -48,70 +59,614 @@ MarkCompactCollector::MarkCompactCollector() : // NOLINT #ifdef DEBUG state_(IDLE), #endif - force_compaction_(false), - compacting_collection_(false), - compact_on_next_gc_(false), - previous_marked_count_(0), + sweep_precisely_(false), + compacting_(false), + was_marked_incrementally_(false), + collect_maps_(FLAG_collect_maps), + flush_monomorphic_ics_(false), tracer_(NULL), -#ifdef DEBUG - live_young_objects_size_(0), - live_old_pointer_objects_size_(0), - live_old_data_objects_size_(0), - live_code_objects_size_(0), - live_map_objects_size_(0), - live_cell_objects_size_(0), - live_lo_objects_size_(0), - live_bytes_(0), -#endif + migration_slots_buffer_(NULL), heap_(NULL), code_flusher_(NULL), encountered_weak_maps_(NULL) { } +#ifdef DEBUG +class VerifyMarkingVisitor: public ObjectVisitor { + public: + void VisitPointers(Object** start, Object** end) { + for (Object** current = start; current < end; current++) { + if ((*current)->IsHeapObject()) { + HeapObject* object = HeapObject::cast(*current); + ASSERT(HEAP->mark_compact_collector()->IsMarked(object)); + } + } + } +}; + + +static void VerifyMarking(Address bottom, Address top) { + VerifyMarkingVisitor visitor; + HeapObject* object; + Address next_object_must_be_here_or_later = bottom; + + for (Address current = bottom; + current < top; + current += kPointerSize) { + object = HeapObject::FromAddress(current); + if (MarkCompactCollector::IsMarked(object)) { + ASSERT(current >= next_object_must_be_here_or_later); + object->Iterate(&visitor); + next_object_must_be_here_or_later = current + object->Size(); + } + } +} + + +static void VerifyMarking(NewSpace* space) { + Address end = space->top(); + NewSpacePageIterator it(space->bottom(), end); + // The bottom position is at the start of its page. Allows us to use + // page->body() as start of range on all pages. + ASSERT_EQ(space->bottom(), + NewSpacePage::FromAddress(space->bottom())->body()); + while (it.has_next()) { + NewSpacePage* page = it.next(); + Address limit = it.has_next() ? page->body_limit() : end; + ASSERT(limit == end || !page->Contains(end)); + VerifyMarking(page->body(), limit); + } +} + + +static void VerifyMarking(PagedSpace* space) { + PageIterator it(space); + + while (it.has_next()) { + Page* p = it.next(); + VerifyMarking(p->ObjectAreaStart(), p->ObjectAreaEnd()); + } +} + + +static void VerifyMarking(Heap* heap) { + VerifyMarking(heap->old_pointer_space()); + VerifyMarking(heap->old_data_space()); + VerifyMarking(heap->code_space()); + VerifyMarking(heap->cell_space()); + VerifyMarking(heap->map_space()); + VerifyMarking(heap->new_space()); + + VerifyMarkingVisitor visitor; + + LargeObjectIterator it(heap->lo_space()); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + if (MarkCompactCollector::IsMarked(obj)) { + obj->Iterate(&visitor); + } + } + + heap->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG); +} + + +class VerifyEvacuationVisitor: public ObjectVisitor { + public: + void VisitPointers(Object** start, Object** end) { + for (Object** current = start; current < end; current++) { + if ((*current)->IsHeapObject()) { + HeapObject* object = HeapObject::cast(*current); + CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(object)); + } + } + } +}; + + +static void VerifyEvacuation(Address bottom, Address top) { + VerifyEvacuationVisitor visitor; + HeapObject* object; + Address next_object_must_be_here_or_later = bottom; + + for (Address current = bottom; + current < top; + current += kPointerSize) { + object = HeapObject::FromAddress(current); + if (MarkCompactCollector::IsMarked(object)) { + ASSERT(current >= next_object_must_be_here_or_later); + object->Iterate(&visitor); + next_object_must_be_here_or_later = current + object->Size(); + } + } +} + + +static void VerifyEvacuation(NewSpace* space) { + NewSpacePageIterator it(space->bottom(), space->top()); + VerifyEvacuationVisitor visitor; + + while (it.has_next()) { + NewSpacePage* page = it.next(); + Address current = page->body(); + Address limit = it.has_next() ? page->body_limit() : space->top(); + ASSERT(limit == space->top() || !page->Contains(space->top())); + while (current < limit) { + HeapObject* object = HeapObject::FromAddress(current); + object->Iterate(&visitor); + current += object->Size(); + } + } +} + + +static void VerifyEvacuation(PagedSpace* space) { + PageIterator it(space); + + while (it.has_next()) { + Page* p = it.next(); + if (p->IsEvacuationCandidate()) continue; + VerifyEvacuation(p->ObjectAreaStart(), p->ObjectAreaEnd()); + } +} + + +static void VerifyEvacuation(Heap* heap) { + VerifyEvacuation(heap->old_pointer_space()); + VerifyEvacuation(heap->old_data_space()); + VerifyEvacuation(heap->code_space()); + VerifyEvacuation(heap->cell_space()); + VerifyEvacuation(heap->map_space()); + VerifyEvacuation(heap->new_space()); + + VerifyEvacuationVisitor visitor; + heap->IterateStrongRoots(&visitor, VISIT_ALL); +} +#endif + + +void MarkCompactCollector::AddEvacuationCandidate(Page* p) { + p->MarkEvacuationCandidate(); + evacuation_candidates_.Add(p); +} + + +static void TraceFragmentation(PagedSpace* space) { + int number_of_pages = space->CountTotalPages(); + intptr_t reserved = (number_of_pages * Page::kObjectAreaSize); + intptr_t free = reserved - space->SizeOfObjects(); + PrintF("[%s]: %d pages, %d (%.1f%%) free\n", + AllocationSpaceName(space->identity()), + number_of_pages, + static_cast<int>(free), + static_cast<double>(free) * 100 / reserved); +} + + +bool MarkCompactCollector::StartCompaction(CompactionMode mode) { + if (!compacting_) { + ASSERT(evacuation_candidates_.length() == 0); + + CollectEvacuationCandidates(heap()->old_pointer_space()); + CollectEvacuationCandidates(heap()->old_data_space()); + + if (FLAG_compact_code_space && mode == NON_INCREMENTAL_COMPACTION) { + CollectEvacuationCandidates(heap()->code_space()); + } else if (FLAG_trace_fragmentation) { + TraceFragmentation(heap()->code_space()); + } + + if (FLAG_trace_fragmentation) { + TraceFragmentation(heap()->map_space()); + TraceFragmentation(heap()->cell_space()); + } + + heap()->old_pointer_space()->EvictEvacuationCandidatesFromFreeLists(); + heap()->old_data_space()->EvictEvacuationCandidatesFromFreeLists(); + heap()->code_space()->EvictEvacuationCandidatesFromFreeLists(); + + compacting_ = evacuation_candidates_.length() > 0; + } + + return compacting_; +} + + void MarkCompactCollector::CollectGarbage() { // Make sure that Prepare() has been called. The individual steps below will // update the state as they proceed. ASSERT(state_ == PREPARE_GC); ASSERT(encountered_weak_maps_ == Smi::FromInt(0)); - // Prepare has selected whether to compact the old generation or not. - // Tell the tracer. - if (IsCompacting()) tracer_->set_is_compacting(); - MarkLiveObjects(); + ASSERT(heap_->incremental_marking()->IsStopped()); - if (FLAG_collect_maps) ClearNonLiveTransitions(); + if (collect_maps_) ClearNonLiveTransitions(); ClearWeakMaps(); - SweepLargeObjectSpace(); +#ifdef DEBUG + if (FLAG_verify_heap) { + VerifyMarking(heap_); + } +#endif - if (IsCompacting()) { - GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_COMPACT); - EncodeForwardingAddresses(); + SweepSpaces(); - heap()->MarkMapPointersAsEncoded(true); - UpdatePointers(); - heap()->MarkMapPointersAsEncoded(false); - heap()->isolate()->pc_to_code_cache()->Flush(); + if (!collect_maps_) ReattachInitialMaps(); - RelocateObjects(); - } else { - SweepSpaces(); - heap()->isolate()->pc_to_code_cache()->Flush(); - } + heap_->isolate()->inner_pointer_to_code_cache()->Flush(); Finish(); - // Save the count of marked objects remaining after the collection and - // null out the GC tracer. - previous_marked_count_ = tracer_->marked_count(); - ASSERT(previous_marked_count_ == 0); tracer_ = NULL; } +#ifdef DEBUG +void MarkCompactCollector::VerifyMarkbitsAreClean(PagedSpace* space) { + PageIterator it(space); + + while (it.has_next()) { + Page* p = it.next(); + CHECK(p->markbits()->IsClean()); + CHECK_EQ(0, p->LiveBytes()); + } +} + +void MarkCompactCollector::VerifyMarkbitsAreClean(NewSpace* space) { + NewSpacePageIterator it(space->bottom(), space->top()); + + while (it.has_next()) { + NewSpacePage* p = it.next(); + CHECK(p->markbits()->IsClean()); + CHECK_EQ(0, p->LiveBytes()); + } +} + +void MarkCompactCollector::VerifyMarkbitsAreClean() { + VerifyMarkbitsAreClean(heap_->old_pointer_space()); + VerifyMarkbitsAreClean(heap_->old_data_space()); + VerifyMarkbitsAreClean(heap_->code_space()); + VerifyMarkbitsAreClean(heap_->cell_space()); + VerifyMarkbitsAreClean(heap_->map_space()); + VerifyMarkbitsAreClean(heap_->new_space()); + + LargeObjectIterator it(heap_->lo_space()); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + MarkBit mark_bit = Marking::MarkBitFrom(obj); + ASSERT(Marking::IsWhite(mark_bit)); + } +} +#endif + + +static void ClearMarkbitsInPagedSpace(PagedSpace* space) { + PageIterator it(space); + + while (it.has_next()) { + Bitmap::Clear(it.next()); + } +} + + +static void ClearMarkbitsInNewSpace(NewSpace* space) { + NewSpacePageIterator it(space->ToSpaceStart(), space->ToSpaceEnd()); + + while (it.has_next()) { + Bitmap::Clear(it.next()); + } +} + + +void MarkCompactCollector::ClearMarkbits() { + ClearMarkbitsInPagedSpace(heap_->code_space()); + ClearMarkbitsInPagedSpace(heap_->map_space()); + ClearMarkbitsInPagedSpace(heap_->old_pointer_space()); + ClearMarkbitsInPagedSpace(heap_->old_data_space()); + ClearMarkbitsInPagedSpace(heap_->cell_space()); + ClearMarkbitsInNewSpace(heap_->new_space()); + + LargeObjectIterator it(heap_->lo_space()); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + MarkBit mark_bit = Marking::MarkBitFrom(obj); + mark_bit.Clear(); + mark_bit.Next().Clear(); + } +} + + +bool Marking::TransferMark(Address old_start, Address new_start) { + // This is only used when resizing an object. + ASSERT(MemoryChunk::FromAddress(old_start) == + MemoryChunk::FromAddress(new_start)); + + // If the mark doesn't move, we don't check the color of the object. + // It doesn't matter whether the object is black, since it hasn't changed + // size, so the adjustment to the live data count will be zero anyway. + if (old_start == new_start) return false; + + MarkBit new_mark_bit = MarkBitFrom(new_start); + MarkBit old_mark_bit = MarkBitFrom(old_start); + +#ifdef DEBUG + ObjectColor old_color = Color(old_mark_bit); +#endif + + if (Marking::IsBlack(old_mark_bit)) { + old_mark_bit.Clear(); + ASSERT(IsWhite(old_mark_bit)); + Marking::MarkBlack(new_mark_bit); + return true; + } else if (Marking::IsGrey(old_mark_bit)) { + ASSERT(heap_->incremental_marking()->IsMarking()); + old_mark_bit.Clear(); + old_mark_bit.Next().Clear(); + ASSERT(IsWhite(old_mark_bit)); + heap_->incremental_marking()->WhiteToGreyAndPush( + HeapObject::FromAddress(new_start), new_mark_bit); + heap_->incremental_marking()->RestartIfNotMarking(); + } + +#ifdef DEBUG + ObjectColor new_color = Color(new_mark_bit); + ASSERT(new_color == old_color); +#endif + + return false; +} + + +const char* AllocationSpaceName(AllocationSpace space) { + switch (space) { + case NEW_SPACE: return "NEW_SPACE"; + case OLD_POINTER_SPACE: return "OLD_POINTER_SPACE"; + case OLD_DATA_SPACE: return "OLD_DATA_SPACE"; + case CODE_SPACE: return "CODE_SPACE"; + case MAP_SPACE: return "MAP_SPACE"; + case CELL_SPACE: return "CELL_SPACE"; + case LO_SPACE: return "LO_SPACE"; + default: + UNREACHABLE(); + } + + return NULL; +} + + +// Returns zero for pages that have so little fragmentation that it is not +// worth defragmenting them. Otherwise a positive integer that gives an +// estimate of fragmentation on an arbitrary scale. +static int FreeListFragmentation(PagedSpace* space, Page* p) { + // If page was not swept then there are no free list items on it. + if (!p->WasSwept()) { + if (FLAG_trace_fragmentation) { + PrintF("%p [%s]: %d bytes live (unswept)\n", + reinterpret_cast<void*>(p), + AllocationSpaceName(space->identity()), + p->LiveBytes()); + } + return 0; + } + + FreeList::SizeStats sizes; + space->CountFreeListItems(p, &sizes); + + intptr_t ratio; + intptr_t ratio_threshold; + if (space->identity() == CODE_SPACE) { + ratio = (sizes.medium_size_ * 10 + sizes.large_size_ * 2) * 100 / + Page::kObjectAreaSize; + ratio_threshold = 10; + } else { + ratio = (sizes.small_size_ * 5 + sizes.medium_size_) * 100 / + Page::kObjectAreaSize; + ratio_threshold = 15; + } + + if (FLAG_trace_fragmentation) { + PrintF("%p [%s]: %d (%.2f%%) %d (%.2f%%) %d (%.2f%%) %d (%.2f%%) %s\n", + reinterpret_cast<void*>(p), + AllocationSpaceName(space->identity()), + static_cast<int>(sizes.small_size_), + static_cast<double>(sizes.small_size_ * 100) / + Page::kObjectAreaSize, + static_cast<int>(sizes.medium_size_), + static_cast<double>(sizes.medium_size_ * 100) / + Page::kObjectAreaSize, + static_cast<int>(sizes.large_size_), + static_cast<double>(sizes.large_size_ * 100) / + Page::kObjectAreaSize, + static_cast<int>(sizes.huge_size_), + static_cast<double>(sizes.huge_size_ * 100) / + Page::kObjectAreaSize, + (ratio > ratio_threshold) ? "[fragmented]" : ""); + } + + if (FLAG_always_compact && sizes.Total() != Page::kObjectAreaSize) { + return 1; + } + + if (ratio <= ratio_threshold) return 0; // Not fragmented. + + return static_cast<int>(ratio - ratio_threshold); +} + + +void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) { + ASSERT(space->identity() == OLD_POINTER_SPACE || + space->identity() == OLD_DATA_SPACE || + space->identity() == CODE_SPACE); + + int number_of_pages = space->CountTotalPages(); + + const int kMaxMaxEvacuationCandidates = 1000; + int max_evacuation_candidates = Min( + kMaxMaxEvacuationCandidates, + static_cast<int>(sqrt(static_cast<double>(number_of_pages / 2)) + 1)); + + if (FLAG_stress_compaction || FLAG_always_compact) { + max_evacuation_candidates = kMaxMaxEvacuationCandidates; + } + + class Candidate { + public: + Candidate() : fragmentation_(0), page_(NULL) { } + Candidate(int f, Page* p) : fragmentation_(f), page_(p) { } + + int fragmentation() { return fragmentation_; } + Page* page() { return page_; } + + private: + int fragmentation_; + Page* page_; + }; + + enum CompactionMode { + COMPACT_FREE_LISTS, + REDUCE_MEMORY_FOOTPRINT + }; + + CompactionMode mode = COMPACT_FREE_LISTS; + + intptr_t reserved = number_of_pages * Page::kObjectAreaSize; + intptr_t over_reserved = reserved - space->SizeOfObjects(); + static const intptr_t kFreenessThreshold = 50; + + if (over_reserved >= 2 * Page::kObjectAreaSize && + reduce_memory_footprint_) { + mode = REDUCE_MEMORY_FOOTPRINT; + + // We expect that empty pages are easier to compact so slightly bump the + // limit. + max_evacuation_candidates += 2; + + if (FLAG_trace_fragmentation) { + PrintF("Estimated over reserved memory: %.1f MB (setting threshold %d)\n", + static_cast<double>(over_reserved) / MB, + static_cast<int>(kFreenessThreshold)); + } + } + + intptr_t estimated_release = 0; + + Candidate candidates[kMaxMaxEvacuationCandidates]; + + int count = 0; + int fragmentation = 0; + Candidate* least = NULL; + + PageIterator it(space); + if (it.has_next()) it.next(); // Never compact the first page. + + while (it.has_next()) { + Page* p = it.next(); + p->ClearEvacuationCandidate(); + + if (FLAG_stress_compaction) { + int counter = space->heap()->ms_count(); + uintptr_t page_number = reinterpret_cast<uintptr_t>(p) >> kPageSizeBits; + if ((counter & 1) == (page_number & 1)) fragmentation = 1; + } else if (mode == REDUCE_MEMORY_FOOTPRINT) { + // Don't try to release too many pages. + if (estimated_release >= ((over_reserved * 3) / 4)) { + continue; + } + + intptr_t free_bytes = 0; + + if (!p->WasSwept()) { + free_bytes = (Page::kObjectAreaSize - p->LiveBytes()); + } else { + FreeList::SizeStats sizes; + space->CountFreeListItems(p, &sizes); + free_bytes = sizes.Total(); + } + + int free_pct = static_cast<int>(free_bytes * 100 / Page::kObjectAreaSize); + + if (free_pct >= kFreenessThreshold) { + estimated_release += Page::kObjectAreaSize + + (Page::kObjectAreaSize - free_bytes); + fragmentation = free_pct; + } else { + fragmentation = 0; + } + + if (FLAG_trace_fragmentation) { + PrintF("%p [%s]: %d (%.2f%%) free %s\n", + reinterpret_cast<void*>(p), + AllocationSpaceName(space->identity()), + static_cast<int>(free_bytes), + static_cast<double>(free_bytes * 100) / Page::kObjectAreaSize, + (fragmentation > 0) ? "[fragmented]" : ""); + } + } else { + fragmentation = FreeListFragmentation(space, p); + } + + if (fragmentation != 0) { + if (count < max_evacuation_candidates) { + candidates[count++] = Candidate(fragmentation, p); + } else { + if (least == NULL) { + for (int i = 0; i < max_evacuation_candidates; i++) { + if (least == NULL || + candidates[i].fragmentation() < least->fragmentation()) { + least = candidates + i; + } + } + } + if (least->fragmentation() < fragmentation) { + *least = Candidate(fragmentation, p); + least = NULL; + } + } + } + } + + for (int i = 0; i < count; i++) { + AddEvacuationCandidate(candidates[i].page()); + } + + if (count > 0 && FLAG_trace_fragmentation) { + PrintF("Collected %d evacuation candidates for space %s\n", + count, + AllocationSpaceName(space->identity())); + } +} + + +void MarkCompactCollector::AbortCompaction() { + if (compacting_) { + int npages = evacuation_candidates_.length(); + for (int i = 0; i < npages; i++) { + Page* p = evacuation_candidates_[i]; + slots_buffer_allocator_.DeallocateChain(p->slots_buffer_address()); + p->ClearEvacuationCandidate(); + p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION); + } + compacting_ = false; + evacuation_candidates_.Rewind(0); + invalidated_code_.Rewind(0); + } + ASSERT_EQ(0, evacuation_candidates_.length()); +} + + void MarkCompactCollector::Prepare(GCTracer* tracer) { + was_marked_incrementally_ = heap()->incremental_marking()->IsMarking(); + + // Disable collection of maps if incremental marking is enabled. + // Map collection algorithm relies on a special map transition tree traversal + // order which is not implemented for incremental marking. + collect_maps_ = FLAG_collect_maps && !was_marked_incrementally_; + + // Monomorphic ICs are preserved when possible, but need to be flushed + // when they might be keeping a Context alive, or when the heap is about + // to be serialized. + flush_monomorphic_ics_ = + heap()->isolate()->context_exit_happened() || Serializer::enabled(); + // Rather than passing the tracer around we stash it in a static member // variable. tracer_ = tracer; @@ -120,16 +675,10 @@ void MarkCompactCollector::Prepare(GCTracer* tracer) { ASSERT(state_ == IDLE); state_ = PREPARE_GC; #endif - ASSERT(!FLAG_always_compact || !FLAG_never_compact); - compacting_collection_ = - FLAG_always_compact || force_compaction_ || compact_on_next_gc_; - compact_on_next_gc_ = false; + ASSERT(!FLAG_never_compact || !FLAG_always_compact); - if (FLAG_never_compact) compacting_collection_ = false; - if (!heap()->map_space()->MapPointersEncodable()) - compacting_collection_ = false; - if (FLAG_collect_maps) CreateBackPointers(); + if (collect_maps_) CreateBackPointers(); #ifdef ENABLE_GDB_JIT_INTERFACE if (FLAG_gdbjit) { // If GDBJIT interface is active disable compaction. @@ -137,21 +686,31 @@ void MarkCompactCollector::Prepare(GCTracer* tracer) { } #endif + // Clear marking bits for precise sweeping to collect all garbage. + if (was_marked_incrementally_ && PreciseSweepingRequired()) { + heap()->incremental_marking()->Abort(); + ClearMarkbits(); + AbortCompaction(); + was_marked_incrementally_ = false; + } + + // Don't start compaction if we are in the middle of incremental + // marking cycle. We did not collect any slots. + if (!FLAG_never_compact && !was_marked_incrementally_) { + StartCompaction(NON_INCREMENTAL_COMPACTION); + } + PagedSpaces spaces; for (PagedSpace* space = spaces.next(); - space != NULL; space = spaces.next()) { - space->PrepareForMarkCompact(compacting_collection_); + space != NULL; + space = spaces.next()) { + space->PrepareForMarkCompact(); } #ifdef DEBUG - live_bytes_ = 0; - live_young_objects_size_ = 0; - live_old_pointer_objects_size_ = 0; - live_old_data_objects_size_ = 0; - live_code_objects_size_ = 0; - live_map_objects_size_ = 0; - live_cell_objects_size_ = 0; - live_lo_objects_size_ = 0; + if (!was_marked_incrementally_ && FLAG_verify_heap) { + VerifyMarkbitsAreClean(); + } #endif } @@ -168,31 +727,6 @@ void MarkCompactCollector::Finish() { heap()->isolate()->stub_cache()->Clear(); heap()->external_string_table_.CleanUp(); - - // If we've just compacted old space there's no reason to check the - // fragmentation limit. Just return. - if (HasCompacted()) return; - - // We compact the old generation on the next GC if it has gotten too - // fragmented (ie, we could recover an expected amount of space by - // reclaiming the waste and free list blocks). - static const int kFragmentationLimit = 15; // Percent. - static const int kFragmentationAllowed = 1 * MB; // Absolute. - intptr_t old_gen_recoverable = 0; - intptr_t old_gen_used = 0; - - OldSpaces spaces; - for (OldSpace* space = spaces.next(); space != NULL; space = spaces.next()) { - old_gen_recoverable += space->Waste() + space->AvailableFree(); - old_gen_used += space->Size(); - } - - int old_gen_fragmentation = - static_cast<int>((old_gen_recoverable * 100.0) / old_gen_used); - if (old_gen_fragmentation > kFragmentationLimit && - old_gen_recoverable > kFragmentationAllowed) { - compact_on_next_gc_ = true; - } } @@ -237,8 +771,7 @@ class CodeFlusher { } void AddCandidate(JSFunction* function) { - ASSERT(function->unchecked_code() == - function->unchecked_shared()->unchecked_code()); + ASSERT(function->code() == function->shared()->code()); SetNextCandidate(function, jsfunction_candidates_head_); jsfunction_candidates_head_ = function; @@ -258,16 +791,26 @@ class CodeFlusher { while (candidate != NULL) { next_candidate = GetNextCandidate(candidate); - SharedFunctionInfo* shared = candidate->unchecked_shared(); + SharedFunctionInfo* shared = candidate->shared(); - Code* code = shared->unchecked_code(); - if (!code->IsMarked()) { + Code* code = shared->code(); + MarkBit code_mark = Marking::MarkBitFrom(code); + if (!code_mark.Get()) { shared->set_code(lazy_compile); candidate->set_code(lazy_compile); } else { - candidate->set_code(shared->unchecked_code()); + candidate->set_code(shared->code()); } + // We are in the middle of a GC cycle so the write barrier in the code + // setter did not record the slot update and we have to do that manually. + Address slot = candidate->address() + JSFunction::kCodeEntryOffset; + Code* target = Code::cast(Code::GetObjectFromEntryAddress(slot)); + isolate_->heap()->mark_compact_collector()-> + RecordCodeEntrySlot(slot, target); + + RecordSharedFunctionInfoCodeSlot(shared); + candidate = next_candidate; } @@ -284,17 +827,27 @@ class CodeFlusher { next_candidate = GetNextCandidate(candidate); SetNextCandidate(candidate, NULL); - Code* code = candidate->unchecked_code(); - if (!code->IsMarked()) { + Code* code = candidate->code(); + MarkBit code_mark = Marking::MarkBitFrom(code); + if (!code_mark.Get()) { candidate->set_code(lazy_compile); } + RecordSharedFunctionInfoCodeSlot(candidate); + candidate = next_candidate; } shared_function_info_candidates_head_ = NULL; } + void RecordSharedFunctionInfoCodeSlot(SharedFunctionInfo* shared) { + Object** slot = HeapObject::RawField(shared, + SharedFunctionInfo::kCodeOffset); + isolate_->heap()->mark_compact_collector()-> + RecordSlot(slot, slot, HeapObject::cast(*slot)); + } + static JSFunction** GetNextCandidateField(JSFunction* candidate) { return reinterpret_cast<JSFunction**>( candidate->address() + JSFunction::kCodeEntryOffset); @@ -311,18 +864,19 @@ class CodeFlusher { static SharedFunctionInfo** GetNextCandidateField( SharedFunctionInfo* candidate) { - Code* code = candidate->unchecked_code(); + Code* code = candidate->code(); return reinterpret_cast<SharedFunctionInfo**>( - code->address() + Code::kNextCodeFlushingCandidateOffset); + code->address() + Code::kGCMetadataOffset); } static SharedFunctionInfo* GetNextCandidate(SharedFunctionInfo* candidate) { - return *GetNextCandidateField(candidate); + return reinterpret_cast<SharedFunctionInfo*>( + candidate->code()->gc_metadata()); } static void SetNextCandidate(SharedFunctionInfo* candidate, SharedFunctionInfo* next_candidate) { - *GetNextCandidateField(candidate) = next_candidate; + candidate->code()->set_gc_metadata(next_candidate); } Isolate* isolate_; @@ -347,7 +901,7 @@ static inline HeapObject* ShortCircuitConsString(Object** p) { // it in place to its left substring. Return the updated value. // // Here we assume that if we change *p, we replace it with a heap object - // (ie, the left substring of a cons string is always a heap object). + // (i.e., the left substring of a cons string is always a heap object). // // The check performed is: // object->IsConsString() && !object->IsSymbol() && @@ -355,14 +909,14 @@ static inline HeapObject* ShortCircuitConsString(Object** p) { // except the maps for the object and its possible substrings might be // marked. HeapObject* object = HeapObject::cast(*p); - MapWord map_word = object->map_word(); - map_word.ClearMark(); - InstanceType type = map_word.ToMap()->instance_type(); + if (!FLAG_clever_optimizations) return object; + Map* map = object->map(); + InstanceType type = map->instance_type(); if ((type & kShortcutTypeMask) != kShortcutTypeTag) return object; Object* second = reinterpret_cast<ConsString*>(object)->unchecked_second(); - Heap* heap = map_word.ToMap()->heap(); - if (second != heap->raw_unchecked_empty_string()) { + Heap* heap = map->GetHeap(); + if (second != heap->empty_string()) { return object; } @@ -404,14 +958,12 @@ class StaticMarkingVisitor : public StaticVisitorBase { FixedArray::BodyDescriptor, void>::Visit); - table_.Register(kVisitFixedDoubleArray, DataObjectVisitor::Visit); + table_.Register(kVisitGlobalContext, &VisitGlobalContext); - table_.Register(kVisitGlobalContext, - &FixedBodyVisitor<StaticMarkingVisitor, - Context::MarkCompactBodyDescriptor, - void>::Visit); + table_.Register(kVisitFixedDoubleArray, DataObjectVisitor::Visit); table_.Register(kVisitByteArray, &DataObjectVisitor::Visit); + table_.Register(kVisitFreeSpace, &DataObjectVisitor::Visit); table_.Register(kVisitSeqAsciiString, &DataObjectVisitor::Visit); table_.Register(kVisitSeqTwoByteString, &DataObjectVisitor::Visit); @@ -456,7 +1008,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { } INLINE(static void VisitPointer(Heap* heap, Object** p)) { - MarkObjectByPointer(heap, p); + MarkObjectByPointer(heap->mark_compact_collector(), p, p); } INLINE(static void VisitPointers(Heap* heap, Object** start, Object** end)) { @@ -466,29 +1018,42 @@ class StaticMarkingVisitor : public StaticVisitorBase { if (VisitUnmarkedObjects(heap, start, end)) return; // We are close to a stack overflow, so just mark the objects. } - for (Object** p = start; p < end; p++) MarkObjectByPointer(heap, p); - } - - static inline void VisitCodeTarget(Heap* heap, RelocInfo* rinfo) { - ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); - Code* code = Code::GetCodeFromTargetAddress(rinfo->target_address()); - if (FLAG_cleanup_code_caches_at_gc && code->is_inline_cache_stub()) { - IC::Clear(rinfo->pc()); - // Please note targets for cleared inline cached do not have to be - // marked since they are contained in HEAP->non_monomorphic_cache(). - } else { - heap->mark_compact_collector()->MarkObject(code); + MarkCompactCollector* collector = heap->mark_compact_collector(); + for (Object** p = start; p < end; p++) { + MarkObjectByPointer(collector, start, p); } } static void VisitGlobalPropertyCell(Heap* heap, RelocInfo* rinfo) { ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL); - Object* cell = rinfo->target_cell(); - Object* old_cell = cell; - VisitPointer(heap, &cell); - if (cell != old_cell) { - rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell)); + JSGlobalPropertyCell* cell = + JSGlobalPropertyCell::cast(rinfo->target_cell()); + MarkBit mark = Marking::MarkBitFrom(cell); + heap->mark_compact_collector()->MarkObject(cell, mark); + } + + static inline void VisitEmbeddedPointer(Heap* heap, RelocInfo* rinfo) { + ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); + // TODO(mstarzinger): We do not short-circuit cons strings here, verify + // that there can be no such embedded pointers and add assertion here. + HeapObject* object = HeapObject::cast(rinfo->target_object()); + heap->mark_compact_collector()->RecordRelocSlot(rinfo, object); + MarkBit mark = Marking::MarkBitFrom(object); + heap->mark_compact_collector()->MarkObject(object, mark); + } + + static inline void VisitCodeTarget(Heap* heap, RelocInfo* rinfo) { + ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); + Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); + if (FLAG_cleanup_code_caches_at_gc && target->is_inline_cache_stub() + && (target->ic_state() == MEGAMORPHIC || + heap->mark_compact_collector()->flush_monomorphic_ics_)) { + IC::Clear(rinfo->pc()); + target = Code::GetCodeFromTargetAddress(rinfo->target_address()); } + MarkBit code_mark = Marking::MarkBitFrom(target); + heap->mark_compact_collector()->MarkObject(target, code_mark); + heap->mark_compact_collector()->RecordRelocSlot(rinfo, target); } static inline void VisitDebugTarget(Heap* heap, RelocInfo* rinfo) { @@ -496,17 +1061,21 @@ class StaticMarkingVisitor : public StaticVisitorBase { rinfo->IsPatchedReturnSequence()) || (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && rinfo->IsPatchedDebugBreakSlotSequence())); - HeapObject* code = Code::GetCodeFromTargetAddress(rinfo->call_address()); - heap->mark_compact_collector()->MarkObject(code); + Code* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); + MarkBit code_mark = Marking::MarkBitFrom(target); + heap->mark_compact_collector()->MarkObject(target, code_mark); + heap->mark_compact_collector()->RecordRelocSlot(rinfo, target); } // Mark object pointed to by p. - INLINE(static void MarkObjectByPointer(Heap* heap, Object** p)) { + INLINE(static void MarkObjectByPointer(MarkCompactCollector* collector, + Object** anchor_slot, + Object** p)) { if (!(*p)->IsHeapObject()) return; HeapObject* object = ShortCircuitConsString(p); - if (!object->IsMarked()) { - heap->mark_compact_collector()->MarkUnmarkedObject(object); - } + collector->RecordSlot(anchor_slot, p, object); + MarkBit mark = Marking::MarkBitFrom(object); + collector->MarkObject(object, mark); } @@ -515,12 +1084,15 @@ class StaticMarkingVisitor : public StaticVisitorBase { HeapObject* obj)) { #ifdef DEBUG ASSERT(Isolate::Current()->heap()->Contains(obj)); - ASSERT(!obj->IsMarked()); + ASSERT(!HEAP->mark_compact_collector()->IsMarked(obj)); #endif Map* map = obj->map(); - collector->SetMark(obj); + Heap* heap = obj->GetHeap(); + MarkBit mark = Marking::MarkBitFrom(obj); + heap->mark_compact_collector()->SetMark(obj, mark); // Mark the map pointer and the body. - if (!map->IsMarked()) collector->MarkUnmarkedObject(map); + MarkBit map_mark = Marking::MarkBitFrom(map); + heap->mark_compact_collector()->MarkObject(map, map_mark); IterateBody(map, obj); } @@ -536,15 +1108,19 @@ class StaticMarkingVisitor : public StaticVisitorBase { MarkCompactCollector* collector = heap->mark_compact_collector(); // Visit the unmarked objects. for (Object** p = start; p < end; p++) { - if (!(*p)->IsHeapObject()) continue; - HeapObject* obj = HeapObject::cast(*p); - if (obj->IsMarked()) continue; + Object* o = *p; + if (!o->IsHeapObject()) continue; + collector->RecordSlot(start, p, o); + HeapObject* obj = HeapObject::cast(o); + MarkBit mark = Marking::MarkBitFrom(obj); + if (mark.Get()) continue; VisitUnmarkedObject(collector, obj); } return true; } static inline void VisitExternalReference(Address* p) { } + static inline void VisitExternalReference(RelocInfo* rinfo) { } static inline void VisitRuntimeEntry(RelocInfo* rinfo) { } private: @@ -567,7 +1143,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { void> StructObjectVisitor; static void VisitJSWeakMap(Map* map, HeapObject* object) { - MarkCompactCollector* collector = map->heap()->mark_compact_collector(); + MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector(); JSWeakMap* weak_map = reinterpret_cast<JSWeakMap*>(object); // Enqueue weak map in linked list of encountered weak maps. @@ -578,25 +1154,36 @@ class StaticMarkingVisitor : public StaticVisitorBase { // Skip visiting the backing hash table containing the mappings. int object_size = JSWeakMap::BodyDescriptor::SizeOf(map, object); BodyVisitorBase<StaticMarkingVisitor>::IteratePointers( - map->heap(), + map->GetHeap(), object, JSWeakMap::BodyDescriptor::kStartOffset, JSWeakMap::kTableOffset); BodyVisitorBase<StaticMarkingVisitor>::IteratePointers( - map->heap(), + map->GetHeap(), object, JSWeakMap::kTableOffset + kPointerSize, object_size); // Mark the backing hash table without pushing it on the marking stack. - ASSERT(!weak_map->unchecked_table()->IsMarked()); - ASSERT(weak_map->unchecked_table()->map()->IsMarked()); - collector->SetMark(weak_map->unchecked_table()); + ObjectHashTable* table = ObjectHashTable::cast(weak_map->table()); + ASSERT(!MarkCompactCollector::IsMarked(table)); + collector->SetMark(table, Marking::MarkBitFrom(table)); + collector->MarkObject(table->map(), Marking::MarkBitFrom(table->map())); + ASSERT(MarkCompactCollector::IsMarked(table->map())); } static void VisitCode(Map* map, HeapObject* object) { - reinterpret_cast<Code*>(object)->CodeIterateBody<StaticMarkingVisitor>( - map->heap()); + Heap* heap = map->GetHeap(); + Code* code = reinterpret_cast<Code*>(object); + if (FLAG_cleanup_code_caches_at_gc) { + TypeFeedbackCells* type_feedback_cells = code->type_feedback_cells(); + for (int i = 0; i < type_feedback_cells->CellCount(); i++) { + ASSERT(type_feedback_cells->AstId(i)->IsSmi()); + JSGlobalPropertyCell* cell = type_feedback_cells->Cell(i); + cell->set_value(TypeFeedbackCells::RawUninitializedSentinel(heap)); + } + } + code->CodeIterateBody<StaticMarkingVisitor>(heap); } // Code flushing support. @@ -608,19 +1195,19 @@ class StaticMarkingVisitor : public StaticVisitorBase { static const int kRegExpCodeThreshold = 5; inline static bool HasSourceCode(Heap* heap, SharedFunctionInfo* info) { - Object* undefined = heap->raw_unchecked_undefined_value(); + Object* undefined = heap->undefined_value(); return (info->script() != undefined) && (reinterpret_cast<Script*>(info->script())->source() != undefined); } inline static bool IsCompiled(JSFunction* function) { - return function->unchecked_code() != + return function->code() != function->GetIsolate()->builtins()->builtin(Builtins::kLazyCompile); } inline static bool IsCompiled(SharedFunctionInfo* function) { - return function->unchecked_code() != + return function->code() != function->GetIsolate()->builtins()->builtin(Builtins::kLazyCompile); } @@ -629,13 +1216,16 @@ class StaticMarkingVisitor : public StaticVisitorBase { // Code is either on stack, in compilation cache or referenced // by optimized version of function. - if (function->unchecked_code()->IsMarked()) { - shared_info->set_code_age(0); + MarkBit code_mark = Marking::MarkBitFrom(function->code()); + if (code_mark.Get()) { + if (!Marking::MarkBitFrom(shared_info).Get()) { + shared_info->set_code_age(0); + } return false; } // We do not flush code for optimized functions. - if (function->code() != shared_info->unchecked_code()) { + if (function->code() != shared_info->code()) { return false; } @@ -645,8 +1235,9 @@ class StaticMarkingVisitor : public StaticVisitorBase { inline static bool IsFlushable(Heap* heap, SharedFunctionInfo* shared_info) { // Code is either on stack, in compilation cache or referenced // by optimized version of function. - if (shared_info->unchecked_code()->IsMarked()) { - shared_info->set_code_age(0); + MarkBit code_mark = + Marking::MarkBitFrom(shared_info->code()); + if (code_mark.Get()) { return false; } @@ -658,9 +1249,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { // We never flush code for Api functions. Object* function_data = shared_info->function_data(); - if (function_data->IsHeapObject() && - (SafeMap(function_data)->instance_type() == - FUNCTION_TEMPLATE_INFO_TYPE)) { + if (function_data->IsFunctionTemplateInfo()) { return false; } @@ -701,40 +1290,9 @@ class StaticMarkingVisitor : public StaticVisitorBase { return true; } - - static inline Map* SafeMap(Object* obj) { - MapWord map_word = HeapObject::cast(obj)->map_word(); - map_word.ClearMark(); - map_word.ClearOverflow(); - return map_word.ToMap(); - } - - - static inline bool IsJSBuiltinsObject(Object* obj) { - return obj->IsHeapObject() && - (SafeMap(obj)->instance_type() == JS_BUILTINS_OBJECT_TYPE); - } - - static inline bool IsValidNotBuiltinContext(Object* ctx) { - if (!ctx->IsHeapObject()) return false; - - Map* map = SafeMap(ctx); - Heap* heap = map->heap(); - if (!(map == heap->raw_unchecked_function_context_map() || - map == heap->raw_unchecked_catch_context_map() || - map == heap->raw_unchecked_with_context_map() || - map == heap->raw_unchecked_global_context_map())) { - return false; - } - - Context* context = reinterpret_cast<Context*>(ctx); - - if (IsJSBuiltinsObject(context->global())) { - return false; - } - - return true; + return ctx->IsContext() && + !Context::cast(ctx)->global()->IsJSBuiltinsObject(); } @@ -754,17 +1312,29 @@ class StaticMarkingVisitor : public StaticVisitorBase { bool is_ascii) { // Make sure that the fixed array is in fact initialized on the RegExp. // We could potentially trigger a GC when initializing the RegExp. - if (SafeMap(re->data())->instance_type() != FIXED_ARRAY_TYPE) return; + if (HeapObject::cast(re->data())->map()->instance_type() != + FIXED_ARRAY_TYPE) return; // Make sure this is a RegExp that actually contains code. if (re->TypeTagUnchecked() != JSRegExp::IRREGEXP) return; Object* code = re->DataAtUnchecked(JSRegExp::code_index(is_ascii)); - if (!code->IsSmi() && SafeMap(code)->instance_type() == CODE_TYPE) { + if (!code->IsSmi() && + HeapObject::cast(code)->map()->instance_type() == CODE_TYPE) { // Save a copy that can be reinstated if we need the code again. re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii), code, heap); + + // Saving a copy might create a pointer into compaction candidate + // that was not observed by marker. This might happen if JSRegExp data + // was marked through the compilation cache before marker reached JSRegExp + // object. + FixedArray* data = FixedArray::cast(re->data()); + Object** slot = data->data_start() + JSRegExp::saved_code_index(is_ascii); + heap->mark_compact_collector()-> + RecordSlot(slot, slot, code); + // Set a number in the 0-255 range to guarantee no smi overflow. re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii), Smi::FromInt(heap->sweep_generation() & 0xff), @@ -796,14 +1366,14 @@ class StaticMarkingVisitor : public StaticVisitorBase { // If we did not use the code for kRegExpCodeThreshold mark sweep GCs // we flush the code. static void VisitRegExpAndFlushCode(Map* map, HeapObject* object) { - Heap* heap = map->heap(); + Heap* heap = map->GetHeap(); MarkCompactCollector* collector = heap->mark_compact_collector(); if (!collector->is_code_flushing_enabled()) { VisitJSRegExpFields(map, object); return; } JSRegExp* re = reinterpret_cast<JSRegExp*>(object); - // Flush code or set age on both ascii and two byte code. + // Flush code or set age on both ASCII and two byte code. UpdateRegExpCodeAgeAndFlush(heap, re, true); UpdateRegExpCodeAgeAndFlush(heap, re, false); // Visit the fields of the RegExp, including the updated FixedArray. @@ -813,7 +1383,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { static void VisitSharedFunctionInfoAndFlushCode(Map* map, HeapObject* object) { - MarkCompactCollector* collector = map->heap()->mark_compact_collector(); + MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector(); if (!collector->is_code_flushing_enabled()) { VisitSharedFunctionInfoGeneric(map, object); return; @@ -824,7 +1394,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { static void VisitSharedFunctionInfoAndFlushCodeGeneric( Map* map, HeapObject* object, bool known_flush_code_candidate) { - Heap* heap = map->heap(); + Heap* heap = map->GetHeap(); SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(object); if (shared->IsInobjectSlackTrackingInProgress()) shared->DetachInitialMap(); @@ -841,18 +1411,30 @@ class StaticMarkingVisitor : public StaticVisitorBase { static void VisitCodeEntry(Heap* heap, Address entry_address) { - Object* code = Code::GetObjectFromEntryAddress(entry_address); - Object* old_code = code; - VisitPointer(heap, &code); - if (code != old_code) { - Memory::Address_at(entry_address) = - reinterpret_cast<Code*>(code)->entry(); - } + Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address)); + MarkBit mark = Marking::MarkBitFrom(code); + heap->mark_compact_collector()->MarkObject(code, mark); + heap->mark_compact_collector()-> + RecordCodeEntrySlot(entry_address, code); } + static void VisitGlobalContext(Map* map, HeapObject* object) { + FixedBodyVisitor<StaticMarkingVisitor, + Context::MarkCompactBodyDescriptor, + void>::Visit(map, object); + + MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector(); + for (int idx = Context::FIRST_WEAK_SLOT; + idx < Context::GLOBAL_CONTEXT_SLOTS; + ++idx) { + Object** slot = + HeapObject::RawField(object, FixedArray::OffsetOfElementAt(idx)); + collector->RecordSlot(slot, slot, *slot); + } + } static void VisitJSFunctionAndFlushCode(Map* map, HeapObject* object) { - Heap* heap = map->heap(); + Heap* heap = map->GetHeap(); MarkCompactCollector* collector = heap->mark_compact_collector(); if (!collector->is_code_flushing_enabled()) { VisitJSFunction(map, object); @@ -867,10 +1449,12 @@ class StaticMarkingVisitor : public StaticVisitorBase { } if (!flush_code_candidate) { - collector->MarkObject(jsfunction->unchecked_shared()->unchecked_code()); + Code* code = jsfunction->shared()->code(); + MarkBit code_mark = Marking::MarkBitFrom(code); + collector->MarkObject(code, code_mark); - if (jsfunction->unchecked_code()->kind() == Code::OPTIMIZED_FUNCTION) { - collector->MarkInlinedFunctionsCode(jsfunction->unchecked_code()); + if (jsfunction->code()->kind() == Code::OPTIMIZED_FUNCTION) { + collector->MarkInlinedFunctionsCode(jsfunction->code()); } } @@ -894,12 +1478,11 @@ class StaticMarkingVisitor : public StaticVisitorBase { static inline void VisitJSFunctionFields(Map* map, JSFunction* object, bool flush_code_candidate) { - Heap* heap = map->heap(); - MarkCompactCollector* collector = heap->mark_compact_collector(); + Heap* heap = map->GetHeap(); VisitPointers(heap, - SLOT_ADDR(object, JSFunction::kPropertiesOffset), - SLOT_ADDR(object, JSFunction::kCodeEntryOffset)); + HeapObject::RawField(object, JSFunction::kPropertiesOffset), + HeapObject::RawField(object, JSFunction::kCodeEntryOffset)); if (!flush_code_candidate) { VisitCodeEntry(heap, object->address() + JSFunction::kCodeEntryOffset); @@ -909,29 +1492,39 @@ class StaticMarkingVisitor : public StaticVisitorBase { // Visit shared function info to avoid double checking of it's // flushability. SharedFunctionInfo* shared_info = object->unchecked_shared(); - if (!shared_info->IsMarked()) { + MarkBit shared_info_mark = Marking::MarkBitFrom(shared_info); + if (!shared_info_mark.Get()) { Map* shared_info_map = shared_info->map(); - collector->SetMark(shared_info); - collector->MarkObject(shared_info_map); + MarkBit shared_info_map_mark = + Marking::MarkBitFrom(shared_info_map); + heap->mark_compact_collector()->SetMark(shared_info, shared_info_mark); + heap->mark_compact_collector()->MarkObject(shared_info_map, + shared_info_map_mark); VisitSharedFunctionInfoAndFlushCodeGeneric(shared_info_map, shared_info, true); } } - VisitPointers(heap, - SLOT_ADDR(object, - JSFunction::kCodeEntryOffset + kPointerSize), - SLOT_ADDR(object, JSFunction::kNonWeakFieldsEndOffset)); + VisitPointers( + heap, + HeapObject::RawField(object, + JSFunction::kCodeEntryOffset + kPointerSize), + HeapObject::RawField(object, + JSFunction::kNonWeakFieldsEndOffset)); // Don't visit the next function list field as it is a weak reference. + Object** next_function = + HeapObject::RawField(object, JSFunction::kNextFunctionLinkOffset); + heap->mark_compact_collector()->RecordSlot( + next_function, next_function, *next_function); } static inline void VisitJSRegExpFields(Map* map, HeapObject* object) { int last_property_offset = JSRegExp::kSize + kPointerSize * map->inobject_properties(); - VisitPointers(map->heap(), + VisitPointers(map->GetHeap(), SLOT_ADDR(object, JSRegExp::kPropertiesOffset), SLOT_ADDR(object, last_property_offset)); } @@ -1007,8 +1600,10 @@ class SharedFunctionInfoMarkingVisitor : public ObjectVisitor { Object* obj = *slot; if (obj->IsSharedFunctionInfo()) { SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(obj); - collector_->MarkObject(shared->unchecked_code()); - collector_->MarkObject(shared); + MarkBit shared_mark = Marking::MarkBitFrom(shared); + MarkBit code_mark = Marking::MarkBitFrom(shared->code()); + collector_->MarkObject(shared->code(), code_mark); + collector_->MarkObject(shared, shared_mark); } } @@ -1022,16 +1617,17 @@ void MarkCompactCollector::MarkInlinedFunctionsCode(Code* code) { // of it's code and non-optimized version of all inlined functions. // This is required to support bailing out from inlined code. DeoptimizationInputData* data = - reinterpret_cast<DeoptimizationInputData*>( - code->unchecked_deoptimization_data()); + DeoptimizationInputData::cast(code->deoptimization_data()); - FixedArray* literals = data->UncheckedLiteralArray(); + FixedArray* literals = data->LiteralArray(); for (int i = 0, count = data->InlinedFunctionCount()->value(); i < count; i++) { - JSFunction* inlined = reinterpret_cast<JSFunction*>(literals->get(i)); - MarkObject(inlined->unchecked_shared()->unchecked_code()); + JSFunction* inlined = JSFunction::cast(literals->get(i)); + Code* inlined_code = inlined->shared()->code(); + MarkBit inlined_code_mark = Marking::MarkBitFrom(inlined_code); + MarkObject(inlined_code, inlined_code_mark); } } @@ -1045,7 +1641,8 @@ void MarkCompactCollector::PrepareThreadForCodeFlushing(Isolate* isolate, // actual optimized code object. StackFrame* frame = it.frame(); Code* code = frame->unchecked_code(); - MarkObject(code); + MarkBit code_mark = Marking::MarkBitFrom(code); + MarkObject(code, code_mark); if (frame->is_optimized()) { MarkInlinedFunctionsCode(frame->LookupCode()); } @@ -1056,7 +1653,8 @@ void MarkCompactCollector::PrepareThreadForCodeFlushing(Isolate* isolate, void MarkCompactCollector::PrepareForCodeFlushing() { ASSERT(heap() == Isolate::Current()->heap()); - if (!FLAG_flush_code) { + // TODO(1609) Currently incremental marker does not support code flushing. + if (!FLAG_flush_code || was_marked_incrementally_) { EnableCodeFlushing(false); return; } @@ -1068,11 +1666,14 @@ void MarkCompactCollector::PrepareForCodeFlushing() { return; } #endif + EnableCodeFlushing(true); // Ensure that empty descriptor array is marked. Method MarkDescriptorArray // relies on it being marked before any other descriptor array. - MarkObject(heap()->raw_unchecked_empty_descriptor_array()); + HeapObject* descriptor_array = heap()->empty_descriptor_array(); + MarkBit descriptor_array_mark = Marking::MarkBitFrom(descriptor_array); + MarkObject(descriptor_array, descriptor_array_mark); // Make sure we are not referencing the code from the stack. ASSERT(this == heap()->mark_compact_collector()); @@ -1089,7 +1690,7 @@ void MarkCompactCollector::PrepareForCodeFlushing() { heap()->isolate()->compilation_cache()->IterateFunctions(&visitor); heap()->isolate()->handle_scope_implementer()->Iterate(&visitor); - ProcessMarkingStack(); + ProcessMarkingDeque(); } @@ -1113,19 +1714,21 @@ class RootMarkingVisitor : public ObjectVisitor { // Replace flat cons strings in place. HeapObject* object = ShortCircuitConsString(p); - if (object->IsMarked()) return; + MarkBit mark_bit = Marking::MarkBitFrom(object); + if (mark_bit.Get()) return; Map* map = object->map(); // Mark the object. - collector_->SetMark(object); + collector_->SetMark(object, mark_bit); // Mark the map pointer and body, and push them on the marking stack. - collector_->MarkObject(map); + MarkBit map_mark = Marking::MarkBitFrom(map); + collector_->MarkObject(map, map_mark); StaticMarkingVisitor::IterateBody(map, object); // Mark all the objects reachable from the map and body. May leave // overflowed objects in the heap. - collector_->EmptyMarkingStack(); + collector_->EmptyMarkingDeque(); } MarkCompactCollector* collector_; @@ -1141,17 +1744,19 @@ class SymbolTableCleaner : public ObjectVisitor { virtual void VisitPointers(Object** start, Object** end) { // Visit all HeapObject pointers in [start, end). for (Object** p = start; p < end; p++) { - if ((*p)->IsHeapObject() && !HeapObject::cast(*p)->IsMarked()) { + Object* o = *p; + if (o->IsHeapObject() && + !Marking::MarkBitFrom(HeapObject::cast(o)).Get()) { // Check if the symbol being pruned is an external symbol. We need to // delete the associated external data as this symbol is going away. // Since no objects have yet been moved we can safely access the map of // the object. - if ((*p)->IsExternalString()) { + if (o->IsExternalString()) { heap_->FinalizeExternalString(String::cast(*p)); } - // Set the entry to null_value (as deleted). - *p = heap_->raw_unchecked_null_value(); + // Set the entry to the_hole_value (as deleted). + *p = heap_->the_hole_value(); pointers_removed_++; } } @@ -1172,8 +1777,7 @@ class SymbolTableCleaner : public ObjectVisitor { class MarkCompactWeakObjectRetainer : public WeakObjectRetainer { public: virtual Object* RetainAs(Object* object) { - MapWord first_word = HeapObject::cast(object)->map_word(); - if (first_word.IsMarked()) { + if (Marking::MarkBitFrom(HeapObject::cast(object)).Get()) { return object; } else { return NULL; @@ -1182,28 +1786,24 @@ class MarkCompactWeakObjectRetainer : public WeakObjectRetainer { }; -void MarkCompactCollector::MarkUnmarkedObject(HeapObject* object) { - ASSERT(!object->IsMarked()); +void MarkCompactCollector::ProcessNewlyMarkedObject(HeapObject* object) { + ASSERT(IsMarked(object)); ASSERT(HEAP->Contains(object)); if (object->IsMap()) { Map* map = Map::cast(object); - if (FLAG_cleanup_code_caches_at_gc) { - map->ClearCodeCache(heap()); - } - SetMark(map); + ClearCacheOnMap(map); // When map collection is enabled we have to mark through map's transitions // in a special way to make transition links weak. // Only maps for subclasses of JSReceiver can have transitions. STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); - if (FLAG_collect_maps && map->instance_type() >= FIRST_JS_RECEIVER_TYPE) { + if (collect_maps_ && map->instance_type() >= FIRST_JS_RECEIVER_TYPE) { MarkMapContents(map); } else { - marking_stack_.Push(map); + marking_deque_.PushBlack(map); } } else { - SetMark(object); - marking_stack_.Push(object); + marking_deque_.PushBlack(object); } } @@ -1212,12 +1812,17 @@ void MarkCompactCollector::MarkMapContents(Map* map) { // Mark prototype transitions array but don't push it into marking stack. // This will make references from it weak. We will clean dead prototype // transitions in ClearNonLiveTransitions. - FixedArray* prototype_transitions = map->unchecked_prototype_transitions(); - if (!prototype_transitions->IsMarked()) SetMark(prototype_transitions); + FixedArray* prototype_transitions = map->prototype_transitions(); + MarkBit mark = Marking::MarkBitFrom(prototype_transitions); + if (!mark.Get()) { + mark.Set(); + MemoryChunk::IncrementLiveBytesFromGC(prototype_transitions->address(), + prototype_transitions->Size()); + } - Object* raw_descriptor_array = - *HeapObject::RawField(map, - Map::kInstanceDescriptorsOrBitField3Offset); + Object** raw_descriptor_array_slot = + HeapObject::RawField(map, Map::kInstanceDescriptorsOrBitField3Offset); + Object* raw_descriptor_array = *raw_descriptor_array_slot; if (!raw_descriptor_array->IsSmi()) { MarkDescriptorArray( reinterpret_cast<DescriptorArray*>(raw_descriptor_array)); @@ -1231,24 +1836,36 @@ void MarkCompactCollector::MarkMapContents(Map* map) { Object** end_slot = HeapObject::RawField(map, Map::kPointerFieldsEndOffset); - StaticMarkingVisitor::VisitPointers(map->heap(), start_slot, end_slot); + StaticMarkingVisitor::VisitPointers(map->GetHeap(), start_slot, end_slot); +} + + +void MarkCompactCollector::MarkAccessorPairSlot(HeapObject* accessors, + int offset) { + Object** slot = HeapObject::RawField(accessors, offset); + HeapObject* accessor = HeapObject::cast(*slot); + if (accessor->IsMap()) return; + RecordSlot(slot, slot, accessor); + MarkObjectAndPush(accessor); } void MarkCompactCollector::MarkDescriptorArray( DescriptorArray* descriptors) { - if (descriptors->IsMarked()) return; + MarkBit descriptors_mark = Marking::MarkBitFrom(descriptors); + if (descriptors_mark.Get()) return; // Empty descriptor array is marked as a root before any maps are marked. - ASSERT(descriptors != HEAP->raw_unchecked_empty_descriptor_array()); - SetMark(descriptors); + ASSERT(descriptors != heap()->empty_descriptor_array()); + SetMark(descriptors, descriptors_mark); FixedArray* contents = reinterpret_cast<FixedArray*>( descriptors->get(DescriptorArray::kContentArrayIndex)); ASSERT(contents->IsHeapObject()); - ASSERT(!contents->IsMarked()); + ASSERT(!IsMarked(contents)); ASSERT(contents->IsFixedArray()); ASSERT(contents->length() >= 2); - SetMark(contents); + MarkBit contents_mark = Marking::MarkBitFrom(contents); + SetMark(contents, contents_mark); // Contents contains (value, details) pairs. If the details say that the type // of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION, // EXTERNAL_ARRAY_TRANSITION or NULL_DESCRIPTOR, we don't mark the value as @@ -1258,27 +1875,54 @@ void MarkCompactCollector::MarkDescriptorArray( // If the pair (value, details) at index i, i+1 is not // a transition or null descriptor, mark the value. PropertyDetails details(Smi::cast(contents->get(i + 1))); - if (details.type() < FIRST_PHANTOM_PROPERTY_TYPE) { - HeapObject* object = reinterpret_cast<HeapObject*>(contents->get(i)); - if (object->IsHeapObject() && !object->IsMarked()) { - SetMark(object); - marking_stack_.Push(object); - } + + Object** slot = contents->data_start() + i; + if (!(*slot)->IsHeapObject()) continue; + HeapObject* value = HeapObject::cast(*slot); + + RecordSlot(slot, slot, *slot); + + switch (details.type()) { + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + MarkObjectAndPush(value); + break; + case CALLBACKS: + if (!value->IsAccessorPair()) { + MarkObjectAndPush(value); + } else if (!MarkObjectWithoutPush(value)) { + MarkAccessorPairSlot(value, AccessorPair::kGetterOffset); + MarkAccessorPairSlot(value, AccessorPair::kSetterOffset); + } + break; + case ELEMENTS_TRANSITION: + // For maps with multiple elements transitions, the transition maps are + // stored in a FixedArray. Keep the fixed array alive but not the maps + // that it refers to. + if (value->IsFixedArray()) MarkObjectWithoutPush(value); + break; + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + case NULL_DESCRIPTOR: + break; } } // The DescriptorArray descriptors contains a pointer to its contents array, // but the contents array is already marked. - marking_stack_.Push(descriptors); + marking_deque_.PushBlack(descriptors); } void MarkCompactCollector::CreateBackPointers() { HeapObjectIterator iterator(heap()->map_space()); - for (HeapObject* next_object = iterator.next(); - next_object != NULL; next_object = iterator.next()) { - if (next_object->IsMap()) { // Could also be ByteArray on free list. + for (HeapObject* next_object = iterator.Next(); + next_object != NULL; next_object = iterator.Next()) { + if (next_object->IsMap()) { // Could also be FreeSpace object on free list. Map* map = Map::cast(next_object); - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); if (map->instance_type() >= FIRST_JS_RECEIVER_TYPE) { map->CreateBackPointers(); } else { @@ -1289,54 +1933,123 @@ void MarkCompactCollector::CreateBackPointers() { } -static int OverflowObjectSize(HeapObject* obj) { - // Recover the normal map pointer, it might be marked as live and - // overflowed. - MapWord map_word = obj->map_word(); - map_word.ClearMark(); - map_word.ClearOverflow(); - return obj->SizeFromMap(map_word.ToMap()); +// Fill the marking stack with overflowed objects returned by the given +// iterator. Stop when the marking stack is filled or the end of the space +// is reached, whichever comes first. +template<class T> +static void DiscoverGreyObjectsWithIterator(Heap* heap, + MarkingDeque* marking_deque, + T* it) { + // The caller should ensure that the marking stack is initially not full, + // so that we don't waste effort pointlessly scanning for objects. + ASSERT(!marking_deque->IsFull()); + + Map* filler_map = heap->one_pointer_filler_map(); + for (HeapObject* object = it->Next(); + object != NULL; + object = it->Next()) { + MarkBit markbit = Marking::MarkBitFrom(object); + if ((object->map() != filler_map) && Marking::IsGrey(markbit)) { + Marking::GreyToBlack(markbit); + MemoryChunk::IncrementLiveBytesFromGC(object->address(), object->Size()); + marking_deque->PushBlack(object); + if (marking_deque->IsFull()) return; + } + } } -class OverflowedObjectsScanner : public AllStatic { - public: - // Fill the marking stack with overflowed objects returned by the given - // iterator. Stop when the marking stack is filled or the end of the space - // is reached, whichever comes first. - template<class T> - static inline void ScanOverflowedObjects(MarkCompactCollector* collector, - T* it) { - // The caller should ensure that the marking stack is initially not full, - // so that we don't waste effort pointlessly scanning for objects. - ASSERT(!collector->marking_stack_.is_full()); - - for (HeapObject* object = it->next(); object != NULL; object = it->next()) { - if (object->IsOverflowed()) { - object->ClearOverflow(); - ASSERT(object->IsMarked()); - ASSERT(HEAP->Contains(object)); - collector->marking_stack_.Push(object); - if (collector->marking_stack_.is_full()) return; - } +static inline int MarkWordToObjectStarts(uint32_t mark_bits, int* starts); + + +static void DiscoverGreyObjectsOnPage(MarkingDeque* marking_deque, Page* p) { + ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); + ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + MarkBit::CellType* cells = p->markbits()->cells(); + + int last_cell_index = + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(p->ObjectAreaEnd()))); + + int cell_index = Page::kFirstUsedCell; + Address cell_base = p->ObjectAreaStart(); + + for (cell_index = Page::kFirstUsedCell; + cell_index < last_cell_index; + cell_index++, cell_base += 32 * kPointerSize) { + ASSERT((unsigned)cell_index == + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(cell_base)))); + + const MarkBit::CellType current_cell = cells[cell_index]; + if (current_cell == 0) continue; + + const MarkBit::CellType next_cell = cells[cell_index + 1]; + MarkBit::CellType grey_objects = current_cell & + ((current_cell >> 1) | (next_cell << (Bitmap::kBitsPerCell - 1))); + + int offset = 0; + while (grey_objects != 0) { + int trailing_zeros = CompilerIntrinsics::CountTrailingZeros(grey_objects); + grey_objects >>= trailing_zeros; + offset += trailing_zeros; + MarkBit markbit(&cells[cell_index], 1 << offset, false); + ASSERT(Marking::IsGrey(markbit)); + Marking::GreyToBlack(markbit); + Address addr = cell_base + offset * kPointerSize; + HeapObject* object = HeapObject::FromAddress(addr); + MemoryChunk::IncrementLiveBytesFromGC(object->address(), object->Size()); + marking_deque->PushBlack(object); + if (marking_deque->IsFull()) return; + offset += 2; + grey_objects >>= 2; } + + grey_objects >>= (Bitmap::kBitsPerCell - 1); } -}; +} + + +static void DiscoverGreyObjectsInSpace(Heap* heap, + MarkingDeque* marking_deque, + PagedSpace* space) { + if (!space->was_swept_conservatively()) { + HeapObjectIterator it(space); + DiscoverGreyObjectsWithIterator(heap, marking_deque, &it); + } else { + PageIterator it(space); + while (it.has_next()) { + Page* p = it.next(); + DiscoverGreyObjectsOnPage(marking_deque, p); + if (marking_deque->IsFull()) return; + } + } +} bool MarkCompactCollector::IsUnmarkedHeapObject(Object** p) { - return (*p)->IsHeapObject() && !HeapObject::cast(*p)->IsMarked(); + Object* o = *p; + if (!o->IsHeapObject()) return false; + HeapObject* heap_object = HeapObject::cast(o); + MarkBit mark = Marking::MarkBitFrom(heap_object); + return !mark.Get(); } void MarkCompactCollector::MarkSymbolTable() { - SymbolTable* symbol_table = heap()->raw_unchecked_symbol_table(); + SymbolTable* symbol_table = heap()->symbol_table(); // Mark the symbol table itself. - SetMark(symbol_table); + MarkBit symbol_table_mark = Marking::MarkBitFrom(symbol_table); + SetMark(symbol_table, symbol_table_mark); // Explicitly mark the prefix. MarkingVisitor marker(heap()); symbol_table->IteratePrefix(&marker); - ProcessMarkingStack(); + ProcessMarkingDeque(); } @@ -1349,9 +2062,9 @@ void MarkCompactCollector::MarkRoots(RootMarkingVisitor* visitor) { MarkSymbolTable(); // There may be overflowed objects in the heap. Visit them now. - while (marking_stack_.overflowed()) { - RefillMarkingStack(); - EmptyMarkingStack(); + while (marking_deque_.overflowed()) { + RefillMarkingDeque(); + EmptyMarkingDeque(); } } @@ -1369,9 +2082,13 @@ void MarkCompactCollector::MarkObjectGroups() { bool group_marked = false; for (size_t j = 0; j < entry->length_; j++) { Object* object = *objects[j]; - if (object->IsHeapObject() && HeapObject::cast(object)->IsMarked()) { - group_marked = true; - break; + if (object->IsHeapObject()) { + HeapObject* heap_object = HeapObject::cast(object); + MarkBit mark = Marking::MarkBitFrom(heap_object); + if (mark.Get()) { + group_marked = true; + break; + } } } @@ -1380,17 +2097,21 @@ void MarkCompactCollector::MarkObjectGroups() { continue; } - // An object in the group is marked, so mark all heap objects in - // the group. + // An object in the group is marked, so mark as grey all white heap + // objects in the group. for (size_t j = 0; j < entry->length_; ++j) { - if ((*objects[j])->IsHeapObject()) { - MarkObject(HeapObject::cast(*objects[j])); + Object* object = *objects[j]; + if (object->IsHeapObject()) { + HeapObject* heap_object = HeapObject::cast(object); + MarkBit mark = Marking::MarkBitFrom(heap_object); + MarkObject(heap_object, mark); } } - // Once the entire group has been marked, dispose it because it's - // not needed anymore. + // Once the entire group has been colored grey, set the object group + // to NULL so it won't be processed again. entry->Dispose(); + object_groups->at(i) = NULL; } object_groups->Rewind(last); } @@ -1405,7 +2126,7 @@ void MarkCompactCollector::MarkImplicitRefGroups() { ImplicitRefGroup* entry = ref_groups->at(i); ASSERT(entry != NULL); - if (!(*entry->parent_)->IsMarked()) { + if (!IsMarked(*entry->parent_)) { (*ref_groups)[last++] = entry; continue; } @@ -1414,7 +2135,9 @@ void MarkCompactCollector::MarkImplicitRefGroups() { // A parent object is marked, so mark all child heap objects. for (size_t j = 0; j < entry->length_; ++j) { if ((*children[j])->IsHeapObject()) { - MarkObject(HeapObject::cast(*children[j])); + HeapObject* child = HeapObject::cast(*children[j]); + MarkBit mark = Marking::MarkBitFrom(child); + MarkObject(child, mark); } } @@ -1430,21 +2153,17 @@ void MarkCompactCollector::MarkImplicitRefGroups() { // Before: the marking stack contains zero or more heap object pointers. // After: the marking stack is empty, and all objects reachable from the // marking stack have been marked, or are overflowed in the heap. -void MarkCompactCollector::EmptyMarkingStack() { - while (!marking_stack_.is_empty()) { - while (!marking_stack_.is_empty()) { - HeapObject* object = marking_stack_.Pop(); +void MarkCompactCollector::EmptyMarkingDeque() { + while (!marking_deque_.IsEmpty()) { + while (!marking_deque_.IsEmpty()) { + HeapObject* object = marking_deque_.Pop(); ASSERT(object->IsHeapObject()); ASSERT(heap()->Contains(object)); - ASSERT(object->IsMarked()); - ASSERT(!object->IsOverflowed()); + ASSERT(Marking::IsBlack(Marking::MarkBitFrom(object))); - // Because the object is marked, we have to recover the original map - // pointer and use it to mark the object's body. - MapWord map_word = object->map_word(); - map_word.ClearMark(); - Map* map = map_word.ToMap(); - MarkObject(map); + Map* map = object->map(); + MarkBit map_mark = Marking::MarkBitFrom(map); + MarkObject(map, map_mark); StaticMarkingVisitor::IterateBody(map, object); } @@ -1461,39 +2180,45 @@ void MarkCompactCollector::EmptyMarkingStack() { // before sweeping completes. If sweeping completes, there are no remaining // overflowed objects in the heap so the overflow flag on the markings stack // is cleared. -void MarkCompactCollector::RefillMarkingStack() { - ASSERT(marking_stack_.overflowed()); - - SemiSpaceIterator new_it(heap()->new_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &new_it); - if (marking_stack_.is_full()) return; - - HeapObjectIterator old_pointer_it(heap()->old_pointer_space(), - &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &old_pointer_it); - if (marking_stack_.is_full()) return; - - HeapObjectIterator old_data_it(heap()->old_data_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &old_data_it); - if (marking_stack_.is_full()) return; - - HeapObjectIterator code_it(heap()->code_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &code_it); - if (marking_stack_.is_full()) return; - - HeapObjectIterator map_it(heap()->map_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &map_it); - if (marking_stack_.is_full()) return; - - HeapObjectIterator cell_it(heap()->cell_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &cell_it); - if (marking_stack_.is_full()) return; - - LargeObjectIterator lo_it(heap()->lo_space(), &OverflowObjectSize); - OverflowedObjectsScanner::ScanOverflowedObjects(this, &lo_it); - if (marking_stack_.is_full()) return; - - marking_stack_.clear_overflowed(); +void MarkCompactCollector::RefillMarkingDeque() { + ASSERT(marking_deque_.overflowed()); + + SemiSpaceIterator new_it(heap()->new_space()); + DiscoverGreyObjectsWithIterator(heap(), &marking_deque_, &new_it); + if (marking_deque_.IsFull()) return; + + DiscoverGreyObjectsInSpace(heap(), + &marking_deque_, + heap()->old_pointer_space()); + if (marking_deque_.IsFull()) return; + + DiscoverGreyObjectsInSpace(heap(), + &marking_deque_, + heap()->old_data_space()); + if (marking_deque_.IsFull()) return; + + DiscoverGreyObjectsInSpace(heap(), + &marking_deque_, + heap()->code_space()); + if (marking_deque_.IsFull()) return; + + DiscoverGreyObjectsInSpace(heap(), + &marking_deque_, + heap()->map_space()); + if (marking_deque_.IsFull()) return; + + DiscoverGreyObjectsInSpace(heap(), + &marking_deque_, + heap()->cell_space()); + if (marking_deque_.IsFull()) return; + + LargeObjectIterator lo_it(heap()->lo_space()); + DiscoverGreyObjectsWithIterator(heap(), + &marking_deque_, + &lo_it); + if (marking_deque_.IsFull()) return; + + marking_deque_.ClearOverflowed(); } @@ -1501,23 +2226,23 @@ void MarkCompactCollector::RefillMarkingStack() { // stack. Before: the marking stack contains zero or more heap object // pointers. After: the marking stack is empty and there are no overflowed // objects in the heap. -void MarkCompactCollector::ProcessMarkingStack() { - EmptyMarkingStack(); - while (marking_stack_.overflowed()) { - RefillMarkingStack(); - EmptyMarkingStack(); +void MarkCompactCollector::ProcessMarkingDeque() { + EmptyMarkingDeque(); + while (marking_deque_.overflowed()) { + RefillMarkingDeque(); + EmptyMarkingDeque(); } } void MarkCompactCollector::ProcessExternalMarking() { bool work_to_do = true; - ASSERT(marking_stack_.is_empty()); + ASSERT(marking_deque_.IsEmpty()); while (work_to_do) { MarkObjectGroups(); MarkImplicitRefGroups(); - work_to_do = !marking_stack_.is_empty(); - ProcessMarkingStack(); + work_to_do = !marking_deque_.IsEmpty(); + ProcessMarkingDeque(); } } @@ -1529,19 +2254,64 @@ void MarkCompactCollector::MarkLiveObjects() { // with the C stack limit check. PostponeInterruptsScope postpone(heap()->isolate()); + bool incremental_marking_overflowed = false; + IncrementalMarking* incremental_marking = heap_->incremental_marking(); + if (was_marked_incrementally_) { + // Finalize the incremental marking and check whether we had an overflow. + // Both markers use grey color to mark overflowed objects so + // non-incremental marker can deal with them as if overflow + // occured during normal marking. + // But incremental marker uses a separate marking deque + // so we have to explicitly copy it's overflow state. + incremental_marking->Finalize(); + incremental_marking_overflowed = + incremental_marking->marking_deque()->overflowed(); + incremental_marking->marking_deque()->ClearOverflowed(); + } else { + // Abort any pending incremental activities e.g. incremental sweeping. + incremental_marking->Abort(); + } + #ifdef DEBUG ASSERT(state_ == PREPARE_GC); state_ = MARK_LIVE_OBJECTS; #endif - // The to space contains live objects, the from space is used as a marking - // stack. - marking_stack_.Initialize(heap()->new_space()->FromSpaceLow(), - heap()->new_space()->FromSpaceHigh()); + // The to space contains live objects, a page in from space is used as a + // marking stack. + Address marking_deque_start = heap()->new_space()->FromSpacePageLow(); + Address marking_deque_end = heap()->new_space()->FromSpacePageHigh(); + if (FLAG_force_marking_deque_overflows) { + marking_deque_end = marking_deque_start + 64 * kPointerSize; + } + marking_deque_.Initialize(marking_deque_start, + marking_deque_end); + ASSERT(!marking_deque_.overflowed()); - ASSERT(!marking_stack_.overflowed()); + if (incremental_marking_overflowed) { + // There are overflowed objects left in the heap after incremental marking. + marking_deque_.SetOverflowed(); + } PrepareForCodeFlushing(); + if (was_marked_incrementally_) { + // There is no write barrier on cells so we have to scan them now at the end + // of the incremental marking. + { + HeapObjectIterator cell_iterator(heap()->cell_space()); + HeapObject* cell; + while ((cell = cell_iterator.Next()) != NULL) { + ASSERT(cell->IsJSGlobalPropertyCell()); + if (IsMarked(cell)) { + int offset = JSGlobalPropertyCell::kValueOffset; + StaticMarkingVisitor::VisitPointer( + heap(), + reinterpret_cast<Object**>(cell->address() + offset)); + } + } + } + } + RootMarkingVisitor root_visitor(heap()); MarkRoots(&root_visitor); @@ -1560,15 +2330,20 @@ void MarkCompactCollector::MarkLiveObjects() { &IsUnmarkedHeapObject); // Then we mark the objects and process the transitive closure. heap()->isolate()->global_handles()->IterateWeakRoots(&root_visitor); - while (marking_stack_.overflowed()) { - RefillMarkingStack(); - EmptyMarkingStack(); + while (marking_deque_.overflowed()) { + RefillMarkingDeque(); + EmptyMarkingDeque(); } // Repeat host application specific marking to mark unmarked objects // reachable from the weak roots. ProcessExternalMarking(); + AfterMarking(); +} + + +void MarkCompactCollector::AfterMarking() { // Object literal map caches reference symbols (cache keys) and maps // (cache values). At this point still useful maps have already been // marked. Mark the keys for the alive values before we process the @@ -1578,7 +2353,7 @@ void MarkCompactCollector::MarkLiveObjects() { // Prune the symbol table removing all symbols only pointed to by the // symbol table. Cannot use symbol_table() here because the symbol // table is marked. - SymbolTable* symbol_table = heap()->raw_unchecked_symbol_table(); + SymbolTable* symbol_table = heap()->symbol_table(); SymbolTableCleaner v(heap()); symbol_table->IterateElements(&v); symbol_table->ElementsRemoved(v.PointersRemoved()); @@ -1598,8 +2373,10 @@ void MarkCompactCollector::MarkLiveObjects() { code_flusher_->ProcessCandidates(); } - // Clean up dead objects from the runtime profiler. - heap()->isolate()->runtime_profiler()->RemoveDeadSamples(); + if (!FLAG_watch_ic_patching) { + // Clean up dead objects from the runtime profiler. + heap()->isolate()->runtime_profiler()->RemoveDeadSamples(); + } } @@ -1607,13 +2384,13 @@ void MarkCompactCollector::ProcessMapCaches() { Object* raw_context = heap()->global_contexts_list_; while (raw_context != heap()->undefined_value()) { Context* context = reinterpret_cast<Context*>(raw_context); - if (context->IsMarked()) { + if (IsMarked(context)) { HeapObject* raw_map_cache = HeapObject::cast(context->get(Context::MAP_CACHE_INDEX)); // A map cache may be reachable from the stack. In this case // it's already transitively marked and it's too late to clean // up its parts. - if (!raw_map_cache->IsMarked() && + if (!IsMarked(raw_map_cache) && raw_map_cache != heap()->undefined_value()) { MapCache* map_cache = reinterpret_cast<MapCache*>(raw_map_cache); int existing_elements = map_cache->NumberOfElements(); @@ -1623,17 +2400,16 @@ void MarkCompactCollector::ProcessMapCaches() { i += MapCache::kEntrySize) { Object* raw_key = map_cache->get(i); if (raw_key == heap()->undefined_value() || - raw_key == heap()->null_value()) continue; + raw_key == heap()->the_hole_value()) continue; STATIC_ASSERT(MapCache::kEntrySize == 2); Object* raw_map = map_cache->get(i + 1); - if (raw_map->IsHeapObject() && - HeapObject::cast(raw_map)->IsMarked()) { + if (raw_map->IsHeapObject() && IsMarked(raw_map)) { ++used_elements; } else { // Delete useless entries with unmarked maps. ASSERT(raw_map->IsMap()); - map_cache->set_null_unchecked(heap(), i); - map_cache->set_null_unchecked(heap(), i + 1); + map_cache->set_the_hole(i); + map_cache->set_the_hole(i + 1); } } if (used_elements == 0) { @@ -1643,64 +2419,38 @@ void MarkCompactCollector::ProcessMapCaches() { // extra complexity during GC. We rely on subsequent cache // usages (EnsureCapacity) to do this. map_cache->ElementsRemoved(existing_elements - used_elements); - MarkObject(map_cache); + MarkBit map_cache_markbit = Marking::MarkBitFrom(map_cache); + MarkObject(map_cache, map_cache_markbit); } } } // Move to next element in the list. raw_context = context->get(Context::NEXT_CONTEXT_LINK); } - ProcessMarkingStack(); -} - - -#ifdef DEBUG -void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) { - live_bytes_ += obj->Size(); - if (heap()->new_space()->Contains(obj)) { - live_young_objects_size_ += obj->Size(); - } else if (heap()->map_space()->Contains(obj)) { - ASSERT(obj->IsMap()); - live_map_objects_size_ += obj->Size(); - } else if (heap()->cell_space()->Contains(obj)) { - ASSERT(obj->IsJSGlobalPropertyCell()); - live_cell_objects_size_ += obj->Size(); - } else if (heap()->old_pointer_space()->Contains(obj)) { - live_old_pointer_objects_size_ += obj->Size(); - } else if (heap()->old_data_space()->Contains(obj)) { - live_old_data_objects_size_ += obj->Size(); - } else if (heap()->code_space()->Contains(obj)) { - live_code_objects_size_ += obj->Size(); - } else if (heap()->lo_space()->Contains(obj)) { - live_lo_objects_size_ += obj->Size(); - } else { - UNREACHABLE(); - } + ProcessMarkingDeque(); } -#endif // DEBUG -void MarkCompactCollector::SweepLargeObjectSpace() { -#ifdef DEBUG - ASSERT(state_ == MARK_LIVE_OBJECTS); - state_ = - compacting_collection_ ? ENCODE_FORWARDING_ADDRESSES : SWEEP_SPACES; -#endif - // Deallocate unmarked objects and clear marked bits for marked objects. - heap()->lo_space()->FreeUnmarkedObjects(); -} +void MarkCompactCollector::ReattachInitialMaps() { + HeapObjectIterator map_iterator(heap()->map_space()); + for (HeapObject* obj = map_iterator.Next(); + obj != NULL; + obj = map_iterator.Next()) { + if (obj->IsFreeSpace()) continue; + Map* map = Map::cast(obj); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + if (map->instance_type() < FIRST_JS_RECEIVER_TYPE) continue; -// Safe to use during marking phase only. -bool MarkCompactCollector::SafeIsMap(HeapObject* object) { - MapWord metamap = object->map_word(); - metamap.ClearMark(); - return metamap.ToMap()->instance_type() == MAP_TYPE; + if (map->attached_to_shared_function_info()) { + JSFunction::cast(map->constructor())->shared()->AttachInitialMap(map); + } + } } void MarkCompactCollector::ClearNonLiveTransitions() { - HeapObjectIterator map_iterator(heap()->map_space(), &SizeOfMarkedObject); + HeapObjectIterator map_iterator(heap()->map_space()); // Iterate over the map space, setting map transitions that go from // a marked map to an unmarked map to null transitions. At the same time, // set all the prototype fields of maps back to their original value, @@ -1711,110 +2461,125 @@ void MarkCompactCollector::ClearNonLiveTransitions() { // scan the descriptor arrays of those maps, not all maps. // All of these actions are carried out only on maps of JSObjects // and related subtypes. - for (HeapObject* obj = map_iterator.next(); - obj != NULL; obj = map_iterator.next()) { + for (HeapObject* obj = map_iterator.Next(); + obj != NULL; obj = map_iterator.Next()) { Map* map = reinterpret_cast<Map*>(obj); - if (!map->IsMarked() && map->IsByteArray()) continue; + MarkBit map_mark = Marking::MarkBitFrom(map); + if (map->IsFreeSpace()) continue; - ASSERT(SafeIsMap(map)); + ASSERT(map->IsMap()); // Only JSObject and subtypes have map transitions and back pointers. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - if (map->instance_type() < FIRST_JS_RECEIVER_TYPE) continue; + STATIC_ASSERT(LAST_TYPE == LAST_JS_OBJECT_TYPE); + if (map->instance_type() < FIRST_JS_OBJECT_TYPE) continue; - if (map->IsMarked() && map->attached_to_shared_function_info()) { + if (map_mark.Get() && + map->attached_to_shared_function_info()) { // This map is used for inobject slack tracking and has been detached // from SharedFunctionInfo during the mark phase. // Since it survived the GC, reattach it now. map->unchecked_constructor()->unchecked_shared()->AttachInitialMap(map); } - // Clear dead prototype transitions. - int number_of_transitions = map->NumberOfProtoTransitions(); - if (number_of_transitions > 0) { - FixedArray* prototype_transitions = - map->unchecked_prototype_transitions(); - int new_number_of_transitions = 0; - const int header = Map::kProtoTransitionHeaderSize; - const int proto_offset = - header + Map::kProtoTransitionPrototypeOffset; - const int map_offset = header + Map::kProtoTransitionMapOffset; - const int step = Map::kProtoTransitionElementsPerEntry; - for (int i = 0; i < number_of_transitions; i++) { - Object* prototype = prototype_transitions->get(proto_offset + i * step); - Object* cached_map = prototype_transitions->get(map_offset + i * step); - if (HeapObject::cast(prototype)->IsMarked() && - HeapObject::cast(cached_map)->IsMarked()) { - if (new_number_of_transitions != i) { - prototype_transitions->set_unchecked( - heap_, - proto_offset + new_number_of_transitions * step, - prototype, - UPDATE_WRITE_BARRIER); - prototype_transitions->set_unchecked( - heap_, - map_offset + new_number_of_transitions * step, - cached_map, - SKIP_WRITE_BARRIER); - } - new_number_of_transitions++; - } - } + ClearNonLivePrototypeTransitions(map); + ClearNonLiveMapTransitions(map, map_mark); + } +} - // Fill slots that became free with undefined value. - Object* undefined = heap()->raw_unchecked_undefined_value(); - for (int i = new_number_of_transitions * step; - i < number_of_transitions * step; - i++) { - prototype_transitions->set_unchecked(heap_, - header + i, - undefined, - SKIP_WRITE_BARRIER); - } - map->SetNumberOfProtoTransitions(new_number_of_transitions); - } - - // Follow the chain of back pointers to find the prototype. - Map* current = map; - while (SafeIsMap(current)) { - current = reinterpret_cast<Map*>(current->prototype()); - ASSERT(current->IsHeapObject()); - } - Object* real_prototype = current; - - // Follow back pointers, setting them to prototype, - // clearing map transitions when necessary. - current = map; - bool on_dead_path = !current->IsMarked(); - Object* next; - while (SafeIsMap(current)) { - next = current->prototype(); - // There should never be a dead map above a live map. - ASSERT(on_dead_path || current->IsMarked()); - - // A live map above a dead map indicates a dead transition. - // This test will always be false on the first iteration. - if (on_dead_path && current->IsMarked()) { - on_dead_path = false; - current->ClearNonLiveTransitions(heap(), real_prototype); + +void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) { + int number_of_transitions = map->NumberOfProtoTransitions(); + FixedArray* prototype_transitions = map->prototype_transitions(); + + int new_number_of_transitions = 0; + const int header = Map::kProtoTransitionHeaderSize; + const int proto_offset = header + Map::kProtoTransitionPrototypeOffset; + const int map_offset = header + Map::kProtoTransitionMapOffset; + const int step = Map::kProtoTransitionElementsPerEntry; + for (int i = 0; i < number_of_transitions; i++) { + Object* prototype = prototype_transitions->get(proto_offset + i * step); + Object* cached_map = prototype_transitions->get(map_offset + i * step); + if (IsMarked(prototype) && IsMarked(cached_map)) { + int proto_index = proto_offset + new_number_of_transitions * step; + int map_index = map_offset + new_number_of_transitions * step; + if (new_number_of_transitions != i) { + prototype_transitions->set_unchecked( + heap_, + proto_index, + prototype, + UPDATE_WRITE_BARRIER); + prototype_transitions->set_unchecked( + heap_, + map_index, + cached_map, + SKIP_WRITE_BARRIER); } - *HeapObject::RawField(current, Map::kPrototypeOffset) = - real_prototype; - current = reinterpret_cast<Map*>(next); + Object** slot = + HeapObject::RawField(prototype_transitions, + FixedArray::OffsetOfElementAt(proto_index)); + RecordSlot(slot, slot, prototype); + new_number_of_transitions++; } } + + if (new_number_of_transitions != number_of_transitions) { + map->SetNumberOfProtoTransitions(new_number_of_transitions); + } + + // Fill slots that became free with undefined value. + for (int i = new_number_of_transitions * step; + i < number_of_transitions * step; + i++) { + prototype_transitions->set_undefined(heap_, header + i); + } +} + + +void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map, + MarkBit map_mark) { + // Follow the chain of back pointers to find the prototype. + Object* real_prototype = map; + while (real_prototype->IsMap()) { + real_prototype = Map::cast(real_prototype)->prototype(); + ASSERT(real_prototype->IsHeapObject()); + } + + // Follow back pointers, setting them to prototype, clearing map transitions + // when necessary. + Map* current = map; + bool current_is_alive = map_mark.Get(); + bool on_dead_path = !current_is_alive; + while (current->IsMap()) { + Object* next = current->prototype(); + // There should never be a dead map above a live map. + ASSERT(on_dead_path || current_is_alive); + + // A live map above a dead map indicates a dead transition. This test will + // always be false on the first iteration. + if (on_dead_path && current_is_alive) { + on_dead_path = false; + current->ClearNonLiveTransitions(heap(), real_prototype); + } + + Object** slot = HeapObject::RawField(current, Map::kPrototypeOffset); + *slot = real_prototype; + if (current_is_alive) RecordSlot(slot, slot, real_prototype); + + current = reinterpret_cast<Map*>(next); + current_is_alive = Marking::MarkBitFrom(current).Get(); + } } void MarkCompactCollector::ProcessWeakMaps() { Object* weak_map_obj = encountered_weak_maps(); while (weak_map_obj != Smi::FromInt(0)) { - ASSERT(HeapObject::cast(weak_map_obj)->IsMarked()); + ASSERT(MarkCompactCollector::IsMarked(HeapObject::cast(weak_map_obj))); JSWeakMap* weak_map = reinterpret_cast<JSWeakMap*>(weak_map_obj); - ObjectHashTable* table = weak_map->unchecked_table(); + ObjectHashTable* table = ObjectHashTable::cast(weak_map->table()); for (int i = 0; i < table->Capacity(); i++) { - if (HeapObject::cast(table->KeyAt(i))->IsMarked()) { + if (MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) { Object* value = table->get(table->EntryToValueIndex(i)); - StaticMarkingVisitor::MarkObjectByPointer(heap(), &value); + StaticMarkingVisitor::VisitPointer(heap(), &value); table->set_unchecked(heap(), table->EntryToValueIndex(i), value, @@ -1829,12 +2594,12 @@ void MarkCompactCollector::ProcessWeakMaps() { void MarkCompactCollector::ClearWeakMaps() { Object* weak_map_obj = encountered_weak_maps(); while (weak_map_obj != Smi::FromInt(0)) { - ASSERT(HeapObject::cast(weak_map_obj)->IsMarked()); + ASSERT(MarkCompactCollector::IsMarked(HeapObject::cast(weak_map_obj))); JSWeakMap* weak_map = reinterpret_cast<JSWeakMap*>(weak_map_obj); - ObjectHashTable* table = weak_map->unchecked_table(); + ObjectHashTable* table = ObjectHashTable::cast(weak_map->table()); for (int i = 0; i < table->Capacity(); i++) { - if (!HeapObject::cast(table->KeyAt(i))->IsMarked()) { - table->RemoveEntry(i, heap()); + if (!MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) { + table->RemoveEntry(i); } } weak_map_obj = weak_map->next(); @@ -1843,316 +2608,97 @@ void MarkCompactCollector::ClearWeakMaps() { set_encountered_weak_maps(Smi::FromInt(0)); } -// ------------------------------------------------------------------------- -// Phase 2: Encode forwarding addresses. -// When compacting, forwarding addresses for objects in old space and map -// space are encoded in their map pointer word (along with an encoding of -// their map pointers). -// -// The excact encoding is described in the comments for class MapWord in -// objects.h. + +// We scavange new space simultaneously with sweeping. This is done in two +// passes. // -// An address range [start, end) can have both live and non-live objects. -// Maximal non-live regions are marked so they can be skipped on subsequent -// sweeps of the heap. A distinguished map-pointer encoding is used to mark -// free regions of one-word size (in which case the next word is the start -// of a live object). A second distinguished map-pointer encoding is used -// to mark free regions larger than one word, and the size of the free -// region (including the first word) is written to the second word of the -// region. +// The first pass migrates all alive objects from one semispace to another or +// promotes them to old space. Forwarding address is written directly into +// first word of object without any encoding. If object is dead we write +// NULL as a forwarding address. // -// Any valid map page offset must lie in the object area of the page, so map -// page offsets less than Page::kObjectStartOffset are invalid. We use a -// pair of distinguished invalid map encodings (for single word and multiple -// words) to indicate free regions in the page found during computation of -// forwarding addresses and skipped over in subsequent sweeps. - - -// Encode a free region, defined by the given start address and size, in the -// first word or two of the region. -void EncodeFreeRegion(Address free_start, int free_size) { - ASSERT(free_size >= kIntSize); - if (free_size == kIntSize) { - Memory::uint32_at(free_start) = MarkCompactCollector::kSingleFreeEncoding; - } else { - ASSERT(free_size >= 2 * kIntSize); - Memory::uint32_at(free_start) = MarkCompactCollector::kMultiFreeEncoding; - Memory::int_at(free_start + kIntSize) = free_size; - } +// The second pass updates pointers to new space in all spaces. It is possible +// to encounter pointers to dead new space objects during traversal of pointers +// to new space. We should clear them to avoid encountering them during next +// pointer iteration. This is an issue if the store buffer overflows and we +// have to scan the entire old space, including dead objects, looking for +// pointers to new space. +void MarkCompactCollector::MigrateObject(Address dst, + Address src, + int size, + AllocationSpace dest) { + HEAP_PROFILE(heap(), ObjectMoveEvent(src, dst)); + if (dest == OLD_POINTER_SPACE || dest == LO_SPACE) { + Address src_slot = src; + Address dst_slot = dst; + ASSERT(IsAligned(size, kPointerSize)); + + for (int remaining = size / kPointerSize; remaining > 0; remaining--) { + Object* value = Memory::Object_at(src_slot); + + Memory::Object_at(dst_slot) = value; + + if (heap_->InNewSpace(value)) { + heap_->store_buffer()->Mark(dst_slot); + } else if (value->IsHeapObject() && IsOnEvacuationCandidate(value)) { + SlotsBuffer::AddTo(&slots_buffer_allocator_, + &migration_slots_buffer_, + reinterpret_cast<Object**>(dst_slot), + SlotsBuffer::IGNORE_OVERFLOW); + } -#ifdef DEBUG - // Zap the body of the free region. - if (FLAG_enable_slow_asserts) { - for (int offset = 2 * kIntSize; - offset < free_size; - offset += kPointerSize) { - Memory::Address_at(free_start + offset) = kZapValue; + src_slot += kPointerSize; + dst_slot += kPointerSize; } - } -#endif -} - - -// Try to promote all objects in new space. Heap numbers and sequential -// strings are promoted to the code space, large objects to large object space, -// and all others to the old space. -inline MaybeObject* MCAllocateFromNewSpace(Heap* heap, - HeapObject* object, - int object_size) { - MaybeObject* forwarded; - if (object_size > heap->MaxObjectSizeInPagedSpace()) { - forwarded = Failure::Exception(); - } else { - OldSpace* target_space = heap->TargetSpace(object); - ASSERT(target_space == heap->old_pointer_space() || - target_space == heap->old_data_space()); - forwarded = target_space->MCAllocateRaw(object_size); - } - Object* result; - if (!forwarded->ToObject(&result)) { - result = heap->new_space()->MCAllocateRaw(object_size)->ToObjectUnchecked(); - } - return result; -} - - -// Allocation functions for the paged spaces call the space's MCAllocateRaw. -MUST_USE_RESULT inline MaybeObject* MCAllocateFromOldPointerSpace( - Heap *heap, - HeapObject* ignore, - int object_size) { - return heap->old_pointer_space()->MCAllocateRaw(object_size); -} - - -MUST_USE_RESULT inline MaybeObject* MCAllocateFromOldDataSpace( - Heap* heap, - HeapObject* ignore, - int object_size) { - return heap->old_data_space()->MCAllocateRaw(object_size); -} - - -MUST_USE_RESULT inline MaybeObject* MCAllocateFromCodeSpace( - Heap* heap, - HeapObject* ignore, - int object_size) { - return heap->code_space()->MCAllocateRaw(object_size); -} - - -MUST_USE_RESULT inline MaybeObject* MCAllocateFromMapSpace( - Heap* heap, - HeapObject* ignore, - int object_size) { - return heap->map_space()->MCAllocateRaw(object_size); -} - - -MUST_USE_RESULT inline MaybeObject* MCAllocateFromCellSpace( - Heap* heap, HeapObject* ignore, int object_size) { - return heap->cell_space()->MCAllocateRaw(object_size); -} - - -// The forwarding address is encoded at the same offset as the current -// to-space object, but in from space. -inline void EncodeForwardingAddressInNewSpace(Heap* heap, - HeapObject* old_object, - int object_size, - Object* new_object, - int* ignored) { - int offset = - heap->new_space()->ToSpaceOffsetForAddress(old_object->address()); - Memory::Address_at(heap->new_space()->FromSpaceLow() + offset) = - HeapObject::cast(new_object)->address(); -} - - -// The forwarding address is encoded in the map pointer of the object as an -// offset (in terms of live bytes) from the address of the first live object -// in the page. -inline void EncodeForwardingAddressInPagedSpace(Heap* heap, - HeapObject* old_object, - int object_size, - Object* new_object, - int* offset) { - // Record the forwarding address of the first live object if necessary. - if (*offset == 0) { - Page::FromAddress(old_object->address())->mc_first_forwarded = - HeapObject::cast(new_object)->address(); - } - - MapWord encoding = - MapWord::EncodeAddress(old_object->map()->address(), *offset); - old_object->set_map_word(encoding); - *offset += object_size; - ASSERT(*offset <= Page::kObjectAreaSize); -} - -// Most non-live objects are ignored. -inline void IgnoreNonLiveObject(HeapObject* object, Isolate* isolate) {} + if (compacting_ && HeapObject::FromAddress(dst)->IsJSFunction()) { + Address code_entry_slot = dst + JSFunction::kCodeEntryOffset; + Address code_entry = Memory::Address_at(code_entry_slot); - -// Function template that, given a range of addresses (eg, a semispace or a -// paged space page), iterates through the objects in the range to clear -// mark bits and compute and encode forwarding addresses. As a side effect, -// maximal free chunks are marked so that they can be skipped on subsequent -// sweeps. -// -// The template parameters are an allocation function, a forwarding address -// encoding function, and a function to process non-live objects. -template<MarkCompactCollector::AllocationFunction Alloc, - MarkCompactCollector::EncodingFunction Encode, - MarkCompactCollector::ProcessNonLiveFunction ProcessNonLive> -inline void EncodeForwardingAddressesInRange(MarkCompactCollector* collector, - Address start, - Address end, - int* offset) { - // The start address of the current free region while sweeping the space. - // This address is set when a transition from live to non-live objects is - // encountered. A value (an encoding of the 'next free region' pointer) - // is written to memory at this address when a transition from non-live to - // live objects is encountered. - Address free_start = NULL; - - // A flag giving the state of the previously swept object. Initially true - // to ensure that free_start is initialized to a proper address before - // trying to write to it. - bool is_prev_alive = true; - - int object_size; // Will be set on each iteration of the loop. - for (Address current = start; current < end; current += object_size) { - HeapObject* object = HeapObject::FromAddress(current); - if (object->IsMarked()) { - object->ClearMark(); - collector->tracer()->decrement_marked_count(); - object_size = object->Size(); - - Object* forwarded = - Alloc(collector->heap(), object, object_size)->ToObjectUnchecked(); - Encode(collector->heap(), object, object_size, forwarded, offset); - -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("forward %p -> %p.\n", object->address(), - HeapObject::cast(forwarded)->address()); + if (Page::FromAddress(code_entry)->IsEvacuationCandidate()) { + SlotsBuffer::AddTo(&slots_buffer_allocator_, + &migration_slots_buffer_, + SlotsBuffer::CODE_ENTRY_SLOT, + code_entry_slot, + SlotsBuffer::IGNORE_OVERFLOW); } -#endif - if (!is_prev_alive) { // Transition from non-live to live. - EncodeFreeRegion(free_start, static_cast<int>(current - free_start)); - is_prev_alive = true; - } - } else { // Non-live object. - object_size = object->Size(); - ProcessNonLive(object, collector->heap()->isolate()); - if (is_prev_alive) { // Transition from live to non-live. - free_start = current; - is_prev_alive = false; - } - LiveObjectList::ProcessNonLive(object); } - } - - // If we ended on a free region, mark it. - if (!is_prev_alive) { - EncodeFreeRegion(free_start, static_cast<int>(end - free_start)); - } -} - - -// Functions to encode the forwarding pointers in each compactable space. -void MarkCompactCollector::EncodeForwardingAddressesInNewSpace() { - int ignored; - EncodeForwardingAddressesInRange<MCAllocateFromNewSpace, - EncodeForwardingAddressInNewSpace, - IgnoreNonLiveObject>( - this, - heap()->new_space()->bottom(), - heap()->new_space()->top(), - &ignored); -} - - -template<MarkCompactCollector::AllocationFunction Alloc, - MarkCompactCollector::ProcessNonLiveFunction ProcessNonLive> -void MarkCompactCollector::EncodeForwardingAddressesInPagedSpace( - PagedSpace* space) { - PageIterator it(space, PageIterator::PAGES_IN_USE); - while (it.has_next()) { - Page* p = it.next(); - - // The offset of each live object in the page from the first live object - // in the page. - int offset = 0; - EncodeForwardingAddressesInRange<Alloc, - EncodeForwardingAddressInPagedSpace, - ProcessNonLive>( - this, - p->ObjectAreaStart(), - p->AllocationTop(), - &offset); - } -} - - -// We scavange new space simultaneously with sweeping. This is done in two -// passes. -// The first pass migrates all alive objects from one semispace to another or -// promotes them to old space. Forwading address is written directly into -// first word of object without any encoding. If object is dead we are writing -// NULL as a forwarding address. -// The second pass updates pointers to new space in all spaces. It is possible -// to encounter pointers to dead objects during traversal of dirty regions we -// should clear them to avoid encountering them during next dirty regions -// iteration. -static void MigrateObject(Heap* heap, - Address dst, - Address src, - int size, - bool to_old_space) { - if (to_old_space) { - heap->CopyBlockToOldSpaceAndUpdateRegionMarks(dst, src, size); + } else if (dest == CODE_SPACE) { + PROFILE(heap()->isolate(), CodeMoveEvent(src, dst)); + heap()->MoveBlock(dst, src, size); + SlotsBuffer::AddTo(&slots_buffer_allocator_, + &migration_slots_buffer_, + SlotsBuffer::RELOCATED_CODE_OBJECT, + dst, + SlotsBuffer::IGNORE_OVERFLOW); + Code::cast(HeapObject::FromAddress(dst))->Relocate(dst - src); } else { - heap->CopyBlock(dst, src, size); + ASSERT(dest == OLD_DATA_SPACE || dest == NEW_SPACE); + heap()->MoveBlock(dst, src, size); } - Memory::Address_at(src) = dst; } -class StaticPointersToNewGenUpdatingVisitor : public - StaticNewSpaceVisitor<StaticPointersToNewGenUpdatingVisitor> { - public: - static inline void VisitPointer(Heap* heap, Object** p) { - if (!(*p)->IsHeapObject()) return; - - HeapObject* obj = HeapObject::cast(*p); - Address old_addr = obj->address(); - - if (heap->new_space()->Contains(obj)) { - ASSERT(heap->InFromSpace(*p)); - *p = HeapObject::FromAddress(Memory::Address_at(old_addr)); - } - } -}; - - // Visitor for updating pointers from live objects in old spaces to new space. // It does not expect to encounter pointers to dead objects. -class PointersToNewGenUpdatingVisitor: public ObjectVisitor { +class PointersUpdatingVisitor: public ObjectVisitor { public: - explicit PointersToNewGenUpdatingVisitor(Heap* heap) : heap_(heap) { } + explicit PointersUpdatingVisitor(Heap* heap) : heap_(heap) { } void VisitPointer(Object** p) { - StaticPointersToNewGenUpdatingVisitor::VisitPointer(heap_, p); + UpdatePointer(p); } void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) { - StaticPointersToNewGenUpdatingVisitor::VisitPointer(heap_, p); - } + for (Object** p = start; p < end; p++) UpdatePointer(p); + } + + void VisitEmbeddedPointer(RelocInfo* rinfo) { + ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); + Object* target = rinfo->target_object(); + VisitPointer(&target); + rinfo->set_target_object(target); } void VisitCodeTarget(RelocInfo* rinfo) { @@ -2172,68 +2718,96 @@ class PointersToNewGenUpdatingVisitor: public ObjectVisitor { rinfo->set_call_address(Code::cast(target)->instruction_start()); } + static inline void UpdateSlot(Heap* heap, Object** slot) { + Object* obj = *slot; + + if (!obj->IsHeapObject()) return; + + HeapObject* heap_obj = HeapObject::cast(obj); + + MapWord map_word = heap_obj->map_word(); + if (map_word.IsForwardingAddress()) { + ASSERT(heap->InFromSpace(heap_obj) || + MarkCompactCollector::IsOnEvacuationCandidate(heap_obj)); + HeapObject* target = map_word.ToForwardingAddress(); + *slot = target; + ASSERT(!heap->InFromSpace(target) && + !MarkCompactCollector::IsOnEvacuationCandidate(target)); + } + } + private: + inline void UpdatePointer(Object** p) { + UpdateSlot(heap_, p); + } + Heap* heap_; }; -// Visitor for updating pointers from live objects in old spaces to new space. -// It can encounter pointers to dead objects in new space when traversing map -// space (see comment for MigrateObject). -static void UpdatePointerToNewGen(HeapObject** p) { - if (!(*p)->IsHeapObject()) return; +static void UpdatePointer(HeapObject** p, HeapObject* object) { + ASSERT(*p == object); - Address old_addr = (*p)->address(); - ASSERT(HEAP->InFromSpace(*p)); + Address old_addr = object->address(); Address new_addr = Memory::Address_at(old_addr); - if (new_addr == NULL) { - // We encountered pointer to a dead object. Clear it so we will - // not visit it again during next iteration of dirty regions. - *p = NULL; - } else { + // The new space sweep will overwrite the map word of dead objects + // with NULL. In this case we do not need to transfer this entry to + // the store buffer which we are rebuilding. + if (new_addr != NULL) { *p = HeapObject::FromAddress(new_addr); + } else { + // We have to zap this pointer, because the store buffer may overflow later, + // and then we have to scan the entire heap and we don't want to find + // spurious newspace pointers in the old space. + *p = reinterpret_cast<HeapObject*>(Smi::FromInt(0)); } } -static String* UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, - Object** p) { - Address old_addr = HeapObject::cast(*p)->address(); - Address new_addr = Memory::Address_at(old_addr); - return String::cast(HeapObject::FromAddress(new_addr)); +static String* UpdateReferenceInExternalStringTableEntry(Heap* heap, + Object** p) { + MapWord map_word = HeapObject::cast(*p)->map_word(); + + if (map_word.IsForwardingAddress()) { + return String::cast(map_word.ToForwardingAddress()); + } + + return String::cast(*p); } -static bool TryPromoteObject(Heap* heap, HeapObject* object, int object_size) { +bool MarkCompactCollector::TryPromoteObject(HeapObject* object, + int object_size) { Object* result; - if (object_size > heap->MaxObjectSizeInPagedSpace()) { + if (object_size > heap()->MaxObjectSizeInPagedSpace()) { MaybeObject* maybe_result = - heap->lo_space()->AllocateRawFixedArray(object_size); + heap()->lo_space()->AllocateRaw(object_size, NOT_EXECUTABLE); if (maybe_result->ToObject(&result)) { HeapObject* target = HeapObject::cast(result); - MigrateObject(heap, target->address(), object->address(), object_size, - true); - heap->mark_compact_collector()->tracer()-> + MigrateObject(target->address(), + object->address(), + object_size, + LO_SPACE); + heap()->mark_compact_collector()->tracer()-> increment_promoted_objects_size(object_size); return true; } } else { - OldSpace* target_space = heap->TargetSpace(object); + OldSpace* target_space = heap()->TargetSpace(object); - ASSERT(target_space == heap->old_pointer_space() || - target_space == heap->old_data_space()); + ASSERT(target_space == heap()->old_pointer_space() || + target_space == heap()->old_data_space()); MaybeObject* maybe_result = target_space->AllocateRaw(object_size); if (maybe_result->ToObject(&result)) { HeapObject* target = HeapObject::cast(result); - MigrateObject(heap, - target->address(), + MigrateObject(target->address(), object->address(), object_size, - target_space == heap->old_pointer_space()); - heap->mark_compact_collector()->tracer()-> + target_space->identity()); + heap()->mark_compact_collector()->tracer()-> increment_promoted_objects_size(object_size); return true; } @@ -2243,1145 +2817,1303 @@ static bool TryPromoteObject(Heap* heap, HeapObject* object, int object_size) { } -static void SweepNewSpace(Heap* heap, NewSpace* space) { - heap->CheckNewSpaceExpansionCriteria(); +void MarkCompactCollector::EvacuateNewSpace() { + // There are soft limits in the allocation code, designed trigger a mark + // sweep collection by failing allocations. But since we are already in + // a mark-sweep allocation, there is no sense in trying to trigger one. + AlwaysAllocateScope scope; + heap()->CheckNewSpaceExpansionCriteria(); + + NewSpace* new_space = heap()->new_space(); - Address from_bottom = space->bottom(); - Address from_top = space->top(); + // Store allocation range before flipping semispaces. + Address from_bottom = new_space->bottom(); + Address from_top = new_space->top(); // Flip the semispaces. After flipping, to space is empty, from space has // live objects. - space->Flip(); - space->ResetAllocationInfo(); + new_space->Flip(); + new_space->ResetAllocationInfo(); - int size = 0; int survivors_size = 0; // First pass: traverse all objects in inactive semispace, remove marks, - // migrate live objects and write forwarding addresses. - for (Address current = from_bottom; current < from_top; current += size) { - HeapObject* object = HeapObject::FromAddress(current); - - if (object->IsMarked()) { - object->ClearMark(); - heap->mark_compact_collector()->tracer()->decrement_marked_count(); - - size = object->Size(); + // migrate live objects and write forwarding addresses. This stage puts + // new entries in the store buffer and may cause some pages to be marked + // scan-on-scavenge. + SemiSpaceIterator from_it(from_bottom, from_top); + for (HeapObject* object = from_it.Next(); + object != NULL; + object = from_it.Next()) { + MarkBit mark_bit = Marking::MarkBitFrom(object); + if (mark_bit.Get()) { + mark_bit.Clear(); + // Don't bother decrementing live bytes count. We'll discard the + // entire page at the end. + int size = object->Size(); survivors_size += size; // Aggressively promote young survivors to the old space. - if (TryPromoteObject(heap, object, size)) { + if (TryPromoteObject(object, size)) { continue; } // Promotion failed. Just migrate object to another semispace. - // Allocation cannot fail at this point: semispaces are of equal size. - Object* target = space->AllocateRaw(size)->ToObjectUnchecked(); + MaybeObject* allocation = new_space->AllocateRaw(size); + if (allocation->IsFailure()) { + if (!new_space->AddFreshPage()) { + // Shouldn't happen. We are sweeping linearly, and to-space + // has the same number of pages as from-space, so there is + // always room. + UNREACHABLE(); + } + allocation = new_space->AllocateRaw(size); + ASSERT(!allocation->IsFailure()); + } + Object* target = allocation->ToObjectUnchecked(); - MigrateObject(heap, - HeapObject::cast(target)->address(), - current, + MigrateObject(HeapObject::cast(target)->address(), + object->address(), size, - false); + NEW_SPACE); } else { // Process the dead object before we write a NULL into its header. LiveObjectList::ProcessNonLive(object); - size = object->Size(); - Memory::Address_at(current) = NULL; + // Mark dead objects in the new space with null in their map field. + Memory::Address_at(object->address()) = NULL; } } - // Second pass: find pointers to new space and update them. - PointersToNewGenUpdatingVisitor updating_visitor(heap); - - // Update pointers in to space. - Address current = space->bottom(); - while (current < space->top()) { - HeapObject* object = HeapObject::FromAddress(current); - current += - StaticPointersToNewGenUpdatingVisitor::IterateBody(object->map(), - object); - } + heap_->IncrementYoungSurvivorsCounter(survivors_size); + new_space->set_age_mark(new_space->top()); +} - // Update roots. - heap->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); - LiveObjectList::IterateElements(&updating_visitor); - // Update pointers in old spaces. - heap->IterateDirtyRegions(heap->old_pointer_space(), - &Heap::IteratePointersInDirtyRegion, - &UpdatePointerToNewGen, - heap->WATERMARK_SHOULD_BE_VALID); +void MarkCompactCollector::EvacuateLiveObjectsFromPage(Page* p) { + AlwaysAllocateScope always_allocate; + PagedSpace* space = static_cast<PagedSpace*>(p->owner()); + ASSERT(p->IsEvacuationCandidate() && !p->WasSwept()); + MarkBit::CellType* cells = p->markbits()->cells(); + p->MarkSweptPrecisely(); + + int last_cell_index = + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(p->ObjectAreaEnd()))); + + int cell_index = Page::kFirstUsedCell; + Address cell_base = p->ObjectAreaStart(); + int offsets[16]; + + for (cell_index = Page::kFirstUsedCell; + cell_index < last_cell_index; + cell_index++, cell_base += 32 * kPointerSize) { + ASSERT((unsigned)cell_index == + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(cell_base)))); + if (cells[cell_index] == 0) continue; + + int live_objects = MarkWordToObjectStarts(cells[cell_index], offsets); + for (int i = 0; i < live_objects; i++) { + Address object_addr = cell_base + offsets[i] * kPointerSize; + HeapObject* object = HeapObject::FromAddress(object_addr); + ASSERT(Marking::IsBlack(Marking::MarkBitFrom(object))); + + int size = object->Size(); + + MaybeObject* target = space->AllocateRaw(size); + if (target->IsFailure()) { + // OS refused to give us memory. + V8::FatalProcessOutOfMemory("Evacuation"); + return; + } - heap->lo_space()->IterateDirtyRegions(&UpdatePointerToNewGen); + Object* target_object = target->ToObjectUnchecked(); - // Update pointers from cells. - HeapObjectIterator cell_iterator(heap->cell_space()); - for (HeapObject* cell = cell_iterator.next(); - cell != NULL; - cell = cell_iterator.next()) { - if (cell->IsJSGlobalPropertyCell()) { - Address value_address = - reinterpret_cast<Address>(cell) + - (JSGlobalPropertyCell::kValueOffset - kHeapObjectTag); - updating_visitor.VisitPointer(reinterpret_cast<Object**>(value_address)); + MigrateObject(HeapObject::cast(target_object)->address(), + object_addr, + size, + space->identity()); + ASSERT(object->map_word().IsForwardingAddress()); } - } - - // Update pointer from the global contexts list. - updating_visitor.VisitPointer(heap->global_contexts_list_address()); - // Update pointers from external string table. - heap->UpdateNewSpaceReferencesInExternalStringTable( - &UpdateNewSpaceReferenceInExternalStringTableEntry); - - // All pointers were updated. Update auxiliary allocation info. - heap->IncrementYoungSurvivorsCounter(survivors_size); - space->set_age_mark(space->top()); - - // Update JSFunction pointers from the runtime profiler. - heap->isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); + // Clear marking bits for current cell. + cells[cell_index] = 0; + } + p->ResetLiveBytes(); } -static void SweepSpace(Heap* heap, PagedSpace* space) { - PageIterator it(space, PageIterator::PAGES_IN_USE); - - // During sweeping of paged space we are trying to find longest sequences - // of pages without live objects and free them (instead of putting them on - // the free list). - - // Page preceding current. - Page* prev = Page::FromAddress(NULL); - - // First empty page in a sequence. - Page* first_empty_page = Page::FromAddress(NULL); - - // Page preceding first empty page. - Page* prec_first_empty_page = Page::FromAddress(NULL); - - // If last used page of space ends with a sequence of dead objects - // we can adjust allocation top instead of puting this free area into - // the free list. Thus during sweeping we keep track of such areas - // and defer their deallocation until the sweeping of the next page - // is done: if one of the next pages contains live objects we have - // to put such area into the free list. - Address last_free_start = NULL; - int last_free_size = 0; - - while (it.has_next()) { - Page* p = it.next(); - - bool is_previous_alive = true; - Address free_start = NULL; - HeapObject* object; - - for (Address current = p->ObjectAreaStart(); - current < p->AllocationTop(); - current += object->Size()) { - object = HeapObject::FromAddress(current); - if (object->IsMarked()) { - object->ClearMark(); - heap->mark_compact_collector()->tracer()->decrement_marked_count(); - - if (!is_previous_alive) { // Transition from free to live. - space->DeallocateBlock(free_start, - static_cast<int>(current - free_start), - true); - is_previous_alive = true; - } +void MarkCompactCollector::EvacuatePages() { + int npages = evacuation_candidates_.length(); + for (int i = 0; i < npages; i++) { + Page* p = evacuation_candidates_[i]; + ASSERT(p->IsEvacuationCandidate() || + p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); + if (p->IsEvacuationCandidate()) { + // During compaction we might have to request a new page. + // Check that space still have room for that. + if (static_cast<PagedSpace*>(p->owner())->CanExpand()) { + EvacuateLiveObjectsFromPage(p); } else { - heap->mark_compact_collector()->ReportDeleteIfNeeded( - object, heap->isolate()); - if (is_previous_alive) { // Transition from live to free. - free_start = current; - is_previous_alive = false; + // Without room for expansion evacuation is not guaranteed to succeed. + // Pessimistically abandon unevacuated pages. + for (int j = i; j < npages; j++) { + Page* page = evacuation_candidates_[j]; + slots_buffer_allocator_.DeallocateChain(page->slots_buffer_address()); + page->ClearEvacuationCandidate(); + page->SetFlag(Page::RESCAN_ON_EVACUATION); } - LiveObjectList::ProcessNonLive(object); + return; } - // The object is now unmarked for the call to Size() at the top of the - // loop. } + } +} - bool page_is_empty = (p->ObjectAreaStart() == p->AllocationTop()) - || (!is_previous_alive && free_start == p->ObjectAreaStart()); - - if (page_is_empty) { - // This page is empty. Check whether we are in the middle of - // sequence of empty pages and start one if not. - if (!first_empty_page->is_valid()) { - first_empty_page = p; - prec_first_empty_page = prev; - } - - if (!is_previous_alive) { - // There are dead objects on this page. Update space accounting stats - // without putting anything into free list. - int size_in_bytes = static_cast<int>(p->AllocationTop() - free_start); - if (size_in_bytes > 0) { - space->DeallocateBlock(free_start, size_in_bytes, false); - } - } - } else { - // This page is not empty. Sequence of empty pages ended on the previous - // one. - if (first_empty_page->is_valid()) { - space->FreePages(prec_first_empty_page, prev); - prec_first_empty_page = first_empty_page = Page::FromAddress(NULL); - } - - // If there is a free ending area on one of the previous pages we have - // deallocate that area and put it on the free list. - if (last_free_size > 0) { - Page::FromAddress(last_free_start)-> - SetAllocationWatermark(last_free_start); - space->DeallocateBlock(last_free_start, last_free_size, true); - last_free_start = NULL; - last_free_size = 0; - } - // If the last region of this page was not live we remember it. - if (!is_previous_alive) { - ASSERT(last_free_size == 0); - last_free_size = static_cast<int>(p->AllocationTop() - free_start); - last_free_start = free_start; +class EvacuationWeakObjectRetainer : public WeakObjectRetainer { + public: + virtual Object* RetainAs(Object* object) { + if (object->IsHeapObject()) { + HeapObject* heap_object = HeapObject::cast(object); + MapWord map_word = heap_object->map_word(); + if (map_word.IsForwardingAddress()) { + return map_word.ToForwardingAddress(); } } - - prev = p; - } - - // We reached end of space. See if we need to adjust allocation top. - Address new_allocation_top = NULL; - - if (first_empty_page->is_valid()) { - // Last used pages in space are empty. We can move allocation top backwards - // to the beginning of first empty page. - ASSERT(prev == space->AllocationTopPage()); - - new_allocation_top = first_empty_page->ObjectAreaStart(); + return object; } +}; - if (last_free_size > 0) { - // There was a free ending area on the previous page. - // Deallocate it without putting it into freelist and move allocation - // top to the beginning of this free area. - space->DeallocateBlock(last_free_start, last_free_size, false); - new_allocation_top = last_free_start; - } - if (new_allocation_top != NULL) { -#ifdef DEBUG - Page* new_allocation_top_page = Page::FromAllocationTop(new_allocation_top); - if (!first_empty_page->is_valid()) { - ASSERT(new_allocation_top_page == space->AllocationTopPage()); - } else if (last_free_size > 0) { - ASSERT(new_allocation_top_page == prec_first_empty_page); - } else { - ASSERT(new_allocation_top_page == first_empty_page); +static inline void UpdateSlot(ObjectVisitor* v, + SlotsBuffer::SlotType slot_type, + Address addr) { + switch (slot_type) { + case SlotsBuffer::CODE_TARGET_SLOT: { + RelocInfo rinfo(addr, RelocInfo::CODE_TARGET, 0, NULL); + rinfo.Visit(v); + break; } -#endif - - space->SetTop(new_allocation_top); + case SlotsBuffer::CODE_ENTRY_SLOT: { + v->VisitCodeEntry(addr); + break; + } + case SlotsBuffer::RELOCATED_CODE_OBJECT: { + HeapObject* obj = HeapObject::FromAddress(addr); + Code::cast(obj)->CodeIterateBody(v); + break; + } + case SlotsBuffer::DEBUG_TARGET_SLOT: { + RelocInfo rinfo(addr, RelocInfo::DEBUG_BREAK_SLOT, 0, NULL); + if (rinfo.IsPatchedDebugBreakSlotSequence()) rinfo.Visit(v); + break; + } + case SlotsBuffer::JS_RETURN_SLOT: { + RelocInfo rinfo(addr, RelocInfo::JS_RETURN, 0, NULL); + if (rinfo.IsPatchedReturnSequence()) rinfo.Visit(v); + break; + } + case SlotsBuffer::EMBEDDED_OBJECT_SLOT: { + RelocInfo rinfo(addr, RelocInfo::EMBEDDED_OBJECT, 0, NULL); + rinfo.Visit(v); + break; + } + default: + UNREACHABLE(); + break; } } -void MarkCompactCollector::EncodeForwardingAddresses() { - ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES); - // Objects in the active semispace of the young generation may be - // relocated to the inactive semispace (if not promoted). Set the - // relocation info to the beginning of the inactive semispace. - heap()->new_space()->MCResetRelocationInfo(); - - // Compute the forwarding pointers in each space. - EncodeForwardingAddressesInPagedSpace<MCAllocateFromOldPointerSpace, - ReportDeleteIfNeeded>( - heap()->old_pointer_space()); - - EncodeForwardingAddressesInPagedSpace<MCAllocateFromOldDataSpace, - IgnoreNonLiveObject>( - heap()->old_data_space()); - - EncodeForwardingAddressesInPagedSpace<MCAllocateFromCodeSpace, - ReportDeleteIfNeeded>( - heap()->code_space()); - - EncodeForwardingAddressesInPagedSpace<MCAllocateFromCellSpace, - IgnoreNonLiveObject>( - heap()->cell_space()); - - - // Compute new space next to last after the old and code spaces have been - // compacted. Objects in new space can be promoted to old or code space. - EncodeForwardingAddressesInNewSpace(); - - // Compute map space last because computing forwarding addresses - // overwrites non-live objects. Objects in the other spaces rely on - // non-live map pointers to get the sizes of non-live objects. - EncodeForwardingAddressesInPagedSpace<MCAllocateFromMapSpace, - IgnoreNonLiveObject>( - heap()->map_space()); - - // Write relocation info to the top page, so we can use it later. This is - // done after promoting objects from the new space so we get the correct - // allocation top. - heap()->old_pointer_space()->MCWriteRelocationInfoToPage(); - heap()->old_data_space()->MCWriteRelocationInfoToPage(); - heap()->code_space()->MCWriteRelocationInfoToPage(); - heap()->map_space()->MCWriteRelocationInfoToPage(); - heap()->cell_space()->MCWriteRelocationInfoToPage(); -} - - -class MapIterator : public HeapObjectIterator { - public: - explicit MapIterator(Heap* heap) - : HeapObjectIterator(heap->map_space(), &SizeCallback) { } +enum SweepingMode { + SWEEP_ONLY, + SWEEP_AND_VISIT_LIVE_OBJECTS +}; - MapIterator(Heap* heap, Address start) - : HeapObjectIterator(heap->map_space(), start, &SizeCallback) { } - private: - static int SizeCallback(HeapObject* unused) { - USE(unused); - return Map::kSize; - } +enum SkipListRebuildingMode { + REBUILD_SKIP_LIST, + IGNORE_SKIP_LIST }; -class MapCompact { - public: - explicit MapCompact(Heap* heap, int live_maps) - : heap_(heap), - live_maps_(live_maps), - to_evacuate_start_(heap->map_space()->TopAfterCompaction(live_maps)), - vacant_map_it_(heap), - map_to_evacuate_it_(heap, to_evacuate_start_), - first_map_to_evacuate_( - reinterpret_cast<Map*>(HeapObject::FromAddress(to_evacuate_start_))) { - } - - void CompactMaps() { - // As we know the number of maps to evacuate beforehand, - // we stop then there is no more vacant maps. - for (Map* next_vacant_map = NextVacantMap(); - next_vacant_map; - next_vacant_map = NextVacantMap()) { - EvacuateMap(next_vacant_map, NextMapToEvacuate()); +// Sweep a space precisely. After this has been done the space can +// be iterated precisely, hitting only the live objects. Code space +// is always swept precisely because we want to be able to iterate +// over it. Map space is swept precisely, because it is not compacted. +// Slots in live objects pointing into evacuation candidates are updated +// if requested. +template<SweepingMode sweeping_mode, SkipListRebuildingMode skip_list_mode> +static void SweepPrecisely(PagedSpace* space, + Page* p, + ObjectVisitor* v) { + ASSERT(!p->IsEvacuationCandidate() && !p->WasSwept()); + ASSERT_EQ(skip_list_mode == REBUILD_SKIP_LIST, + space->identity() == CODE_SPACE); + ASSERT((p->skip_list() == NULL) || (skip_list_mode == REBUILD_SKIP_LIST)); + + MarkBit::CellType* cells = p->markbits()->cells(); + p->MarkSweptPrecisely(); + + int last_cell_index = + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(p->ObjectAreaEnd()))); + + int cell_index = Page::kFirstUsedCell; + Address free_start = p->ObjectAreaStart(); + ASSERT(reinterpret_cast<intptr_t>(free_start) % (32 * kPointerSize) == 0); + Address object_address = p->ObjectAreaStart(); + int offsets[16]; + + SkipList* skip_list = p->skip_list(); + int curr_region = -1; + if ((skip_list_mode == REBUILD_SKIP_LIST) && skip_list) { + skip_list->Clear(); + } + + for (cell_index = Page::kFirstUsedCell; + cell_index < last_cell_index; + cell_index++, object_address += 32 * kPointerSize) { + ASSERT((unsigned)cell_index == + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(object_address)))); + int live_objects = MarkWordToObjectStarts(cells[cell_index], offsets); + int live_index = 0; + for ( ; live_objects != 0; live_objects--) { + Address free_end = object_address + offsets[live_index++] * kPointerSize; + if (free_end != free_start) { + space->Free(free_start, static_cast<int>(free_end - free_start)); + } + HeapObject* live_object = HeapObject::FromAddress(free_end); + ASSERT(Marking::IsBlack(Marking::MarkBitFrom(live_object))); + Map* map = live_object->map(); + int size = live_object->SizeFromMap(map); + if (sweeping_mode == SWEEP_AND_VISIT_LIVE_OBJECTS) { + live_object->IterateBody(map->instance_type(), size, v); + } + if ((skip_list_mode == REBUILD_SKIP_LIST) && skip_list != NULL) { + int new_region_start = + SkipList::RegionNumber(free_end); + int new_region_end = + SkipList::RegionNumber(free_end + size - kPointerSize); + if (new_region_start != curr_region || + new_region_end != curr_region) { + skip_list->AddObject(free_end, size); + curr_region = new_region_end; + } + } + free_start = free_end + size; } - -#ifdef DEBUG - CheckNoMapsToEvacuate(); -#endif + // Clear marking bits for current cell. + cells[cell_index] = 0; } - - void UpdateMapPointersInRoots() { - MapUpdatingVisitor map_updating_visitor; - heap()->IterateRoots(&map_updating_visitor, VISIT_ONLY_STRONG); - heap()->isolate()->global_handles()->IterateWeakRoots( - &map_updating_visitor); - LiveObjectList::IterateElements(&map_updating_visitor); - } - - void UpdateMapPointersInPagedSpace(PagedSpace* space) { - ASSERT(space != heap()->map_space()); - - PageIterator it(space, PageIterator::PAGES_IN_USE); - while (it.has_next()) { - Page* p = it.next(); - UpdateMapPointersInRange(heap(), - p->ObjectAreaStart(), - p->AllocationTop()); - } + if (free_start != p->ObjectAreaEnd()) { + space->Free(free_start, static_cast<int>(p->ObjectAreaEnd() - free_start)); } + p->ResetLiveBytes(); +} - void UpdateMapPointersInNewSpace() { - NewSpace* space = heap()->new_space(); - UpdateMapPointersInRange(heap(), space->bottom(), space->top()); - } - void UpdateMapPointersInLargeObjectSpace() { - LargeObjectIterator it(heap()->lo_space()); - for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) - UpdateMapPointersInObject(heap(), obj); - } +static bool SetMarkBitsUnderInvalidatedCode(Code* code, bool value) { + Page* p = Page::FromAddress(code->address()); - void Finish() { - heap()->map_space()->FinishCompaction(to_evacuate_start_, live_maps_); + if (p->IsEvacuationCandidate() || + p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { + return false; } - inline Heap* heap() const { return heap_; } + Address code_start = code->address(); + Address code_end = code_start + code->Size(); - private: - Heap* heap_; - int live_maps_; - Address to_evacuate_start_; - MapIterator vacant_map_it_; - MapIterator map_to_evacuate_it_; - Map* first_map_to_evacuate_; - - // Helper class for updating map pointers in HeapObjects. - class MapUpdatingVisitor: public ObjectVisitor { - public: - MapUpdatingVisitor() {} + uint32_t start_index = MemoryChunk::FastAddressToMarkbitIndex(code_start); + uint32_t end_index = + MemoryChunk::FastAddressToMarkbitIndex(code_end - kPointerSize); - void VisitPointer(Object** p) { - UpdateMapPointer(p); - } + Bitmap* b = p->markbits(); - void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) UpdateMapPointer(p); - } + MarkBit start_mark_bit = b->MarkBitFromIndex(start_index); + MarkBit end_mark_bit = b->MarkBitFromIndex(end_index); - private: - void UpdateMapPointer(Object** p) { - if (!(*p)->IsHeapObject()) return; - HeapObject* old_map = reinterpret_cast<HeapObject*>(*p); + MarkBit::CellType* start_cell = start_mark_bit.cell(); + MarkBit::CellType* end_cell = end_mark_bit.cell(); - // Moved maps are tagged with overflowed map word. They are the only - // objects those map word is overflowed as marking is already complete. - MapWord map_word = old_map->map_word(); - if (!map_word.IsOverflowed()) return; + if (value) { + MarkBit::CellType start_mask = ~(start_mark_bit.mask() - 1); + MarkBit::CellType end_mask = (end_mark_bit.mask() << 1) - 1; - *p = GetForwardedMap(map_word); + if (start_cell == end_cell) { + *start_cell |= start_mask & end_mask; + } else { + *start_cell |= start_mask; + for (MarkBit::CellType* cell = start_cell + 1; cell < end_cell; cell++) { + *cell = ~0; + } + *end_cell |= end_mask; } - }; - - static Map* NextMap(MapIterator* it, HeapObject* last, bool live) { - while (true) { - HeapObject* next = it->next(); - ASSERT(next != NULL); - if (next == last) - return NULL; - ASSERT(!next->IsOverflowed()); - ASSERT(!next->IsMarked()); - ASSERT(next->IsMap() || FreeListNode::IsFreeListNode(next)); - if (next->IsMap() == live) - return reinterpret_cast<Map*>(next); + } else { + for (MarkBit::CellType* cell = start_cell ; cell <= end_cell; cell++) { + *cell = 0; } } - Map* NextVacantMap() { - Map* map = NextMap(&vacant_map_it_, first_map_to_evacuate_, false); - ASSERT(map == NULL || FreeListNode::IsFreeListNode(map)); - return map; - } - - Map* NextMapToEvacuate() { - Map* map = NextMap(&map_to_evacuate_it_, NULL, true); - ASSERT(map != NULL); - ASSERT(map->IsMap()); - return map; - } - - static void EvacuateMap(Map* vacant_map, Map* map_to_evacuate) { - ASSERT(FreeListNode::IsFreeListNode(vacant_map)); - ASSERT(map_to_evacuate->IsMap()); + return true; +} - ASSERT(Map::kSize % 4 == 0); - map_to_evacuate->heap()->CopyBlockToOldSpaceAndUpdateRegionMarks( - vacant_map->address(), map_to_evacuate->address(), Map::kSize); +static bool IsOnInvalidatedCodeObject(Address addr) { + // We did not record any slots in large objects thus + // we can safely go to the page from the slot address. + Page* p = Page::FromAddress(addr); - ASSERT(vacant_map->IsMap()); // Due to memcpy above. + // First check owner's identity because old pointer and old data spaces + // are swept lazily and might still have non-zero mark-bits on some + // pages. + if (p->owner()->identity() != CODE_SPACE) return false; - MapWord forwarding_map_word = MapWord::FromMap(vacant_map); - forwarding_map_word.SetOverflow(); - map_to_evacuate->set_map_word(forwarding_map_word); + // In code space only bits on evacuation candidates (but we don't record + // any slots on them) and under invalidated code objects are non-zero. + MarkBit mark_bit = + p->markbits()->MarkBitFromIndex(Page::FastAddressToMarkbitIndex(addr)); - ASSERT(map_to_evacuate->map_word().IsOverflowed()); - ASSERT(GetForwardedMap(map_to_evacuate->map_word()) == vacant_map); - } + return mark_bit.Get(); +} - static Map* GetForwardedMap(MapWord map_word) { - ASSERT(map_word.IsOverflowed()); - map_word.ClearOverflow(); - Map* new_map = map_word.ToMap(); - ASSERT_MAP_ALIGNED(new_map->address()); - return new_map; - } - static int UpdateMapPointersInObject(Heap* heap, HeapObject* obj) { - ASSERT(!obj->IsMarked()); - Map* map = obj->map(); - ASSERT(heap->map_space()->Contains(map)); - MapWord map_word = map->map_word(); - ASSERT(!map_word.IsMarked()); - if (map_word.IsOverflowed()) { - Map* new_map = GetForwardedMap(map_word); - ASSERT(heap->map_space()->Contains(new_map)); - obj->set_map(new_map); +void MarkCompactCollector::InvalidateCode(Code* code) { + if (heap_->incremental_marking()->IsCompacting() && + !ShouldSkipEvacuationSlotRecording(code)) { + ASSERT(compacting_); -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("update %p : %p -> %p\n", - obj->address(), - reinterpret_cast<void*>(map), - reinterpret_cast<void*>(new_map)); - } -#endif - } + // If the object is white than no slots were recorded on it yet. + MarkBit mark_bit = Marking::MarkBitFrom(code); + if (Marking::IsWhite(mark_bit)) return; - int size = obj->SizeFromMap(map); - MapUpdatingVisitor map_updating_visitor; - obj->IterateBody(map->instance_type(), size, &map_updating_visitor); - return size; + invalidated_code_.Add(code); } +} - static void UpdateMapPointersInRange(Heap* heap, Address start, Address end) { - HeapObject* object; - int size; - for (Address current = start; current < end; current += size) { - object = HeapObject::FromAddress(current); - size = UpdateMapPointersInObject(heap, object); - ASSERT(size > 0); - } - } - -#ifdef DEBUG - void CheckNoMapsToEvacuate() { - if (!FLAG_enable_slow_asserts) - return; - - for (HeapObject* obj = map_to_evacuate_it_.next(); - obj != NULL; obj = map_to_evacuate_it_.next()) - ASSERT(FreeListNode::IsFreeListNode(obj)); - } -#endif -}; +bool MarkCompactCollector::MarkInvalidatedCode() { + bool code_marked = false; -void MarkCompactCollector::SweepSpaces() { - GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP); + int length = invalidated_code_.length(); + for (int i = 0; i < length; i++) { + Code* code = invalidated_code_[i]; - ASSERT(state_ == SWEEP_SPACES); - ASSERT(!IsCompacting()); - // Noncompacting collections simply sweep the spaces to clear the mark - // bits and free the nonlive blocks (for old and map spaces). We sweep - // the map space last because freeing non-live maps overwrites them and - // the other spaces rely on possibly non-live maps to get the sizes for - // non-live objects. - SweepSpace(heap(), heap()->old_pointer_space()); - SweepSpace(heap(), heap()->old_data_space()); - SweepSpace(heap(), heap()->code_space()); - SweepSpace(heap(), heap()->cell_space()); - { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE); - SweepNewSpace(heap(), heap()->new_space()); + if (SetMarkBitsUnderInvalidatedCode(code, true)) { + code_marked = true; + } } - SweepSpace(heap(), heap()->map_space()); - - heap()->IterateDirtyRegions(heap()->map_space(), - &heap()->IteratePointersInDirtyMapsRegion, - &UpdatePointerToNewGen, - heap()->WATERMARK_SHOULD_BE_VALID); - - intptr_t live_maps_size = heap()->map_space()->Size(); - int live_maps = static_cast<int>(live_maps_size / Map::kSize); - ASSERT(live_map_objects_size_ == live_maps_size); - if (heap()->map_space()->NeedsCompaction(live_maps)) { - MapCompact map_compact(heap(), live_maps); - - map_compact.CompactMaps(); - map_compact.UpdateMapPointersInRoots(); + return code_marked; +} - PagedSpaces spaces; - for (PagedSpace* space = spaces.next(); - space != NULL; space = spaces.next()) { - if (space == heap()->map_space()) continue; - map_compact.UpdateMapPointersInPagedSpace(space); - } - map_compact.UpdateMapPointersInNewSpace(); - map_compact.UpdateMapPointersInLargeObjectSpace(); - map_compact.Finish(); +void MarkCompactCollector::RemoveDeadInvalidatedCode() { + int length = invalidated_code_.length(); + for (int i = 0; i < length; i++) { + if (!IsMarked(invalidated_code_[i])) invalidated_code_[i] = NULL; } } -// Iterate the live objects in a range of addresses (eg, a page or a -// semispace). The live regions of the range have been linked into a list. -// The first live region is [first_live_start, first_live_end), and the last -// address in the range is top. The callback function is used to get the -// size of each live object. -int MarkCompactCollector::IterateLiveObjectsInRange( - Address start, - Address end, - LiveObjectCallback size_func) { - int live_objects_size = 0; - Address current = start; - while (current < end) { - uint32_t encoded_map = Memory::uint32_at(current); - if (encoded_map == kSingleFreeEncoding) { - current += kPointerSize; - } else if (encoded_map == kMultiFreeEncoding) { - current += Memory::int_at(current + kIntSize); - } else { - int size = (this->*size_func)(HeapObject::FromAddress(current)); - current += size; - live_objects_size += size; +void MarkCompactCollector::ProcessInvalidatedCode(ObjectVisitor* visitor) { + int length = invalidated_code_.length(); + for (int i = 0; i < length; i++) { + Code* code = invalidated_code_[i]; + if (code != NULL) { + code->Iterate(visitor); + SetMarkBitsUnderInvalidatedCode(code, false); } } - return live_objects_size; + invalidated_code_.Rewind(0); } -int MarkCompactCollector::IterateLiveObjects( - NewSpace* space, LiveObjectCallback size_f) { - ASSERT(MARK_LIVE_OBJECTS < state_ && state_ <= RELOCATE_OBJECTS); - return IterateLiveObjectsInRange(space->bottom(), space->top(), size_f); -} - +void MarkCompactCollector::EvacuateNewSpaceAndCandidates() { + bool code_slots_filtering_required; + { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE); + code_slots_filtering_required = MarkInvalidatedCode(); -int MarkCompactCollector::IterateLiveObjects( - PagedSpace* space, LiveObjectCallback size_f) { - ASSERT(MARK_LIVE_OBJECTS < state_ && state_ <= RELOCATE_OBJECTS); - int total = 0; - PageIterator it(space, PageIterator::PAGES_IN_USE); - while (it.has_next()) { - Page* p = it.next(); - total += IterateLiveObjectsInRange(p->ObjectAreaStart(), - p->AllocationTop(), - size_f); + EvacuateNewSpace(); } - return total; -} - - -// ------------------------------------------------------------------------- -// Phase 3: Update pointers -// Helper class for updating pointers in HeapObjects. -class UpdatingVisitor: public ObjectVisitor { - public: - explicit UpdatingVisitor(Heap* heap) : heap_(heap) {} - void VisitPointer(Object** p) { - UpdatePointer(p); + { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_EVACUATE_PAGES); + EvacuatePages(); } - void VisitPointers(Object** start, Object** end) { - // Mark all HeapObject pointers in [start, end) - for (Object** p = start; p < end; p++) UpdatePointer(p); + // Second pass: find pointers to new space and update them. + PointersUpdatingVisitor updating_visitor(heap()); + + { GCTracer::Scope gc_scope(tracer_, + GCTracer::Scope::MC_UPDATE_NEW_TO_NEW_POINTERS); + // Update pointers in to space. + SemiSpaceIterator to_it(heap()->new_space()->bottom(), + heap()->new_space()->top()); + for (HeapObject* object = to_it.Next(); + object != NULL; + object = to_it.Next()) { + Map* map = object->map(); + object->IterateBody(map->instance_type(), + object->SizeFromMap(map), + &updating_visitor); + } } - void VisitCodeTarget(RelocInfo* rinfo) { - ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); - Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); - VisitPointer(&target); - rinfo->set_target_address( - reinterpret_cast<Code*>(target)->instruction_start()); + { GCTracer::Scope gc_scope(tracer_, + GCTracer::Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS); + // Update roots. + heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); + LiveObjectList::IterateElements(&updating_visitor); } - void VisitDebugTarget(RelocInfo* rinfo) { - ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) && - rinfo->IsPatchedReturnSequence()) || - (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && - rinfo->IsPatchedDebugBreakSlotSequence())); - Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address()); - VisitPointer(&target); - rinfo->set_call_address( - reinterpret_cast<Code*>(target)->instruction_start()); + { GCTracer::Scope gc_scope(tracer_, + GCTracer::Scope::MC_UPDATE_OLD_TO_NEW_POINTERS); + StoreBufferRebuildScope scope(heap_, + heap_->store_buffer(), + &Heap::ScavengeStoreBufferCallback); + heap_->store_buffer()->IteratePointersToNewSpace(&UpdatePointer); } - inline Heap* heap() const { return heap_; } - - private: - void UpdatePointer(Object** p) { - if (!(*p)->IsHeapObject()) return; - - HeapObject* obj = HeapObject::cast(*p); - Address old_addr = obj->address(); - Address new_addr; - ASSERT(!heap()->InFromSpace(obj)); - - if (heap()->new_space()->Contains(obj)) { - Address forwarding_pointer_addr = - heap()->new_space()->FromSpaceLow() + - heap()->new_space()->ToSpaceOffsetForAddress(old_addr); - new_addr = Memory::Address_at(forwarding_pointer_addr); - -#ifdef DEBUG - ASSERT(heap()->old_pointer_space()->Contains(new_addr) || - heap()->old_data_space()->Contains(new_addr) || - heap()->new_space()->FromSpaceContains(new_addr) || - heap()->lo_space()->Contains(HeapObject::FromAddress(new_addr))); - - if (heap()->new_space()->FromSpaceContains(new_addr)) { - ASSERT(heap()->new_space()->FromSpaceOffsetForAddress(new_addr) <= - heap()->new_space()->ToSpaceOffsetForAddress(old_addr)); - } -#endif - - } else if (heap()->lo_space()->Contains(obj)) { - // Don't move objects in the large object space. - return; + { GCTracer::Scope gc_scope(tracer_, + GCTracer::Scope::MC_UPDATE_POINTERS_TO_EVACUATED); + SlotsBuffer::UpdateSlotsRecordedIn(heap_, + migration_slots_buffer_, + code_slots_filtering_required); + if (FLAG_trace_fragmentation) { + PrintF(" migration slots buffer: %d\n", + SlotsBuffer::SizeOfChain(migration_slots_buffer_)); + } - } else { -#ifdef DEBUG - PagedSpaces spaces; - PagedSpace* original_space = spaces.next(); - while (original_space != NULL) { - if (original_space->Contains(obj)) break; - original_space = spaces.next(); + if (compacting_ && was_marked_incrementally_) { + // It's difficult to filter out slots recorded for large objects. + LargeObjectIterator it(heap_->lo_space()); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + // LargeObjectSpace is not swept yet thus we have to skip + // dead objects explicitly. + if (!IsMarked(obj)) continue; + + Page* p = Page::FromAddress(obj->address()); + if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { + obj->Iterate(&updating_visitor); + p->ClearFlag(Page::RESCAN_ON_EVACUATION); + } } - ASSERT(original_space != NULL); -#endif - new_addr = MarkCompactCollector::GetForwardingAddressInOldSpace(obj); - ASSERT(original_space->Contains(new_addr)); - ASSERT(original_space->MCSpaceOffsetForAddress(new_addr) <= - original_space->MCSpaceOffsetForAddress(old_addr)); } + } - *p = HeapObject::FromAddress(new_addr); + int npages = evacuation_candidates_.length(); + { GCTracer::Scope gc_scope( + tracer_, GCTracer::Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED); + for (int i = 0; i < npages; i++) { + Page* p = evacuation_candidates_[i]; + ASSERT(p->IsEvacuationCandidate() || + p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); + + if (p->IsEvacuationCandidate()) { + SlotsBuffer::UpdateSlotsRecordedIn(heap_, + p->slots_buffer(), + code_slots_filtering_required); + if (FLAG_trace_fragmentation) { + PrintF(" page %p slots buffer: %d\n", + reinterpret_cast<void*>(p), + SlotsBuffer::SizeOfChain(p->slots_buffer())); + } -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("update %p : %p -> %p\n", - reinterpret_cast<Address>(p), old_addr, new_addr); + // Important: skip list should be cleared only after roots were updated + // because root iteration traverses the stack and might have to find + // code objects from non-updated pc pointing into evacuation candidate. + SkipList* list = p->skip_list(); + if (list != NULL) list->Clear(); + } else { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " during evacuation.\n", + reinterpret_cast<intptr_t>(p)); + } + PagedSpace* space = static_cast<PagedSpace*>(p->owner()); + p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION); + + switch (space->identity()) { + case OLD_DATA_SPACE: + SweepConservatively(space, p); + break; + case OLD_POINTER_SPACE: + SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, IGNORE_SKIP_LIST>( + space, p, &updating_visitor); + break; + case CODE_SPACE: + SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, REBUILD_SKIP_LIST>( + space, p, &updating_visitor); + break; + default: + UNREACHABLE(); + break; + } + } } -#endif } - Heap* heap_; -}; - + GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_UPDATE_MISC_POINTERS); -void MarkCompactCollector::UpdatePointers() { -#ifdef DEBUG - ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES); - state_ = UPDATE_POINTERS; -#endif - UpdatingVisitor updating_visitor(heap()); - heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact( - &updating_visitor); - heap()->IterateRoots(&updating_visitor, VISIT_ONLY_STRONG); - heap()->isolate()->global_handles()->IterateWeakRoots(&updating_visitor); - - // Update the pointer to the head of the weak list of global contexts. - updating_visitor.VisitPointer(&heap()->global_contexts_list_); - - LiveObjectList::IterateElements(&updating_visitor); - - int live_maps_size = IterateLiveObjects( - heap()->map_space(), &MarkCompactCollector::UpdatePointersInOldObject); - int live_pointer_olds_size = IterateLiveObjects( - heap()->old_pointer_space(), - &MarkCompactCollector::UpdatePointersInOldObject); - int live_data_olds_size = IterateLiveObjects( - heap()->old_data_space(), - &MarkCompactCollector::UpdatePointersInOldObject); - int live_codes_size = IterateLiveObjects( - heap()->code_space(), &MarkCompactCollector::UpdatePointersInOldObject); - int live_cells_size = IterateLiveObjects( - heap()->cell_space(), &MarkCompactCollector::UpdatePointersInOldObject); - int live_news_size = IterateLiveObjects( - heap()->new_space(), &MarkCompactCollector::UpdatePointersInNewObject); - - // Large objects do not move, the map word can be updated directly. - LargeObjectIterator it(heap()->lo_space()); - for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { - UpdatePointersInNewObject(obj); - } - - USE(live_maps_size); - USE(live_pointer_olds_size); - USE(live_data_olds_size); - USE(live_codes_size); - USE(live_cells_size); - USE(live_news_size); - ASSERT(live_maps_size == live_map_objects_size_); - ASSERT(live_data_olds_size == live_old_data_objects_size_); - ASSERT(live_pointer_olds_size == live_old_pointer_objects_size_); - ASSERT(live_codes_size == live_code_objects_size_); - ASSERT(live_cells_size == live_cell_objects_size_); - ASSERT(live_news_size == live_young_objects_size_); -} - - -int MarkCompactCollector::UpdatePointersInNewObject(HeapObject* obj) { - // Keep old map pointers - Map* old_map = obj->map(); - ASSERT(old_map->IsHeapObject()); - - Address forwarded = GetForwardingAddressInOldSpace(old_map); - - ASSERT(heap()->map_space()->Contains(old_map)); - ASSERT(heap()->map_space()->Contains(forwarded)); -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("update %p : %p -> %p\n", obj->address(), old_map->address(), - forwarded); + // Update pointers from cells. + HeapObjectIterator cell_iterator(heap_->cell_space()); + for (HeapObject* cell = cell_iterator.Next(); + cell != NULL; + cell = cell_iterator.Next()) { + if (cell->IsJSGlobalPropertyCell()) { + Address value_address = + reinterpret_cast<Address>(cell) + + (JSGlobalPropertyCell::kValueOffset - kHeapObjectTag); + updating_visitor.VisitPointer(reinterpret_cast<Object**>(value_address)); + } } -#endif - // Update the map pointer. - obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(forwarded))); - // We have to compute the object size relying on the old map because - // map objects are not relocated yet. - int obj_size = obj->SizeFromMap(old_map); + // Update pointer from the global contexts list. + updating_visitor.VisitPointer(heap_->global_contexts_list_address()); - // Update pointers in the object body. - UpdatingVisitor updating_visitor(heap()); - obj->IterateBody(old_map->instance_type(), obj_size, &updating_visitor); - return obj_size; -} + heap_->symbol_table()->Iterate(&updating_visitor); + // Update pointers from external string table. + heap_->UpdateReferencesInExternalStringTable( + &UpdateReferenceInExternalStringTableEntry); -int MarkCompactCollector::UpdatePointersInOldObject(HeapObject* obj) { - // Decode the map pointer. - MapWord encoding = obj->map_word(); - Address map_addr = encoding.DecodeMapAddress(heap()->map_space()); - ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr))); + if (!FLAG_watch_ic_patching) { + // Update JSFunction pointers from the runtime profiler. + heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact( + &updating_visitor); + } - // At this point, the first word of map_addr is also encoded, cannot - // cast it to Map* using Map::cast. - Map* map = reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr)); - int obj_size = obj->SizeFromMap(map); - InstanceType type = map->instance_type(); + EvacuationWeakObjectRetainer evacuation_object_retainer; + heap()->ProcessWeakReferences(&evacuation_object_retainer); - // Update map pointer. - Address new_map_addr = GetForwardingAddressInOldSpace(map); - int offset = encoding.DecodeOffset(); - obj->set_map_word(MapWord::EncodeAddress(new_map_addr, offset)); + // Visit invalidated code (we ignored all slots on it) and clear mark-bits + // under it. + ProcessInvalidatedCode(&updating_visitor); #ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("update %p : %p -> %p\n", obj->address(), - map_addr, new_map_addr); + if (FLAG_verify_heap) { + VerifyEvacuation(heap_); } #endif - // Update pointers in the object body. - UpdatingVisitor updating_visitor(heap()); - obj->IterateBody(type, obj_size, &updating_visitor); - return obj_size; + slots_buffer_allocator_.DeallocateChain(&migration_slots_buffer_); + ASSERT(migration_slots_buffer_ == NULL); + for (int i = 0; i < npages; i++) { + Page* p = evacuation_candidates_[i]; + if (!p->IsEvacuationCandidate()) continue; + PagedSpace* space = static_cast<PagedSpace*>(p->owner()); + space->Free(p->ObjectAreaStart(), Page::kObjectAreaSize); + p->set_scan_on_scavenge(false); + slots_buffer_allocator_.DeallocateChain(p->slots_buffer_address()); + p->ClearEvacuationCandidate(); + p->ResetLiveBytes(); + space->ReleasePage(p); + } + evacuation_candidates_.Rewind(0); + compacting_ = false; } -Address MarkCompactCollector::GetForwardingAddressInOldSpace(HeapObject* obj) { - // Object should either in old or map space. - MapWord encoding = obj->map_word(); +static const int kStartTableEntriesPerLine = 5; +static const int kStartTableLines = 171; +static const int kStartTableInvalidLine = 127; +static const int kStartTableUnusedEntry = 126; - // Offset to the first live object's forwarding address. - int offset = encoding.DecodeOffset(); - Address obj_addr = obj->address(); +#define _ kStartTableUnusedEntry +#define X kStartTableInvalidLine +// Mark-bit to object start offset table. +// +// The line is indexed by the mark bits in a byte. The first number on +// the line describes the number of live object starts for the line and the +// other numbers on the line describe the offsets (in words) of the object +// starts. +// +// Since objects are at least 2 words large we don't have entries for two +// consecutive 1 bits. All entries after 170 have at least 2 consecutive bits. +char kStartTable[kStartTableLines * kStartTableEntriesPerLine] = { + 0, _, _, _, _, // 0 + 1, 0, _, _, _, // 1 + 1, 1, _, _, _, // 2 + X, _, _, _, _, // 3 + 1, 2, _, _, _, // 4 + 2, 0, 2, _, _, // 5 + X, _, _, _, _, // 6 + X, _, _, _, _, // 7 + 1, 3, _, _, _, // 8 + 2, 0, 3, _, _, // 9 + 2, 1, 3, _, _, // 10 + X, _, _, _, _, // 11 + X, _, _, _, _, // 12 + X, _, _, _, _, // 13 + X, _, _, _, _, // 14 + X, _, _, _, _, // 15 + 1, 4, _, _, _, // 16 + 2, 0, 4, _, _, // 17 + 2, 1, 4, _, _, // 18 + X, _, _, _, _, // 19 + 2, 2, 4, _, _, // 20 + 3, 0, 2, 4, _, // 21 + X, _, _, _, _, // 22 + X, _, _, _, _, // 23 + X, _, _, _, _, // 24 + X, _, _, _, _, // 25 + X, _, _, _, _, // 26 + X, _, _, _, _, // 27 + X, _, _, _, _, // 28 + X, _, _, _, _, // 29 + X, _, _, _, _, // 30 + X, _, _, _, _, // 31 + 1, 5, _, _, _, // 32 + 2, 0, 5, _, _, // 33 + 2, 1, 5, _, _, // 34 + X, _, _, _, _, // 35 + 2, 2, 5, _, _, // 36 + 3, 0, 2, 5, _, // 37 + X, _, _, _, _, // 38 + X, _, _, _, _, // 39 + 2, 3, 5, _, _, // 40 + 3, 0, 3, 5, _, // 41 + 3, 1, 3, 5, _, // 42 + X, _, _, _, _, // 43 + X, _, _, _, _, // 44 + X, _, _, _, _, // 45 + X, _, _, _, _, // 46 + X, _, _, _, _, // 47 + X, _, _, _, _, // 48 + X, _, _, _, _, // 49 + X, _, _, _, _, // 50 + X, _, _, _, _, // 51 + X, _, _, _, _, // 52 + X, _, _, _, _, // 53 + X, _, _, _, _, // 54 + X, _, _, _, _, // 55 + X, _, _, _, _, // 56 + X, _, _, _, _, // 57 + X, _, _, _, _, // 58 + X, _, _, _, _, // 59 + X, _, _, _, _, // 60 + X, _, _, _, _, // 61 + X, _, _, _, _, // 62 + X, _, _, _, _, // 63 + 1, 6, _, _, _, // 64 + 2, 0, 6, _, _, // 65 + 2, 1, 6, _, _, // 66 + X, _, _, _, _, // 67 + 2, 2, 6, _, _, // 68 + 3, 0, 2, 6, _, // 69 + X, _, _, _, _, // 70 + X, _, _, _, _, // 71 + 2, 3, 6, _, _, // 72 + 3, 0, 3, 6, _, // 73 + 3, 1, 3, 6, _, // 74 + X, _, _, _, _, // 75 + X, _, _, _, _, // 76 + X, _, _, _, _, // 77 + X, _, _, _, _, // 78 + X, _, _, _, _, // 79 + 2, 4, 6, _, _, // 80 + 3, 0, 4, 6, _, // 81 + 3, 1, 4, 6, _, // 82 + X, _, _, _, _, // 83 + 3, 2, 4, 6, _, // 84 + 4, 0, 2, 4, 6, // 85 + X, _, _, _, _, // 86 + X, _, _, _, _, // 87 + X, _, _, _, _, // 88 + X, _, _, _, _, // 89 + X, _, _, _, _, // 90 + X, _, _, _, _, // 91 + X, _, _, _, _, // 92 + X, _, _, _, _, // 93 + X, _, _, _, _, // 94 + X, _, _, _, _, // 95 + X, _, _, _, _, // 96 + X, _, _, _, _, // 97 + X, _, _, _, _, // 98 + X, _, _, _, _, // 99 + X, _, _, _, _, // 100 + X, _, _, _, _, // 101 + X, _, _, _, _, // 102 + X, _, _, _, _, // 103 + X, _, _, _, _, // 104 + X, _, _, _, _, // 105 + X, _, _, _, _, // 106 + X, _, _, _, _, // 107 + X, _, _, _, _, // 108 + X, _, _, _, _, // 109 + X, _, _, _, _, // 110 + X, _, _, _, _, // 111 + X, _, _, _, _, // 112 + X, _, _, _, _, // 113 + X, _, _, _, _, // 114 + X, _, _, _, _, // 115 + X, _, _, _, _, // 116 + X, _, _, _, _, // 117 + X, _, _, _, _, // 118 + X, _, _, _, _, // 119 + X, _, _, _, _, // 120 + X, _, _, _, _, // 121 + X, _, _, _, _, // 122 + X, _, _, _, _, // 123 + X, _, _, _, _, // 124 + X, _, _, _, _, // 125 + X, _, _, _, _, // 126 + X, _, _, _, _, // 127 + 1, 7, _, _, _, // 128 + 2, 0, 7, _, _, // 129 + 2, 1, 7, _, _, // 130 + X, _, _, _, _, // 131 + 2, 2, 7, _, _, // 132 + 3, 0, 2, 7, _, // 133 + X, _, _, _, _, // 134 + X, _, _, _, _, // 135 + 2, 3, 7, _, _, // 136 + 3, 0, 3, 7, _, // 137 + 3, 1, 3, 7, _, // 138 + X, _, _, _, _, // 139 + X, _, _, _, _, // 140 + X, _, _, _, _, // 141 + X, _, _, _, _, // 142 + X, _, _, _, _, // 143 + 2, 4, 7, _, _, // 144 + 3, 0, 4, 7, _, // 145 + 3, 1, 4, 7, _, // 146 + X, _, _, _, _, // 147 + 3, 2, 4, 7, _, // 148 + 4, 0, 2, 4, 7, // 149 + X, _, _, _, _, // 150 + X, _, _, _, _, // 151 + X, _, _, _, _, // 152 + X, _, _, _, _, // 153 + X, _, _, _, _, // 154 + X, _, _, _, _, // 155 + X, _, _, _, _, // 156 + X, _, _, _, _, // 157 + X, _, _, _, _, // 158 + X, _, _, _, _, // 159 + 2, 5, 7, _, _, // 160 + 3, 0, 5, 7, _, // 161 + 3, 1, 5, 7, _, // 162 + X, _, _, _, _, // 163 + 3, 2, 5, 7, _, // 164 + 4, 0, 2, 5, 7, // 165 + X, _, _, _, _, // 166 + X, _, _, _, _, // 167 + 3, 3, 5, 7, _, // 168 + 4, 0, 3, 5, 7, // 169 + 4, 1, 3, 5, 7 // 170 +}; +#undef _ +#undef X + + +// Takes a word of mark bits. Returns the number of objects that start in the +// range. Puts the offsets of the words in the supplied array. +static inline int MarkWordToObjectStarts(uint32_t mark_bits, int* starts) { + int objects = 0; + int offset = 0; + + // No consecutive 1 bits. + ASSERT((mark_bits & 0x180) != 0x180); + ASSERT((mark_bits & 0x18000) != 0x18000); + ASSERT((mark_bits & 0x1800000) != 0x1800000); + + while (mark_bits != 0) { + int byte = (mark_bits & 0xff); + mark_bits >>= 8; + if (byte != 0) { + ASSERT(byte < kStartTableLines); // No consecutive 1 bits. + char* table = kStartTable + byte * kStartTableEntriesPerLine; + int objects_in_these_8_words = table[0]; + ASSERT(objects_in_these_8_words != kStartTableInvalidLine); + ASSERT(objects_in_these_8_words < kStartTableEntriesPerLine); + for (int i = 0; i < objects_in_these_8_words; i++) { + starts[objects++] = offset + table[1 + i]; + } + } + offset += 8; + } + return objects; +} - // Find the first live object's forwarding address. - Page* p = Page::FromAddress(obj_addr); - Address first_forwarded = p->mc_first_forwarded; - // Page start address of forwarded address. - Page* forwarded_page = Page::FromAddress(first_forwarded); - int forwarded_offset = forwarded_page->Offset(first_forwarded); +static inline Address DigestFreeStart(Address approximate_free_start, + uint32_t free_start_cell) { + ASSERT(free_start_cell != 0); - // Find end of allocation in the page of first_forwarded. - int mc_top_offset = forwarded_page->AllocationWatermarkOffset(); + // No consecutive 1 bits. + ASSERT((free_start_cell & (free_start_cell << 1)) == 0); - // Check if current object's forward pointer is in the same page - // as the first live object's forwarding pointer - if (forwarded_offset + offset < mc_top_offset) { - // In the same page. - return first_forwarded + offset; - } + int offsets[16]; + uint32_t cell = free_start_cell; + int offset_of_last_live; + if ((cell & 0x80000000u) != 0) { + // This case would overflow below. + offset_of_last_live = 31; + } else { + // Remove all but one bit, the most significant. This is an optimization + // that may or may not be worthwhile. + cell |= cell >> 16; + cell |= cell >> 8; + cell |= cell >> 4; + cell |= cell >> 2; + cell |= cell >> 1; + cell = (cell + 1) >> 1; + int live_objects = MarkWordToObjectStarts(cell, offsets); + ASSERT(live_objects == 1); + offset_of_last_live = offsets[live_objects - 1]; + } + Address last_live_start = + approximate_free_start + offset_of_last_live * kPointerSize; + HeapObject* last_live = HeapObject::FromAddress(last_live_start); + Address free_start = last_live_start + last_live->Size(); + return free_start; +} - // Must be in the next page, NOTE: this may cross chunks. - Page* next_page = forwarded_page->next_page(); - ASSERT(next_page->is_valid()); - offset -= (mc_top_offset - forwarded_offset); - offset += Page::kObjectStartOffset; +static inline Address StartOfLiveObject(Address block_address, uint32_t cell) { + ASSERT(cell != 0); - ASSERT_PAGE_OFFSET(offset); - ASSERT(next_page->OffsetToAddress(offset) < next_page->AllocationTop()); + // No consecutive 1 bits. + ASSERT((cell & (cell << 1)) == 0); - return next_page->OffsetToAddress(offset); + int offsets[16]; + if (cell == 0x80000000u) { // Avoid overflow below. + return block_address + 31 * kPointerSize; + } + uint32_t first_set_bit = ((cell ^ (cell - 1)) + 1) >> 1; + ASSERT((first_set_bit & cell) == first_set_bit); + int live_objects = MarkWordToObjectStarts(first_set_bit, offsets); + ASSERT(live_objects == 1); + USE(live_objects); + return block_address + offsets[0] * kPointerSize; } -// ------------------------------------------------------------------------- -// Phase 4: Relocate objects - -void MarkCompactCollector::RelocateObjects() { -#ifdef DEBUG - ASSERT(state_ == UPDATE_POINTERS); - state_ = RELOCATE_OBJECTS; -#endif - // Relocates objects, always relocate map objects first. Relocating - // objects in other space relies on map objects to get object size. - int live_maps_size = IterateLiveObjects( - heap()->map_space(), &MarkCompactCollector::RelocateMapObject); - int live_pointer_olds_size = IterateLiveObjects( - heap()->old_pointer_space(), - &MarkCompactCollector::RelocateOldPointerObject); - int live_data_olds_size = IterateLiveObjects( - heap()->old_data_space(), &MarkCompactCollector::RelocateOldDataObject); - int live_codes_size = IterateLiveObjects( - heap()->code_space(), &MarkCompactCollector::RelocateCodeObject); - int live_cells_size = IterateLiveObjects( - heap()->cell_space(), &MarkCompactCollector::RelocateCellObject); - int live_news_size = IterateLiveObjects( - heap()->new_space(), &MarkCompactCollector::RelocateNewObject); - - USE(live_maps_size); - USE(live_pointer_olds_size); - USE(live_data_olds_size); - USE(live_codes_size); - USE(live_cells_size); - USE(live_news_size); - ASSERT(live_maps_size == live_map_objects_size_); - ASSERT(live_data_olds_size == live_old_data_objects_size_); - ASSERT(live_pointer_olds_size == live_old_pointer_objects_size_); - ASSERT(live_codes_size == live_code_objects_size_); - ASSERT(live_cells_size == live_cell_objects_size_); - ASSERT(live_news_size == live_young_objects_size_); - - // Flip from and to spaces - heap()->new_space()->Flip(); - - heap()->new_space()->MCCommitRelocationInfo(); - - // Set age_mark to bottom in to space - Address mark = heap()->new_space()->bottom(); - heap()->new_space()->set_age_mark(mark); +// Sweeps a space conservatively. After this has been done the larger free +// spaces have been put on the free list and the smaller ones have been +// ignored and left untouched. A free space is always either ignored or put +// on the free list, never split up into two parts. This is important +// because it means that any FreeSpace maps left actually describe a region of +// memory that can be ignored when scanning. Dead objects other than free +// spaces will not contain the free space map. +intptr_t MarkCompactCollector::SweepConservatively(PagedSpace* space, Page* p) { + ASSERT(!p->IsEvacuationCandidate() && !p->WasSwept()); + MarkBit::CellType* cells = p->markbits()->cells(); + p->MarkSweptConservatively(); + + int last_cell_index = + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(p->ObjectAreaEnd()))); + + int cell_index = Page::kFirstUsedCell; + intptr_t freed_bytes = 0; + + // This is the start of the 32 word block that we are currently looking at. + Address block_address = p->ObjectAreaStart(); + + // Skip over all the dead objects at the start of the page and mark them free. + for (cell_index = Page::kFirstUsedCell; + cell_index < last_cell_index; + cell_index++, block_address += 32 * kPointerSize) { + if (cells[cell_index] != 0) break; + } + size_t size = block_address - p->ObjectAreaStart(); + if (cell_index == last_cell_index) { + freed_bytes += static_cast<int>(space->Free(p->ObjectAreaStart(), + static_cast<int>(size))); + ASSERT_EQ(0, p->LiveBytes()); + return freed_bytes; + } + // Grow the size of the start-of-page free space a little to get up to the + // first live object. + Address free_end = StartOfLiveObject(block_address, cells[cell_index]); + // Free the first free space. + size = free_end - p->ObjectAreaStart(); + freed_bytes += space->Free(p->ObjectAreaStart(), + static_cast<int>(size)); + // The start of the current free area is represented in undigested form by + // the address of the last 32-word section that contained a live object and + // the marking bitmap for that cell, which describes where the live object + // started. Unless we find a large free space in the bitmap we will not + // digest this pair into a real address. We start the iteration here at the + // first word in the marking bit map that indicates a live object. + Address free_start = block_address; + uint32_t free_start_cell = cells[cell_index]; + + for ( ; + cell_index < last_cell_index; + cell_index++, block_address += 32 * kPointerSize) { + ASSERT((unsigned)cell_index == + Bitmap::IndexToCell( + Bitmap::CellAlignIndex( + p->AddressToMarkbitIndex(block_address)))); + uint32_t cell = cells[cell_index]; + if (cell != 0) { + // We have a live object. Check approximately whether it is more than 32 + // words since the last live object. + if (block_address - free_start > 32 * kPointerSize) { + free_start = DigestFreeStart(free_start, free_start_cell); + if (block_address - free_start > 32 * kPointerSize) { + // Now that we know the exact start of the free space it still looks + // like we have a large enough free space to be worth bothering with. + // so now we need to find the start of the first live object at the + // end of the free space. + free_end = StartOfLiveObject(block_address, cell); + freed_bytes += space->Free(free_start, + static_cast<int>(free_end - free_start)); + } + } + // Update our undigested record of where the current free area started. + free_start = block_address; + free_start_cell = cell; + // Clear marking bits for current cell. + cells[cell_index] = 0; + } + } - PagedSpaces spaces; - for (PagedSpace* space = spaces.next(); space != NULL; space = spaces.next()) - space->MCCommitRelocationInfo(); + // Handle the free space at the end of the page. + if (block_address - free_start > 32 * kPointerSize) { + free_start = DigestFreeStart(free_start, free_start_cell); + freed_bytes += space->Free(free_start, + static_cast<int>(block_address - free_start)); + } - heap()->CheckNewSpaceExpansionCriteria(); - heap()->IncrementYoungSurvivorsCounter(live_news_size); + p->ResetLiveBytes(); + return freed_bytes; } -int MarkCompactCollector::RelocateMapObject(HeapObject* obj) { - // Recover map pointer. - MapWord encoding = obj->map_word(); - Address map_addr = encoding.DecodeMapAddress(heap()->map_space()); - ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr))); +void MarkCompactCollector::SweepSpace(PagedSpace* space, SweeperType sweeper) { + space->set_was_swept_conservatively(sweeper == CONSERVATIVE || + sweeper == LAZY_CONSERVATIVE); - // Get forwarding address before resetting map pointer - Address new_addr = GetForwardingAddressInOldSpace(obj); + space->ClearStats(); - // Reset map pointer. The meta map object may not be copied yet so - // Map::cast does not yet work. - obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr))); + PageIterator it(space); - Address old_addr = obj->address(); + intptr_t freed_bytes = 0; + int pages_swept = 0; + intptr_t newspace_size = space->heap()->new_space()->Size(); + bool lazy_sweeping_active = false; + bool unused_page_present = false; - if (new_addr != old_addr) { - // Move contents. - heap()->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr, - old_addr, - Map::kSize); - } + intptr_t old_space_size = heap()->PromotedSpaceSize(); + intptr_t space_left = + Min(heap()->OldGenPromotionLimit(old_space_size), + heap()->OldGenAllocationLimit(old_space_size)) - old_space_size; -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("relocate %p -> %p\n", old_addr, new_addr); - } -#endif + while (it.has_next()) { + Page* p = it.next(); - return Map::kSize; -} + // Clear sweeping flags indicating that marking bits are still intact. + p->ClearSweptPrecisely(); + p->ClearSweptConservatively(); + if (p->IsEvacuationCandidate()) { + ASSERT(evacuation_candidates_.length() > 0); + continue; + } -static inline int RestoreMap(HeapObject* obj, - PagedSpace* space, - Address new_addr, - Address map_addr) { - // This must be a non-map object, and the function relies on the - // assumption that the Map space is compacted before the other paged - // spaces (see RelocateObjects). + if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { + // Will be processed in EvacuateNewSpaceAndCandidates. + continue; + } - // Reset map pointer. - obj->set_map(Map::cast(HeapObject::FromAddress(map_addr))); + // One unused page is kept, all further are released before sweeping them. + if (p->LiveBytes() == 0) { + if (unused_page_present) { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " released page.\n", + reinterpret_cast<intptr_t>(p)); + } + // Adjust unswept free bytes because releasing a page expects said + // counter to be accurate for unswept pages. + space->IncreaseUnsweptFreeBytes(p); + space->ReleasePage(p); + continue; + } + unused_page_present = true; + } - int obj_size = obj->Size(); - ASSERT_OBJECT_SIZE(obj_size); + if (lazy_sweeping_active) { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " lazily postponed.\n", + reinterpret_cast<intptr_t>(p)); + } + space->IncreaseUnsweptFreeBytes(p); + continue; + } - ASSERT(space->MCSpaceOffsetForAddress(new_addr) <= - space->MCSpaceOffsetForAddress(obj->address())); + switch (sweeper) { + case CONSERVATIVE: { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " conservatively.\n", + reinterpret_cast<intptr_t>(p)); + } + SweepConservatively(space, p); + pages_swept++; + break; + } + case LAZY_CONSERVATIVE: { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " conservatively as needed.\n", + reinterpret_cast<intptr_t>(p)); + } + freed_bytes += SweepConservatively(space, p); + pages_swept++; + if (space_left + freed_bytes > newspace_size) { + space->SetPagesToSweep(p->next_page()); + lazy_sweeping_active = true; + } else { + if (FLAG_gc_verbose) { + PrintF("Only %" V8PRIdPTR " bytes freed. Still sweeping.\n", + freed_bytes); + } + } + break; + } + case PRECISE: { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " precisely.\n", + reinterpret_cast<intptr_t>(p)); + } + if (space->identity() == CODE_SPACE) { + SweepPrecisely<SWEEP_ONLY, REBUILD_SKIP_LIST>(space, p, NULL); + } else { + SweepPrecisely<SWEEP_ONLY, IGNORE_SKIP_LIST>(space, p, NULL); + } + pages_swept++; + break; + } + default: { + UNREACHABLE(); + } + } + } -#ifdef DEBUG if (FLAG_gc_verbose) { - PrintF("relocate %p -> %p\n", obj->address(), new_addr); + PrintF("SweepSpace: %s (%d pages swept)\n", + AllocationSpaceName(space->identity()), + pages_swept); } -#endif - return obj_size; + // Give pages that are queued to be freed back to the OS. + heap()->FreeQueuedChunks(); } -int MarkCompactCollector::RelocateOldNonCodeObject(HeapObject* obj, - PagedSpace* space) { - // Recover map pointer. - MapWord encoding = obj->map_word(); - Address map_addr = encoding.DecodeMapAddress(heap()->map_space()); - ASSERT(heap()->map_space()->Contains(map_addr)); - - // Get forwarding address before resetting map pointer. - Address new_addr = GetForwardingAddressInOldSpace(obj); - - // Reset the map pointer. - int obj_size = RestoreMap(obj, space, new_addr, map_addr); +void MarkCompactCollector::SweepSpaces() { + GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP); +#ifdef DEBUG + state_ = SWEEP_SPACES; +#endif + SweeperType how_to_sweep = + FLAG_lazy_sweeping ? LAZY_CONSERVATIVE : CONSERVATIVE; + if (FLAG_expose_gc) how_to_sweep = CONSERVATIVE; + if (sweep_precisely_) how_to_sweep = PRECISE; + // Noncompacting collections simply sweep the spaces to clear the mark + // bits and free the nonlive blocks (for old and map spaces). We sweep + // the map space last because freeing non-live maps overwrites them and + // the other spaces rely on possibly non-live maps to get the sizes for + // non-live objects. + SweepSpace(heap()->old_pointer_space(), how_to_sweep); + SweepSpace(heap()->old_data_space(), how_to_sweep); - Address old_addr = obj->address(); + RemoveDeadInvalidatedCode(); + SweepSpace(heap()->code_space(), PRECISE); - if (new_addr != old_addr) { - // Move contents. - if (space == heap()->old_data_space()) { - heap()->MoveBlock(new_addr, old_addr, obj_size); - } else { - heap()->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr, - old_addr, - obj_size); - } - } + SweepSpace(heap()->cell_space(), PRECISE); - ASSERT(!HeapObject::FromAddress(new_addr)->IsCode()); + EvacuateNewSpaceAndCandidates(); - HeapObject* copied_to = HeapObject::FromAddress(new_addr); - if (copied_to->IsSharedFunctionInfo()) { - PROFILE(heap()->isolate(), - SharedFunctionInfoMoveEvent(old_addr, new_addr)); - } - HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr)); + // ClearNonLiveTransitions depends on precise sweeping of map space to + // detect whether unmarked map became dead in this collection or in one + // of the previous ones. + SweepSpace(heap()->map_space(), PRECISE); - return obj_size; + // Deallocate unmarked objects and clear marked bits for marked objects. + heap_->lo_space()->FreeUnmarkedObjects(); } -int MarkCompactCollector::RelocateOldPointerObject(HeapObject* obj) { - return RelocateOldNonCodeObject(obj, heap()->old_pointer_space()); +void MarkCompactCollector::EnableCodeFlushing(bool enable) { + if (enable) { + if (code_flusher_ != NULL) return; + code_flusher_ = new CodeFlusher(heap()->isolate()); + } else { + if (code_flusher_ == NULL) return; + delete code_flusher_; + code_flusher_ = NULL; + } } -int MarkCompactCollector::RelocateOldDataObject(HeapObject* obj) { - return RelocateOldNonCodeObject(obj, heap()->old_data_space()); +// TODO(1466) ReportDeleteIfNeeded is not called currently. +// Our profiling tools do not expect intersections between +// code objects. We should either reenable it or change our tools. +void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj, + Isolate* isolate) { +#ifdef ENABLE_GDB_JIT_INTERFACE + if (obj->IsCode()) { + GDBJITInterface::RemoveCode(reinterpret_cast<Code*>(obj)); + } +#endif + if (obj->IsCode()) { + PROFILE(isolate, CodeDeleteEvent(obj->address())); + } } -int MarkCompactCollector::RelocateCellObject(HeapObject* obj) { - return RelocateOldNonCodeObject(obj, heap()->cell_space()); +void MarkCompactCollector::Initialize() { + StaticMarkingVisitor::Initialize(); } -int MarkCompactCollector::RelocateCodeObject(HeapObject* obj) { - // Recover map pointer. - MapWord encoding = obj->map_word(); - Address map_addr = encoding.DecodeMapAddress(heap()->map_space()); - ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr))); +bool SlotsBuffer::IsTypedSlot(ObjectSlot slot) { + return reinterpret_cast<uintptr_t>(slot) < NUMBER_OF_SLOT_TYPES; +} - // Get forwarding address before resetting map pointer - Address new_addr = GetForwardingAddressInOldSpace(obj); - // Reset the map pointer. - int obj_size = RestoreMap(obj, heap()->code_space(), new_addr, map_addr); +bool SlotsBuffer::AddTo(SlotsBufferAllocator* allocator, + SlotsBuffer** buffer_address, + SlotType type, + Address addr, + AdditionMode mode) { + SlotsBuffer* buffer = *buffer_address; + if (buffer == NULL || !buffer->HasSpaceForTypedSlot()) { + if (mode == FAIL_ON_OVERFLOW && ChainLengthThresholdReached(buffer)) { + allocator->DeallocateChain(buffer_address); + return false; + } + buffer = allocator->AllocateBuffer(buffer); + *buffer_address = buffer; + } + ASSERT(buffer->HasSpaceForTypedSlot()); + buffer->Add(reinterpret_cast<ObjectSlot>(type)); + buffer->Add(reinterpret_cast<ObjectSlot>(addr)); + return true; +} - Address old_addr = obj->address(); - if (new_addr != old_addr) { - // Move contents. - heap()->MoveBlock(new_addr, old_addr, obj_size); +static inline SlotsBuffer::SlotType SlotTypeForRMode(RelocInfo::Mode rmode) { + if (RelocInfo::IsCodeTarget(rmode)) { + return SlotsBuffer::CODE_TARGET_SLOT; + } else if (RelocInfo::IsEmbeddedObject(rmode)) { + return SlotsBuffer::EMBEDDED_OBJECT_SLOT; + } else if (RelocInfo::IsDebugBreakSlot(rmode)) { + return SlotsBuffer::DEBUG_TARGET_SLOT; + } else if (RelocInfo::IsJSReturn(rmode)) { + return SlotsBuffer::JS_RETURN_SLOT; } + UNREACHABLE(); + return SlotsBuffer::NUMBER_OF_SLOT_TYPES; +} - HeapObject* copied_to = HeapObject::FromAddress(new_addr); - if (copied_to->IsCode()) { - // May also update inline cache target. - Code::cast(copied_to)->Relocate(new_addr - old_addr); - // Notify the logger that compiled code has moved. - PROFILE(heap()->isolate(), CodeMoveEvent(old_addr, new_addr)); - } - HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr)); - return obj_size; +void MarkCompactCollector::RecordRelocSlot(RelocInfo* rinfo, Object* target) { + Page* target_page = Page::FromAddress(reinterpret_cast<Address>(target)); + if (target_page->IsEvacuationCandidate() && + (rinfo->host() == NULL || + !ShouldSkipEvacuationSlotRecording(rinfo->host()))) { + if (!SlotsBuffer::AddTo(&slots_buffer_allocator_, + target_page->slots_buffer_address(), + SlotTypeForRMode(rinfo->rmode()), + rinfo->pc(), + SlotsBuffer::FAIL_ON_OVERFLOW)) { + EvictEvacuationCandidate(target_page); + } + } } -int MarkCompactCollector::RelocateNewObject(HeapObject* obj) { - int obj_size = obj->Size(); - - // Get forwarding address - Address old_addr = obj->address(); - int offset = heap()->new_space()->ToSpaceOffsetForAddress(old_addr); +void MarkCompactCollector::RecordCodeEntrySlot(Address slot, Code* target) { + Page* target_page = Page::FromAddress(reinterpret_cast<Address>(target)); + if (target_page->IsEvacuationCandidate() && + !ShouldSkipEvacuationSlotRecording(reinterpret_cast<Object**>(slot))) { + if (!SlotsBuffer::AddTo(&slots_buffer_allocator_, + target_page->slots_buffer_address(), + SlotsBuffer::CODE_ENTRY_SLOT, + slot, + SlotsBuffer::FAIL_ON_OVERFLOW)) { + EvictEvacuationCandidate(target_page); + } + } +} - Address new_addr = - Memory::Address_at(heap()->new_space()->FromSpaceLow() + offset); -#ifdef DEBUG - if (heap()->new_space()->FromSpaceContains(new_addr)) { - ASSERT(heap()->new_space()->FromSpaceOffsetForAddress(new_addr) <= - heap()->new_space()->ToSpaceOffsetForAddress(old_addr)); - } else { - ASSERT(heap()->TargetSpace(obj) == heap()->old_pointer_space() || - heap()->TargetSpace(obj) == heap()->old_data_space()); - } -#endif +static inline SlotsBuffer::SlotType DecodeSlotType( + SlotsBuffer::ObjectSlot slot) { + return static_cast<SlotsBuffer::SlotType>(reinterpret_cast<intptr_t>(slot)); +} - // New and old addresses cannot overlap. - if (heap()->InNewSpace(HeapObject::FromAddress(new_addr))) { - heap()->CopyBlock(new_addr, old_addr, obj_size); - } else { - heap()->CopyBlockToOldSpaceAndUpdateRegionMarks(new_addr, - old_addr, - obj_size); - } -#ifdef DEBUG - if (FLAG_gc_verbose) { - PrintF("relocate %p -> %p\n", old_addr, new_addr); - } -#endif +void SlotsBuffer::UpdateSlots(Heap* heap) { + PointersUpdatingVisitor v(heap); - HeapObject* copied_to = HeapObject::FromAddress(new_addr); - if (copied_to->IsSharedFunctionInfo()) { - PROFILE(heap()->isolate(), - SharedFunctionInfoMoveEvent(old_addr, new_addr)); + for (int slot_idx = 0; slot_idx < idx_; ++slot_idx) { + ObjectSlot slot = slots_[slot_idx]; + if (!IsTypedSlot(slot)) { + PointersUpdatingVisitor::UpdateSlot(heap, slot); + } else { + ++slot_idx; + ASSERT(slot_idx < idx_); + UpdateSlot(&v, + DecodeSlotType(slot), + reinterpret_cast<Address>(slots_[slot_idx])); + } } - HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr)); - - return obj_size; } -void MarkCompactCollector::EnableCodeFlushing(bool enable) { - if (enable) { - if (code_flusher_ != NULL) return; - code_flusher_ = new CodeFlusher(heap()->isolate()); - } else { - if (code_flusher_ == NULL) return; - delete code_flusher_; - code_flusher_ = NULL; +void SlotsBuffer::UpdateSlotsWithFilter(Heap* heap) { + PointersUpdatingVisitor v(heap); + + for (int slot_idx = 0; slot_idx < idx_; ++slot_idx) { + ObjectSlot slot = slots_[slot_idx]; + if (!IsTypedSlot(slot)) { + if (!IsOnInvalidatedCodeObject(reinterpret_cast<Address>(slot))) { + PointersUpdatingVisitor::UpdateSlot(heap, slot); + } + } else { + ++slot_idx; + ASSERT(slot_idx < idx_); + Address pc = reinterpret_cast<Address>(slots_[slot_idx]); + if (!IsOnInvalidatedCodeObject(pc)) { + UpdateSlot(&v, + DecodeSlotType(slot), + reinterpret_cast<Address>(slots_[slot_idx])); + } + } } } -void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj, - Isolate* isolate) { -#ifdef ENABLE_GDB_JIT_INTERFACE - if (obj->IsCode()) { - GDBJITInterface::RemoveCode(reinterpret_cast<Code*>(obj)); - } -#endif - if (obj->IsCode()) { - PROFILE(isolate, CodeDeleteEvent(obj->address())); - } +SlotsBuffer* SlotsBufferAllocator::AllocateBuffer(SlotsBuffer* next_buffer) { + return new SlotsBuffer(next_buffer); } -int MarkCompactCollector::SizeOfMarkedObject(HeapObject* obj) { - MapWord map_word = obj->map_word(); - map_word.ClearMark(); - return obj->SizeFromMap(map_word.ToMap()); +void SlotsBufferAllocator::DeallocateBuffer(SlotsBuffer* buffer) { + delete buffer; } -void MarkCompactCollector::Initialize() { - StaticPointersToNewGenUpdatingVisitor::Initialize(); - StaticMarkingVisitor::Initialize(); +void SlotsBufferAllocator::DeallocateChain(SlotsBuffer** buffer_address) { + SlotsBuffer* buffer = *buffer_address; + while (buffer != NULL) { + SlotsBuffer* next_buffer = buffer->next(); + DeallocateBuffer(buffer); + buffer = next_buffer; + } + *buffer_address = NULL; } diff --git a/deps/v8/src/mark-compact.h b/deps/v8/src/mark-compact.h index f72c813040..dc4bcee259 100644 --- a/deps/v8/src/mark-compact.h +++ b/deps/v8/src/mark-compact.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,6 +28,7 @@ #ifndef V8_MARK_COMPACT_H_ #define V8_MARK_COMPACT_H_ +#include "compiler-intrinsics.h" #include "spaces.h" namespace v8 { @@ -45,54 +46,340 @@ class MarkingVisitor; class RootMarkingVisitor; +class Marking { + public: + explicit Marking(Heap* heap) + : heap_(heap) { + } + + static inline MarkBit MarkBitFrom(Address addr); + + static inline MarkBit MarkBitFrom(HeapObject* obj) { + return MarkBitFrom(reinterpret_cast<Address>(obj)); + } + + // Impossible markbits: 01 + static const char* kImpossibleBitPattern; + static inline bool IsImpossible(MarkBit mark_bit) { + return !mark_bit.Get() && mark_bit.Next().Get(); + } + + // Black markbits: 10 - this is required by the sweeper. + static const char* kBlackBitPattern; + static inline bool IsBlack(MarkBit mark_bit) { + return mark_bit.Get() && !mark_bit.Next().Get(); + } + + // White markbits: 00 - this is required by the mark bit clearer. + static const char* kWhiteBitPattern; + static inline bool IsWhite(MarkBit mark_bit) { + return !mark_bit.Get(); + } + + // Grey markbits: 11 + static const char* kGreyBitPattern; + static inline bool IsGrey(MarkBit mark_bit) { + return mark_bit.Get() && mark_bit.Next().Get(); + } + + static inline void MarkBlack(MarkBit mark_bit) { + mark_bit.Set(); + mark_bit.Next().Clear(); + } + + static inline void BlackToGrey(MarkBit markbit) { + markbit.Next().Set(); + } + + static inline void WhiteToGrey(MarkBit markbit) { + markbit.Set(); + markbit.Next().Set(); + } + + static inline void GreyToBlack(MarkBit markbit) { + markbit.Next().Clear(); + } + + static inline void BlackToGrey(HeapObject* obj) { + BlackToGrey(MarkBitFrom(obj)); + } + + static inline void AnyToGrey(MarkBit markbit) { + markbit.Set(); + markbit.Next().Set(); + } + + // Returns true if the the object whose mark is transferred is marked black. + bool TransferMark(Address old_start, Address new_start); + +#ifdef DEBUG + enum ObjectColor { + BLACK_OBJECT, + WHITE_OBJECT, + GREY_OBJECT, + IMPOSSIBLE_COLOR + }; + + static const char* ColorName(ObjectColor color) { + switch (color) { + case BLACK_OBJECT: return "black"; + case WHITE_OBJECT: return "white"; + case GREY_OBJECT: return "grey"; + case IMPOSSIBLE_COLOR: return "impossible"; + } + return "error"; + } + + static ObjectColor Color(HeapObject* obj) { + return Color(Marking::MarkBitFrom(obj)); + } + + static ObjectColor Color(MarkBit mark_bit) { + if (IsBlack(mark_bit)) return BLACK_OBJECT; + if (IsWhite(mark_bit)) return WHITE_OBJECT; + if (IsGrey(mark_bit)) return GREY_OBJECT; + UNREACHABLE(); + return IMPOSSIBLE_COLOR; + } +#endif + + // Returns true if the transferred color is black. + INLINE(static bool TransferColor(HeapObject* from, + HeapObject* to)) { + MarkBit from_mark_bit = MarkBitFrom(from); + MarkBit to_mark_bit = MarkBitFrom(to); + bool is_black = false; + if (from_mark_bit.Get()) { + to_mark_bit.Set(); + is_black = true; // Looks black so far. + } + if (from_mark_bit.Next().Get()) { + to_mark_bit.Next().Set(); + is_black = false; // Was actually gray. + } + return is_black; + } + + private: + Heap* heap_; +}; + // ---------------------------------------------------------------------------- -// Marking stack for tracing live objects. +// Marking deque for tracing live objects. -class MarkingStack { +class MarkingDeque { public: - MarkingStack() : low_(NULL), top_(NULL), high_(NULL), overflowed_(false) { } + MarkingDeque() + : array_(NULL), top_(0), bottom_(0), mask_(0), overflowed_(false) { } void Initialize(Address low, Address high) { - top_ = low_ = reinterpret_cast<HeapObject**>(low); - high_ = reinterpret_cast<HeapObject**>(high); + HeapObject** obj_low = reinterpret_cast<HeapObject**>(low); + HeapObject** obj_high = reinterpret_cast<HeapObject**>(high); + array_ = obj_low; + mask_ = RoundDownToPowerOf2(static_cast<int>(obj_high - obj_low)) - 1; + top_ = bottom_ = 0; overflowed_ = false; } - bool is_full() const { return top_ >= high_; } + inline bool IsFull() { return ((top_ + 1) & mask_) == bottom_; } - bool is_empty() const { return top_ <= low_; } + inline bool IsEmpty() { return top_ == bottom_; } bool overflowed() const { return overflowed_; } - void clear_overflowed() { overflowed_ = false; } + void ClearOverflowed() { overflowed_ = false; } + + void SetOverflowed() { overflowed_ = true; } // Push the (marked) object on the marking stack if there is room, // otherwise mark the object as overflowed and wait for a rescan of the // heap. - void Push(HeapObject* object) { - CHECK(object->IsHeapObject()); - if (is_full()) { - object->SetOverflow(); - overflowed_ = true; + inline void PushBlack(HeapObject* object) { + ASSERT(object->IsHeapObject()); + if (IsFull()) { + Marking::BlackToGrey(object); + MemoryChunk::IncrementLiveBytesFromGC(object->address(), -object->Size()); + SetOverflowed(); } else { - *(top_++) = object; + array_[top_] = object; + top_ = ((top_ + 1) & mask_); } } - HeapObject* Pop() { - ASSERT(!is_empty()); - HeapObject* object = *(--top_); - CHECK(object->IsHeapObject()); + inline void PushGrey(HeapObject* object) { + ASSERT(object->IsHeapObject()); + if (IsFull()) { + SetOverflowed(); + } else { + array_[top_] = object; + top_ = ((top_ + 1) & mask_); + } + } + + inline HeapObject* Pop() { + ASSERT(!IsEmpty()); + top_ = ((top_ - 1) & mask_); + HeapObject* object = array_[top_]; + ASSERT(object->IsHeapObject()); return object; } + inline void UnshiftGrey(HeapObject* object) { + ASSERT(object->IsHeapObject()); + if (IsFull()) { + SetOverflowed(); + } else { + bottom_ = ((bottom_ - 1) & mask_); + array_[bottom_] = object; + } + } + + HeapObject** array() { return array_; } + int bottom() { return bottom_; } + int top() { return top_; } + int mask() { return mask_; } + void set_top(int top) { top_ = top; } + private: - HeapObject** low_; - HeapObject** top_; - HeapObject** high_; + HeapObject** array_; + // array_[(top - 1) & mask_] is the top element in the deque. The Deque is + // empty when top_ == bottom_. It is full when top_ + 1 == bottom + // (mod mask + 1). + int top_; + int bottom_; + int mask_; bool overflowed_; - DISALLOW_COPY_AND_ASSIGN(MarkingStack); + DISALLOW_COPY_AND_ASSIGN(MarkingDeque); +}; + + +class SlotsBufferAllocator { + public: + SlotsBuffer* AllocateBuffer(SlotsBuffer* next_buffer); + void DeallocateBuffer(SlotsBuffer* buffer); + + void DeallocateChain(SlotsBuffer** buffer_address); +}; + + +// SlotsBuffer records a sequence of slots that has to be updated +// after live objects were relocated from evacuation candidates. +// All slots are either untyped or typed: +// - Untyped slots are expected to contain a tagged object pointer. +// They are recorded by an address. +// - Typed slots are expected to contain an encoded pointer to a heap +// object where the way of encoding depends on the type of the slot. +// They are recorded as a pair (SlotType, slot address). +// We assume that zero-page is never mapped this allows us to distinguish +// untyped slots from typed slots during iteration by a simple comparison: +// if element of slots buffer is less than NUMBER_OF_SLOT_TYPES then it +// is the first element of typed slot's pair. +class SlotsBuffer { + public: + typedef Object** ObjectSlot; + + explicit SlotsBuffer(SlotsBuffer* next_buffer) + : idx_(0), chain_length_(1), next_(next_buffer) { + if (next_ != NULL) { + chain_length_ = next_->chain_length_ + 1; + } + } + + ~SlotsBuffer() { + } + + void Add(ObjectSlot slot) { + ASSERT(0 <= idx_ && idx_ < kNumberOfElements); + slots_[idx_++] = slot; + } + + enum SlotType { + EMBEDDED_OBJECT_SLOT, + RELOCATED_CODE_OBJECT, + CODE_TARGET_SLOT, + CODE_ENTRY_SLOT, + DEBUG_TARGET_SLOT, + JS_RETURN_SLOT, + NUMBER_OF_SLOT_TYPES + }; + + void UpdateSlots(Heap* heap); + + void UpdateSlotsWithFilter(Heap* heap); + + SlotsBuffer* next() { return next_; } + + static int SizeOfChain(SlotsBuffer* buffer) { + if (buffer == NULL) return 0; + return static_cast<int>(buffer->idx_ + + (buffer->chain_length_ - 1) * kNumberOfElements); + } + + inline bool IsFull() { + return idx_ == kNumberOfElements; + } + + inline bool HasSpaceForTypedSlot() { + return idx_ < kNumberOfElements - 1; + } + + static void UpdateSlotsRecordedIn(Heap* heap, + SlotsBuffer* buffer, + bool code_slots_filtering_required) { + while (buffer != NULL) { + if (code_slots_filtering_required) { + buffer->UpdateSlotsWithFilter(heap); + } else { + buffer->UpdateSlots(heap); + } + buffer = buffer->next(); + } + } + + enum AdditionMode { + FAIL_ON_OVERFLOW, + IGNORE_OVERFLOW + }; + + static bool ChainLengthThresholdReached(SlotsBuffer* buffer) { + return buffer != NULL && buffer->chain_length_ >= kChainLengthThreshold; + } + + static bool AddTo(SlotsBufferAllocator* allocator, + SlotsBuffer** buffer_address, + ObjectSlot slot, + AdditionMode mode) { + SlotsBuffer* buffer = *buffer_address; + if (buffer == NULL || buffer->IsFull()) { + if (mode == FAIL_ON_OVERFLOW && ChainLengthThresholdReached(buffer)) { + allocator->DeallocateChain(buffer_address); + return false; + } + buffer = allocator->AllocateBuffer(buffer); + *buffer_address = buffer; + } + buffer->Add(slot); + return true; + } + + static bool IsTypedSlot(ObjectSlot slot); + + static bool AddTo(SlotsBufferAllocator* allocator, + SlotsBuffer** buffer_address, + SlotType type, + Address addr, + AdditionMode mode); + + static const int kNumberOfElements = 1021; + + private: + static const int kChainLengthThreshold = 15; + + intptr_t idx_; + intptr_t chain_length_; + SlotsBuffer* next_; + ObjectSlot slots_[kNumberOfElements]; }; @@ -102,9 +389,6 @@ class ThreadLocalTop; // ------------------------------------------------------------------------- // Mark-Compact collector - -class OverflowedObjectsScanner; - class MarkCompactCollector { public: // Type of functions to compute forwarding addresses of objects in @@ -123,7 +407,7 @@ class MarkCompactCollector { // object from the forwarding address of the previous live object in the // page as input, and is updated to contain the offset to be used for the // next live object in the same page. For spaces using a different - // encoding (ie, contiguous spaces), the offset parameter is ignored. + // encoding (i.e., contiguous spaces), the offset parameter is ignored. typedef void (*EncodingFunction)(Heap* heap, HeapObject* old_object, int object_size, @@ -138,13 +422,18 @@ class MarkCompactCollector { // Set the global force_compaction flag, it must be called before Prepare // to take effect. - void SetForceCompaction(bool value) { - force_compaction_ = value; - } + inline void SetFlags(int flags); + inline bool PreciseSweepingRequired() { + return sweep_precisely_; + } static void Initialize(); + void CollectEvacuationCandidates(PagedSpace* space); + + void AddEvacuationCandidate(Page* p); + // Prepares for GC by resetting relocation info in old and map spaces and // choosing spaces to compact. void Prepare(GCTracer* tracer); @@ -152,23 +441,14 @@ class MarkCompactCollector { // Performs a global garbage collection. void CollectGarbage(); - // True if the last full GC performed heap compaction. - bool HasCompacted() { return compacting_collection_; } + enum CompactionMode { + INCREMENTAL_COMPACTION, + NON_INCREMENTAL_COMPACTION + }; - // True after the Prepare phase if the compaction is taking place. - bool IsCompacting() { -#ifdef DEBUG - // For the purposes of asserts we don't want this to keep returning true - // after the collection is completed. - return state_ != IDLE && compacting_collection_; -#else - return compacting_collection_; -#endif - } + bool StartCompaction(CompactionMode mode); - // The count of the number of objects left marked at the end of the last - // completed full GC (expected to be zero). - int previous_marked_count() { return previous_marked_count_; } + void AbortCompaction(); // During a full GC, there is a stack-allocated GCTracer that is used for // bookkeeping information. Return a pointer to that tracer. @@ -183,29 +463,101 @@ class MarkCompactCollector { // Determine type of object and emit deletion log event. static void ReportDeleteIfNeeded(HeapObject* obj, Isolate* isolate); - // Returns size of a possibly marked object. - static int SizeOfMarkedObject(HeapObject* obj); - // Distinguishable invalid map encodings (for single word and multiple words) // that indicate free regions. static const uint32_t kSingleFreeEncoding = 0; static const uint32_t kMultiFreeEncoding = 1; + static inline bool IsMarked(Object* obj); + inline Heap* heap() const { return heap_; } CodeFlusher* code_flusher() { return code_flusher_; } inline bool is_code_flushing_enabled() const { return code_flusher_ != NULL; } void EnableCodeFlushing(bool enable); + enum SweeperType { + CONSERVATIVE, + LAZY_CONSERVATIVE, + PRECISE + }; + +#ifdef DEBUG + void VerifyMarkbitsAreClean(); + static void VerifyMarkbitsAreClean(PagedSpace* space); + static void VerifyMarkbitsAreClean(NewSpace* space); +#endif + + // Sweep a single page from the given space conservatively. + // Return a number of reclaimed bytes. + static intptr_t SweepConservatively(PagedSpace* space, Page* p); + + INLINE(static bool ShouldSkipEvacuationSlotRecording(Object** anchor)) { + return Page::FromAddress(reinterpret_cast<Address>(anchor))-> + ShouldSkipEvacuationSlotRecording(); + } + + INLINE(static bool ShouldSkipEvacuationSlotRecording(Object* host)) { + return Page::FromAddress(reinterpret_cast<Address>(host))-> + ShouldSkipEvacuationSlotRecording(); + } + + INLINE(static bool IsOnEvacuationCandidate(Object* obj)) { + return Page::FromAddress(reinterpret_cast<Address>(obj))-> + IsEvacuationCandidate(); + } + + void EvictEvacuationCandidate(Page* page) { + if (FLAG_trace_fragmentation) { + PrintF("Page %p is too popular. Disabling evacuation.\n", + reinterpret_cast<void*>(page)); + } + + // TODO(gc) If all evacuation candidates are too popular we + // should stop slots recording entirely. + page->ClearEvacuationCandidate(); + + // We were not collecting slots on this page that point + // to other evacuation candidates thus we have to + // rescan the page after evacuation to discover and update all + // pointers to evacuated objects. + if (page->owner()->identity() == OLD_DATA_SPACE) { + evacuation_candidates_.RemoveElement(page); + } else { + page->SetFlag(Page::RESCAN_ON_EVACUATION); + } + } + + void RecordRelocSlot(RelocInfo* rinfo, Object* target); + void RecordCodeEntrySlot(Address slot, Code* target); + + INLINE(void RecordSlot(Object** anchor_slot, Object** slot, Object* object)); + + void MigrateObject(Address dst, + Address src, + int size, + AllocationSpace to_old_space); + + bool TryPromoteObject(HeapObject* object, int object_size); + inline Object* encountered_weak_maps() { return encountered_weak_maps_; } inline void set_encountered_weak_maps(Object* weak_map) { encountered_weak_maps_ = weak_map; } + void InvalidateCode(Code* code); + + void ClearMarkbits(); + private: MarkCompactCollector(); ~MarkCompactCollector(); + bool MarkInvalidatedCode(); + void RemoveDeadInvalidatedCode(); + void ProcessInvalidatedCode(ObjectVisitor* visitor); + + #ifdef DEBUG enum CollectorState { IDLE, @@ -221,23 +573,30 @@ class MarkCompactCollector { CollectorState state_; #endif - // Global flag that forces a compaction. - bool force_compaction_; + // Global flag that forces sweeping to be precise, so we can traverse the + // heap. + bool sweep_precisely_; + + bool reduce_memory_footprint_; + + // True if we are collecting slots to perform evacuation from evacuation + // candidates. + bool compacting_; - // Global flag indicating whether spaces were compacted on the last GC. - bool compacting_collection_; + bool was_marked_incrementally_; - // Global flag indicating whether spaces will be compacted on the next GC. - bool compact_on_next_gc_; + bool collect_maps_; - // The number of objects left marked at the end of the last completed full - // GC (expected to be zero). - int previous_marked_count_; + bool flush_monomorphic_ics_; // A pointer to the current stack-allocated GC tracer object during a full // collection (NULL before and after). GCTracer* tracer_; + SlotsBufferAllocator slots_buffer_allocator_; + + SlotsBuffer* migration_slots_buffer_; + // Finishes GC, performs heap verification if enabled. void Finish(); @@ -270,13 +629,22 @@ class MarkCompactCollector { // Marking operations for objects reachable from roots. void MarkLiveObjects(); - void MarkUnmarkedObject(HeapObject* obj); + void AfterMarking(); - inline void MarkObject(HeapObject* obj) { - if (!obj->IsMarked()) MarkUnmarkedObject(obj); - } + // Marks the object black and pushes it on the marking stack. + // This is for non-incremental marking. + INLINE(void MarkObject(HeapObject* obj, MarkBit mark_bit)); + + INLINE(bool MarkObjectWithoutPush(HeapObject* object)); + INLINE(void MarkObjectAndPush(HeapObject* value)); - inline void SetMark(HeapObject* obj); + // Marks the object black. This is for non-incremental marking. + INLINE(void SetMark(HeapObject* obj, MarkBit mark_bit)); + + // Clears the cache of ICs related to this map. + INLINE(void ClearCacheOnMap(Map* map)); + + void ProcessNewlyMarkedObject(HeapObject* obj); // Creates back pointers for all map transitions, stores them in // the prototype field. The original prototype pointers are restored @@ -287,6 +655,7 @@ class MarkCompactCollector { // Mark a Map and its DescriptorArray together, skipping transitions. void MarkMapContents(Map* map); + void MarkAccessorPairSlot(HeapObject* accessors, int offset); void MarkDescriptorArray(DescriptorArray* descriptors); // Mark the heap roots and all objects reachable from them. @@ -310,18 +679,18 @@ class MarkCompactCollector { // Mark objects reachable (transitively) from objects in the marking stack // or overflowed in the heap. - void ProcessMarkingStack(); + void ProcessMarkingDeque(); // Mark objects reachable (transitively) from objects in the marking // stack. This function empties the marking stack, but may leave // overflowed objects in the heap, in which case the marking stack's // overflow flag will be set. - void EmptyMarkingStack(); + void EmptyMarkingDeque(); // Refill the marking stack with overflowed objects from the heap. This // function either leaves the marking stack full or clears the overflow // flag on the marking stack. - void RefillMarkingStack(); + void RefillMarkingDeque(); // After reachable maps have been marked process per context object // literal map caches removing unmarked entries. @@ -331,20 +700,17 @@ class MarkCompactCollector { // heap object. static bool IsUnmarkedHeapObject(Object** p); -#ifdef DEBUG - void UpdateLiveObjectCount(HeapObject* obj); -#endif - - // We sweep the large object space in the same way whether we are - // compacting or not, because the large object space is never compacted. - void SweepLargeObjectSpace(); - - // Test whether a (possibly marked) object is a Map. - static inline bool SafeIsMap(HeapObject* object); - // Map transitions from a live map to a dead map must be killed. // We replace them with a null descriptor, with the same key. void ClearNonLiveTransitions(); + void ClearNonLivePrototypeTransitions(Map* map); + void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark); + + // Marking detaches initial maps from SharedFunctionInfo objects + // to make this reference weak. We need to reattach initial maps + // back after collection. This is either done during + // ClearNonLiveTransitions pass or by calling this function. + void ReattachInitialMaps(); // Mark all values associated with reachable keys in weak maps encountered // so far. This might push new object or even new weak maps onto the @@ -358,164 +724,31 @@ class MarkCompactCollector { // ----------------------------------------------------------------------- // Phase 2: Sweeping to clear mark bits and free non-live objects for - // a non-compacting collection, or else computing and encoding - // forwarding addresses for a compacting collection. + // a non-compacting collection. // // Before: Live objects are marked and non-live objects are unmarked. // - // After: (Non-compacting collection.) Live objects are unmarked, - // non-live regions have been added to their space's free - // list. - // - // After: (Compacting collection.) The forwarding address of live - // objects in the paged spaces is encoded in their map word - // along with their (non-forwarded) map pointer. - // - // The forwarding address of live objects in the new space is - // written to their map word's offset in the inactive - // semispace. - // - // Bookkeeping data is written to the page header of - // eached paged-space page that contains live objects after - // compaction: - // - // The allocation watermark field is used to track the - // relocation top address, the address of the first word - // after the end of the last live object in the page after - // compaction. + // After: Live objects are unmarked, non-live regions have been added to + // their space's free list. Active eden semispace is compacted by + // evacuation. // - // The Page::mc_page_index field contains the zero-based index of the - // page in its space. This word is only used for map space pages, in - // order to encode the map addresses in 21 bits to free 11 - // bits per map word for the forwarding address. - // - // The Page::mc_first_forwarded field contains the (nonencoded) - // forwarding address of the first live object in the page. - // - // In both the new space and the paged spaces, a linked list - // of live regions is constructructed (linked through - // pointers in the non-live region immediately following each - // live region) to speed further passes of the collector. - - // Encodes forwarding addresses of objects in compactable parts of the - // heap. - void EncodeForwardingAddresses(); - - // Encodes the forwarding addresses of objects in new space. - void EncodeForwardingAddressesInNewSpace(); - - // Function template to encode the forwarding addresses of objects in - // paged spaces, parameterized by allocation and non-live processing - // functions. - template<AllocationFunction Alloc, ProcessNonLiveFunction ProcessNonLive> - void EncodeForwardingAddressesInPagedSpace(PagedSpace* space); - - // Iterates live objects in a space, passes live objects - // to a callback function which returns the heap size of the object. - // Returns the number of live objects iterated. - int IterateLiveObjects(NewSpace* space, LiveObjectCallback size_f); - int IterateLiveObjects(PagedSpace* space, LiveObjectCallback size_f); - - // Iterates the live objects between a range of addresses, returning the - // number of live objects. - int IterateLiveObjectsInRange(Address start, Address end, - LiveObjectCallback size_func); // If we are not compacting the heap, we simply sweep the spaces except // for the large object space, clearing mark bits and adding unmarked // regions to each space's free list. void SweepSpaces(); - // ----------------------------------------------------------------------- - // Phase 3: Updating pointers in live objects. - // - // Before: Same as after phase 2 (compacting collection). - // - // After: All pointers in live objects, including encoded map - // pointers, are updated to point to their target's new - // location. - - friend class UpdatingVisitor; // helper for updating visited objects + void EvacuateNewSpace(); - // Updates pointers in all spaces. - void UpdatePointers(); + void EvacuateLiveObjectsFromPage(Page* p); - // Updates pointers in an object in new space. - // Returns the heap size of the object. - int UpdatePointersInNewObject(HeapObject* obj); + void EvacuatePages(); - // Updates pointers in an object in old spaces. - // Returns the heap size of the object. - int UpdatePointersInOldObject(HeapObject* obj); + void EvacuateNewSpaceAndCandidates(); - // Calculates the forwarding address of an object in an old space. - static Address GetForwardingAddressInOldSpace(HeapObject* obj); - - // ----------------------------------------------------------------------- - // Phase 4: Relocating objects. - // - // Before: Pointers to live objects are updated to point to their - // target's new location. - // - // After: Objects have been moved to their new addresses. - - // Relocates objects in all spaces. - void RelocateObjects(); - - // Converts a code object's inline target to addresses, convention from - // address to target happens in the marking phase. - int ConvertCodeICTargetToAddress(HeapObject* obj); - - // Relocate a map object. - int RelocateMapObject(HeapObject* obj); - - // Relocates an old object. - int RelocateOldPointerObject(HeapObject* obj); - int RelocateOldDataObject(HeapObject* obj); - - // Relocate a property cell object. - int RelocateCellObject(HeapObject* obj); - - // Helper function. - inline int RelocateOldNonCodeObject(HeapObject* obj, - PagedSpace* space); - - // Relocates an object in the code space. - int RelocateCodeObject(HeapObject* obj); - - // Copy a new object. - int RelocateNewObject(HeapObject* obj); + void SweepSpace(PagedSpace* space, SweeperType sweeper); #ifdef DEBUG - // ----------------------------------------------------------------------- - // Debugging variables, functions and classes - // Counters used for debugging the marking phase of mark-compact or - // mark-sweep collection. - - // Size of live objects in Heap::to_space_. - int live_young_objects_size_; - - // Size of live objects in Heap::old_pointer_space_. - int live_old_pointer_objects_size_; - - // Size of live objects in Heap::old_data_space_. - int live_old_data_objects_size_; - - // Size of live objects in Heap::code_space_. - int live_code_objects_size_; - - // Size of live objects in Heap::map_space_. - int live_map_objects_size_; - - // Size of live objects in Heap::cell_space_. - int live_cell_objects_size_; - - // Size of live objects in Heap::lo_space_. - int live_lo_objects_size_; - - // Number of live bytes in this collection. - int live_bytes_; - friend class MarkObjectVisitor; static void VisitObject(HeapObject* obj); @@ -524,15 +757,19 @@ class MarkCompactCollector { #endif Heap* heap_; - MarkingStack marking_stack_; + MarkingDeque marking_deque_; CodeFlusher* code_flusher_; Object* encountered_weak_maps_; + List<Page*> evacuation_candidates_; + List<Code*> invalidated_code_; + friend class Heap; - friend class OverflowedObjectsScanner; }; +const char* AllocationSpaceName(AllocationSpace space); + } } // namespace v8::internal #endif // V8_MARK_COMPACT_H_ diff --git a/deps/v8/src/math.js b/deps/v8/src/math.js index b5a6d18117..f4426f4a00 100644 --- a/deps/v8/src/math.js +++ b/deps/v8/src/math.js @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -119,6 +119,19 @@ function MathLog(x) { // ECMA 262 - 15.8.2.11 function MathMax(arg1, arg2) { // length == 2 var length = %_ArgumentsLength(); + if (length == 2) { + if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1); + if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2); + if (arg2 > arg1) return arg2; + if (arg1 > arg2) return arg1; + if (arg1 == arg2) { + // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be + // a Smi or a heap number. + return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg2 : arg1; + } + // All comparisons failed, one of the arguments must be NaN. + return 0/0; // Compiler constant-folds this to NaN. + } if (length == 0) { return -1/0; // Compiler constant-folds this to -Infinity. } @@ -131,7 +144,7 @@ function MathMax(arg1, arg2) { // length == 2 if (NUMBER_IS_NAN(n)) return n; // Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be // a Smi or heap number. - if (n > r || (r === 0 && n === 0 && !%_IsSmi(r) && 1 / r < 0)) r = n; + if (n > r || (r == 0 && n == 0 && !%_IsSmi(r) && 1 / r < 0)) r = n; } return r; } @@ -139,6 +152,19 @@ function MathMax(arg1, arg2) { // length == 2 // ECMA 262 - 15.8.2.12 function MathMin(arg1, arg2) { // length == 2 var length = %_ArgumentsLength(); + if (length == 2) { + if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1); + if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2); + if (arg2 > arg1) return arg1; + if (arg1 > arg2) return arg2; + if (arg1 == arg2) { + // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be + // a Smi or a heap number. + return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg1 : arg2; + } + // All comparisons failed, one of the arguments must be NaN. + return 0/0; // Compiler constant-folds this to NaN. + } if (length == 0) { return 1/0; // Compiler constant-folds this to Infinity. } @@ -149,9 +175,9 @@ function MathMin(arg1, arg2) { // length == 2 var n = %_Arguments(i); if (!IS_NUMBER(n)) n = NonNumberToNumber(n); if (NUMBER_IS_NAN(n)) return n; - // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can b a + // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be a // Smi or a heap number. - if (n < r || (r === 0 && n === 0 && !%_IsSmi(n) && 1 / n < 0)) r = n; + if (n < r || (r == 0 && n == 0 && !%_IsSmi(n) && 1 / n < 0)) r = n; } return r; } @@ -189,7 +215,7 @@ function MathSqrt(x) { // ECMA 262 - 15.8.2.18 function MathTan(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); - return %Math_tan(x); + return %_MathTan(x); } @@ -239,7 +265,7 @@ function SetUpMath() { // Set up non-enumerable functions of the Math object and // set their names. - InstallFunctionsOnHiddenPrototype($Math, DONT_ENUM, $Array( + InstallFunctions($Math, DONT_ENUM, $Array( "random", MathRandom, "abs", MathAbs, "acos", MathAcos, diff --git a/deps/v8/src/messages.cc b/deps/v8/src/messages.cc index b6ad5ac352..a0793c2dfd 100644 --- a/deps/v8/src/messages.cc +++ b/deps/v8/src/messages.cc @@ -1,5 +1,4 @@ - -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -81,11 +80,11 @@ Handle<JSMessageObject> MessageHandler::MakeMessageObject( } Handle<Object> stack_trace_handle = stack_trace.is_null() - ? FACTORY->undefined_value() + ? Handle<Object>::cast(FACTORY->undefined_value()) : Handle<Object>::cast(stack_trace); Handle<Object> stack_frames_handle = stack_frames.is_null() - ? FACTORY->undefined_value() + ? Handle<Object>::cast(FACTORY->undefined_value()) : Handle<Object>::cast(stack_frames); Handle<JSMessageObject> message = @@ -127,7 +126,7 @@ void MessageHandler::ReportMessage(Isolate* isolate, v8::NeanderObject listener(JSObject::cast(global_listeners.get(i))); Handle<Foreign> callback_obj(Foreign::cast(listener.get(0))); v8::MessageCallback callback = - FUNCTION_CAST<v8::MessageCallback>(callback_obj->address()); + FUNCTION_CAST<v8::MessageCallback>(callback_obj->foreign_address()); Handle<Object> callback_data(listener.get(1)); { // Do not allow exceptions to propagate. @@ -149,12 +148,15 @@ Handle<String> MessageHandler::GetMessage(Handle<Object> data) { JSFunction::cast( Isolate::Current()->js_builtins_object()-> GetPropertyNoExceptionThrown(*fmt_str))); - Object** argv[1] = { data.location() }; + Handle<Object> argv[] = { data }; bool caught_exception; Handle<Object> result = Execution::TryCall(fun, - Isolate::Current()->js_builtins_object(), 1, argv, &caught_exception); + Isolate::Current()->js_builtins_object(), + ARRAY_SIZE(argv), + argv, + &caught_exception); if (caught_exception || !result->IsString()) { return FACTORY->LookupAsciiSymbol("<error>"); diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index a9993af229..cd4add4bcb 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -83,7 +83,7 @@ function IsNativeErrorObject(obj) { // objects between script tags in a browser setting. function ToStringCheckErrorObject(obj) { if (IsNativeErrorObject(obj)) { - return %_CallFunction(obj, errorToString); + return %_CallFunction(obj, ErrorToString); } else { return ToString(obj); } @@ -185,18 +185,20 @@ function FormatMessage(message) { "define_disallowed", ["Cannot define property:", "%0", ", object is not extensible."], "non_extensible_proto", ["%0", " is not extensible"], "handler_non_object", ["Proxy.", "%0", " called with non-object as handler"], - "trap_function_expected", ["Proxy.", "%0", " called with non-function for ", "%1", " trap"], + "proto_non_object", ["Proxy.", "%0", " called with non-object as prototype"], + "trap_function_expected", ["Proxy.", "%0", " called with non-function for '", "%1", "' trap"], "handler_trap_missing", ["Proxy handler ", "%0", " has no '", "%1", "' trap"], "handler_trap_must_be_callable", ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"], - "handler_returned_false", ["Proxy handler ", "%0", " returned false for '", "%1", "' trap"], - "handler_returned_undefined", ["Proxy handler ", "%0", " returned undefined for '", "%1", "' trap"], - "proxy_prop_not_configurable", ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"], - "proxy_non_object_prop_names", ["Trap ", "%1", " returned non-object ", "%0"], - "proxy_repeated_prop_name", ["Trap ", "%1", " returned repeated property name ", "%2"], + "handler_returned_false", ["Proxy handler ", "%0", " returned false from '", "%1", "' trap"], + "handler_returned_undefined", ["Proxy handler ", "%0", " returned undefined from '", "%1", "' trap"], + "proxy_prop_not_configurable", ["Proxy handler ", "%0", " returned non-configurable descriptor for property '", "%2", "' from '", "%1", "' trap"], + "proxy_non_object_prop_names", ["Trap '", "%1", "' returned non-object ", "%0"], + "proxy_repeated_prop_name", ["Trap '", "%1", "' returned repeated property name '", "%2", "'"], "invalid_weakmap_key", ["Invalid value used as weak map key"], // RangeError "invalid_array_length", ["Invalid array length"], "stack_overflow", ["Maximum call stack size exceeded"], + "invalid_time_value", ["Invalid time value"], // SyntaxError "unable_to_parse", ["Parse error"], "invalid_regexp_flags", ["Invalid flags supplied to RegExp constructor '", "%0", "'"], @@ -204,6 +206,7 @@ function FormatMessage(message) { "illegal_break", ["Illegal break statement"], "illegal_continue", ["Illegal continue statement"], "illegal_return", ["Illegal return statement"], + "illegal_let", ["Illegal let declaration outside extended mode"], "error_loading_debugger", ["Error loading debugger"], "no_input_to_regexp", ["No input to ", "%0"], "invalid_json", ["String '", "%0", "' is not valid JSON"], @@ -240,20 +243,27 @@ function FormatMessage(message) { "strict_poison_pill", ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"], "strict_caller", ["Illegal access to a strict mode caller function."], "unprotected_let", ["Illegal let declaration in unprotected statement context."], + "unprotected_const", ["Illegal const declaration in unprotected statement context."], "cant_prevent_ext_external_array_elements", ["Cannot prevent extension of an object with external array elements"], "redef_external_array_element", ["Cannot redefine a property of an object with external array elements"], + "harmony_const_assign", ["Assignment to constant variable."], ]; var messages = { __proto__ : null }; - var desc = new PropertyDescriptor(); - desc.setConfigurable(false); - desc.setEnumerable(false); - desc.setWritable(false); for (var i = 0; i < messagesDictionary.length; i += 2) { var key = messagesDictionary[i]; var format = messagesDictionary[i + 1]; - ObjectFreeze(format); - desc.setValue(format); - DefineOwnProperty(messages, key, desc); + + for (var j = 0; j < format.length; j++) { + %IgnoreAttributesAndSetProperty(format, %_NumberToString(j), format[j], + DONT_DELETE | READ_ONLY | DONT_ENUM); + } + %IgnoreAttributesAndSetProperty(format, 'length', format.length, + DONT_DELETE | READ_ONLY | DONT_ENUM); + %PreventExtensions(format); + %IgnoreAttributesAndSetProperty(messages, + key, + format, + DONT_DELETE | DONT_ENUM | READ_ONLY); } %PreventExtensions(messages); %IgnoreAttributesAndSetProperty(builtins, "kMessages", @@ -386,7 +396,7 @@ function ScriptLocationFromPosition(position, } return new SourceLocation(this, position, line, column, start, end); -}; +} /** @@ -416,7 +426,7 @@ function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { // resource. var column = opt_column || 0; if (line == 0) { - column -= this.column_offset + column -= this.column_offset; } var offset_position = opt_offset_position || 0; @@ -431,7 +441,8 @@ function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { return null; } - return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. + return this.locationFromPosition( + this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. } } @@ -447,8 +458,10 @@ function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { * invalid */ function ScriptSourceSlice(opt_from_line, opt_to_line) { - var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line; - var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line + var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset + : opt_from_line; + var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() + : opt_to_line; // Adjust according to the offset within the resource. from_line -= this.line_offset; @@ -468,8 +481,10 @@ function ScriptSourceSlice(opt_from_line, opt_to_line) { var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1; // Return a source slice with line numbers re-adjusted to the resource. - return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset, - from_position, to_position); + return new SourceSlice(this, + from_line + this.line_offset, + to_line + this.line_offset, + from_position, to_position); } @@ -502,7 +517,7 @@ function ScriptSourceLine(opt_line) { function ScriptLineCount() { // Return number of source lines. return this.line_ends.length; -}; +} /** @@ -567,10 +582,10 @@ SetUpLockedPrototype(Script, * position : position within the source * start : position of start of source context (inclusive) * end : position of end of source context (not inclusive) - * Source text for the source context is the character interval [start, end[. In - * most cases end will point to a newline character. It might point just past - * the final position of the source if the last source line does not end with a - * newline character. + * Source text for the source context is the character interval + * [start, end[. In most cases end will point to a newline character. + * It might point just past the final position of the source if the last + * source line does not end with a newline character. * @param {Script} script The Script object for which this is a location * @param {number} position Source position for the location * @param {number} line The line number for the location @@ -637,7 +652,7 @@ function SourceLocationRestrict(opt_limit, opt_before) { this.end = this.start + limit; } } -}; +} /** @@ -646,8 +661,11 @@ function SourceLocationRestrict(opt_limit, opt_before) { * Source text for this location. */ function SourceLocationSourceText() { - return %_CallFunction(this.script.source, this.start, this.end, StringSubstring); -}; + return %_CallFunction(this.script.source, + this.start, + this.end, + StringSubstring); +} SetUpLockedPrototype(SourceLocation, @@ -655,7 +673,7 @@ SetUpLockedPrototype(SourceLocation, $Array( "restrict", SourceLocationRestrict, "sourceText", SourceLocationSourceText - ) + ) ); @@ -695,7 +713,7 @@ function SourceSliceSourceText() { this.from_position, this.to_position, StringSubstring); -}; +} SetUpLockedPrototype(SourceSlice, $Array("script", "from_line", "to_line", "from_position", "to_position"), @@ -742,12 +760,8 @@ function DefineOneShotAccessor(obj, name, fun) { hasBeenSet = true; value = v; } - var desc = { get: getter, - set: setter, - enumerable: false, - configurable: true }; - desc = ToPropertyDescriptor(desc); - DefineOwnProperty(obj, name, desc, true); + %DefineOrRedefineAccessorProperty(obj, name, GETTER, getter, DONT_ENUM); + %DefineOrRedefineAccessorProperty(obj, name, SETTER, setter, DONT_ENUM); } function CallSite(receiver, fun, pos) { @@ -758,7 +772,7 @@ function CallSite(receiver, fun, pos) { function CallSiteGetThis() { return this.receiver; -}; +} function CallSiteGetTypeName() { var constructor = this.receiver.constructor; @@ -770,33 +784,33 @@ function CallSiteGetTypeName() { return %_CallFunction(this.receiver, ObjectToString); } return constructorName; -}; +} function CallSiteIsToplevel() { if (this.receiver == null) { return true; } return IS_GLOBAL(this.receiver); -}; +} function CallSiteIsEval() { var script = %FunctionGetScript(this.fun); return script && script.compilation_type == COMPILATION_TYPE_EVAL; -}; +} function CallSiteGetEvalOrigin() { var script = %FunctionGetScript(this.fun); return FormatEvalOrigin(script); -}; +} function CallSiteGetScriptNameOrSourceURL() { var script = %FunctionGetScript(this.fun); return script ? script.nameOrSourceURL() : null; -}; +} function CallSiteGetFunction() { return this.fun; -}; +} function CallSiteGetFunctionName() { // See if the function knows its own name @@ -812,15 +826,19 @@ function CallSiteGetFunctionName() { return "eval"; } return null; -}; +} function CallSiteGetMethodName() { // See if we can find a unique property on the receiver that holds // this function. var ownName = this.fun.name; if (ownName && this.receiver && - (%_CallFunction(this.receiver, ownName, ObjectLookupGetter) === this.fun || - %_CallFunction(this.receiver, ownName, ObjectLookupSetter) === this.fun || + (%_CallFunction(this.receiver, + ownName, + ObjectLookupGetter) === this.fun || + %_CallFunction(this.receiver, + ownName, + ObjectLookupSetter) === this.fun || this.receiver[ownName] === this.fun)) { // To handle DontEnum properties we guess that the method has // the same name as the function. @@ -830,7 +848,8 @@ function CallSiteGetMethodName() { for (var prop in this.receiver) { if (this.receiver.__lookupGetter__(prop) === this.fun || this.receiver.__lookupSetter__(prop) === this.fun || - (!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) { + (!this.receiver.__lookupGetter__(prop) && + this.receiver[prop] === this.fun)) { // If we find more than one match bail out to avoid confusion. if (name) { return null; @@ -842,12 +861,12 @@ function CallSiteGetMethodName() { return name; } return null; -}; +} function CallSiteGetFileName() { var script = %FunctionGetScript(this.fun); return script ? script.name : null; -}; +} function CallSiteGetLineNumber() { if (this.pos == -1) { @@ -859,7 +878,7 @@ function CallSiteGetLineNumber() { location = script.locationFromPosition(this.pos, true); } return location ? location.line + 1 : null; -}; +} function CallSiteGetColumnNumber() { if (this.pos == -1) { @@ -871,16 +890,16 @@ function CallSiteGetColumnNumber() { location = script.locationFromPosition(this.pos, true); } return location ? location.column + 1: null; -}; +} function CallSiteIsNative() { var script = %FunctionGetScript(this.fun); return script ? (script.type == TYPE_NATIVE) : false; -}; +} function CallSiteGetPosition() { return this.pos; -}; +} function CallSiteIsConstructor() { var constructor = this.receiver ? this.receiver.constructor : null; @@ -888,7 +907,7 @@ function CallSiteIsConstructor() { return false; } return this.fun === constructor; -}; +} SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array( "getThis", CallSiteGetThis, @@ -931,12 +950,13 @@ function FormatEvalOrigin(script) { // eval script originated from "real" source. if (eval_from_script.name) { eval_origin += " (" + eval_from_script.name; - var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true); + var location = eval_from_script.locationFromPosition( + script.eval_from_script_position, true); if (location) { eval_origin += ":" + (location.line + 1); eval_origin += ":" + (location.column + 1); } - eval_origin += ")" + eval_origin += ")"; } else { eval_origin += " (unknown source)"; } @@ -944,7 +964,7 @@ function FormatEvalOrigin(script) { } return eval_origin; -}; +} function FormatSourcePosition(frame) { var fileName; @@ -953,8 +973,9 @@ function FormatSourcePosition(frame) { fileLocation = "native"; } else if (frame.isEval()) { fileName = frame.getScriptNameOrSourceURL(); - if (!fileName) + if (!fileName) { fileLocation = frame.getEvalOrigin(); + } } else { fileName = frame.getFileName(); } @@ -1057,13 +1078,13 @@ function captureStackTrace(obj, cons_opt) { if (stackTraceLimit < 0 || stackTraceLimit > 10000) { stackTraceLimit = 10000; } - var raw_stack = %CollectStackTrace(cons_opt - ? cons_opt - : captureStackTrace, stackTraceLimit); + var raw_stack = %CollectStackTrace(obj, + cons_opt ? cons_opt : captureStackTrace, + stackTraceLimit); DefineOneShotAccessor(obj, 'stack', function (obj) { return FormatRawStackTrace(obj, raw_stack); }); -}; +} function SetUpError() { @@ -1126,6 +1147,7 @@ function SetUpError() { return new f(m); } }); + %SetNativeFlag(f); } DefineError(function Error() { }); @@ -1143,42 +1165,43 @@ $Error.captureStackTrace = captureStackTrace; %SetProperty($Error.prototype, 'message', '', DONT_ENUM); -// Global list of error objects visited during errorToString. This is +// Global list of error objects visited during ErrorToString. This is // used to detect cycles in error toString formatting. const visited_errors = new InternalArray(); const cyclic_error_marker = new $Object(); -function errorToStringDetectCycle(error) { +function ErrorToStringDetectCycle(error) { if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker; try { var type = error.type; + var name = error.name; + name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name); + var message = error.message; var hasMessage = %_CallFunction(error, "message", ObjectHasOwnProperty); if (type && !hasMessage) { - var formatted = FormatMessage(%NewMessageObject(type, error.arguments)); - return error.name + ": " + formatted; + message = FormatMessage(%NewMessageObject(type, error.arguments)); } - var message = hasMessage ? (": " + error.message) : ""; - return error.name + message; + message = IS_UNDEFINED(message) ? "" : TO_STRING_INLINE(message); + if (name === "") return message; + if (message === "") return name; + return name + ": " + message; } finally { visited_errors.length = visited_errors.length - 1; } } -function errorToString() { +function ErrorToString() { if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { throw MakeTypeError("called_on_null_or_undefined", ["Error.prototype.toString"]); } - // This helper function is needed because access to properties on - // the builtins object do not work inside of a catch clause. - function isCyclicErrorMarker(o) { return o === cyclic_error_marker; } try { - return errorToStringDetectCycle(this); + return ErrorToStringDetectCycle(this); } catch(e) { // If this error message was encountered already return the empty // string for it instead of recursively formatting it. - if (isCyclicErrorMarker(e)) { + if (e === cyclic_error_marker) { return ''; } throw e; @@ -1186,7 +1209,7 @@ function errorToString() { } -InstallFunctions($Error.prototype, DONT_ENUM, ['toString', errorToString]); +InstallFunctions($Error.prototype, DONT_ENUM, ['toString', ErrorToString]); // Boilerplate for exceptions for stack overflows. Used from // Isolate::StackOverflow(). diff --git a/deps/v8/src/mips/assembler-mips-inl.h b/deps/v8/src/mips/assembler-mips-inl.h index c4c4fd2590..f9e75face8 100644 --- a/deps/v8/src/mips/assembler-mips-inl.h +++ b/deps/v8/src/mips/assembler-mips-inl.h @@ -30,13 +30,14 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. #ifndef V8_MIPS_ASSEMBLER_MIPS_INL_H_ #define V8_MIPS_ASSEMBLER_MIPS_INL_H_ #include "mips/assembler-mips.h" + #include "cpu.h" #include "debug.h" @@ -78,6 +79,15 @@ bool Operand::is_reg() const { } +int FPURegister::ToAllocationIndex(FPURegister reg) { + ASSERT(reg.code() % 2 == 0); + ASSERT(reg.code() / 2 < kNumAllocatableRegisters); + ASSERT(reg.is_valid()); + ASSERT(!reg.is(kDoubleRegZero)); + ASSERT(!reg.is(kLithiumScratchDouble)); + return (reg.code() / 2); +} + // ----------------------------------------------------------------------------- // RelocInfo. @@ -117,9 +127,14 @@ int RelocInfo::target_address_size() { } -void RelocInfo::set_target_address(Address target) { +void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); Assembler::set_target_address_at(pc_, target); + if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -129,7 +144,7 @@ Object* RelocInfo::target_object() { } -Handle<Object> RelocInfo::target_object_handle(Assembler *origin) { +Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); return Handle<Object>(reinterpret_cast<Object**>( Assembler::target_address_at(pc_))); @@ -146,9 +161,15 @@ Object** RelocInfo::target_object_address() { } -void RelocInfo::set_target_object(Object* target) { +void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target)); + if (mode == UPDATE_WRITE_BARRIER && + host() != NULL && + target->IsHeapObject()) { + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), &Memory::Object_at(pc_), HeapObject::cast(target)); + } } @@ -176,10 +197,17 @@ JSGlobalPropertyCell* RelocInfo::target_cell() { } -void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell) { +void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, + WriteBarrierMode mode) { ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; Memory::Address_at(pc_) = address; + if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { + // TODO(1550) We are passing NULL as a slot because cell can never be on + // evacuation candidate. + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), NULL, cell); + } } @@ -200,6 +228,11 @@ void RelocInfo::set_call_address(Address target) { // debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or // debug break slot per BreakLocationIterator::SetDebugBreakAtSlot(). Assembler::set_target_address_at(pc_, target); + if (host() != NULL) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -242,12 +275,7 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() { void RelocInfo::Visit(ObjectVisitor* visitor) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - Object** p = target_object_address(); - Object* orig = *p; - visitor->VisitPointer(p); - if (*p != orig) { - set_target_object(*p); - } + visitor->VisitEmbeddedPointer(this); } else if (RelocInfo::IsCodeTarget(mode)) { visitor->VisitCodeTarget(this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { @@ -257,9 +285,9 @@ void RelocInfo::Visit(ObjectVisitor* visitor) { #ifdef ENABLE_DEBUGGER_SUPPORT // TODO(isolates): Get a cached isolate below. } else if (((RelocInfo::IsJSReturn(mode) && - IsPatchedReturnSequence()) || - (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence())) && + IsPatchedReturnSequence()) || + (RelocInfo::IsDebugBreakSlot(mode) && + IsPatchedDebugBreakSlotSequence())) && Isolate::Current()->debug()->has_break_points()) { visitor->VisitDebugTarget(this); #endif @@ -273,7 +301,7 @@ template<typename StaticVisitor> void RelocInfo::Visit(Heap* heap) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitPointer(heap, target_object_address()); + StaticVisitor::VisitEmbeddedPointer(heap, this); } else if (RelocInfo::IsCodeTarget(mode)) { StaticVisitor::VisitCodeTarget(heap, this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { diff --git a/deps/v8/src/mips/assembler-mips.cc b/deps/v8/src/mips/assembler-mips.cc index e01a0ca70b..9f803d9c1f 100644 --- a/deps/v8/src/mips/assembler-mips.cc +++ b/deps/v8/src/mips/assembler-mips.cc @@ -74,7 +74,9 @@ static uint64_t CpuFeaturesImpliedByCompiler() { void CpuFeatures::Probe() { - ASSERT(!initialized_); + unsigned standard_features = (OS::CpuFeaturesImpliedByPlatform() | + CpuFeaturesImpliedByCompiler()); + ASSERT(supported_ == 0 || supported_ == standard_features); #ifdef DEBUG initialized_ = true; #endif @@ -82,8 +84,7 @@ void CpuFeatures::Probe() { // Get the features implied by the OS and the compiler settings. This is the // minimal set of features which is also allowed for generated code in the // snapshot. - supported_ |= OS::CpuFeaturesImpliedByPlatform(); - supported_ |= CpuFeaturesImpliedByCompiler(); + supported_ |= standard_features; if (Serializer::enabled()) { // No probing for features if we might serialize (generate snapshot). @@ -300,7 +301,7 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) own_buffer_ = false; } - // Setup buffer pointers. + // Set up buffer pointers. ASSERT(buffer_ != NULL); pc_ = buffer_; reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); @@ -336,7 +337,7 @@ Assembler::~Assembler() { void Assembler::GetCode(CodeDesc* desc) { ASSERT(pc_ <= reloc_info_writer.pos()); // No overlap. - // Setup code descriptor. + // Set up code descriptor. desc->buffer = buffer_; desc->buffer_size = buffer_size_; desc->instr_size = pc_offset(); @@ -1244,6 +1245,7 @@ void Assembler::and_(Register rd, Register rs, Register rt) { void Assembler::andi(Register rt, Register rs, int32_t j) { + ASSERT(is_uint16(j)); GenInstrImmediate(ANDI, rs, rt, j); } @@ -1254,6 +1256,7 @@ void Assembler::or_(Register rd, Register rs, Register rt) { void Assembler::ori(Register rt, Register rs, int32_t j) { + ASSERT(is_uint16(j)); GenInstrImmediate(ORI, rs, rt, j); } @@ -1264,6 +1267,7 @@ void Assembler::xor_(Register rd, Register rs, Register rt) { void Assembler::xori(Register rt, Register rs, int32_t j) { + ASSERT(is_uint16(j)); GenInstrImmediate(XORI, rs, rt, j); } @@ -1444,6 +1448,7 @@ void Assembler::swr(Register rd, const MemOperand& rs) { void Assembler::lui(Register rd, int32_t j) { + ASSERT(is_uint16(j)); GenInstrImmediate(LUI, zero_reg, rd, j); } @@ -1969,7 +1974,7 @@ void Assembler::GrowBuffer() { } CHECK_GT(desc.buffer_size, 0); // No overflow. - // Setup new buffer. + // Set up new buffer. desc.buffer = NewArray<byte>(desc.buffer_size); desc.instr_size = pc_offset(); @@ -2018,7 +2023,8 @@ void Assembler::dd(uint32_t data) { void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { - RelocInfo rinfo(pc_, rmode, data); // We do not try to reuse pool constants. + // We do not try to reuse pool constants. + RelocInfo rinfo(pc_, rmode, data, NULL); if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::DEBUG_BREAK_SLOT) { // Adjust code for new modes. ASSERT(RelocInfo::IsDebugBreakSlot(rmode) @@ -2041,7 +2047,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { } ASSERT(buffer_space() >= kMaxRelocSize); // Too late to grow buffer here. if (rmode == RelocInfo::CODE_TARGET_WITH_ID) { - RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId()); + RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId(), NULL); ClearRecordedAstId(); reloc_info_writer.Write(&reloc_info_with_ast_id); } else { diff --git a/deps/v8/src/mips/assembler-mips.h b/deps/v8/src/mips/assembler-mips.h index 38e9537afb..b1ffc45c0a 100644 --- a/deps/v8/src/mips/assembler-mips.h +++ b/deps/v8/src/mips/assembler-mips.h @@ -30,7 +30,7 @@ // The original source code covered by the above license above has been // modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. #ifndef V8_MIPS_ASSEMBLER_MIPS_H_ @@ -182,12 +182,7 @@ struct FPURegister { kNumReservedRegisters; - static int ToAllocationIndex(FPURegister reg) { - ASSERT(reg.code() % 2 == 0); - ASSERT(reg.code() / 2 < kNumAllocatableRegisters); - ASSERT(reg.is_valid()); - return (reg.code() / 2); - } + inline static int ToAllocationIndex(FPURegister reg); static FPURegister FromAllocationIndex(int index) { ASSERT(index >= 0 && index < kNumAllocatableRegisters); @@ -302,7 +297,15 @@ const FPURegister f29 = { 29 }; const FPURegister f30 = { 30 }; const FPURegister f31 = { 31 }; -const FPURegister kDoubleRegZero = f28; +// Register aliases. +// cp is assumed to be a callee saved register. +static const Register& kLithiumScratchReg = s3; // Scratch register. +static const Register& kLithiumScratchReg2 = s4; // Scratch register. +static const Register& kRootRegister = s6; // Roots array pointer. +static const Register& cp = s7; // JavaScript context pointer. +static const Register& fp = s8_fp; // Alias for fp. +static const DoubleRegister& kLithiumScratchDouble = f30; +static const FPURegister& kDoubleRegZero = f28; // FPU (coprocessor 1) control registers. // Currently only FCSR (#31) is implemented. @@ -667,7 +670,7 @@ class Assembler : public AssemblerBase { // Never use the int16_t b(l)cond version with a branch offset // instead of using the Label* version. - // Jump targets must be in the current 256 MB-aligned region. ie 28 bits. + // Jump targets must be in the current 256 MB-aligned region. i.e. 28 bits. void j(int32_t target); void jal(int32_t target); void jalr(Register rs, Register rd = ra); diff --git a/deps/v8/src/mips/builtins-mips.cc b/deps/v8/src/mips/builtins-mips.cc index d77230448f..259df211bd 100644 --- a/deps/v8/src/mips/builtins-mips.cc +++ b/deps/v8/src/mips/builtins-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -74,24 +74,34 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, } -// Load the built-in Array function from the current context. -static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { +// Load the built-in InternalArray function from the current context. +static void GenerateLoadInternalArrayFunction(MacroAssembler* masm, + Register result) { // Load the global context. __ lw(result, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); __ lw(result, - FieldMemOperand(result, GlobalObject::kGlobalContextOffset)); - // Load the Array function from the global context. + FieldMemOperand(result, GlobalObject::kGlobalContextOffset)); + // Load the InternalArray function from the global context. __ lw(result, MemOperand(result, - Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); + Context::SlotOffset( + Context::INTERNAL_ARRAY_FUNCTION_INDEX))); } -// This constant has the same value as JSArray::kPreallocatedArrayElements and -// if JSArray::kPreallocatedArrayElements is changed handling of loop unfolding -// below should be reconsidered. -static const int kLoopUnfoldLimit = 4; +// Load the built-in Array function from the current context. +static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { + // Load the global context. + + __ lw(result, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ lw(result, + FieldMemOperand(result, GlobalObject::kGlobalContextOffset)); + // Load the Array function from the global context. + __ lw(result, + MemOperand(result, + Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); +} // Allocate an empty JSArray. The allocated array is put into the result @@ -103,16 +113,17 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Register scratch1, Register scratch2, Register scratch3, - int initial_capacity, Label* gc_required) { - ASSERT(initial_capacity > 0); - // Load the initial map from the array function. - __ lw(scratch1, FieldMemOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + const int initial_capacity = JSArray::kPreallocatedArrayElements; + STATIC_ASSERT(initial_capacity >= 0); + __ LoadInitialArrayMap(array_function, scratch2, scratch1); // Allocate the JSArray object together with space for a fixed array with the // requested elements. - int size = JSArray::kSize + FixedArray::SizeFor(initial_capacity); + int size = JSArray::kSize; + if (initial_capacity > 0) { + size += FixedArray::SizeFor(initial_capacity); + } __ AllocateInNewSpace(size, result, scratch2, @@ -131,6 +142,11 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, __ mov(scratch3, zero_reg); __ sw(scratch3, FieldMemOperand(result, JSArray::kLengthOffset)); + if (initial_capacity == 0) { + __ sw(scratch1, FieldMemOperand(result, JSArray::kElementsOffset)); + return; + } + // Calculate the location of the elements array and set elements array member // of the JSArray. // result: JSObject @@ -147,21 +163,31 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // scratch1: elements array (untagged) // scratch2: start of next object __ LoadRoot(scratch3, Heap::kFixedArrayMapRootIndex); - ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset); + STATIC_ASSERT(0 * kPointerSize == FixedArray::kMapOffset); __ sw(scratch3, MemOperand(scratch1)); __ Addu(scratch1, scratch1, kPointerSize); __ li(scratch3, Operand(Smi::FromInt(initial_capacity))); - ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + STATIC_ASSERT(1 * kPointerSize == FixedArray::kLengthOffset); __ sw(scratch3, MemOperand(scratch1)); __ Addu(scratch1, scratch1, kPointerSize); - // Fill the FixedArray with the hole value. - ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); - ASSERT(initial_capacity <= kLoopUnfoldLimit); + // Fill the FixedArray with the hole value. Inline the code if short. + STATIC_ASSERT(2 * kPointerSize == FixedArray::kHeaderSize); __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex); - for (int i = 0; i < initial_capacity; i++) { + static const int kLoopUnfoldLimit = 4; + if (initial_capacity <= kLoopUnfoldLimit) { + for (int i = 0; i < initial_capacity; i++) { + __ sw(scratch3, MemOperand(scratch1, i * kPointerSize)); + } + } else { + Label loop, entry; + __ Addu(scratch2, scratch1, Operand(initial_capacity * kPointerSize)); + __ Branch(&entry); + __ bind(&loop); __ sw(scratch3, MemOperand(scratch1)); __ Addu(scratch1, scratch1, kPointerSize); + __ bind(&entry); + __ Branch(&loop, lt, scratch1, Operand(scratch2)); } } @@ -177,7 +203,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // register elements_array_storage is scratched. static void AllocateJSArray(MacroAssembler* masm, Register array_function, // Array function. - Register array_size, // As a smi. + Register array_size, // As a smi, cannot be 0. Register result, Register elements_array_storage, Register elements_array_end, @@ -185,31 +211,16 @@ static void AllocateJSArray(MacroAssembler* masm, Register scratch2, bool fill_with_hole, Label* gc_required) { - Label not_empty, allocated; - // Load the initial map from the array function. - __ lw(elements_array_storage, - FieldMemOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + __ LoadInitialArrayMap(array_function, scratch2, elements_array_storage); - // Check whether an empty sized array is requested. - __ Branch(¬_empty, ne, array_size, Operand(zero_reg)); - - // If an empty array is requested allocate a small elements array anyway. This - // keeps the code below free of special casing for the empty array. - int size = JSArray::kSize + - FixedArray::SizeFor(JSArray::kPreallocatedArrayElements); - __ AllocateInNewSpace(size, - result, - elements_array_end, - scratch1, - gc_required, - TAG_OBJECT); - __ Branch(&allocated); + if (FLAG_debug_code) { // Assert that array size is not zero. + __ Assert( + ne, "array size is unexpectedly 0", array_size, Operand(zero_reg)); + } // Allocate the JSArray object together with space for a FixedArray with the // requested number of elements. - __ bind(¬_empty); STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); __ li(elements_array_end, (JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize); @@ -228,7 +239,6 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array_storage: initial map // array_size: size of array (smi) - __ bind(&allocated); __ sw(elements_array_storage, FieldMemOperand(result, JSObject::kMapOffset)); __ LoadRoot(elements_array_storage, Heap::kEmptyFixedArrayRootIndex); __ sw(elements_array_storage, @@ -262,8 +272,6 @@ static void AllocateJSArray(MacroAssembler* masm, // the actual JSArray has length 0 and the size of the JSArray for non-empty // JSArrays. The length of a FixedArray is stored as a smi. STATIC_ASSERT(kSmiTag == 0); - __ li(at, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements))); - __ movz(array_size, at, array_size); ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); __ sw(array_size, MemOperand(elements_array_storage)); @@ -312,21 +320,22 @@ static void AllocateJSArray(MacroAssembler* masm, static void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code) { Counters* counters = masm->isolate()->counters(); - Label argc_one_or_more, argc_two_or_more; + Label argc_one_or_more, argc_two_or_more, not_empty_array, empty_array, + has_non_smi_element; // Check for array construction with zero arguments or one. __ Branch(&argc_one_or_more, ne, a0, Operand(zero_reg)); // Handle construction of an empty array. + __ bind(&empty_array); AllocateEmptyJSArray(masm, a1, a2, a3, t0, t1, - JSArray::kPreallocatedArrayElements, call_generic_code); __ IncrementCounter(counters->array_function_native(), 1, a3, t0); - // Setup return value, remove receiver from stack and return. + // Set up return value, remove receiver from stack and return. __ mov(v0, a2); __ Addu(sp, sp, Operand(kPointerSize)); __ Ret(); @@ -338,6 +347,12 @@ static void ArrayNativeCode(MacroAssembler* masm, STATIC_ASSERT(kSmiTag == 0); __ lw(a2, MemOperand(sp)); // Get the argument from the stack. + __ Branch(¬_empty_array, ne, a2, Operand(zero_reg)); + __ Drop(1); // Adjust stack. + __ mov(a0, zero_reg); // Treat this as a call with argc of zero. + __ Branch(&empty_array); + + __ bind(¬_empty_array); __ And(a3, a2, Operand(kIntptrSignBit | kSmiTagMask)); __ Branch(call_generic_code, eq, a3, Operand(zero_reg)); @@ -363,7 +378,7 @@ static void ArrayNativeCode(MacroAssembler* masm, call_generic_code); __ IncrementCounter(counters->array_function_native(), 1, a2, t0); - // Setup return value, remove receiver and argument from stack and return. + // Set up return value, remove receiver and argument from stack and return. __ mov(v0, a3); __ Addu(sp, sp, Operand(2 * kPointerSize)); __ Ret(); @@ -398,13 +413,19 @@ static void ArrayNativeCode(MacroAssembler* masm, // sp[0]: last argument Label loop, entry; - __ Branch(&entry); + __ Branch(USE_DELAY_SLOT, &entry); + __ mov(t3, sp); __ bind(&loop); - __ pop(a2); + __ lw(a2, MemOperand(t3)); + __ Addu(t3, t3, kPointerSize); + if (FLAG_smi_only_arrays) { + __ JumpIfNotSmi(a2, &has_non_smi_element); + } __ Addu(t1, t1, -kPointerSize); __ sw(a2, MemOperand(t1)); __ bind(&entry); __ Branch(&loop, lt, t0, Operand(t1)); + __ mov(sp, t3); // Remove caller arguments and receiver from the stack, setup return value and // return. @@ -414,6 +435,46 @@ static void ArrayNativeCode(MacroAssembler* masm, __ Addu(sp, sp, Operand(kPointerSize)); __ mov(v0, a3); __ Ret(); + + __ bind(&has_non_smi_element); + __ UndoAllocationInNewSpace(a3, t0); + __ b(call_generic_code); +} + + +void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : number of arguments + // -- ra : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + Label generic_array_code, one_or_more_arguments, two_or_more_arguments; + + // Get the InternalArray function. + GenerateLoadInternalArrayFunction(masm, a1); + + if (FLAG_debug_code) { + // Initial map for the builtin InternalArray functions should be maps. + __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset)); + __ And(t0, a2, Operand(kSmiTagMask)); + __ Assert(ne, "Unexpected initial map for InternalArray function", + t0, Operand(zero_reg)); + __ GetObjectType(a2, a3, t0); + __ Assert(eq, "Unexpected initial map for InternalArray function", + t0, Operand(MAP_TYPE)); + } + + // Run the native code for the InternalArray function called as a normal + // function. + ArrayNativeCode(masm, &generic_array_code); + + // Jump to the generic array code if the specialized code cannot handle the + // construction. + __ bind(&generic_array_code); + + Handle<Code> array_code = + masm->isolate()->builtins()->InternalArrayCodeGeneric(); + __ Jump(array_code, RelocInfo::CODE_TARGET); } @@ -587,10 +648,11 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ bind(&convert_argument); __ push(function); // Preserve the function. __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0); - __ EnterInternalFrame(); - __ push(v0); - __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(v0); + __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); + } __ pop(function); __ mov(argument, v0); __ Branch(&argument_is_string); @@ -606,15 +668,18 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // create a string wrapper. __ bind(&gc_required); __ IncrementCounter(counters->string_ctor_gc_required(), 1, a3, t0); - __ EnterInternalFrame(); - __ push(argument); - __ CallRuntime(Runtime::kNewStringWrapper, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(argument); + __ CallRuntime(Runtime::kNewStringWrapper, 1); + } __ Ret(); } -void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { +static void Generate_JSConstructStubHelper(MacroAssembler* masm, + bool is_api_function, + bool count_constructions) { // ----------- S t a t e ------------- // -- a0 : number of arguments // -- a1 : constructor function @@ -622,38 +687,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // -- sp[...]: constructor arguments // ----------------------------------- - Label non_function_call; - // Check that the function is not a smi. - __ And(t0, a1, Operand(kSmiTagMask)); - __ Branch(&non_function_call, eq, t0, Operand(zero_reg)); - // Check that the function is a JSFunction. - __ GetObjectType(a1, a2, a2); - __ Branch(&non_function_call, ne, a2, Operand(JS_FUNCTION_TYPE)); - - // Jump to the function-specific construct stub. - __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); - __ lw(a2, FieldMemOperand(a2, SharedFunctionInfo::kConstructStubOffset)); - __ Addu(t9, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); - __ Jump(t9); - - // a0: number of arguments - // a1: called object - __ bind(&non_function_call); - // CALL_NON_FUNCTION expects the non-function constructor as receiver - // (instead of the original receiver from the call site). The receiver is - // stack element argc. - // Set expected number of arguments to zero (not changing a0). - __ mov(a2, zero_reg); - __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); - __ SetCallKind(t1, CALL_AS_METHOD); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); -} - - -static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool count_constructions) { // Should never count constructions for api objects. ASSERT(!is_api_function || !count_constructions); @@ -667,331 +700,318 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // ----------------------------------- // Enter a construct frame. - __ EnterConstructFrame(); + { + FrameScope scope(masm, StackFrame::CONSTRUCT); - // Preserve the two incoming parameters on the stack. - __ sll(a0, a0, kSmiTagSize); // Tag arguments count. - __ MultiPushReversed(a0.bit() | a1.bit()); + // Preserve the two incoming parameters on the stack. + __ sll(a0, a0, kSmiTagSize); // Tag arguments count. + __ MultiPushReversed(a0.bit() | a1.bit()); - // Use t7 to hold undefined, which is used in several places below. - __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); + // Use t7 to hold undefined, which is used in several places below. + __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); - Label rt_call, allocated; - // Try to allocate the object without transitioning into C code. If any of the - // preconditions is not met, the code bails out to the runtime call. - if (FLAG_inline_new) { - Label undo_allocation; + Label rt_call, allocated; + // Try to allocate the object without transitioning into C code. If any of + // the preconditions is not met, the code bails out to the runtime call. + if (FLAG_inline_new) { + Label undo_allocation; #ifdef ENABLE_DEBUGGER_SUPPORT - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(isolate); - __ li(a2, Operand(debug_step_in_fp)); - __ lw(a2, MemOperand(a2)); - __ Branch(&rt_call, ne, a2, Operand(zero_reg)); + ExternalReference debug_step_in_fp = + ExternalReference::debug_step_in_fp_address(isolate); + __ li(a2, Operand(debug_step_in_fp)); + __ lw(a2, MemOperand(a2)); + __ Branch(&rt_call, ne, a2, Operand(zero_reg)); #endif - // Load the initial map and verify that it is in fact a map. - // a1: constructor function - __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset)); - __ And(t0, a2, Operand(kSmiTagMask)); - __ Branch(&rt_call, eq, t0, Operand(zero_reg)); - __ GetObjectType(a2, a3, t4); - __ Branch(&rt_call, ne, t4, Operand(MAP_TYPE)); - - // Check that the constructor is not constructing a JSFunction (see comments - // in Runtime_NewObject in runtime.cc). In which case the initial map's - // instance type would be JS_FUNCTION_TYPE. - // a1: constructor function - // a2: initial map - __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset)); - __ Branch(&rt_call, eq, a3, Operand(JS_FUNCTION_TYPE)); - - if (count_constructions) { - Label allocate; - // Decrease generous allocation count. - __ lw(a3, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); - MemOperand constructor_count = - FieldMemOperand(a3, SharedFunctionInfo::kConstructionCountOffset); - __ lbu(t0, constructor_count); - __ Subu(t0, t0, Operand(1)); - __ sb(t0, constructor_count); - __ Branch(&allocate, ne, t0, Operand(zero_reg)); - - __ Push(a1, a2); - - __ push(a1); // Constructor. - // The call will replace the stub, so the countdown is only done once. - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - - __ pop(a2); - __ pop(a1); - - __ bind(&allocate); - } + // Load the initial map and verify that it is in fact a map. + // a1: constructor function + __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset)); + __ JumpIfSmi(a2, &rt_call); + __ GetObjectType(a2, a3, t4); + __ Branch(&rt_call, ne, t4, Operand(MAP_TYPE)); + + // Check that the constructor is not constructing a JSFunction (see + // comments in Runtime_NewObject in runtime.cc). In which case the + // initial map's instance type would be JS_FUNCTION_TYPE. + // a1: constructor function + // a2: initial map + __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset)); + __ Branch(&rt_call, eq, a3, Operand(JS_FUNCTION_TYPE)); - // Now allocate the JSObject on the heap. - // a1: constructor function - // a2: initial map - __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset)); - __ AllocateInNewSpace(a3, t4, t5, t6, &rt_call, SIZE_IN_WORDS); + if (count_constructions) { + Label allocate; + // Decrease generous allocation count. + __ lw(a3, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + MemOperand constructor_count = + FieldMemOperand(a3, SharedFunctionInfo::kConstructionCountOffset); + __ lbu(t0, constructor_count); + __ Subu(t0, t0, Operand(1)); + __ sb(t0, constructor_count); + __ Branch(&allocate, ne, t0, Operand(zero_reg)); + + __ Push(a1, a2); + + __ push(a1); // Constructor. + // The call will replace the stub, so the countdown is only done once. + __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); + + __ pop(a2); + __ pop(a1); + + __ bind(&allocate); + } - // Allocated the JSObject, now initialize the fields. Map is set to initial - // map and properties and elements are set to empty fixed array. - // a1: constructor function - // a2: initial map - // a3: object size - // t4: JSObject (not tagged) - __ LoadRoot(t6, Heap::kEmptyFixedArrayRootIndex); - __ mov(t5, t4); - __ sw(a2, MemOperand(t5, JSObject::kMapOffset)); - __ sw(t6, MemOperand(t5, JSObject::kPropertiesOffset)); - __ sw(t6, MemOperand(t5, JSObject::kElementsOffset)); - __ Addu(t5, t5, Operand(3*kPointerSize)); - ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); - ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset); - ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset); - - // Fill all the in-object properties with appropriate filler. - // a1: constructor function - // a2: initial map - // a3: object size (in words) - // t4: JSObject (not tagged) - // t5: First in-object property of JSObject (not tagged) - __ sll(t0, a3, kPointerSizeLog2); - __ addu(t6, t4, t0); // End of object. - ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize); - { Label loop, entry; + // Now allocate the JSObject on the heap. + // a1: constructor function + // a2: initial map + __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset)); + __ AllocateInNewSpace(a3, t4, t5, t6, &rt_call, SIZE_IN_WORDS); + + // Allocated the JSObject, now initialize the fields. Map is set to + // initial map and properties and elements are set to empty fixed array. + // a1: constructor function + // a2: initial map + // a3: object size + // t4: JSObject (not tagged) + __ LoadRoot(t6, Heap::kEmptyFixedArrayRootIndex); + __ mov(t5, t4); + __ sw(a2, MemOperand(t5, JSObject::kMapOffset)); + __ sw(t6, MemOperand(t5, JSObject::kPropertiesOffset)); + __ sw(t6, MemOperand(t5, JSObject::kElementsOffset)); + __ Addu(t5, t5, Operand(3*kPointerSize)); + ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); + ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset); + ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset); + + // Fill all the in-object properties with appropriate filler. + // a1: constructor function + // a2: initial map + // a3: object size (in words) + // t4: JSObject (not tagged) + // t5: First in-object property of JSObject (not tagged) + __ sll(t0, a3, kPointerSizeLog2); + __ addu(t6, t4, t0); // End of object. + ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize); + __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); if (count_constructions) { + __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset)); + __ Ext(a0, a0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte, + kBitsPerByte); + __ sll(t0, a0, kPointerSizeLog2); + __ addu(a0, t5, t0); + // a0: offset of first field after pre-allocated fields + if (FLAG_debug_code) { + __ Assert(le, "Unexpected number of pre-allocated property fields.", + a0, Operand(t6)); + } + __ InitializeFieldsWithFiller(t5, a0, t7); // To allow for truncation. __ LoadRoot(t7, Heap::kOnePointerFillerMapRootIndex); - } else { - __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); } - __ jmp(&entry); - __ bind(&loop); - __ sw(t7, MemOperand(t5, 0)); - __ addiu(t5, t5, kPointerSize); - __ bind(&entry); - __ Branch(&loop, Uless, t5, Operand(t6)); - } - - // Add the object tag to make the JSObject real, so that we can continue and - // jump into the continuation code at any time from now on. Any failures - // need to undo the allocation, so that the heap is in a consistent state - // and verifiable. - __ Addu(t4, t4, Operand(kHeapObjectTag)); - - // Check if a non-empty properties array is needed. Continue with allocated - // object if not fall through to runtime call if it is. - // a1: constructor function - // t4: JSObject - // t5: start of next object (not tagged) - __ lbu(a3, FieldMemOperand(a2, Map::kUnusedPropertyFieldsOffset)); - // The field instance sizes contains both pre-allocated property fields and - // in-object properties. - __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset)); - __ And(t6, - a0, - Operand(0x000000FF << Map::kPreAllocatedPropertyFieldsByte * 8)); - __ srl(t0, t6, Map::kPreAllocatedPropertyFieldsByte * 8); - __ Addu(a3, a3, Operand(t0)); - __ And(t6, a0, Operand(0x000000FF << Map::kInObjectPropertiesByte * 8)); - __ srl(t0, t6, Map::kInObjectPropertiesByte * 8); - __ subu(a3, a3, t0); - - // Done if no extra properties are to be allocated. - __ Branch(&allocated, eq, a3, Operand(zero_reg)); - __ Assert(greater_equal, "Property allocation count failed.", - a3, Operand(zero_reg)); - - // Scale the number of elements by pointer size and add the header for - // FixedArrays to the start of the next object calculation from above. - // a1: constructor - // a3: number of elements in properties array - // t4: JSObject - // t5: start of next object - __ Addu(a0, a3, Operand(FixedArray::kHeaderSize / kPointerSize)); - __ AllocateInNewSpace( - a0, - t5, - t6, - a2, - &undo_allocation, - static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS)); - - // Initialize the FixedArray. - // a1: constructor - // a3: number of elements in properties array (un-tagged) - // t4: JSObject - // t5: start of next object - __ LoadRoot(t6, Heap::kFixedArrayMapRootIndex); - __ mov(a2, t5); - __ sw(t6, MemOperand(a2, JSObject::kMapOffset)); - __ sll(a0, a3, kSmiTagSize); - __ sw(a0, MemOperand(a2, FixedArray::kLengthOffset)); - __ Addu(a2, a2, Operand(2 * kPointerSize)); - - ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); - ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); - - // Initialize the fields to undefined. - // a1: constructor - // a2: First element of FixedArray (not tagged) - // a3: number of elements in properties array - // t4: JSObject - // t5: FixedArray (not tagged) - __ sll(t3, a3, kPointerSizeLog2); - __ addu(t6, a2, t3); // End of object. - ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); - { Label loop, entry; - if (count_constructions) { - __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); - } else if (FLAG_debug_code) { - __ LoadRoot(t8, Heap::kUndefinedValueRootIndex); - __ Assert(eq, "Undefined value not loaded.", t7, Operand(t8)); + __ InitializeFieldsWithFiller(t5, t6, t7); + + // Add the object tag to make the JSObject real, so that we can continue + // and jump into the continuation code at any time from now on. Any + // failures need to undo the allocation, so that the heap is in a + // consistent state and verifiable. + __ Addu(t4, t4, Operand(kHeapObjectTag)); + + // Check if a non-empty properties array is needed. Continue with + // allocated object if not fall through to runtime call if it is. + // a1: constructor function + // t4: JSObject + // t5: start of next object (not tagged) + __ lbu(a3, FieldMemOperand(a2, Map::kUnusedPropertyFieldsOffset)); + // The field instance sizes contains both pre-allocated property fields + // and in-object properties. + __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset)); + __ Ext(t6, a0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte, + kBitsPerByte); + __ Addu(a3, a3, Operand(t6)); + __ Ext(t6, a0, Map::kInObjectPropertiesByte * kBitsPerByte, + kBitsPerByte); + __ subu(a3, a3, t6); + + // Done if no extra properties are to be allocated. + __ Branch(&allocated, eq, a3, Operand(zero_reg)); + __ Assert(greater_equal, "Property allocation count failed.", + a3, Operand(zero_reg)); + + // Scale the number of elements by pointer size and add the header for + // FixedArrays to the start of the next object calculation from above. + // a1: constructor + // a3: number of elements in properties array + // t4: JSObject + // t5: start of next object + __ Addu(a0, a3, Operand(FixedArray::kHeaderSize / kPointerSize)); + __ AllocateInNewSpace( + a0, + t5, + t6, + a2, + &undo_allocation, + static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS)); + + // Initialize the FixedArray. + // a1: constructor + // a3: number of elements in properties array (untagged) + // t4: JSObject + // t5: start of next object + __ LoadRoot(t6, Heap::kFixedArrayMapRootIndex); + __ mov(a2, t5); + __ sw(t6, MemOperand(a2, JSObject::kMapOffset)); + __ sll(a0, a3, kSmiTagSize); + __ sw(a0, MemOperand(a2, FixedArray::kLengthOffset)); + __ Addu(a2, a2, Operand(2 * kPointerSize)); + + ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset); + ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + + // Initialize the fields to undefined. + // a1: constructor + // a2: First element of FixedArray (not tagged) + // a3: number of elements in properties array + // t4: JSObject + // t5: FixedArray (not tagged) + __ sll(t3, a3, kPointerSizeLog2); + __ addu(t6, a2, t3); // End of object. + ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); + { Label loop, entry; + if (count_constructions) { + __ LoadRoot(t7, Heap::kUndefinedValueRootIndex); + } else if (FLAG_debug_code) { + __ LoadRoot(t8, Heap::kUndefinedValueRootIndex); + __ Assert(eq, "Undefined value not loaded.", t7, Operand(t8)); + } + __ jmp(&entry); + __ bind(&loop); + __ sw(t7, MemOperand(a2)); + __ addiu(a2, a2, kPointerSize); + __ bind(&entry); + __ Branch(&loop, less, a2, Operand(t6)); } - __ jmp(&entry); - __ bind(&loop); - __ sw(t7, MemOperand(a2)); - __ addiu(a2, a2, kPointerSize); - __ bind(&entry); - __ Branch(&loop, less, a2, Operand(t6)); + + // Store the initialized FixedArray into the properties field of + // the JSObject. + // a1: constructor function + // t4: JSObject + // t5: FixedArray (not tagged) + __ Addu(t5, t5, Operand(kHeapObjectTag)); // Add the heap tag. + __ sw(t5, FieldMemOperand(t4, JSObject::kPropertiesOffset)); + + // Continue with JSObject being successfully allocated. + // a1: constructor function + // a4: JSObject + __ jmp(&allocated); + + // Undo the setting of the new top so that the heap is verifiable. For + // example, the map's unused properties potentially do not match the + // allocated objects unused properties. + // t4: JSObject (previous new top) + __ bind(&undo_allocation); + __ UndoAllocationInNewSpace(t4, t5); } - // Store the initialized FixedArray into the properties field of - // the JSObject. + __ bind(&rt_call); + // Allocate the new receiver object using the runtime call. // a1: constructor function - // t4: JSObject - // t5: FixedArray (not tagged) - __ Addu(t5, t5, Operand(kHeapObjectTag)); // Add the heap tag. - __ sw(t5, FieldMemOperand(t4, JSObject::kPropertiesOffset)); + __ push(a1); // Argument for Runtime_NewObject. + __ CallRuntime(Runtime::kNewObject, 1); + __ mov(t4, v0); - // Continue with JSObject being successfully allocated. + // Receiver for constructor call allocated. + // t4: JSObject + __ bind(&allocated); + __ push(t4); + __ push(t4); + + // Reload the number of arguments from the stack. + // sp[0]: receiver + // sp[1]: receiver + // sp[2]: constructor function + // sp[3]: number of arguments (smi-tagged) + __ lw(a1, MemOperand(sp, 2 * kPointerSize)); + __ lw(a3, MemOperand(sp, 3 * kPointerSize)); + + // Set up pointer to last argument. + __ Addu(a2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); + + // Set up number of arguments for function call below. + __ srl(a0, a3, kSmiTagSize); + + // Copy arguments and receiver to the expression stack. + // a0: number of arguments // a1: constructor function - // a4: JSObject - __ jmp(&allocated); - - // Undo the setting of the new top so that the heap is verifiable. For - // example, the map's unused properties potentially do not match the - // allocated objects unused properties. - // t4: JSObject (previous new top) - __ bind(&undo_allocation); - __ UndoAllocationInNewSpace(t4, t5); - } + // a2: address of last argument (caller sp) + // a3: number of arguments (smi-tagged) + // sp[0]: receiver + // sp[1]: receiver + // sp[2]: constructor function + // sp[3]: number of arguments (smi-tagged) + Label loop, entry; + __ jmp(&entry); + __ bind(&loop); + __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize); + __ Addu(t0, a2, Operand(t0)); + __ lw(t1, MemOperand(t0)); + __ push(t1); + __ bind(&entry); + __ Addu(a3, a3, Operand(-2)); + __ Branch(&loop, greater_equal, a3, Operand(zero_reg)); - __ bind(&rt_call); - // Allocate the new receiver object using the runtime call. - // a1: constructor function - __ push(a1); // Argument for Runtime_NewObject. - __ CallRuntime(Runtime::kNewObject, 1); - __ mov(t4, v0); - - // Receiver for constructor call allocated. - // t4: JSObject - __ bind(&allocated); - __ push(t4); - - // Push the function and the allocated receiver from the stack. - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ lw(a1, MemOperand(sp, kPointerSize)); - __ MultiPushReversed(a1.bit() | t4.bit()); - - // Reload the number of arguments from the stack. - // a1: constructor function - // sp[0]: receiver - // sp[1]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - __ lw(a3, MemOperand(sp, 4 * kPointerSize)); - - // Setup pointer to last argument. - __ Addu(a2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); - - // Setup number of arguments for function call below. - __ srl(a0, a3, kSmiTagSize); - - // Copy arguments and receiver to the expression stack. - // a0: number of arguments - // a1: constructor function - // a2: address of last argument (caller sp) - // a3: number of arguments (smi-tagged) - // sp[0]: receiver - // sp[1]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - Label loop, entry; - __ jmp(&entry); - __ bind(&loop); - __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize); - __ Addu(t0, a2, Operand(t0)); - __ lw(t1, MemOperand(t0)); - __ push(t1); - __ bind(&entry); - __ Addu(a3, a3, Operand(-2)); - __ Branch(&loop, greater_equal, a3, Operand(zero_reg)); + // Call the function. + // a0: number of arguments + // a1: constructor function + if (is_api_function) { + __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + Handle<Code> code = + masm->isolate()->builtins()->HandleApiCallConstruct(); + ParameterCount expected(0); + __ InvokeCode(code, expected, expected, + RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD); + } else { + ParameterCount actual(a0); + __ InvokeFunction(a1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - // Call the function. - // a0: number of arguments - // a1: constructor function - if (is_api_function) { - __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); - Handle<Code> code = - masm->isolate()->builtins()->HandleApiCallConstruct(); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, - RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD); - } else { - ParameterCount actual(a0); - __ InvokeFunction(a1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Restore context from the frame. + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, exit; + + // If the result is a smi, it is *not* an object in the ECMA sense. + // v0: result + // sp[0]: receiver (newly allocated object) + // sp[1]: constructor function + // sp[2]: number of arguments (smi-tagged) + __ JumpIfSmi(v0, &use_receiver); + + // If the type of the result (stored in its map) is less than + // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. + __ GetObjectType(v0, a3, a3); + __ Branch(&exit, greater_equal, a3, Operand(FIRST_SPEC_OBJECT_TYPE)); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ lw(v0, MemOperand(sp)); + + // Remove receiver from the stack, remove caller arguments, and + // return. + __ bind(&exit); + // v0: result + // sp[0]: receiver (newly allocated object) + // sp[1]: constructor function + // sp[2]: number of arguments (smi-tagged) + __ lw(a1, MemOperand(sp, 2 * kPointerSize)); + + // Leave construct frame. } - // Pop the function from the stack. - // v0: result - // sp[0]: constructor function - // sp[2]: receiver - // sp[3]: constructor function - // sp[4]: number of arguments (smi-tagged) - __ Pop(); - - // Restore context from the frame. - __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; - - // If the result is a smi, it is *not* an object in the ECMA sense. - // v0: result - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ And(t0, v0, Operand(kSmiTagMask)); - __ Branch(&use_receiver, eq, t0, Operand(zero_reg)); - - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - __ GetObjectType(v0, a3, a3); - __ Branch(&exit, greater_equal, a3, Operand(FIRST_SPEC_OBJECT_TYPE)); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ lw(v0, MemOperand(sp)); - - // Remove receiver from the stack, remove caller arguments, and - // return. - __ bind(&exit); - // v0: result - // sp[0]: receiver (newly allocated object) - // sp[1]: constructor function - // sp[2]: number of arguments (smi-tagged) - __ lw(a1, MemOperand(sp, 2 * kPointerSize)); - __ LeaveConstructFrame(); __ sll(t0, a1, kPointerSizeLog2 - 1); __ Addu(sp, sp, t0); __ Addu(sp, sp, kPointerSize); @@ -1022,7 +1042,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // ----------- S t a t e ------------- // -- a0: code entry // -- a1: function - // -- a2: reveiver_pointer + // -- a2: receiver_pointer // -- a3: argc // -- s0: argv // ----------------------------------- @@ -1031,58 +1051,58 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ mov(cp, zero_reg); // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Set up the context from the function argument. - __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + // Set up the context from the function argument. + __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); - // Set up the roots register. - ExternalReference roots_address = - ExternalReference::roots_address(masm->isolate()); - __ li(s6, Operand(roots_address)); + __ InitializeRootRegister(); - // Push the function and the receiver onto the stack. - __ Push(a1, a2); + // Push the function and the receiver onto the stack. + __ Push(a1, a2); - // Copy arguments to the stack in a loop. - // a3: argc - // s0: argv, ie points to first arg - Label loop, entry; - __ sll(t0, a3, kPointerSizeLog2); - __ addu(t2, s0, t0); - __ b(&entry); - __ nop(); // Branch delay slot nop. - // t2 points past last arg. - __ bind(&loop); - __ lw(t0, MemOperand(s0)); // Read next parameter. - __ addiu(s0, s0, kPointerSize); - __ lw(t0, MemOperand(t0)); // Dereference handle. - __ push(t0); // Push parameter. - __ bind(&entry); - __ Branch(&loop, ne, s0, Operand(t2)); - - // Initialize all JavaScript callee-saved registers, since they will be seen - // by the garbage collector as part of handlers. - __ LoadRoot(t0, Heap::kUndefinedValueRootIndex); - __ mov(s1, t0); - __ mov(s2, t0); - __ mov(s3, t0); - __ mov(s4, t0); - __ mov(s5, t0); - // s6 holds the root address. Do not clobber. - // s7 is cp. Do not init. - - // Invoke the code and pass argc as a0. - __ mov(a0, a3); - if (is_construct) { - __ Call(masm->isolate()->builtins()->JSConstructCall()); - } else { - ParameterCount actual(a0); - __ InvokeFunction(a1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - } + // Copy arguments to the stack in a loop. + // a3: argc + // s0: argv, i.e. points to first arg + Label loop, entry; + __ sll(t0, a3, kPointerSizeLog2); + __ addu(t2, s0, t0); + __ b(&entry); + __ nop(); // Branch delay slot nop. + // t2 points past last arg. + __ bind(&loop); + __ lw(t0, MemOperand(s0)); // Read next parameter. + __ addiu(s0, s0, kPointerSize); + __ lw(t0, MemOperand(t0)); // Dereference handle. + __ push(t0); // Push parameter. + __ bind(&entry); + __ Branch(&loop, ne, s0, Operand(t2)); + + // Initialize all JavaScript callee-saved registers, since they will be seen + // by the garbage collector as part of handlers. + __ LoadRoot(t0, Heap::kUndefinedValueRootIndex); + __ mov(s1, t0); + __ mov(s2, t0); + __ mov(s3, t0); + __ mov(s4, t0); + __ mov(s5, t0); + // s6 holds the root address. Do not clobber. + // s7 is cp. Do not init. + + // Invoke the code and pass argc as a0. + __ mov(a0, a3); + if (is_construct) { + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); + __ CallStub(&stub); + } else { + ParameterCount actual(a0); + __ InvokeFunction(a1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - __ LeaveInternalFrame(); + // Leave internal frame. + } __ Jump(ra); } @@ -1100,27 +1120,28 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { void Builtins::Generate_LazyCompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve the function. - __ push(a1); - // Push call kind information. - __ push(t1); + // Preserve the function. + __ push(a1); + // Push call kind information. + __ push(t1); - // Push the function on the stack as the argument to the runtime function. - __ push(a1); - // Call the runtime function. - __ CallRuntime(Runtime::kLazyCompile, 1); - // Calculate the entry point. - __ addiu(t9, v0, Code::kHeaderSize - kHeapObjectTag); + // Push the function on the stack as the argument to the runtime function. + __ push(a1); + // Call the runtime function. + __ CallRuntime(Runtime::kLazyCompile, 1); + // Calculate the entry point. + __ addiu(t9, v0, Code::kHeaderSize - kHeapObjectTag); - // Restore call kind information. - __ pop(t1); - // Restore saved function. - __ pop(a1); + // Restore call kind information. + __ pop(t1); + // Restore saved function. + __ pop(a1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down temporary frame. + } // Do a tail-call of the compiled function. __ Jump(t9); @@ -1129,50 +1150,120 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) { void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve the function. - __ push(a1); - // Push call kind information. - __ push(t1); + // Preserve the function. + __ push(a1); + // Push call kind information. + __ push(t1); - // Push the function on the stack as the argument to the runtime function. - __ push(a1); - __ CallRuntime(Runtime::kLazyRecompile, 1); - // Calculate the entry point. - __ Addu(t9, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); + // Push the function on the stack as the argument to the runtime function. + __ push(a1); + __ CallRuntime(Runtime::kLazyRecompile, 1); + // Calculate the entry point. + __ Addu(t9, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); - // Restore call kind information. - __ pop(t1); - // Restore saved function. - __ pop(a1); + // Restore call kind information. + __ pop(t1); + // Restore saved function. + __ pop(a1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down temporary frame. + } // Do a tail-call of the compiled function. __ Jump(t9); } -// These functions are called from C++ but cannot be used in live code. +static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, + Deoptimizer::BailoutType type) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Pass the function and deoptimization type to the runtime system. + __ li(a0, Operand(Smi::FromInt(static_cast<int>(type)))); + __ push(a0); + __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + } + + // Get the full codegen state from the stack and untag it -> t2. + __ lw(t2, MemOperand(sp, 0 * kPointerSize)); + __ SmiUntag(t2); + // Switch on the state. + Label with_tos_register, unknown_state; + __ Branch(&with_tos_register, + ne, t2, Operand(FullCodeGenerator::NO_REGISTERS)); + __ Addu(sp, sp, Operand(1 * kPointerSize)); // Remove state. + __ Ret(); + + __ bind(&with_tos_register); + __ lw(v0, MemOperand(sp, 1 * kPointerSize)); + __ Branch(&unknown_state, ne, t2, Operand(FullCodeGenerator::TOS_REG)); + + __ Addu(sp, sp, Operand(2 * kPointerSize)); // Remove state. + __ Ret(); + + __ bind(&unknown_state); + __ stop("no cases left"); +} + + void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { - __ Abort("Call to unimplemented function in builtins-mips.cc"); + Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); } void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { - __ Abort("Call to unimplemented function in builtins-mips.cc"); + Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); } void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { - __ Abort("Call to unimplemented function in builtins-mips.cc"); + // For now, we are relying on the fact that Runtime::NotifyOSR + // doesn't do any garbage collection which allows us to save/restore + // the registers without worrying about which of them contain + // pointers. This seems a bit fragile. + RegList saved_regs = + (kJSCallerSaved | kCalleeSaved | ra.bit() | fp.bit()) & ~sp.bit(); + __ MultiPush(saved_regs); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kNotifyOSR, 0); + } + __ MultiPop(saved_regs); + __ Ret(); } void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { - __ Abort("Call to unimplemented function in builtins-mips.cc"); + CpuFeatures::TryForceFeatureScope scope(VFP3); + if (!CpuFeatures::IsSupported(FPU)) { + __ Abort("Unreachable code: Cannot optimize without FPU support."); + return; + } + + // Lookup the function in the JavaScript frame and push it as an + // argument to the on-stack replacement function. + __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(a0); + __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + } + + // If the result was -1 it means that we couldn't optimize the + // function. Just return and continue in the unoptimized version. + __ Ret(eq, v0, Operand(Smi::FromInt(-1))); + + // Untag the AST id and push it on the stack. + __ SmiUntag(v0); + __ push(v0); + + // Generate the code for doing the frame-to-frame translation using + // the deoptimizer infrastructure. + Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR); + generator.Generate(); } @@ -1190,19 +1281,19 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 2. Get the function to call (passed as receiver) from the stack, check // if it is a function. // a0: actual number of arguments - Label non_function; + Label slow, non_function; __ sll(at, a0, kPointerSizeLog2); __ addu(at, sp, at); __ lw(a1, MemOperand(at)); - __ And(at, a1, Operand(kSmiTagMask)); - __ Branch(&non_function, eq, at, Operand(zero_reg)); + __ JumpIfSmi(a1, &non_function); __ GetObjectType(a1, a2, a2); - __ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_TYPE)); + __ Branch(&slow, ne, a2, Operand(JS_FUNCTION_TYPE)); // 3a. Patch the first argument if necessary when calling a function. // a0: actual number of arguments // a1: function Label shift_arguments; + __ li(t0, Operand(0, RelocInfo::NONE)); // Indicate regular JS_FUNCTION. { Label convert_to_object, use_global_receiver, patch_receiver; // Change context eagerly in case we need the global receiver. __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); @@ -1210,13 +1301,13 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // Do not transform the receiver for strict mode functions. __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); __ lw(a3, FieldMemOperand(a2, SharedFunctionInfo::kCompilerHintsOffset)); - __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + + __ And(t3, a3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + kSmiTagSize))); - __ Branch(&shift_arguments, ne, t0, Operand(zero_reg)); + __ Branch(&shift_arguments, ne, t3, Operand(zero_reg)); // Do not transform the receiver for native (Compilerhints already in a3). - __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ Branch(&shift_arguments, ne, t0, Operand(zero_reg)); + __ And(t3, a3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); + __ Branch(&shift_arguments, ne, t3, Operand(zero_reg)); // Compute the receiver in non-strict mode. // Load first argument in a2. a2 = -kPointerSize(sp + n_args << 2). @@ -1238,21 +1329,25 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ Branch(&shift_arguments, ge, a3, Operand(FIRST_SPEC_OBJECT_TYPE)); __ bind(&convert_to_object); - __ EnterInternalFrame(); // In order to preserve argument count. - __ sll(a0, a0, kSmiTagSize); // Smi tagged. - __ push(a0); - - __ push(a2); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(a2, v0); - - __ pop(a0); - __ sra(a0, a0, kSmiTagSize); // Un-tag. - __ LeaveInternalFrame(); - // Restore the function to a1. + // Enter an internal frame in order to preserve argument count. + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ sll(a0, a0, kSmiTagSize); // Smi tagged. + __ push(a0); + + __ push(a2); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ mov(a2, v0); + + __ pop(a0); + __ sra(a0, a0, kSmiTagSize); // Un-tag. + // Leave internal frame. + } + // Restore the function to a1, and the flag to t0. __ sll(at, a0, kPointerSizeLog2); __ addu(at, sp, at); __ lw(a1, MemOperand(at)); + __ li(t0, Operand(0, RelocInfo::NONE)); __ Branch(&patch_receiver); // Use the global receiver object from the called function as the @@ -1273,25 +1368,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ Branch(&shift_arguments); } - // 3b. Patch the first argument when calling a non-function. The + // 3b. Check for function proxy. + __ bind(&slow); + __ li(t0, Operand(1, RelocInfo::NONE)); // Indicate function proxy. + __ Branch(&shift_arguments, eq, a2, Operand(JS_FUNCTION_PROXY_TYPE)); + + __ bind(&non_function); + __ li(t0, Operand(2, RelocInfo::NONE)); // Indicate non-function. + + // 3c. Patch the first argument when calling a non-function. The // CALL_NON_FUNCTION builtin expects the non-function callee as // receiver, so overwrite the first argument which will ultimately // become the receiver. // a0: actual number of arguments // a1: function - __ bind(&non_function); - // Restore the function in case it has been modified. + // t0: call type (0: JS function, 1: function proxy, 2: non-function) __ sll(at, a0, kPointerSizeLog2); __ addu(a2, sp, at); __ sw(a1, MemOperand(a2, -kPointerSize)); - // Clear a1 to indicate a non-function being called. - __ mov(a1, zero_reg); // 4. Shift arguments and return address one slot down on the stack // (overwriting the original receiver). Adjust argument count to make // the original first argument the new receiver. // a0: actual number of arguments // a1: function + // t0: call type (0: JS function, 1: function proxy, 2: non-function) __ bind(&shift_arguments); { Label loop; // Calculate the copy start address (destination). Copy end address is sp. @@ -1309,14 +1410,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ Pop(); } - // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. + // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin, + // or a function proxy via CALL_FUNCTION_PROXY. // a0: actual number of arguments // a1: function - { Label function; - __ Branch(&function, ne, a1, Operand(zero_reg)); - __ mov(a2, zero_reg); // expected arguments is 0 for CALL_NON_FUNCTION - __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION); + // t0: call type (0: JS function, 1: function proxy, 2: non-function) + { Label function, non_proxy; + __ Branch(&function, eq, t0, Operand(zero_reg)); + // Expected number of arguments is 0 for CALL_NON_FUNCTION. + __ mov(a2, zero_reg); __ SetCallKind(t1, CALL_AS_METHOD); + __ Branch(&non_proxy, ne, t0, Operand(1)); + + __ push(a1); // Re-add proxy object as additional argument. + __ Addu(a0, a0, Operand(1)); + __ GetBuiltinEntry(a3, Builtins::CALL_FUNCTION_PROXY); + __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); + + __ bind(&non_proxy); + __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION); __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), RelocInfo::CODE_TARGET); __ bind(&function); @@ -1350,134 +1463,158 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { const int kRecvOffset = 3 * kPointerSize; const int kFunctionOffset = 4 * kPointerSize; - __ EnterInternalFrame(); - - __ lw(a0, MemOperand(fp, kFunctionOffset)); // Get the function. - __ push(a0); - __ lw(a0, MemOperand(fp, kArgsOffset)); // Get the args array. - __ push(a0); - // Returns (in v0) number of arguments to copy to stack as Smi. - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); - - // Check the stack for overflow. We are not trying need to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - Label okay; - __ LoadRoot(a2, Heap::kRealStackLimitRootIndex); - // Make a2 the space we have left. The stack might already be overflowed - // here which will cause a2 to become negative. - __ subu(a2, sp, a2); - // Check if the arguments will overflow the stack. - __ sll(t0, v0, kPointerSizeLog2 - kSmiTagSize); - __ Branch(&okay, gt, a2, Operand(t0)); // Signed comparison. - - // Out of stack space. - __ lw(a1, MemOperand(fp, kFunctionOffset)); - __ push(a1); - __ push(v0); - __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); - // End of stack check. - - // Push current limit and index. - __ bind(&okay); - __ push(v0); // Limit. - __ mov(a1, zero_reg); // Initial index. - __ push(a1); - - // Change context eagerly to get the right global object if necessary. - __ lw(a0, MemOperand(fp, kFunctionOffset)); - __ lw(cp, FieldMemOperand(a0, JSFunction::kContextOffset)); - // Load the shared function info while the function is still in a0. - __ lw(a1, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset)); - - // Compute the receiver. - Label call_to_object, use_global_receiver, push_receiver; - __ lw(a0, MemOperand(fp, kRecvOffset)); - - // Do not transform the receiver for strict mode functions. - __ lw(a2, FieldMemOperand(a1, SharedFunctionInfo::kCompilerHintsOffset)); - __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ Branch(&push_receiver, ne, t0, Operand(zero_reg)); - - // Do not transform the receiver for native (Compilerhints already in a2). - __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ Branch(&push_receiver, ne, t0, Operand(zero_reg)); - - // Compute the receiver in non-strict mode. - __ And(t0, a0, Operand(kSmiTagMask)); - __ Branch(&call_to_object, eq, t0, Operand(zero_reg)); - __ LoadRoot(a1, Heap::kNullValueRootIndex); - __ Branch(&use_global_receiver, eq, a0, Operand(a1)); - __ LoadRoot(a2, Heap::kUndefinedValueRootIndex); - __ Branch(&use_global_receiver, eq, a0, Operand(a2)); - - // Check if the receiver is already a JavaScript object. - // a0: receiver - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ GetObjectType(a0, a1, a1); - __ Branch(&push_receiver, ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); - - // Convert the receiver to a regular object. - // a0: receiver - __ bind(&call_to_object); - __ push(a0); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ mov(a0, v0); // Put object in a0 to match other paths to push_receiver. - __ Branch(&push_receiver); - - // Use the current global receiver object as the receiver. - __ bind(&use_global_receiver); - const int kGlobalOffset = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; - __ lw(a0, FieldMemOperand(cp, kGlobalOffset)); - __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset)); - __ lw(a0, FieldMemOperand(a0, kGlobalOffset)); - __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset)); - - // Push the receiver. - // a0: receiver - __ bind(&push_receiver); - __ push(a0); - - // Copy all arguments from the array to the stack. - Label entry, loop; - __ lw(a0, MemOperand(fp, kIndexOffset)); - __ Branch(&entry); - - // Load the current argument from the arguments array and push it to the - // stack. - // a0: current argument index - __ bind(&loop); - __ lw(a1, MemOperand(fp, kArgsOffset)); - __ push(a1); - __ push(a0); + { + FrameScope frame_scope(masm, StackFrame::INTERNAL); + __ lw(a0, MemOperand(fp, kFunctionOffset)); // Get the function. + __ push(a0); + __ lw(a0, MemOperand(fp, kArgsOffset)); // Get the args array. + __ push(a0); + // Returns (in v0) number of arguments to copy to stack as Smi. + __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); + + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + Label okay; + __ LoadRoot(a2, Heap::kRealStackLimitRootIndex); + // Make a2 the space we have left. The stack might already be overflowed + // here which will cause a2 to become negative. + __ subu(a2, sp, a2); + // Check if the arguments will overflow the stack. + __ sll(t3, v0, kPointerSizeLog2 - kSmiTagSize); + __ Branch(&okay, gt, a2, Operand(t3)); // Signed comparison. + + // Out of stack space. + __ lw(a1, MemOperand(fp, kFunctionOffset)); + __ push(a1); + __ push(v0); + __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); + // End of stack check. + + // Push current limit and index. + __ bind(&okay); + __ push(v0); // Limit. + __ mov(a1, zero_reg); // Initial index. + __ push(a1); - // Call the runtime to access the property in the arguments array. - __ CallRuntime(Runtime::kGetProperty, 2); - __ push(v0); + // Get the receiver. + __ lw(a0, MemOperand(fp, kRecvOffset)); - // Use inline caching to access the arguments. - __ lw(a0, MemOperand(fp, kIndexOffset)); - __ Addu(a0, a0, Operand(1 << kSmiTagSize)); - __ sw(a0, MemOperand(fp, kIndexOffset)); + // Check that the function is a JS function (otherwise it must be a proxy). + Label push_receiver; + __ lw(a1, MemOperand(fp, kFunctionOffset)); + __ GetObjectType(a1, a2, a2); + __ Branch(&push_receiver, ne, a2, Operand(JS_FUNCTION_TYPE)); - // Test if the copy loop has finished copying all the elements from the - // arguments object. - __ bind(&entry); - __ lw(a1, MemOperand(fp, kLimitOffset)); - __ Branch(&loop, ne, a0, Operand(a1)); - // Invoke the function. - ParameterCount actual(a0); - __ sra(a0, a0, kSmiTagSize); - __ lw(a1, MemOperand(fp, kFunctionOffset)); - __ InvokeFunction(a1, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - - // Tear down the internal frame and remove function, receiver and args. - __ LeaveInternalFrame(); - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + // Change context eagerly to get the right global object if necessary. + __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + // Load the shared function info while the function is still in a1. + __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + + // Compute the receiver. + // Do not transform the receiver for strict mode functions. + Label call_to_object, use_global_receiver; + __ lw(a2, FieldMemOperand(a2, SharedFunctionInfo::kCompilerHintsOffset)); + __ And(t3, a2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + + kSmiTagSize))); + __ Branch(&push_receiver, ne, t3, Operand(zero_reg)); + + // Do not transform the receiver for native (Compilerhints already in a2). + __ And(t3, a2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); + __ Branch(&push_receiver, ne, t3, Operand(zero_reg)); + + // Compute the receiver in non-strict mode. + __ JumpIfSmi(a0, &call_to_object); + __ LoadRoot(a1, Heap::kNullValueRootIndex); + __ Branch(&use_global_receiver, eq, a0, Operand(a1)); + __ LoadRoot(a2, Heap::kUndefinedValueRootIndex); + __ Branch(&use_global_receiver, eq, a0, Operand(a2)); + + // Check if the receiver is already a JavaScript object. + // a0: receiver + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ GetObjectType(a0, a1, a1); + __ Branch(&push_receiver, ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); + + // Convert the receiver to a regular object. + // a0: receiver + __ bind(&call_to_object); + __ push(a0); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ mov(a0, v0); // Put object in a0 to match other paths to push_receiver. + __ Branch(&push_receiver); + + // Use the current global receiver object as the receiver. + __ bind(&use_global_receiver); + const int kGlobalOffset = + Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + __ lw(a0, FieldMemOperand(cp, kGlobalOffset)); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset)); + __ lw(a0, FieldMemOperand(a0, kGlobalOffset)); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset)); + + // Push the receiver. + // a0: receiver + __ bind(&push_receiver); + __ push(a0); + + // Copy all arguments from the array to the stack. + Label entry, loop; + __ lw(a0, MemOperand(fp, kIndexOffset)); + __ Branch(&entry); + + // Load the current argument from the arguments array and push it to the + // stack. + // a0: current argument index + __ bind(&loop); + __ lw(a1, MemOperand(fp, kArgsOffset)); + __ push(a1); + __ push(a0); + + // Call the runtime to access the property in the arguments array. + __ CallRuntime(Runtime::kGetProperty, 2); + __ push(v0); + + // Use inline caching to access the arguments. + __ lw(a0, MemOperand(fp, kIndexOffset)); + __ Addu(a0, a0, Operand(1 << kSmiTagSize)); + __ sw(a0, MemOperand(fp, kIndexOffset)); + + // Test if the copy loop has finished copying all the elements from the + // arguments object. + __ bind(&entry); + __ lw(a1, MemOperand(fp, kLimitOffset)); + __ Branch(&loop, ne, a0, Operand(a1)); + + // Invoke the function. + Label call_proxy; + ParameterCount actual(a0); + __ sra(a0, a0, kSmiTagSize); + __ lw(a1, MemOperand(fp, kFunctionOffset)); + __ GetObjectType(a1, a2, a2); + __ Branch(&call_proxy, ne, a2, Operand(JS_FUNCTION_TYPE)); + + __ InvokeFunction(a1, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + + frame_scope.GenerateLeaveFrame(); + __ Ret(USE_DELAY_SLOT); + __ Addu(sp, sp, Operand(3 * kPointerSize)); // In delay slot. + + // Invoke the function proxy. + __ bind(&call_proxy); + __ push(a1); // Add function proxy as last argument. + __ Addu(a0, a0, Operand(1)); + __ li(a2, Operand(0, RelocInfo::NONE)); + __ SetCallKind(t1, CALL_AS_METHOD); + __ GetBuiltinEntry(a3, Builtins::CALL_FUNCTION_PROXY); + __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); + // Tear down the internal frame and remove function, receiver and args. + } + + __ Ret(USE_DELAY_SLOT); + __ Addu(sp, sp, Operand(3 * kPointerSize)); // In delay slot. } @@ -1607,6 +1744,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ Call(a3); + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); // Exit frame and return. LeaveArgumentsAdaptorFrame(masm); __ Ret(); diff --git a/deps/v8/src/mips/code-stubs-mips.cc b/deps/v8/src/mips/code-stubs-mips.cc index c3c3874422..852b3c9e66 100644 --- a/deps/v8/src/mips/code-stubs-mips.cc +++ b/deps/v8/src/mips/code-stubs-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -100,9 +100,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { &gc, TAG_OBJECT); - int map_index = strict_mode_ == kStrictMode - ? Context::STRICT_MODE_FUNCTION_MAP_INDEX - : Context::FUNCTION_MAP_INDEX; + int map_index = (language_mode_ == CLASSIC_MODE) + ? Context::FUNCTION_MAP_INDEX + : Context::STRICT_MODE_FUNCTION_MAP_INDEX; // Compute the function map in the current global context and set that // as the map of the allocated object. @@ -157,21 +157,19 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { // Load the function from the stack. __ lw(a3, MemOperand(sp, 0)); - // Setup the object header. - __ LoadRoot(a2, Heap::kFunctionContextMapRootIndex); - __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset)); + // Set up the object header. + __ LoadRoot(a1, Heap::kFunctionContextMapRootIndex); __ li(a2, Operand(Smi::FromInt(length))); __ sw(a2, FieldMemOperand(v0, FixedArray::kLengthOffset)); + __ sw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); - // Setup the fixed slots. + // Set up the fixed slots, copy the global object from the previous context. + __ lw(a2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); __ li(a1, Operand(Smi::FromInt(0))); __ sw(a3, MemOperand(v0, Context::SlotOffset(Context::CLOSURE_INDEX))); __ sw(cp, MemOperand(v0, Context::SlotOffset(Context::PREVIOUS_INDEX))); __ sw(a1, MemOperand(v0, Context::SlotOffset(Context::EXTENSION_INDEX))); - - // Copy the global object from the previous context. - __ lw(a1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ sw(a1, MemOperand(v0, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ sw(a2, MemOperand(v0, Context::SlotOffset(Context::GLOBAL_INDEX))); // Initialize the rest of the slots to undefined. __ LoadRoot(a1, Heap::kUndefinedValueRootIndex); @@ -190,16 +188,124 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastNewBlockContextStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [sp]: function. + // [sp + kPointerSize]: serialized scope info + + // Try to allocate the context in new space. + Label gc; + int length = slots_ + Context::MIN_CONTEXT_SLOTS; + __ AllocateInNewSpace(FixedArray::SizeFor(length), + v0, a1, a2, &gc, TAG_OBJECT); + + // Load the function from the stack. + __ lw(a3, MemOperand(sp, 0)); + + // Load the serialized scope info from the stack. + __ lw(a1, MemOperand(sp, 1 * kPointerSize)); + + // Set up the object header. + __ LoadRoot(a2, Heap::kBlockContextMapRootIndex); + __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset)); + __ li(a2, Operand(Smi::FromInt(length))); + __ sw(a2, FieldMemOperand(v0, FixedArray::kLengthOffset)); + + // If this block context is nested in the global context we get a smi + // sentinel instead of a function. The block context should get the + // canonical empty function of the global context as its closure which + // we still have to look up. + Label after_sentinel; + __ JumpIfNotSmi(a3, &after_sentinel); + if (FLAG_debug_code) { + const char* message = "Expected 0 as a Smi sentinel"; + __ Assert(eq, message, a3, Operand(zero_reg)); + } + __ lw(a3, GlobalObjectOperand()); + __ lw(a3, FieldMemOperand(a3, GlobalObject::kGlobalContextOffset)); + __ lw(a3, ContextOperand(a3, Context::CLOSURE_INDEX)); + __ bind(&after_sentinel); + + // Set up the fixed slots, copy the global object from the previous context. + __ lw(a2, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ sw(a3, ContextOperand(v0, Context::CLOSURE_INDEX)); + __ sw(cp, ContextOperand(v0, Context::PREVIOUS_INDEX)); + __ sw(a1, ContextOperand(v0, Context::EXTENSION_INDEX)); + __ sw(a2, ContextOperand(v0, Context::GLOBAL_INDEX)); + + // Initialize the rest of the slots to the hole value. + __ LoadRoot(a1, Heap::kTheHoleValueRootIndex); + for (int i = 0; i < slots_; i++) { + __ sw(a1, ContextOperand(v0, i + Context::MIN_CONTEXT_SLOTS)); + } + + // Remove the on-stack argument and return. + __ mov(cp, v0); + __ Addu(sp, sp, Operand(2 * kPointerSize)); + __ Ret(); + + // Need to collect. Call into runtime system. + __ bind(&gc); + __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1); +} + + +static void GenerateFastCloneShallowArrayCommon( + MacroAssembler* masm, + int length, + FastCloneShallowArrayStub::Mode mode, + Label* fail) { + // Registers on entry: + // a3: boilerplate literal array. + ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS); + + // All sizes here are multiples of kPointerSize. + int elements_size = 0; + if (length > 0) { + elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + ? FixedDoubleArray::SizeFor(length) + : FixedArray::SizeFor(length); + } + int size = JSArray::kSize + elements_size; + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size, + v0, + a1, + a2, + fail, + TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length == 0)) { + __ lw(a1, FieldMemOperand(a3, i)); + __ sw(a1, FieldMemOperand(v0, i)); + } + } + + if (length > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ lw(a3, FieldMemOperand(a3, JSArray::kElementsOffset)); + __ Addu(a2, v0, Operand(JSArray::kSize)); + __ sw(a2, FieldMemOperand(v0, JSArray::kElementsOffset)); + + // Copy the elements array. + ASSERT((elements_size % kPointerSize) == 0); + __ CopyFields(a2, a3, a1.bit(), elements_size / kPointerSize); + } +} + void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // Stack layout on entry: + // // [sp]: constant elements. // [sp + kPointerSize]: literal index. // [sp + (2 * kPointerSize)]: literals array. - // All sizes here are multiples of kPointerSize. - int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; - int size = JSArray::kSize + elements_size; - // Load boilerplate object into r3 and check if we need to create a // boilerplate. Label slow_case; @@ -212,14 +318,42 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ LoadRoot(t1, Heap::kUndefinedValueRootIndex); __ Branch(&slow_case, eq, a3, Operand(t1)); + FastCloneShallowArrayStub::Mode mode = mode_; + if (mode == CLONE_ANY_ELEMENTS) { + Label double_elements, check_fast_elements; + __ lw(v0, FieldMemOperand(a3, JSArray::kElementsOffset)); + __ lw(v0, FieldMemOperand(v0, HeapObject::kMapOffset)); + __ LoadRoot(t1, Heap::kFixedCOWArrayMapRootIndex); + __ Branch(&check_fast_elements, ne, v0, Operand(t1)); + GenerateFastCloneShallowArrayCommon(masm, 0, + COPY_ON_WRITE_ELEMENTS, &slow_case); + // Return and remove the on-stack parameters. + __ DropAndRet(3); + + __ bind(&check_fast_elements); + __ LoadRoot(t1, Heap::kFixedArrayMapRootIndex); + __ Branch(&double_elements, ne, v0, Operand(t1)); + GenerateFastCloneShallowArrayCommon(masm, length_, + CLONE_ELEMENTS, &slow_case); + // Return and remove the on-stack parameters. + __ DropAndRet(3); + + __ bind(&double_elements); + mode = CLONE_DOUBLE_ELEMENTS; + // Fall through to generate the code to handle double elements. + } + if (FLAG_debug_code) { const char* message; Heap::RootListIndex expected_map_index; - if (mode_ == CLONE_ELEMENTS) { + if (mode == CLONE_ELEMENTS) { message = "Expected (writable) fixed array"; expected_map_index = Heap::kFixedArrayMapRootIndex; + } else if (mode == CLONE_DOUBLE_ELEMENTS) { + message = "Expected (writable) fixed double array"; + expected_map_index = Heap::kFixedDoubleArrayMapRootIndex; } else { - ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS); + ASSERT(mode == COPY_ON_WRITE_ELEMENTS); message = "Expected copy-on-write fixed array"; expected_map_index = Heap::kFixedCOWArrayMapRootIndex; } @@ -231,41 +365,59 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ pop(a3); } - // Allocate both the JS array and the elements array in one big - // allocation. This avoids multiple limit checks. - // Return new object in v0. - __ AllocateInNewSpace(size, - v0, - a1, - a2, - &slow_case, - TAG_OBJECT); + GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case); - // Copy the JS array part. - for (int i = 0; i < JSArray::kSize; i += kPointerSize) { - if ((i != JSArray::kElementsOffset) || (length_ == 0)) { - __ lw(a1, FieldMemOperand(a3, i)); - __ sw(a1, FieldMemOperand(v0, i)); - } - } + // Return and remove the on-stack parameters. + __ Addu(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); - if (length_ > 0) { - // Get hold of the elements array of the boilerplate and setup the - // elements pointer in the resulting object. - __ lw(a3, FieldMemOperand(a3, JSArray::kElementsOffset)); - __ Addu(a2, v0, Operand(JSArray::kSize)); - __ sw(a2, FieldMemOperand(v0, JSArray::kElementsOffset)); + __ bind(&slow_case); + __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); +} - // Copy the elements array. - __ CopyFields(a2, a3, a1.bit(), elements_size / kPointerSize); + +void FastCloneShallowObjectStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [sp]: object literal flags. + // [sp + kPointerSize]: constant properties. + // [sp + (2 * kPointerSize)]: literal index. + // [sp + (3 * kPointerSize)]: literals array. + + // Load boilerplate object into a3 and check if we need to create a + // boilerplate. + Label slow_case; + __ lw(a3, MemOperand(sp, 3 * kPointerSize)); + __ lw(a0, MemOperand(sp, 2 * kPointerSize)); + __ Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize); + __ Addu(a3, t0, a3); + __ lw(a3, MemOperand(a3)); + __ LoadRoot(t0, Heap::kUndefinedValueRootIndex); + __ Branch(&slow_case, eq, a3, Operand(t0)); + + // Check that the boilerplate contains only fast properties and we can + // statically determine the instance size. + int size = JSObject::kHeaderSize + length_ * kPointerSize; + __ lw(a0, FieldMemOperand(a3, HeapObject::kMapOffset)); + __ lbu(a0, FieldMemOperand(a0, Map::kInstanceSizeOffset)); + __ Branch(&slow_case, ne, a0, Operand(size >> kPointerSizeLog2)); + + // Allocate the JS object and copy header together with all in-object + // properties from the boilerplate. + __ AllocateInNewSpace(size, a0, a1, a2, &slow_case, TAG_OBJECT); + for (int i = 0; i < size; i += kPointerSize) { + __ lw(a1, FieldMemOperand(a3, i)); + __ sw(a1, FieldMemOperand(a0, i)); } // Return and remove the on-stack parameters. - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ Drop(4); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); __ bind(&slow_case); - __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); + __ TailCallRuntime(Runtime::kCreateObjectLiteralShallow, 4, 1); } @@ -436,7 +588,9 @@ void FloatingPointHelper::LoadNumber(MacroAssembler* masm, Label is_smi, done; - __ JumpIfSmi(object, &is_smi); + // Smi-check + __ UntagAndJumpIfSmi(scratch1, object, &is_smi); + // Heap number check __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number); // Handle loading a double from a heap number. @@ -463,7 +617,6 @@ void FloatingPointHelper::LoadNumber(MacroAssembler* masm, if (CpuFeatures::IsSupported(FPU)) { CpuFeatures::Scope scope(FPU); // Convert smi to double using FPU instructions. - __ SmiUntag(scratch1, object); __ mtc1(scratch1, dst); __ cvt_d_w(dst, dst); if (destination == kCoreRegisters) { @@ -498,11 +651,10 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm, Heap::kHeapNumberMapRootIndex, "HeapNumberMap register clobbered."); } - Label is_smi; Label done; Label not_in_int32_range; - __ JumpIfSmi(object, &is_smi); + __ UntagAndJumpIfSmi(dst, object, &done); __ lw(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset)); __ Branch(not_number, ne, scratch1, Operand(heap_number_map)); __ ConvertToInt32(object, @@ -522,10 +674,6 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm, scratch2, scratch3); - __ jmp(&done); - - __ bind(&is_smi); - __ SmiUntag(dst, object); __ bind(&done); } @@ -570,7 +718,7 @@ void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm, __ Subu(int_scratch, zero_reg, int_scratch); __ bind(&skip_sub); - // Get mantisssa[51:20]. + // Get mantissa[51:20]. // Get the position of the first set bit. __ clz(dst1, int_scratch); @@ -615,7 +763,7 @@ void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm, void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, Register object, Destination destination, - FPURegister double_dst, + DoubleRegister double_dst, Register dst1, Register dst2, Register heap_number_map, @@ -651,25 +799,16 @@ void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, // Load the double value. __ ldc1(double_dst, FieldMemOperand(object, HeapNumber::kValueOffset)); - // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate). - // On MIPS a lot of things cannot be implemented the same way so right - // now it makes a lot more sense to just do things manually. - - // Save FCSR. - __ cfc1(scratch1, FCSR); - // Disable FPU exceptions. - __ ctc1(zero_reg, FCSR); - __ trunc_w_d(single_scratch, double_dst); - // Retrieve FCSR. - __ cfc1(scratch2, FCSR); - // Restore FCSR. - __ ctc1(scratch1, FCSR); - - // Check for inexact conversion or exception. - __ And(scratch2, scratch2, kFCSRFlagMask); + Register except_flag = scratch2; + __ EmitFPUTruncate(kRoundToZero, + single_scratch, + double_dst, + scratch1, + except_flag, + kCheckForInexactConversion); // Jump to not_int32 if the operation did not succeed. - __ Branch(not_int32, ne, scratch2, Operand(zero_reg)); + __ Branch(not_int32, ne, except_flag, Operand(zero_reg)); if (destination == kCoreRegisters) { __ Move(dst1, dst2, double_dst); @@ -706,7 +845,7 @@ void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm, Register scratch1, Register scratch2, Register scratch3, - FPURegister double_scratch, + DoubleRegister double_scratch, Label* not_int32) { ASSERT(!dst.is(object)); ASSERT(!scratch1.is(object) && !scratch2.is(object) && !scratch3.is(object)); @@ -716,10 +855,7 @@ void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm, Label done; - // Untag the object into the destination register. - __ SmiUntag(dst, object); - // Just return if the object is a smi. - __ JumpIfSmi(object, &done); + __ UntagAndJumpIfSmi(dst, object, &done); if (FLAG_debug_code) { __ AbortIfNotRootValue(heap_number_map, @@ -735,27 +871,19 @@ void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm, // Load the double value. __ ldc1(double_scratch, FieldMemOperand(object, HeapNumber::kValueOffset)); - // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate). - // On MIPS a lot of things cannot be implemented the same way so right - // now it makes a lot more sense to just do things manually. - - // Save FCSR. - __ cfc1(scratch1, FCSR); - // Disable FPU exceptions. - __ ctc1(zero_reg, FCSR); - __ trunc_w_d(double_scratch, double_scratch); - // Retrieve FCSR. - __ cfc1(scratch2, FCSR); - // Restore FCSR. - __ ctc1(scratch1, FCSR); - - // Check for inexact conversion or exception. - __ And(scratch2, scratch2, kFCSRFlagMask); + FPURegister single_scratch = double_scratch.low(); + Register except_flag = scratch2; + __ EmitFPUTruncate(kRoundToZero, + single_scratch, + double_scratch, + scratch1, + except_flag, + kCheckForInexactConversion); // Jump to not_int32 if the operation did not succeed. - __ Branch(not_int32, ne, scratch2, Operand(zero_reg)); + __ Branch(not_int32, ne, except_flag, Operand(zero_reg)); // Get the result in the destination register. - __ mfc1(dst, double_scratch); + __ mfc1(dst, single_scratch); } else { // Load the double value in the destination registers. @@ -832,7 +960,7 @@ void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm, // non zero bits left. So we need the (30 - exponent) last bits of the // 31 higher bits of the mantissa to be null. // Because bits [21:0] are null, we can check instead that the - // (32 - exponent) last bits of the 32 higher bits of the mantisssa are null. + // (32 - exponent) last bits of the 32 higher bits of the mantissa are null. // Get the 32 higher bits of the mantissa in dst. __ Ext(dst, @@ -881,9 +1009,11 @@ void FloatingPointHelper::CallCCodeForDoubleOperation( __ Move(f12, a0, a1); __ Move(f14, a2, a3); } - // Call C routine that may not cause GC or other trouble. - __ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()), - 4); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction( + ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2); + } // Store answer in the overwritable heap number. if (!IsMipsSoftFloatABI) { CpuFeatures::Scope scope(FPU); @@ -901,6 +1031,35 @@ void FloatingPointHelper::CallCCodeForDoubleOperation( } +bool WriteInt32ToHeapNumberStub::IsPregenerated() { + // These variants are compiled ahead of time. See next method. + if (the_int_.is(a1) && + the_heap_number_.is(v0) && + scratch_.is(a2) && + sign_.is(a3)) { + return true; + } + if (the_int_.is(a2) && + the_heap_number_.is(v0) && + scratch_.is(a3) && + sign_.is(a0)) { + return true; + } + // Other register combinations are generated as and when they are needed, + // so it is unsafe to call them from stubs (we can't generate a stub while + // we are generating a stub). + return false; +} + + +void WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime() { + WriteInt32ToHeapNumberStub stub1(a1, v0, a2, a3); + WriteInt32ToHeapNumberStub stub2(a2, v0, a3, a0); + stub1.GetCode()->set_is_pregenerated(true); + stub2.GetCode()->set_is_pregenerated(true); +} + + // See comment for class, this does NOT work for int32's that are in Smi range. void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) { Label max_negative_int; @@ -1068,8 +1227,7 @@ static void EmitSmiNonsmiComparison(MacroAssembler* masm, (lhs.is(a1) && rhs.is(a0))); Label lhs_is_smi; - __ And(t0, lhs, Operand(kSmiTagMask)); - __ Branch(&lhs_is_smi, eq, t0, Operand(zero_reg)); + __ JumpIfSmi(lhs, &lhs_is_smi); // Rhs is a Smi. // Check whether the non-smi is a heap number. __ GetObjectType(lhs, t4, t4); @@ -1258,7 +1416,7 @@ static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) { if (!CpuFeatures::IsSupported(FPU)) { __ push(ra); - __ PrepareCallCFunction(4, t4); // Two doubles count as 4 arguments. + __ PrepareCallCFunction(0, 2, t4); if (!IsMipsSoftFloatABI) { // We are not using MIPS FPU instructions, and parameters for the runtime // function call are prepaired in a0-a3 registers, but function we are @@ -1268,19 +1426,17 @@ static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) { __ Move(f12, a0, a1); __ Move(f14, a2, a3); } - __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), 4); + + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), + 0, 2); __ pop(ra); // Because this function returns int, result is in v0. __ Ret(); } else { CpuFeatures::Scope scope(FPU); Label equal, less_than; - __ c(EQ, D, f12, f14); - __ bc1t(&equal); - __ nop(); - - __ c(OLT, D, f12, f14); - __ bc1t(&less_than); - __ nop(); + __ BranchF(&equal, NULL, eq, f12, f14); + __ BranchF(&less_than, NULL, lt, f12, f14); // Not equal, not less, not NaN, must be greater. __ li(v0, Operand(GREATER)); @@ -1303,7 +1459,7 @@ static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm, // If either operand is a JS object or an oddball value, then they are // not equal since their pointers are different. // There is no test for undetectability in strict equality. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); + STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); Label first_non_object; // Get the type of the first operand into a2 and compare it with // FIRST_SPEC_OBJECT_TYPE. @@ -1473,9 +1629,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, __ JumpIfSmi(probe, not_found); __ ldc1(f12, FieldMemOperand(object, HeapNumber::kValueOffset)); __ ldc1(f14, FieldMemOperand(probe, HeapNumber::kValueOffset)); - __ c(EQ, D, f12, f14); - __ bc1t(&load_result_from_cache); - __ nop(); // bc1t() requires explicit fill of branch delay slot. + __ BranchF(&load_result_from_cache, NULL, eq, f12, f14); __ Branch(not_found); } else { // Note that there is no cache check for non-FPU case, even though @@ -1591,9 +1745,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ li(t2, Operand(EQUAL)); // Check if either rhs or lhs is NaN. - __ c(UN, D, f12, f14); - __ bc1t(&nan); - __ nop(); + __ BranchF(NULL, &nan, eq, f12, f14); // Check if LESS condition is satisfied. If true, move conditionally // result to v0. @@ -1711,88 +1863,144 @@ void CompareStub::Generate(MacroAssembler* masm) { } -// The stub returns zero for false, and a non-zero value for true. +// The stub expects its argument in the tos_ register and returns its result in +// it, too: zero for false, and a non-zero value for true. void ToBooleanStub::Generate(MacroAssembler* masm) { // This stub uses FPU instructions. CpuFeatures::Scope scope(FPU); - Label false_result; - Label not_heap_number; - Register scratch0 = t5.is(tos_) ? t3 : t5; - - // undefined -> false - __ LoadRoot(scratch0, Heap::kUndefinedValueRootIndex); - __ Branch(&false_result, eq, tos_, Operand(scratch0)); - - // Boolean -> its value - __ LoadRoot(scratch0, Heap::kFalseValueRootIndex); - __ Branch(&false_result, eq, tos_, Operand(scratch0)); - __ LoadRoot(scratch0, Heap::kTrueValueRootIndex); - // "tos_" is a register and contains a non-zero value. Hence we implicitly - // return true if the equal condition is satisfied. - __ Ret(eq, tos_, Operand(scratch0)); - - // Smis: 0 -> false, all other -> true - __ And(scratch0, tos_, tos_); - __ Branch(&false_result, eq, scratch0, Operand(zero_reg)); - __ And(scratch0, tos_, Operand(kSmiTagMask)); - // "tos_" is a register and contains a non-zero value. Hence we implicitly - // return true if the not equal condition is satisfied. - __ Ret(eq, scratch0, Operand(zero_reg)); - - // 'null' -> false - __ LoadRoot(scratch0, Heap::kNullValueRootIndex); - __ Branch(&false_result, eq, tos_, Operand(scratch0)); - - // HeapNumber => false if +0, -0, or NaN. - __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset)); - __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); - __ Branch(¬_heap_number, ne, scratch0, Operand(at)); - - __ ldc1(f12, FieldMemOperand(tos_, HeapNumber::kValueOffset)); - __ fcmp(f12, 0.0, UEQ); - - // "tos_" is a register, and contains a non zero value by default. - // Hence we only need to overwrite "tos_" with zero to return false for - // FP_ZERO or FP_NAN cases. Otherwise, by default it returns true. - __ movt(tos_, zero_reg); - __ Ret(); + Label patch; + const Register map = t5.is(tos_) ? t3 : t5; - __ bind(¬_heap_number); - - // It can be an undetectable object. - // Undetectable => false. - __ lw(at, FieldMemOperand(tos_, HeapObject::kMapOffset)); - __ lbu(scratch0, FieldMemOperand(at, Map::kBitFieldOffset)); - __ And(scratch0, scratch0, Operand(1 << Map::kIsUndetectable)); - __ Branch(&false_result, eq, scratch0, Operand(1 << Map::kIsUndetectable)); - - // JavaScript object => true. - __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset)); - __ lbu(scratch0, FieldMemOperand(scratch0, Map::kInstanceTypeOffset)); - - // "tos_" is a register and contains a non-zero value. - // Hence we implicitly return true if the greater than - // condition is satisfied. - __ Ret(ge, scratch0, Operand(FIRST_SPEC_OBJECT_TYPE)); - - // Check for string. - __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset)); - __ lbu(scratch0, FieldMemOperand(scratch0, Map::kInstanceTypeOffset)); - // "tos_" is a register and contains a non-zero value. - // Hence we implicitly return true if the greater than - // condition is satisfied. - __ Ret(ge, scratch0, Operand(FIRST_NONSTRING_TYPE)); - - // String value => false iff empty, i.e., length is zero. - __ lw(tos_, FieldMemOperand(tos_, String::kLengthOffset)); - // If length is zero, "tos_" contains zero ==> false. - // If length is not zero, "tos_" contains a non-zero value ==> true. - __ Ret(); + // undefined -> false. + CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false); + + // Boolean -> its value. + CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false); + CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true); + + // 'null' -> false. + CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false); + + if (types_.Contains(SMI)) { + // Smis: 0 -> false, all other -> true + __ And(at, tos_, kSmiTagMask); + // tos_ contains the correct return value already + __ Ret(eq, at, Operand(zero_reg)); + } else if (types_.NeedsMap()) { + // If we need a map later and have a Smi -> patch. + __ JumpIfSmi(tos_, &patch); + } + + if (types_.NeedsMap()) { + __ lw(map, FieldMemOperand(tos_, HeapObject::kMapOffset)); + + if (types_.CanBeUndetectable()) { + __ lbu(at, FieldMemOperand(map, Map::kBitFieldOffset)); + __ And(at, at, Operand(1 << Map::kIsUndetectable)); + // Undetectable -> false. + __ movn(tos_, zero_reg, at); + __ Ret(ne, at, Operand(zero_reg)); + } + } + + if (types_.Contains(SPEC_OBJECT)) { + // Spec object -> true. + __ lbu(at, FieldMemOperand(map, Map::kInstanceTypeOffset)); + // tos_ contains the correct non-zero return value already. + __ Ret(ge, at, Operand(FIRST_SPEC_OBJECT_TYPE)); + } + + if (types_.Contains(STRING)) { + // String value -> false iff empty. + __ lbu(at, FieldMemOperand(map, Map::kInstanceTypeOffset)); + Label skip; + __ Branch(&skip, ge, at, Operand(FIRST_NONSTRING_TYPE)); + __ lw(tos_, FieldMemOperand(tos_, String::kLengthOffset)); + __ Ret(); // the string length is OK as the return value + __ bind(&skip); + } - // Return 0 in "tos_" for false. - __ bind(&false_result); - __ mov(tos_, zero_reg); + if (types_.Contains(HEAP_NUMBER)) { + // Heap number -> false iff +0, -0, or NaN. + Label not_heap_number; + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + __ Branch(¬_heap_number, ne, map, Operand(at)); + Label zero_or_nan, number; + __ ldc1(f2, FieldMemOperand(tos_, HeapNumber::kValueOffset)); + __ BranchF(&number, &zero_or_nan, ne, f2, kDoubleRegZero); + // "tos_" is a register, and contains a non zero value by default. + // Hence we only need to overwrite "tos_" with zero to return false for + // FP_ZERO or FP_NAN cases. Otherwise, by default it returns true. + __ bind(&zero_or_nan); + __ mov(tos_, zero_reg); + __ bind(&number); + __ Ret(); + __ bind(¬_heap_number); + } + + __ bind(&patch); + GenerateTypeTransition(masm); +} + + +void ToBooleanStub::CheckOddball(MacroAssembler* masm, + Type type, + Heap::RootListIndex value, + bool result) { + if (types_.Contains(type)) { + // If we see an expected oddball, return its ToBoolean value tos_. + __ LoadRoot(at, value); + __ Subu(at, at, tos_); // This is a check for equality for the movz below. + // The value of a root is never NULL, so we can avoid loading a non-null + // value into tos_ when we want to return 'true'. + if (!result) { + __ movz(tos_, zero_reg, at); + } + __ Ret(eq, at, Operand(zero_reg)); + } +} + + +void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) { + __ Move(a3, tos_); + __ li(a2, Operand(Smi::FromInt(tos_.code()))); + __ li(a1, Operand(Smi::FromInt(types_.ToByte()))); + __ Push(a3, a2, a1); + // Patch the caller to an appropriate specialized stub and return the + // operation result to the caller of the stub. + __ TailCallExternalReference( + ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()), + 3, + 1); +} + + +void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + __ MultiPush(kJSCallerSaved | ra.bit()); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(FPU); + __ MultiPushFPU(kCallerSavedFPU); + } + const int argument_count = 1; + const int fp_argument_count = 0; + const Register scratch = a1; + + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(argument_count, fp_argument_count, scratch); + __ li(a0, Operand(ExternalReference::isolate_address())); + __ CallCFunction( + ExternalReference::store_buffer_overflow_function(masm->isolate()), + argument_count); + if (save_doubles_ == kSaveFPRegs) { + CpuFeatures::Scope scope(FPU); + __ MultiPopFPU(kCallerSavedFPU); + } + + __ MultiPop(kJSCallerSaved | ra.bit()); __ Ret(); } @@ -1951,12 +2159,13 @@ void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm, __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(a0); - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ mov(a1, v0); - __ pop(a0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(a0); + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(a1, v0); + __ pop(a0); + } __ bind(&heapnumber_allocated); __ lw(a3, FieldMemOperand(a0, HeapNumber::kMantissaOffset)); @@ -1998,13 +2207,14 @@ void UnaryOpStub::GenerateHeapNumberCodeBitNot( __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(v0); // Push the heap number, not the untagged int32. - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ mov(a2, v0); // Move the new heap number into a2. - // Get the heap number into v0, now that the new heap number is in a2. - __ pop(v0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(v0); // Push the heap number, not the untagged int32. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(a2, v0); // Move the new heap number into a2. + // Get the heap number into v0, now that the new heap number is in a2. + __ pop(v0); + } // Convert the heap number in v0 to an untagged integer in a1. // This can't go slow-case because it's the same number we already @@ -2115,6 +2325,9 @@ void BinaryOpStub::GenerateTypeTransitionWithSavedArgs( void BinaryOpStub::Generate(MacroAssembler* masm) { + // Explicitly allow generation of nested stubs. It is safe here because + // generation code does not use any raw pointers. + AllowStubCallsScope allow_stub_calls(masm, true); switch (operands_type_) { case BinaryOpIC::UNINITIALIZED: GenerateTypeTransition(masm); @@ -2717,26 +2930,16 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { // Otherwise return a heap number if allowed, or jump to type // transition. - // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate). - // On MIPS a lot of things cannot be implemented the same way so right - // now it makes a lot more sense to just do things manually. - - // Save FCSR. - __ cfc1(scratch1, FCSR); - // Disable FPU exceptions. - __ ctc1(zero_reg, FCSR); - __ trunc_w_d(single_scratch, f10); - // Retrieve FCSR. - __ cfc1(scratch2, FCSR); - // Restore FCSR. - __ ctc1(scratch1, FCSR); - - // Check for inexact conversion or exception. - __ And(scratch2, scratch2, kFCSRFlagMask); + Register except_flag = scratch2; + __ EmitFPUTruncate(kRoundToZero, + single_scratch, + f10, + scratch1, + except_flag); if (result_type_ <= BinaryOpIC::INT32) { - // If scratch2 != 0, result does not fit in a 32-bit integer. - __ Branch(&transition, ne, scratch2, Operand(zero_reg)); + // If except_flag != 0, result does not fit in a 32-bit integer. + __ Branch(&transition, ne, except_flag, Operand(zero_reg)); } // Check if the result fits in a smi. @@ -2929,9 +3132,9 @@ void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { __ Ret(); } else { // Tail call that writes the int32 in a2 to the heap number in v0, using - // a3 and a1 as scratch. v0 is preserved and returned. + // a3 and a0 as scratch. v0 is preserved and returned. __ mov(a0, t1); - WriteInt32ToHeapNumberStub stub(a2, v0, a3, a1); + WriteInt32ToHeapNumberStub stub(a2, v0, a3, a0); __ TailCallStub(&stub); } @@ -3225,10 +3428,12 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ lw(t0, MemOperand(cache_entry, 0)); __ lw(t1, MemOperand(cache_entry, 4)); __ lw(t2, MemOperand(cache_entry, 8)); - __ Addu(cache_entry, cache_entry, 12); __ Branch(&calculate, ne, a2, Operand(t0)); __ Branch(&calculate, ne, a3, Operand(t1)); // Cache hit. Load result, cleanup and return. + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter( + counters->transcendental_cache_hit(), 1, scratch0, scratch1); if (tagged) { // Pop input value from stack and load result into v0. __ Drop(1); @@ -3241,6 +3446,9 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { } // if (CpuFeatures::IsSupported(FPU)) __ bind(&calculate); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter( + counters->transcendental_cache_miss(), 1, scratch0, scratch1); if (tagged) { __ bind(&invalid_cache); __ TailCallExternalReference(ExternalReference(RuntimeFunction(), @@ -3259,13 +3467,13 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // Register a0 holds precalculated cache entry address; preserve // it on the stack and pop it into register cache_entry after the // call. - __ push(cache_entry); + __ Push(cache_entry, a2, a3); GenerateCallCFunction(masm, scratch0); __ GetCFunctionDoubleResult(f4); // Try to update the cache. If we cannot allocate a // heap number, we return the result without updating. - __ pop(cache_entry); + __ Pop(cache_entry, a2, a3); __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex); __ AllocateHeapNumber(t2, scratch0, scratch1, t1, &no_update); __ sdc1(f4, FieldMemOperand(t2, HeapNumber::kValueOffset)); @@ -3283,10 +3491,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex); __ AllocateHeapNumber(a0, scratch0, scratch1, t1, &skip_cache); __ sdc1(f4, FieldMemOperand(a0, HeapNumber::kValueOffset)); - __ EnterInternalFrame(); - __ push(a0); - __ CallRuntime(RuntimeFunction(), 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(a0); + __ CallRuntime(RuntimeFunction(), 1); + } __ ldc1(f4, FieldMemOperand(v0, HeapNumber::kValueOffset)); __ Ret(); @@ -3299,14 +3508,15 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { // We return the value in f4 without adding it to the cache, but // we cause a scavenging GC so that future allocations will succeed. - __ EnterInternalFrame(); - - // Allocate an aligned object larger than a HeapNumber. - ASSERT(4 * kPointerSize >= HeapNumber::kSize); - __ li(scratch0, Operand(4 * kPointerSize)); - __ push(scratch0); - __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Allocate an aligned object larger than a HeapNumber. + ASSERT(4 * kPointerSize >= HeapNumber::kSize); + __ li(scratch0, Operand(4 * kPointerSize)); + __ push(scratch0); + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); + } __ Ret(); } } @@ -3317,22 +3527,31 @@ void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm, __ push(ra); __ PrepareCallCFunction(2, scratch); if (IsMipsSoftFloatABI) { - __ Move(v0, v1, f4); + __ Move(a0, a1, f4); } else { __ mov_d(f12, f4); } + AllowExternalCallThatCantCauseGC scope(masm); + Isolate* isolate = masm->isolate(); switch (type_) { case TranscendentalCache::SIN: __ CallCFunction( - ExternalReference::math_sin_double_function(masm->isolate()), 2); + ExternalReference::math_sin_double_function(isolate), + 0, 1); break; case TranscendentalCache::COS: __ CallCFunction( - ExternalReference::math_cos_double_function(masm->isolate()), 2); + ExternalReference::math_cos_double_function(isolate), + 0, 1); + break; + case TranscendentalCache::TAN: + __ CallCFunction(ExternalReference::math_tan_double_function(isolate), + 0, 1); break; case TranscendentalCache::LOG: __ CallCFunction( - ExternalReference::math_log_double_function(masm->isolate()), 2); + ExternalReference::math_log_double_function(isolate), + 0, 1); break; default: UNIMPLEMENTED(); @@ -3347,6 +3566,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { // Add more cases when necessary. case TranscendentalCache::SIN: return Runtime::kMath_sin; case TranscendentalCache::COS: return Runtime::kMath_cos; + case TranscendentalCache::TAN: return Runtime::kMath_tan; case TranscendentalCache::LOG: return Runtime::kMath_log; default: UNIMPLEMENTED(); @@ -3361,105 +3581,218 @@ void StackCheckStub::Generate(MacroAssembler* masm) { void MathPowStub::Generate(MacroAssembler* masm) { - Label call_runtime; - - if (CpuFeatures::IsSupported(FPU)) { - CpuFeatures::Scope scope(FPU); - - Label base_not_smi; - Label exponent_not_smi; - Label convert_exponent; - - const Register base = a0; - const Register exponent = a2; - const Register heapnumbermap = t1; - const Register heapnumber = s0; // Callee-saved register. - const Register scratch = t2; - const Register scratch2 = t3; - - // Alocate FP values in the ABI-parameter-passing regs. - const DoubleRegister double_base = f12; - const DoubleRegister double_exponent = f14; - const DoubleRegister double_result = f0; - const DoubleRegister double_scratch = f2; - - __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex); + CpuFeatures::Scope fpu_scope(FPU); + const Register base = a1; + const Register exponent = a2; + const Register heapnumbermap = t1; + const Register heapnumber = v0; + const DoubleRegister double_base = f2; + const DoubleRegister double_exponent = f4; + const DoubleRegister double_result = f0; + const DoubleRegister double_scratch = f6; + const FPURegister single_scratch = f8; + const Register scratch = t5; + const Register scratch2 = t3; + + Label call_runtime, done, int_exponent; + if (exponent_type_ == ON_STACK) { + Label base_is_smi, unpack_exponent; + // The exponent and base are supplied as arguments on the stack. + // This can only happen if the stub is called from non-optimized code. + // Load input parameters from stack to double registers. __ lw(base, MemOperand(sp, 1 * kPointerSize)); __ lw(exponent, MemOperand(sp, 0 * kPointerSize)); - // Convert base to double value and store it in f0. - __ JumpIfNotSmi(base, &base_not_smi); - // Base is a Smi. Untag and convert it. - __ SmiUntag(base); - __ mtc1(base, double_scratch); - __ cvt_d_w(double_base, double_scratch); - __ Branch(&convert_exponent); + __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex); - __ bind(&base_not_smi); + __ UntagAndJumpIfSmi(scratch, base, &base_is_smi); __ lw(scratch, FieldMemOperand(base, JSObject::kMapOffset)); __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap)); - // Base is a heapnumber. Load it into double register. + __ ldc1(double_base, FieldMemOperand(base, HeapNumber::kValueOffset)); + __ jmp(&unpack_exponent); - __ bind(&convert_exponent); - __ JumpIfNotSmi(exponent, &exponent_not_smi); - __ SmiUntag(exponent); - - // The base is in a double register and the exponent is - // an untagged smi. Allocate a heap number and call a - // C function for integer exponents. The register containing - // the heap number is callee-saved. - __ AllocateHeapNumber(heapnumber, - scratch, - scratch2, - heapnumbermap, - &call_runtime); - __ push(ra); - __ PrepareCallCFunction(3, scratch); - __ SetCallCDoubleArguments(double_base, exponent); - __ CallCFunction( - ExternalReference::power_double_int_function(masm->isolate()), 3); - __ pop(ra); - __ GetCFunctionDoubleResult(double_result); - __ sdc1(double_result, - FieldMemOperand(heapnumber, HeapNumber::kValueOffset)); - __ mov(v0, heapnumber); - __ DropAndRet(2 * kPointerSize); + __ bind(&base_is_smi); + __ mtc1(scratch, single_scratch); + __ cvt_d_w(double_base, single_scratch); + __ bind(&unpack_exponent); + + __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent); - __ bind(&exponent_not_smi); __ lw(scratch, FieldMemOperand(exponent, JSObject::kMapOffset)); __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap)); - // Exponent is a heapnumber. Load it into double register. __ ldc1(double_exponent, FieldMemOperand(exponent, HeapNumber::kValueOffset)); + } else if (exponent_type_ == TAGGED) { + // Base is already in double_base. + __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent); + + __ ldc1(double_exponent, + FieldMemOperand(exponent, HeapNumber::kValueOffset)); + } + + if (exponent_type_ != INTEGER) { + Label int_exponent_convert; + // Detect integer exponents stored as double. + __ EmitFPUTruncate(kRoundToMinusInf, + single_scratch, + double_exponent, + scratch, + scratch2, + kCheckForInexactConversion); + // scratch2 == 0 means there was no conversion error. + __ Branch(&int_exponent_convert, eq, scratch2, Operand(zero_reg)); + + if (exponent_type_ == ON_STACK) { + // Detect square root case. Crankshaft detects constant +/-0.5 at + // compile time and uses DoMathPowHalf instead. We then skip this check + // for non-constant cases of +/-0.5 as these hardly occur. + Label not_plus_half; + + // Test for 0.5. + __ Move(double_scratch, 0.5); + __ BranchF(USE_DELAY_SLOT, + ¬_plus_half, + NULL, + ne, + double_exponent, + double_scratch); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13). + __ Move(double_scratch, -V8_INFINITY); + __ BranchF(USE_DELAY_SLOT, &done, NULL, eq, double_base, double_scratch); + __ neg_d(double_result, double_scratch); + + // Add +0 to convert -0 to +0. + __ add_d(double_scratch, double_base, kDoubleRegZero); + __ sqrt_d(double_result, double_scratch); + __ jmp(&done); + + __ bind(¬_plus_half); + __ Move(double_scratch, -0.5); + __ BranchF(USE_DELAY_SLOT, + &call_runtime, + NULL, + ne, + double_exponent, + double_scratch); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13). + __ Move(double_scratch, -V8_INFINITY); + __ BranchF(USE_DELAY_SLOT, &done, NULL, eq, double_base, double_scratch); + __ Move(double_result, kDoubleRegZero); + + // Add +0 to convert -0 to +0. + __ add_d(double_scratch, double_base, kDoubleRegZero); + __ Move(double_result, 1); + __ sqrt_d(double_scratch, double_scratch); + __ div_d(double_result, double_result, double_scratch); + __ jmp(&done); + } - // The base and the exponent are in double registers. - // Allocate a heap number and call a C function for - // double exponents. The register containing - // the heap number is callee-saved. - __ AllocateHeapNumber(heapnumber, - scratch, - scratch2, - heapnumbermap, - &call_runtime); __ push(ra); - __ PrepareCallCFunction(4, scratch); - // ABI (o32) for func(double a, double b): a in f12, b in f14. - ASSERT(double_base.is(f12)); - ASSERT(double_exponent.is(f14)); - __ SetCallCDoubleArguments(double_base, double_exponent); - __ CallCFunction( - ExternalReference::power_double_double_function(masm->isolate()), 4); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(0, 2, scratch); + __ SetCallCDoubleArguments(double_base, double_exponent); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), + 0, 2); + } __ pop(ra); __ GetCFunctionDoubleResult(double_result); + __ jmp(&done); + + __ bind(&int_exponent_convert); + __ mfc1(scratch, single_scratch); + } + + // Calculate power with integer exponent. + __ bind(&int_exponent); + + // Get two copies of exponent in the registers scratch and exponent. + if (exponent_type_ == INTEGER) { + __ mov(scratch, exponent); + } else { + // Exponent has previously been stored into scratch as untagged integer. + __ mov(exponent, scratch); + } + + __ mov_d(double_scratch, double_base); // Back up base. + __ Move(double_result, 1.0); + + // Get absolute value of exponent. + Label positive_exponent; + __ Branch(&positive_exponent, ge, scratch, Operand(zero_reg)); + __ Subu(scratch, zero_reg, scratch); + __ bind(&positive_exponent); + + Label while_true, no_carry, loop_end; + __ bind(&while_true); + + __ And(scratch2, scratch, 1); + + __ Branch(&no_carry, eq, scratch2, Operand(zero_reg)); + __ mul_d(double_result, double_result, double_scratch); + __ bind(&no_carry); + + __ sra(scratch, scratch, 1); + + __ Branch(&loop_end, eq, scratch, Operand(zero_reg)); + __ mul_d(double_scratch, double_scratch, double_scratch); + + __ Branch(&while_true); + + __ bind(&loop_end); + + __ Branch(&done, ge, exponent, Operand(zero_reg)); + __ Move(double_scratch, 1.0); + __ div_d(double_result, double_scratch, double_result); + // Test whether result is zero. Bail out to check for subnormal result. + // Due to subnormals, x^-y == (1/x)^y does not hold in all cases. + __ BranchF(&done, NULL, ne, double_result, kDoubleRegZero); + + // double_exponent may not contain the exponent value if the input was a + // smi. We set it with exponent value before bailing out. + __ mtc1(exponent, single_scratch); + __ cvt_d_w(double_exponent, single_scratch); + + // Returning or bailing out. + Counters* counters = masm->isolate()->counters(); + if (exponent_type_ == ON_STACK) { + // The arguments are still on the stack. + __ bind(&call_runtime); + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + + // The stub is called from non-optimized code, which expects the result + // as heap number in exponent. + __ bind(&done); + __ AllocateHeapNumber( + heapnumber, scratch, scratch2, heapnumbermap, &call_runtime); __ sdc1(double_result, FieldMemOperand(heapnumber, HeapNumber::kValueOffset)); - __ mov(v0, heapnumber); - __ DropAndRet(2 * kPointerSize); - } + ASSERT(heapnumber.is(v0)); + __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2); + __ DropAndRet(2); + } else { + __ push(ra); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(0, 2, scratch); + __ SetCallCDoubleArguments(double_base, double_exponent); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), + 0, 2); + } + __ pop(ra); + __ GetCFunctionDoubleResult(double_result); - __ bind(&call_runtime); - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + __ bind(&done); + __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2); + __ Ret(); + } } @@ -3468,6 +3801,37 @@ bool CEntryStub::NeedsImmovableCode() { } +bool CEntryStub::IsPregenerated() { + return (!save_doubles_ || ISOLATE->fp_stubs_generated()) && + result_size_ == 1; +} + + +void CodeStub::GenerateStubsAheadOfTime() { + CEntryStub::GenerateAheadOfTime(); + WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(); + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(); + RecordWriteStub::GenerateFixedRegStubsAheadOfTime(); +} + + +void CodeStub::GenerateFPStubs() { + CEntryStub save_doubles(1, kSaveFPRegs); + Handle<Code> code = save_doubles.GetCode(); + code->set_is_pregenerated(true); + StoreBufferOverflowStub stub(kSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + code->GetIsolate()->set_fp_stubs_generated(true); +} + + +void CEntryStub::GenerateAheadOfTime() { + CEntryStub stub(1, kDontSaveFPRegs); + Handle<Code> code = stub.GetCode(); + code->set_is_pregenerated(true); +} + + void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { __ Throw(v0); } @@ -3490,16 +3854,17 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // s1: pointer to the first argument (C callee-saved) // s2: pointer to builtin function (C callee-saved) + Isolate* isolate = masm->isolate(); + if (do_gc) { // Move result passed in v0 into a0 to call PerformGC. __ mov(a0, v0); - __ PrepareCallCFunction(1, a1); - __ CallCFunction( - ExternalReference::perform_gc_function(masm->isolate()), 1); + __ PrepareCallCFunction(1, 0, a1); + __ CallCFunction(ExternalReference::perform_gc_function(isolate), 1, 0); } ExternalReference scope_depth = - ExternalReference::heap_always_allocate_scope_depth(masm->isolate()); + ExternalReference::heap_always_allocate_scope_depth(isolate); if (always_allocate) { __ li(a0, Operand(scope_depth)); __ lw(a1, MemOperand(a0)); @@ -3588,18 +3953,16 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, v0, Operand(reinterpret_cast<int32_t>(out_of_memory))); // Retrieve the pending exception and clear the variable. - __ li(t0, - Operand(ExternalReference::the_hole_value_location(masm->isolate()))); - __ lw(a3, MemOperand(t0)); + __ li(a3, Operand(isolate->factory()->the_hole_value())); __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - masm->isolate()))); + isolate))); __ lw(v0, MemOperand(t0)); __ sw(a3, MemOperand(t0)); // Special handling of termination exceptions which are uncatchable // by javascript code. __ Branch(throw_termination_exception, eq, - v0, Operand(masm->isolate()->factory()->termination_exception())); + v0, Operand(isolate->factory()->termination_exception())); // Handle normal exception. __ jmp(throw_normal_exception); @@ -3628,9 +3991,10 @@ void CEntryStub::Generate(MacroAssembler* masm) { __ Subu(s1, s1, Operand(kPointerSize)); // Enter the exit frame that transitions from JavaScript to C++. + FrameScope scope(masm, StackFrame::MANUAL); __ EnterExitFrame(save_doubles_); - // Setup argc and the builtin function in callee-saved registers. + // Set up argc and the builtin function in callee-saved registers. __ mov(s0, a0); __ mov(s2, a1); @@ -3680,12 +4044,13 @@ void CEntryStub::Generate(MacroAssembler* masm) { void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { - Label invoke, exit; + Label invoke, handler_entry, exit; + Isolate* isolate = masm->isolate(); // Registers: // a0: entry address // a1: function - // a2: reveiver + // a2: receiver // a3: argc // // Stack: @@ -3699,8 +4064,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { CpuFeatures::Scope scope(FPU); // Save callee-saved FPU registers. __ MultiPushFPU(kCalleeSavedFPU); + // Set up the reserved register for 0.0. + __ Move(kDoubleRegZero, 0.0); } + // Load argv in s0 register. int offset_to_argv = (kNumCalleeSaved + 1) * kPointerSize; if (CpuFeatures::IsSupported(FPU)) { @@ -3715,16 +4083,16 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ li(t2, Operand(Smi::FromInt(marker))); __ li(t1, Operand(Smi::FromInt(marker))); __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress, - masm->isolate()))); + isolate))); __ lw(t0, MemOperand(t0)); __ Push(t3, t2, t1, t0); - // Setup frame pointer for the frame to be pushed. + // Set up frame pointer for the frame to be pushed. __ addiu(fp, sp, -EntryFrameConstants::kCallerFPOffset); // Registers: // a0: entry_address // a1: function - // a2: reveiver_pointer + // a2: receiver_pointer // a3: argc // s0: argv // @@ -3739,8 +4107,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // If this is the outermost JS call, set js_entry_sp value. Label non_outermost_js; - ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, - masm->isolate()); + ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate); __ li(t1, Operand(ExternalReference(js_entry_sp))); __ lw(t2, MemOperand(t1)); __ Branch(&non_outermost_js, ne, t2, Operand(zero_reg)); @@ -3754,35 +4121,35 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ bind(&cont); __ push(t0); - // Call a faked try-block that does the invoke. - __ bal(&invoke); // bal exposes branch delay slot. - __ nop(); // Branch delay slot nop. - - // Caught exception: Store result (exception) in the pending - // exception field in the JSEnv and return a failure sentinel. - // Coming in here the fp will be invalid because the PushTryHandler below - // sets it to 0 to signal the existence of the JSEntry frame. + // Jump to a faked try block that does the invoke, with a faked catch + // block that sets the pending exception. + __ jmp(&invoke); + __ bind(&handler_entry); + handler_offset_ = handler_entry.pos(); + // Caught exception: Store result (exception) in the pending exception + // field in the JSEnv and return a failure sentinel. Coming in here the + // fp will be invalid because the PushTryHandler below sets it to 0 to + // signal the existence of the JSEntry frame. __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - masm->isolate()))); + isolate))); __ sw(v0, MemOperand(t0)); // We come back from 'invoke'. result is in v0. __ li(v0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); __ b(&exit); // b exposes branch delay slot. __ nop(); // Branch delay slot nop. - // Invoke: Link this frame into the handler chain. + // Invoke: Link this frame into the handler chain. There's only one + // handler block in this code object, so its index is 0. __ bind(&invoke); - __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); + __ PushTryHandler(StackHandler::JS_ENTRY, 0); // If an exception not caught by another handler occurs, this handler // returns control to the code after the bal(&invoke) above, which // restores all kCalleeSaved registers (including cp and fp) to their // saved values before returning a failure to C. // Clear any pending exceptions. - __ li(t0, - Operand(ExternalReference::the_hole_value_location(masm->isolate()))); - __ lw(t1, MemOperand(t0)); + __ li(t1, Operand(isolate->factory()->the_hole_value())); __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - masm->isolate()))); + isolate))); __ sw(t1, MemOperand(t0)); // Invoke the function by calling through JS entry trampoline builtin. @@ -3792,7 +4159,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Registers: // a0: entry_address // a1: function - // a2: reveiver_pointer + // a2: receiver_pointer // a3: argc // s0: argv // @@ -3805,7 +4172,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { if (is_construct) { ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline, - masm->isolate()); + isolate); __ li(t0, Operand(construct_entry)); } else { ExternalReference entry(Builtins::kJSEntryTrampoline, masm->isolate()); @@ -3833,7 +4200,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Restore the top frame descriptors from the stack. __ pop(t1); __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress, - masm->isolate()))); + isolate))); __ sw(t1, MemOperand(t0)); // Reset the stack to the callee saved registers. @@ -3857,11 +4224,10 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // * object: a0 or at sp + 1 * kPointerSize. // * function: a1 or at sp. // -// Inlined call site patching is a crankshaft-specific feature that is not -// implemented on MIPS. +// An inlined call site may have been generated before calling this stub. +// In this case the offset to the inline site to patch is passed on the stack, +// in the safepoint slot for register t0. void InstanceofStub::Generate(MacroAssembler* masm) { - // This is a crankshaft-specific feature that has not been implemented yet. - ASSERT(!HasCallSiteInlineCheck()); // Call site inlining and patching implies arguments in registers. ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck()); // ReturnTrueFalse is only implemented for inlined call sites. @@ -3875,6 +4241,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { const Register inline_site = t5; const Register scratch = a2; + const int32_t kDeltaToLoadBoolResult = 5 * kPointerSize; + Label slow, loop, is_instance, is_not_instance, not_js_object; if (!HasArgsInRegisters()) { @@ -3890,10 +4258,10 @@ void InstanceofStub::Generate(MacroAssembler* masm) { // real lookup and update the call site cache. if (!HasCallSiteInlineCheck()) { Label miss; - __ LoadRoot(t1, Heap::kInstanceofCacheFunctionRootIndex); - __ Branch(&miss, ne, function, Operand(t1)); - __ LoadRoot(t1, Heap::kInstanceofCacheMapRootIndex); - __ Branch(&miss, ne, map, Operand(t1)); + __ LoadRoot(at, Heap::kInstanceofCacheFunctionRootIndex); + __ Branch(&miss, ne, function, Operand(at)); + __ LoadRoot(at, Heap::kInstanceofCacheMapRootIndex); + __ Branch(&miss, ne, map, Operand(at)); __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); __ DropAndRet(HasArgsInRegisters() ? 0 : 2); @@ -3901,7 +4269,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } // Get the prototype of the function. - __ TryGetFunctionPrototype(function, prototype, scratch, &slow); + __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(prototype, &slow); @@ -3913,7 +4281,16 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex); __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex); } else { - UNIMPLEMENTED_MIPS(); + ASSERT(HasArgsInRegisters()); + // Patch the (relocated) inlined map check. + + // The offset was stored in t0 safepoint slot. + // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal). + __ LoadFromSafepointRegisterSlot(scratch, t0); + __ Subu(inline_site, ra, scratch); + // Get the map location in scratch and patch it. + __ GetRelocatedValue(inline_site, scratch, v1); // v1 used as scratch. + __ sw(map, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); } // Register mapping: a3 is object map and t0 is function prototype. @@ -3939,7 +4316,16 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ mov(v0, zero_reg); __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); } else { - UNIMPLEMENTED_MIPS(); + // Patch the call site to return true. + __ LoadRoot(v0, Heap::kTrueValueRootIndex); + __ Addu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult)); + // Get the boolean result location in scratch and patch it. + __ PatchRelocatedValue(inline_site, scratch, v0); + + if (!ReturnTrueFalseObject()) { + ASSERT_EQ(Smi::FromInt(0), 0); + __ mov(v0, zero_reg); + } } __ DropAndRet(HasArgsInRegisters() ? 0 : 2); @@ -3948,8 +4334,17 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ li(v0, Operand(Smi::FromInt(1))); __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex); } else { - UNIMPLEMENTED_MIPS(); + // Patch the call site to return false. + __ LoadRoot(v0, Heap::kFalseValueRootIndex); + __ Addu(inline_site, inline_site, Operand(kDeltaToLoadBoolResult)); + // Get the boolean result location in scratch and patch it. + __ PatchRelocatedValue(inline_site, scratch, v0); + + if (!ReturnTrueFalseObject()) { + __ li(v0, Operand(Smi::FromInt(1))); + } } + __ DropAndRet(HasArgsInRegisters() ? 0 : 2); Label object_not_null, object_not_null_or_smi; @@ -3986,10 +4381,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); } else { - __ EnterInternalFrame(); - __ Push(a0, a1); - __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a0, a1); + __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); + } __ mov(a0, v0); __ LoadRoot(v0, Heap::kTrueValueRootIndex); __ DropAndRet(HasArgsInRegisters() ? 0 : 2, eq, a0, Operand(zero_reg)); @@ -4178,7 +4574,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ sw(a3, FieldMemOperand(v0, i)); } - // Setup the callee in-object property. + // Set up the callee in-object property. STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); __ lw(a3, MemOperand(sp, 2 * kPointerSize)); const int kCalleeOffset = JSObject::kHeaderSize + @@ -4191,7 +4587,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Heap::kArgumentsLengthIndex * kPointerSize; __ sw(a2, FieldMemOperand(v0, kLengthOffset)); - // Setup the elements pointer in the allocated arguments object. + // Set up the elements pointer in the allocated arguments object. // If we allocated a parameter map, t0 will point there, otherwise // it will point to the backing store. __ Addu(t0, v0, Operand(Heap::kArgumentsObjectSize)); @@ -4293,7 +4689,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ Ret(); // Do the runtime call to allocate the arguments object. - // a2 = argument count (taggged) + // a2 = argument count (tagged) __ bind(&runtime); __ sw(a2, MemOperand(sp, 0 * kPointerSize)); // Patch argument count. __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1); @@ -4368,7 +4764,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Get the parameters pointer from the stack. __ lw(a2, MemOperand(sp, 1 * kPointerSize)); - // Setup the elements pointer in the allocated arguments object and + // Set up the elements pointer in the allocated arguments object and // initialize the header in the elements fixed array. __ Addu(t0, v0, Operand(Heap::kArgumentsObjectSizeStrict)); __ sw(t0, FieldMemOperand(v0, JSObject::kElementsOffset)); @@ -4380,7 +4776,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Copy the fixed array slots. Label loop; - // Setup t0 to point to the first array slot. + // Set up t0 to point to the first array slot. __ Addu(t0, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ bind(&loop); // Pre-decrement a2 with kPointerSize on each iteration. @@ -4411,10 +4807,6 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { #ifdef V8_INTERPRETED_REGEXP __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); #else // V8_INTERPRETED_REGEXP - if (!FLAG_regexp_entry_native) { - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); - return; - } // Stack frame on entry. // sp[0]: last_match_info (expected JSArray) @@ -4427,6 +4819,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { static const int kSubjectOffset = 2 * kPointerSize; static const int kJSRegExpOffset = 3 * kPointerSize; + Isolate* isolate = masm->isolate(); + Label runtime, invoke_regexp; // Allocation of registers for this function. These are in callee save @@ -4442,9 +4836,9 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Ensure that a RegExp stack is allocated. ExternalReference address_of_regexp_stack_memory_address = ExternalReference::address_of_regexp_stack_memory_address( - masm->isolate()); + isolate); ExternalReference address_of_regexp_stack_memory_size = - ExternalReference::address_of_regexp_stack_memory_size(masm->isolate()); + ExternalReference::address_of_regexp_stack_memory_size(isolate); __ li(a0, Operand(address_of_regexp_stack_memory_size)); __ lw(a0, MemOperand(a0, 0)); __ Branch(&runtime, eq, a0, Operand(zero_reg)); @@ -4508,8 +4902,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Check that the third argument is a positive smi less than the subject // string length. A negative value will be greater (unsigned comparison). __ lw(a0, MemOperand(sp, kPreviousIndexOffset)); - __ And(at, a0, Operand(kSmiTagMask)); - __ Branch(&runtime, ne, at, Operand(zero_reg)); + __ JumpIfNotSmi(a0, &runtime); __ Branch(&runtime, ls, a3, Operand(a0)); // a2: Number of capture registers @@ -4525,7 +4918,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { FieldMemOperand(a0, JSArray::kElementsOffset)); __ lw(a0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset)); __ Branch(&runtime, ne, a0, Operand( - masm->isolate()->factory()->fixed_array_map())); + isolate->factory()->fixed_array_map())); // Check that the last match info has space for the capture registers and the // additional information. __ lw(a0, @@ -4542,25 +4935,38 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { Label seq_string; __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset)); __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset)); - // First check for flat string. - __ And(a1, a0, Operand(kIsNotStringMask | kStringRepresentationMask)); + // First check for flat string. None of the following string type tests will + // succeed if subject is not a string or a short external string. + __ And(a1, + a0, + Operand(kIsNotStringMask | + kStringRepresentationMask | + kShortExternalStringMask)); STATIC_ASSERT((kStringTag | kSeqStringTag) == 0); __ Branch(&seq_string, eq, a1, Operand(zero_reg)); // subject: Subject string // a0: instance type if Subject string // regexp_data: RegExp data (FixedArray) + // a1: whether subject is a string and if yes, its string representation // Check for flat cons string or sliced string. // A flat cons string is a cons string where the second part is the empty // string. In that case the subject string is just the first part of the cons // string. Also in this case the first part of the cons string is known to be // a sequential string or an external string. // In the case of a sliced string its offset has to be taken into account. - Label cons_string, check_encoding; + Label cons_string, external_string, check_encoding; STATIC_ASSERT(kConsStringTag < kExternalStringTag); STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); + STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); + STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); __ Branch(&cons_string, lt, a1, Operand(kExternalStringTag)); - __ Branch(&runtime, eq, a1, Operand(kExternalStringTag)); + __ Branch(&external_string, eq, a1, Operand(kExternalStringTag)); + + // Catch non-string subject or short external string. + STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0); + __ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask)); + __ Branch(&runtime, ne, at, Operand(zero_reg)); // String is sliced. __ lw(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset)); @@ -4580,7 +4986,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset)); STATIC_ASSERT(kSeqStringTag == 0); __ And(at, a0, Operand(kStringRepresentationMask)); - __ Branch(&runtime, ne, at, Operand(zero_reg)); + __ Branch(&external_string, ne, at, Operand(zero_reg)); __ bind(&seq_string); // subject: Subject string @@ -4590,9 +4996,9 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { STATIC_ASSERT(kAsciiStringTag == 4); STATIC_ASSERT(kTwoByteStringTag == 0); // Find the code object based on the assumptions above. - __ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for ascii. + __ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for ASCII. __ lw(t9, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset)); - __ sra(a3, a0, 2); // a3 is 1 for ascii, 0 for UC16 (usyed below). + __ sra(a3, a0, 2); // a3 is 1 for ASCII, 0 for UC16 (used below). __ lw(t1, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset)); __ movz(t9, t1, a0); // If UC16 (a0 is 0), replace t9 w/kDataUC16CodeOffset. @@ -4616,7 +5022,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // subject: Subject string // regexp_data: RegExp data (FixedArray) // All checks done. Now push arguments for native regexp code. - __ IncrementCounter(masm->isolate()->counters()->regexp_entry_native(), + __ IncrementCounter(isolate->counters()->regexp_entry_native(), 1, a0, a2); // Isolates: note we add an additional parameter here (isolate pointer). @@ -4656,13 +5062,12 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Argument 5: static offsets vector buffer. __ li(a0, Operand( - ExternalReference::address_of_static_offsets_vector(masm->isolate()))); + ExternalReference::address_of_static_offsets_vector(isolate))); __ sw(a0, MemOperand(sp, 1 * kPointerSize)); // For arguments 4 and 3 get string length, calculate start of string data // and calculate the shift of the index (0 for ASCII and 1 for two byte). - STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); - __ Addu(t2, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ Addu(t2, subject, Operand(SeqString::kHeaderSize - kHeapObjectTag)); __ Xor(a3, a3, Operand(1)); // 1 for 2-byte str, 0 for 1-byte. // Load the length from the original subject string from the previous stack // frame. Therefore we have to use fp, which points exactly to two pointer @@ -4715,11 +5120,9 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // stack overflow (on the backtrack stack) was detected in RegExp code but // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. - __ li(a1, Operand( - ExternalReference::the_hole_value_location(masm->isolate()))); - __ lw(a1, MemOperand(a1, 0)); + __ li(a1, Operand(isolate->factory()->the_hole_value())); __ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - masm->isolate()))); + isolate))); __ lw(v0, MemOperand(a2, 0)); __ Branch(&runtime, eq, v0, Operand(a1)); @@ -4737,7 +5140,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ bind(&failure); // For failure and exception return null. - __ li(v0, Operand(masm->isolate()->factory()->null_value())); + __ li(v0, Operand(isolate->factory()->null_value())); __ Addu(sp, sp, Operand(4 * kPointerSize)); __ Ret(); @@ -4757,20 +5160,29 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ sw(a2, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastCaptureCountOffset)); // Store last subject and last input. - __ mov(a3, last_match_info_elements); // Moved up to reduce latency. __ sw(subject, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastSubjectOffset)); - __ RecordWrite(a3, Operand(RegExpImpl::kLastSubjectOffset), a2, t0); + __ mov(a2, subject); + __ RecordWriteField(last_match_info_elements, + RegExpImpl::kLastSubjectOffset, + a2, + t3, + kRAHasNotBeenSaved, + kDontSaveFPRegs); __ sw(subject, FieldMemOperand(last_match_info_elements, RegExpImpl::kLastInputOffset)); - __ mov(a3, last_match_info_elements); - __ RecordWrite(a3, Operand(RegExpImpl::kLastInputOffset), a2, t0); + __ RecordWriteField(last_match_info_elements, + RegExpImpl::kLastInputOffset, + subject, + t3, + kRAHasNotBeenSaved, + kDontSaveFPRegs); // Get the static offsets vector filled by the native regexp code. ExternalReference address_of_static_offsets_vector = - ExternalReference::address_of_static_offsets_vector(masm->isolate()); + ExternalReference::address_of_static_offsets_vector(isolate); __ li(a2, Operand(address_of_static_offsets_vector)); // a1: number of capture registers @@ -4800,6 +5212,29 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ Addu(sp, sp, Operand(4 * kPointerSize)); __ Ret(); + // External string. Short external strings have already been ruled out. + // a0: scratch + __ bind(&external_string); + __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset)); + __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset)); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ And(at, a0, Operand(kIsIndirectStringMask)); + __ Assert(eq, + "external string expected, but not found", + at, + Operand(zero_reg)); + } + __ lw(subject, + FieldMemOperand(subject, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ Subu(subject, + subject, + SeqTwoByteString::kHeaderSize - kHeapObjectTag); + __ jmp(&seq_string); + // Do the runtime call to execute the regexp. __ bind(&runtime); __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); @@ -4852,11 +5287,11 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // Set input, index and length fields from arguments. __ lw(a1, MemOperand(sp, kPointerSize * 0)); + __ lw(a2, MemOperand(sp, kPointerSize * 1)); + __ lw(t2, MemOperand(sp, kPointerSize * 2)); __ sw(a1, FieldMemOperand(v0, JSRegExpResult::kInputOffset)); - __ lw(a1, MemOperand(sp, kPointerSize * 1)); - __ sw(a1, FieldMemOperand(v0, JSRegExpResult::kIndexOffset)); - __ lw(a1, MemOperand(sp, kPointerSize * 2)); - __ sw(a1, FieldMemOperand(v0, JSArray::kLengthOffset)); + __ sw(a2, FieldMemOperand(v0, JSRegExpResult::kIndexOffset)); + __ sw(t2, FieldMemOperand(v0, JSArray::kLengthOffset)); // Fill out the elements FixedArray. // v0: JSArray, tagged. @@ -4895,8 +5330,50 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { } +static void GenerateRecordCallTarget(MacroAssembler* masm) { + // Cache the called function in a global property cell. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // a1 : the function to call + // a2 : cache cell for call target + Label done; + + ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()), + masm->isolate()->heap()->undefined_value()); + ASSERT_EQ(*TypeFeedbackCells::UninitializedSentinel(masm->isolate()), + masm->isolate()->heap()->the_hole_value()); + + // Load the cache state into a3. + __ lw(a3, FieldMemOperand(a2, JSGlobalPropertyCell::kValueOffset)); + + // A monomorphic cache hit or an already megamorphic state: invoke the + // function without changing the state. + __ Branch(&done, eq, a3, Operand(a1)); + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + __ Branch(&done, eq, a3, Operand(at)); + + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ Branch(&done, eq, a3, Operand(at)); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + __ sw(at, FieldMemOperand(a2, JSGlobalPropertyCell::kValueOffset)); + __ Branch(&done); + + // An uninitialized cache is patched with the function. + __ sw(a1, FieldMemOperand(a2, JSGlobalPropertyCell::kValueOffset)); + // No need for a write barrier here - cells are rescanned. + + __ bind(&done); +} + + void CallFunctionStub::Generate(MacroAssembler* masm) { - Label slow; + // a1 : the function to call + // a2 : cache cell for call target + Label slow, non_function; // The receiver might implicitly be the global object. This is // indicated by passing the hole as the receiver to the call @@ -4910,19 +5387,15 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ Branch(&call, ne, t0, Operand(at)); // Patch the receiver on the stack with the global receiver object. - __ lw(a1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset)); - __ sw(a1, MemOperand(sp, argc_ * kPointerSize)); + __ lw(a2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset)); + __ sw(a2, MemOperand(sp, argc_ * kPointerSize)); __ bind(&call); } - // Get the function to call from the stack. - // function, receiver [, arguments] - __ lw(a1, MemOperand(sp, (argc_ + 1) * kPointerSize)); - // Check that the function is really a JavaScript function. // a1: pushed function (to be verified) - __ JumpIfSmi(a1, &slow); + __ JumpIfSmi(a1, &non_function); // Get the map of the function object. __ GetObjectType(a1, a2, a2); __ Branch(&slow, ne, a2, Operand(JS_FUNCTION_TYPE)); @@ -4950,10 +5423,24 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Slow-case: Non-function called. __ bind(&slow); + // Check for function proxy. + __ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_PROXY_TYPE)); + __ push(a1); // Put proxy as additional argument. + __ li(a0, Operand(argc_ + 1, RelocInfo::NONE)); + __ li(a2, Operand(0, RelocInfo::NONE)); + __ GetBuiltinEntry(a3, Builtins::CALL_FUNCTION_PROXY); + __ SetCallKind(t1, CALL_AS_METHOD); + { + Handle<Code> adaptor = + masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); + __ Jump(adaptor, RelocInfo::CODE_TARGET); + } + // CALL_NON_FUNCTION expects the non-function callee as receiver (instead // of the original receiver from the call site). + __ bind(&non_function); __ sw(a1, MemOperand(sp, argc_ * kPointerSize)); - __ li(a0, Operand(argc_)); // Setup the number of arguments. + __ li(a0, Operand(argc_)); // Set up the number of arguments. __ mov(a2, zero_reg); __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION); __ SetCallKind(t1, CALL_AS_METHOD); @@ -4962,6 +5449,48 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { } +void CallConstructStub::Generate(MacroAssembler* masm) { + // a0 : number of arguments + // a1 : the function to call + // a2 : cache cell for call target + Label slow, non_function_call; + + // Check that the function is not a smi. + __ JumpIfSmi(a1, &non_function_call); + // Check that the function is a JSFunction. + __ GetObjectType(a1, a3, a3); + __ Branch(&slow, ne, a3, Operand(JS_FUNCTION_TYPE)); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + } + + // Jump to the function-specific construct stub. + __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ lw(a2, FieldMemOperand(a2, SharedFunctionInfo::kConstructStubOffset)); + __ Addu(at, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(at); + + // a0: number of arguments + // a1: called object + // a3: object type + Label do_call; + __ bind(&slow); + __ Branch(&non_function_call, ne, a3, Operand(JS_FUNCTION_PROXY_TYPE)); + __ GetBuiltinEntry(a3, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); + __ jmp(&do_call); + + __ bind(&non_function_call); + __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); + __ bind(&do_call); + // Set expected number of arguments to zero (not changing r0). + __ li(a2, Operand(0, RelocInfo::NONE)); + __ SetCallKind(t1, CALL_AS_METHOD); + __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); +} + + // Unfortunately you have to run without snapshots to see most of these // names in the profile since most compare stubs end up in the snapshot. void CompareStub::PrintName(StringStream* stream) { @@ -5008,7 +5537,6 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { Label got_char_code; Label sliced_string; - ASSERT(!t0.is(scratch_)); ASSERT(!t0.is(index_)); ASSERT(!t0.is(result_)); ASSERT(!t0.is(object_)); @@ -5026,102 +5554,41 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // If the index is non-smi trigger the non-smi case. __ JumpIfNotSmi(index_, &index_not_smi_); - // Put smi-tagged index into scratch register. - __ mov(scratch_, index_); __ bind(&got_smi_index_); // Check for index out of range. __ lw(t0, FieldMemOperand(object_, String::kLengthOffset)); - __ Branch(index_out_of_range_, ls, t0, Operand(scratch_)); - - // We need special handling for non-flat strings. - STATIC_ASSERT(kSeqStringTag == 0); - __ And(t0, result_, Operand(kStringRepresentationMask)); - __ Branch(&flat_string, eq, t0, Operand(zero_reg)); + __ Branch(index_out_of_range_, ls, t0, Operand(index_)); - // Handle non-flat strings. - __ And(result_, result_, Operand(kStringRepresentationMask)); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - __ Branch(&sliced_string, gt, result_, Operand(kExternalStringTag)); - __ Branch(&call_runtime_, eq, result_, Operand(kExternalStringTag)); - - // ConsString. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - Label assure_seq_string; - __ lw(result_, FieldMemOperand(object_, ConsString::kSecondOffset)); - __ LoadRoot(t0, Heap::kEmptyStringRootIndex); - __ Branch(&call_runtime_, ne, result_, Operand(t0)); - - // Get the first of the two strings and load its instance type. - __ lw(object_, FieldMemOperand(object_, ConsString::kFirstOffset)); - __ jmp(&assure_seq_string); - - // SlicedString, unpack and add offset. - __ bind(&sliced_string); - __ lw(result_, FieldMemOperand(object_, SlicedString::kOffsetOffset)); - __ addu(scratch_, scratch_, result_); - __ lw(object_, FieldMemOperand(object_, SlicedString::kParentOffset)); - - // Assure that we are dealing with a sequential string. Go to runtime if not. - __ bind(&assure_seq_string); - __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); - __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); - // Check that parent is not an external string. Go to runtime otherwise. - STATIC_ASSERT(kSeqStringTag == 0); + __ sra(index_, index_, kSmiTagSize); - __ And(t0, result_, Operand(kStringRepresentationMask)); - __ Branch(&call_runtime_, ne, t0, Operand(zero_reg)); + StringCharLoadGenerator::Generate(masm, + object_, + index_, + result_, + &call_runtime_); - // Check for 1-byte or 2-byte string. - __ bind(&flat_string); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ And(t0, result_, Operand(kStringEncodingMask)); - __ Branch(&ascii_string, ne, t0, Operand(zero_reg)); - - // 2-byte string. - // Load the 2-byte character code into the result register. We can - // add without shifting since the smi tag size is the log2 of the - // number of bytes in a two-byte character. - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0); - __ Addu(scratch_, object_, Operand(scratch_)); - __ lhu(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize)); - __ Branch(&got_char_code); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - - __ srl(t0, scratch_, kSmiTagSize); - __ Addu(scratch_, object_, t0); - - __ lbu(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize)); - - __ bind(&got_char_code); __ sll(result_, result_, kSmiTagSize); __ bind(&exit_); } void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharCodeAt slow case"); // Index is not a smi. __ bind(&index_not_smi_); // If index is a heap number, try converting it to an integer. __ CheckMap(index_, - scratch_, + result_, Heap::kHeapNumberMapRootIndex, index_not_number_, DONT_DO_SMI_CHECK); call_helper.BeforeCall(masm); // Consumed by runtime conversion function: - __ Push(object_, index_, index_); + __ Push(object_, index_); if (index_flags_ == STRING_INDEX_IS_NUMBER) { __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); } else { @@ -5133,16 +5600,14 @@ void StringCharCodeAtGenerator::GenerateSlow( // Save the conversion result before the pop instructions below // have a chance to overwrite it. - __ Move(scratch_, v0); - - __ pop(index_); + __ Move(index_, v0); __ pop(object_); // Reload the instance type. __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); call_helper.AfterCall(masm); // If index is still not a smi, it must be out of range. - __ JumpIfNotSmi(scratch_, index_out_of_range_); + __ JumpIfNotSmi(index_, index_out_of_range_); // Otherwise, return to the fast path. __ Branch(&got_smi_index_); @@ -5151,6 +5616,7 @@ void StringCharCodeAtGenerator::GenerateSlow( // is too complex (e.g., when the string needs to be flattened). __ bind(&call_runtime_); call_helper.BeforeCall(masm); + __ sll(index_, index_, kSmiTagSize); __ Push(object_, index_); __ CallRuntime(Runtime::kStringCharCodeAt, 2); @@ -5194,7 +5660,8 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { void StringCharFromCodeGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharFromCode slow case"); __ bind(&slow_case_); @@ -5220,76 +5687,13 @@ void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) { void StringCharAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { char_code_at_generator_.GenerateSlow(masm, call_helper); char_from_code_generator_.GenerateSlow(masm, call_helper); } -class StringHelper : public AllStatic { - public: - // Generate code for copying characters using a simple loop. This should only - // be used in places where the number of characters is small and the - // additional setup and checking in GenerateCopyCharactersLong adds too much - // overhead. Copying of overlapping regions is not supported. - // Dest register ends at the position after the last character written. - static void GenerateCopyCharacters(MacroAssembler* masm, - Register dest, - Register src, - Register count, - Register scratch, - bool ascii); - - // Generate code for copying a large number of characters. This function - // is allowed to spend extra time setting up conditions to make copying - // faster. Copying of overlapping regions is not supported. - // Dest register ends at the position after the last character written. - static void GenerateCopyCharactersLong(MacroAssembler* masm, - Register dest, - Register src, - Register count, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4, - Register scratch5, - int flags); - - - // Probe the symbol table for a two character string. If the string is - // not found by probing a jump to the label not_found is performed. This jump - // does not guarantee that the string is not in the symbol table. If the - // string is found the code falls through with the string in register r0. - // Contents of both c1 and c2 registers are modified. At the exit c1 is - // guaranteed to contain halfword with low and high bytes equal to - // initial contents of c1 and c2 respectively. - static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, - Register c1, - Register c2, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4, - Register scratch5, - Label* not_found); - - // Generate string hash. - static void GenerateHashInit(MacroAssembler* masm, - Register hash, - Register character); - - static void GenerateHashAddCharacter(MacroAssembler* masm, - Register hash, - Register character); - - static void GenerateHashGetHash(MacroAssembler* masm, - Register hash); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); -}; - - void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, Register dest, Register src, @@ -5540,10 +5944,10 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, __ Branch(&is_string, ne, scratch, Operand(ODDBALL_TYPE)); __ Branch(not_found, eq, undefined, Operand(candidate)); - // Must be null (deleted entry). + // Must be the hole (deleted entry). if (FLAG_debug_code) { - __ LoadRoot(scratch, Heap::kNullValueRootIndex); - __ Assert(eq, "oddball in symbol table is not undefined or null", + __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex); + __ Assert(eq, "oddball in symbol table is not undefined or the hole", scratch, Operand(candidate)); } __ jmp(&next_probe[i]); @@ -5587,7 +5991,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, __ sll(at, hash, 10); __ addu(hash, hash, at); // hash ^= hash >> 6; - __ sra(at, hash, 6); + __ srl(at, hash, 6); __ xor_(hash, hash, at); } @@ -5601,7 +6005,7 @@ void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm, __ sll(at, hash, 10); __ addu(hash, hash, at); // hash ^= hash >> 6; - __ sra(at, hash, 6); + __ srl(at, hash, 6); __ xor_(hash, hash, at); } @@ -5612,20 +6016,23 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, __ sll(at, hash, 3); __ addu(hash, hash, at); // hash ^= hash >> 11; - __ sra(at, hash, 11); + __ srl(at, hash, 11); __ xor_(hash, hash, at); // hash += hash << 15; __ sll(at, hash, 15); __ addu(hash, hash, at); + __ li(at, Operand(String::kHashBitMask)); + __ and_(hash, hash, at); + // if (hash == 0) hash = 27; - __ ori(at, zero_reg, 27); + __ ori(at, zero_reg, StringHasher::kZeroHash); __ movz(hash, at, hash); } void SubStringStub::Generate(MacroAssembler* masm) { - Label sub_string_runtime; + Label runtime; // Stack frame on entry. // ra: return address // sp[0]: to @@ -5643,53 +6050,33 @@ void SubStringStub::Generate(MacroAssembler* masm) { static const int kFromOffset = 1 * kPointerSize; static const int kStringOffset = 2 * kPointerSize; - Register to = t2; - Register from = t3; - - // Check bounds and smi-ness. - __ lw(to, MemOperand(sp, kToOffset)); - __ lw(from, MemOperand(sp, kFromOffset)); + __ lw(a2, MemOperand(sp, kToOffset)); + __ lw(a3, MemOperand(sp, kFromOffset)); STATIC_ASSERT(kFromOffset == kToOffset + 4); STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1); - __ JumpIfNotSmi(from, &sub_string_runtime); - __ JumpIfNotSmi(to, &sub_string_runtime); + // Utilize delay slots. SmiUntag doesn't emit a jump, everything else is + // safe in this case. + __ UntagAndJumpIfSmi(a2, a2, &runtime); + __ UntagAndJumpIfSmi(a3, a3, &runtime); - __ sra(a3, from, kSmiTagSize); // Remove smi tag. - __ sra(t5, to, kSmiTagSize); // Remove smi tag. + // Both a2 and a3 are untagged integers. - // a3: from index (untagged smi) - // t5: to index (untagged smi) - - __ Branch(&sub_string_runtime, lt, a3, Operand(zero_reg)); // From < 0. + __ Branch(&runtime, lt, a3, Operand(zero_reg)); // From < 0. __ subu(a2, t5, a3); - __ Branch(&sub_string_runtime, gt, a3, Operand(t5)); // Fail if from > to. - - // Special handling of sub-strings of length 1 and 2. One character strings - // are handled in the runtime system (looked up in the single character - // cache). Two character strings are looked for in the symbol cache in - // generated code. - __ Branch(&sub_string_runtime, lt, a2, Operand(2)); - - // Both to and from are smis. - - // a2: result string length - // a3: from index (untagged smi) - // t2: (a.k.a. to): to (smi) - // t3: (a.k.a. from): from offset (smi) - // t5: to index (untagged smi) + __ Branch(&runtime, gt, a3, Operand(t5)); // Fail if from > to. - // Make sure first argument is a sequential (or flat) string. + // Make sure first argument is a string. __ lw(v0, MemOperand(sp, kStringOffset)); - __ Branch(&sub_string_runtime, eq, v0, Operand(kSmiTagMask)); + __ Branch(&runtime, eq, v0, Operand(kSmiTagMask)); __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset)); __ And(t4, v0, Operand(kIsNotStringMask)); - __ Branch(&sub_string_runtime, ne, t4, Operand(zero_reg)); + __ Branch(&runtime, ne, t4, Operand(zero_reg)); // Short-cut for the case of trivial substring. Label return_v0; @@ -5699,74 +6086,16 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ sra(t0, t0, 1); __ Branch(&return_v0, eq, a2, Operand(t0)); - Label create_slice; - if (FLAG_string_slices) { - __ Branch(&create_slice, ge, a2, Operand(SlicedString::kMinLength)); - } - - // v0: original string - // a1: instance type - // a2: result string length - // a3: from index (untagged smi) - // t2: (a.k.a. to): to (smi) - // t3: (a.k.a. from): from offset (smi) - // t5: to index (untagged smi) - - Label seq_string; - __ And(t0, a1, Operand(kStringRepresentationMask)); - STATIC_ASSERT(kSeqStringTag < kConsStringTag); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kConsStringTag < kSlicedStringTag); - - // Slices and external strings go to runtime. - __ Branch(&sub_string_runtime, gt, t0, Operand(kConsStringTag)); - - // Sequential strings are handled directly. - __ Branch(&seq_string, lt, t0, Operand(kConsStringTag)); - - // Cons string. Try to recurse (once) on the first substring. - // (This adds a little more generality than necessary to handle flattened - // cons strings, but not much). - __ lw(v0, FieldMemOperand(v0, ConsString::kFirstOffset)); - __ lw(t0, FieldMemOperand(v0, HeapObject::kMapOffset)); - __ lbu(a1, FieldMemOperand(t0, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kSeqStringTag == 0); - // Cons, slices and external strings go to runtime. - __ Branch(&sub_string_runtime, ne, a1, Operand(kStringRepresentationMask)); - - // Definitly a sequential string. - __ bind(&seq_string); - - // v0: original string - // a1: instance type - // a2: result string length - // a3: from index (untagged smi) - // t2: (a.k.a. to): to (smi) - // t3: (a.k.a. from): from offset (smi) - // t5: to index (untagged smi) - - __ lw(t0, FieldMemOperand(v0, String::kLengthOffset)); - __ Branch(&sub_string_runtime, lt, t0, Operand(to)); // Fail if to > length. - to = no_reg; - - // v0: original string or left hand side of the original cons string. - // a1: instance type - // a2: result string length - // a3: from index (untagged smi) - // t3: (a.k.a. from): from offset (smi) - // t5: to index (untagged smi) - - // Check for flat ASCII string. - Label non_ascii_flat; - STATIC_ASSERT(kTwoByteStringTag == 0); - - __ And(t4, a1, Operand(kStringEncodingMask)); - __ Branch(&non_ascii_flat, eq, t4, Operand(zero_reg)); Label result_longer_than_two; - __ Branch(&result_longer_than_two, gt, a2, Operand(2)); + // Check for special case of two character ASCII string, in which case + // we do a lookup in the symbol table first. + __ li(t0, 2); + __ Branch(&result_longer_than_two, gt, a2, Operand(t0)); + __ Branch(&runtime, lt, a2, Operand(t0)); + + __ JumpIfInstanceTypeIsNotSequentialAscii(a1, a1, &runtime); - // Sub string of length 2 requested. // Get the two characters forming the sub string. __ Addu(v0, v0, Operand(a3)); __ lbu(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize)); @@ -5776,31 +6105,126 @@ void SubStringStub::Generate(MacroAssembler* masm) { Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( masm, a3, t0, a1, t1, t2, t3, t4, &make_two_character_string); - Counters* counters = masm->isolate()->counters(); __ jmp(&return_v0); // a2: result string length. // a3: two characters combined into halfword in little endian byte order. __ bind(&make_two_character_string); - __ AllocateAsciiString(v0, a2, t0, t1, t4, &sub_string_runtime); + __ AllocateAsciiString(v0, a2, t0, t1, t4, &runtime); __ sh(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize)); __ jmp(&return_v0); __ bind(&result_longer_than_two); - // Locate 'from' character of string. - __ Addu(t1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ sra(t4, from, 1); - __ Addu(t1, t1, t4); + // Deal with different string types: update the index if necessary + // and put the underlying string into t1. + // v0: original string + // a1: instance type + // a2: length + // a3: from index (untagged) + Label underlying_unpacked, sliced_string, seq_or_external_string; + // If the string is not indirect, it can only be sequential or external. + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ And(t0, a1, Operand(kIsIndirectStringMask)); + __ Branch(USE_DELAY_SLOT, &seq_or_external_string, eq, t0, Operand(zero_reg)); + + __ And(t0, a1, Operand(kSlicedNotConsMask)); + __ Branch(&sliced_string, ne, t0, Operand(zero_reg)); + // Cons string. Check whether it is flat, then fetch first part. + __ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset)); + __ LoadRoot(t0, Heap::kEmptyStringRootIndex); + __ Branch(&runtime, ne, t1, Operand(t0)); + __ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset)); + // Update instance type. + __ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset)); + __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked); - // Allocate the result. - __ AllocateAsciiString(v0, a2, t4, t0, a1, &sub_string_runtime); + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ lw(t0, FieldMemOperand(v0, SlicedString::kOffsetOffset)); + __ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); + __ sra(t0, t0, 1); // Add offset to index. + __ Addu(a3, a3, t0); + // Update instance type. + __ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset)); + __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked); + + __ bind(&seq_or_external_string); + // Sequential or external string. Just move string to the expected register. + __ mov(t1, v0); + + __ bind(&underlying_unpacked); + + if (FLAG_string_slices) { + Label copy_routine; + // t1: underlying subject string + // a1: instance type of underlying subject string + // a2: length + // a3: adjusted start index (untagged) + // Short slice. Copy instead of slicing. + __ Branch(©_routine, lt, a2, Operand(SlicedString::kMinLength)); + // Allocate new sliced string. At this point we do not reload the instance + // type including the string encoding because we simply rely on the info + // provided by the original string. It does not matter if the original + // string's encoding is wrong because we always have to recheck encoding of + // the newly created string's parent anyways due to externalized strings. + Label two_byte_slice, set_slice_header; + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ And(t0, a1, Operand(kStringEncodingMask)); + __ Branch(&two_byte_slice, eq, t0, Operand(zero_reg)); + __ AllocateAsciiSlicedString(v0, a2, t2, t3, &runtime); + __ jmp(&set_slice_header); + __ bind(&two_byte_slice); + __ AllocateTwoByteSlicedString(v0, a2, t2, t3, &runtime); + __ bind(&set_slice_header); + __ sll(a3, a3, 1); + __ sw(a3, FieldMemOperand(v0, SlicedString::kOffsetOffset)); + __ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); + __ jmp(&return_v0); + + __ bind(©_routine); + } + + // t1: underlying subject string + // a1: instance type of underlying subject string + // a2: length + // a3: adjusted start index (untagged) + Label two_byte_sequential, sequential_string, allocate_result; + STATIC_ASSERT(kExternalStringTag != 0); + STATIC_ASSERT(kSeqStringTag == 0); + __ And(t0, a1, Operand(kExternalStringTag)); + __ Branch(&sequential_string, eq, t0, Operand(zero_reg)); + + // Handle external string. + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ And(t0, a1, Operand(kShortExternalStringTag)); + __ Branch(&runtime, ne, t0, Operand(zero_reg)); + __ lw(t1, FieldMemOperand(t1, ExternalString::kResourceDataOffset)); + // t1 already points to the first character of underlying string. + __ jmp(&allocate_result); + + __ bind(&sequential_string); + // Locate first character of underlying subject string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ Addu(t1, t1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + + __ bind(&allocate_result); + // Sequential acii string. Allocate the result. + STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0); + __ And(t0, a1, Operand(kStringEncodingMask)); + __ Branch(&two_byte_sequential, eq, t0, Operand(zero_reg)); + + // Allocate and copy the resulting ASCII string. + __ AllocateAsciiString(v0, a2, t0, t2, t3, &runtime); + + // Locate first character of substring to copy. + __ Addu(t1, t1, a3); - // v0: result string - // a2: result string length - // a3: from index (untagged smi) - // t1: first character of substring to copy - // t3: (a.k.a. from): from offset (smi) // Locate first character of result. __ Addu(a1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); @@ -5813,30 +6237,17 @@ void SubStringStub::Generate(MacroAssembler* masm) { masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED); __ jmp(&return_v0); - __ bind(&non_ascii_flat); - // a2: result string length - // t1: string - // t3: (a.k.a. from): from offset (smi) - // Check for flat two byte string. - - // Locate 'from' character of string. - __ Addu(t1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // As "from" is a smi it is 2 times the value which matches the size of a two - // byte character. - STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); - __ Addu(t1, t1, Operand(from)); - - // Allocate the result. - __ AllocateTwoByteString(v0, a2, a1, a3, t0, &sub_string_runtime); + // Allocate and copy the resulting two-byte string. + __ bind(&two_byte_sequential); + __ AllocateTwoByteString(v0, a2, t0, t2, t3, &runtime); - // v0: result string - // a2: result string length - // t1: first character of substring to copy + // Locate first character of substring to copy. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ sll(t0, a3, 1); + __ Addu(t1, t1, t0); // Locate first character of result. __ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - from = no_reg; - // v0: result string. // a1: first character of result. // a2: result length. @@ -5844,77 +6255,14 @@ void SubStringStub::Generate(MacroAssembler* masm) { STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong( masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED); - __ jmp(&return_v0); - - if (FLAG_string_slices) { - __ bind(&create_slice); - // v0: original string - // a1: instance type - // a2: length - // a3: from index (untagged smi) - // t2 (a.k.a. to): to (smi) - // t3 (a.k.a. from): from offset (smi) - Label allocate_slice, sliced_string, seq_string; - STATIC_ASSERT(kSeqStringTag == 0); - __ And(t4, a1, Operand(kStringRepresentationMask)); - __ Branch(&seq_string, eq, t4, Operand(zero_reg)); - STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); - STATIC_ASSERT(kIsIndirectStringMask != 0); - __ And(t4, a1, Operand(kIsIndirectStringMask)); - // External string. Jump to runtime. - __ Branch(&sub_string_runtime, eq, t4, Operand(zero_reg)); - - __ And(t4, a1, Operand(kSlicedNotConsMask)); - __ Branch(&sliced_string, ne, t4, Operand(zero_reg)); - // Cons string. Check whether it is flat, then fetch first part. - __ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset)); - __ LoadRoot(t5, Heap::kEmptyStringRootIndex); - __ Branch(&sub_string_runtime, ne, t1, Operand(t5)); - __ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset)); - __ jmp(&allocate_slice); - - __ bind(&sliced_string); - // Sliced string. Fetch parent and correct start index by offset. - __ lw(t1, FieldMemOperand(v0, SlicedString::kOffsetOffset)); - __ addu(t3, t3, t1); - __ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); - __ jmp(&allocate_slice); - - __ bind(&seq_string); - // Sequential string. Just move string to the right register. - __ mov(t1, v0); - - __ bind(&allocate_slice); - // a1: instance type of original string - // a2: length - // t1: underlying subject string - // t3 (a.k.a. from): from offset (smi) - // Allocate new sliced string. At this point we do not reload the instance - // type including the string encoding because we simply rely on the info - // provided by the original string. It does not matter if the original - // string's encoding is wrong because we always have to recheck encoding of - // the newly created string's parent anyways due to externalized strings. - Label two_byte_slice, set_slice_header; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ And(t4, a1, Operand(kStringEncodingMask)); - __ Branch(&two_byte_slice, eq, t4, Operand(zero_reg)); - __ AllocateAsciiSlicedString(v0, a2, a3, t0, &sub_string_runtime); - __ jmp(&set_slice_header); - __ bind(&two_byte_slice); - __ AllocateTwoByteSlicedString(v0, a2, a3, t0, &sub_string_runtime); - __ bind(&set_slice_header); - __ sw(t3, FieldMemOperand(v0, SlicedString::kOffsetOffset)); - __ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); - } __ bind(&return_v0); + Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->sub_string_native(), 1, a3, t0); - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ DropAndRet(3); // Just jump to runtime to create the sub string. - __ bind(&sub_string_runtime); + __ bind(&runtime); __ TailCallRuntime(Runtime::kSubString, 3, 1); } @@ -6072,7 +6420,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) { - Label string_add_runtime, call_builtin; + Label call_runtime, call_builtin; Builtins::JavaScript builtin_id = Builtins::ADD; Counters* counters = masm->isolate()->counters(); @@ -6087,7 +6435,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Make sure that both arguments are strings if not known in advance. if (flags_ == NO_STRING_ADD_FLAGS) { - __ JumpIfEitherSmi(a0, a1, &string_add_runtime); + __ JumpIfEitherSmi(a0, a1, &call_runtime); // Load instance types. __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset)); __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset)); @@ -6097,7 +6445,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // If either is not a string, go to runtime. __ Or(t4, t0, Operand(t1)); __ And(t4, t4, Operand(kIsNotStringMask)); - __ Branch(&string_add_runtime, ne, t4, Operand(zero_reg)); + __ Branch(&call_runtime, ne, t4, Operand(zero_reg)); } else { // Here at least one of the arguments is definitely a string. // We convert the one that is not known to be a string. @@ -6136,8 +6484,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ Branch(&strings_not_empty, ne, t4, Operand(zero_reg)); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); __ bind(&strings_not_empty); } @@ -6170,7 +6517,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset)); } __ JumpIfBothInstanceTypesAreNotSequentialAscii(t0, t1, t2, t3, - &string_add_runtime); + &call_runtime); // Get the two characters forming the sub string. __ lbu(a2, FieldMemOperand(a0, SeqAsciiString::kHeaderSize)); @@ -6180,10 +6527,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { // just allocate a new one. Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( - masm, a2, a3, t2, t3, t0, t1, t4, &make_two_character_string); + masm, a2, a3, t2, t3, t0, t1, t5, &make_two_character_string); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); __ bind(&make_two_character_string); // Resulting string has length 2 and first chars of two strings @@ -6192,21 +6538,20 @@ void StringAddStub::Generate(MacroAssembler* masm) { // halfword store instruction (which assumes that processor is // in a little endian mode). __ li(t2, Operand(2)); - __ AllocateAsciiString(v0, t2, t0, t1, t4, &string_add_runtime); + __ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime); __ sh(a2, FieldMemOperand(v0, SeqAsciiString::kHeaderSize)); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); __ bind(&longer_than_two); // Check if resulting string will be flat. __ Branch(&string_add_flat_result, lt, t2, - Operand(String::kMinNonFlatLength)); + Operand(ConsString::kMinLength)); // Handle exceptionally long strings in the runtime system. STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0); ASSERT(IsPowerOf2(String::kMaxLength + 1)); // kMaxLength + 1 is representable as shifted literal, kMaxLength is not. - __ Branch(&string_add_runtime, hs, t2, Operand(String::kMaxLength + 1)); + __ Branch(&call_runtime, hs, t2, Operand(String::kMaxLength + 1)); // If result is not supposed to be flat, allocate a cons string object. // If both strings are ASCII the result is an ASCII cons string. @@ -6218,22 +6563,20 @@ void StringAddStub::Generate(MacroAssembler* masm) { } Label non_ascii, allocated, ascii_data; STATIC_ASSERT(kTwoByteStringTag == 0); - // Branch to non_ascii if either string-encoding field is zero (non-ascii). + // Branch to non_ascii if either string-encoding field is zero (non-ASCII). __ And(t4, t0, Operand(t1)); __ And(t4, t4, Operand(kStringEncodingMask)); __ Branch(&non_ascii, eq, t4, Operand(zero_reg)); // Allocate an ASCII cons string. __ bind(&ascii_data); - __ AllocateAsciiConsString(t3, t2, t0, t1, &string_add_runtime); + __ AllocateAsciiConsString(v0, t2, t0, t1, &call_runtime); __ bind(&allocated); // Fill the fields of the cons string. - __ sw(a0, FieldMemOperand(t3, ConsString::kFirstOffset)); - __ sw(a1, FieldMemOperand(t3, ConsString::kSecondOffset)); - __ mov(v0, t3); + __ sw(a0, FieldMemOperand(v0, ConsString::kFirstOffset)); + __ sw(a1, FieldMemOperand(v0, ConsString::kSecondOffset)); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); __ bind(&non_ascii); // At least one of the strings is two-byte. Check whether it happens @@ -6251,11 +6594,13 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ Branch(&ascii_data, eq, t0, Operand(kAsciiStringTag | kAsciiDataHintTag)); // Allocate a two byte cons string. - __ AllocateTwoByteConsString(t3, t2, t0, t1, &string_add_runtime); + __ AllocateTwoByteConsString(v0, t2, t0, t1, &call_runtime); __ Branch(&allocated); - // Handle creating a flat result. First check that both strings are - // sequential and that they have the same encoding. + // We cannot encounter sliced strings or cons strings here since: + STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength); + // Handle creating a flat result from either external or sequential strings. + // Locate the first characters' locations. // a0: first string // a1: second string // a2: length of first string @@ -6263,6 +6608,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS) // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS) // t2: sum of lengths. + Label first_prepared, second_prepared; __ bind(&string_add_flat_result); if (flags_ != NO_STRING_ADD_FLAGS) { __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset)); @@ -6270,101 +6616,86 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset)); __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset)); } - // Check that both strings are sequential, meaning that we - // branch to runtime if either string tag is non-zero. + // Check whether both strings have same encoding + __ Xor(t3, t0, Operand(t1)); + __ And(t3, t3, Operand(kStringEncodingMask)); + __ Branch(&call_runtime, ne, t3, Operand(zero_reg)); + STATIC_ASSERT(kSeqStringTag == 0); - __ Or(t4, t0, Operand(t1)); - __ And(t4, t4, Operand(kStringRepresentationMask)); - __ Branch(&string_add_runtime, ne, t4, Operand(zero_reg)); + __ And(t4, t0, Operand(kStringRepresentationMask)); - // Now check if both strings have the same encoding (ASCII/Two-byte). - // a0: first string - // a1: second string + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + Label skip_first_add; + __ Branch(&skip_first_add, ne, t4, Operand(zero_reg)); + __ Branch(USE_DELAY_SLOT, &first_prepared); + __ addiu(t3, a0, SeqAsciiString::kHeaderSize - kHeapObjectTag); + __ bind(&skip_first_add); + // External string: rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ And(t4, t0, Operand(kShortExternalStringMask)); + __ Branch(&call_runtime, ne, t4, Operand(zero_reg)); + __ lw(t3, FieldMemOperand(a0, ExternalString::kResourceDataOffset)); + __ bind(&first_prepared); + + STATIC_ASSERT(kSeqStringTag == 0); + __ And(t4, t1, Operand(kStringRepresentationMask)); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + Label skip_second_add; + __ Branch(&skip_second_add, ne, t4, Operand(zero_reg)); + __ Branch(USE_DELAY_SLOT, &second_prepared); + __ addiu(a1, a1, SeqAsciiString::kHeaderSize - kHeapObjectTag); + __ bind(&skip_second_add); + // External string: rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ And(t4, t1, Operand(kShortExternalStringMask)); + __ Branch(&call_runtime, ne, t4, Operand(zero_reg)); + __ lw(a1, FieldMemOperand(a1, ExternalString::kResourceDataOffset)); + __ bind(&second_prepared); + + Label non_ascii_string_add_flat_result; + // t3: first character of first string + // a1: first character of second string // a2: length of first string // a3: length of second string - // t0: first string instance type - // t1: second string instance type // t2: sum of lengths. - Label non_ascii_string_add_flat_result; - ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test. - __ xor_(t3, t1, t0); - __ And(t3, t3, Operand(kStringEncodingMask)); - __ Branch(&string_add_runtime, ne, t3, Operand(zero_reg)); - // And see if it's ASCII (0) or two-byte (1). - __ And(t3, t0, Operand(kStringEncodingMask)); - __ Branch(&non_ascii_string_add_flat_result, eq, t3, Operand(zero_reg)); - - // Both strings are sequential ASCII strings. We also know that they are - // short (since the sum of the lengths is less than kMinNonFlatLength). - // t2: length of resulting flat string - __ AllocateAsciiString(t3, t2, t0, t1, t4, &string_add_runtime); - // Locate first character of result. - __ Addu(t2, t3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument. - __ Addu(a0, a0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // a0: first character of first string. - // a1: second string. + // Both strings have the same encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ And(t4, t1, Operand(kStringEncodingMask)); + __ Branch(&non_ascii_string_add_flat_result, eq, t4, Operand(zero_reg)); + + __ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime); + __ Addu(t2, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + // v0: result string. + // t3: first character of first string. + // a1: first character of second string // a2: length of first string. // a3: length of second string. // t2: first character of result. - // t3: result string. - StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, true); - // Load second argument and locate first character. - __ Addu(a1, a1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // a1: first character of second string. - // a3: length of second string. + StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, true); // t2: next character of result. - // t3: result string. StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, true); - __ mov(v0, t3); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); __ bind(&non_ascii_string_add_flat_result); - // Both strings are sequential two byte strings. - // a0: first string. - // a1: second string. - // a2: length of first string. - // a3: length of second string. - // t2: sum of length of strings. - __ AllocateTwoByteString(t3, t2, t0, t1, t4, &string_add_runtime); - // a0: first string. - // a1: second string. - // a2: length of first string. - // a3: length of second string. - // t3: result string. - - // Locate first character of result. - __ Addu(t2, t3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument. - __ Addu(a0, a0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - - // a0: first character of first string. - // a1: second string. + __ AllocateTwoByteString(v0, t2, t0, t1, t5, &call_runtime); + __ Addu(t2, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // v0: result string. + // t3: first character of first string. + // a1: first character of second string. // a2: length of first string. // a3: length of second string. // t2: first character of result. - // t3: result string. - StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, false); - - // Locate first character of second argument. - __ Addu(a1, a1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - - // a1: first character of second string. - // a3: length of second string. - // t2: next character of result (after copy of first string). - // t3: result string. + StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, false); + // t2: next character of result. StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false); - __ mov(v0, t3); __ IncrementCounter(counters->string_add_native(), 1, a2, a3); - __ Addu(sp, sp, Operand(2 * kPointerSize)); - __ Ret(); + __ DropAndRet(2); // Just jump to runtime to add the two strings. - __ bind(&string_add_runtime); + __ bind(&call_runtime); __ TailCallRuntime(Runtime::kStringAdd, 2, 1); if (call_builtin.is_linked()) { @@ -6467,39 +6798,25 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) { __ Subu(a2, a0, Operand(kHeapObjectTag)); __ ldc1(f2, MemOperand(a2, HeapNumber::kValueOffset)); - Label fpu_eq, fpu_lt, fpu_gt; - // Compare operands (test if unordered). - __ c(UN, D, f0, f2); - // Don't base result on status bits when a NaN is involved. - __ bc1t(&unordered); - __ nop(); + // Return a result of -1, 0, or 1, or use CompareStub for NaNs. + Label fpu_eq, fpu_lt; + // Test if equal, and also handle the unordered/NaN case. + __ BranchF(&fpu_eq, &unordered, eq, f0, f2); - // Test if equal. - __ c(EQ, D, f0, f2); - __ bc1t(&fpu_eq); - __ nop(); + // Test if less (unordered case is already handled). + __ BranchF(&fpu_lt, NULL, lt, f0, f2); - // Test if unordered or less (unordered case is already handled). - __ c(ULT, D, f0, f2); - __ bc1t(&fpu_lt); - __ nop(); + // Otherwise it's greater, so just fall thru, and return. + __ Ret(USE_DELAY_SLOT); + __ li(v0, Operand(GREATER)); // In delay slot. - // Otherwise it's greater. - __ bc1f(&fpu_gt); - __ nop(); - - // Return a result of -1, 0, or 1. __ bind(&fpu_eq); - __ li(v0, Operand(EQUAL)); - __ Ret(); + __ Ret(USE_DELAY_SLOT); + __ li(v0, Operand(EQUAL)); // In delay slot. __ bind(&fpu_lt); - __ li(v0, Operand(LESS)); - __ Ret(); - - __ bind(&fpu_gt); - __ li(v0, Operand(GREATER)); - __ Ret(); + __ Ret(USE_DELAY_SLOT); + __ li(v0, Operand(LESS)); // In delay slot. __ bind(&unordered); } @@ -6643,25 +6960,39 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { } -void ICCompareStub::GenerateMiss(MacroAssembler* masm) { - __ Push(a1, a0); - __ push(ra); +void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) { + Label miss; + __ And(a2, a1, a0); + __ JumpIfSmi(a2, &miss); + __ lw(a2, FieldMemOperand(a0, HeapObject::kMapOffset)); + __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset)); + __ Branch(&miss, ne, a2, Operand(known_map_)); + __ Branch(&miss, ne, a3, Operand(known_map_)); - // Call the runtime system in a fresh internal frame. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss), - masm->isolate()); - __ EnterInternalFrame(); - __ Push(a1, a0); - __ li(t0, Operand(Smi::FromInt(op_))); - __ push(t0); - __ CallExternalReference(miss, 3); - __ LeaveInternalFrame(); - // Compute the entry point of the rewritten stub. - __ Addu(a2, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); - // Restore registers. - __ pop(ra); - __ pop(a0); - __ pop(a1); + __ Ret(USE_DELAY_SLOT); + __ subu(v0, a0, a1); + + __ bind(&miss); + GenerateMiss(masm); +} + +void ICCompareStub::GenerateMiss(MacroAssembler* masm) { + { + // Call the runtime system in a fresh internal frame. + ExternalReference miss = + ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a1, a0); + __ push(ra); + __ Push(a1, a0); + __ li(t0, Operand(Smi::FromInt(op_))); + __ push(t0); + __ CallExternalReference(miss, 3); + // Compute the entry point of the rewritten stub. + __ Addu(a2, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); + // Restore registers. + __ Pop(a1, a0, ra); + } __ Jump(a2); } @@ -6672,7 +7003,7 @@ void DirectCEntryStub::Generate(MacroAssembler* masm) { // The saved ra is after the reserved stack space for the 4 args. __ lw(t9, MemOperand(sp, kCArgsSlotsSize)); - if (FLAG_debug_code && EnableSlowAsserts()) { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { // In case of an error the return address may point to a memory area // filled with kZapValue by the GC. // Dereference the address and check for this. @@ -6722,15 +7053,14 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm, } -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - String* name, - Register scratch0) { -// If names of slots in range from 1 to kProbes - 1 for the hash value are +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle<String> name, + Register scratch0) { + // If names of slots in range from 1 to kProbes - 1 for the hash value are // not equal to the name and kProbes-th slot is not used (its name is the // undefined value), it guarantees the hash table doesn't contain the // property. It's true even if some slots represent deleted properties @@ -6743,20 +7073,17 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( __ lw(index, FieldMemOperand(properties, kCapacityOffset)); __ Subu(index, index, Operand(1)); __ And(index, index, Operand( - Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i)))); + Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i)))); // Scale the index by multiplying by the entry size. ASSERT(StringDictionary::kEntrySize == 3); - // index *= 3. - __ mov(at, index); - __ sll(index, index, 1); + __ sll(at, index, 1); __ Addu(index, index, at); Register entity_name = scratch0; // Having undefined at this place means the name is not contained. ASSERT_EQ(kSmiTagSize, 1); Register tmp = properties; - __ sll(scratch0, index, 1); __ Addu(tmp, properties, scratch0); __ lw(entity_name, FieldMemOperand(tmp, kElementsStartOffset)); @@ -6784,19 +7111,18 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( const int spill_mask = (ra.bit() | t2.bit() | t1.bit() | t0.bit() | a3.bit() | - a2.bit() | a1.bit() | a0.bit()); + a2.bit() | a1.bit() | a0.bit() | v0.bit()); __ MultiPush(spill_mask); __ lw(a0, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); __ li(a1, Operand(Handle<String>(name))); StringDictionaryLookupStub stub(NEGATIVE_LOOKUP); - MaybeObject* result = masm->TryCallStub(&stub); - if (result->IsFailure()) return result; + __ CallStub(&stub); + __ mov(at, v0); __ MultiPop(spill_mask); - __ Branch(done, eq, v0, Operand(zero_reg)); - __ Branch(miss, ne, v0, Operand(zero_reg)); - return result; + __ Branch(done, eq, at, Operand(zero_reg)); + __ Branch(miss, ne, at, Operand(zero_reg)); } @@ -6811,6 +7137,11 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, Register name, Register scratch1, Register scratch2) { + ASSERT(!elements.is(scratch1)); + ASSERT(!elements.is(scratch2)); + ASSERT(!name.is(scratch1)); + ASSERT(!name.is(scratch2)); + // Assert that name contains a string. if (FLAG_debug_code) __ AbortIfNotString(name); @@ -6841,8 +7172,7 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, ASSERT(StringDictionary::kEntrySize == 3); // scratch2 = scratch2 * 3. - __ mov(at, scratch2); - __ sll(scratch2, scratch2, 1); + __ sll(at, scratch2, 1); __ Addu(scratch2, scratch2, at); // Check if the key is identical to the name. @@ -6854,23 +7184,32 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, const int spill_mask = (ra.bit() | t2.bit() | t1.bit() | t0.bit() | - a3.bit() | a2.bit() | a1.bit() | a0.bit()) & + a3.bit() | a2.bit() | a1.bit() | a0.bit() | v0.bit()) & ~(scratch1.bit() | scratch2.bit()); __ MultiPush(spill_mask); - __ Move(a0, elements); - __ Move(a1, name); + if (name.is(a0)) { + ASSERT(!elements.is(a1)); + __ Move(a1, name); + __ Move(a0, elements); + } else { + __ Move(a0, elements); + __ Move(a1, name); + } StringDictionaryLookupStub stub(POSITIVE_LOOKUP); __ CallStub(&stub); __ mov(scratch2, a2); + __ mov(at, v0); __ MultiPop(spill_mask); - __ Branch(done, ne, v0, Operand(zero_reg)); - __ Branch(miss, eq, v0, Operand(zero_reg)); + __ Branch(done, ne, at, Operand(zero_reg)); + __ Branch(miss, eq, at, Operand(zero_reg)); } void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. // Registers: // result: StringDictionary to probe // a1: key @@ -6964,6 +7303,341 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { } +struct AheadOfTimeWriteBarrierStubList { + Register object, value, address; + RememberedSetAction action; +}; + + +struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { + // Used in RegExpExecStub. + { s2, s0, t3, EMIT_REMEMBERED_SET }, + { s2, a2, t3, EMIT_REMEMBERED_SET }, + // Used in CompileArrayPushCall. + // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore. + // Also used in KeyedStoreIC::GenerateGeneric. + { a3, t0, t1, EMIT_REMEMBERED_SET }, + // Used in CompileStoreGlobal. + { t0, a1, a2, OMIT_REMEMBERED_SET }, + // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField. + { a1, a2, a3, EMIT_REMEMBERED_SET }, + { a3, a2, a1, EMIT_REMEMBERED_SET }, + // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField. + { a2, a1, a3, EMIT_REMEMBERED_SET }, + { a3, a1, a2, EMIT_REMEMBERED_SET }, + // KeyedStoreStubCompiler::GenerateStoreFastElement. + { t0, a2, a3, EMIT_REMEMBERED_SET }, + // ElementsTransitionGenerator::GenerateSmiOnlyToObject + // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // and ElementsTransitionGenerator::GenerateDoubleToObject + { a2, a3, t5, EMIT_REMEMBERED_SET }, + // ElementsTransitionGenerator::GenerateDoubleToObject + { t2, a2, a0, EMIT_REMEMBERED_SET }, + { a2, t2, t5, EMIT_REMEMBERED_SET }, + // StoreArrayLiteralElementStub::Generate + { t1, a0, t2, EMIT_REMEMBERED_SET }, + // Null termination. + { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET} +}; + + +bool RecordWriteStub::IsPregenerated() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + if (object_.is(entry->object) && + value_.is(entry->value) && + address_.is(entry->address) && + remembered_set_action_ == entry->action && + save_fp_regs_mode_ == kDontSaveFPRegs) { + return true; + } + } + return false; +} + + +bool StoreBufferOverflowStub::IsPregenerated() { + return save_doubles_ == kDontSaveFPRegs || ISOLATE->fp_stubs_generated(); +} + + +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() { + StoreBufferOverflowStub stub1(kDontSaveFPRegs); + stub1.GetCode()->set_is_pregenerated(true); +} + + +void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + RecordWriteStub stub(entry->object, + entry->value, + entry->address, + entry->action, + kDontSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + } +} + + +// Takes the input in 3 registers: address_ value_ and object_. A pointer to +// the value has just been written into the object, now this stub makes sure +// we keep the GC informed. The word in the object where the value has been +// written is in the address register. +void RecordWriteStub::Generate(MacroAssembler* masm) { + Label skip_to_incremental_noncompacting; + Label skip_to_incremental_compacting; + + // The first two branch+nop instructions are generated with labels so as to + // get the offset fixed up correctly by the bind(Label*) call. We patch it + // back and forth between a "bne zero_reg, zero_reg, ..." (a nop in this + // position) and the "beq zero_reg, zero_reg, ..." when we start and stop + // incremental heap marking. + // See RecordWriteStub::Patch for details. + __ beq(zero_reg, zero_reg, &skip_to_incremental_noncompacting); + __ nop(); + __ beq(zero_reg, zero_reg, &skip_to_incremental_compacting); + __ nop(); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } + __ Ret(); + + __ bind(&skip_to_incremental_noncompacting); + GenerateIncremental(masm, INCREMENTAL); + + __ bind(&skip_to_incremental_compacting); + GenerateIncremental(masm, INCREMENTAL_COMPACTION); + + // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. + // Will be checked in IncrementalMarking::ActivateGeneratedStub. + + PatchBranchIntoNop(masm, 0); + PatchBranchIntoNop(masm, 2 * Assembler::kInstrSize); +} + + +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { + regs_.Save(masm); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + Label dont_need_remembered_set; + + __ lw(regs_.scratch0(), MemOperand(regs_.address(), 0)); + __ JumpIfNotInNewSpace(regs_.scratch0(), // Value. + regs_.scratch0(), + &dont_need_remembered_set); + + __ CheckPageFlag(regs_.object(), + regs_.scratch0(), + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + ne, + &dont_need_remembered_set); + + // First notify the incremental marker if necessary, then update the + // remembered set. + CheckNeedsToInformIncrementalMarker( + masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + + __ bind(&dont_need_remembered_set); + } + + CheckNeedsToInformIncrementalMarker( + masm, kReturnOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ Ret(); +} + + +void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) { + regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_); + int argument_count = 3; + __ PrepareCallCFunction(argument_count, regs_.scratch0()); + Register address = + a0.is(regs_.address()) ? regs_.scratch0() : regs_.address(); + ASSERT(!address.is(regs_.object())); + ASSERT(!address.is(a0)); + __ Move(address, regs_.address()); + __ Move(a0, regs_.object()); + if (mode == INCREMENTAL_COMPACTION) { + __ Move(a1, address); + } else { + ASSERT(mode == INCREMENTAL); + __ lw(a1, MemOperand(address, 0)); + } + __ li(a2, Operand(ExternalReference::isolate_address())); + + AllowExternalCallThatCantCauseGC scope(masm); + if (mode == INCREMENTAL_COMPACTION) { + __ CallCFunction( + ExternalReference::incremental_evacuation_record_write_function( + masm->isolate()), + argument_count); + } else { + ASSERT(mode == INCREMENTAL); + __ CallCFunction( + ExternalReference::incremental_marking_record_write_function( + masm->isolate()), + argument_count); + } + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_); +} + + +void RecordWriteStub::CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode) { + Label on_black; + Label need_incremental; + Label need_incremental_pop_scratch; + + // Let's look at the color of the object: If it is not black we don't have + // to inform the incremental marker. + __ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ Ret(); + } + + __ bind(&on_black); + + // Get the value from the slot. + __ lw(regs_.scratch0(), MemOperand(regs_.address(), 0)); + + if (mode == INCREMENTAL_COMPACTION) { + Label ensure_not_white; + + __ CheckPageFlag(regs_.scratch0(), // Contains value. + regs_.scratch1(), // Scratch. + MemoryChunk::kEvacuationCandidateMask, + eq, + &ensure_not_white); + + __ CheckPageFlag(regs_.object(), + regs_.scratch1(), // Scratch. + MemoryChunk::kSkipEvacuationSlotsRecordingMask, + eq, + &need_incremental); + + __ bind(&ensure_not_white); + } + + // We need extra registers for this, so we push the object and the address + // register temporarily. + __ Push(regs_.object(), regs_.address()); + __ EnsureNotWhite(regs_.scratch0(), // The value. + regs_.scratch1(), // Scratch. + regs_.object(), // Scratch. + regs_.address(), // Scratch. + &need_incremental_pop_scratch); + __ Pop(regs_.object(), regs_.address()); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ Ret(); + } + + __ bind(&need_incremental_pop_scratch); + __ Pop(regs_.object(), regs_.address()); + + __ bind(&need_incremental); + + // Fall through when we need to inform the incremental marker. +} + + +void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : element value to store + // -- a1 : array literal + // -- a2 : map of array literal + // -- a3 : element index as smi + // -- t0 : array literal index in function as smi + // ----------------------------------- + + Label element_done; + Label double_elements; + Label smi_element; + Label slow_elements; + Label fast_elements; + + __ CheckFastElements(a2, t1, &double_elements); + // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + __ JumpIfSmi(a0, &smi_element); + __ CheckFastSmiOnlyElements(a2, t1, &fast_elements); + + // Store into the array literal requires a elements transition. Call into + // the runtime. + __ bind(&slow_elements); + // call. + __ Push(a1, a3, a0); + __ lw(t1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ lw(t1, FieldMemOperand(t1, JSFunction::kLiteralsOffset)); + __ Push(t1, t0); + __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); + + // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + __ bind(&fast_elements); + __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); + __ sll(t2, a3, kPointerSizeLog2 - kSmiTagSize); + __ Addu(t2, t1, t2); + __ Addu(t2, t2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sw(a0, MemOperand(t2, 0)); + // Update the write barrier for the array store. + __ RecordWrite(t1, t2, a0, kRAHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + + // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or + // FAST_ELEMENTS, and value is Smi. + __ bind(&smi_element); + __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); + __ sll(t2, a3, kPointerSizeLog2 - kSmiTagSize); + __ Addu(t2, t1, t2); + __ sw(a0, FieldMemOperand(t2, FixedArray::kHeaderSize)); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + + // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + __ bind(&double_elements); + __ lw(t1, FieldMemOperand(a1, JSObject::kElementsOffset)); + __ StoreNumberToDoubleElements(a0, a3, a1, t1, t2, t3, t5, t6, + &slow_elements); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); +} + + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/mips/code-stubs-mips.h b/deps/v8/src/mips/code-stubs-mips.h index aa224bcfa6..e0954d837e 100644 --- a/deps/v8/src/mips/code-stubs-mips.h +++ b/deps/v8/src/mips/code-stubs-mips.h @@ -59,6 +59,25 @@ class TranscendentalCacheStub: public CodeStub { }; +class StoreBufferOverflowStub: public CodeStub { + public: + explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) + : save_doubles_(save_fp) { } + + void Generate(MacroAssembler* masm); + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + private: + SaveFPRegsMode save_doubles_; + + Major MajorKey() { return StoreBufferOverflow; } + int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } +}; + + class UnaryOpStub: public CodeStub { public: UnaryOpStub(Token::Value op, @@ -118,7 +137,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_unary_op_type(operand_type_); } }; @@ -217,7 +236,7 @@ class BinaryOpStub: public CodeStub { return BinaryOpIC::ToState(operands_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } @@ -226,6 +245,70 @@ class BinaryOpStub: public CodeStub { }; +class StringHelper : public AllStatic { + public: + // Generate code for copying characters using a simple loop. This should only + // be used in places where the number of characters is small and the + // additional setup and checking in GenerateCopyCharactersLong adds too much + // overhead. Copying of overlapping regions is not supported. + // Dest register ends at the position after the last character written. + static void GenerateCopyCharacters(MacroAssembler* masm, + Register dest, + Register src, + Register count, + Register scratch, + bool ascii); + + // Generate code for copying a large number of characters. This function + // is allowed to spend extra time setting up conditions to make copying + // faster. Copying of overlapping regions is not supported. + // Dest register ends at the position after the last character written. + static void GenerateCopyCharactersLong(MacroAssembler* masm, + Register dest, + Register src, + Register count, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Register scratch5, + int flags); + + + // Probe the symbol table for a two character string. If the string is + // not found by probing a jump to the label not_found is performed. This jump + // does not guarantee that the string is not in the symbol table. If the + // string is found the code falls through with the string in register r0. + // Contents of both c1 and c2 registers are modified. At the exit c1 is + // guaranteed to contain halfword with low and high bytes equal to + // initial contents of c1 and c2 respectively. + static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, + Register c1, + Register c2, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Register scratch5, + Label* not_found); + + // Generate string hash. + static void GenerateHashInit(MacroAssembler* masm, + Register hash, + Register character); + + static void GenerateHashAddCharacter(MacroAssembler* masm, + Register hash, + Register character); + + static void GenerateHashGetHash(MacroAssembler* masm, + Register hash); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); +}; + + // Flag that indicates how to generate code for the stub StringAddStub. enum StringAddFlags { NO_STRING_ADD_FLAGS = 0, @@ -324,7 +407,15 @@ class WriteInt32ToHeapNumberStub : public CodeStub { : the_int_(the_int), the_heap_number_(the_heap_number), scratch_(scratch), - sign_(scratch2) { } + sign_(scratch2) { + ASSERT(IntRegisterBits::is_valid(the_int_.code())); + ASSERT(HeapNumberRegisterBits::is_valid(the_heap_number_.code())); + ASSERT(ScratchRegisterBits::is_valid(scratch_.code())); + ASSERT(SignRegisterBits::is_valid(sign_.code())); + } + + bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); private: Register the_int_; @@ -336,13 +427,15 @@ class WriteInt32ToHeapNumberStub : public CodeStub { class IntRegisterBits: public BitField<int, 0, 4> {}; class HeapNumberRegisterBits: public BitField<int, 4, 4> {}; class ScratchRegisterBits: public BitField<int, 8, 4> {}; + class SignRegisterBits: public BitField<int, 12, 4> {}; Major MajorKey() { return WriteInt32ToHeapNumber; } int MinorKey() { // Encode the parameters in a unique 16 bit value. return IntRegisterBits::encode(the_int_.code()) | HeapNumberRegisterBits::encode(the_heap_number_.code()) - | ScratchRegisterBits::encode(scratch_.code()); + | ScratchRegisterBits::encode(scratch_.code()) + | SignRegisterBits::encode(sign_.code()); } void Generate(MacroAssembler* masm); @@ -375,6 +468,208 @@ class NumberToStringStub: public CodeStub { }; +class RecordWriteStub: public CodeStub { + public: + RecordWriteStub(Register object, + Register value, + Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode) + : object_(object), + value_(value), + address_(address), + remembered_set_action_(remembered_set_action), + save_fp_regs_mode_(fp_mode), + regs_(object, // An input reg. + address, // An input reg. + value) { // One scratch reg. + } + + enum Mode { + STORE_BUFFER_ONLY, + INCREMENTAL, + INCREMENTAL_COMPACTION + }; + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + static void PatchBranchIntoNop(MacroAssembler* masm, int pos) { + const unsigned offset = masm->instr_at(pos) & kImm16Mask; + masm->instr_at_put(pos, BNE | (zero_reg.code() << kRsShift) | + (zero_reg.code() << kRtShift) | (offset & kImm16Mask)); + ASSERT(Assembler::IsBne(masm->instr_at(pos))); + } + + static void PatchNopIntoBranch(MacroAssembler* masm, int pos) { + const unsigned offset = masm->instr_at(pos) & kImm16Mask; + masm->instr_at_put(pos, BEQ | (zero_reg.code() << kRsShift) | + (zero_reg.code() << kRtShift) | (offset & kImm16Mask)); + ASSERT(Assembler::IsBeq(masm->instr_at(pos))); + } + + static Mode GetMode(Code* stub) { + Instr first_instruction = Assembler::instr_at(stub->instruction_start()); + Instr second_instruction = Assembler::instr_at(stub->instruction_start() + + 2 * Assembler::kInstrSize); + + if (Assembler::IsBeq(first_instruction)) { + return INCREMENTAL; + } + + ASSERT(Assembler::IsBne(first_instruction)); + + if (Assembler::IsBeq(second_instruction)) { + return INCREMENTAL_COMPACTION; + } + + ASSERT(Assembler::IsBne(second_instruction)); + + return STORE_BUFFER_ONLY; + } + + static void Patch(Code* stub, Mode mode) { + MacroAssembler masm(NULL, + stub->instruction_start(), + stub->instruction_size()); + switch (mode) { + case STORE_BUFFER_ONLY: + ASSERT(GetMode(stub) == INCREMENTAL || + GetMode(stub) == INCREMENTAL_COMPACTION); + PatchBranchIntoNop(&masm, 0); + PatchBranchIntoNop(&masm, 2 * Assembler::kInstrSize); + break; + case INCREMENTAL: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + PatchNopIntoBranch(&masm, 0); + break; + case INCREMENTAL_COMPACTION: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + PatchNopIntoBranch(&masm, 2 * Assembler::kInstrSize); + break; + } + ASSERT(GetMode(stub) == mode); + CPU::FlushICache(stub->instruction_start(), 4 * Assembler::kInstrSize); + } + + private: + // This is a helper class for freeing up 3 scratch registers. The input is + // two registers that must be preserved and one scratch register provided by + // the caller. + class RegisterAllocation { + public: + RegisterAllocation(Register object, + Register address, + Register scratch0) + : object_(object), + address_(address), + scratch0_(scratch0) { + ASSERT(!AreAliased(scratch0, object, address, no_reg)); + scratch1_ = GetRegThatIsNotOneOf(object_, address_, scratch0_); + } + + void Save(MacroAssembler* masm) { + ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); + // We don't have to save scratch0_ because it was given to us as + // a scratch register. + masm->push(scratch1_); + } + + void Restore(MacroAssembler* masm) { + masm->pop(scratch1_); + } + + // If we have to call into C then we need to save and restore all caller- + // saved registers that were not already preserved. The scratch registers + // will be restored by other means so we don't bother pushing them here. + void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { + masm->MultiPush((kJSCallerSaved | ra.bit()) & ~scratch1_.bit()); + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(FPU); + masm->MultiPushFPU(kCallerSavedFPU); + } + } + + inline void RestoreCallerSaveRegisters(MacroAssembler*masm, + SaveFPRegsMode mode) { + if (mode == kSaveFPRegs) { + CpuFeatures::Scope scope(FPU); + masm->MultiPopFPU(kCallerSavedFPU); + } + masm->MultiPop((kJSCallerSaved | ra.bit()) & ~scratch1_.bit()); + } + + inline Register object() { return object_; } + inline Register address() { return address_; } + inline Register scratch0() { return scratch0_; } + inline Register scratch1() { return scratch1_; } + + private: + Register object_; + Register address_; + Register scratch0_; + Register scratch1_; + + Register GetRegThatIsNotOneOf(Register r1, + Register r2, + Register r3) { + for (int i = 0; i < Register::kNumAllocatableRegisters; i++) { + Register candidate = Register::FromAllocationIndex(i); + if (candidate.is(r1)) continue; + if (candidate.is(r2)) continue; + if (candidate.is(r3)) continue; + return candidate; + } + UNREACHABLE(); + return no_reg; + } + friend class RecordWriteStub; + }; + + enum OnNoNeedToInformIncrementalMarker { + kReturnOnNoNeedToInformIncrementalMarker, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker + }; + + void Generate(MacroAssembler* masm); + void GenerateIncremental(MacroAssembler* masm, Mode mode); + void CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode); + void InformIncrementalMarker(MacroAssembler* masm, Mode mode); + + Major MajorKey() { return RecordWrite; } + + int MinorKey() { + return ObjectBits::encode(object_.code()) | + ValueBits::encode(value_.code()) | + AddressBits::encode(address_.code()) | + RememberedSetActionBits::encode(remembered_set_action_) | + SaveFPRegsModeBits::encode(save_fp_regs_mode_); + } + + void Activate(Code* code) { + code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); + } + + class ObjectBits: public BitField<int, 0, 5> {}; + class ValueBits: public BitField<int, 5, 5> {}; + class AddressBits: public BitField<int, 10, 5> {}; + class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {}; + class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {}; + + Register object_; + Register value_; + Register address_; + RememberedSetAction remembered_set_action_; + SaveFPRegsMode save_fp_regs_mode_; + Label slow_; + RegisterAllocation regs_; +}; + + // Enter C code from generated RegExp code in a way that allows // the C code to fix the return address in case of a GC. // Currently only needed on ARM and MIPS. @@ -561,14 +856,13 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - String* name, - Register scratch0); + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle<String> name, + Register scratch0); static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss, @@ -578,6 +872,8 @@ class StringDictionaryLookupStub: public CodeStub { Register r0, Register r1); + virtual bool SometimesSetsUpAFrame() { return false; } + private: static const int kInlinedProbes = 4; static const int kTotalProbes = 20; @@ -590,7 +886,7 @@ class StringDictionaryLookupStub: public CodeStub { StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; - Major MajorKey() { return StringDictionaryNegativeLookup; } + Major MajorKey() { return StringDictionaryLookup; } int MinorKey() { return LookupModeBits::encode(mode_); diff --git a/deps/v8/src/mips/codegen-mips.cc b/deps/v8/src/mips/codegen-mips.cc index 4400b643ad..c48432c702 100644 --- a/deps/v8/src/mips/codegen-mips.cc +++ b/deps/v8/src/mips/codegen-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -30,22 +30,381 @@ #if defined(V8_TARGET_ARCH_MIPS) #include "codegen.h" +#include "macro-assembler.h" namespace v8 { namespace internal { +#define __ ACCESS_MASM(masm) + // ------------------------------------------------------------------------- // Platform-specific RuntimeCallHelper functions. void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterInternalFrame(); + masm->EnterFrame(StackFrame::INTERNAL); + ASSERT(!masm->has_frame()); + masm->set_has_frame(true); } void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveInternalFrame(); + masm->LeaveFrame(StackFrame::INTERNAL); + ASSERT(masm->has_frame()); + masm->set_has_frame(false); +} + +// ------------------------------------------------------------------------- +// Code generators + +void ElementsTransitionGenerator::GenerateSmiOnlyToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : value + // -- a1 : key + // -- a2 : receiver + // -- ra : return address + // -- a3 : target map, scratch for subsequent call + // -- t0 : scratch (elements) + // ----------------------------------- + // Set transitioned map. + __ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); + __ RecordWriteField(a2, + HeapObject::kMapOffset, + a3, + t5, + kRAHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); +} + + +void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- a0 : value + // -- a1 : key + // -- a2 : receiver + // -- ra : return address + // -- a3 : target map, scratch for subsequent call + // -- t0 : scratch (elements) + // ----------------------------------- + Label loop, entry, convert_hole, gc_required; + bool fpu_supported = CpuFeatures::IsSupported(FPU); + __ push(ra); + + Register scratch = t6; + + __ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset)); + __ lw(t1, FieldMemOperand(t0, FixedArray::kLengthOffset)); + // t0: source FixedArray + // t1: number of elements (smi-tagged) + + // Allocate new FixedDoubleArray. + __ sll(scratch, t1, 2); + __ Addu(scratch, scratch, FixedDoubleArray::kHeaderSize); + __ AllocateInNewSpace(scratch, t2, t3, t5, &gc_required, NO_ALLOCATION_FLAGS); + // t2: destination FixedDoubleArray, not tagged as heap object + // Set destination FixedDoubleArray's length and map. + __ LoadRoot(t5, Heap::kFixedDoubleArrayMapRootIndex); + __ sw(t1, MemOperand(t2, FixedDoubleArray::kLengthOffset)); + __ sw(t5, MemOperand(t2, HeapObject::kMapOffset)); + // Update receiver's map. + + __ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); + __ RecordWriteField(a2, + HeapObject::kMapOffset, + a3, + t5, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created FixedDoubleArray. + __ Addu(a3, t2, Operand(kHeapObjectTag)); + __ sw(a3, FieldMemOperand(a2, JSObject::kElementsOffset)); + __ RecordWriteField(a2, + JSObject::kElementsOffset, + a3, + t5, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + + + // Prepare for conversion loop. + __ Addu(a3, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ Addu(t3, t2, Operand(FixedDoubleArray::kHeaderSize)); + __ sll(t2, t1, 2); + __ Addu(t2, t2, t3); + __ li(t0, Operand(kHoleNanLower32)); + __ li(t1, Operand(kHoleNanUpper32)); + // t0: kHoleNanLower32 + // t1: kHoleNanUpper32 + // t2: end of destination FixedDoubleArray, not tagged + // t3: begin of FixedDoubleArray element fields, not tagged + + if (!fpu_supported) __ Push(a1, a0); + + __ Branch(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ pop(ra); + __ Branch(fail); + + // Convert and copy elements. + __ bind(&loop); + __ lw(t5, MemOperand(a3)); + __ Addu(a3, a3, kIntSize); + // t5: current element + __ UntagAndJumpIfNotSmi(t5, t5, &convert_hole); + + // Normal smi, convert to double and store. + if (fpu_supported) { + CpuFeatures::Scope scope(FPU); + __ mtc1(t5, f0); + __ cvt_d_w(f0, f0); + __ sdc1(f0, MemOperand(t3)); + __ Addu(t3, t3, kDoubleSize); + } else { + FloatingPointHelper::ConvertIntToDouble(masm, + t5, + FloatingPointHelper::kCoreRegisters, + f0, + a0, + a1, + t7, + f0); + __ sw(a0, MemOperand(t3)); // mantissa + __ sw(a1, MemOperand(t3, kIntSize)); // exponent + __ Addu(t3, t3, kDoubleSize); + } + __ Branch(&entry); + + // Hole found, store the-hole NaN. + __ bind(&convert_hole); + if (FLAG_debug_code) { + // Restore a "smi-untagged" heap object. + __ SmiTag(t5); + __ Or(t5, t5, Operand(1)); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ Assert(eq, "object found in smi-only array", at, Operand(t5)); + } + __ sw(t0, MemOperand(t3)); // mantissa + __ sw(t1, MemOperand(t3, kIntSize)); // exponent + __ Addu(t3, t3, kDoubleSize); + + __ bind(&entry); + __ Branch(&loop, lt, t3, Operand(t2)); + + if (!fpu_supported) __ Pop(a1, a0); + __ pop(ra); +} + + +void ElementsTransitionGenerator::GenerateDoubleToObject( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- a0 : value + // -- a1 : key + // -- a2 : receiver + // -- ra : return address + // -- a3 : target map, scratch for subsequent call + // -- t0 : scratch (elements) + // ----------------------------------- + Label entry, loop, convert_hole, gc_required; + __ MultiPush(a0.bit() | a1.bit() | a2.bit() | a3.bit() | ra.bit()); + + __ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset)); + __ lw(t1, FieldMemOperand(t0, FixedArray::kLengthOffset)); + // t0: source FixedArray + // t1: number of elements (smi-tagged) + + // Allocate new FixedArray. + __ sll(a0, t1, 1); + __ Addu(a0, a0, FixedDoubleArray::kHeaderSize); + __ AllocateInNewSpace(a0, t2, t3, t5, &gc_required, NO_ALLOCATION_FLAGS); + // t2: destination FixedArray, not tagged as heap object + // Set destination FixedDoubleArray's length and map. + __ LoadRoot(t5, Heap::kFixedArrayMapRootIndex); + __ sw(t1, MemOperand(t2, FixedDoubleArray::kLengthOffset)); + __ sw(t5, MemOperand(t2, HeapObject::kMapOffset)); + + // Prepare for conversion loop. + __ Addu(t0, t0, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag + 4)); + __ Addu(a3, t2, Operand(FixedArray::kHeaderSize)); + __ Addu(t2, t2, Operand(kHeapObjectTag)); + __ sll(t1, t1, 1); + __ Addu(t1, a3, t1); + __ LoadRoot(t3, Heap::kTheHoleValueRootIndex); + __ LoadRoot(t5, Heap::kHeapNumberMapRootIndex); + // Using offsetted addresses. + // a3: begin of destination FixedArray element fields, not tagged + // t0: begin of source FixedDoubleArray element fields, not tagged, +4 + // t1: end of destination FixedArray, not tagged + // t2: destination FixedArray + // t3: the-hole pointer + // t5: heap number map + __ Branch(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ MultiPop(a0.bit() | a1.bit() | a2.bit() | a3.bit() | ra.bit()); + + __ Branch(fail); + + __ bind(&loop); + __ lw(a1, MemOperand(t0)); + __ Addu(t0, t0, kDoubleSize); + // a1: current element's upper 32 bit + // t0: address of next element's upper 32 bit + __ Branch(&convert_hole, eq, a1, Operand(kHoleNanUpper32)); + + // Non-hole double, copy value into a heap number. + __ AllocateHeapNumber(a2, a0, t6, t5, &gc_required); + // a2: new heap number + __ lw(a0, MemOperand(t0, -12)); + __ sw(a0, FieldMemOperand(a2, HeapNumber::kMantissaOffset)); + __ sw(a1, FieldMemOperand(a2, HeapNumber::kExponentOffset)); + __ mov(a0, a3); + __ sw(a2, MemOperand(a3)); + __ Addu(a3, a3, kIntSize); + __ RecordWrite(t2, + a0, + a2, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ Branch(&entry); + + // Replace the-hole NaN with the-hole pointer. + __ bind(&convert_hole); + __ sw(t3, MemOperand(a3)); + __ Addu(a3, a3, kIntSize); + + __ bind(&entry); + __ Branch(&loop, lt, a3, Operand(t1)); + + __ MultiPop(a2.bit() | a3.bit() | a0.bit() | a1.bit()); + // Update receiver's map. + __ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); + __ RecordWriteField(a2, + HeapObject::kMapOffset, + a3, + t5, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created and filled FixedArray. + __ sw(t2, FieldMemOperand(a2, JSObject::kElementsOffset)); + __ RecordWriteField(a2, + JSObject::kElementsOffset, + t2, + t5, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ pop(ra); +} + + +void StringCharLoadGenerator::Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime) { + // Fetch the instance type of the receiver into result register. + __ lw(result, FieldMemOperand(string, HeapObject::kMapOffset)); + __ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for indirect strings. + Label check_sequential; + __ And(at, result, Operand(kIsIndirectStringMask)); + __ Branch(&check_sequential, eq, at, Operand(zero_reg)); + + // Dispatch on the indirect string shape: slice or cons. + Label cons_string; + __ And(at, result, Operand(kSlicedNotConsMask)); + __ Branch(&cons_string, eq, at, Operand(zero_reg)); + + // Handle slices. + Label indirect_string_loaded; + __ lw(result, FieldMemOperand(string, SlicedString::kOffsetOffset)); + __ lw(string, FieldMemOperand(string, SlicedString::kParentOffset)); + __ sra(at, result, kSmiTagSize); + __ Addu(index, index, at); + __ jmp(&indirect_string_loaded); + + // Handle cons strings. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ bind(&cons_string); + __ lw(result, FieldMemOperand(string, ConsString::kSecondOffset)); + __ LoadRoot(at, Heap::kEmptyStringRootIndex); + __ Branch(call_runtime, ne, result, Operand(at)); + // Get the first of the two strings and load its instance type. + __ lw(string, FieldMemOperand(string, ConsString::kFirstOffset)); + + __ bind(&indirect_string_loaded); + __ lw(result, FieldMemOperand(string, HeapObject::kMapOffset)); + __ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); + + // Distinguish sequential and external strings. Only these two string + // representations can reach here (slices and flat cons strings have been + // reduced to the underlying sequential or external string). + Label external_string, check_encoding; + __ bind(&check_sequential); + STATIC_ASSERT(kSeqStringTag == 0); + __ And(at, result, Operand(kStringRepresentationMask)); + __ Branch(&external_string, ne, at, Operand(zero_reg)); + + // Prepare sequential strings + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ Addu(string, + string, + SeqTwoByteString::kHeaderSize - kHeapObjectTag); + __ jmp(&check_encoding); + + // Handle external strings. + __ bind(&external_string); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ And(at, result, Operand(kIsIndirectStringMask)); + __ Assert(eq, "external string expected, but not found", + at, Operand(zero_reg)); + } + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ And(at, result, Operand(kShortExternalStringMask)); + __ Branch(call_runtime, ne, at, Operand(zero_reg)); + __ lw(string, FieldMemOperand(string, ExternalString::kResourceDataOffset)); + + Label ascii, done; + __ bind(&check_encoding); + STATIC_ASSERT(kTwoByteStringTag == 0); + __ And(at, result, Operand(kStringEncodingMask)); + __ Branch(&ascii, ne, at, Operand(zero_reg)); + // Two-byte string. + __ sll(at, index, 1); + __ Addu(at, string, at); + __ lhu(result, MemOperand(at)); + __ jmp(&done); + __ bind(&ascii); + // Ascii string. + __ Addu(at, string, index); + __ lbu(result, MemOperand(at)); + __ bind(&done); } +#undef __ } } // namespace v8::internal diff --git a/deps/v8/src/mips/codegen-mips.h b/deps/v8/src/mips/codegen-mips.h index a8de9c8610..e704c4f56c 100644 --- a/deps/v8/src/mips/codegen-mips.h +++ b/deps/v8/src/mips/codegen-mips.h @@ -31,7 +31,6 @@ #include "ast.h" -#include "code-stubs-mips.h" #include "ic-inl.h" namespace v8 { @@ -71,26 +70,26 @@ class CodeGenerator: public AstVisitor { int pos, bool right_here = false); - // Constants related to patching of inlined load/store. - static int GetInlinedKeyedLoadInstructionsAfterPatch() { - // This is in correlation with the padding in MacroAssembler::Abort. - return FLAG_debug_code ? 45 : 20; - } + private: + DISALLOW_COPY_AND_ASSIGN(CodeGenerator); +}; - static const int kInlinedKeyedStoreInstructionsAfterPatch = 13; - static int GetInlinedNamedStoreInstructionsAfterPatch() { - ASSERT(Isolate::Current()->inlined_write_barrier_size() != -1); - // Magic number 5: instruction count after patched map load: - // li: 2 (liu & ori), Branch : 2 (bne & nop), sw : 1 - return Isolate::Current()->inlined_write_barrier_size() + 5; - } +class StringCharLoadGenerator : public AllStatic { + public: + // Generates the code for handling different string types and loading the + // indexed character into |result|. We expect |index| as untagged input and + // |result| as untagged output. + static void Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime); private: - DISALLOW_COPY_AND_ASSIGN(CodeGenerator); + DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); }; - } } // namespace v8::internal #endif // V8_MIPS_CODEGEN_MIPS_H_ diff --git a/deps/v8/src/mips/constants-mips.cc b/deps/v8/src/mips/constants-mips.cc index d0a7af5c35..7d654f6d62 100644 --- a/deps/v8/src/mips/constants-mips.cc +++ b/deps/v8/src/mips/constants-mips.cc @@ -302,7 +302,7 @@ Instruction::Type Instruction::InstructionType() const { return kRegisterType; }; break; - // 16 bits Immediate type instructions. eg: addi dest, src, imm16. + // 16 bits Immediate type instructions. e.g.: addi dest, src, imm16. case REGIMM: case BEQ: case BNE: @@ -337,7 +337,7 @@ Instruction::Type Instruction::InstructionType() const { case SWC1: case SDC1: return kImmediateType; - // 26 bits immediate type instructions. eg: j imm26. + // 26 bits immediate type instructions. e.g.: j imm26. case J: case JAL: return kJumpType; diff --git a/deps/v8/src/mips/constants-mips.h b/deps/v8/src/mips/constants-mips.h index d76ae59ff6..d62a8901f0 100644 --- a/deps/v8/src/mips/constants-mips.h +++ b/deps/v8/src/mips/constants-mips.h @@ -50,13 +50,13 @@ #if(defined(__mips_hard_float) && __mips_hard_float != 0) // Use floating-point coprocessor instructions. This flag is raised when // -mhard-float is passed to the compiler. -static const bool IsMipsSoftFloatABI = false; +const bool IsMipsSoftFloatABI = false; #elif(defined(__mips_soft_float) && __mips_soft_float != 0) // Not using floating-point coprocessor instructions. This flag is raised when // -msoft-float is passed to the compiler. -static const bool IsMipsSoftFloatABI = true; +const bool IsMipsSoftFloatABI = true; #else -static const bool IsMipsSoftFloatABI = true; +const bool IsMipsSoftFloatABI = true; #endif @@ -74,46 +74,45 @@ namespace internal { // Registers and FPURegisters. // Number of general purpose registers. -static const int kNumRegisters = 32; -static const int kInvalidRegister = -1; +const int kNumRegisters = 32; +const int kInvalidRegister = -1; // Number of registers with HI, LO, and pc. -static const int kNumSimuRegisters = 35; +const int kNumSimuRegisters = 35; // In the simulator, the PC register is simulated as the 34th register. -static const int kPCRegister = 34; +const int kPCRegister = 34; // Number coprocessor registers. -static const int kNumFPURegisters = 32; -static const int kInvalidFPURegister = -1; +const int kNumFPURegisters = 32; +const int kInvalidFPURegister = -1; // FPU (coprocessor 1) control registers. Currently only FCSR is implemented. -static const int kFCSRRegister = 31; -static const int kInvalidFPUControlRegister = -1; -static const uint32_t kFPUInvalidResult = (uint32_t) (1 << 31) - 1; +const int kFCSRRegister = 31; +const int kInvalidFPUControlRegister = -1; +const uint32_t kFPUInvalidResult = (uint32_t) (1 << 31) - 1; // FCSR constants. -static const uint32_t kFCSRInexactFlagBit = 2; -static const uint32_t kFCSRUnderflowFlagBit = 3; -static const uint32_t kFCSROverflowFlagBit = 4; -static const uint32_t kFCSRDivideByZeroFlagBit = 5; -static const uint32_t kFCSRInvalidOpFlagBit = 6; - -static const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit; -static const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit; -static const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit; -static const uint32_t kFCSRDivideByZeroFlagMask = 1 << kFCSRDivideByZeroFlagBit; -static const uint32_t kFCSRInvalidOpFlagMask = 1 << kFCSRInvalidOpFlagBit; - -static const uint32_t kFCSRFlagMask = +const uint32_t kFCSRInexactFlagBit = 2; +const uint32_t kFCSRUnderflowFlagBit = 3; +const uint32_t kFCSROverflowFlagBit = 4; +const uint32_t kFCSRDivideByZeroFlagBit = 5; +const uint32_t kFCSRInvalidOpFlagBit = 6; + +const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit; +const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit; +const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit; +const uint32_t kFCSRDivideByZeroFlagMask = 1 << kFCSRDivideByZeroFlagBit; +const uint32_t kFCSRInvalidOpFlagMask = 1 << kFCSRInvalidOpFlagBit; + +const uint32_t kFCSRFlagMask = kFCSRInexactFlagMask | kFCSRUnderflowFlagMask | kFCSROverflowFlagMask | kFCSRDivideByZeroFlagMask | kFCSRInvalidOpFlagMask; -static const uint32_t kFCSRExceptionFlagMask = - kFCSRFlagMask ^ kFCSRInexactFlagMask; +const uint32_t kFCSRExceptionFlagMask = kFCSRFlagMask ^ kFCSRInexactFlagMask; // Helper functions for converting between register numbers and names. class Registers { @@ -126,7 +125,7 @@ class Registers { struct RegisterAlias { int reg; - const char *name; + const char* name; }; static const int32_t kMaxValue = 0x7fffffff; @@ -148,7 +147,7 @@ class FPURegisters { struct RegisterAlias { int creg; - const char *name; + const char* name; }; private: @@ -177,67 +176,66 @@ enum SoftwareInterruptCodes { // instructions (see Assembler::stop()). // - Breaks larger than kMaxStopCode are simple breaks, dropping you into the // debugger. -static const uint32_t kMaxWatchpointCode = 31; -static const uint32_t kMaxStopCode = 127; +const uint32_t kMaxWatchpointCode = 31; +const uint32_t kMaxStopCode = 127; STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode); // ----- Fields offset and length. -static const int kOpcodeShift = 26; -static const int kOpcodeBits = 6; -static const int kRsShift = 21; -static const int kRsBits = 5; -static const int kRtShift = 16; -static const int kRtBits = 5; -static const int kRdShift = 11; -static const int kRdBits = 5; -static const int kSaShift = 6; -static const int kSaBits = 5; -static const int kFunctionShift = 0; -static const int kFunctionBits = 6; -static const int kLuiShift = 16; - -static const int kImm16Shift = 0; -static const int kImm16Bits = 16; -static const int kImm26Shift = 0; -static const int kImm26Bits = 26; -static const int kImm28Shift = 0; -static const int kImm28Bits = 28; +const int kOpcodeShift = 26; +const int kOpcodeBits = 6; +const int kRsShift = 21; +const int kRsBits = 5; +const int kRtShift = 16; +const int kRtBits = 5; +const int kRdShift = 11; +const int kRdBits = 5; +const int kSaShift = 6; +const int kSaBits = 5; +const int kFunctionShift = 0; +const int kFunctionBits = 6; +const int kLuiShift = 16; + +const int kImm16Shift = 0; +const int kImm16Bits = 16; +const int kImm26Shift = 0; +const int kImm26Bits = 26; +const int kImm28Shift = 0; +const int kImm28Bits = 28; // In branches and jumps immediate fields point to words, not bytes, // and are therefore shifted by 2. -static const int kImmFieldShift = 2; - -static const int kFsShift = 11; -static const int kFsBits = 5; -static const int kFtShift = 16; -static const int kFtBits = 5; -static const int kFdShift = 6; -static const int kFdBits = 5; -static const int kFCccShift = 8; -static const int kFCccBits = 3; -static const int kFBccShift = 18; -static const int kFBccBits = 3; -static const int kFBtrueShift = 16; -static const int kFBtrueBits = 1; +const int kImmFieldShift = 2; + +const int kFsShift = 11; +const int kFsBits = 5; +const int kFtShift = 16; +const int kFtBits = 5; +const int kFdShift = 6; +const int kFdBits = 5; +const int kFCccShift = 8; +const int kFCccBits = 3; +const int kFBccShift = 18; +const int kFBccBits = 3; +const int kFBtrueShift = 16; +const int kFBtrueBits = 1; // ----- Miscellaneous useful masks. // Instruction bit masks. -static const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift; -static const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift; -static const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift; -static const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift; -static const int kRsFieldMask = ((1 << kRsBits) - 1) << kRsShift; -static const int kRtFieldMask = ((1 << kRtBits) - 1) << kRtShift; -static const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; -static const int kSaFieldMask = ((1 << kSaBits) - 1) << kSaShift; -static const int kFunctionFieldMask = - ((1 << kFunctionBits) - 1) << kFunctionShift; +const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift; +const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift; +const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift; +const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift; +const int kRsFieldMask = ((1 << kRsBits) - 1) << kRsShift; +const int kRtFieldMask = ((1 << kRtBits) - 1) << kRtShift; +const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; +const int kSaFieldMask = ((1 << kSaBits) - 1) << kSaShift; +const int kFunctionFieldMask = ((1 << kFunctionBits) - 1) << kFunctionShift; // Misc masks. -static const int kHiMask = 0xffff << 16; -static const int kLoMask = 0xffff; -static const int kSignMask = 0x80000000; -static const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1; +const int kHiMask = 0xffff << 16; +const int kLoMask = 0xffff; +const int kSignMask = 0x80000000; +const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1; // ----- MIPS Opcodes and Function Fields. // We use this presentation to stay close to the table representation in @@ -529,7 +527,7 @@ enum FPURoundingMode { kRoundToMinusInf = RM }; -static const uint32_t kFPURoundingModeMask = 3 << 0; +const uint32_t kFPURoundingModeMask = 3 << 0; enum CheckForInexactConversion { kCheckForInexactConversion, @@ -749,7 +747,7 @@ class Instruction { // Say if the instruction should not be used in a branch delay slot. bool IsForbiddenInBranchDelay() const; - // Say if the instruction 'links'. eg: jal, bal. + // Say if the instruction 'links'. e.g. jal, bal. bool IsLinkingInstruction() const; // Say if the instruction is a break or a trap. bool IsTrap() const; @@ -772,18 +770,18 @@ class Instruction { // MIPS assembly various constants. // C/C++ argument slots size. -static const int kCArgSlotCount = 4; -static const int kCArgsSlotsSize = kCArgSlotCount * Instruction::kInstrSize; +const int kCArgSlotCount = 4; +const int kCArgsSlotsSize = kCArgSlotCount * Instruction::kInstrSize; // JS argument slots size. -static const int kJSArgsSlotsSize = 0 * Instruction::kInstrSize; +const int kJSArgsSlotsSize = 0 * Instruction::kInstrSize; // Assembly builtins argument slots size. -static const int kBArgsSlotsSize = 0 * Instruction::kInstrSize; +const int kBArgsSlotsSize = 0 * Instruction::kInstrSize; -static const int kBranchReturnOffset = 2 * Instruction::kInstrSize; +const int kBranchReturnOffset = 2 * Instruction::kInstrSize; -static const int kDoubleAlignmentBits = 3; -static const int kDoubleAlignment = (1 << kDoubleAlignmentBits); -static const int kDoubleAlignmentMask = kDoubleAlignment - 1; +const int kDoubleAlignmentBits = 3; +const int kDoubleAlignment = (1 << kDoubleAlignmentBits); +const int kDoubleAlignmentMask = kDoubleAlignment - 1; } } // namespace v8::internal diff --git a/deps/v8/src/mips/cpu-mips.cc b/deps/v8/src/mips/cpu-mips.cc index 26e95fb24c..93ebeda800 100644 --- a/deps/v8/src/mips/cpu-mips.cc +++ b/deps/v8/src/mips/cpu-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -47,7 +47,7 @@ namespace v8 { namespace internal { -void CPU::Setup() { +void CPU::SetUp() { CpuFeatures::Probe(); } @@ -64,15 +64,19 @@ void CPU::FlushICache(void* start, size_t size) { } #if !defined (USE_SIMULATOR) +#if defined(ANDROID) + // Bionic cacheflush can typically run in userland, avoiding kernel call. + char *end = reinterpret_cast<char *>(start) + size; + cacheflush( + reinterpret_cast<intptr_t>(start), reinterpret_cast<intptr_t>(end), 0); +#else // ANDROID int res; - // See http://www.linux-mips.org/wiki/Cacheflush_Syscall. res = syscall(__NR_cacheflush, start, size, ICACHE); - if (res) { V8_Fatal(__FILE__, __LINE__, "Failed to flush the instruction cache"); } - +#endif // ANDROID #else // USE_SIMULATOR. // Not generating mips instructions for C-code. This means that we are // building a mips emulator based target. We should notify the simulator diff --git a/deps/v8/src/mips/debug-mips.cc b/deps/v8/src/mips/debug-mips.cc index e323c505e4..26b343c873 100644 --- a/deps/v8/src/mips/debug-mips.cc +++ b/deps/v8/src/mips/debug-mips.cc @@ -124,55 +124,58 @@ void BreakLocationIterator::ClearDebugBreakAtSlot() { static void Generate_DebugBreakCallHelper(MacroAssembler* masm, RegList object_regs, RegList non_object_regs) { - __ EnterInternalFrame(); - - // Store the registers containing live values on the expression stack to - // make sure that these are correctly updated during GC. Non object values - // are stored as a smi causing it to be untouched by GC. - ASSERT((object_regs & ~kJSCallerSaved) == 0); - ASSERT((non_object_regs & ~kJSCallerSaved) == 0); - ASSERT((object_regs & non_object_regs) == 0); - if ((object_regs | non_object_regs) != 0) { - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if ((non_object_regs & (1 << r)) != 0) { - if (FLAG_debug_code) { - __ And(at, reg, 0xc0000000); - __ Assert(eq, "Unable to encode value as smi", at, Operand(zero_reg)); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Store the registers containing live values on the expression stack to + // make sure that these are correctly updated during GC. Non object values + // are stored as a smi causing it to be untouched by GC. + ASSERT((object_regs & ~kJSCallerSaved) == 0); + ASSERT((non_object_regs & ~kJSCallerSaved) == 0); + ASSERT((object_regs & non_object_regs) == 0); + if ((object_regs | non_object_regs) != 0) { + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if ((non_object_regs & (1 << r)) != 0) { + if (FLAG_debug_code) { + __ And(at, reg, 0xc0000000); + __ Assert( + eq, "Unable to encode value as smi", at, Operand(zero_reg)); + } + __ sll(reg, reg, kSmiTagSize); } - __ sll(reg, reg, kSmiTagSize); } + __ MultiPush(object_regs | non_object_regs); } - __ MultiPush(object_regs | non_object_regs); - } #ifdef DEBUG - __ RecordComment("// Calling from debug break to runtime - come in - over"); + __ RecordComment("// Calling from debug break to runtime - come in - over"); #endif - __ mov(a0, zero_reg); // No arguments. - __ li(a1, Operand(ExternalReference::debug_break(masm->isolate()))); - - CEntryStub ceb(1); - __ CallStub(&ceb); - - // Restore the register values from the expression stack. - if ((object_regs | non_object_regs) != 0) { - __ MultiPop(object_regs | non_object_regs); - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if ((non_object_regs & (1 << r)) != 0) { - __ srl(reg, reg, kSmiTagSize); - } - if (FLAG_debug_code && - (((object_regs |non_object_regs) & (1 << r)) == 0)) { - __ li(reg, kDebugZapValue); + __ mov(a0, zero_reg); // No arguments. + __ li(a1, Operand(ExternalReference::debug_break(masm->isolate()))); + + CEntryStub ceb(1); + __ CallStub(&ceb); + + // Restore the register values from the expression stack. + if ((object_regs | non_object_regs) != 0) { + __ MultiPop(object_regs | non_object_regs); + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if ((non_object_regs & (1 << r)) != 0) { + __ srl(reg, reg, kSmiTagSize); + } + if (FLAG_debug_code && + (((object_regs |non_object_regs) & (1 << r)) == 0)) { + __ li(reg, kDebugZapValue); + } } } - } - __ LeaveInternalFrame(); + // Leave the internal frame. + } // Now that the break point has been handled, resume normal execution by // jumping to the target address intended by the caller and that was @@ -240,14 +243,6 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { - // Calling convention for construct call (from builtins-mips.cc). - // -- a0 : number of arguments (not smi) - // -- a1 : constructor function - Generate_DebugBreakCallHelper(masm, a1.bit(), a0.bit()); -} - - void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that v0 is TOS which // is an object - this is not generally the case so this should be used with @@ -256,11 +251,43 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { +void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-mips.cc). // ----------- S t a t e ------------- - // No registers used on entry. + // -- a1 : function // ----------------------------------- - Generate_DebugBreakCallHelper(masm, 0, 0); + Generate_DebugBreakCallHelper(masm, a1.bit(), 0); +} + + +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-mips.cc). + // ----------- S t a t e ------------- + // -- a1 : function + // -- a2 : cache cell for call target + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), 0); +} + + +void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { + // Calling convention for CallConstructStub (from code-stubs-mips.cc). + // ----------- S t a t e ------------- + // -- a0 : number of arguments (not smi) + // -- a1 : constructor function + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit()); +} + + +void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { + // Calling convention for CallConstructStub (from code-stubs-mips.cc). + // ----------- S t a t e ------------- + // -- a0 : number of arguments (not smi) + // -- a1 : constructor function + // -- a2 : cache cell for call target + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), a0.bit()); } diff --git a/deps/v8/src/mips/deoptimizer-mips.cc b/deps/v8/src/mips/deoptimizer-mips.cc index 18b6231999..26a406333f 100644 --- a/deps/v8/src/mips/deoptimizer-mips.cc +++ b/deps/v8/src/mips/deoptimizer-mips.cc @@ -32,65 +32,842 @@ #include "full-codegen.h" #include "safepoint-table.h" -// Note: this file was taken from the X64 version. ARM has a partially working -// lithium implementation, but for now it is not ported to mips. - namespace v8 { namespace internal { -const int Deoptimizer::table_entry_size_ = 10; +const int Deoptimizer::table_entry_size_ = 32; int Deoptimizer::patch_size() { - const int kCallInstructionSizeInWords = 3; + const int kCallInstructionSizeInWords = 4; return kCallInstructionSizeInWords * Assembler::kInstrSize; } void Deoptimizer::DeoptimizeFunction(JSFunction* function) { - UNIMPLEMENTED(); + HandleScope scope; + AssertNoAllocation no_allocation; + + if (!function->IsOptimized()) return; + + // Get the optimized code. + Code* code = function->code(); + Address code_start_address = code->instruction_start(); + + // Invalidate the relocation information, as it will become invalid by the + // code patching below, and is not needed any more. + code->InvalidateRelocation(); + + // For each LLazyBailout instruction insert a call to the corresponding + // deoptimization entry. + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); +#ifdef DEBUG + Address prev_call_address = NULL; +#endif + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + if (deopt_data->Pc(i)->value() == -1) continue; + Address call_address = code_start_address + deopt_data->Pc(i)->value(); + Address deopt_entry = GetDeoptimizationEntry(i, LAZY); + int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry, + RelocInfo::NONE); + int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize; + ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0); + ASSERT(call_size_in_bytes <= patch_size()); + CodePatcher patcher(call_address, call_size_in_words); + patcher.masm()->Call(deopt_entry, RelocInfo::NONE); + ASSERT(prev_call_address == NULL || + call_address >= prev_call_address + patch_size()); + ASSERT(call_address + patch_size() <= code->instruction_end()); + +#ifdef DEBUG + prev_call_address = call_address; +#endif + } + + Isolate* isolate = code->GetIsolate(); + + // Add the deoptimizing code to the list. + DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); + DeoptimizerData* data = isolate->deoptimizer_data(); + node->set_next(data->deoptimizing_code_list_); + data->deoptimizing_code_list_ = node; + + // We might be in the middle of incremental marking with compaction. + // Tell collector to treat this code object in a special way and + // ignore all slots that might have been recorded on it. + isolate->heap()->mark_compact_collector()->InvalidateCode(code); + + // Set the code for the function to non-optimized version. + function->ReplaceCode(function->shared()->code()); + + if (FLAG_trace_deopt) { + PrintF("[forced deoptimization: "); + function->PrintName(); + PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function)); +#ifdef DEBUG + if (FLAG_print_code) { + code->PrintLn(); + } +#endif + } } -void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, +void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { - UNIMPLEMENTED(); + const int kInstrSize = Assembler::kInstrSize; + // This structure comes from FullCodeGenerator::EmitStackCheck. + // The call of the stack guard check has the following form: + // sltu at, sp, t0 + // beq at, zero_reg, ok + // lui t9, <stack guard address> upper + // ori t9, <stack guard address> lower + // jalr t9 + // nop + // ----- pc_after points here + + ASSERT(Assembler::IsBeq(Assembler::instr_at(pc_after - 5 * kInstrSize))); + + // Replace the sltu instruction with load-imm 1 to at, so beq is not taken. + CodePatcher patcher(pc_after - 6 * kInstrSize, 1); + patcher.masm()->addiu(at, zero_reg, 1); + + // Replace the stack check address in the load-immediate (lui/ori pair) + // with the entry address of the replacement code. + ASSERT(reinterpret_cast<uint32_t>( + Assembler::target_address_at(pc_after - 4 * kInstrSize)) == + reinterpret_cast<uint32_t>(check_code->entry())); + Assembler::set_target_address_at(pc_after - 4 * kInstrSize, + replacement_code->entry()); + + // We patched the code to the following form: + // addiu at, zero_reg, 1 + // beq at, zero_reg, ok ;; Not changed + // lui t9, <on-stack replacement address> upper + // ori t9, <on-stack replacement address> lower + // jalr t9 ;; Not changed + // nop ;; Not changed + // ----- pc_after points here + + unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, pc_after - 4 * kInstrSize, replacement_code); } -void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, +void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { - UNIMPLEMENTED(); + // Exact opposite of the function above. + const int kInstrSize = Assembler::kInstrSize; + ASSERT(Assembler::IsAddImmediate( + Assembler::instr_at(pc_after - 6 * kInstrSize))); + ASSERT(Assembler::IsBeq(Assembler::instr_at(pc_after - 5 * kInstrSize))); + + // Restore the sltu instruction so beq can be taken again. + CodePatcher patcher(pc_after - 6 * kInstrSize, 1); + patcher.masm()->sltu(at, sp, t0); + + // Replace the on-stack replacement address in the load-immediate (lui/ori + // pair) with the entry address of the normal stack-check code. + ASSERT(reinterpret_cast<uint32_t>( + Assembler::target_address_at(pc_after - 4 * kInstrSize)) == + reinterpret_cast<uint32_t>(replacement_code->entry())); + Assembler::set_target_address_at(pc_after - 4 * kInstrSize, + check_code->entry()); + + check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, pc_after - 4 * kInstrSize, check_code); +} + + +static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) { + ByteArray* translations = data->TranslationByteArray(); + int length = data->DeoptCount(); + for (int i = 0; i < length; i++) { + if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) { + TranslationIterator it(translations, data->TranslationIndex(i)->value()); + int value = it.Next(); + ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value)); + // Read the number of frames. + value = it.Next(); + if (value == 1) return i; + } + } + UNREACHABLE(); + return -1; } void Deoptimizer::DoComputeOsrOutputFrame() { - UNIMPLEMENTED(); + DeoptimizationInputData* data = DeoptimizationInputData::cast( + optimized_code_->deoptimization_data()); + unsigned ast_id = data->OsrAstId()->value(); + + int bailout_id = LookupBailoutId(data, ast_id); + unsigned translation_index = data->TranslationIndex(bailout_id)->value(); + ByteArray* translations = data->TranslationByteArray(); + + TranslationIterator iterator(translations, translation_index); + Translation::Opcode opcode = + static_cast<Translation::Opcode>(iterator.Next()); + ASSERT(Translation::BEGIN == opcode); + USE(opcode); + int count = iterator.Next(); + iterator.Skip(1); // Drop JS frame count. + ASSERT(count == 1); + USE(count); + + opcode = static_cast<Translation::Opcode>(iterator.Next()); + USE(opcode); + ASSERT(Translation::JS_FRAME == opcode); + unsigned node_id = iterator.Next(); + USE(node_id); + ASSERT(node_id == ast_id); + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator.Next())); + USE(function); + ASSERT(function == function_); + unsigned height = iterator.Next(); + unsigned height_in_bytes = height * kPointerSize; + USE(height_in_bytes); + + unsigned fixed_size = ComputeFixedSize(function_); + unsigned input_frame_size = input_->GetFrameSize(); + ASSERT(fixed_size + height_in_bytes == input_frame_size); + + unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize; + unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value(); + unsigned outgoing_size = outgoing_height * kPointerSize; + unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size; + ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call. + + if (FLAG_trace_osr) { + PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ", + reinterpret_cast<intptr_t>(function_)); + function_->PrintName(); + PrintF(" => node=%u, frame=%d->%d]\n", + ast_id, + input_frame_size, + output_frame_size); + } + + // There's only one output frame in the OSR case. + output_count_ = 1; + output_ = new FrameDescription*[1]; + output_[0] = new(output_frame_size) FrameDescription( + output_frame_size, function_); + output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); + + // Clear the incoming parameters in the optimized frame to avoid + // confusing the garbage collector. + unsigned output_offset = output_frame_size - kPointerSize; + int parameter_count = function_->shared()->formal_parameter_count() + 1; + for (int i = 0; i < parameter_count; ++i) { + output_[0]->SetFrameSlot(output_offset, 0); + output_offset -= kPointerSize; + } + + // Translate the incoming parameters. This may overwrite some of the + // incoming argument slots we've just cleared. + int input_offset = input_frame_size - kPointerSize; + bool ok = true; + int limit = input_offset - (parameter_count * kPointerSize); + while (ok && input_offset > limit) { + ok = DoOsrTranslateCommand(&iterator, &input_offset); + } + + // There are no translation commands for the caller's pc and fp, the + // context, and the function. Set them up explicitly. + for (int i = StandardFrameConstants::kCallerPCOffset; + ok && i >= StandardFrameConstants::kMarkerOffset; + i -= kPointerSize) { + uint32_t input_value = input_->GetFrameSlot(input_offset); + if (FLAG_trace_osr) { + const char* name = "UNKNOWN"; + switch (i) { + case StandardFrameConstants::kCallerPCOffset: + name = "caller's pc"; + break; + case StandardFrameConstants::kCallerFPOffset: + name = "fp"; + break; + case StandardFrameConstants::kContextOffset: + name = "context"; + break; + case StandardFrameConstants::kMarkerOffset: + name = "function"; + break; + } + PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n", + output_offset, + input_value, + input_offset, + name); + } + + output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset)); + input_offset -= kPointerSize; + output_offset -= kPointerSize; + } + + // Translate the rest of the frame. + while (ok && input_offset >= 0) { + ok = DoOsrTranslateCommand(&iterator, &input_offset); + } + + // If translation of any command failed, continue using the input frame. + if (!ok) { + delete output_[0]; + output_[0] = input_; + output_[0]->SetPc(reinterpret_cast<uint32_t>(from_)); + } else { + // Set up the frame pointer and the context pointer. + output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code())); + output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code())); + + unsigned pc_offset = data->OsrPcOffset()->value(); + uint32_t pc = reinterpret_cast<uint32_t>( + optimized_code_->entry() + pc_offset); + output_[0]->SetPc(pc); + } + Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR); + output_[0]->SetContinuation( + reinterpret_cast<uint32_t>(continuation->entry())); + + if (FLAG_trace_osr) { + PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", + ok ? "finished" : "aborted", + reinterpret_cast<intptr_t>(function)); + function->PrintName(); + PrintF(" => pc=0x%0x]\n", output_[0]->GetPc()); + } } -void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, - int frame_index) { - UNIMPLEMENTED(); +void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; + unsigned input_frame_size = input_->GetFrameSize(); + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, function); + output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR); + + // Arguments adaptor can not be topmost or bottommost. + ASSERT(frame_index > 0 && frame_index < output_count_ - 1); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address of the frame is computed from the previous + // frame's top and this frame's size. + uint32_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + unsigned input_offset = input_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + input_offset -= (parameter_count * kPointerSize); + + // Read caller's PC from the previous frame. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t callers_pc = output_[frame_index - 1]->GetPc(); + output_frame->SetFrameSlot(output_offset, callers_pc); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n", + top_address + output_offset, output_offset, callers_pc); + } + + // Read caller's FP from the previous frame, and set this frame's FP. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t value = output_[frame_index - 1]->GetFp(); + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + output_frame->SetFp(fp_value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n", + fp_value, output_offset, value); + } + + // A marker value is used in place of the context. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t context = reinterpret_cast<intptr_t>( + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); + output_frame->SetFrameSlot(output_offset, context); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n", + top_address + output_offset, output_offset, context); + } + + // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(function); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* adaptor_trampoline = + builtins->builtin(Builtins::kArgumentsAdaptorTrampoline); + uint32_t pc = reinterpret_cast<uint32_t>( + adaptor_trampoline->instruction_start() + + isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value()); + output_frame->SetPc(pc); } +// This code is very similar to ia32/arm code, but relies on register names +// (fp, sp) and how the frame is laid out. +void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, + int frame_index) { + // Read the ast node id, function, and frame height for this output frame. + int node_id = iterator->Next(); + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating "); + function->PrintName(); + PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes); + } + + // The 'fixed' part of the frame consists of the incoming parameters and + // the part described by JavaScriptFrameConstants. + unsigned fixed_frame_size = ComputeFixedSize(function); + unsigned input_frame_size = input_->GetFrameSize(); + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, function); + output_frame->SetFrameType(StackFrame::JAVA_SCRIPT); + + bool is_bottommost = (0 == frame_index); + bool is_topmost = (output_count_ - 1 == frame_index); + ASSERT(frame_index >= 0 && frame_index < output_count_); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address for the bottommost output frame can be computed from + // the input frame pointer and the output frame's height. For all + // subsequent output frames, it can be computed from the previous one's + // top address and the current frame's size. + uint32_t top_address; + if (is_bottommost) { + // 2 = context and function in the frame. + top_address = + input_->GetRegister(fp.code()) - (2 * kPointerSize) - height_in_bytes; + } else { + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + } + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = function->shared()->formal_parameter_count() + 1; + unsigned output_offset = output_frame_size; + unsigned input_offset = input_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + input_offset -= (parameter_count * kPointerSize); + + // There are no translation commands for the caller's pc and fp, the + // context, and the function. Synthesize their values and set them up + // explicitly. + // + // The caller's pc for the bottommost output frame is the same as in the + // input frame. For all subsequent output frames, it can be read from the + // previous one. This frame's pc can be computed from the non-optimized + // function code and AST id of the bailout. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t value; + if (is_bottommost) { + value = input_->GetFrameSlot(input_offset); + } else { + value = output_[frame_index - 1]->GetPc(); + } + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n", + top_address + output_offset, output_offset, value); + } + + // The caller's frame pointer for the bottommost output frame is the same + // as in the input frame. For all subsequent output frames, it can be + // read from the previous one. Also compute and set this frame's frame + // pointer. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + if (is_bottommost) { + value = input_->GetFrameSlot(input_offset); + } else { + value = output_[frame_index - 1]->GetFp(); + } + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + ASSERT(!is_bottommost || input_->GetRegister(fp.code()) == fp_value); + output_frame->SetFp(fp_value); + if (is_topmost) { + output_frame->SetRegister(fp.code(), fp_value); + } + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n", + fp_value, output_offset, value); + } + + // For the bottommost output frame the context can be gotten from the input + // frame. For all subsequent output frames it can be gotten from the function + // so long as we don't inline functions that need local contexts. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + if (is_bottommost) { + value = input_->GetFrameSlot(input_offset); + } else { + value = reinterpret_cast<intptr_t>(function->context()); + } + output_frame->SetFrameSlot(output_offset, value); + if (is_topmost) { + output_frame->SetRegister(cp.code(), value); + } + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n", + top_address + output_offset, output_offset, value); + } + + // The function was mentioned explicitly in the BEGIN_FRAME. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<uint32_t>(function); + // The function for the bottommost output frame should also agree with the + // input frame. + ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n", + top_address + output_offset, output_offset, value); + } + + // Translate the rest of the frame. + for (unsigned i = 0; i < height; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + ASSERT(0 == output_offset); + + // Compute this frame's PC, state, and continuation. + Code* non_optimized_code = function->shared()->code(); + FixedArray* raw_data = non_optimized_code->deoptimization_data(); + DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data); + Address start = non_optimized_code->instruction_start(); + unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared()); + unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state); + uint32_t pc_value = reinterpret_cast<uint32_t>(start + pc_offset); + output_frame->SetPc(pc_value); + + FullCodeGenerator::State state = + FullCodeGenerator::StateField::decode(pc_and_state); + output_frame->SetState(Smi::FromInt(state)); + + + // Set the continuation for the topmost frame. + if (is_topmost && bailout_type_ != DEBUGGER) { + Builtins* builtins = isolate_->builtins(); + Code* continuation = (bailout_type_ == EAGER) + ? builtins->builtin(Builtins::kNotifyDeoptimized) + : builtins->builtin(Builtins::kNotifyLazyDeoptimized); + output_frame->SetContinuation( + reinterpret_cast<uint32_t>(continuation->entry())); + } +} + void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { - UNIMPLEMENTED(); + // Set the register values. The values are not important as there are no + // callee saved registers in JavaScript frames, so all registers are + // spilled. Registers fp and sp are set to the correct values though. + + for (int i = 0; i < Register::kNumRegisters; i++) { + input_->SetRegister(i, i * 4); + } + input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp())); + input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp())); + for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) { + input_->SetDoubleRegister(i, 0.0); + } + + // Fill the frame content from the actual data on the frame. + for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { + input_->SetFrameSlot(i, Memory::uint32_at(tos + i)); + } } +#define __ masm()-> + + +// This code tries to be close to ia32 code so that any changes can be +// easily ported. void Deoptimizer::EntryGenerator::Generate() { - UNIMPLEMENTED(); + GeneratePrologue(); + + Isolate* isolate = masm()->isolate(); + + CpuFeatures::Scope scope(FPU); + // Unlike on ARM we don't save all the registers, just the useful ones. + // For the rest, there are gaps on the stack, so the offsets remain the same. + const int kNumberOfRegisters = Register::kNumRegisters; + + RegList restored_regs = kJSCallerSaved | kCalleeSaved; + RegList saved_regs = restored_regs | sp.bit() | ra.bit(); + + const int kDoubleRegsSize = + kDoubleSize * FPURegister::kNumAllocatableRegisters; + + // Save all FPU registers before messing with them. + __ Subu(sp, sp, Operand(kDoubleRegsSize)); + for (int i = 0; i < FPURegister::kNumAllocatableRegisters; ++i) { + FPURegister fpu_reg = FPURegister::FromAllocationIndex(i); + int offset = i * kDoubleSize; + __ sdc1(fpu_reg, MemOperand(sp, offset)); + } + + // Push saved_regs (needed to populate FrameDescription::registers_). + // Leave gaps for other registers. + __ Subu(sp, sp, kNumberOfRegisters * kPointerSize); + for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) { + if ((saved_regs & (1 << i)) != 0) { + __ sw(ToRegister(i), MemOperand(sp, kPointerSize * i)); + } + } + + const int kSavedRegistersAreaSize = + (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize; + + // Get the bailout id from the stack. + __ lw(a2, MemOperand(sp, kSavedRegistersAreaSize)); + + // Get the address of the location in the code object if possible (a3) (return + // address for lazy deoptimization) and compute the fp-to-sp delta in + // register t0. + if (type() == EAGER) { + __ mov(a3, zero_reg); + // Correct one word for bailout id. + __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); + } else if (type() == OSR) { + __ mov(a3, ra); + // Correct one word for bailout id. + __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); + } else { + __ mov(a3, ra); + // Correct two words for bailout id and return address. + __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize))); + } + + __ Subu(t0, fp, t0); + + // Allocate a new deoptimizer object. + // Pass four arguments in a0 to a3 and fifth & sixth arguments on stack. + __ PrepareCallCFunction(6, t1); + __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ li(a1, Operand(type())); // bailout type, + // a2: bailout id already loaded. + // a3: code address or 0 already loaded. + __ sw(t0, CFunctionArgumentOperand(5)); // Fp-to-sp delta. + __ li(t1, Operand(ExternalReference::isolate_address())); + __ sw(t1, CFunctionArgumentOperand(6)); // Isolate. + // Call Deoptimizer::New(). + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + } + + // Preserve "deoptimizer" object in register v0 and get the input + // frame descriptor pointer to a1 (deoptimizer->input_); + // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below. + __ mov(a0, v0); + __ lw(a1, MemOperand(v0, Deoptimizer::input_offset())); + + // Copy core registers into FrameDescription::registers_[kNumRegisters]. + ASSERT(Register::kNumRegisters == kNumberOfRegisters); + for (int i = 0; i < kNumberOfRegisters; i++) { + int offset = (i * kPointerSize) + FrameDescription::registers_offset(); + if ((saved_regs & (1 << i)) != 0) { + __ lw(a2, MemOperand(sp, i * kPointerSize)); + __ sw(a2, MemOperand(a1, offset)); + } else if (FLAG_debug_code) { + __ li(a2, kDebugZapValue); + __ sw(a2, MemOperand(a1, offset)); + } + } + + // Copy FPU registers to + // double_registers_[DoubleRegister::kNumAllocatableRegisters] + int double_regs_offset = FrameDescription::double_registers_offset(); + for (int i = 0; i < FPURegister::kNumAllocatableRegisters; ++i) { + int dst_offset = i * kDoubleSize + double_regs_offset; + int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize; + __ ldc1(f0, MemOperand(sp, src_offset)); + __ sdc1(f0, MemOperand(a1, dst_offset)); + } + + // Remove the bailout id, eventually return address, and the saved registers + // from the stack. + if (type() == EAGER || type() == OSR) { + __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize))); + } else { + __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize))); + } + + // Compute a pointer to the unwinding limit in register a2; that is + // the first stack slot not part of the input frame. + __ lw(a2, MemOperand(a1, FrameDescription::frame_size_offset())); + __ Addu(a2, a2, sp); + + // Unwind the stack down to - but not including - the unwinding + // limit and copy the contents of the activation frame to the input + // frame description. + __ Addu(a3, a1, Operand(FrameDescription::frame_content_offset())); + Label pop_loop; + __ bind(&pop_loop); + __ pop(t0); + __ sw(t0, MemOperand(a3, 0)); + __ Branch(USE_DELAY_SLOT, &pop_loop, ne, a2, Operand(sp)); + __ addiu(a3, a3, sizeof(uint32_t)); // In delay slot. + + // Compute the output frame in the deoptimizer. + __ push(a0); // Preserve deoptimizer object across call. + // a0: deoptimizer object; a1: scratch. + __ PrepareCallCFunction(1, a1); + // Call Deoptimizer::ComputeOutputFrames(). + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction( + ExternalReference::compute_output_frames_function(isolate), 1); + } + __ pop(a0); // Restore deoptimizer object (class Deoptimizer). + + // Replace the current (input) frame with the output frames. + Label outer_push_loop, inner_push_loop; + // Outer loop state: a0 = current "FrameDescription** output_", + // a1 = one past the last FrameDescription**. + __ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset())); + __ lw(a0, MemOperand(a0, Deoptimizer::output_offset())); // a0 is output_. + __ sll(a1, a1, kPointerSizeLog2); // Count to offset. + __ addu(a1, a0, a1); // a1 = one past the last FrameDescription**. + __ bind(&outer_push_loop); + // Inner loop state: a2 = current FrameDescription*, a3 = loop index. + __ lw(a2, MemOperand(a0, 0)); // output_[ix] + __ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset())); + __ bind(&inner_push_loop); + __ Subu(a3, a3, Operand(sizeof(uint32_t))); + __ Addu(t2, a2, Operand(a3)); + __ lw(t3, MemOperand(t2, FrameDescription::frame_content_offset())); + __ push(t3); + __ Branch(&inner_push_loop, ne, a3, Operand(zero_reg)); + + __ Addu(a0, a0, Operand(kPointerSize)); + __ Branch(&outer_push_loop, lt, a0, Operand(a1)); + + + // Push state, pc, and continuation from the last output frame. + if (type() != OSR) { + __ lw(t2, MemOperand(a2, FrameDescription::state_offset())); + __ push(t2); + } + + __ lw(t2, MemOperand(a2, FrameDescription::pc_offset())); + __ push(t2); + __ lw(t2, MemOperand(a2, FrameDescription::continuation_offset())); + __ push(t2); + + + // Technically restoring 'at' should work unless zero_reg is also restored + // but it's safer to check for this. + ASSERT(!(at.bit() & restored_regs)); + // Restore the registers from the last output frame. + __ mov(at, a2); + for (int i = kNumberOfRegisters - 1; i >= 0; i--) { + int offset = (i * kPointerSize) + FrameDescription::registers_offset(); + if ((restored_regs & (1 << i)) != 0) { + __ lw(ToRegister(i), MemOperand(at, offset)); + } + } + + __ InitializeRootRegister(); + + __ pop(at); // Get continuation, leave pc on stack. + __ pop(ra); + __ Jump(at); + __ stop("Unreachable."); } void Deoptimizer::TableEntryGenerator::GeneratePrologue() { - UNIMPLEMENTED(); + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm()); + + // Create a sequence of deoptimization entries. Note that any + // registers may be still live. + + Label done; + for (int i = 0; i < count(); i++) { + int start = masm()->pc_offset(); + USE(start); + if (type() != EAGER) { + // Emulate ia32 like call by pushing return address to stack. + __ push(ra); + } + __ li(at, Operand(i)); + __ push(at); + __ Branch(&done); + + // Pad the rest of the code. + while (table_entry_size_ > (masm()->pc_offset() - start)) { + __ nop(); + } + + ASSERT_EQ(table_entry_size_, masm()->pc_offset() - start); + } + __ bind(&done); } +#undef __ + } } // namespace v8::internal diff --git a/deps/v8/src/mips/frames-mips.h b/deps/v8/src/mips/frames-mips.h index 2c838938b7..2ed358a913 100644 --- a/deps/v8/src/mips/frames-mips.h +++ b/deps/v8/src/mips/frames-mips.h @@ -36,9 +36,9 @@ namespace internal { // Register lists. // Note that the bit values must match those used in actual instruction // encoding. -static const int kNumRegs = 32; +const int kNumRegs = 32; -static const RegList kJSCallerSaved = +const RegList kJSCallerSaved = 1 << 2 | // v0 1 << 3 | // v1 1 << 4 | // a0 @@ -54,7 +54,7 @@ static const RegList kJSCallerSaved = 1 << 14 | // t6 1 << 15; // t7 -static const int kNumJSCallerSaved = 14; +const int kNumJSCallerSaved = 14; // Return the code of the n-th caller-saved register available to JavaScript @@ -63,7 +63,7 @@ int JSCallerSavedCode(int n); // Callee-saved registers preserved when switching from C to JavaScript. -static const RegList kCalleeSaved = +const RegList kCalleeSaved = 1 << 16 | // s0 1 << 17 | // s1 1 << 18 | // s2 @@ -74,9 +74,9 @@ static const RegList kCalleeSaved = 1 << 23 | // s7 (cp in Javascript code) 1 << 30; // fp/s8 -static const int kNumCalleeSaved = 9; +const int kNumCalleeSaved = 9; -static const RegList kCalleeSavedFPU = +const RegList kCalleeSavedFPU = 1 << 20 | // f20 1 << 22 | // f22 1 << 24 | // f24 @@ -84,23 +84,37 @@ static const RegList kCalleeSavedFPU = 1 << 28 | // f28 1 << 30; // f30 -static const int kNumCalleeSavedFPU = 6; +const int kNumCalleeSavedFPU = 6; + +const RegList kCallerSavedFPU = + 1 << 0 | // f0 + 1 << 2 | // f2 + 1 << 4 | // f4 + 1 << 6 | // f6 + 1 << 8 | // f8 + 1 << 10 | // f10 + 1 << 12 | // f12 + 1 << 14 | // f14 + 1 << 16 | // f16 + 1 << 18; // f18 + + // Number of registers for which space is reserved in safepoints. Must be a // multiple of 8. -static const int kNumSafepointRegisters = 24; +const int kNumSafepointRegisters = 24; // Define the list of registers actually saved at safepoints. // Note that the number of saved registers may be smaller than the reserved // space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. -static const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; -static const int kNumSafepointSavedRegisters = +const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; +const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved; typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; -static const int kUndefIndex = -1; +const int kUndefIndex = -1; // Map with indexes on stack that corresponds to codes of saved registers. -static const int kSafepointRegisterStackIndexMap[kNumRegs] = { +const int kSafepointRegisterStackIndexMap[kNumRegs] = { kUndefIndex, // zero_reg kUndefIndex, // at 0, // v0 @@ -140,13 +154,13 @@ static const int kSafepointRegisterStackIndexMap[kNumRegs] = { class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kStateOffset = 1 * kPointerSize; - static const int kContextOffset = 2 * kPointerSize; - static const int kFPOffset = 3 * kPointerSize; - static const int kPCOffset = 4 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kCodeOffset = 1 * kPointerSize; + static const int kStateOffset = 2 * kPointerSize; + static const int kContextOffset = 3 * kPointerSize; + static const int kFPOffset = 4 * kPointerSize; - static const int kSize = kPCOffset + kPointerSize; + static const int kSize = kFPOffset + kPointerSize; }; @@ -181,6 +195,9 @@ class ExitFrameConstants : public AllStatic { class StandardFrameConstants : public AllStatic { public: + // Fixed part of the frame consists of return address, caller fp, + // context and function. + static const int kFixedFrameSize = 4 * kPointerSize; static const int kExpressionsOffset = -3 * kPointerSize; static const int kMarkerOffset = -2 * kPointerSize; static const int kContextOffset = -1 * kPointerSize; @@ -216,6 +233,8 @@ class JavaScriptFrameConstants : public AllStatic { class ArgumentsAdaptorFrameConstants : public AllStatic { public: static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset; + static const int kFrameSize = + StandardFrameConstants::kFixedFrameSize + kPointerSize; }; diff --git a/deps/v8/src/mips/full-codegen-mips.cc b/deps/v8/src/mips/full-codegen-mips.cc index 9a210c49ea..201742efec 100644 --- a/deps/v8/src/mips/full-codegen-mips.cc +++ b/deps/v8/src/mips/full-codegen-mips.cc @@ -47,6 +47,7 @@ #include "stub-cache.h" #include "mips/code-stubs-mips.h" +#include "mips/macro-assembler-mips.h" namespace v8 { namespace internal { @@ -54,17 +55,14 @@ namespace internal { #define __ ACCESS_MASM(masm_) -static unsigned GetPropertyId(Property* property) { - return property->id(); -} - - // A patch site is a location in the code which it is possible to patch. This // class has a number of methods to emit the code which is patchable and the // method EmitPatchInfo to record a marker back to the patchable code. This -// marker is a andi at, rx, #yyy instruction, and x * 0x0000ffff + yyy (raw 16 -// bit immediate value is used) is the delta from the pc to the first +// marker is a andi zero_reg, rx, #yyyy instruction, and rx * 0x0000ffff + yyyy +// (raw 16 bit immediate value is used) is the delta from the pc to the first // instruction of the patchable code. +// The marker instruction is effectively a NOP (dest is zero_reg) and will +// never be emitted by normal code. class JumpPatchSite BASE_EMBEDDED { public: explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { @@ -103,7 +101,7 @@ class JumpPatchSite BASE_EMBEDDED { if (patch_site_.is_bound()) { int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_); Register reg = Register::from_code(delta_to_patch_site / kImm16Mask); - __ andi(at, reg, delta_to_patch_site % kImm16Mask); + __ andi(zero_reg, reg, delta_to_patch_site % kImm16Mask); #ifdef DEBUG info_emitted_ = true; #endif @@ -127,7 +125,7 @@ class JumpPatchSite BASE_EMBEDDED { // function. // // The live registers are: -// o a1: the JS function object being called (ie, ourselves) +// o a1: the JS function object being called (i.e. ourselves) // o cp: our context // o fp: our caller's frame pointer // o sp: stack pointer @@ -139,6 +137,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ASSERT(info_ == NULL); info_ = info; scope_ = info->scope(); + handler_table_ = + isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); @@ -153,7 +153,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // with undefined when called as functions (without an explicit // receiver object). t1 is zero for method calls and non-zero for // function calls. - if (info->is_strict_mode() || info->is_native()) { + if (!info->is_classic_mode() || info->is_native()) { Label ok; __ Branch(&ok, eq, t1, Operand(zero_reg)); int receiver_offset = info->scope()->num_parameters() * kPointerSize; @@ -162,6 +162,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { __ bind(&ok); } + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + int locals_count = info->scope()->num_stack_slots(); __ Push(ra, fp, cp, a1); @@ -207,14 +212,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Load parameter from stack. __ lw(a0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ li(a1, Operand(Context::SlotOffset(var->index()))); - __ addu(a2, cp, a1); - __ sw(a0, MemOperand(a2, 0)); - // Update the write barrier. This clobbers all involved - // registers, so we have to use two more registers to avoid - // clobbering cp. - __ mov(a2, cp); - __ RecordWrite(a2, a1, a3); + MemOperand target = ContextOperand(cp, var->index()); + __ sw(a0, target); + + // Update the write barrier. + __ RecordWriteContextSlot( + cp, target.offset(), a0, a3, kRAHasBeenSaved, kDontSaveFPRegs); } } } @@ -242,7 +245,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // The stub will rewrite receiever and parameter count if the previous // stack frame was an arguments adapter frame. ArgumentsAccessStub::Type type; - if (is_strict_mode()) { + if (!is_classic_mode()) { type = ArgumentsAccessStub::NEW_STRICT; } else if (function()->has_duplicate_parameters()) { type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; @@ -272,7 +275,10 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { int ignored = 0; - EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); + VariableProxy* proxy = scope()->function(); + ASSERT(proxy->var()->mode() == CONST || + proxy->var()->mode() == CONST_HARMONY); + EmitDeclaration(proxy, proxy->var()->mode(), NULL, &ignored); } VisitDeclarations(scope()->declarations()); } @@ -310,17 +316,25 @@ void FullCodeGenerator::ClearAccumulator() { void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { + // The generated code is used in Deoptimizer::PatchStackCheckCodeAt so we need + // to make sure it is constant. Branch may emit a skip-or-jump sequence + // instead of the normal Branch. It seems that the "skip" part of that + // sequence is about as long as this Branch would be so it is safe to ignore + // that. + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_); Comment cmnt(masm_, "[ Stack check"); Label ok; __ LoadRoot(t0, Heap::kStackLimitRootIndex); - __ Branch(&ok, hs, sp, Operand(t0)); + __ sltu(at, sp, t0); + __ beq(at, zero_reg, &ok); + // CallStub will emit a li t9, ... first, so it is safe to use the delay slot. StackCheckStub stub; + __ CallStub(&stub); // Record a mapping of this PC offset to the OSR id. This is used to find // the AST id from the unoptimized code in order to use it as a key into // the deoptimization input data found in the optimized code. RecordStackCheck(stmt->OsrEntryId()); - __ CallStub(&stub); __ bind(&ok); PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); // Record a mapping of the OSR id to this PC. This is used if the OSR @@ -393,7 +407,7 @@ void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { void FullCodeGenerator::TestContext::Plug(Variable* var) const { // For simplicity we always test the accumulator register. codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -416,7 +430,7 @@ void FullCodeGenerator::StackValueContext::Plug( void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -451,7 +465,7 @@ void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -510,7 +524,7 @@ void FullCodeGenerator::TestContext::DropAndPlug(int count, // For simplicity we always test the accumulator register. __ Drop(count); __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -577,7 +591,7 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const { void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -670,15 +684,17 @@ void FullCodeGenerator::SetVar(Variable* var, __ sw(src, location); // Emit the write barrier code if the location is in the heap. if (var->IsContextSlot()) { - __ RecordWrite(scratch0, - Operand(Context::SlotOffset(var->index())), - scratch1, - src); + __ RecordWriteContextSlot(scratch0, + location.offset(), + src, + scratch1, + kRAHasBeenSaved, + kDontSaveFPRegs); } } -void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, +void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false) { @@ -689,13 +705,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, Label skip; if (should_normalize) __ Branch(&skip); - - ForwardBailoutStack* current = forward_bailout_stack_; - while (current != NULL) { - PrepareForBailout(current->expr(), state); - current = current->parent(); - } - + PrepareForBailout(expr, TOS_REG); if (should_normalize) { __ LoadRoot(t0, Heap::kTrueValueRootIndex); Split(eq, a0, Operand(t0), if_true, if_false, NULL); @@ -705,13 +715,15 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, + VariableMode mode, FunctionLiteral* function, int* global_count) { // If it was not possible to allocate the variable at compile time, we // need to "declare" it at runtime to make sure it actually exists in the // local context. Variable* variable = proxy->var(); + bool binding_needs_init = (function == NULL) && + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (variable->location()) { case Variable::UNALLOCATED: ++(*global_count); @@ -723,7 +735,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ sw(result_register(), StackOperand(variable)); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); __ sw(t0, StackOperand(variable)); @@ -750,10 +762,16 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ sw(result_register(), ContextOperand(cp, variable->index())); int offset = Context::SlotOffset(variable->index()); // We know that we have written a function, which is not a smi. - __ mov(a1, cp); - __ RecordWrite(a1, Operand(offset), a2, result_register()); + __ RecordWriteContextSlot(cp, + offset, + result_register(), + a2, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ sw(at, ContextOperand(cp, variable->index())); @@ -765,11 +783,13 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, case Variable::LOOKUP: { Comment cmnt(masm_, "[ Declaration"); __ li(a2, Operand(variable->name())); - // Declaration nodes are always introduced in one of three modes. - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; + // Declaration nodes are always introduced in one of four modes. + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); + PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY) + ? READ_ONLY : NONE; __ li(a1, Operand(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -779,7 +799,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ Push(cp, a2, a1); // Push initial value for function declaration. VisitForStackValue(function); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { __ LoadRoot(a0, Heap::kTheHoleValueRootIndex); __ Push(cp, a2, a1, a0); } else { @@ -922,11 +942,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&done_convert); __ push(a0); + // Check for proxies. + Label call_runtime; + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ GetObjectType(a0, a1, a1); + __ Branch(&call_runtime, le, a1, Operand(LAST_JS_PROXY_TYPE)); + // Check cache validity in generated code. This is a fast case for // the JSObject::IsSimpleEnum cache validity checks. If we cannot // guarantee cache validity, call the runtime system to check cache // validity or get the property names in a fixed array. - Label next, call_runtime; + Label next; // Preload a couple of values used in the loop. Register empty_fixed_array_value = t2; __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); @@ -991,7 +1017,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ lw(a1, FieldMemOperand(a1, DescriptorArray::kEnumerationIndexOffset)); __ lw(a2, FieldMemOperand(a1, DescriptorArray::kEnumCacheBridgeCacheOffset)); - // Setup the four remaining stack slots. + // Set up the four remaining stack slots. __ push(v0); // Map. __ lw(a1, FieldMemOperand(a2, FixedArray::kLengthOffset)); __ li(a0, Operand(Smi::FromInt(0))); @@ -1000,9 +1026,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ jmp(&loop); // We got a fixed array in register v0. Iterate through that. + Label non_proxy; __ bind(&fixed_array); - __ li(a1, Operand(Smi::FromInt(0))); // Map (0) - force slow check. - __ Push(a1, v0); + __ li(a1, Operand(Smi::FromInt(1))); // Smi indicates slow check + __ lw(a2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ GetObjectType(a2, a3, a3); + __ Branch(&non_proxy, gt, a3, Operand(LAST_JS_PROXY_TYPE)); + __ li(a1, Operand(Smi::FromInt(0))); // Zero indicates proxy + __ bind(&non_proxy); + __ Push(a1, v0); // Smi and array __ lw(a1, FieldMemOperand(v0, FixedArray::kLengthOffset)); __ li(a0, Operand(Smi::FromInt(0))); __ Push(a1, a0); // Fixed array length (as smi) and initial index. @@ -1021,17 +1054,22 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ addu(t0, a2, t0); // Array base + scaled (smi) index. __ lw(a3, MemOperand(t0)); // Current entry. - // Get the expected map from the stack or a zero map in the + // Get the expected map from the stack or a smi in the // permanent slow case into register a2. __ lw(a2, MemOperand(sp, 3 * kPointerSize)); // Check if the expected map still matches that of the enumerable. - // If not, we have to filter the key. + // If not, we may have to filter the key. Label update_each; __ lw(a1, MemOperand(sp, 4 * kPointerSize)); __ lw(t0, FieldMemOperand(a1, HeapObject::kMapOffset)); __ Branch(&update_each, eq, t0, Operand(a2)); + // For proxies, no filtering is done. + // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. + ASSERT_EQ(Smi::FromInt(0), 0); + __ Branch(&update_each, eq, a2, Operand(zero_reg)); + // Convert the entry to a string or (smi) 0 if it isn't a property // any more. If the property has been removed while iterating, we // just skip it. @@ -1086,7 +1124,7 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, !pretenure && scope()->is_function_scope() && info->num_literals() == 0) { - FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(info->language_mode()); __ li(a0, Operand(info)); __ push(a0); __ CallStub(&stub); @@ -1117,7 +1155,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ lw(temp, ContextOperand(current, Context::EXTENSION_INDEX)); __ Branch(slow, ne, temp, Operand(zero_reg)); @@ -1129,7 +1167,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, } // If no outer scope calls eval, we do not need to check more // context extensions. - if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; + if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; s = s->outer_scope(); } @@ -1171,7 +1209,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ lw(temp, ContextOperand(context, Context::EXTENSION_INDEX)); __ Branch(slow, ne, temp, Operand(zero_reg)); @@ -1201,17 +1239,26 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (var->mode() == Variable::DYNAMIC_GLOBAL) { + if (var->mode() == DYNAMIC_GLOBAL) { EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ Branch(done); - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + } else if (var->mode() == DYNAMIC_LOCAL) { Variable* local = var->local_if_not_shadowed(); __ lw(v0, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == Variable::CONST) { + if (local->mode() == CONST || + local->mode() == CONST_HARMONY || + local->mode() == LET) { __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move: return Undefined if TheHole. + if (local->mode() == CONST) { + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: return Undefined if TheHole. + } else { // LET || CONST_HARMONY + __ Branch(done, ne, at, Operand(zero_reg)); + __ li(a0, Operand(var->name())); + __ push(a0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } } __ Branch(done); } @@ -1244,26 +1291,66 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { Comment cmnt(masm_, var->IsContextSlot() ? "Context variable" : "Stack variable"); - if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { - context()->Plug(var); - } else { - // Let and const need a read barrier. - GetVar(v0, var); - __ LoadRoot(at, Heap::kTheHoleValueRootIndex); - __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - if (var->mode() == Variable::LET) { - Label done; - __ Branch(&done, ne, at, Operand(zero_reg)); - __ li(a0, Operand(var->name())); - __ push(a0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - __ bind(&done); + if (var->binding_needs_init()) { + // var->scope() may be NULL when the proxy is located in eval code and + // refers to a potential outside binding. Currently those bindings are + // always looked up dynamically, i.e. in that case + // var->location() == LOOKUP. + // always holds. + ASSERT(var->scope() != NULL); + + // Check if the binding really needs an initialization check. The check + // can be skipped in the following situation: we have a LET or CONST + // binding in harmony mode, both the Variable and the VariableProxy have + // the same declaration scope (i.e. they are both in global code, in the + // same function or in the same eval code) and the VariableProxy is in + // the source physically located after the initializer of the variable. + // + // We cannot skip any initialization checks for CONST in non-harmony + // mode because const variables may be declared but never initialized: + // if (false) { const x; }; var y = x; + // + // The condition on the declaration scopes is a conservative check for + // nested functions that access a binding and are called before the + // binding is initialized: + // function() { f(); let x = 1; function f() { x = 2; } } + // + bool skip_init_check; + if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { + skip_init_check = false; } else { - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move: Undefined if TheHole. + // Check that we always have valid source position. + ASSERT(var->initializer_position() != RelocInfo::kNoPosition); + ASSERT(proxy->position() != RelocInfo::kNoPosition); + skip_init_check = var->mode() != CONST && + var->initializer_position() < proxy->position(); + } + + if (!skip_init_check) { + // Let and const need a read barrier. + GetVar(v0, var); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ subu(at, v0, at); // Sub as compare: at == 0 on eq. + if (var->mode() == LET || var->mode() == CONST_HARMONY) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + Label done; + __ Branch(&done, ne, at, Operand(zero_reg)); + __ li(a0, Operand(var->name())); + __ push(a0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&done); + } else { + // Uninitalized const bindings outside of harmony mode are unholed. + ASSERT(var->mode() == CONST); + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: Undefined if TheHole. + } + context()->Plug(v0); + break; } - context()->Plug(v0); } + context()->Plug(var); break; } @@ -1337,10 +1424,11 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Comment cmnt(masm_, "[ ObjectLiteral"); + Handle<FixedArray> constant_properties = expr->constant_properties(); __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset)); __ li(a2, Operand(Smi::FromInt(expr->literal_index()))); - __ li(a1, Operand(expr->constant_properties())); + __ li(a1, Operand(constant_properties)); int flags = expr->fast_elements() ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags; @@ -1349,10 +1437,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { : ObjectLiteral::kNoFlags; __ li(a0, Operand(Smi::FromInt(flags))); __ Push(a3, a2, a1, a0); + int properties_count = constant_properties->length() / 2; if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); + } else { + FastCloneShallowObjectStub stub(properties_count); + __ CallStub(&stub); } // If result_saved is true the result is on top of the stack. If @@ -1387,9 +1480,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ mov(a0, result_register()); __ li(a2, Operand(key->handle())); __ lw(a1, MemOperand(sp)); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, key->id()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1448,13 +1541,22 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); int length = subexprs->length(); + + Handle<FixedArray> constant_elements = expr->constant_elements(); + ASSERT_EQ(2, constant_elements->length()); + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); + bool has_fast_elements = constant_elements_kind == FAST_ELEMENTS; + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(constant_elements->get(1))); + __ mov(a0, result_register()); __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset)); __ li(a2, Operand(Smi::FromInt(expr->literal_index()))); - __ li(a1, Operand(expr->constant_elements())); + __ li(a1, Operand(constant_elements)); __ Push(a3, a2, a1); - if (expr->constant_elements()->map() == + if (has_fast_elements && constant_elements_values->map() == isolate()->heap()->fixed_cow_array_map()) { FastCloneShallowArrayStub stub( FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); @@ -1466,8 +1568,13 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::CLONE_ELEMENTS, length); + ASSERT(constant_elements_kind == FAST_ELEMENTS || + constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + FLAG_smi_only_arrays); + FastCloneShallowArrayStub::Mode mode = has_fast_elements + ? FastCloneShallowArrayStub::CLONE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); __ CallStub(&stub); } @@ -1488,21 +1595,30 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ push(v0); result_saved = true; } - VisitForAccumulatorValue(subexpr); - // Store the subexpression value in the array's elements. - __ lw(a1, MemOperand(sp)); // Copy of array literal. - __ lw(a1, FieldMemOperand(a1, JSObject::kElementsOffset)); - int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ sw(result_register(), FieldMemOperand(a1, offset)); + VisitForAccumulatorValue(subexpr); - // Update the write barrier for the array store with v0 as the scratch - // register. - __ RecordWrite(a1, Operand(offset), a2, result_register()); + if (constant_elements_kind == FAST_ELEMENTS) { + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ lw(t2, MemOperand(sp)); // Copy of array literal. + __ lw(a1, FieldMemOperand(t2, JSObject::kElementsOffset)); + __ sw(result_register(), FieldMemOperand(a1, offset)); + // Update the write barrier for the array store. + __ RecordWriteField(a1, offset, result_register(), a2, + kRAHasBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, INLINE_SMI_CHECK); + } else { + __ lw(a1, MemOperand(sp)); // Copy of array literal. + __ lw(a2, FieldMemOperand(a1, JSObject::kMapOffset)); + __ li(a3, Operand(Smi::FromInt(i))); + __ li(t0, Operand(Smi::FromInt(expr->literal_index()))); + __ mov(a0, result_register()); + StoreArrayLiteralElementStub stub; + __ CallStub(&stub); + } PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } - if (result_saved) { context()->PlugTOS(); } else { @@ -1632,7 +1748,7 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { __ li(a2, Operand(key->handle())); // Call load IC. It has arguments receiver and property name a0 and a2. Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1641,7 +1757,7 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { __ mov(a0, result_register()); // Call keyed load IC. It has arguments key and receiver in a0 and a1. Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1790,9 +1906,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(a1, result_register()); __ pop(a0); // Restore value. __ li(a2, Operand(prop->key()->AsLiteral()->handle())); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1803,9 +1919,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(a1, result_register()); __ pop(a2); __ pop(a0); // Restore value. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1822,9 +1938,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ mov(a0, result_register()); __ li(a2, Operand(var->name())); __ lw(a1, GlobalObjectOperand()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { @@ -1850,12 +1966,12 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { __ push(v0); // Value. __ li(a1, Operand(var->name())); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ li(a0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, a1, a0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { @@ -1875,12 +1991,14 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // RecordWrite may destroy all its register arguments. __ mov(a3, result_register()); int offset = Context::SlotOffset(var->index()); - __ RecordWrite(a1, Operand(offset), a2, a3); + __ RecordWriteContextSlot( + a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs); } } - } else if (var->mode() != Variable::CONST) { - // Assignment to var or initializing assignment to let. + } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { + // Assignment to var or initializing assignment to let/const + // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, a1); if (FLAG_debug_code && op == Token::INIT_LET) { @@ -1893,13 +2011,15 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ sw(v0, location); if (var->IsContextSlot()) { __ mov(a3, v0); - __ RecordWrite(a1, Operand(Context::SlotOffset(var->index())), a2, a3); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot( + a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs); } } else { ASSERT(var->IsLookupSlot()); __ push(v0); // Value. __ li(a1, Operand(var->name())); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ li(a0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, a1, a0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } @@ -1937,9 +2057,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ pop(a1); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1989,9 +2109,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(a2); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -2097,6 +2217,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, flags); + __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2105,8 +2226,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } -void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, - int arg_count) { +void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { __ lw(a1, MemOperand(sp, arg_count * kPointerSize)); @@ -2115,22 +2235,20 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, } __ push(a1); - // Push the receiver of the enclosing function and do runtime call. + // Push the receiver of the enclosing function. int receiver_offset = 2 + info_->scope()->num_parameters(); __ lw(a1, MemOperand(fp, receiver_offset * kPointerSize)); __ push(a1); - // Push the strict mode flag. In harmony mode every eval call - // is a strict mode eval call. - StrictModeFlag strict_mode = strict_mode_flag(); - if (FLAG_harmony_block_scoping) { - strict_mode = kStrictMode; - } - __ li(a1, Operand(Smi::FromInt(strict_mode))); + // Push the language mode. + __ li(a1, Operand(Smi::FromInt(language_mode()))); __ push(a1); - __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP - ? Runtime::kResolvePossiblyDirectEvalNoLookup - : Runtime::kResolvePossiblyDirectEval, 4); + // Push the start position of the scope the calls resides in. + __ li(a1, Operand(Smi::FromInt(scope()->start_position()))); + __ push(a1); + + // Do the runtime call. + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); } @@ -2164,28 +2282,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly - // in generated code. If we succeed, there is no need to perform a - // context lookup in the runtime system. - Label done; - Variable* var = proxy->var(); - if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { - Label slow; - EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); - // Push the function and resolve eval. - __ push(v0); - EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); - __ jmp(&done); - __ bind(&slow); - } - // Push a copy of the function (found below the arguments) and // resolve eval. __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(a1); - EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - __ bind(&done); + EmitResolvePossiblyDirectEval(arg_count); // The runtime call returns a pair of values in v0 (function) and // v1 (receiver). Touch up the stack with the right values. @@ -2195,6 +2296,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); + __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2301,14 +2403,28 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ li(a0, Operand(arg_count)); __ lw(a1, MemOperand(sp, arg_count * kPointerSize)); - Handle<Code> construct_builtin = - isolate()->builtins()->JSConstructCall(); - __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); + // Record call targets in unoptimized code, but not in the snapshot. + CallFunctionFlags flags; + if (!Serializer::enabled()) { + flags = RECORD_CALL_TARGET; + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->id(), cell); + __ li(a2, Operand(cell)); + } else { + flags = NO_CALL_FUNCTION_FLAGS; + } + + CallConstructStub stub(flags); + __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); context()->Plug(v0); } -void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2320,7 +2436,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ And(t0, v0, Operand(kSmiTagMask)); Split(eq, t0, Operand(zero_reg), if_true, if_false, fall_through); @@ -2328,7 +2444,8 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2340,7 +2457,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ And(at, v0, Operand(kSmiTagMask | 0x80000000)); Split(eq, at, Operand(zero_reg), if_true, if_false, fall_through); @@ -2348,7 +2465,8 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2370,7 +2488,7 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ Branch(if_false, ne, at, Operand(zero_reg)); __ lbu(a1, FieldMemOperand(a2, Map::kInstanceTypeOffset)); __ Branch(if_false, lt, a1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(le, a1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE), if_true, if_false, fall_through); @@ -2378,7 +2496,8 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2392,7 +2511,7 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE), if_true, if_false, fall_through); @@ -2400,7 +2519,8 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2416,7 +2536,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset)); __ And(at, a1, Operand(1 << Map::kIsUndetectable)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ne, at, Operand(zero_reg), if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2424,8 +2544,8 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - ZoneList<Expression*>* args) { - + CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2501,12 +2621,13 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ sb(a2, FieldMemOperand(a1, Map::kBitField2Offset)); __ jmp(if_true); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2520,7 +2641,7 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a2); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ Branch(if_true, eq, a2, Operand(JS_FUNCTION_TYPE)); __ Branch(if_false); @@ -2528,7 +2649,8 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2542,7 +2664,7 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(JS_ARRAY_TYPE), if_true, if_false, fall_through); @@ -2550,7 +2672,8 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2564,15 +2687,15 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(JS_REGEXP_TYPE), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label materialize_true, materialize_false; Label* if_true = NULL; @@ -2594,7 +2717,7 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { // Check the marker in the calling frame. __ bind(&check_frame_marker); __ lw(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)), if_true, if_false, fall_through); @@ -2602,7 +2725,8 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -2617,14 +2741,15 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { &if_true, &if_false, &fall_through); __ pop(a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, v0, Operand(a1), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitArguments(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); // ArgumentsAccessStub expects the key in a1 and the formal @@ -2638,9 +2763,8 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label exit; // Get the number of formal parameters. __ li(v0, Operand(Smi::FromInt(info_->scope()->num_parameters()))); @@ -2660,7 +2784,8 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); Label done, null, function, non_function_constructor; @@ -2671,18 +2796,23 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { // Check that the object is a JS object but take special care of JS // functions to make sure they have 'Function' as their class. + // Assume that there are only two callable types, and one of them is at + // either end of the type range for JS object types. Saves extra comparisons. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ GetObjectType(v0, v0, a1); // Map is now in v0. __ Branch(&null, lt, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - __ Branch(&function, ge, a1, Operand(FIRST_CALLABLE_SPEC_OBJECT_TYPE)); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + __ Branch(&function, eq, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); + + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + __ Branch(&function, eq, a1, Operand(LAST_SPEC_OBJECT_TYPE)); + // Assume that there is no larger type. + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); - // Check if the constructor in the map is a function. + // Check if the constructor in the map is a JS function. __ lw(v0, FieldMemOperand(v0, Map::kConstructorOffset)); __ GetObjectType(v0, a1, a1); __ Branch(&non_function_constructor, ne, a1, Operand(JS_FUNCTION_TYPE)); @@ -2714,7 +2844,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitLog(CallRuntime* expr) { // Conditionally generate a log call. // Args: // 0 (literal string): The type of logging (corresponds to the flags). @@ -2722,6 +2852,7 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { // 1 (string): Format string. Access the string at argument index 2 // with '%2s' (see Logger::LogRuntime for all the formats). // 2 (array): Arguments to the format string. + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 3); if (CodeGenerator::ShouldGenerateLog(args->at(0))) { VisitForStackValue(args->at(1)); @@ -2735,9 +2866,8 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label slow_allocate_heapnumber; Label heapnumber_allocated; @@ -2760,10 +2890,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). if (CpuFeatures::IsSupported(FPU)) { __ PrepareCallCFunction(1, a0); - __ li(a0, Operand(ExternalReference::isolate_address())); + __ lw(a0, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset)); __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); - CpuFeatures::Scope scope(FPU); // 0x41300000 is the top half of 1.0 x 2^20 as a double. __ li(a1, Operand(0x41300000)); @@ -2778,7 +2908,8 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } else { __ PrepareCallCFunction(2, a0); __ mov(a0, s0); - __ li(a1, Operand(ExternalReference::isolate_address())); + __ lw(a1, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalContextOffset)); __ CallCFunction( ExternalReference::fill_heap_number_with_random_function(isolate()), 2); } @@ -2787,9 +2918,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2799,9 +2931,10 @@ void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpExecStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 4); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2812,7 +2945,8 @@ void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); // Load the object. @@ -2831,18 +2965,24 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { // Load the arguments on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); - MathPowStub stub; - __ CallStub(&stub); + if (CpuFeatures::IsSupported(FPU)) { + MathPowStub stub(MathPowStub::ON_STACK); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kMath_pow, 2); + } context()->Plug(v0); } -void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); // Load the object. @@ -2861,14 +3001,17 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { __ sw(v0, FieldMemOperand(a1, JSValue::kValueOffset)); // Update the write barrier. Save the value as it will be // overwritten by the write barrier code and is needed afterward. - __ RecordWrite(a1, Operand(JSValue::kValueOffset - kHeapObjectTag), a2, a3); + __ mov(a2, v0); + __ RecordWriteField( + a1, JSValue::kValueOffset, a2, a3, kRAHasBeenSaved, kDontSaveFPRegs); __ bind(&done); context()->Plug(v0); } -void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 1); // Load the argument on the stack and call the stub. @@ -2880,7 +3023,8 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2898,7 +3042,8 @@ void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2907,7 +3052,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Register object = a1; Register index = a0; - Register scratch = a2; Register result = v0; __ pop(object); @@ -2917,7 +3061,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Label done; StringCharCodeAtGenerator generator(object, index, - scratch, result, &need_conversion, &need_conversion, @@ -2946,7 +3089,8 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2955,8 +3099,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Register object = a1; Register index = a0; - Register scratch1 = a2; - Register scratch2 = a3; + Register scratch = a3; Register result = v0; __ pop(object); @@ -2966,8 +3109,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Label done; StringCharAtGenerator generator(object, index, - scratch1, - scratch2, + scratch, result, &need_conversion, &need_conversion, @@ -2996,9 +3138,9 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3008,7 +3150,8 @@ void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -3020,10 +3163,11 @@ void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3032,10 +3176,11 @@ void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3044,10 +3189,24 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); + ASSERT(args->length() == 1); + VisitForStackValue(args->at(0)); + __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. + __ CallStub(&stub); + context()->Plug(v0); +} + + +void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::LOG, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3056,8 +3215,9 @@ void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { // Load the argument on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallRuntime(Runtime::kMath_sqrt, 1); @@ -3065,7 +3225,8 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() >= 2); int arg_count = args->length() - 2; // 2 ~ receiver and function. @@ -3074,18 +3235,31 @@ void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { } VisitForAccumulatorValue(args->last()); // Function. + // Check for proxy. + Label proxy, done; + __ GetObjectType(v0, a1, a1); + __ Branch(&proxy, eq, a1, Operand(JS_FUNCTION_PROXY_TYPE)); + // InvokeFunction requires the function in a1. Move it in there. __ mov(a1, result_register()); ParameterCount count(arg_count); __ InvokeFunction(a1, count, CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ jmp(&done); + + __ bind(&proxy); + __ push(v0); + __ CallRuntime(Runtime::kCall, args->length()); + __ bind(&done); + context()->Plug(v0); } -void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { RegExpConstructResultStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3095,7 +3269,8 @@ void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSwapElements(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3154,16 +3329,31 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ sw(scratch1, MemOperand(index2, 0)); __ sw(scratch2, MemOperand(index1, 0)); - Label new_space; - __ InNewSpace(elements, scratch1, eq, &new_space); + Label no_remembered_set; + __ CheckPageFlag(elements, + scratch1, + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + ne, + &no_remembered_set); // Possible optimization: do a check that both values are Smis // (or them and test against Smi mask). - __ mov(scratch1, elements); - __ RecordWriteHelper(elements, index1, scratch2); - __ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements. + // We are swapping two objects in an array and the incremental marker never + // pauses in the middle of scanning a single object. Therefore the + // incremental marker is not disturbed, so we don't need to call the + // RecordWrite stub that notifies the incremental marker. + __ RememberedSetHelper(elements, + index1, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + __ RememberedSetHelper(elements, + index2, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); - __ bind(&new_space); + __ bind(&no_remembered_set); // We are done. Drop elements from the stack, and return undefined. __ Drop(3); __ LoadRoot(v0, Heap::kUndefinedValueRootIndex); @@ -3177,7 +3367,8 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); ASSERT_NE(NULL, args->at(0)->AsLiteral()); @@ -3230,7 +3421,8 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); Register right = v0; @@ -3246,8 +3438,7 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { __ Branch(&ok, eq, left, Operand(right)); // Fail if either is a non-HeapObject. __ And(tmp, left, Operand(right)); - __ And(at, tmp, Operand(kSmiTagMask)); - __ Branch(&fail, eq, at, Operand(zero_reg)); + __ JumpIfSmi(tmp, &fail); __ lw(tmp, FieldMemOperand(left, HeapObject::kMapOffset)); __ lbu(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset)); __ Branch(&fail, ne, tmp2, Operand(JS_REGEXP_TYPE)); @@ -3267,7 +3458,8 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); VisitForAccumulatorValue(args->at(0)); Label materialize_true, materialize_false; @@ -3280,14 +3472,15 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { __ lw(a0, FieldMemOperand(v0, String::kHashFieldOffset)); __ And(a0, a0, Operand(String::kContainsCachedArrayIndexMask)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a0, Operand(zero_reg), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3302,12 +3495,12 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { Label bailout, done, one_char_separator, long_separator, non_trivial_array, not_size_one_array, loop, empty_separator_loop, one_char_separator_loop, one_char_separator_loop_entry, long_separator_loop; - + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(1)); VisitForAccumulatorValue(args->at(0)); @@ -3475,7 +3668,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // One-character separator case. __ bind(&one_char_separator); - // Replace separator with its ascii character value. + // Replace separator with its ASCII character value. __ lbu(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize)); // Jump into the loop after the code that copies the separator, so the first // element is not preceded by a separator. @@ -3486,7 +3679,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // result_pos: the position to which we are currently copying characters. // element: Current array element. // elements_end: Array end. - // separator: Single separator ascii char (in lower byte). + // separator: Single separator ASCII char (in lower byte). // Copy the separator character to the result. __ sb(separator, MemOperand(result_pos)); @@ -3592,7 +3785,9 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { if (property != NULL) { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); - __ li(a1, Operand(Smi::FromInt(strict_mode_flag()))); + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ li(a1, Operand(Smi::FromInt(strict_mode_flag))); __ push(a1); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(v0); @@ -3600,7 +3795,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode // but "delete this" is allowed. - ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); + ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); if (var->IsUnallocated()) { __ lw(a2, GlobalObjectOperand()); __ li(a1, Operand(var->name())); @@ -3643,18 +3838,35 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { // Unary NOT has no side effects so it's only necessary to visit the // subexpression. Match the optimizing compiler by not branching. VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); } else { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - - // Notice that the labels are swapped. - context()->PrepareTest(&materialize_true, &materialize_false, - &if_false, &if_true, &fall_through); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitForControl(expr->expression(), if_true, if_false, fall_through); - context()->Plug(if_false, if_true); // Labels swapped. + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + __ bind(&materialize_true); + PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); + __ LoadRoot(v0, Heap::kTrueValueRootIndex); + if (context()->IsStackValue()) __ push(v0); + __ jmp(&done); + __ bind(&materialize_false); + PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); + __ LoadRoot(v0, Heap::kFalseValueRootIndex); + if (context()->IsStackValue()) __ push(v0); + __ bind(&done); } break; } @@ -3849,9 +4061,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(a0, result_register()); // Value. __ li(a2, Operand(prop->key()->AsLiteral()->handle())); // Name. __ pop(a1); // Receiver. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3867,9 +4079,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(a0, result_register()); // Value. __ pop(a1); // Key. __ pop(a2); // Receiver. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3916,19 +4128,24 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { context()->Plug(v0); } else { // This expression cannot throw a reference error at the top level. - VisitInCurrentContext(expr); + VisitInDuplicateContext(expr); } } void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through) { + Expression* sub_expr, + Handle<String> check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + { AccumulatorValueContext context(this); - VisitForTypeofValue(expr); + VisitForTypeofValue(sub_expr); } - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); if (check->Equals(isolate()->heap()->number_symbol())) { __ JumpIfSmi(v0, if_true); @@ -3964,10 +4181,11 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(ne, a1, Operand(zero_reg), if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->function_symbol())) { __ JumpIfSmi(v0, if_false); - __ GetObjectType(v0, a1, v0); // Leave map in a1. - Split(ge, v0, Operand(FIRST_CALLABLE_SPEC_OBJECT_TYPE), - if_true, if_false, fall_through); - + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ GetObjectType(v0, v0, a1); + __ Branch(if_true, eq, a1, Operand(JS_FUNCTION_TYPE)); + Split(eq, a1, Operand(JS_FUNCTION_PROXY_TYPE), + if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(v0, if_false); if (!FLAG_harmony_typeof) { @@ -3986,18 +4204,7 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else { if (if_false != fall_through) __ jmp(if_false); } -} - - -void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through) { - VisitForAccumulatorValue(expr); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ LoadRoot(at, Heap::kUndefinedValueRootIndex); - Split(eq, v0, Operand(at), if_true, if_false, fall_through); + context()->Plug(if_true, if_false); } @@ -4005,9 +4212,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); SetSourcePosition(expr->position()); + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4015,20 +4225,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { - context()->Plug(if_true, if_false); - return; - } - Token::Value op = expr->op(); VisitForStackValue(expr->left()); switch (op) { case Token::IN: VisitForStackValue(expr->right()); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); __ LoadRoot(t0, Heap::kTrueValueRootIndex); Split(eq, v0, Operand(t0), if_true, if_false, fall_through); break; @@ -4037,7 +4240,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForStackValue(expr->right()); InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); // The stub returns 0 for true. Split(eq, v0, Operand(zero_reg), if_true, if_false, fall_through); break; @@ -4050,36 +4253,26 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: case Token::EQ: cc = eq; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::LT: cc = lt; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::GT: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cc = lt; - __ mov(a1, result_register()); - __ pop(a0); + cc = gt; break; case Token::LTE: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cc = ge; - __ mov(a1, result_register()); - __ pop(a0); + cc = le; break; case Token::GTE: cc = ge; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::IN: case Token::INSTANCEOF: default: UNREACHABLE(); } + __ mov(a0, result_register()); + __ pop(a1); bool inline_smi_code = ShouldInlineSmiCase(op); JumpPatchSite patch_site(masm_); @@ -4095,7 +4288,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Handle<Code> ic = CompareIC::GetUninitialized(op); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(cc, v0, Operand(zero_reg), if_true, if_false, fall_through); } } @@ -4106,8 +4299,9 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } -void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { - Comment cmnt(masm_, "[ CompareToNull"); +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4115,18 +4309,23 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - VisitForAccumulatorValue(expr->expression()); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + VisitForAccumulatorValue(sub_expr); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + Heap::RootListIndex nil_value = nil == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; __ mov(a0, result_register()); - __ LoadRoot(a1, Heap::kNullValueRootIndex); - if (expr->is_strict()) { + __ LoadRoot(a1, nil_value); + if (expr->op() == Token::EQ_STRICT) { Split(eq, a0, Operand(a1), if_true, if_false, fall_through); } else { + Heap::RootListIndex other_nil_value = nil == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; __ Branch(if_true, eq, a0, Operand(a1)); - __ LoadRoot(a1, Heap::kUndefinedValueRootIndex); + __ LoadRoot(a1, other_nil_value); __ Branch(if_true, eq, a0, Operand(a1)); - __ And(at, a0, Operand(kSmiTagMask)); - __ Branch(if_false, eq, at, Operand(zero_reg)); + __ JumpIfSmi(a0, if_false); // It can be an undetectable object. __ lw(a1, FieldMemOperand(a0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset)); diff --git a/deps/v8/src/mips/ic-mips.cc b/deps/v8/src/mips/ic-mips.cc index a76c215a48..c3cdb4cd38 100644 --- a/deps/v8/src/mips/ic-mips.cc +++ b/deps/v8/src/mips/ic-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -210,7 +210,8 @@ static void GenerateDictionaryStore(MacroAssembler* masm, // Update the write barrier. Make sure not to clobber the value. __ mov(scratch1, value); - __ RecordWrite(elements, scratch2, scratch1); + __ RecordWrite( + elements, scratch2, scratch1, kRAHasNotBeenSaved, kDontSaveFPRegs); } @@ -383,10 +384,10 @@ Object* CallIC_Miss(Arguments args); // The generated code does not accept smi keys. // The generated code falls through if both probes miss. -static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, + int argc, + Code::Kind kind, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- a1 : receiver // -- a2 : name @@ -396,7 +397,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, - extra_ic_state, + extra_state, NORMAL, argc); Isolate::Current()->stub_cache()->GenerateProbe( @@ -462,7 +463,7 @@ static void GenerateFunctionTailCall(MacroAssembler* masm, } -static void GenerateCallNormal(MacroAssembler* masm, int argc) { +void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -485,10 +486,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { } -static void GenerateCallMiss(MacroAssembler* masm, - int argc, - IC::UtilityId id, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -504,29 +505,29 @@ static void GenerateCallMiss(MacroAssembler* masm, // Get the receiver of the function from the stack. __ lw(a3, MemOperand(sp, argc*kPointerSize)); - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push the receiver and the name of the function. - __ Push(a3, a2); + // Push the receiver and the name of the function. + __ Push(a3, a2); - // Call the entry. - __ li(a0, Operand(2)); - __ li(a1, Operand(ExternalReference(IC_Utility(id), isolate))); + // Call the entry. + __ li(a0, Operand(2)); + __ li(a1, Operand(ExternalReference(IC_Utility(id), isolate))); - CEntryStub stub(1); - __ CallStub(&stub); + CEntryStub stub(1); + __ CallStub(&stub); - // Move result to a1 and leave the internal frame. - __ mov(a1, v0); - __ LeaveInternalFrame(); + // Move result to a1 and leave the internal frame. + __ mov(a1, v0); + } // Check if the receiver is a global object of some sort. // This can happen only for regular CallIC but not KeyedCallIC. if (id == IC::kCallIC_Miss) { Label invoke, global; __ lw(a2, MemOperand(sp, argc * kPointerSize)); - __ andi(t0, a2, kSmiTagMask); - __ Branch(&invoke, eq, t0, Operand(zero_reg)); + __ JumpIfSmi(a2, &invoke); __ GetObjectType(a2, a3, a3); __ Branch(&global, eq, a3, Operand(JS_GLOBAL_OBJECT_TYPE)); __ Branch(&invoke, ne, a3, Operand(JS_BUILTINS_OBJECT_TYPE)); @@ -538,7 +539,7 @@ static void GenerateCallMiss(MacroAssembler* masm, __ bind(&invoke); } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state) + CallKind call_kind = CallICBase::Contextual::decode(extra_state) ? CALL_AS_FUNCTION : CALL_AS_METHOD; ParameterCount actual(argc); @@ -550,18 +551,6 @@ static void GenerateCallMiss(MacroAssembler* masm, } -void CallIC::GenerateMiss(MacroAssembler* masm, - int argc, - Code::ExtraICState extra_ic_state) { - // ----------- S t a t e ------------- - // -- a2 : name - // -- ra : return address - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state); -} - - void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc, Code::ExtraICState extra_ic_state) { @@ -577,27 +566,6 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, } -void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- a2 : name - // -- ra : return address - // ----------------------------------- - - GenerateCallNormal(masm, argc); - GenerateMiss(masm, argc, Code::kNoExtraICState); -} - - -void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // -- a2 : name - // -- ra : return address - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState); -} - - void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // -- a2 : name @@ -649,12 +617,13 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // This branch is taken when calling KeyedCallIC_Miss is neither required // nor beneficial. __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, a0, a3); - __ EnterInternalFrame(); - __ push(a2); // Save the key. - __ Push(a1, a2); // Pass the receiver and the key. - __ CallRuntime(Runtime::kKeyedGetProperty, 2); - __ pop(a2); // Restore the key. - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(a2); // Save the key. + __ Push(a1, a2); // Pass the receiver and the key. + __ CallRuntime(Runtime::kKeyedGetProperty, 2); + __ pop(a2); // Restore the key. + } __ mov(a1, v0); __ jmp(&do_call); @@ -713,7 +682,7 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ JumpIfSmi(a2, &miss); __ IsObjectJSStringType(a2, a0, &miss); - GenerateCallNormal(masm, argc); + CallICBase::GenerateNormal(masm, argc); __ bind(&miss); GenerateMiss(masm, argc); } @@ -899,21 +868,26 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { // -- lr : return address // ----------------------------------- Label slow, notin; + // Store address is returned in register (of MemOperand) mapped_location. MemOperand mapped_location = GenerateMappedArgumentsLookup(masm, a2, a1, a3, t0, t1, ¬in, &slow); __ sw(a0, mapped_location); - // Verify mapped_location MemOperand is register, with no offset. + __ mov(t5, a0); ASSERT_EQ(mapped_location.offset(), 0); - __ RecordWrite(a3, mapped_location.rm(), t5); + __ RecordWrite(a3, mapped_location.rm(), t5, + kRAHasNotBeenSaved, kDontSaveFPRegs); __ Ret(USE_DELAY_SLOT); __ mov(v0, a0); // (In delay slot) return the value stored in v0. __ bind(¬in); // The unmapped lookup expects that the parameter map is in a3. + // Store address is returned in register (of MemOperand) unmapped_location. MemOperand unmapped_location = GenerateUnmappedArgumentsLookup(masm, a1, a3, t0, &slow); __ sw(a0, unmapped_location); + __ mov(t5, a0); ASSERT_EQ(unmapped_location.offset(), 0); - __ RecordWrite(a3, unmapped_location.rm(), t5); + __ RecordWrite(a3, unmapped_location.rm(), t5, + kRAHasNotBeenSaved, kDontSaveFPRegs); __ Ret(USE_DELAY_SLOT); __ mov(v0, a0); // (In delay slot) return the value stored in v0. __ bind(&slow); @@ -1059,19 +1033,32 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ lw(t0, FieldMemOperand(a0, String::kHashFieldOffset)); __ sra(at, t0, String::kHashShift); __ xor_(a3, a3, at); - __ And(a3, a3, Operand(KeyedLookupCache::kCapacityMask)); + int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask; + __ And(a3, a3, Operand(mask)); // Load the key (consisting of map and symbol) from the cache and // check for match. + Label load_in_object_property; + static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; + Label hit_on_nth_entry[kEntriesPerBucket]; ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys(isolate); __ li(t0, Operand(cache_keys)); __ sll(at, a3, kPointerSizeLog2 + 1); __ addu(t0, t0, at); - __ lw(t1, MemOperand(t0)); // Move t0 to symbol. - __ Addu(t0, t0, Operand(kPointerSize)); + + for (int i = 0; i < kEntriesPerBucket - 1; i++) { + Label try_next_entry; + __ lw(t1, MemOperand(t0, kPointerSize * i * 2)); + __ Branch(&try_next_entry, ne, a2, Operand(t1)); + __ lw(t1, MemOperand(t0, kPointerSize * (i * 2 + 1))); + __ Branch(&hit_on_nth_entry[i], eq, a0, Operand(t1)); + __ bind(&try_next_entry); + } + + __ lw(t1, MemOperand(t0, kPointerSize * (kEntriesPerBucket - 1) * 2)); __ Branch(&slow, ne, a2, Operand(t1)); - __ lw(t1, MemOperand(t0)); + __ lw(t1, MemOperand(t0, kPointerSize * ((kEntriesPerBucket - 1) * 2 + 1))); __ Branch(&slow, ne, a0, Operand(t1)); // Get field offset. @@ -1081,15 +1068,24 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // a3 : lookup cache index ExternalReference cache_field_offsets = ExternalReference::keyed_lookup_cache_field_offsets(isolate); - __ li(t0, Operand(cache_field_offsets)); - __ sll(at, a3, kPointerSizeLog2); - __ addu(at, t0, at); - __ lw(t1, MemOperand(at)); - __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset)); - __ Subu(t1, t1, t2); - __ Branch(&property_array_property, ge, t1, Operand(zero_reg)); + + // Hit on nth entry. + for (int i = kEntriesPerBucket - 1; i >= 0; i--) { + __ bind(&hit_on_nth_entry[i]); + __ li(t0, Operand(cache_field_offsets)); + __ sll(at, a3, kPointerSizeLog2); + __ addu(at, t0, at); + __ lw(t1, MemOperand(at, kPointerSize * i)); + __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset)); + __ Subu(t1, t1, t2); + __ Branch(&property_array_property, ge, t1, Operand(zero_reg)); + if (i != 0) { + __ Branch(&load_in_object_property); + } + } // Load in-object property. + __ bind(&load_in_object_property); __ lbu(t2, FieldMemOperand(a2, Map::kInstanceSizeOffset)); __ addu(t2, t2, t1); // Index from start of object. __ Subu(a1, a1, Operand(kHeapObjectTag)); // Remove the heap tag. @@ -1150,14 +1146,12 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { Register receiver = a1; Register index = a0; - Register scratch1 = a2; - Register scratch2 = a3; + Register scratch = a3; Register result = v0; StringCharAtGenerator char_at_generator(receiver, index, - scratch1, - scratch2, + scratch, result, &miss, // When not a string. &miss, // When not a number. @@ -1201,109 +1195,192 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // -- a2 : receiver // -- ra : return address // ----------------------------------- - - Label slow, fast, array, extra, exit; + Label slow, array, extra, check_if_double_array; + Label fast_object_with_map_check, fast_object_without_map_check; + Label fast_double_with_map_check, fast_double_without_map_check; + Label transition_smi_elements, finish_object_store, non_double_value; + Label transition_double_elements; // Register usage. Register value = a0; Register key = a1; Register receiver = a2; - Register elements = a3; // Elements array of the receiver. - // t0 is used as ip in the arm version. - // t3-t4 are used as temporaries. + Register receiver_map = a3; + Register elements_map = t2; + Register elements = t3; // Elements array of the receiver. + // t0 and t1 are used as general scratch registers. // Check that the key is a smi. __ JumpIfNotSmi(key, &slow); // Check that the object isn't a smi. __ JumpIfSmi(receiver, &slow); - // Get the map of the object. - __ lw(t3, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ lw(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); // Check that the receiver does not require access checks. We need // to do this because this generic stub does not perform map checks. - __ lbu(t0, FieldMemOperand(t3, Map::kBitFieldOffset)); + __ lbu(t0, FieldMemOperand(receiver_map, Map::kBitFieldOffset)); __ And(t0, t0, Operand(1 << Map::kIsAccessCheckNeeded)); __ Branch(&slow, ne, t0, Operand(zero_reg)); // Check if the object is a JS array or not. - __ lbu(t3, FieldMemOperand(t3, Map::kInstanceTypeOffset)); - - __ Branch(&array, eq, t3, Operand(JS_ARRAY_TYPE)); + __ lbu(t0, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset)); + __ Branch(&array, eq, t0, Operand(JS_ARRAY_TYPE)); // Check that the object is some kind of JSObject. - __ Branch(&slow, lt, t3, Operand(FIRST_JS_RECEIVER_TYPE)); - __ Branch(&slow, eq, t3, Operand(JS_PROXY_TYPE)); - __ Branch(&slow, eq, t3, Operand(JS_FUNCTION_PROXY_TYPE)); + __ Branch(&slow, lt, t0, Operand(FIRST_JS_OBJECT_TYPE)); // Object case: Check key against length in the elements array. __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - // Check that the object is in fast mode and writable. - __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex); - __ Branch(&slow, ne, t3, Operand(t0)); // Check array bounds. Both the key and the length of FixedArray are smis. __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Branch(&fast, lo, key, Operand(t0)); - // Fall thru to slow if un-tagged index >= length. + __ Branch(&fast_object_with_map_check, lo, key, Operand(t0)); // Slow case, handle jump to runtime. __ bind(&slow); - // Entry registers are intact. // a0: value. // a1: key. // a2: receiver. - GenerateRuntimeSetProperty(masm, strict_mode); // Extra capacity case: Check if there is extra capacity to // perform the store and update the length. Used for adding one // element to the array by writing to array[array.length]. - __ bind(&extra); + // Condition code from comparing key and array length is still available. // Only support writing to array[array.length]. __ Branch(&slow, ne, key, Operand(t0)); // Check for room in the elements backing store. // Both the key and the length of FixedArray are smis. __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); __ Branch(&slow, hs, key, Operand(t0)); + __ lw(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); + __ Branch(&check_if_double_array, ne, elements_map, + Operand(masm->isolate()->factory()->fixed_array_map())); // Calculate key + 1 as smi. - STATIC_ASSERT(0 == kSmiTag); - __ Addu(t3, key, Operand(Smi::FromInt(1))); - __ sw(t3, FieldMemOperand(receiver, JSArray::kLengthOffset)); - __ Branch(&fast); - + STATIC_ASSERT(kSmiTag == 0); + __ Addu(t0, key, Operand(Smi::FromInt(1))); + __ sw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ Branch(&fast_object_without_map_check); + + __ bind(&check_if_double_array); + __ Branch(&slow, ne, elements_map, + Operand(masm->isolate()->factory()->fixed_double_array_map())); + // Add 1 to key, and go to common element store code for doubles. + STATIC_ASSERT(kSmiTag == 0); + __ Addu(t0, key, Operand(Smi::FromInt(1))); + __ sw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ jmp(&fast_double_without_map_check); // Array case: Get the length and the elements array from the JS // array. Check that the array is in fast mode (and writable); if it // is the length is always a smi. - __ bind(&array); __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex); - __ Branch(&slow, ne, t3, Operand(t0)); // Check the key against the length in the array. __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset)); __ Branch(&extra, hs, key, Operand(t0)); // Fall through to fast case. - __ bind(&fast); - // Fast case, store the value to the elements backing store. - __ Addu(t4, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - __ sll(t1, key, kPointerSizeLog2 - kSmiTagSize); - __ Addu(t4, t4, Operand(t1)); - __ sw(value, MemOperand(t4)); - // Skip write barrier if the written value is a smi. - __ JumpIfSmi(value, &exit); - + __ bind(&fast_object_with_map_check); + Register scratch_value = t0; + Register address = t1; + __ lw(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); + __ Branch(&fast_double_with_map_check, ne, elements_map, + Operand(masm->isolate()->factory()->fixed_array_map())); + __ bind(&fast_object_without_map_check); + // Smi stores don't require further checks. + Label non_smi_value; + __ JumpIfNotSmi(value, &non_smi_value); + // It's irrelevant whether array is smi-only or not when writing a smi. + __ Addu(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sll(scratch_value, key, kPointerSizeLog2 - kSmiTagSize); + __ Addu(address, address, scratch_value); + __ sw(value, MemOperand(address)); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, value); + + __ bind(&non_smi_value); + // Escape to elements kind transition case. + __ CheckFastObjectElements(receiver_map, scratch_value, + &transition_smi_elements); + // Fast elements array, store the value to the elements backing store. + __ bind(&finish_object_store); + __ Addu(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sll(scratch_value, key, kPointerSizeLog2 - kSmiTagSize); + __ Addu(address, address, scratch_value); + __ sw(value, MemOperand(address)); // Update write barrier for the elements array address. - __ Subu(t3, t4, Operand(elements)); - - __ RecordWrite(elements, Operand(t3), t4, t5); - __ bind(&exit); - - __ mov(v0, a0); // Return the value written. + __ mov(v0, value); // Preserve the value which is returned. + __ RecordWrite(elements, + address, + value, + kRAHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); __ Ret(); + + __ bind(&fast_double_with_map_check); + // Check for fast double array case. If this fails, call through to the + // runtime. + __ Branch(&slow, ne, elements_map, + Operand(masm->isolate()->factory()->fixed_double_array_map())); + __ bind(&fast_double_without_map_check); + __ StoreNumberToDoubleElements(value, + key, + receiver, + elements, + a3, + t0, + t1, + t2, + &transition_double_elements); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, value); + + __ bind(&transition_smi_elements); + // Transition the array appropriately depending on the value type. + __ lw(t0, FieldMemOperand(value, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + __ Branch(&non_double_value, ne, t0, Operand(at)); + + // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // FAST_DOUBLE_ELEMENTS and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_DOUBLE_ELEMENTS, + receiver_map, + t0, + &slow); + ASSERT(receiver_map.is(a3)); // Transition code expects map in a3 + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&fast_double_without_map_check); + + __ bind(&non_double_value); + // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + receiver_map, + t0, + &slow); + ASSERT(receiver_map.is(a3)); // Transition code expects map in a3 + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); + + __ bind(&transition_double_elements); + // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a + // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and + // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, + FAST_ELEMENTS, + receiver_map, + t0, + &slow); + ASSERT(receiver_map.is(a3)); // Transition code expects map in a3 + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); } @@ -1382,6 +1459,47 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- a2 : receiver + // -- a3 : target map + // -- ra : return address + // ----------------------------------- + // Must return the modified receiver in v0. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a2); + __ bind(&fail); + } + + __ push(a2); + __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1); +} + + +void KeyedStoreIC::GenerateTransitionElementsDoubleToObject( + MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- a2 : receiver + // -- a3 : target map + // -- ra : return address + // ----------------------------------- + // Must return the modified receiver in v0. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a2); + __ bind(&fail); + } + + __ push(a2); + __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1); +} + + void StoreIC::GenerateMegamorphic(MacroAssembler* masm, StrictModeFlag strict_mode) { // ----------- S t a t e ------------- @@ -1426,11 +1544,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { // -- ra : return address // ----------------------------------- // - // This accepts as a receiver anything JSObject::SetElementsLength accepts - // (currently anything except for external and pixel arrays which means - // anything with elements of FixedArray type.), but currently is restricted - // to JSArray. - // Value must be a number, but only smis are accepted as the most common case. + // This accepts as a receiver anything JSArray::SetElementsLength accepts + // (currently anything except for external arrays which means anything with + // elements of FixedArray type). Value must be a number, but only smis are + // accepted as the most common case. Label miss; @@ -1452,6 +1569,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { __ GetObjectType(scratch, scratch, scratch); __ Branch(&miss, ne, scratch, Operand(FIXED_ARRAY_TYPE)); + // Check that the array has fast properties, otherwise the length + // property might have been redefined. + // TODO(mstarzinger): Port this check to MIPS. + // Check that value is a smi. __ JumpIfNotSmi(value, &miss); @@ -1521,11 +1642,9 @@ Condition CompareIC::ComputeCondition(Token::Value op) { case Token::LT: return lt; case Token::GT: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return lt; + return gt; case Token::LTE: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return ge; + return le; case Token::GTE: return ge; default: @@ -1545,6 +1664,9 @@ void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { rewritten = stub.GetCode(); } else { ICCompareStub stub(op_, state); + if (state == KNOWN_OBJECTS) { + stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map())); + } rewritten = stub.GetCode(); } set_target(*rewritten); @@ -1572,7 +1694,8 @@ void PatchInlinedSmiCode(Address address) { // If the instruction following the call is not a andi at, rx, #yyy, nothing // was inlined. Instr instr = Assembler::instr_at(andi_instruction_address); - if (!Assembler::IsAndImmediate(instr)) { + if (!(Assembler::IsAndImmediate(instr) && + Assembler::GetRt(instr) == (uint32_t)zero_reg.code())) { return; } diff --git a/deps/v8/src/mips/lithium-codegen-mips.cc b/deps/v8/src/mips/lithium-codegen-mips.cc new file mode 100644 index 0000000000..aead65c440 --- /dev/null +++ b/deps/v8/src/mips/lithium-codegen-mips.cc @@ -0,0 +1,4745 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "mips/lithium-codegen-mips.h" +#include "mips/lithium-gap-resolver-mips.h" +#include "code-stubs.h" +#include "stub-cache.h" + +namespace v8 { +namespace internal { + + +class SafepointGenerator : public CallWrapper { + public: + SafepointGenerator(LCodeGen* codegen, + LPointerMap* pointers, + Safepoint::DeoptMode mode) + : codegen_(codegen), + pointers_(pointers), + deopt_mode_(mode) { } + virtual ~SafepointGenerator() { } + + virtual void BeforeCall(int call_size) const { } + + virtual void AfterCall() const { + codegen_->RecordSafepoint(pointers_, deopt_mode_); + } + + private: + LCodeGen* codegen_; + LPointerMap* pointers_; + Safepoint::DeoptMode deopt_mode_; +}; + + +#define __ masm()-> + +bool LCodeGen::GenerateCode() { + HPhase phase("Code generation", chunk()); + ASSERT(is_unused()); + status_ = GENERATING; + CpuFeatures::Scope scope(FPU); + + CodeStub::GenerateFPStubs(); + + // Open a frame scope to indicate that there is a frame on the stack. The + // NONE indicates that the scope shouldn't actually generate code to set up + // the frame (that is done in GeneratePrologue). + FrameScope frame_scope(masm_, StackFrame::NONE); + + return GeneratePrologue() && + GenerateBody() && + GenerateDeferredCode() && + GenerateSafepointTable(); +} + + +void LCodeGen::FinishCode(Handle<Code> code) { + ASSERT(is_done()); + code->set_stack_slots(GetStackSlotCount()); + code->set_safepoint_table_offset(safepoints_.GetCodeOffset()); + PopulateDeoptimizationData(code); +} + + +void LCodeGen::Abort(const char* format, ...) { + if (FLAG_trace_bailout) { + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); + PrintF("Aborting LCodeGen in @\"%s\": ", *name); + va_list arguments; + va_start(arguments, format); + OS::VPrint(format, arguments); + va_end(arguments); + PrintF("\n"); + } + status_ = ABORTED; +} + + +void LCodeGen::Comment(const char* format, ...) { + if (!FLAG_code_comments) return; + char buffer[4 * KB]; + StringBuilder builder(buffer, ARRAY_SIZE(buffer)); + va_list arguments; + va_start(arguments, format); + builder.AddFormattedList(format, arguments); + va_end(arguments); + + // Copy the string before recording it in the assembler to avoid + // issues when the stack allocated buffer goes out of scope. + size_t length = builder.position(); + Vector<char> copy = Vector<char>::New(length + 1); + memcpy(copy.start(), builder.Finalize(), copy.length()); + masm()->RecordComment(copy.start()); +} + + +bool LCodeGen::GeneratePrologue() { + ASSERT(is_generating()); + +#ifdef DEBUG + if (strlen(FLAG_stop_at) > 0 && + info_->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { + __ stop("stop_at"); + } +#endif + + // a1: Callee's JS function. + // cp: Callee's context. + // fp: Caller's frame pointer. + // lr: Caller's pc. + + // Strict mode functions and builtins need to replace the receiver + // with undefined when called as functions (without an explicit + // receiver object). r5 is zero for method calls and non-zero for + // function calls. + if (!info_->is_classic_mode() || info_->is_native()) { + Label ok; + __ Branch(&ok, eq, t1, Operand(zero_reg)); + + int receiver_offset = scope()->num_parameters() * kPointerSize; + __ LoadRoot(a2, Heap::kUndefinedValueRootIndex); + __ sw(a2, MemOperand(sp, receiver_offset)); + __ bind(&ok); + } + + __ Push(ra, fp, cp, a1); + __ Addu(fp, sp, Operand(2 * kPointerSize)); // Adj. FP to point to saved FP. + + // Reserve space for the stack slots needed by the code. + int slots = GetStackSlotCount(); + if (slots > 0) { + if (FLAG_debug_code) { + __ li(a0, Operand(slots)); + __ li(a2, Operand(kSlotsZapValue)); + Label loop; + __ bind(&loop); + __ push(a2); + __ Subu(a0, a0, 1); + __ Branch(&loop, ne, a0, Operand(zero_reg)); + } else { + __ Subu(sp, sp, Operand(slots * kPointerSize)); + } + } + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; + if (heap_slots > 0) { + Comment(";;; Allocate local context"); + // Argument to NewContext is the function, which is in a1. + __ push(a1); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewFunctionContext, 1); + } + RecordSafepoint(Safepoint::kNoLazyDeopt); + // Context is returned in both v0 and cp. It replaces the context + // passed to us. It's saved in the stack and kept live in cp. + __ sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + // Copy any necessary parameters into the context. + int num_parameters = scope()->num_parameters(); + for (int i = 0; i < num_parameters; i++) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { + int parameter_offset = StandardFrameConstants::kCallerSPOffset + + (num_parameters - 1 - i) * kPointerSize; + // Load parameter from stack. + __ lw(a0, MemOperand(fp, parameter_offset)); + // Store it in the context. + MemOperand target = ContextOperand(cp, var->index()); + __ sw(a0, target); + // Update the write barrier. This clobbers a3 and a0. + __ RecordWriteContextSlot( + cp, target.offset(), a0, a3, kRAHasBeenSaved, kSaveFPRegs); + } + } + Comment(";;; End allocate local context"); + } + + // Trace the call. + if (FLAG_trace) { + __ CallRuntime(Runtime::kTraceEnter, 0); + } + EnsureSpaceForLazyDeopt(); + return !is_aborted(); +} + + +bool LCodeGen::GenerateBody() { + ASSERT(is_generating()); + bool emit_instructions = true; + for (current_instruction_ = 0; + !is_aborted() && current_instruction_ < instructions_->length(); + current_instruction_++) { + LInstruction* instr = instructions_->at(current_instruction_); + if (instr->IsLabel()) { + LLabel* label = LLabel::cast(instr); + emit_instructions = !label->HasReplacement(); + } + + if (emit_instructions) { + Comment(";;; @%d: %s.", current_instruction_, instr->Mnemonic()); + instr->CompileToNative(this); + } + } + return !is_aborted(); +} + + +bool LCodeGen::GenerateDeferredCode() { + ASSERT(is_generating()); + if (deferred_.length() > 0) { + for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { + LDeferredCode* code = deferred_[i]; + __ bind(code->entry()); + Comment(";;; Deferred code @%d: %s.", + code->instruction_index(), + code->instr()->Mnemonic()); + code->Generate(); + __ jmp(code->exit()); + } + } + // Deferred code is the last part of the instruction sequence. Mark + // the generated code as done unless we bailed out. + if (!is_aborted()) status_ = DONE; + return !is_aborted(); +} + + +bool LCodeGen::GenerateDeoptJumpTable() { + // TODO(plind): not clear that this will have advantage for MIPS. + // Skipping it for now. Raised issue #100 for this. + Abort("Unimplemented: %s", "GenerateDeoptJumpTable"); + return false; +} + + +bool LCodeGen::GenerateSafepointTable() { + ASSERT(is_done()); + safepoints_.Emit(masm(), GetStackSlotCount()); + return !is_aborted(); +} + + +Register LCodeGen::ToRegister(int index) const { + return Register::FromAllocationIndex(index); +} + + +DoubleRegister LCodeGen::ToDoubleRegister(int index) const { + return DoubleRegister::FromAllocationIndex(index); +} + + +Register LCodeGen::ToRegister(LOperand* op) const { + ASSERT(op->IsRegister()); + return ToRegister(op->index()); +} + + +Register LCodeGen::EmitLoadRegister(LOperand* op, Register scratch) { + if (op->IsRegister()) { + return ToRegister(op->index()); + } else if (op->IsConstantOperand()) { + LConstantOperand* const_op = LConstantOperand::cast(op); + Handle<Object> literal = chunk_->LookupLiteral(const_op); + Representation r = chunk_->LookupLiteralRepresentation(const_op); + if (r.IsInteger32()) { + ASSERT(literal->IsNumber()); + __ li(scratch, Operand(static_cast<int32_t>(literal->Number()))); + } else if (r.IsDouble()) { + Abort("EmitLoadRegister: Unsupported double immediate."); + } else { + ASSERT(r.IsTagged()); + if (literal->IsSmi()) { + __ li(scratch, Operand(literal)); + } else { + __ LoadHeapObject(scratch, Handle<HeapObject>::cast(literal)); + } + } + return scratch; + } else if (op->IsStackSlot() || op->IsArgument()) { + __ lw(scratch, ToMemOperand(op)); + return scratch; + } + UNREACHABLE(); + return scratch; +} + + +DoubleRegister LCodeGen::ToDoubleRegister(LOperand* op) const { + ASSERT(op->IsDoubleRegister()); + return ToDoubleRegister(op->index()); +} + + +DoubleRegister LCodeGen::EmitLoadDoubleRegister(LOperand* op, + FloatRegister flt_scratch, + DoubleRegister dbl_scratch) { + if (op->IsDoubleRegister()) { + return ToDoubleRegister(op->index()); + } else if (op->IsConstantOperand()) { + LConstantOperand* const_op = LConstantOperand::cast(op); + Handle<Object> literal = chunk_->LookupLiteral(const_op); + Representation r = chunk_->LookupLiteralRepresentation(const_op); + if (r.IsInteger32()) { + ASSERT(literal->IsNumber()); + __ li(at, Operand(static_cast<int32_t>(literal->Number()))); + __ mtc1(at, flt_scratch); + __ cvt_d_w(dbl_scratch, flt_scratch); + return dbl_scratch; + } else if (r.IsDouble()) { + Abort("unsupported double immediate"); + } else if (r.IsTagged()) { + Abort("unsupported tagged immediate"); + } + } else if (op->IsStackSlot() || op->IsArgument()) { + MemOperand mem_op = ToMemOperand(op); + __ ldc1(dbl_scratch, mem_op); + return dbl_scratch; + } + UNREACHABLE(); + return dbl_scratch; +} + + +Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const { + Handle<Object> literal = chunk_->LookupLiteral(op); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); + return literal; +} + + +bool LCodeGen::IsInteger32(LConstantOperand* op) const { + return chunk_->LookupLiteralRepresentation(op).IsInteger32(); +} + + +int LCodeGen::ToInteger32(LConstantOperand* op) const { + Handle<Object> value = chunk_->LookupLiteral(op); + ASSERT(chunk_->LookupLiteralRepresentation(op).IsInteger32()); + ASSERT(static_cast<double>(static_cast<int32_t>(value->Number())) == + value->Number()); + return static_cast<int32_t>(value->Number()); +} + + +double LCodeGen::ToDouble(LConstantOperand* op) const { + Handle<Object> value = chunk_->LookupLiteral(op); + return value->Number(); +} + + +Operand LCodeGen::ToOperand(LOperand* op) { + if (op->IsConstantOperand()) { + LConstantOperand* const_op = LConstantOperand::cast(op); + Handle<Object> literal = chunk_->LookupLiteral(const_op); + Representation r = chunk_->LookupLiteralRepresentation(const_op); + if (r.IsInteger32()) { + ASSERT(literal->IsNumber()); + return Operand(static_cast<int32_t>(literal->Number())); + } else if (r.IsDouble()) { + Abort("ToOperand Unsupported double immediate."); + } + ASSERT(r.IsTagged()); + return Operand(literal); + } else if (op->IsRegister()) { + return Operand(ToRegister(op)); + } else if (op->IsDoubleRegister()) { + Abort("ToOperand IsDoubleRegister unimplemented"); + return Operand(0); + } + // Stack slots not implemented, use ToMemOperand instead. + UNREACHABLE(); + return Operand(0); +} + + +MemOperand LCodeGen::ToMemOperand(LOperand* op) const { + ASSERT(!op->IsRegister()); + ASSERT(!op->IsDoubleRegister()); + ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot()); + int index = op->index(); + if (index >= 0) { + // Local or spill slot. Skip the frame pointer, function, and + // context in the fixed part of the frame. + return MemOperand(fp, -(index + 3) * kPointerSize); + } else { + // Incoming parameter. Skip the return address. + return MemOperand(fp, -(index - 1) * kPointerSize); + } +} + + +MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const { + ASSERT(op->IsDoubleStackSlot()); + int index = op->index(); + if (index >= 0) { + // Local or spill slot. Skip the frame pointer, function, context, + // and the first word of the double in the fixed part of the frame. + return MemOperand(fp, -(index + 3) * kPointerSize + kPointerSize); + } else { + // Incoming parameter. Skip the return address and the first word of + // the double. + return MemOperand(fp, -(index - 1) * kPointerSize + kPointerSize); + } +} + + +void LCodeGen::WriteTranslation(LEnvironment* environment, + Translation* translation) { + if (environment == NULL) return; + + // The translation includes one command per value in the environment. + int translation_size = environment->values()->length(); + // The output frame height does not include the parameters. + int height = translation_size - environment->parameter_count(); + + WriteTranslation(environment->outer(), translation); + int closure_id = DefineDeoptimizationLiteral(environment->closure()); + if (environment->is_arguments_adaptor()) { + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + } else { + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + } + for (int i = 0; i < translation_size; ++i) { + LOperand* value = environment->values()->at(i); + // spilled_registers_ and spilled_double_registers_ are either + // both NULL or both set. + if (environment->spilled_registers() != NULL && value != NULL) { + if (value->IsRegister() && + environment->spilled_registers()[value->index()] != NULL) { + translation->MarkDuplicate(); + AddToTranslation(translation, + environment->spilled_registers()[value->index()], + environment->HasTaggedValueAt(i)); + } else if ( + value->IsDoubleRegister() && + environment->spilled_double_registers()[value->index()] != NULL) { + translation->MarkDuplicate(); + AddToTranslation( + translation, + environment->spilled_double_registers()[value->index()], + false); + } + } + + AddToTranslation(translation, value, environment->HasTaggedValueAt(i)); + } +} + + +void LCodeGen::AddToTranslation(Translation* translation, + LOperand* op, + bool is_tagged) { + if (op == NULL) { + // TODO(twuerthinger): Introduce marker operands to indicate that this value + // is not present and must be reconstructed from the deoptimizer. Currently + // this is only used for the arguments object. + translation->StoreArgumentsObject(); + } else if (op->IsStackSlot()) { + if (is_tagged) { + translation->StoreStackSlot(op->index()); + } else { + translation->StoreInt32StackSlot(op->index()); + } + } else if (op->IsDoubleStackSlot()) { + translation->StoreDoubleStackSlot(op->index()); + } else if (op->IsArgument()) { + ASSERT(is_tagged); + int src_index = GetStackSlotCount() + op->index(); + translation->StoreStackSlot(src_index); + } else if (op->IsRegister()) { + Register reg = ToRegister(op); + if (is_tagged) { + translation->StoreRegister(reg); + } else { + translation->StoreInt32Register(reg); + } + } else if (op->IsDoubleRegister()) { + DoubleRegister reg = ToDoubleRegister(op); + translation->StoreDoubleRegister(reg); + } else if (op->IsConstantOperand()) { + Handle<Object> literal = chunk()->LookupLiteral(LConstantOperand::cast(op)); + int src_index = DefineDeoptimizationLiteral(literal); + translation->StoreLiteral(src_index); + } else { + UNREACHABLE(); + } +} + + +void LCodeGen::CallCode(Handle<Code> code, + RelocInfo::Mode mode, + LInstruction* instr) { + CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT); +} + + +void LCodeGen::CallCodeGeneric(Handle<Code> code, + RelocInfo::Mode mode, + LInstruction* instr, + SafepointMode safepoint_mode) { + ASSERT(instr != NULL); + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + __ Call(code, mode); + RecordSafepointWithLazyDeopt(instr, safepoint_mode); +} + + +void LCodeGen::CallRuntime(const Runtime::Function* function, + int num_arguments, + LInstruction* instr) { + ASSERT(instr != NULL); + LPointerMap* pointers = instr->pointer_map(); + ASSERT(pointers != NULL); + RecordPosition(pointers->position()); + + __ CallRuntime(function, num_arguments); + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); +} + + +void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, + int argc, + LInstruction* instr) { + __ CallRuntimeSaveDoubles(id); + RecordSafepointWithRegisters( + instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); +} + + +void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode) { + if (!environment->HasBeenRegistered()) { + // Physical stack frame layout: + // -x ............. -4 0 ..................................... y + // [incoming arguments] [spill slots] [pushed outgoing arguments] + + // Layout of the environment: + // 0 ..................................................... size-1 + // [parameters] [locals] [expression stack including arguments] + + // Layout of the translation: + // 0 ........................................................ size - 1 + 4 + // [expression stack including arguments] [locals] [4 words] [parameters] + // |>------------ translation_size ------------<| + + int frame_count = 0; + int jsframe_count = 0; + for (LEnvironment* e = environment; e != NULL; e = e->outer()) { + ++frame_count; + if (!e->is_arguments_adaptor()) { + ++jsframe_count; + } + } + Translation translation(&translations_, frame_count, jsframe_count); + WriteTranslation(environment, &translation); + int deoptimization_index = deoptimizations_.length(); + int pc_offset = masm()->pc_offset(); + environment->Register(deoptimization_index, + translation.index(), + (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); + deoptimizations_.Add(environment); + } +} + + +void LCodeGen::DeoptimizeIf(Condition cc, + LEnvironment* environment, + Register src1, + const Operand& src2) { + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); + ASSERT(environment->HasBeenRegistered()); + int id = environment->deoptimization_index(); + Address entry = Deoptimizer::GetDeoptimizationEntry(id, Deoptimizer::EAGER); + ASSERT(entry != NULL); + if (entry == NULL) { + Abort("bailout was not prepared"); + return; + } + + ASSERT(FLAG_deopt_every_n_times < 2); // Other values not supported on MIPS. + + if (FLAG_deopt_every_n_times == 1 && + info_->shared_info()->opt_count() == id) { + __ Jump(entry, RelocInfo::RUNTIME_ENTRY); + return; + } + + if (FLAG_trap_on_deopt) { + Label skip; + if (cc != al) { + __ Branch(&skip, NegateCondition(cc), src1, src2); + } + __ stop("trap_on_deopt"); + __ bind(&skip); + } + + if (cc == al) { + __ Jump(entry, RelocInfo::RUNTIME_ENTRY); + } else { + // TODO(plind): The Arm port is a little different here, due to their + // DeOpt jump table, which is not used for Mips yet. + __ Jump(entry, RelocInfo::RUNTIME_ENTRY, cc, src1, src2); + } +} + + +void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) { + int length = deoptimizations_.length(); + if (length == 0) return; + Handle<DeoptimizationInputData> data = + factory()->NewDeoptimizationInputData(length, TENURED); + + Handle<ByteArray> translations = translations_.CreateByteArray(); + data->SetTranslationByteArray(*translations); + data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); + + Handle<FixedArray> literals = + factory()->NewFixedArray(deoptimization_literals_.length(), TENURED); + for (int i = 0; i < deoptimization_literals_.length(); i++) { + literals->set(i, *deoptimization_literals_[i]); + } + data->SetLiteralArray(*literals); + + data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id())); + data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_)); + + // Populate the deoptimization entries. + for (int i = 0; i < length; i++) { + LEnvironment* env = deoptimizations_[i]; + data->SetAstId(i, Smi::FromInt(env->ast_id())); + data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); + data->SetArgumentsStackHeight(i, + Smi::FromInt(env->arguments_stack_height())); + data->SetPc(i, Smi::FromInt(env->pc_offset())); + } + code->set_deoptimization_data(*data); +} + + +int LCodeGen::DefineDeoptimizationLiteral(Handle<Object> literal) { + int result = deoptimization_literals_.length(); + for (int i = 0; i < deoptimization_literals_.length(); ++i) { + if (deoptimization_literals_[i].is_identical_to(literal)) return i; + } + deoptimization_literals_.Add(literal); + return result; +} + + +void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() { + ASSERT(deoptimization_literals_.length() == 0); + + const ZoneList<Handle<JSFunction> >* inlined_closures = + chunk()->inlined_closures(); + + for (int i = 0, length = inlined_closures->length(); + i < length; + i++) { + DefineDeoptimizationLiteral(inlined_closures->at(i)); + } + + inlined_function_count_ = deoptimization_literals_.length(); +} + + +void LCodeGen::RecordSafepointWithLazyDeopt( + LInstruction* instr, SafepointMode safepoint_mode) { + if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { + RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt); + } else { + ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + RecordSafepointWithRegisters( + instr->pointer_map(), 0, Safepoint::kLazyDeopt); + } +} + + +void LCodeGen::RecordSafepoint( + LPointerMap* pointers, + Safepoint::Kind kind, + int arguments, + Safepoint::DeoptMode deopt_mode) { + ASSERT(expected_safepoint_kind_ == kind); + + const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands(); + Safepoint safepoint = safepoints_.DefineSafepoint(masm(), + kind, arguments, deopt_mode); + for (int i = 0; i < operands->length(); i++) { + LOperand* pointer = operands->at(i); + if (pointer->IsStackSlot()) { + safepoint.DefinePointerSlot(pointer->index()); + } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) { + safepoint.DefinePointerRegister(ToRegister(pointer)); + } + } + if (kind & Safepoint::kWithRegisters) { + // Register cp always contains a pointer to the context. + safepoint.DefinePointerRegister(cp); + } +} + + +void LCodeGen::RecordSafepoint(LPointerMap* pointers, + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint(pointers, Safepoint::kSimple, 0, deopt_mode); +} + + +void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) { + LPointerMap empty_pointers(RelocInfo::kNoPosition); + RecordSafepoint(&empty_pointers, deopt_mode); +} + + +void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, + int arguments, + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint( + pointers, Safepoint::kWithRegisters, arguments, deopt_mode); +} + + +void LCodeGen::RecordSafepointWithRegistersAndDoubles( + LPointerMap* pointers, + int arguments, + Safepoint::DeoptMode deopt_mode) { + RecordSafepoint( + pointers, Safepoint::kWithRegistersAndDoubles, arguments, deopt_mode); +} + + +void LCodeGen::RecordPosition(int position) { + if (position == RelocInfo::kNoPosition) return; + masm()->positions_recorder()->RecordPosition(position); +} + + +void LCodeGen::DoLabel(LLabel* label) { + if (label->is_loop_header()) { + Comment(";;; B%d - LOOP entry", label->block_id()); + } else { + Comment(";;; B%d", label->block_id()); + } + __ bind(label->label()); + current_block_ = label->block_id(); + DoGap(label); +} + + +void LCodeGen::DoParallelMove(LParallelMove* move) { + resolver_.Resolve(move); +} + + +void LCodeGen::DoGap(LGap* gap) { + for (int i = LGap::FIRST_INNER_POSITION; + i <= LGap::LAST_INNER_POSITION; + i++) { + LGap::InnerPosition inner_pos = static_cast<LGap::InnerPosition>(i); + LParallelMove* move = gap->GetParallelMove(inner_pos); + if (move != NULL) DoParallelMove(move); + } +} + + +void LCodeGen::DoInstructionGap(LInstructionGap* instr) { + DoGap(instr); +} + + +void LCodeGen::DoParameter(LParameter* instr) { + // Nothing to do. +} + + +void LCodeGen::DoCallStub(LCallStub* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + switch (instr->hydrogen()->major_key()) { + case CodeStub::RegExpConstructResult: { + RegExpConstructResultStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::RegExpExec: { + RegExpExecStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::SubString: { + SubStringStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::NumberToString: { + NumberToStringStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::StringAdd: { + StringAddStub stub(NO_STRING_ADD_FLAGS); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::StringCompare: { + StringCompareStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + case CodeStub::TranscendentalCache: { + __ lw(a0, MemOperand(sp, 0)); + TranscendentalCacheStub stub(instr->transcendental_type(), + TranscendentalCacheStub::TAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + break; + } + default: + UNREACHABLE(); + } +} + + +void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { + // Nothing to do. +} + + +void LCodeGen::DoModI(LModI* instr) { + Register scratch = scratch0(); + const Register left = ToRegister(instr->InputAt(0)); + const Register result = ToRegister(instr->result()); + + Label done; + + if (instr->hydrogen()->HasPowerOf2Divisor()) { + Register scratch = scratch0(); + ASSERT(!left.is(scratch)); + __ mov(scratch, left); + int32_t p2constant = HConstant::cast( + instr->hydrogen()->right())->Integer32Value(); + ASSERT(p2constant != 0); + // Result always takes the sign of the dividend (left). + p2constant = abs(p2constant); + + Label positive_dividend; + __ Branch(USE_DELAY_SLOT, &positive_dividend, ge, left, Operand(zero_reg)); + __ subu(result, zero_reg, left); + __ And(result, result, p2constant - 1); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + DeoptimizeIf(eq, instr->environment(), result, Operand(zero_reg)); + } + __ Branch(USE_DELAY_SLOT, &done); + __ subu(result, zero_reg, result); + __ bind(&positive_dividend); + __ And(result, scratch, p2constant - 1); + } else { + // div runs in the background while we check for special cases. + Register right = EmitLoadRegister(instr->InputAt(1), scratch); + __ div(left, right); + + // Check for x % 0. + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { + DeoptimizeIf(eq, instr->environment(), right, Operand(zero_reg)); + } + + __ Branch(USE_DELAY_SLOT, &done, ge, left, Operand(zero_reg)); + __ mfhi(result); + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + DeoptimizeIf(eq, instr->environment(), result, Operand(zero_reg)); + } + } + __ bind(&done); +} + + +void LCodeGen::DoDivI(LDivI* instr) { + const Register left = ToRegister(instr->InputAt(0)); + const Register right = ToRegister(instr->InputAt(1)); + const Register result = ToRegister(instr->result()); + + // On MIPS div is asynchronous - it will run in the background while we + // check for special cases. + __ div(left, right); + + // Check for x / 0. + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { + DeoptimizeIf(eq, instr->environment(), right, Operand(zero_reg)); + } + + // Check for (0 / -x) that will produce negative zero. + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + Label left_not_zero; + __ Branch(&left_not_zero, ne, left, Operand(zero_reg)); + DeoptimizeIf(lt, instr->environment(), right, Operand(zero_reg)); + __ bind(&left_not_zero); + } + + // Check for (-kMinInt / -1). + if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { + Label left_not_min_int; + __ Branch(&left_not_min_int, ne, left, Operand(kMinInt)); + DeoptimizeIf(eq, instr->environment(), right, Operand(-1)); + __ bind(&left_not_min_int); + } + + __ mfhi(result); + DeoptimizeIf(ne, instr->environment(), result, Operand(zero_reg)); + __ mflo(result); +} + + +void LCodeGen::DoMulI(LMulI* instr) { + Register scratch = scratch0(); + Register result = ToRegister(instr->result()); + // Note that result may alias left. + Register left = ToRegister(instr->InputAt(0)); + LOperand* right_op = instr->InputAt(1); + + bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); + bool bailout_on_minus_zero = + instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero); + + if (right_op->IsConstantOperand() && !can_overflow) { + // Use optimized code for specific constants. + int32_t constant = ToInteger32(LConstantOperand::cast(right_op)); + + if (bailout_on_minus_zero && (constant < 0)) { + // The case of a null constant will be handled separately. + // If constant is negative and left is null, the result should be -0. + DeoptimizeIf(eq, instr->environment(), left, Operand(zero_reg)); + } + + switch (constant) { + case -1: + __ Subu(result, zero_reg, left); + break; + case 0: + if (bailout_on_minus_zero) { + // If left is strictly negative and the constant is null, the + // result is -0. Deoptimize if required, otherwise return 0. + DeoptimizeIf(lt, instr->environment(), left, Operand(zero_reg)); + } + __ mov(result, zero_reg); + break; + case 1: + // Nothing to do. + __ Move(result, left); + break; + default: + // Multiplying by powers of two and powers of two plus or minus + // one can be done faster with shifted operands. + // For other constants we emit standard code. + int32_t mask = constant >> 31; + uint32_t constant_abs = (constant + mask) ^ mask; + + if (IsPowerOf2(constant_abs) || + IsPowerOf2(constant_abs - 1) || + IsPowerOf2(constant_abs + 1)) { + if (IsPowerOf2(constant_abs)) { + int32_t shift = WhichPowerOf2(constant_abs); + __ sll(result, left, shift); + } else if (IsPowerOf2(constant_abs - 1)) { + int32_t shift = WhichPowerOf2(constant_abs - 1); + __ sll(result, left, shift); + __ Addu(result, result, left); + } else if (IsPowerOf2(constant_abs + 1)) { + int32_t shift = WhichPowerOf2(constant_abs + 1); + __ sll(result, left, shift); + __ Subu(result, result, left); + } + + // Correct the sign of the result is the constant is negative. + if (constant < 0) { + __ Subu(result, zero_reg, result); + } + + } else { + // Generate standard code. + __ li(at, constant); + __ mul(result, left, at); + } + } + + } else { + Register right = EmitLoadRegister(right_op, scratch); + if (bailout_on_minus_zero) { + __ Or(ToRegister(instr->TempAt(0)), left, right); + } + + if (can_overflow) { + // hi:lo = left * right. + __ mult(left, right); + __ mfhi(scratch); + __ mflo(result); + __ sra(at, result, 31); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(at)); + } else { + __ mul(result, left, right); + } + + if (bailout_on_minus_zero) { + // Bail out if the result is supposed to be negative zero. + Label done; + __ Branch(&done, ne, result, Operand(zero_reg)); + DeoptimizeIf(lt, + instr->environment(), + ToRegister(instr->TempAt(0)), + Operand(zero_reg)); + __ bind(&done); + } + } +} + + +void LCodeGen::DoBitI(LBitI* instr) { + LOperand* left_op = instr->InputAt(0); + LOperand* right_op = instr->InputAt(1); + ASSERT(left_op->IsRegister()); + Register left = ToRegister(left_op); + Register result = ToRegister(instr->result()); + Operand right(no_reg); + + if (right_op->IsStackSlot() || right_op->IsArgument()) { + right = Operand(EmitLoadRegister(right_op, at)); + } else { + ASSERT(right_op->IsRegister() || right_op->IsConstantOperand()); + right = ToOperand(right_op); + } + + switch (instr->op()) { + case Token::BIT_AND: + __ And(result, left, right); + break; + case Token::BIT_OR: + __ Or(result, left, right); + break; + case Token::BIT_XOR: + __ Xor(result, left, right); + break; + default: + UNREACHABLE(); + break; + } +} + + +void LCodeGen::DoShiftI(LShiftI* instr) { + // Both 'left' and 'right' are "used at start" (see LCodeGen::DoShift), so + // result may alias either of them. + LOperand* right_op = instr->InputAt(1); + Register left = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + + if (right_op->IsRegister()) { + // No need to mask the right operand on MIPS, it is built into the variable + // shift instructions. + switch (instr->op()) { + case Token::SAR: + __ srav(result, left, ToRegister(right_op)); + break; + case Token::SHR: + __ srlv(result, left, ToRegister(right_op)); + if (instr->can_deopt()) { + DeoptimizeIf(lt, instr->environment(), result, Operand(zero_reg)); + } + break; + case Token::SHL: + __ sllv(result, left, ToRegister(right_op)); + break; + default: + UNREACHABLE(); + break; + } + } else { + // Mask the right_op operand. + int value = ToInteger32(LConstantOperand::cast(right_op)); + uint8_t shift_count = static_cast<uint8_t>(value & 0x1F); + switch (instr->op()) { + case Token::SAR: + if (shift_count != 0) { + __ sra(result, left, shift_count); + } else { + __ Move(result, left); + } + break; + case Token::SHR: + if (shift_count != 0) { + __ srl(result, left, shift_count); + } else { + if (instr->can_deopt()) { + __ And(at, left, Operand(0x80000000)); + DeoptimizeIf(ne, instr->environment(), at, Operand(zero_reg)); + } + __ Move(result, left); + } + break; + case Token::SHL: + if (shift_count != 0) { + __ sll(result, left, shift_count); + } else { + __ Move(result, left); + } + break; + default: + UNREACHABLE(); + break; + } + } +} + + +void LCodeGen::DoSubI(LSubI* instr) { + LOperand* left = instr->InputAt(0); + LOperand* right = instr->InputAt(1); + LOperand* result = instr->result(); + bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); + + if (!can_overflow) { + if (right->IsStackSlot() || right->IsArgument()) { + Register right_reg = EmitLoadRegister(right, at); + __ Subu(ToRegister(result), ToRegister(left), Operand(right_reg)); + } else { + ASSERT(right->IsRegister() || right->IsConstantOperand()); + __ Subu(ToRegister(result), ToRegister(left), ToOperand(right)); + } + } else { // can_overflow. + Register overflow = scratch0(); + Register scratch = scratch1(); + if (right->IsStackSlot() || + right->IsArgument() || + right->IsConstantOperand()) { + Register right_reg = EmitLoadRegister(right, scratch); + __ SubuAndCheckForOverflow(ToRegister(result), + ToRegister(left), + right_reg, + overflow); // Reg at also used as scratch. + } else { + ASSERT(right->IsRegister()); + // Due to overflow check macros not supporting constant operands, + // handling the IsConstantOperand case was moved to prev if clause. + __ SubuAndCheckForOverflow(ToRegister(result), + ToRegister(left), + ToRegister(right), + overflow); // Reg at also used as scratch. + } + DeoptimizeIf(lt, instr->environment(), overflow, Operand(zero_reg)); + } +} + + +void LCodeGen::DoConstantI(LConstantI* instr) { + ASSERT(instr->result()->IsRegister()); + __ li(ToRegister(instr->result()), Operand(instr->value())); +} + + +void LCodeGen::DoConstantD(LConstantD* instr) { + ASSERT(instr->result()->IsDoubleRegister()); + DoubleRegister result = ToDoubleRegister(instr->result()); + double v = instr->value(); + __ Move(result, v); +} + + +void LCodeGen::DoConstantT(LConstantT* instr) { + Handle<Object> value = instr->value(); + if (value->IsSmi()) { + __ li(ToRegister(instr->result()), Operand(value)); + } else { + __ LoadHeapObject(ToRegister(instr->result()), + Handle<HeapObject>::cast(value)); + } +} + + +void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) { + Register result = ToRegister(instr->result()); + Register array = ToRegister(instr->InputAt(0)); + __ lw(result, FieldMemOperand(array, JSArray::kLengthOffset)); +} + + +void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { + Register result = ToRegister(instr->result()); + Register array = ToRegister(instr->InputAt(0)); + __ lw(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset)); +} + + +void LCodeGen::DoElementsKind(LElementsKind* instr) { + Register result = ToRegister(instr->result()); + Register input = ToRegister(instr->InputAt(0)); + + // Load map into |result|. + __ lw(result, FieldMemOperand(input, HeapObject::kMapOffset)); + // Load the map's "bit field 2" into |result|. We only need the first byte, + // but the following bit field extraction takes care of that anyway. + __ lbu(result, FieldMemOperand(result, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ Ext(result, result, Map::kElementsKindShift, Map::kElementsKindBitCount); +} + + +void LCodeGen::DoValueOf(LValueOf* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + Register map = ToRegister(instr->TempAt(0)); + Label done; + + // If the object is a smi return the object. + __ Move(result, input); + __ JumpIfSmi(input, &done); + + // If the object is not a value type, return the object. + __ GetObjectType(input, map, map); + __ Branch(&done, ne, map, Operand(JS_VALUE_TYPE)); + __ lw(result, FieldMemOperand(input, JSValue::kValueOffset)); + + __ bind(&done); +} + + +void LCodeGen::DoBitNotI(LBitNotI* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + __ Nor(result, zero_reg, Operand(input)); +} + + +void LCodeGen::DoThrow(LThrow* instr) { + Register input_reg = EmitLoadRegister(instr->InputAt(0), at); + __ push(input_reg); + CallRuntime(Runtime::kThrow, 1, instr); + + if (FLAG_debug_code) { + __ stop("Unreachable code."); + } +} + + +void LCodeGen::DoAddI(LAddI* instr) { + LOperand* left = instr->InputAt(0); + LOperand* right = instr->InputAt(1); + LOperand* result = instr->result(); + bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); + + if (!can_overflow) { + if (right->IsStackSlot() || right->IsArgument()) { + Register right_reg = EmitLoadRegister(right, at); + __ Addu(ToRegister(result), ToRegister(left), Operand(right_reg)); + } else { + ASSERT(right->IsRegister() || right->IsConstantOperand()); + __ Addu(ToRegister(result), ToRegister(left), ToOperand(right)); + } + } else { // can_overflow. + Register overflow = scratch0(); + Register scratch = scratch1(); + if (right->IsStackSlot() || + right->IsArgument() || + right->IsConstantOperand()) { + Register right_reg = EmitLoadRegister(right, scratch); + __ AdduAndCheckForOverflow(ToRegister(result), + ToRegister(left), + right_reg, + overflow); // Reg at also used as scratch. + } else { + ASSERT(right->IsRegister()); + // Due to overflow check macros not supporting constant operands, + // handling the IsConstantOperand case was moved to prev if clause. + __ AdduAndCheckForOverflow(ToRegister(result), + ToRegister(left), + ToRegister(right), + overflow); // Reg at also used as scratch. + } + DeoptimizeIf(lt, instr->environment(), overflow, Operand(zero_reg)); + } +} + + +void LCodeGen::DoArithmeticD(LArithmeticD* instr) { + DoubleRegister left = ToDoubleRegister(instr->InputAt(0)); + DoubleRegister right = ToDoubleRegister(instr->InputAt(1)); + DoubleRegister result = ToDoubleRegister(instr->result()); + switch (instr->op()) { + case Token::ADD: + __ add_d(result, left, right); + break; + case Token::SUB: + __ sub_d(result, left, right); + break; + case Token::MUL: + __ mul_d(result, left, right); + break; + case Token::DIV: + __ div_d(result, left, right); + break; + case Token::MOD: { + // Save a0-a3 on the stack. + RegList saved_regs = a0.bit() | a1.bit() | a2.bit() | a3.bit(); + __ MultiPush(saved_regs); + + __ PrepareCallCFunction(0, 2, scratch0()); + __ SetCallCDoubleArguments(left, right); + __ CallCFunction( + ExternalReference::double_fp_operation(Token::MOD, isolate()), + 0, 2); + // Move the result in the double result register. + __ GetCFunctionDoubleResult(result); + + // Restore saved register. + __ MultiPop(saved_regs); + break; + } + default: + UNREACHABLE(); + break; + } +} + + +void LCodeGen::DoArithmeticT(LArithmeticT* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(a1)); + ASSERT(ToRegister(instr->InputAt(1)).is(a0)); + ASSERT(ToRegister(instr->result()).is(v0)); + + BinaryOpStub stub(instr->op(), NO_OVERWRITE); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + // Other arch use a nop here, to signal that there is no inlined + // patchable code. Mips does not need the nop, since our marker + // instruction (andi zero_reg) will never be used in normal code. +} + + +int LCodeGen::GetNextEmittedBlock(int block) { + for (int i = block + 1; i < graph()->blocks()->length(); ++i) { + LLabel* label = chunk_->GetLabel(i); + if (!label->HasReplacement()) return i; + } + return -1; +} + + +void LCodeGen::EmitBranch(int left_block, int right_block, + Condition cc, Register src1, const Operand& src2) { + int next_block = GetNextEmittedBlock(current_block_); + right_block = chunk_->LookupDestination(right_block); + left_block = chunk_->LookupDestination(left_block); + if (right_block == left_block) { + EmitGoto(left_block); + } else if (left_block == next_block) { + __ Branch(chunk_->GetAssemblyLabel(right_block), + NegateCondition(cc), src1, src2); + } else if (right_block == next_block) { + __ Branch(chunk_->GetAssemblyLabel(left_block), cc, src1, src2); + } else { + __ Branch(chunk_->GetAssemblyLabel(left_block), cc, src1, src2); + __ Branch(chunk_->GetAssemblyLabel(right_block)); + } +} + + +void LCodeGen::EmitBranchF(int left_block, int right_block, + Condition cc, FPURegister src1, FPURegister src2) { + int next_block = GetNextEmittedBlock(current_block_); + right_block = chunk_->LookupDestination(right_block); + left_block = chunk_->LookupDestination(left_block); + if (right_block == left_block) { + EmitGoto(left_block); + } else if (left_block == next_block) { + __ BranchF(chunk_->GetAssemblyLabel(right_block), NULL, + NegateCondition(cc), src1, src2); + } else if (right_block == next_block) { + __ BranchF(chunk_->GetAssemblyLabel(left_block), NULL, cc, src1, src2); + } else { + __ BranchF(chunk_->GetAssemblyLabel(left_block), NULL, cc, src1, src2); + __ Branch(chunk_->GetAssemblyLabel(right_block)); + } +} + + +void LCodeGen::DoBranch(LBranch* instr) { + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Representation r = instr->hydrogen()->value()->representation(); + if (r.IsInteger32()) { + Register reg = ToRegister(instr->InputAt(0)); + EmitBranch(true_block, false_block, ne, reg, Operand(zero_reg)); + } else if (r.IsDouble()) { + DoubleRegister reg = ToDoubleRegister(instr->InputAt(0)); + // Test the double value. Zero and NaN are false. + EmitBranchF(true_block, false_block, ne, reg, kDoubleRegZero); + } else { + ASSERT(r.IsTagged()); + Register reg = ToRegister(instr->InputAt(0)); + HType type = instr->hydrogen()->value()->type(); + if (type.IsBoolean()) { + __ LoadRoot(at, Heap::kTrueValueRootIndex); + EmitBranch(true_block, false_block, eq, reg, Operand(at)); + } else if (type.IsSmi()) { + EmitBranch(true_block, false_block, ne, reg, Operand(zero_reg)); + } else { + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types(); + // Avoid deopts in the case where we've never executed this path before. + if (expected.IsEmpty()) expected = ToBooleanStub::all_types(); + + if (expected.Contains(ToBooleanStub::UNDEFINED)) { + // undefined -> false. + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + __ Branch(false_label, eq, reg, Operand(at)); + } + if (expected.Contains(ToBooleanStub::BOOLEAN)) { + // Boolean -> its value. + __ LoadRoot(at, Heap::kTrueValueRootIndex); + __ Branch(true_label, eq, reg, Operand(at)); + __ LoadRoot(at, Heap::kFalseValueRootIndex); + __ Branch(false_label, eq, reg, Operand(at)); + } + if (expected.Contains(ToBooleanStub::NULL_TYPE)) { + // 'null' -> false. + __ LoadRoot(at, Heap::kNullValueRootIndex); + __ Branch(false_label, eq, reg, Operand(at)); + } + + if (expected.Contains(ToBooleanStub::SMI)) { + // Smis: 0 -> false, all other -> true. + __ Branch(false_label, eq, reg, Operand(zero_reg)); + __ JumpIfSmi(reg, true_label); + } else if (expected.NeedsMap()) { + // If we need a map later and have a Smi -> deopt. + __ And(at, reg, Operand(kSmiTagMask)); + DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg)); + } + + const Register map = scratch0(); + if (expected.NeedsMap()) { + __ lw(map, FieldMemOperand(reg, HeapObject::kMapOffset)); + if (expected.CanBeUndetectable()) { + // Undetectable -> false. + __ lbu(at, FieldMemOperand(map, Map::kBitFieldOffset)); + __ And(at, at, Operand(1 << Map::kIsUndetectable)); + __ Branch(false_label, ne, at, Operand(zero_reg)); + } + } + + if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) { + // spec object -> true. + __ lbu(at, FieldMemOperand(map, Map::kInstanceTypeOffset)); + __ Branch(true_label, ge, at, Operand(FIRST_SPEC_OBJECT_TYPE)); + } + + if (expected.Contains(ToBooleanStub::STRING)) { + // String value -> false iff empty. + Label not_string; + __ lbu(at, FieldMemOperand(map, Map::kInstanceTypeOffset)); + __ Branch(¬_string, ge , at, Operand(FIRST_NONSTRING_TYPE)); + __ lw(at, FieldMemOperand(reg, String::kLengthOffset)); + __ Branch(true_label, ne, at, Operand(zero_reg)); + __ Branch(false_label); + __ bind(¬_string); + } + + if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { + // heap number -> false iff +0, -0, or NaN. + DoubleRegister dbl_scratch = double_scratch0(); + Label not_heap_number; + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + __ Branch(¬_heap_number, ne, map, Operand(at)); + __ ldc1(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset)); + __ BranchF(true_label, false_label, ne, dbl_scratch, kDoubleRegZero); + // Falls through if dbl_scratch == 0. + __ Branch(false_label); + __ bind(¬_heap_number); + } + + // We've seen something for the first time -> deopt. + DeoptimizeIf(al, instr->environment(), zero_reg, Operand(zero_reg)); + } + } +} + + +void LCodeGen::EmitGoto(int block) { + block = chunk_->LookupDestination(block); + int next_block = GetNextEmittedBlock(current_block_); + if (block != next_block) { + __ jmp(chunk_->GetAssemblyLabel(block)); + } +} + + +void LCodeGen::DoGoto(LGoto* instr) { + EmitGoto(instr->block_id()); +} + + +Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { + Condition cond = kNoCondition; + switch (op) { + case Token::EQ: + case Token::EQ_STRICT: + cond = eq; + break; + case Token::LT: + cond = is_unsigned ? lo : lt; + break; + case Token::GT: + cond = is_unsigned ? hi : gt; + break; + case Token::LTE: + cond = is_unsigned ? ls : le; + break; + case Token::GTE: + cond = is_unsigned ? hs : ge; + break; + case Token::IN: + case Token::INSTANCEOF: + default: + UNREACHABLE(); + } + return cond; +} + + +void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { + LOperand* left = instr->InputAt(0); + LOperand* right = instr->InputAt(1); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + + Condition cond = TokenToCondition(instr->op(), false); + + if (left->IsConstantOperand() && right->IsConstantOperand()) { + // We can statically evaluate the comparison. + double left_val = ToDouble(LConstantOperand::cast(left)); + double right_val = ToDouble(LConstantOperand::cast(right)); + int next_block = + EvalComparison(instr->op(), left_val, right_val) ? true_block + : false_block; + EmitGoto(next_block); + } else { + if (instr->is_double()) { + // Compare left and right as doubles and load the + // resulting flags into the normal status register. + FPURegister left_reg = ToDoubleRegister(left); + FPURegister right_reg = ToDoubleRegister(right); + + // If a NaN is involved, i.e. the result is unordered, + // jump to false block label. + __ BranchF(NULL, chunk_->GetAssemblyLabel(false_block), eq, + left_reg, right_reg); + + EmitBranchF(true_block, false_block, cond, left_reg, right_reg); + } else { + Register cmp_left; + Operand cmp_right = Operand(0); + + if (right->IsConstantOperand()) { + cmp_left = ToRegister(left); + cmp_right = Operand(ToInteger32(LConstantOperand::cast(right))); + } else if (left->IsConstantOperand()) { + cmp_left = ToRegister(right); + cmp_right = Operand(ToInteger32(LConstantOperand::cast(left))); + // We transposed the operands. Reverse the condition. + cond = ReverseCondition(cond); + } else { + cmp_left = ToRegister(left); + cmp_right = Operand(ToRegister(right)); + } + + EmitBranch(true_block, false_block, cond, cmp_left, cmp_right); + } + } +} + + +void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { + Register left = ToRegister(instr->InputAt(0)); + Register right = ToRegister(instr->InputAt(1)); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + + EmitBranch(true_block, false_block, eq, left, Operand(right)); +} + + +void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { + Register left = ToRegister(instr->InputAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + EmitBranch(true_block, false_block, eq, left, + Operand(instr->hydrogen()->right())); +} + + + +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { + Register scratch = scratch0(); + Register reg = ToRegister(instr->InputAt(0)); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + // If the expression is known to be untagged or a smi, then it's definitely + // not null, and it can't be a an undetectable object. + if (instr->hydrogen()->representation().IsSpecialization() || + instr->hydrogen()->type().IsSmi()) { + EmitGoto(false_block); + return; + } + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + + Heap::RootListIndex nil_value = instr->nil() == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; + __ LoadRoot(at, nil_value); + if (instr->kind() == kStrictEquality) { + EmitBranch(true_block, false_block, eq, reg, Operand(at)); + } else { + Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + __ Branch(USE_DELAY_SLOT, true_label, eq, reg, Operand(at)); + __ LoadRoot(at, other_nil_value); // In the delay slot. + __ Branch(USE_DELAY_SLOT, true_label, eq, reg, Operand(at)); + __ JumpIfSmi(reg, false_label); // In the delay slot. + // Check for undetectable objects by looking in the bit field in + // the map. The object has already been smi checked. + __ lw(scratch, FieldMemOperand(reg, HeapObject::kMapOffset)); + __ lbu(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset)); + __ And(scratch, scratch, 1 << Map::kIsUndetectable); + EmitBranch(true_block, false_block, ne, scratch, Operand(zero_reg)); + } +} + + +Condition LCodeGen::EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object) { + __ JumpIfSmi(input, is_not_object); + + __ LoadRoot(temp2, Heap::kNullValueRootIndex); + __ Branch(is_object, eq, input, Operand(temp2)); + + // Load map. + __ lw(temp1, FieldMemOperand(input, HeapObject::kMapOffset)); + // Undetectable objects behave like undefined. + __ lbu(temp2, FieldMemOperand(temp1, Map::kBitFieldOffset)); + __ And(temp2, temp2, Operand(1 << Map::kIsUndetectable)); + __ Branch(is_not_object, ne, temp2, Operand(zero_reg)); + + // Load instance type and check that it is in object type range. + __ lbu(temp2, FieldMemOperand(temp1, Map::kInstanceTypeOffset)); + __ Branch(is_not_object, + lt, temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + + return le; +} + + +void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp1 = ToRegister(instr->TempAt(0)); + Register temp2 = scratch0(); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = + EmitIsObject(reg, temp1, temp2, false_label, true_label); + + EmitBranch(true_block, false_block, true_cond, temp2, + Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE)); +} + + +Condition LCodeGen::EmitIsString(Register input, + Register temp1, + Label* is_not_string) { + __ JumpIfSmi(input, is_not_string); + __ GetObjectType(input, temp1, temp1); + + return lt; +} + + +void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp1 = ToRegister(instr->TempAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = + EmitIsString(reg, temp1, false_label); + + EmitBranch(true_block, false_block, true_cond, temp1, + Operand(FIRST_NONSTRING_TYPE)); +} + + +void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Register input_reg = EmitLoadRegister(instr->InputAt(0), at); + __ And(at, input_reg, kSmiTagMask); + EmitBranch(true_block, false_block, eq, at, Operand(zero_reg)); +} + + +void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register temp = ToRegister(instr->TempAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + __ JumpIfSmi(input, chunk_->GetAssemblyLabel(false_block)); + __ lw(temp, FieldMemOperand(input, HeapObject::kMapOffset)); + __ lbu(temp, FieldMemOperand(temp, Map::kBitFieldOffset)); + __ And(at, temp, Operand(1 << Map::kIsUndetectable)); + EmitBranch(true_block, false_block, ne, at, Operand(zero_reg)); +} + + +static Condition ComputeCompareCondition(Token::Value op) { + switch (op) { + case Token::EQ_STRICT: + case Token::EQ: + return eq; + case Token::LT: + return lt; + case Token::GT: + return gt; + case Token::LTE: + return le; + case Token::GTE: + return ge; + default: + UNREACHABLE(); + return kNoCondition; + } +} + + +void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) { + Token::Value op = instr->op(); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Handle<Code> ic = CompareIC::GetUninitialized(op); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + + Condition condition = ComputeCompareCondition(op); + + EmitBranch(true_block, false_block, condition, v0, Operand(zero_reg)); +} + + +static InstanceType TestType(HHasInstanceTypeAndBranch* instr) { + InstanceType from = instr->from(); + InstanceType to = instr->to(); + if (from == FIRST_TYPE) return to; + ASSERT(from == to || to == LAST_TYPE); + return from; +} + + +static Condition BranchCondition(HHasInstanceTypeAndBranch* instr) { + InstanceType from = instr->from(); + InstanceType to = instr->to(); + if (from == to) return eq; + if (to == LAST_TYPE) return hs; + if (from == FIRST_TYPE) return ls; + UNREACHABLE(); + return eq; +} + + +void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) { + Register scratch = scratch0(); + Register input = ToRegister(instr->InputAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + __ JumpIfSmi(input, false_label); + + __ GetObjectType(input, scratch, scratch); + EmitBranch(true_block, + false_block, + BranchCondition(instr->hydrogen()), + scratch, + Operand(TestType(instr->hydrogen()))); +} + + +void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + + if (FLAG_debug_code) { + __ AbortIfNotString(input); + } + + __ lw(result, FieldMemOperand(input, String::kHashFieldOffset)); + __ IndexFromHash(result, result); +} + + +void LCodeGen::DoHasCachedArrayIndexAndBranch( + LHasCachedArrayIndexAndBranch* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register scratch = scratch0(); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + __ lw(scratch, + FieldMemOperand(input, String::kHashFieldOffset)); + __ And(at, scratch, Operand(String::kContainsCachedArrayIndexMask)); + EmitBranch(true_block, false_block, eq, at, Operand(zero_reg)); +} + + +// Branches to a label or falls through with the answer in flags. Trashes +// the temp registers, but not the input. +void LCodeGen::EmitClassOfTest(Label* is_true, + Label* is_false, + Handle<String>class_name, + Register input, + Register temp, + Register temp2) { + ASSERT(!input.is(temp)); + ASSERT(!input.is(temp2)); + ASSERT(!temp.is(temp2)); + + __ JumpIfSmi(input, is_false); + + if (class_name->IsEqualTo(CStrVector("Function"))) { + // Assuming the following assertions, we can use the same compares to test + // for both being a function type and being in the object type range. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + + __ GetObjectType(input, temp, temp2); + __ Branch(is_false, lt, temp2, Operand(FIRST_SPEC_OBJECT_TYPE)); + __ Branch(is_true, eq, temp2, Operand(FIRST_SPEC_OBJECT_TYPE)); + __ Branch(is_true, eq, temp2, Operand(LAST_SPEC_OBJECT_TYPE)); + } else { + // Faster code path to avoid two compares: subtract lower bound from the + // actual type and do a signed compare with the width of the type range. + __ GetObjectType(input, temp, temp2); + __ Subu(temp2, temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ Branch(is_false, gt, temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE - + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + } + + // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range. + // Check if the constructor in the map is a function. + __ lw(temp, FieldMemOperand(temp, Map::kConstructorOffset)); + + // Objects with a non-function constructor have class 'Object'. + __ GetObjectType(temp, temp2, temp2); + if (class_name->IsEqualTo(CStrVector("Object"))) { + __ Branch(is_true, ne, temp2, Operand(JS_FUNCTION_TYPE)); + } else { + __ Branch(is_false, ne, temp2, Operand(JS_FUNCTION_TYPE)); + } + + // temp now contains the constructor function. Grab the + // instance class name from there. + __ lw(temp, FieldMemOperand(temp, JSFunction::kSharedFunctionInfoOffset)); + __ lw(temp, FieldMemOperand(temp, + SharedFunctionInfo::kInstanceClassNameOffset)); + // The class name we are testing against is a symbol because it's a literal. + // The name in the constructor is a symbol because of the way the context is + // booted. This routine isn't expected to work for random API-created + // classes and it doesn't have to because you can't access it with natives + // syntax. Since both sides are symbols it is sufficient to use an identity + // comparison. + + // End with the address of this class_name instance in temp register. + // On MIPS, the caller must do the comparison with Handle<String>class_name. +} + + +void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register temp = scratch0(); + Register temp2 = ToRegister(instr->TempAt(0)); + Handle<String> class_name = instr->hydrogen()->class_name(); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + EmitClassOfTest(true_label, false_label, class_name, input, temp, temp2); + + EmitBranch(true_block, false_block, eq, temp, Operand(class_name)); +} + + +void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp = ToRegister(instr->TempAt(0)); + int true_block = instr->true_block_id(); + int false_block = instr->false_block_id(); + + __ lw(temp, FieldMemOperand(reg, HeapObject::kMapOffset)); + EmitBranch(true_block, false_block, eq, temp, Operand(instr->map())); +} + + +void LCodeGen::DoInstanceOf(LInstanceOf* instr) { + Label true_label, done; + ASSERT(ToRegister(instr->InputAt(0)).is(a0)); // Object is in a0. + ASSERT(ToRegister(instr->InputAt(1)).is(a1)); // Function is in a1. + Register result = ToRegister(instr->result()); + ASSERT(result.is(v0)); + + InstanceofStub stub(InstanceofStub::kArgsInRegisters); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + + __ Branch(&true_label, eq, result, Operand(zero_reg)); + __ li(result, Operand(factory()->false_value())); + __ Branch(&done); + __ bind(&true_label); + __ li(result, Operand(factory()->true_value())); + __ bind(&done); +} + + +void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { + class DeferredInstanceOfKnownGlobal: public LDeferredCode { + public: + DeferredInstanceOfKnownGlobal(LCodeGen* codegen, + LInstanceOfKnownGlobal* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { + codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); + } + virtual LInstruction* instr() { return instr_; } + Label* map_check() { return &map_check_; } + + private: + LInstanceOfKnownGlobal* instr_; + Label map_check_; + }; + + DeferredInstanceOfKnownGlobal* deferred; + deferred = new DeferredInstanceOfKnownGlobal(this, instr); + + Label done, false_result; + Register object = ToRegister(instr->InputAt(0)); + Register temp = ToRegister(instr->TempAt(0)); + Register result = ToRegister(instr->result()); + + ASSERT(object.is(a0)); + ASSERT(result.is(v0)); + + // A Smi is not instance of anything. + __ JumpIfSmi(object, &false_result); + + // This is the inlined call site instanceof cache. The two occurences of the + // hole value will be patched to the last map/result pair generated by the + // instanceof stub. + Label cache_miss; + Register map = temp; + __ lw(map, FieldMemOperand(object, HeapObject::kMapOffset)); + + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_); + __ bind(deferred->map_check()); // Label for calculating code patching. + // We use Factory::the_hole_value() on purpose instead of loading from the + // root array to force relocation to be able to later patch with + // the cached map. + Handle<JSGlobalPropertyCell> cell = + factory()->NewJSGlobalPropertyCell(factory()->the_hole_value()); + __ li(at, Operand(Handle<Object>(cell))); + __ lw(at, FieldMemOperand(at, JSGlobalPropertyCell::kValueOffset)); + __ Branch(&cache_miss, ne, map, Operand(at)); + // We use Factory::the_hole_value() on purpose instead of loading from the + // root array to force relocation to be able to later patch + // with true or false. + __ li(result, Operand(factory()->the_hole_value()), true); + __ Branch(&done); + + // The inlined call site cache did not match. Check null and string before + // calling the deferred code. + __ bind(&cache_miss); + // Null is not instance of anything. + __ LoadRoot(temp, Heap::kNullValueRootIndex); + __ Branch(&false_result, eq, object, Operand(temp)); + + // String values is not instance of anything. + Condition cc = __ IsObjectStringType(object, temp, temp); + __ Branch(&false_result, cc, temp, Operand(zero_reg)); + + // Go to the deferred code. + __ Branch(deferred->entry()); + + __ bind(&false_result); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + + // Here result has either true or false. Deferred code also produces true or + // false object. + __ bind(deferred->exit()); + __ bind(&done); +} + + +void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check) { + Register result = ToRegister(instr->result()); + ASSERT(result.is(v0)); + + InstanceofStub::Flags flags = InstanceofStub::kNoFlags; + flags = static_cast<InstanceofStub::Flags>( + flags | InstanceofStub::kArgsInRegisters); + flags = static_cast<InstanceofStub::Flags>( + flags | InstanceofStub::kCallSiteInlineCheck); + flags = static_cast<InstanceofStub::Flags>( + flags | InstanceofStub::kReturnTrueFalseObject); + InstanceofStub stub(flags); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + + // Get the temp register reserved by the instruction. This needs to be t0 as + // its slot of the pushing of safepoint registers is used to communicate the + // offset to the location of the map check. + Register temp = ToRegister(instr->TempAt(0)); + ASSERT(temp.is(t0)); + __ LoadHeapObject(InstanceofStub::right(), instr->function()); + static const int kAdditionalDelta = 7; + int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta; + Label before_push_delta; + __ bind(&before_push_delta); + { + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_); + __ li(temp, Operand(delta * kPointerSize), true); + __ StoreToSafepointRegisterSlot(temp, temp); + } + CallCodeGeneric(stub.GetCode(), + RelocInfo::CODE_TARGET, + instr, + RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasDeoptimizationEnvironment()); + LEnvironment* env = instr->deoptimization_environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); + // Put the result value into the result register slot and + // restore all registers. + __ StoreToSafepointRegisterSlot(result, result); +} + + +void LCodeGen::DoCmpT(LCmpT* instr) { + Token::Value op = instr->op(); + + Handle<Code> ic = CompareIC::GetUninitialized(op); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + // On MIPS there is no need for a "no inlined smi code" marker (nop). + + Condition condition = ComputeCompareCondition(op); + // A minor optimization that relies on LoadRoot always emitting one + // instruction. + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm()); + Label done; + __ Branch(USE_DELAY_SLOT, &done, condition, v0, Operand(zero_reg)); + __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex); + __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex); + ASSERT_EQ(3, masm()->InstructionsGeneratedSince(&done)); + __ bind(&done); +} + + +void LCodeGen::DoReturn(LReturn* instr) { + if (FLAG_trace) { + // Push the return value on the stack as the parameter. + // Runtime::TraceExit returns its parameter in v0. + __ push(v0); + __ CallRuntime(Runtime::kTraceExit, 1); + } + int32_t sp_delta = (GetParameterCount() + 1) * kPointerSize; + __ mov(sp, fp); + __ Pop(ra, fp); + __ Addu(sp, sp, Operand(sp_delta)); + __ Jump(ra); +} + + +void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { + Register result = ToRegister(instr->result()); + __ li(at, Operand(Handle<Object>(instr->hydrogen()->cell()))); + __ lw(result, FieldMemOperand(at, JSGlobalPropertyCell::kValueOffset)); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + DeoptimizeIf(eq, instr->environment(), result, Operand(at)); + } +} + + +void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) { + ASSERT(ToRegister(instr->global_object()).is(a0)); + ASSERT(ToRegister(instr->result()).is(v0)); + + __ li(a2, Operand(instr->name())); + RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET + : RelocInfo::CODE_TARGET_CONTEXT; + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + CallCode(ic, mode, instr); +} + + +void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { + Register value = ToRegister(instr->value()); + Register cell = scratch0(); + + // Load the cell. + __ li(cell, Operand(instr->hydrogen()->cell())); + + // If the cell we are storing to contains the hole it could have + // been deleted from the property dictionary. In that case, we need + // to update the property details in the property dictionary to mark + // it as no longer deleted. + if (instr->hydrogen()->RequiresHoleCheck()) { + // We use a temp to check the payload. + Register payload = ToRegister(instr->TempAt(0)); + __ lw(payload, FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset)); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + DeoptimizeIf(eq, instr->environment(), payload, Operand(at)); + } + + // Store the value. + __ sw(value, FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset)); + // Cells are always rescanned, so no write barrier here. +} + + +void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) { + ASSERT(ToRegister(instr->global_object()).is(a1)); + ASSERT(ToRegister(instr->value()).is(a0)); + + __ li(a2, Operand(instr->name())); + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) + ? isolate()->builtins()->StoreIC_Initialize_Strict() + : isolate()->builtins()->StoreIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr); +} + + +void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { + Register context = ToRegister(instr->context()); + Register result = ToRegister(instr->result()); + + __ lw(result, ContextOperand(context, instr->slot_index())); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(eq, instr->environment(), result, Operand(at)); + } else { + Label is_not_hole; + __ Branch(&is_not_hole, ne, result, Operand(at)); + __ LoadRoot(result, Heap::kUndefinedValueRootIndex); + __ bind(&is_not_hole); + } + } +} + + +void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { + Register context = ToRegister(instr->context()); + Register value = ToRegister(instr->value()); + Register scratch = scratch0(); + MemOperand target = ContextOperand(context, instr->slot_index()); + + Label skip_assignment; + + if (instr->hydrogen()->RequiresHoleCheck()) { + __ lw(scratch, target); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(eq, instr->environment(), scratch, Operand(at)); + } else { + __ Branch(&skip_assignment, ne, scratch, Operand(at)); + } + } + + __ sw(value, target); + if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; + __ RecordWriteContextSlot(context, + target.offset(), + value, + scratch0(), + kRAHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); + } + + __ bind(&skip_assignment); +} + + +void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { + Register object = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + if (instr->hydrogen()->is_in_object()) { + __ lw(result, FieldMemOperand(object, instr->hydrogen()->offset())); + } else { + __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); + __ lw(result, FieldMemOperand(result, instr->hydrogen()->offset())); + } +} + + +void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, + Register object, + Handle<Map> type, + Handle<String> name) { + LookupResult lookup(isolate()); + type->LookupInDescriptors(NULL, *name, &lookup); + ASSERT(lookup.IsFound() && + (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION)); + if (lookup.type() == FIELD) { + int index = lookup.GetLocalFieldIndexFromMap(*type); + int offset = index * kPointerSize; + if (index < 0) { + // Negative property indices are in-object properties, indexed + // from the end of the fixed part of the object. + __ lw(result, FieldMemOperand(object, offset + type->instance_size())); + } else { + // Non-negative property indices are in the properties array. + __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); + __ lw(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize)); + } + } else { + Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type)); + __ LoadHeapObject(result, function); + } +} + + +void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { + Register object = ToRegister(instr->object()); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + int map_count = instr->hydrogen()->types()->length(); + Handle<String> name = instr->hydrogen()->name(); + if (map_count == 0) { + ASSERT(instr->hydrogen()->need_generic()); + __ li(a2, Operand(name)); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + } else { + Label done; + __ lw(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); + for (int i = 0; i < map_count - 1; ++i) { + Handle<Map> map = instr->hydrogen()->types()->at(i); + Label next; + __ Branch(&next, ne, scratch, Operand(map)); + EmitLoadFieldOrConstantFunction(result, object, map, name); + __ Branch(&done); + __ bind(&next); + } + Handle<Map> map = instr->hydrogen()->types()->last(); + if (instr->hydrogen()->need_generic()) { + Label generic; + __ Branch(&generic, ne, scratch, Operand(map)); + EmitLoadFieldOrConstantFunction(result, object, map, name); + __ Branch(&done); + __ bind(&generic); + __ li(a2, Operand(name)); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + } else { + DeoptimizeIf(ne, instr->environment(), scratch, Operand(map)); + EmitLoadFieldOrConstantFunction(result, object, map, name); + } + __ bind(&done); + } +} + + +void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { + ASSERT(ToRegister(instr->object()).is(a0)); + ASSERT(ToRegister(instr->result()).is(v0)); + + // Name is always in a2. + __ li(a2, Operand(instr->name())); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) { + Register scratch = scratch0(); + Register function = ToRegister(instr->function()); + Register result = ToRegister(instr->result()); + + // Check that the function really is a function. Load map into the + // result register. + __ GetObjectType(function, result, scratch); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(JS_FUNCTION_TYPE)); + + // Make sure that the function has an instance prototype. + Label non_instance; + __ lbu(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); + __ And(scratch, scratch, Operand(1 << Map::kHasNonInstancePrototype)); + __ Branch(&non_instance, ne, scratch, Operand(zero_reg)); + + // Get the prototype or initial map from the function. + __ lw(result, + FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); + + // Check that the function has a prototype or an initial map. + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + DeoptimizeIf(eq, instr->environment(), result, Operand(at)); + + // If the function does not have an initial map, we're done. + Label done; + __ GetObjectType(result, scratch, scratch); + __ Branch(&done, ne, scratch, Operand(MAP_TYPE)); + + // Get the prototype from the initial map. + __ lw(result, FieldMemOperand(result, Map::kPrototypeOffset)); + __ Branch(&done); + + // Non-instance prototype: Fetch prototype from constructor field + // in initial map. + __ bind(&non_instance); + __ lw(result, FieldMemOperand(result, Map::kConstructorOffset)); + + // All done. + __ bind(&done); +} + + +void LCodeGen::DoLoadElements(LLoadElements* instr) { + Register result = ToRegister(instr->result()); + Register input = ToRegister(instr->InputAt(0)); + Register scratch = scratch0(); + + __ lw(result, FieldMemOperand(input, JSObject::kElementsOffset)); + if (FLAG_debug_code) { + Label done, fail; + __ lw(scratch, FieldMemOperand(result, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kFixedArrayMapRootIndex); + __ Branch(USE_DELAY_SLOT, &done, eq, scratch, Operand(at)); + __ LoadRoot(at, Heap::kFixedCOWArrayMapRootIndex); // In the delay slot. + __ Branch(&done, eq, scratch, Operand(at)); + // |scratch| still contains |input|'s map. + __ lbu(scratch, FieldMemOperand(scratch, Map::kBitField2Offset)); + __ Ext(scratch, scratch, Map::kElementsKindShift, + Map::kElementsKindBitCount); + __ Branch(&done, eq, scratch, + Operand(FAST_ELEMENTS)); + __ Branch(&fail, lt, scratch, + Operand(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ Branch(&done, le, scratch, + Operand(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ bind(&fail); + __ Abort("Check for fast or external elements failed."); + __ bind(&done); + } +} + + +void LCodeGen::DoLoadExternalArrayPointer( + LLoadExternalArrayPointer* instr) { + Register to_reg = ToRegister(instr->result()); + Register from_reg = ToRegister(instr->InputAt(0)); + __ lw(to_reg, FieldMemOperand(from_reg, + ExternalArray::kExternalPointerOffset)); +} + + +void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { + Register arguments = ToRegister(instr->arguments()); + Register length = ToRegister(instr->length()); + Register index = ToRegister(instr->index()); + Register result = ToRegister(instr->result()); + + // Bailout index is not a valid argument index. Use unsigned check to get + // negative check for free. + + // TODO(plind): Shoud be optimized to do the sub before the DeoptimizeIf(), + // as they do in Arm. It will save us an instruction. + DeoptimizeIf(ls, instr->environment(), length, Operand(index)); + + // There are two words between the frame pointer and the last argument. + // Subtracting from length accounts for one of them, add one more. + __ subu(length, length, index); + __ Addu(length, length, Operand(1)); + __ sll(length, length, kPointerSizeLog2); + __ Addu(at, arguments, Operand(length)); + __ lw(result, MemOperand(at, 0)); +} + + +void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { + Register elements = ToRegister(instr->elements()); + Register key = EmitLoadRegister(instr->key(), scratch0()); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + + // Load the result. + __ sll(scratch, key, kPointerSizeLog2); // Key indexes words. + __ addu(scratch, elements, scratch); + __ lw(result, FieldMemOperand(scratch, FixedArray::kHeaderSize)); + + // Check for the hole value. + if (instr->hydrogen()->RequiresHoleCheck()) { + __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex); + DeoptimizeIf(eq, instr->environment(), result, Operand(scratch)); + } +} + + +void LCodeGen::DoLoadKeyedFastDoubleElement( + LLoadKeyedFastDoubleElement* instr) { + Register elements = ToRegister(instr->elements()); + bool key_is_constant = instr->key()->IsConstantOperand(); + Register key = no_reg; + DoubleRegister result = ToDoubleRegister(instr->result()); + Register scratch = scratch0(); + + int shift_size = + ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS); + int constant_key = 0; + if (key_is_constant) { + constant_key = ToInteger32(LConstantOperand::cast(instr->key())); + if (constant_key & 0xF0000000) { + Abort("array index constant value too big."); + } + } else { + key = ToRegister(instr->key()); + } + + if (key_is_constant) { + __ Addu(elements, elements, Operand(constant_key * (1 << shift_size) + + FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + } else { + __ sll(scratch, key, shift_size); + __ Addu(elements, elements, Operand(scratch)); + __ Addu(elements, elements, + Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + } + + __ lw(scratch, MemOperand(elements, sizeof(kHoleNanLower32))); + DeoptimizeIf(eq, instr->environment(), scratch, Operand(kHoleNanUpper32)); + + __ ldc1(result, MemOperand(elements)); +} + + +void LCodeGen::DoLoadKeyedSpecializedArrayElement( + LLoadKeyedSpecializedArrayElement* instr) { + Register external_pointer = ToRegister(instr->external_pointer()); + Register key = no_reg; + ElementsKind elements_kind = instr->elements_kind(); + bool key_is_constant = instr->key()->IsConstantOperand(); + int constant_key = 0; + if (key_is_constant) { + constant_key = ToInteger32(LConstantOperand::cast(instr->key())); + if (constant_key & 0xF0000000) { + Abort("array index constant value too big."); + } + } else { + key = ToRegister(instr->key()); + } + int shift_size = ElementsKindToShiftSize(elements_kind); + + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { + FPURegister result = ToDoubleRegister(instr->result()); + if (key_is_constant) { + __ Addu(scratch0(), external_pointer, constant_key * (1 << shift_size)); + } else { + __ sll(scratch0(), key, shift_size); + __ Addu(scratch0(), scratch0(), external_pointer); + } + + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { + __ lwc1(result, MemOperand(scratch0())); + __ cvt_d_s(result, result); + } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS + __ ldc1(result, MemOperand(scratch0())); + } + } else { + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + MemOperand mem_operand(zero_reg); + if (key_is_constant) { + mem_operand = MemOperand(external_pointer, + constant_key * (1 << shift_size)); + } else { + __ sll(scratch, key, shift_size); + __ Addu(scratch, scratch, external_pointer); + mem_operand = MemOperand(scratch); + } + switch (elements_kind) { + case EXTERNAL_BYTE_ELEMENTS: + __ lb(result, mem_operand); + break; + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + __ lbu(result, mem_operand); + break; + case EXTERNAL_SHORT_ELEMENTS: + __ lh(result, mem_operand); + break; + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + __ lhu(result, mem_operand); + break; + case EXTERNAL_INT_ELEMENTS: + __ lw(result, mem_operand); + break; + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + __ lw(result, mem_operand); + // TODO(danno): we could be more clever here, perhaps having a special + // version of the stub that detects if the overflow case actually + // happens, and generate code that returns a double rather than int. + DeoptimizeIf(Ugreater_equal, instr->environment(), + result, Operand(0x80000000)); + break; + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: + UNREACHABLE(); + break; + } + } +} + + +void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) { + ASSERT(ToRegister(instr->object()).is(a1)); + ASSERT(ToRegister(instr->key()).is(a0)); + + Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) { + Register scratch = scratch0(); + Register temp = scratch1(); + Register result = ToRegister(instr->result()); + + // Check if the calling frame is an arguments adaptor frame. + Label done, adapted; + __ lw(scratch, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + __ lw(result, MemOperand(scratch, StandardFrameConstants::kContextOffset)); + __ Xor(temp, result, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + + // Result is the frame pointer for the frame if not adapted and for the real + // frame below the adaptor frame if adapted. + __ movn(result, fp, temp); // move only if temp is not equal to zero (ne) + __ movz(result, scratch, temp); // move only if temp is equal to zero (eq) +} + + +void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { + Register elem = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + + Label done; + + // If no arguments adaptor frame the number of arguments is fixed. + __ Addu(result, zero_reg, Operand(scope()->num_parameters())); + __ Branch(&done, eq, fp, Operand(elem)); + + // Arguments adaptor frame present. Get argument length from there. + __ lw(result, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + __ lw(result, + MemOperand(result, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ SmiUntag(result); + + // Argument length is in result register. + __ bind(&done); +} + + +void LCodeGen::DoApplyArguments(LApplyArguments* instr) { + Register receiver = ToRegister(instr->receiver()); + Register function = ToRegister(instr->function()); + Register length = ToRegister(instr->length()); + Register elements = ToRegister(instr->elements()); + Register scratch = scratch0(); + ASSERT(receiver.is(a0)); // Used for parameter count. + ASSERT(function.is(a1)); // Required by InvokeFunction. + ASSERT(ToRegister(instr->result()).is(v0)); + + // If the receiver is null or undefined, we have to pass the global + // object as a receiver to normal functions. Values have to be + // passed unchanged to builtins and strict-mode functions. + Label global_object, receiver_ok; + + // Do not transform the receiver to object for strict mode + // functions. + __ lw(scratch, + FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); + __ lw(scratch, + FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); + + // Do not transform the receiver to object for builtins. + int32_t strict_mode_function_mask = + 1 << (SharedFunctionInfo::kStrictModeFunction + kSmiTagSize); + int32_t native_mask = 1 << (SharedFunctionInfo::kNative + kSmiTagSize); + __ And(scratch, scratch, Operand(strict_mode_function_mask | native_mask)); + __ Branch(&receiver_ok, ne, scratch, Operand(zero_reg)); + + // Normal function. Replace undefined or null with global receiver. + __ LoadRoot(scratch, Heap::kNullValueRootIndex); + __ Branch(&global_object, eq, receiver, Operand(scratch)); + __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex); + __ Branch(&global_object, eq, receiver, Operand(scratch)); + + // Deoptimize if the receiver is not a JS object. + __ And(scratch, receiver, Operand(kSmiTagMask)); + DeoptimizeIf(eq, instr->environment(), scratch, Operand(zero_reg)); + + __ GetObjectType(receiver, scratch, scratch); + DeoptimizeIf(lt, instr->environment(), + scratch, Operand(FIRST_SPEC_OBJECT_TYPE)); + __ Branch(&receiver_ok); + + __ bind(&global_object); + __ lw(receiver, GlobalObjectOperand()); + __ lw(receiver, + FieldMemOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); + __ bind(&receiver_ok); + + // Copy the arguments to this function possibly from the + // adaptor frame below it. + const uint32_t kArgumentsLimit = 1 * KB; + DeoptimizeIf(hi, instr->environment(), length, Operand(kArgumentsLimit)); + + // Push the receiver and use the register to keep the original + // number of arguments. + __ push(receiver); + __ Move(receiver, length); + // The arguments are at a one pointer size offset from elements. + __ Addu(elements, elements, Operand(1 * kPointerSize)); + + // Loop through the arguments pushing them onto the execution + // stack. + Label invoke, loop; + // length is a small non-negative integer, due to the test above. + __ Branch(USE_DELAY_SLOT, &invoke, eq, length, Operand(zero_reg)); + __ sll(scratch, length, 2); + __ bind(&loop); + __ Addu(scratch, elements, scratch); + __ lw(scratch, MemOperand(scratch)); + __ push(scratch); + __ Subu(length, length, Operand(1)); + __ Branch(USE_DELAY_SLOT, &loop, ne, length, Operand(zero_reg)); + __ sll(scratch, length, 2); + + __ bind(&invoke); + ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); + // The number of arguments is stored in receiver which is a0, as expected + // by InvokeFunction. + ParameterCount actual(receiver); + __ InvokeFunction(function, actual, CALL_FUNCTION, + safepoint_generator, CALL_AS_METHOD); + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoPushArgument(LPushArgument* instr) { + LOperand* argument = instr->InputAt(0); + if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) { + Abort("DoPushArgument not implemented for double type."); + } else { + Register argument_reg = EmitLoadRegister(argument, at); + __ push(argument_reg); + } +} + + +void LCodeGen::DoThisFunction(LThisFunction* instr) { + Register result = ToRegister(instr->result()); + __ LoadHeapObject(result, instr->hydrogen()->closure()); +} + + +void LCodeGen::DoContext(LContext* instr) { + Register result = ToRegister(instr->result()); + __ mov(result, cp); +} + + +void LCodeGen::DoOuterContext(LOuterContext* instr) { + Register context = ToRegister(instr->context()); + Register result = ToRegister(instr->result()); + __ lw(result, + MemOperand(context, Context::SlotOffset(Context::PREVIOUS_INDEX))); +} + + +void LCodeGen::DoGlobalObject(LGlobalObject* instr) { + Register context = ToRegister(instr->context()); + Register result = ToRegister(instr->result()); + __ lw(result, ContextOperand(cp, Context::GLOBAL_INDEX)); +} + + +void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) { + Register global = ToRegister(instr->global()); + Register result = ToRegister(instr->result()); + __ lw(result, FieldMemOperand(global, GlobalObject::kGlobalReceiverOffset)); +} + + +void LCodeGen::CallKnownFunction(Handle<JSFunction> function, + int arity, + LInstruction* instr, + CallKind call_kind) { + bool can_invoke_directly = !function->NeedsArgumentsAdaption() || + function->shared()->formal_parameter_count() == arity; + + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + + if (can_invoke_directly) { + __ LoadHeapObject(a1, function); + // Change context if needed. + bool change_context = + (info()->closure()->context() != function->context()) || + scope()->contains_with() || + (scope()->num_heap_slots() > 0); + if (change_context) { + __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + } + + // Set r0 to arguments count if adaption is not needed. Assumes that r0 + // is available to write to at this point. + if (!function->NeedsArgumentsAdaption()) { + __ li(a0, Operand(arity)); + } + + // Invoke function. + __ SetCallKind(t1, call_kind); + __ lw(at, FieldMemOperand(a1, JSFunction::kCodeEntryOffset)); + __ Call(at); + + // Set up deoptimization. + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); + } else { + SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); + ParameterCount count(arity); + __ InvokeFunction(function, count, CALL_FUNCTION, generator, call_kind); + } + + // Restore context. + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + __ mov(a0, v0); + CallKnownFunction(instr->function(), instr->arity(), instr, CALL_AS_METHOD); +} + + +void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + + // Deoptimize if not a heap number. + __ lw(scratch, FieldMemOperand(input, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(at)); + + Label done; + Register exponent = scratch0(); + scratch = no_reg; + __ lw(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset)); + // Check the sign of the argument. If the argument is positive, just + // return it. + __ Move(result, input); + __ And(at, exponent, Operand(HeapNumber::kSignMask)); + __ Branch(&done, eq, at, Operand(zero_reg)); + + // Input is negative. Reverse its sign. + // Preserve the value of all registers. + { + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + + // Registers were saved at the safepoint, so we can use + // many scratch registers. + Register tmp1 = input.is(a1) ? a0 : a1; + Register tmp2 = input.is(a2) ? a0 : a2; + Register tmp3 = input.is(a3) ? a0 : a3; + Register tmp4 = input.is(t0) ? a0 : t0; + + // exponent: floating point exponent value. + + Label allocated, slow; + __ LoadRoot(tmp4, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(tmp1, tmp2, tmp3, tmp4, &slow); + __ Branch(&allocated); + + // Slow case: Call the runtime system to do the number allocation. + __ bind(&slow); + + CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); + // Set the pointer to the new heap number in tmp. + if (!tmp1.is(v0)) + __ mov(tmp1, v0); + // Restore input_reg after call to runtime. + __ LoadFromSafepointRegisterSlot(input, input); + __ lw(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset)); + + __ bind(&allocated); + // exponent: floating point exponent value. + // tmp1: allocated heap number. + __ And(exponent, exponent, Operand(~HeapNumber::kSignMask)); + __ sw(exponent, FieldMemOperand(tmp1, HeapNumber::kExponentOffset)); + __ lw(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset)); + __ sw(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset)); + + __ StoreToSafepointRegisterSlot(tmp1, result); + } + + __ bind(&done); +} + + +void LCodeGen::EmitIntegerMathAbs(LUnaryMathOperation* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_); + Label done; + __ Branch(USE_DELAY_SLOT, &done, ge, input, Operand(zero_reg)); + __ mov(result, input); + ASSERT_EQ(2, masm()->InstructionsGeneratedSince(&done)); + __ subu(result, zero_reg, input); + // Overflow if result is still negative, i.e. 0x80000000. + DeoptimizeIf(lt, instr->environment(), result, Operand(zero_reg)); + __ bind(&done); +} + + +void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { + // Class for deferred case. + class DeferredMathAbsTaggedHeapNumber: public LDeferredCode { + public: + DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen, + LUnaryMathOperation* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { + codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_); + } + virtual LInstruction* instr() { return instr_; } + private: + LUnaryMathOperation* instr_; + }; + + Representation r = instr->hydrogen()->value()->representation(); + if (r.IsDouble()) { + FPURegister input = ToDoubleRegister(instr->InputAt(0)); + FPURegister result = ToDoubleRegister(instr->result()); + __ abs_d(result, input); + } else if (r.IsInteger32()) { + EmitIntegerMathAbs(instr); + } else { + // Representation is tagged. + DeferredMathAbsTaggedHeapNumber* deferred = + new DeferredMathAbsTaggedHeapNumber(this, instr); + Register input = ToRegister(instr->InputAt(0)); + // Smi check. + __ JumpIfNotSmi(input, deferred->entry()); + // If smi, handle it directly. + EmitIntegerMathAbs(instr); + __ bind(deferred->exit()); + } +} + + +void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { + DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + FPURegister single_scratch = double_scratch0().low(); + Register scratch1 = scratch0(); + Register except_flag = ToRegister(instr->TempAt(0)); + + __ EmitFPUTruncate(kRoundToMinusInf, + single_scratch, + input, + scratch1, + except_flag); + + // Deopt if the operation did not succeed. + DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg)); + + // Load the result. + __ mfc1(result, single_scratch); + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Test for -0. + Label done; + __ Branch(&done, ne, result, Operand(zero_reg)); + __ mfc1(scratch1, input.high()); + __ And(scratch1, scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg)); + __ bind(&done); + } +} + + +void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { + DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + Label done, check_sign_on_zero; + + // Extract exponent bits. + __ mfc1(result, input.high()); + __ Ext(scratch, + result, + HeapNumber::kExponentShift, + HeapNumber::kExponentBits); + + // If the number is in ]-0.5, +0.5[, the result is +/- 0. + Label skip1; + __ Branch(&skip1, gt, scratch, Operand(HeapNumber::kExponentBias - 2)); + __ mov(result, zero_reg); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ Branch(&check_sign_on_zero); + } else { + __ Branch(&done); + } + __ bind(&skip1); + + // The following conversion will not work with numbers + // outside of ]-2^32, 2^32[. + DeoptimizeIf(ge, instr->environment(), scratch, + Operand(HeapNumber::kExponentBias + 32)); + + // Save the original sign for later comparison. + __ And(scratch, result, Operand(HeapNumber::kSignMask)); + + __ Move(double_scratch0(), 0.5); + __ add_d(double_scratch0(), input, double_scratch0()); + + // Check sign of the result: if the sign changed, the input + // value was in ]0.5, 0[ and the result should be -0. + __ mfc1(result, double_scratch0().high()); + __ Xor(result, result, Operand(scratch)); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // ARM uses 'mi' here, which is 'lt' + DeoptimizeIf(lt, instr->environment(), result, + Operand(zero_reg)); + } else { + Label skip2; + // ARM uses 'mi' here, which is 'lt' + // Negating it results in 'ge' + __ Branch(&skip2, ge, result, Operand(zero_reg)); + __ mov(result, zero_reg); + __ Branch(&done); + __ bind(&skip2); + } + + Register except_flag = scratch; + + __ EmitFPUTruncate(kRoundToMinusInf, + double_scratch0().low(), + double_scratch0(), + result, + except_flag); + + DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg)); + + __ mfc1(result, double_scratch0().low()); + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Test for -0. + __ Branch(&done, ne, result, Operand(zero_reg)); + __ bind(&check_sign_on_zero); + __ mfc1(scratch, input.high()); + __ And(scratch, scratch, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(zero_reg)); + } + __ bind(&done); +} + + +void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { + DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); + DoubleRegister result = ToDoubleRegister(instr->result()); + __ sqrt_d(result, input); +} + + +void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { + DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); + DoubleRegister result = ToDoubleRegister(instr->result()); + DoubleRegister temp = ToDoubleRegister(instr->TempAt(0)); + + ASSERT(!input.is(result)); + + // Note that according to ECMA-262 15.8.2.13: + // Math.pow(-Infinity, 0.5) == Infinity + // Math.sqrt(-Infinity) == NaN + Label done; + __ Move(temp, -V8_INFINITY); + __ BranchF(USE_DELAY_SLOT, &done, NULL, eq, temp, input); + // Set up Infinity in the delay slot. + // result is overwritten if the branch is not taken. + __ neg_d(result, temp); + + // Add +0 to convert -0 to +0. + __ add_d(result, input, kDoubleRegZero); + __ sqrt_d(result, result); + __ bind(&done); +} + + +void LCodeGen::DoPower(LPower* instr) { + Representation exponent_type = instr->hydrogen()->right()->representation(); + // Having marked this as a call, we can use any registers. + // Just make sure that the input/output registers are the expected ones. + ASSERT(!instr->InputAt(1)->IsDoubleRegister() || + ToDoubleRegister(instr->InputAt(1)).is(f4)); + ASSERT(!instr->InputAt(1)->IsRegister() || + ToRegister(instr->InputAt(1)).is(a2)); + ASSERT(ToDoubleRegister(instr->InputAt(0)).is(f2)); + ASSERT(ToDoubleRegister(instr->result()).is(f0)); + + if (exponent_type.IsTagged()) { + Label no_deopt; + __ JumpIfSmi(a2, &no_deopt); + __ lw(t3, FieldMemOperand(a2, HeapObject::kMapOffset)); + DeoptimizeIf(ne, instr->environment(), t3, Operand(at)); + __ bind(&no_deopt); + MathPowStub stub(MathPowStub::TAGGED); + __ CallStub(&stub); + } else if (exponent_type.IsInteger32()) { + MathPowStub stub(MathPowStub::INTEGER); + __ CallStub(&stub); + } else { + ASSERT(exponent_type.IsDouble()); + MathPowStub stub(MathPowStub::DOUBLE); + __ CallStub(&stub); + } +} + + +void LCodeGen::DoRandom(LRandom* instr) { + // Having marked this instruction as a call we can use any + // registers. + ASSERT(ToDoubleRegister(instr->result()).is(f0)); + ASSERT(ToRegister(instr->InputAt(0)).is(a0)); + + __ PrepareCallCFunction(1, a1); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + __ li(a2, Operand(0x41300000)); + // Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU. + __ Move(f12, v0, a2); + // Move 0x4130000000000000 to FPU. + __ Move(f14, zero_reg, a2); + // Subtract to get the result. + __ sub_d(f0, f12, f14); +} + + +void LCodeGen::DoMathLog(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(f4)); + TranscendentalCacheStub stub(TranscendentalCache::LOG, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoMathTan(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(f4)); + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoMathCos(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(f4)); + TranscendentalCacheStub stub(TranscendentalCache::COS, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoMathSin(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(f4)); + TranscendentalCacheStub stub(TranscendentalCache::SIN, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { + switch (instr->op()) { + case kMathAbs: + DoMathAbs(instr); + break; + case kMathFloor: + DoMathFloor(instr); + break; + case kMathRound: + DoMathRound(instr); + break; + case kMathSqrt: + DoMathSqrt(instr); + break; + case kMathPowHalf: + DoMathPowHalf(instr); + break; + case kMathCos: + DoMathCos(instr); + break; + case kMathSin: + DoMathSin(instr); + break; + case kMathTan: + DoMathTan(instr); + break; + case kMathLog: + DoMathLog(instr); + break; + default: + Abort("Unimplemented type of LUnaryMathOperation."); + UNREACHABLE(); + } +} + + +void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { + ASSERT(ToRegister(instr->function()).is(a1)); + ASSERT(instr->HasPointerMap()); + ASSERT(instr->HasDeoptimizationEnvironment()); + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); + ParameterCount count(instr->arity()); + __ InvokeFunction(a1, count, CALL_FUNCTION, generator, CALL_AS_METHOD); + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallKeyed(LCallKeyed* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + + int arity = instr->arity(); + Handle<Code> ic = + isolate()->stub_cache()->ComputeKeyedCallInitialize(arity); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallNamed(LCallNamed* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + + int arity = instr->arity(); + RelocInfo::Mode mode = RelocInfo::CODE_TARGET; + Handle<Code> ic = + isolate()->stub_cache()->ComputeCallInitialize(arity, mode); + __ li(a2, Operand(instr->name())); + CallCode(ic, mode, instr); + // Restore context register. + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallFunction(LCallFunction* instr) { + ASSERT(ToRegister(instr->function()).is(a1)); + ASSERT(ToRegister(instr->result()).is(v0)); + + int arity = instr->arity(); + CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallGlobal(LCallGlobal* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + + int arity = instr->arity(); + RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT; + Handle<Code> ic = + isolate()->stub_cache()->ComputeCallInitialize(arity, mode); + __ li(a2, Operand(instr->name())); + CallCode(ic, mode, instr); + __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); +} + + +void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION); +} + + +void LCodeGen::DoCallNew(LCallNew* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(a1)); + ASSERT(ToRegister(instr->result()).is(v0)); + + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); + __ li(a0, Operand(instr->arity())); + CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr); +} + + +void LCodeGen::DoCallRuntime(LCallRuntime* instr) { + CallRuntime(instr->function(), instr->arity(), instr); +} + + +void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { + Register object = ToRegister(instr->object()); + Register value = ToRegister(instr->value()); + Register scratch = scratch0(); + int offset = instr->offset(); + + ASSERT(!object.is(value)); + + if (!instr->transition().is_null()) { + __ li(scratch, Operand(instr->transition())); + __ sw(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); + } + + // Do the store. + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; + if (instr->is_in_object()) { + __ sw(value, FieldMemOperand(object, offset)); + if (instr->hydrogen()->NeedsWriteBarrier()) { + // Update the write barrier for the object for in-object properties. + __ RecordWriteField(object, + offset, + value, + scratch, + kRAHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); + } + } else { + __ lw(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset)); + __ sw(value, FieldMemOperand(scratch, offset)); + if (instr->hydrogen()->NeedsWriteBarrier()) { + // Update the write barrier for the properties array. + // object is used as a scratch register. + __ RecordWriteField(scratch, + offset, + value, + object, + kRAHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); + } + } +} + + +void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { + ASSERT(ToRegister(instr->object()).is(a1)); + ASSERT(ToRegister(instr->value()).is(a0)); + + // Name is always in a2. + __ li(a2, Operand(instr->name())); + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) + ? isolate()->builtins()->StoreIC_Initialize_Strict() + : isolate()->builtins()->StoreIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { + DeoptimizeIf(hs, + instr->environment(), + ToRegister(instr->index()), + Operand(ToRegister(instr->length()))); +} + + +void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { + Register value = ToRegister(instr->value()); + Register elements = ToRegister(instr->object()); + Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg; + Register scratch = scratch0(); + + // Do the store. + if (instr->key()->IsConstantOperand()) { + ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); + LConstantOperand* const_operand = LConstantOperand::cast(instr->key()); + int offset = + ToInteger32(const_operand) * kPointerSize + FixedArray::kHeaderSize; + __ sw(value, FieldMemOperand(elements, offset)); + } else { + __ sll(scratch, key, kPointerSizeLog2); + __ addu(scratch, elements, scratch); + __ sw(value, FieldMemOperand(scratch, FixedArray::kHeaderSize)); + } + + if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; + // Compute address of modified element and store it into key register. + __ Addu(key, scratch, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ RecordWrite(elements, + key, + value, + kRAHasBeenSaved, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); + } +} + + +void LCodeGen::DoStoreKeyedFastDoubleElement( + LStoreKeyedFastDoubleElement* instr) { + DoubleRegister value = ToDoubleRegister(instr->value()); + Register elements = ToRegister(instr->elements()); + Register key = no_reg; + Register scratch = scratch0(); + bool key_is_constant = instr->key()->IsConstantOperand(); + int constant_key = 0; + Label not_nan; + + // Calculate the effective address of the slot in the array to store the + // double value. + if (key_is_constant) { + constant_key = ToInteger32(LConstantOperand::cast(instr->key())); + if (constant_key & 0xF0000000) { + Abort("array index constant value too big."); + } + } else { + key = ToRegister(instr->key()); + } + int shift_size = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS); + if (key_is_constant) { + __ Addu(scratch, elements, Operand(constant_key * (1 << shift_size) + + FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + } else { + __ sll(scratch, key, shift_size); + __ Addu(scratch, elements, Operand(scratch)); + __ Addu(scratch, scratch, + Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + } + + Label is_nan; + // Check for NaN. All NaNs must be canonicalized. + __ BranchF(NULL, &is_nan, eq, value, value); + __ Branch(¬_nan); + + // Only load canonical NaN if the comparison above set the overflow. + __ bind(&is_nan); + __ Move(value, FixedDoubleArray::canonical_not_the_hole_nan_as_double()); + + __ bind(¬_nan); + __ sdc1(value, MemOperand(scratch)); +} + + +void LCodeGen::DoStoreKeyedSpecializedArrayElement( + LStoreKeyedSpecializedArrayElement* instr) { + + Register external_pointer = ToRegister(instr->external_pointer()); + Register key = no_reg; + ElementsKind elements_kind = instr->elements_kind(); + bool key_is_constant = instr->key()->IsConstantOperand(); + int constant_key = 0; + if (key_is_constant) { + constant_key = ToInteger32(LConstantOperand::cast(instr->key())); + if (constant_key & 0xF0000000) { + Abort("array index constant value too big."); + } + } else { + key = ToRegister(instr->key()); + } + int shift_size = ElementsKindToShiftSize(elements_kind); + + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { + FPURegister value(ToDoubleRegister(instr->value())); + if (key_is_constant) { + __ Addu(scratch0(), external_pointer, constant_key * (1 << shift_size)); + } else { + __ sll(scratch0(), key, shift_size); + __ Addu(scratch0(), scratch0(), external_pointer); + } + + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { + __ cvt_s_d(double_scratch0(), value); + __ swc1(double_scratch0(), MemOperand(scratch0())); + } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS + __ sdc1(value, MemOperand(scratch0())); + } + } else { + Register value(ToRegister(instr->value())); + MemOperand mem_operand(zero_reg); + Register scratch = scratch0(); + if (key_is_constant) { + mem_operand = MemOperand(external_pointer, + constant_key * (1 << shift_size)); + } else { + __ sll(scratch, key, shift_size); + __ Addu(scratch, scratch, external_pointer); + mem_operand = MemOperand(scratch); + } + switch (elements_kind) { + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + __ sb(value, mem_operand); + break; + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + __ sh(value, mem_operand); + break; + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + __ sw(value, mem_operand); + break; + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: + UNREACHABLE(); + break; + } + } +} + +void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { + ASSERT(ToRegister(instr->object()).is(a2)); + ASSERT(ToRegister(instr->key()).is(a1)); + ASSERT(ToRegister(instr->value()).is(a0)); + + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) + ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() + : isolate()->builtins()->KeyedStoreIC_Initialize(); + CallCode(ic, RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { + Register object_reg = ToRegister(instr->object()); + Register new_map_reg = ToRegister(instr->new_map_reg()); + Register scratch = scratch0(); + + Handle<Map> from_map = instr->original_map(); + Handle<Map> to_map = instr->transitioned_map(); + ElementsKind from_kind = from_map->elements_kind(); + ElementsKind to_kind = to_map->elements_kind(); + + __ mov(ToRegister(instr->result()), object_reg); + + Label not_applicable; + __ lw(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset)); + __ Branch(¬_applicable, ne, scratch, Operand(from_map)); + + __ li(new_map_reg, Operand(to_map)); + if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + __ sw(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset)); + // Write barrier. + __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, + scratch, kRAHasBeenSaved, kDontSaveFPRegs); + } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && + to_kind == FAST_DOUBLE_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(a2)); + ASSERT(new_map_reg.is(a3)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), + RelocInfo::CODE_TARGET, instr); + } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(a2)); + ASSERT(new_map_reg.is(a3)); + __ mov(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(), + RelocInfo::CODE_TARGET, instr); + } else { + UNREACHABLE(); + } + __ bind(¬_applicable); +} + + +void LCodeGen::DoStringAdd(LStringAdd* instr) { + __ push(ToRegister(instr->left())); + __ push(ToRegister(instr->right())); + StringAddStub stub(NO_STRING_CHECK_IN_STUB); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + +void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { + class DeferredStringCharCodeAt: public LDeferredCode { + public: + DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LStringCharCodeAt* instr_; + }; + + DeferredStringCharCodeAt* deferred = + new DeferredStringCharCodeAt(this, instr); + StringCharLoadGenerator::Generate(masm(), + ToRegister(instr->string()), + ToRegister(instr->index()), + ToRegister(instr->result()), + deferred->entry()); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) { + Register string = ToRegister(instr->string()); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ mov(result, zero_reg); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ push(string); + // Push the index as a smi. This is safe because of the checks in + // DoStringCharCodeAt above. + if (instr->index()->IsConstantOperand()) { + int const_index = ToInteger32(LConstantOperand::cast(instr->index())); + __ Addu(scratch, zero_reg, Operand(Smi::FromInt(const_index))); + __ push(scratch); + } else { + Register index = ToRegister(instr->index()); + __ SmiTag(index); + __ push(index); + } + CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr); + if (FLAG_debug_code) { + __ AbortIfNotSmi(v0); + } + __ SmiUntag(v0); + __ StoreToSafepointRegisterSlot(v0, result); +} + + +void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { + class DeferredStringCharFromCode: public LDeferredCode { + public: + DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LStringCharFromCode* instr_; + }; + + DeferredStringCharFromCode* deferred = + new DeferredStringCharFromCode(this, instr); + + ASSERT(instr->hydrogen()->value()->representation().IsInteger32()); + Register char_code = ToRegister(instr->char_code()); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + ASSERT(!char_code.is(result)); + + __ Branch(deferred->entry(), hi, + char_code, Operand(String::kMaxAsciiCharCode)); + __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex); + __ sll(scratch, char_code, kPointerSizeLog2); + __ Addu(result, result, scratch); + __ lw(result, FieldMemOperand(result, FixedArray::kHeaderSize)); + __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex); + __ Branch(deferred->entry(), eq, result, Operand(scratch)); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) { + Register char_code = ToRegister(instr->char_code()); + Register result = ToRegister(instr->result()); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ mov(result, zero_reg); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ SmiTag(char_code); + __ push(char_code); + CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr); + __ StoreToSafepointRegisterSlot(v0, result); +} + + +void LCodeGen::DoStringLength(LStringLength* instr) { + Register string = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + __ lw(result, FieldMemOperand(string, String::kLengthOffset)); +} + + +void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { + LOperand* input = instr->InputAt(0); + ASSERT(input->IsRegister() || input->IsStackSlot()); + LOperand* output = instr->result(); + ASSERT(output->IsDoubleRegister()); + FPURegister single_scratch = double_scratch0().low(); + if (input->IsStackSlot()) { + Register scratch = scratch0(); + __ lw(scratch, ToMemOperand(input)); + __ mtc1(scratch, single_scratch); + } else { + __ mtc1(ToRegister(input), single_scratch); + } + __ cvt_d_w(ToDoubleRegister(output), single_scratch); +} + + +void LCodeGen::DoNumberTagI(LNumberTagI* instr) { + class DeferredNumberTagI: public LDeferredCode { + public: + DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LNumberTagI* instr_; + }; + + Register src = ToRegister(instr->InputAt(0)); + Register dst = ToRegister(instr->result()); + Register overflow = scratch0(); + + DeferredNumberTagI* deferred = new DeferredNumberTagI(this, instr); + __ SmiTagCheckOverflow(dst, src, overflow); + __ BranchOnOverflow(deferred->entry(), overflow); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) { + Label slow; + Register src = ToRegister(instr->InputAt(0)); + Register dst = ToRegister(instr->result()); + FPURegister dbl_scratch = double_scratch0(); + + // Preserve the value of all registers. + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + + // There was overflow, so bits 30 and 31 of the original integer + // disagree. Try to allocate a heap number in new space and store + // the value in there. If that fails, call the runtime system. + Label done; + if (dst.is(src)) { + __ SmiUntag(src, dst); + __ Xor(src, src, Operand(0x80000000)); + } + __ mtc1(src, dbl_scratch); + __ cvt_d_w(dbl_scratch, dbl_scratch); + if (FLAG_inline_new) { + __ LoadRoot(t2, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(t1, a3, t0, t2, &slow); + __ Move(dst, t1); + __ Branch(&done); + } + + // Slow case: Call the runtime system to do the number allocation. + __ bind(&slow); + + // TODO(3095996): Put a valid pointer value in the stack slot where the result + // register is stored, as this register is in the pointer map, but contains an + // integer value. + __ StoreToSafepointRegisterSlot(zero_reg, dst); + CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); + __ Move(dst, v0); + + // Done. Put the value in dbl_scratch into the value of the allocated heap + // number. + __ bind(&done); + __ sdc1(dbl_scratch, FieldMemOperand(dst, HeapNumber::kValueOffset)); + __ StoreToSafepointRegisterSlot(dst, dst); +} + + +void LCodeGen::DoNumberTagD(LNumberTagD* instr) { + class DeferredNumberTagD: public LDeferredCode { + public: + DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LNumberTagD* instr_; + }; + + DoubleRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + Register scratch = scratch0(); + Register reg = ToRegister(instr->result()); + Register temp1 = ToRegister(instr->TempAt(0)); + Register temp2 = ToRegister(instr->TempAt(1)); + + DeferredNumberTagD* deferred = new DeferredNumberTagD(this, instr); + if (FLAG_inline_new) { + __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(reg, temp1, temp2, scratch, deferred->entry()); + } else { + __ Branch(deferred->entry()); + } + __ bind(deferred->exit()); + __ sdc1(input_reg, FieldMemOperand(reg, HeapNumber::kValueOffset)); +} + + +void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register reg = ToRegister(instr->result()); + __ mov(reg, zero_reg); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); + __ StoreToSafepointRegisterSlot(v0, reg); +} + + +void LCodeGen::DoSmiTag(LSmiTag* instr) { + ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)); + __ SmiTag(ToRegister(instr->result()), ToRegister(instr->InputAt(0))); +} + + +void LCodeGen::DoSmiUntag(LSmiUntag* instr) { + Register scratch = scratch0(); + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + if (instr->needs_check()) { + STATIC_ASSERT(kHeapObjectTag == 1); + // If the input is a HeapObject, value of scratch won't be zero. + __ And(scratch, input, Operand(kHeapObjectTag)); + __ SmiUntag(result, input); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(zero_reg)); + } else { + __ SmiUntag(result, input); + } +} + + +void LCodeGen::EmitNumberUntagD(Register input_reg, + DoubleRegister result_reg, + bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, + LEnvironment* env) { + Register scratch = scratch0(); + + Label load_smi, heap_number, done; + + // Smi check. + __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi); + + // Heap number map check. + __ lw(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + if (deoptimize_on_undefined) { + DeoptimizeIf(ne, env, scratch, Operand(at)); + } else { + Label heap_number; + __ Branch(&heap_number, eq, scratch, Operand(at)); + + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + DeoptimizeIf(ne, env, input_reg, Operand(at)); + + // Convert undefined to NaN. + __ LoadRoot(at, Heap::kNanValueRootIndex); + __ ldc1(result_reg, FieldMemOperand(at, HeapNumber::kValueOffset)); + __ Branch(&done); + + __ bind(&heap_number); + } + // Heap number to double register conversion. + __ ldc1(result_reg, FieldMemOperand(input_reg, HeapNumber::kValueOffset)); + if (deoptimize_on_minus_zero) { + __ mfc1(at, result_reg.low()); + __ Branch(&done, ne, at, Operand(zero_reg)); + __ mfc1(scratch, result_reg.high()); + DeoptimizeIf(eq, env, scratch, Operand(HeapNumber::kSignMask)); + } + __ Branch(&done); + + // Smi to double register conversion + __ bind(&load_smi); + // scratch: untagged value of input_reg + __ mtc1(scratch, result_reg); + __ cvt_d_w(result_reg, result_reg); + __ bind(&done); +} + + +void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { + Register input_reg = ToRegister(instr->InputAt(0)); + Register scratch1 = scratch0(); + Register scratch2 = ToRegister(instr->TempAt(0)); + DoubleRegister double_scratch = double_scratch0(); + FPURegister single_scratch = double_scratch.low(); + + ASSERT(!scratch1.is(input_reg) && !scratch1.is(scratch2)); + ASSERT(!scratch2.is(input_reg) && !scratch2.is(scratch1)); + + Label done; + + // The input is a tagged HeapObject. + // Heap number map check. + __ lw(scratch1, FieldMemOperand(input_reg, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + // This 'at' value and scratch1 map value are used for tests in both clauses + // of the if. + + if (instr->truncating()) { + Register scratch3 = ToRegister(instr->TempAt(1)); + DoubleRegister double_scratch2 = ToDoubleRegister(instr->TempAt(2)); + ASSERT(!scratch3.is(input_reg) && + !scratch3.is(scratch1) && + !scratch3.is(scratch2)); + // Performs a truncating conversion of a floating point number as used by + // the JS bitwise operations. + Label heap_number; + __ Branch(&heap_number, eq, scratch1, Operand(at)); // HeapNumber map? + // Check for undefined. Undefined is converted to zero for truncating + // conversions. + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + DeoptimizeIf(ne, instr->environment(), input_reg, Operand(at)); + ASSERT(ToRegister(instr->result()).is(input_reg)); + __ mov(input_reg, zero_reg); + __ Branch(&done); + + __ bind(&heap_number); + __ ldc1(double_scratch2, + FieldMemOperand(input_reg, HeapNumber::kValueOffset)); + __ EmitECMATruncate(input_reg, + double_scratch2, + single_scratch, + scratch1, + scratch2, + scratch3); + } else { + // Deoptimize if we don't have a heap number. + DeoptimizeIf(ne, instr->environment(), scratch1, Operand(at)); + + // Load the double value. + __ ldc1(double_scratch, + FieldMemOperand(input_reg, HeapNumber::kValueOffset)); + + Register except_flag = scratch2; + __ EmitFPUTruncate(kRoundToZero, + single_scratch, + double_scratch, + scratch1, + except_flag, + kCheckForInexactConversion); + + // Deopt if the operation did not succeed. + DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg)); + + // Load the result. + __ mfc1(input_reg, single_scratch); + + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ Branch(&done, ne, input_reg, Operand(zero_reg)); + + __ mfc1(scratch1, double_scratch.high()); + __ And(scratch1, scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg)); + } + } + __ bind(&done); +} + + +void LCodeGen::DoTaggedToI(LTaggedToI* instr) { + class DeferredTaggedToI: public LDeferredCode { + public: + DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LTaggedToI* instr_; + }; + + LOperand* input = instr->InputAt(0); + ASSERT(input->IsRegister()); + ASSERT(input->Equals(instr->result())); + + Register input_reg = ToRegister(input); + + DeferredTaggedToI* deferred = new DeferredTaggedToI(this, instr); + + // Let the deferred code handle the HeapObject case. + __ JumpIfNotSmi(input_reg, deferred->entry()); + + // Smi to int32 conversion. + __ SmiUntag(input_reg); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { + LOperand* input = instr->InputAt(0); + ASSERT(input->IsRegister()); + LOperand* result = instr->result(); + ASSERT(result->IsDoubleRegister()); + + Register input_reg = ToRegister(input); + DoubleRegister result_reg = ToDoubleRegister(result); + + EmitNumberUntagD(input_reg, result_reg, + instr->hydrogen()->deoptimize_on_undefined(), + instr->hydrogen()->deoptimize_on_minus_zero(), + instr->environment()); +} + + +void LCodeGen::DoDoubleToI(LDoubleToI* instr) { + Register result_reg = ToRegister(instr->result()); + Register scratch1 = scratch0(); + Register scratch2 = ToRegister(instr->TempAt(0)); + DoubleRegister double_input = ToDoubleRegister(instr->InputAt(0)); + DoubleRegister double_scratch = double_scratch0(); + FPURegister single_scratch = double_scratch0().low(); + + if (instr->truncating()) { + Register scratch3 = ToRegister(instr->TempAt(1)); + __ EmitECMATruncate(result_reg, + double_input, + single_scratch, + scratch1, + scratch2, + scratch3); + } else { + Register except_flag = scratch2; + + __ EmitFPUTruncate(kRoundToMinusInf, + single_scratch, + double_input, + scratch1, + except_flag, + kCheckForInexactConversion); + + // Deopt if the operation did not succeed (except_flag != 0). + DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg)); + + // Load the result. + __ mfc1(result_reg, single_scratch); + } +} + + +void LCodeGen::DoCheckSmi(LCheckSmi* instr) { + LOperand* input = instr->InputAt(0); + __ And(at, ToRegister(input), Operand(kSmiTagMask)); + DeoptimizeIf(ne, instr->environment(), at, Operand(zero_reg)); +} + + +void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { + LOperand* input = instr->InputAt(0); + __ And(at, ToRegister(input), Operand(kSmiTagMask)); + DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg)); +} + + +void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { + Register input = ToRegister(instr->InputAt(0)); + Register scratch = scratch0(); + + __ GetObjectType(input, scratch, scratch); + + if (instr->hydrogen()->is_interval_check()) { + InstanceType first; + InstanceType last; + instr->hydrogen()->GetCheckInterval(&first, &last); + + // If there is only one type in the interval check for equality. + if (first == last) { + DeoptimizeIf(ne, instr->environment(), scratch, Operand(first)); + } else { + DeoptimizeIf(lo, instr->environment(), scratch, Operand(first)); + // Omit check for the last type. + if (last != LAST_TYPE) { + DeoptimizeIf(hi, instr->environment(), scratch, Operand(last)); + } + } + } else { + uint8_t mask; + uint8_t tag; + instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag); + + if (IsPowerOf2(mask)) { + ASSERT(tag == 0 || IsPowerOf2(tag)); + __ And(at, scratch, mask); + DeoptimizeIf(tag == 0 ? ne : eq, instr->environment(), + at, Operand(zero_reg)); + } else { + __ And(scratch, scratch, Operand(mask)); + DeoptimizeIf(ne, instr->environment(), scratch, Operand(tag)); + } + } +} + + +void LCodeGen::DoCheckFunction(LCheckFunction* instr) { + Register reg = ToRegister(instr->value()); + Handle<JSFunction> target = instr->hydrogen()->target(); + if (isolate()->heap()->InNewSpace(*target)) { + Register reg = ToRegister(instr->value()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(target); + __ li(at, Operand(Handle<Object>(cell))); + __ lw(at, FieldMemOperand(at, JSGlobalPropertyCell::kValueOffset)); + DeoptimizeIf(ne, instr->environment(), reg, + Operand(at)); + } else { + DeoptimizeIf(ne, instr->environment(), reg, + Operand(target)); + } +} + + +void LCodeGen::DoCheckMapCommon(Register reg, + Register scratch, + Handle<Map> map, + CompareMapMode mode, + LEnvironment* env) { + Label success; + __ CompareMapAndBranch(reg, scratch, map, &success, eq, &success, mode); + DeoptimizeIf(al, env); + __ bind(&success); +} + + +void LCodeGen::DoCheckMap(LCheckMap* instr) { + Register scratch = scratch0(); + LOperand* input = instr->InputAt(0); + ASSERT(input->IsRegister()); + Register reg = ToRegister(input); + Handle<Map> map = instr->hydrogen()->map(); + DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(), + instr->environment()); +} + + +void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) { + DoubleRegister value_reg = ToDoubleRegister(instr->unclamped()); + Register result_reg = ToRegister(instr->result()); + DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0)); + __ ClampDoubleToUint8(result_reg, value_reg, temp_reg); +} + + +void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) { + Register unclamped_reg = ToRegister(instr->unclamped()); + Register result_reg = ToRegister(instr->result()); + __ ClampUint8(result_reg, unclamped_reg); +} + + +void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { + Register scratch = scratch0(); + Register input_reg = ToRegister(instr->unclamped()); + Register result_reg = ToRegister(instr->result()); + DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0)); + Label is_smi, done, heap_number; + + // Both smi and heap number cases are handled. + __ UntagAndJumpIfSmi(scratch, input_reg, &is_smi); + + // Check for heap number + __ lw(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset)); + __ Branch(&heap_number, eq, scratch, Operand(factory()->heap_number_map())); + + // Check for undefined. Undefined is converted to zero for clamping + // conversions. + DeoptimizeIf(ne, instr->environment(), input_reg, + Operand(factory()->undefined_value())); + __ mov(result_reg, zero_reg); + __ jmp(&done); + + // Heap number + __ bind(&heap_number); + __ ldc1(double_scratch0(), FieldMemOperand(input_reg, + HeapNumber::kValueOffset)); + __ ClampDoubleToUint8(result_reg, double_scratch0(), temp_reg); + __ jmp(&done); + + __ bind(&is_smi); + __ ClampUint8(result_reg, scratch); + + __ bind(&done); +} + + +void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { + Register temp1 = ToRegister(instr->TempAt(0)); + Register temp2 = ToRegister(instr->TempAt(1)); + + Handle<JSObject> holder = instr->holder(); + Handle<JSObject> current_prototype = instr->prototype(); + + // Load prototype object. + __ LoadHeapObject(temp1, current_prototype); + + // Check prototype maps up to the holder. + while (!current_prototype.is_identical_to(holder)) { + DoCheckMapCommon(temp1, temp2, + Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); + current_prototype = + Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype())); + // Load next prototype object. + __ LoadHeapObject(temp1, current_prototype); + } + + // Check the holder map. + DoCheckMapCommon(temp1, temp2, + Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); +} + + +void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { + Heap* heap = isolate()->heap(); + ElementsKind boilerplate_elements_kind = + instr->hydrogen()->boilerplate_elements_kind(); + + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to FAST_ELEMENTS. + if (boilerplate_elements_kind != FAST_ELEMENTS) { + __ LoadHeapObject(a1, instr->hydrogen()->boilerplate_object()); + // Load map into a2. + __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); + // Load the map's "bit field 2". + __ lbu(a2, FieldMemOperand(a2, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ Ext(a2, a2, Map::kElementsKindShift, Map::kElementsKindBitCount); + DeoptimizeIf(ne, + instr->environment(), + a2, + Operand(boilerplate_elements_kind)); + } + __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset)); + __ li(a2, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); + // Boilerplate already exists, constant elements are never accessed. + // Pass an empty fixed array. + __ li(a1, Operand(Handle<FixedArray>(heap->empty_fixed_array()))); + __ Push(a3, a2, a1); + + // Pick the right runtime function or stub to call. + int length = instr->hydrogen()->length(); + if (instr->hydrogen()->IsCopyOnWrite()) { + ASSERT(instr->hydrogen()->depth() == 1); + FastCloneShallowArrayStub::Mode mode = + FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + } else if (instr->hydrogen()->depth() > 1) { + CallRuntime(Runtime::kCreateArrayLiteral, 3, instr); + } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { + CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr); + } else { + FastCloneShallowArrayStub::Mode mode = + boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS + ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + } +} + + +void LCodeGen::EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset) { + ASSERT(!source.is(a2)); + ASSERT(!result.is(a2)); + + // Increase the offset so that subsequent objects end up right after + // this one. + int current_offset = *offset; + int size = object->map()->instance_size(); + *offset += size; + + // Copy object header. + ASSERT(object->properties()->length() == 0); + ASSERT(object->elements()->length() == 0 || + object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); + int inobject_properties = object->map()->inobject_properties(); + int header_size = size - inobject_properties * kPointerSize; + for (int i = 0; i < header_size; i += kPointerSize) { + __ lw(a2, FieldMemOperand(source, i)); + __ sw(a2, FieldMemOperand(result, current_offset + i)); + } + + // Copy in-object properties. + for (int i = 0; i < inobject_properties; i++) { + int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + __ Addu(a2, result, Operand(*offset)); + __ sw(a2, FieldMemOperand(result, total_offset)); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(a2, Handle<HeapObject>::cast(value)); + __ sw(a2, FieldMemOperand(result, total_offset)); + } else { + __ li(a2, Operand(value)); + __ sw(a2, FieldMemOperand(result, total_offset)); + } + } +} + + +void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { + int size = instr->hydrogen()->total_size(); + + // Allocate all objects that are part of the literal in one big + // allocation. This avoids multiple limit checks. + Label allocated, runtime_allocate; + __ AllocateInNewSpace(size, v0, a2, a3, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ li(a0, Operand(Smi::FromInt(size))); + __ push(a0); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + + __ bind(&allocated); + int offset = 0; + __ LoadHeapObject(a1, instr->hydrogen()->boilerplate()); + EmitDeepCopy(instr->hydrogen()->boilerplate(), v0, a1, &offset); + ASSERT_EQ(size, offset); +} + + +void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + + Handle<FixedArray> constant_properties = + instr->hydrogen()->constant_properties(); + + __ lw(t0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ lw(t0, FieldMemOperand(t0, JSFunction::kLiteralsOffset)); + __ li(a3, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); + __ li(a2, Operand(constant_properties)); + int flags = instr->hydrogen()->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + __ li(a1, Operand(Smi::FromInt(flags))); + __ Push(t0, a3, a2, a1); + + // Pick the right runtime function to call. + int properties_count = constant_properties->length() / 2; + if (instr->hydrogen()->depth() > 1) { + CallRuntime(Runtime::kCreateObjectLiteral, 4, instr); + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { + CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr); + } else { + FastCloneShallowObjectStub stub(properties_count); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + } +} + + +void LCodeGen::DoToFastProperties(LToFastProperties* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(a0)); + ASSERT(ToRegister(instr->result()).is(v0)); + __ push(a0); + CallRuntime(Runtime::kToFastProperties, 1, instr); +} + + +void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { + Label materialized; + // Registers will be used as follows: + // a3 = JS function. + // t3 = literals array. + // a1 = regexp literal. + // a0 = regexp literal clone. + // a2 and t0-t2 are used as temporaries. + __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); + __ lw(t3, FieldMemOperand(a3, JSFunction::kLiteralsOffset)); + int literal_offset = FixedArray::kHeaderSize + + instr->hydrogen()->literal_index() * kPointerSize; + __ lw(a1, FieldMemOperand(t3, literal_offset)); + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + __ Branch(&materialized, ne, a1, Operand(at)); + + // Create regexp literal using runtime function + // Result will be in v0. + __ li(t2, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); + __ li(t1, Operand(instr->hydrogen()->pattern())); + __ li(t0, Operand(instr->hydrogen()->flags())); + __ Push(t3, t2, t1, t0); + CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr); + __ mov(a1, v0); + + __ bind(&materialized); + int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; + Label allocated, runtime_allocate; + + __ AllocateInNewSpace(size, v0, a2, a3, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ li(a0, Operand(Smi::FromInt(size))); + __ Push(a1, a0); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + __ pop(a1); + + __ bind(&allocated); + // Copy the content into the newly allocated memory. + // (Unroll copy loop once for better throughput). + for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) { + __ lw(a3, FieldMemOperand(a1, i)); + __ lw(a2, FieldMemOperand(a1, i + kPointerSize)); + __ sw(a3, FieldMemOperand(v0, i)); + __ sw(a2, FieldMemOperand(v0, i + kPointerSize)); + } + if ((size % (2 * kPointerSize)) != 0) { + __ lw(a3, FieldMemOperand(a1, size - kPointerSize)); + __ sw(a3, FieldMemOperand(v0, size - kPointerSize)); + } +} + + +void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { + // Use the fast case closure allocation code that allocates in new + // space for nested functions that don't need literals cloning. + Handle<SharedFunctionInfo> shared_info = instr->shared_info(); + bool pretenure = instr->hydrogen()->pretenure(); + if (!pretenure && shared_info->num_literals() == 0) { + FastNewClosureStub stub(shared_info->language_mode()); + __ li(a1, Operand(shared_info)); + __ push(a1); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + } else { + __ li(a2, Operand(shared_info)); + __ li(a1, Operand(pretenure + ? factory()->true_value() + : factory()->false_value())); + __ Push(cp, a2, a1); + CallRuntime(Runtime::kNewClosure, 3, instr); + } +} + + +void LCodeGen::DoTypeof(LTypeof* instr) { + ASSERT(ToRegister(instr->result()).is(v0)); + Register input = ToRegister(instr->InputAt(0)); + __ push(input); + CallRuntime(Runtime::kTypeof, 1, instr); +} + + +void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { + Register input = ToRegister(instr->InputAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* true_label = chunk_->GetAssemblyLabel(true_block); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Register cmp1 = no_reg; + Operand cmp2 = Operand(no_reg); + + Condition final_branch_condition = EmitTypeofIs(true_label, + false_label, + input, + instr->type_literal(), + cmp1, + cmp2); + + ASSERT(cmp1.is_valid()); + ASSERT(!cmp2.is_reg() || cmp2.rm().is_valid()); + + if (final_branch_condition != kNoCondition) { + EmitBranch(true_block, false_block, final_branch_condition, cmp1, cmp2); + } +} + + +Condition LCodeGen::EmitTypeofIs(Label* true_label, + Label* false_label, + Register input, + Handle<String> type_name, + Register& cmp1, + Operand& cmp2) { + // This function utilizes the delay slot heavily. This is used to load + // values that are always usable without depending on the type of the input + // register. + Condition final_branch_condition = kNoCondition; + Register scratch = scratch0(); + if (type_name->Equals(heap()->number_symbol())) { + __ JumpIfSmi(input, true_label); + __ lw(input, FieldMemOperand(input, HeapObject::kMapOffset)); + __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); + cmp1 = input; + cmp2 = Operand(at); + final_branch_condition = eq; + + } else if (type_name->Equals(heap()->string_symbol())) { + __ JumpIfSmi(input, false_label); + __ GetObjectType(input, input, scratch); + __ Branch(USE_DELAY_SLOT, false_label, + ge, scratch, Operand(FIRST_NONSTRING_TYPE)); + // input is an object so we can load the BitFieldOffset even if we take the + // other branch. + __ lbu(at, FieldMemOperand(input, Map::kBitFieldOffset)); + __ And(at, at, 1 << Map::kIsUndetectable); + cmp1 = at; + cmp2 = Operand(zero_reg); + final_branch_condition = eq; + + } else if (type_name->Equals(heap()->boolean_symbol())) { + __ LoadRoot(at, Heap::kTrueValueRootIndex); + __ Branch(USE_DELAY_SLOT, true_label, eq, at, Operand(input)); + __ LoadRoot(at, Heap::kFalseValueRootIndex); + cmp1 = at; + cmp2 = Operand(input); + final_branch_condition = eq; + + } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_symbol())) { + __ LoadRoot(at, Heap::kNullValueRootIndex); + cmp1 = at; + cmp2 = Operand(input); + final_branch_condition = eq; + + } else if (type_name->Equals(heap()->undefined_symbol())) { + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + __ Branch(USE_DELAY_SLOT, true_label, eq, at, Operand(input)); + // The first instruction of JumpIfSmi is an And - it is safe in the delay + // slot. + __ JumpIfSmi(input, false_label); + // Check for undetectable objects => true. + __ lw(input, FieldMemOperand(input, HeapObject::kMapOffset)); + __ lbu(at, FieldMemOperand(input, Map::kBitFieldOffset)); + __ And(at, at, 1 << Map::kIsUndetectable); + cmp1 = at; + cmp2 = Operand(zero_reg); + final_branch_condition = ne; + + } else if (type_name->Equals(heap()->function_symbol())) { + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ JumpIfSmi(input, false_label); + __ GetObjectType(input, scratch, input); + __ Branch(true_label, eq, input, Operand(JS_FUNCTION_TYPE)); + cmp1 = input; + cmp2 = Operand(JS_FUNCTION_PROXY_TYPE); + final_branch_condition = eq; + + } else if (type_name->Equals(heap()->object_symbol())) { + __ JumpIfSmi(input, false_label); + if (!FLAG_harmony_typeof) { + __ LoadRoot(at, Heap::kNullValueRootIndex); + __ Branch(USE_DELAY_SLOT, true_label, eq, at, Operand(input)); + } + // input is an object, it is safe to use GetObjectType in the delay slot. + __ GetObjectType(input, input, scratch); + __ Branch(USE_DELAY_SLOT, false_label, + lt, scratch, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + // Still an object, so the InstanceType can be loaded. + __ lbu(scratch, FieldMemOperand(input, Map::kInstanceTypeOffset)); + __ Branch(USE_DELAY_SLOT, false_label, + gt, scratch, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE)); + // Still an object, so the BitField can be loaded. + // Check for undetectable objects => false. + __ lbu(at, FieldMemOperand(input, Map::kBitFieldOffset)); + __ And(at, at, 1 << Map::kIsUndetectable); + cmp1 = at; + cmp2 = Operand(zero_reg); + final_branch_condition = eq; + + } else { + cmp1 = at; + cmp2 = Operand(zero_reg); // Set to valid regs, to avoid caller assertion. + __ Branch(false_label); + } + + return final_branch_condition; +} + + +void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { + Register temp1 = ToRegister(instr->TempAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + EmitIsConstructCall(temp1, scratch0()); + + EmitBranch(true_block, false_block, eq, temp1, + Operand(Smi::FromInt(StackFrame::CONSTRUCT))); +} + + +void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) { + ASSERT(!temp1.is(temp2)); + // Get the frame pointer for the calling frame. + __ lw(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + Label check_frame_marker; + __ lw(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset)); + __ Branch(&check_frame_marker, ne, temp2, + Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ lw(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ lw(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset)); +} + + +void LCodeGen::EnsureSpaceForLazyDeopt() { + // Ensure that we have enough space after the previous lazy-bailout + // instruction for patching the code here. + int current_pc = masm()->pc_offset(); + int patch_size = Deoptimizer::patch_size(); + if (current_pc < last_lazy_deopt_pc_ + patch_size) { + int padding_size = last_lazy_deopt_pc_ + patch_size - current_pc; + ASSERT_EQ(0, padding_size % Assembler::kInstrSize); + while (padding_size > 0) { + __ nop(); + padding_size -= Assembler::kInstrSize; + } + } + last_lazy_deopt_pc_ = masm()->pc_offset(); +} + + +void LCodeGen::DoLazyBailout(LLazyBailout* instr) { + EnsureSpaceForLazyDeopt(); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); +} + + +void LCodeGen::DoDeoptimize(LDeoptimize* instr) { + DeoptimizeIf(al, instr->environment(), zero_reg, Operand(zero_reg)); +} + + +void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) { + Register object = ToRegister(instr->object()); + Register key = ToRegister(instr->key()); + Register strict = scratch0(); + __ li(strict, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(object, key, strict); + ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + SafepointGenerator safepoint_generator( + this, pointers, Safepoint::kLazyDeopt); + __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator); +} + + +void LCodeGen::DoIn(LIn* instr) { + Register obj = ToRegister(instr->object()); + Register key = ToRegister(instr->key()); + __ Push(key, obj); + ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment()); + LPointerMap* pointers = instr->pointer_map(); + RecordPosition(pointers->position()); + SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); + __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator); +} + + +void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) { + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ CallRuntimeSaveDoubles(Runtime::kStackGuard); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); +} + + +void LCodeGen::DoStackCheck(LStackCheck* instr) { + class DeferredStackCheck: public LDeferredCode { + public: + DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LStackCheck* instr_; + }; + + ASSERT(instr->HasEnvironment()); + LEnvironment* env = instr->environment(); + // There is no LLazyBailout instruction for stack-checks. We have to + // prepare for lazy deoptimization explicitly here. + if (instr->hydrogen()->is_function_entry()) { + // Perform stack overflow check. + Label done; + __ LoadRoot(at, Heap::kStackLimitRootIndex); + __ Branch(&done, hs, sp, Operand(at)); + StackCheckStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); + EnsureSpaceForLazyDeopt(); + __ bind(&done); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); + } else { + ASSERT(instr->hydrogen()->is_backwards_branch()); + // Perform stack overflow check if this goto needs it before jumping. + DeferredStackCheck* deferred_stack_check = + new DeferredStackCheck(this, instr); + __ LoadRoot(at, Heap::kStackLimitRootIndex); + __ Branch(deferred_stack_check->entry(), lo, sp, Operand(at)); + EnsureSpaceForLazyDeopt(); + __ bind(instr->done_label()); + deferred_stack_check->SetExit(instr->done_label()); + RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); + // Don't record a deoptimization index for the safepoint here. + // This will be done explicitly when emitting call and the safepoint in + // the deferred code. + } +} + + +void LCodeGen::DoOsrEntry(LOsrEntry* instr) { + // This is a pseudo-instruction that ensures that the environment here is + // properly registered for deoptimization and records the assembler's PC + // offset. + LEnvironment* environment = instr->environment(); + environment->SetSpilledRegisters(instr->SpilledRegisterArray(), + instr->SpilledDoubleRegisterArray()); + + // If the environment were already registered, we would have no way of + // backpatching it with the spill slot operands. + ASSERT(!environment->HasBeenRegistered()); + RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); + ASSERT(osr_pc_offset_ == -1); + osr_pc_offset_ = masm()->pc_offset(); +} + + +#undef __ + +} } // namespace v8::internal diff --git a/deps/v8/src/mips/lithium-codegen-mips.h b/deps/v8/src/mips/lithium-codegen-mips.h index 2aec68456d..513992c67a 100644 --- a/deps/v8/src/mips/lithium-codegen-mips.h +++ b/deps/v8/src/mips/lithium-codegen-mips.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,35 +29,419 @@ #define V8_MIPS_LITHIUM_CODEGEN_MIPS_H_ #include "mips/lithium-mips.h" - +#include "mips/lithium-gap-resolver-mips.h" #include "deoptimizer.h" #include "safepoint-table.h" #include "scopes.h" -// Note: this file was taken from the X64 version. ARM has a partially working -// lithium implementation, but for now it is not ported to mips. - namespace v8 { namespace internal { // Forward declarations. class LDeferredCode; +class SafepointGenerator; class LCodeGen BASE_EMBEDDED { public: - LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) { } + LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) + : chunk_(chunk), + masm_(assembler), + info_(info), + current_block_(-1), + current_instruction_(-1), + instructions_(chunk->instructions()), + deoptimizations_(4), + deopt_jump_table_(4), + deoptimization_literals_(8), + inlined_function_count_(0), + scope_(info->scope()), + status_(UNUSED), + deferred_(8), + osr_pc_offset_(-1), + last_lazy_deopt_pc_(0), + resolver_(this), + expected_safepoint_kind_(Safepoint::kSimple) { + PopulateDeoptimizationLiteralsWithInlinedFunctions(); + } + + + // Simple accessors. + MacroAssembler* masm() const { return masm_; } + CompilationInfo* info() const { return info_; } + Isolate* isolate() const { return info_->isolate(); } + Factory* factory() const { return isolate()->factory(); } + Heap* heap() const { return isolate()->heap(); } + + // Support for converting LOperands to assembler types. + // LOperand must be a register. + Register ToRegister(LOperand* op) const; + + // LOperand is loaded into scratch, unless already a register. + Register EmitLoadRegister(LOperand* op, Register scratch); + + // LOperand must be a double register. + DoubleRegister ToDoubleRegister(LOperand* op) const; + + // LOperand is loaded into dbl_scratch, unless already a double register. + DoubleRegister EmitLoadDoubleRegister(LOperand* op, + FloatRegister flt_scratch, + DoubleRegister dbl_scratch); + int ToInteger32(LConstantOperand* op) const; + double ToDouble(LConstantOperand* op) const; + Operand ToOperand(LOperand* op); + MemOperand ToMemOperand(LOperand* op) const; + // Returns a MemOperand pointing to the high word of a DoubleStackSlot. + MemOperand ToHighMemOperand(LOperand* op) const; + + bool IsInteger32(LConstantOperand* op) const; + Handle<Object> ToHandle(LConstantOperand* op) const; // Try to generate code for the entire chunk, but it may fail if the // chunk contains constructs we cannot handle. Returns true if the // code generation attempt succeeded. - bool GenerateCode() { - UNIMPLEMENTED(); - return false; - } + bool GenerateCode(); // Finish the code by setting stack height, safepoint, and bailout // information on it. - void FinishCode(Handle<Code> code) { UNIMPLEMENTED(); } + void FinishCode(Handle<Code> code); + + void DoDeferredNumberTagD(LNumberTagD* instr); + void DoDeferredNumberTagI(LNumberTagI* instr); + void DoDeferredTaggedToI(LTaggedToI* instr); + void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); + void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); + void DoDeferredStringCharFromCode(LStringCharFromCode* instr); + void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, + Label* map_check); + + void DoCheckMapCommon(Register reg, Register scratch, Handle<Map> map, + CompareMapMode mode, LEnvironment* env); + + // Parallel move support. + void DoParallelMove(LParallelMove* move); + void DoGap(LGap* instr); + + // Emit frame translation commands for an environment. + void WriteTranslation(LEnvironment* environment, Translation* translation); + + // Declare methods that deal with the individual node types. +#define DECLARE_DO(type) void Do##type(L##type* node); + LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) +#undef DECLARE_DO + + private: + enum Status { + UNUSED, + GENERATING, + DONE, + ABORTED + }; + + bool is_unused() const { return status_ == UNUSED; } + bool is_generating() const { return status_ == GENERATING; } + bool is_done() const { return status_ == DONE; } + bool is_aborted() const { return status_ == ABORTED; } + + StrictModeFlag strict_mode_flag() const { + return info()->is_classic_mode() ? kNonStrictMode : kStrictMode; + } + + LChunk* chunk() const { return chunk_; } + Scope* scope() const { return scope_; } + HGraph* graph() const { return chunk_->graph(); } + + Register scratch0() { return kLithiumScratchReg; } + Register scratch1() { return kLithiumScratchReg2; } + DoubleRegister double_scratch0() { return kLithiumScratchDouble; } + + int GetNextEmittedBlock(int block); + LInstruction* GetNextInstruction(); + + void EmitClassOfTest(Label* if_true, + Label* if_false, + Handle<String> class_name, + Register input, + Register temporary, + Register temporary2); + + int GetStackSlotCount() const { return chunk()->spill_slot_count(); } + int GetParameterCount() const { return scope()->num_parameters(); } + + void Abort(const char* format, ...); + void Comment(const char* format, ...); + + void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code); } + + // Code generation passes. Returns true if code generation should + // continue. + bool GeneratePrologue(); + bool GenerateBody(); + bool GenerateDeferredCode(); + bool GenerateDeoptJumpTable(); + bool GenerateSafepointTable(); + + enum SafepointMode { + RECORD_SIMPLE_SAFEPOINT, + RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS + }; + + void CallCode(Handle<Code> code, + RelocInfo::Mode mode, + LInstruction* instr); + + void CallCodeGeneric(Handle<Code> code, + RelocInfo::Mode mode, + LInstruction* instr, + SafepointMode safepoint_mode); + + void CallRuntime(const Runtime::Function* function, + int num_arguments, + LInstruction* instr); + + void CallRuntime(Runtime::FunctionId id, + int num_arguments, + LInstruction* instr) { + const Runtime::Function* function = Runtime::FunctionForId(id); + CallRuntime(function, num_arguments, instr); + } + + void CallRuntimeFromDeferred(Runtime::FunctionId id, + int argc, + LInstruction* instr); + + // Generate a direct call to a known function. Expects the function + // to be in a1. + void CallKnownFunction(Handle<JSFunction> function, + int arity, + LInstruction* instr, + CallKind call_kind); + + void LoadHeapObject(Register result, Handle<HeapObject> object); + + void RecordSafepointWithLazyDeopt(LInstruction* instr, + SafepointMode safepoint_mode); + + void RegisterEnvironmentForDeoptimization(LEnvironment* environment, + Safepoint::DeoptMode mode); + void DeoptimizeIf(Condition cc, + LEnvironment* environment, + Register src1 = zero_reg, + const Operand& src2 = Operand(zero_reg)); + + void AddToTranslation(Translation* translation, + LOperand* op, + bool is_tagged); + void PopulateDeoptimizationData(Handle<Code> code); + int DefineDeoptimizationLiteral(Handle<Object> literal); + + void PopulateDeoptimizationLiteralsWithInlinedFunctions(); + + Register ToRegister(int index) const; + DoubleRegister ToDoubleRegister(int index) const; + + // Specific math operations - used from DoUnaryMathOperation. + void EmitIntegerMathAbs(LUnaryMathOperation* instr); + void DoMathAbs(LUnaryMathOperation* instr); + void DoMathFloor(LUnaryMathOperation* instr); + void DoMathRound(LUnaryMathOperation* instr); + void DoMathSqrt(LUnaryMathOperation* instr); + void DoMathPowHalf(LUnaryMathOperation* instr); + void DoMathLog(LUnaryMathOperation* instr); + void DoMathTan(LUnaryMathOperation* instr); + void DoMathCos(LUnaryMathOperation* instr); + void DoMathSin(LUnaryMathOperation* instr); + + // Support for recording safepoint and position information. + void RecordSafepoint(LPointerMap* pointers, + Safepoint::Kind kind, + int arguments, + Safepoint::DeoptMode mode); + void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode); + void RecordSafepoint(Safepoint::DeoptMode mode); + void RecordSafepointWithRegisters(LPointerMap* pointers, + int arguments, + Safepoint::DeoptMode mode); + void RecordSafepointWithRegistersAndDoubles(LPointerMap* pointers, + int arguments, + Safepoint::DeoptMode mode); + void RecordPosition(int position); + + static Condition TokenToCondition(Token::Value op, bool is_unsigned); + void EmitGoto(int block); + void EmitBranch(int left_block, + int right_block, + Condition cc, + Register src1, + const Operand& src2); + void EmitBranchF(int left_block, + int right_block, + Condition cc, + FPURegister src1, + FPURegister src2); + void EmitCmpI(LOperand* left, LOperand* right); + void EmitNumberUntagD(Register input, + DoubleRegister result, + bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, + LEnvironment* env); + + // Emits optimized code for typeof x == "y". Modifies input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + // Returns two registers in cmp1 and cmp2 that can be used in the + // Branch instruction after EmitTypeofIs. + Condition EmitTypeofIs(Label* true_label, + Label* false_label, + Register input, + Handle<String> type_name, + Register& cmp1, + Operand& cmp2); + + // Emits optimized code for %_IsObject(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsObject(Register input, + Register temp1, + Register temp2, + Label* is_not_object, + Label* is_object); + + // Emits optimized code for %_IsString(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsString(Register input, + Register temp1, + Label* is_not_string); + + // Emits optimized code for %_IsConstructCall(). + // Caller should branch on equal condition. + void EmitIsConstructCall(Register temp1, Register temp2); + + void EmitLoadFieldOrConstantFunction(Register result, + Register object, + Handle<Map> type, + Handle<String> name); + + // Emits optimized code to deep-copy the contents of statically known + // object graphs (e.g. object literal boilerplate). + void EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset); + + struct JumpTableEntry { + explicit inline JumpTableEntry(Address entry) + : label(), + address(entry) { } + Label label; + Address address; + }; + + void EnsureSpaceForLazyDeopt(); + + LChunk* const chunk_; + MacroAssembler* const masm_; + CompilationInfo* const info_; + + int current_block_; + int current_instruction_; + const ZoneList<LInstruction*>* instructions_; + ZoneList<LEnvironment*> deoptimizations_; + ZoneList<JumpTableEntry> deopt_jump_table_; + ZoneList<Handle<Object> > deoptimization_literals_; + int inlined_function_count_; + Scope* const scope_; + Status status_; + TranslationBuffer translations_; + ZoneList<LDeferredCode*> deferred_; + int osr_pc_offset_; + int last_lazy_deopt_pc_; + + // Builder that keeps track of safepoints in the code. The table + // itself is emitted at the end of the generated code. + SafepointTableBuilder safepoints_; + + // Compiler from a set of parallel moves to a sequential list of moves. + LGapResolver resolver_; + + Safepoint::Kind expected_safepoint_kind_; + + class PushSafepointRegistersScope BASE_EMBEDDED { + public: + PushSafepointRegistersScope(LCodeGen* codegen, + Safepoint::Kind kind) + : codegen_(codegen) { + ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple); + codegen_->expected_safepoint_kind_ = kind; + + switch (codegen_->expected_safepoint_kind_) { + case Safepoint::kWithRegisters: + codegen_->masm_->PushSafepointRegisters(); + break; + case Safepoint::kWithRegistersAndDoubles: + codegen_->masm_->PushSafepointRegistersAndDoubles(); + break; + default: + UNREACHABLE(); + } + } + + ~PushSafepointRegistersScope() { + Safepoint::Kind kind = codegen_->expected_safepoint_kind_; + ASSERT((kind & Safepoint::kWithRegisters) != 0); + switch (kind) { + case Safepoint::kWithRegisters: + codegen_->masm_->PopSafepointRegisters(); + break; + case Safepoint::kWithRegistersAndDoubles: + codegen_->masm_->PopSafepointRegistersAndDoubles(); + break; + default: + UNREACHABLE(); + } + codegen_->expected_safepoint_kind_ = Safepoint::kSimple; + } + + private: + LCodeGen* codegen_; + }; + + friend class LDeferredCode; + friend class LEnvironment; + friend class SafepointGenerator; + DISALLOW_COPY_AND_ASSIGN(LCodeGen); +}; + + +class LDeferredCode: public ZoneObject { + public: + explicit LDeferredCode(LCodeGen* codegen) + : codegen_(codegen), + external_exit_(NULL), + instruction_index_(codegen->current_instruction_) { + codegen->AddDeferredCode(this); + } + + virtual ~LDeferredCode() { } + virtual void Generate() = 0; + virtual LInstruction* instr() = 0; + + void SetExit(Label* exit) { external_exit_ = exit; } + Label* entry() { return &entry_; } + Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; } + int instruction_index() const { return instruction_index_; } + + protected: + LCodeGen* codegen() const { return codegen_; } + MacroAssembler* masm() const { return codegen_->masm(); } + + private: + LCodeGen* codegen_; + Label entry_; + Label exit_; + Label* external_exit_; + int instruction_index_; }; } } // namespace v8::internal diff --git a/deps/v8/src/mips/lithium-gap-resolver-mips.cc b/deps/v8/src/mips/lithium-gap-resolver-mips.cc new file mode 100644 index 0000000000..41b060debc --- /dev/null +++ b/deps/v8/src/mips/lithium-gap-resolver-mips.cc @@ -0,0 +1,318 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "mips/lithium-gap-resolver-mips.h" +#include "mips/lithium-codegen-mips.h" + +namespace v8 { +namespace internal { + +static const Register kSavedValueRegister = kLithiumScratchReg; + +LGapResolver::LGapResolver(LCodeGen* owner) + : cgen_(owner), + moves_(32), + root_index_(0), + in_cycle_(false), + saved_destination_(NULL) {} + + +void LGapResolver::Resolve(LParallelMove* parallel_move) { + ASSERT(moves_.is_empty()); + // Build up a worklist of moves. + BuildInitialMoveList(parallel_move); + + for (int i = 0; i < moves_.length(); ++i) { + LMoveOperands move = moves_[i]; + // Skip constants to perform them last. They don't block other moves + // and skipping such moves with register destinations keeps those + // registers free for the whole algorithm. + if (!move.IsEliminated() && !move.source()->IsConstantOperand()) { + root_index_ = i; // Any cycle is found when by reaching this move again. + PerformMove(i); + if (in_cycle_) { + RestoreValue(); + } + } + } + + // Perform the moves with constant sources. + for (int i = 0; i < moves_.length(); ++i) { + if (!moves_[i].IsEliminated()) { + ASSERT(moves_[i].source()->IsConstantOperand()); + EmitMove(i); + } + } + + moves_.Rewind(0); +} + + +void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) { + // Perform a linear sweep of the moves to add them to the initial list of + // moves to perform, ignoring any move that is redundant (the source is + // the same as the destination, the destination is ignored and + // unallocated, or the move was already eliminated). + const ZoneList<LMoveOperands>* moves = parallel_move->move_operands(); + for (int i = 0; i < moves->length(); ++i) { + LMoveOperands move = moves->at(i); + if (!move.IsRedundant()) moves_.Add(move); + } + Verify(); +} + + +void LGapResolver::PerformMove(int index) { + // Each call to this function performs a move and deletes it from the move + // graph. We first recursively perform any move blocking this one. We + // mark a move as "pending" on entry to PerformMove in order to detect + // cycles in the move graph. + + // We can only find a cycle, when doing a depth-first traversal of moves, + // be encountering the starting move again. So by spilling the source of + // the starting move, we break the cycle. All moves are then unblocked, + // and the starting move is completed by writing the spilled value to + // its destination. All other moves from the spilled source have been + // completed prior to breaking the cycle. + // An additional complication is that moves to MemOperands with large + // offsets (more than 1K or 4K) require us to spill this spilled value to + // the stack, to free up the register. + ASSERT(!moves_[index].IsPending()); + ASSERT(!moves_[index].IsRedundant()); + + // Clear this move's destination to indicate a pending move. The actual + // destination is saved in a stack allocated local. Multiple moves can + // be pending because this function is recursive. + ASSERT(moves_[index].source() != NULL); // Or else it will look eliminated. + LOperand* destination = moves_[index].destination(); + moves_[index].set_destination(NULL); + + // Perform a depth-first traversal of the move graph to resolve + // dependencies. Any unperformed, unpending move with a source the same + // as this one's destination blocks this one so recursively perform all + // such moves. + for (int i = 0; i < moves_.length(); ++i) { + LMoveOperands other_move = moves_[i]; + if (other_move.Blocks(destination) && !other_move.IsPending()) { + PerformMove(i); + // If there is a blocking, pending move it must be moves_[root_index_] + // and all other moves with the same source as moves_[root_index_] are + // sucessfully executed (because they are cycle-free) by this loop. + } + } + + // We are about to resolve this move and don't need it marked as + // pending, so restore its destination. + moves_[index].set_destination(destination); + + // The move may be blocked on a pending move, which must be the starting move. + // In this case, we have a cycle, and we save the source of this move to + // a scratch register to break it. + LMoveOperands other_move = moves_[root_index_]; + if (other_move.Blocks(destination)) { + ASSERT(other_move.IsPending()); + BreakCycle(index); + return; + } + + // This move is no longer blocked. + EmitMove(index); +} + + +void LGapResolver::Verify() { +#ifdef ENABLE_SLOW_ASSERTS + // No operand should be the destination for more than one move. + for (int i = 0; i < moves_.length(); ++i) { + LOperand* destination = moves_[i].destination(); + for (int j = i + 1; j < moves_.length(); ++j) { + SLOW_ASSERT(!destination->Equals(moves_[j].destination())); + } + } +#endif +} + +#define __ ACCESS_MASM(cgen_->masm()) + +void LGapResolver::BreakCycle(int index) { + // We save in a register the value that should end up in the source of + // moves_[root_index]. After performing all moves in the tree rooted + // in that move, we save the value to that source. + ASSERT(moves_[index].destination()->Equals(moves_[root_index_].source())); + ASSERT(!in_cycle_); + in_cycle_ = true; + LOperand* source = moves_[index].source(); + saved_destination_ = moves_[index].destination(); + if (source->IsRegister()) { + __ mov(kSavedValueRegister, cgen_->ToRegister(source)); + } else if (source->IsStackSlot()) { + __ lw(kSavedValueRegister, cgen_->ToMemOperand(source)); + } else if (source->IsDoubleRegister()) { + __ mov_d(kLithiumScratchDouble, cgen_->ToDoubleRegister(source)); + } else if (source->IsDoubleStackSlot()) { + __ ldc1(kLithiumScratchDouble, cgen_->ToMemOperand(source)); + } else { + UNREACHABLE(); + } + // This move will be done by restoring the saved value to the destination. + moves_[index].Eliminate(); +} + + +void LGapResolver::RestoreValue() { + ASSERT(in_cycle_); + ASSERT(saved_destination_ != NULL); + + // Spilled value is in kSavedValueRegister or kLithiumScratchDouble. + if (saved_destination_->IsRegister()) { + __ mov(cgen_->ToRegister(saved_destination_), kSavedValueRegister); + } else if (saved_destination_->IsStackSlot()) { + __ sw(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_)); + } else if (saved_destination_->IsDoubleRegister()) { + __ mov_d(cgen_->ToDoubleRegister(saved_destination_), + kLithiumScratchDouble); + } else if (saved_destination_->IsDoubleStackSlot()) { + __ sdc1(kLithiumScratchDouble, + cgen_->ToMemOperand(saved_destination_)); + } else { + UNREACHABLE(); + } + + in_cycle_ = false; + saved_destination_ = NULL; +} + + +void LGapResolver::EmitMove(int index) { + LOperand* source = moves_[index].source(); + LOperand* destination = moves_[index].destination(); + + // Dispatch on the source and destination operand kinds. Not all + // combinations are possible. + + if (source->IsRegister()) { + Register source_register = cgen_->ToRegister(source); + if (destination->IsRegister()) { + __ mov(cgen_->ToRegister(destination), source_register); + } else { + ASSERT(destination->IsStackSlot()); + __ sw(source_register, cgen_->ToMemOperand(destination)); + } + + } else if (source->IsStackSlot()) { + MemOperand source_operand = cgen_->ToMemOperand(source); + if (destination->IsRegister()) { + __ lw(cgen_->ToRegister(destination), source_operand); + } else { + ASSERT(destination->IsStackSlot()); + MemOperand destination_operand = cgen_->ToMemOperand(destination); + if (in_cycle_) { + if (!destination_operand.OffsetIsInt16Encodable()) { + // 'at' is overwritten while saving the value to the destination. + // Therefore we can't use 'at'. It is OK if the read from the source + // destroys 'at', since that happens before the value is read. + // This uses only a single reg of the double reg-pair. + __ lwc1(kLithiumScratchDouble, source_operand); + __ swc1(kLithiumScratchDouble, destination_operand); + } else { + __ lw(at, source_operand); + __ sw(at, destination_operand); + } + } else { + __ lw(kSavedValueRegister, source_operand); + __ sw(kSavedValueRegister, destination_operand); + } + } + + } else if (source->IsConstantOperand()) { + LConstantOperand* constant_source = LConstantOperand::cast(source); + if (destination->IsRegister()) { + Register dst = cgen_->ToRegister(destination); + if (cgen_->IsInteger32(constant_source)) { + __ li(dst, Operand(cgen_->ToInteger32(constant_source))); + } else { + __ LoadObject(dst, cgen_->ToHandle(constant_source)); + } + } else { + ASSERT(destination->IsStackSlot()); + ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone. + if (cgen_->IsInteger32(constant_source)) { + __ li(kSavedValueRegister, + Operand(cgen_->ToInteger32(constant_source))); + } else { + __ LoadObject(kSavedValueRegister, + cgen_->ToHandle(constant_source)); + } + __ sw(kSavedValueRegister, cgen_->ToMemOperand(destination)); + } + + } else if (source->IsDoubleRegister()) { + DoubleRegister source_register = cgen_->ToDoubleRegister(source); + if (destination->IsDoubleRegister()) { + __ mov_d(cgen_->ToDoubleRegister(destination), source_register); + } else { + ASSERT(destination->IsDoubleStackSlot()); + MemOperand destination_operand = cgen_->ToMemOperand(destination); + __ sdc1(source_register, destination_operand); + } + + } else if (source->IsDoubleStackSlot()) { + MemOperand source_operand = cgen_->ToMemOperand(source); + if (destination->IsDoubleRegister()) { + __ ldc1(cgen_->ToDoubleRegister(destination), source_operand); + } else { + ASSERT(destination->IsDoubleStackSlot()); + MemOperand destination_operand = cgen_->ToMemOperand(destination); + if (in_cycle_) { + // kLithiumScratchDouble was used to break the cycle, + // but kSavedValueRegister is free. + MemOperand source_high_operand = + cgen_->ToHighMemOperand(source); + MemOperand destination_high_operand = + cgen_->ToHighMemOperand(destination); + __ lw(kSavedValueRegister, source_operand); + __ sw(kSavedValueRegister, destination_operand); + __ lw(kSavedValueRegister, source_high_operand); + __ sw(kSavedValueRegister, destination_high_operand); + } else { + __ ldc1(kLithiumScratchDouble, source_operand); + __ sdc1(kLithiumScratchDouble, destination_operand); + } + } + } else { + UNREACHABLE(); + } + + moves_[index].Eliminate(); +} + + +#undef __ + +} } // namespace v8::internal diff --git a/deps/v8/src/mips/lithium-gap-resolver-mips.h b/deps/v8/src/mips/lithium-gap-resolver-mips.h new file mode 100644 index 0000000000..2506e38c35 --- /dev/null +++ b/deps/v8/src/mips/lithium-gap-resolver-mips.h @@ -0,0 +1,83 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_MIPS_LITHIUM_GAP_RESOLVER_MIPS_H_ +#define V8_MIPS_LITHIUM_GAP_RESOLVER_MIPS_H_ + +#include "v8.h" + +#include "lithium.h" + +namespace v8 { +namespace internal { + +class LCodeGen; +class LGapResolver; + +class LGapResolver BASE_EMBEDDED { + public: + explicit LGapResolver(LCodeGen* owner); + + // Resolve a set of parallel moves, emitting assembler instructions. + void Resolve(LParallelMove* parallel_move); + + private: + // Build the initial list of moves. + void BuildInitialMoveList(LParallelMove* parallel_move); + + // Perform the move at the moves_ index in question (possibly requiring + // other moves to satisfy dependencies). + void PerformMove(int index); + + // If a cycle is found in the series of moves, save the blocking value to + // a scratch register. The cycle must be found by hitting the root of the + // depth-first search. + void BreakCycle(int index); + + // After a cycle has been resolved, restore the value from the scratch + // register to its proper destination. + void RestoreValue(); + + // Emit a move and remove it from the move graph. + void EmitMove(int index); + + // Verify the move list before performing moves. + void Verify(); + + LCodeGen* cgen_; + + // List of moves not yet resolved. + ZoneList<LMoveOperands> moves_; + + int root_index_; + bool in_cycle_; + LOperand* saved_destination_; +}; + +} } // namespace v8::internal + +#endif // V8_MIPS_LITHIUM_GAP_RESOLVER_MIPS_H_ diff --git a/deps/v8/src/mips/lithium-mips.cc b/deps/v8/src/mips/lithium-mips.cc new file mode 100644 index 0000000000..0bc222339f --- /dev/null +++ b/deps/v8/src/mips/lithium-mips.cc @@ -0,0 +1,2272 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "lithium-allocator-inl.h" +#include "mips/lithium-mips.h" +#include "mips/lithium-codegen-mips.h" + +namespace v8 { +namespace internal { + +#define DEFINE_COMPILE(type) \ + void L##type::CompileToNative(LCodeGen* generator) { \ + generator->Do##type(this); \ + } +LITHIUM_CONCRETE_INSTRUCTION_LIST(DEFINE_COMPILE) +#undef DEFINE_COMPILE + +LOsrEntry::LOsrEntry() { + for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) { + register_spills_[i] = NULL; + } + for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; ++i) { + double_register_spills_[i] = NULL; + } +} + + +void LOsrEntry::MarkSpilledRegister(int allocation_index, + LOperand* spill_operand) { + ASSERT(spill_operand->IsStackSlot()); + ASSERT(register_spills_[allocation_index] == NULL); + register_spills_[allocation_index] = spill_operand; +} + + +#ifdef DEBUG +void LInstruction::VerifyCall() { + // Call instructions can use only fixed registers as temporaries and + // outputs because all registers are blocked by the calling convention. + // Inputs operands must use a fixed register or use-at-start policy or + // a non-register policy. + ASSERT(Output() == NULL || + LUnallocated::cast(Output())->HasFixedPolicy() || + !LUnallocated::cast(Output())->HasRegisterPolicy()); + for (UseIterator it(this); !it.Done(); it.Advance()) { + LUnallocated* operand = LUnallocated::cast(it.Current()); + ASSERT(operand->HasFixedPolicy() || + operand->IsUsedAtStart()); + } + for (TempIterator it(this); !it.Done(); it.Advance()) { + LUnallocated* operand = LUnallocated::cast(it.Current()); + ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy()); + } +} +#endif + + +void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index, + LOperand* spill_operand) { + ASSERT(spill_operand->IsDoubleStackSlot()); + ASSERT(double_register_spills_[allocation_index] == NULL); + double_register_spills_[allocation_index] = spill_operand; +} + + +void LInstruction::PrintTo(StringStream* stream) { + stream->Add("%s ", this->Mnemonic()); + + PrintOutputOperandTo(stream); + + PrintDataTo(stream); + + if (HasEnvironment()) { + stream->Add(" "); + environment()->PrintTo(stream); + } + + if (HasPointerMap()) { + stream->Add(" "); + pointer_map()->PrintTo(stream); + } +} + + +template<int R, int I, int T> +void LTemplateInstruction<R, I, T>::PrintDataTo(StringStream* stream) { + stream->Add("= "); + for (int i = 0; i < inputs_.length(); i++) { + if (i > 0) stream->Add(" "); + inputs_[i]->PrintTo(stream); + } +} + + +template<int R, int I, int T> +void LTemplateInstruction<R, I, T>::PrintOutputOperandTo(StringStream* stream) { + for (int i = 0; i < results_.length(); i++) { + if (i > 0) stream->Add(" "); + results_[i]->PrintTo(stream); + } +} + + +void LLabel::PrintDataTo(StringStream* stream) { + LGap::PrintDataTo(stream); + LLabel* rep = replacement(); + if (rep != NULL) { + stream->Add(" Dead block replaced with B%d", rep->block_id()); + } +} + + +bool LGap::IsRedundant() const { + for (int i = 0; i < 4; i++) { + if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant()) { + return false; + } + } + + return true; +} + + +void LGap::PrintDataTo(StringStream* stream) { + for (int i = 0; i < 4; i++) { + stream->Add("("); + if (parallel_moves_[i] != NULL) { + parallel_moves_[i]->PrintDataTo(stream); + } + stream->Add(") "); + } +} + + +const char* LArithmeticD::Mnemonic() const { + switch (op()) { + case Token::ADD: return "add-d"; + case Token::SUB: return "sub-d"; + case Token::MUL: return "mul-d"; + case Token::DIV: return "div-d"; + case Token::MOD: return "mod-d"; + default: + UNREACHABLE(); + return NULL; + } +} + + +const char* LArithmeticT::Mnemonic() const { + switch (op()) { + case Token::ADD: return "add-t"; + case Token::SUB: return "sub-t"; + case Token::MUL: return "mul-t"; + case Token::MOD: return "mod-t"; + case Token::DIV: return "div-t"; + case Token::BIT_AND: return "bit-and-t"; + case Token::BIT_OR: return "bit-or-t"; + case Token::BIT_XOR: return "bit-xor-t"; + case Token::SHL: return "sll-t"; + case Token::SAR: return "sra-t"; + case Token::SHR: return "srl-t"; + default: + UNREACHABLE(); + return NULL; + } +} + + +void LGoto::PrintDataTo(StringStream* stream) { + stream->Add("B%d", block_id()); +} + + +void LBranch::PrintDataTo(StringStream* stream) { + stream->Add("B%d | B%d on ", true_block_id(), false_block_id()); + InputAt(0)->PrintTo(stream); +} + + +void LCmpIDAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if "); + InputAt(0)->PrintTo(stream); + stream->Add(" %s ", Token::String(op())); + InputAt(1)->PrintTo(stream); + stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LIsNilAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if "); + InputAt(0)->PrintTo(stream); + stream->Add(kind() == kStrictEquality ? " === " : " == "); + stream->Add(nil() == kNullValue ? "null" : "undefined"); + stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_object("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LIsStringAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_string("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LIsSmiAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_smi("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_undetectable("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if string_compare("); + InputAt(0)->PrintTo(stream); + InputAt(1)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if has_instance_type("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if has_cached_array_index("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + +void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if class_of_test("); + InputAt(0)->PrintTo(stream); + stream->Add(", \"%o\") then B%d else B%d", + *hydrogen()->class_name(), + true_block_id(), + false_block_id()); +} + + +void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if typeof "); + InputAt(0)->PrintTo(stream); + stream->Add(" == \"%s\" then B%d else B%d", + *hydrogen()->type_literal()->ToCString(), + true_block_id(), false_block_id()); +} + + +void LCallConstantFunction::PrintDataTo(StringStream* stream) { + stream->Add("#%d / ", arity()); +} + + +void LUnaryMathOperation::PrintDataTo(StringStream* stream) { + stream->Add("/%s ", hydrogen()->OpName()); + InputAt(0)->PrintTo(stream); +} + + +void LLoadContextSlot::PrintDataTo(StringStream* stream) { + InputAt(0)->PrintTo(stream); + stream->Add("[%d]", slot_index()); +} + + +void LStoreContextSlot::PrintDataTo(StringStream* stream) { + InputAt(0)->PrintTo(stream); + stream->Add("[%d] <- ", slot_index()); + InputAt(1)->PrintTo(stream); +} + + +void LInvokeFunction::PrintDataTo(StringStream* stream) { + stream->Add("= "); + InputAt(0)->PrintTo(stream); + stream->Add(" #%d / ", arity()); +} + + +void LCallKeyed::PrintDataTo(StringStream* stream) { + stream->Add("[a2] #%d / ", arity()); +} + + +void LCallNamed::PrintDataTo(StringStream* stream) { + SmartArrayPointer<char> name_string = name()->ToCString(); + stream->Add("%s #%d / ", *name_string, arity()); +} + + +void LCallGlobal::PrintDataTo(StringStream* stream) { + SmartArrayPointer<char> name_string = name()->ToCString(); + stream->Add("%s #%d / ", *name_string, arity()); +} + + +void LCallKnownGlobal::PrintDataTo(StringStream* stream) { + stream->Add("#%d / ", arity()); +} + + +void LCallNew::PrintDataTo(StringStream* stream) { + stream->Add("= "); + InputAt(0)->PrintTo(stream); + stream->Add(" #%d / ", arity()); +} + + +void LAccessArgumentsAt::PrintDataTo(StringStream* stream) { + arguments()->PrintTo(stream); + + stream->Add(" length "); + length()->PrintTo(stream); + + stream->Add(" index "); + index()->PrintTo(stream); +} + + +void LStoreNamedField::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add("."); + stream->Add(*String::cast(*name())->ToCString()); + stream->Add(" <- "); + value()->PrintTo(stream); +} + + +void LStoreNamedGeneric::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add("."); + stream->Add(*String::cast(*name())->ToCString()); + stream->Add(" <- "); + value()->PrintTo(stream); +} + + +void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add("["); + key()->PrintTo(stream); + stream->Add("] <- "); + value()->PrintTo(stream); +} + + +void LStoreKeyedFastDoubleElement::PrintDataTo(StringStream* stream) { + elements()->PrintTo(stream); + stream->Add("["); + key()->PrintTo(stream); + stream->Add("] <- "); + value()->PrintTo(stream); +} + + +void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add("["); + key()->PrintTo(stream); + stream->Add("] <- "); + value()->PrintTo(stream); +} + + +void LTransitionElementsKind::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add(" %p -> %p", *original_map(), *transitioned_map()); +} + + +LChunk::LChunk(CompilationInfo* info, HGraph* graph) + : spill_slot_count_(0), + info_(info), + graph_(graph), + instructions_(32), + pointer_maps_(8), + inlined_closures_(1) { +} + + +int LChunk::GetNextSpillIndex(bool is_double) { + // Skip a slot if for a double-width slot. + if (is_double) spill_slot_count_++; + return spill_slot_count_++; +} + + +LOperand* LChunk::GetNextSpillSlot(bool is_double) { + int index = GetNextSpillIndex(is_double); + if (is_double) { + return LDoubleStackSlot::Create(index); + } else { + return LStackSlot::Create(index); + } +} + + +void LChunk::MarkEmptyBlocks() { + HPhase phase("Mark empty blocks", this); + for (int i = 0; i < graph()->blocks()->length(); ++i) { + HBasicBlock* block = graph()->blocks()->at(i); + int first = block->first_instruction_index(); + int last = block->last_instruction_index(); + LInstruction* first_instr = instructions()->at(first); + LInstruction* last_instr = instructions()->at(last); + + LLabel* label = LLabel::cast(first_instr); + if (last_instr->IsGoto()) { + LGoto* goto_instr = LGoto::cast(last_instr); + if (label->IsRedundant() && + !label->is_loop_header()) { + bool can_eliminate = true; + for (int i = first + 1; i < last && can_eliminate; ++i) { + LInstruction* cur = instructions()->at(i); + if (cur->IsGap()) { + LGap* gap = LGap::cast(cur); + if (!gap->IsRedundant()) { + can_eliminate = false; + } + } else { + can_eliminate = false; + } + } + + if (can_eliminate) { + label->set_replacement(GetLabel(goto_instr->block_id())); + } + } + } + } +} + + +void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) { + LInstructionGap* gap = new LInstructionGap(block); + int index = -1; + if (instr->IsControl()) { + instructions_.Add(gap); + index = instructions_.length(); + instructions_.Add(instr); + } else { + index = instructions_.length(); + instructions_.Add(instr); + instructions_.Add(gap); + } + if (instr->HasPointerMap()) { + pointer_maps_.Add(instr->pointer_map()); + instr->pointer_map()->set_lithium_position(index); + } +} + + +LConstantOperand* LChunk::DefineConstantOperand(HConstant* constant) { + return LConstantOperand::Create(constant->id()); +} + + +int LChunk::GetParameterStackSlot(int index) const { + // The receiver is at index 0, the first parameter at index 1, so we + // shift all parameter indexes down by the number of parameters, and + // make sure they end up negative so they are distinguishable from + // spill slots. + int result = index - info()->scope()->num_parameters() - 1; + ASSERT(result < 0); + return result; +} + +// A parameter relative to ebp in the arguments stub. +int LChunk::ParameterAt(int index) { + ASSERT(-1 <= index); // -1 is the receiver. + return (1 + info()->scope()->num_parameters() - index) * + kPointerSize; +} + + +LGap* LChunk::GetGapAt(int index) const { + return LGap::cast(instructions_[index]); +} + + +bool LChunk::IsGapAt(int index) const { + return instructions_[index]->IsGap(); +} + + +int LChunk::NearestGapPos(int index) const { + while (!IsGapAt(index)) index--; + return index; +} + + +void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) { + GetGapAt(index)->GetOrCreateParallelMove(LGap::START)->AddMove(from, to); +} + + +Handle<Object> LChunk::LookupLiteral(LConstantOperand* operand) const { + return HConstant::cast(graph_->LookupValue(operand->index()))->handle(); +} + + +Representation LChunk::LookupLiteralRepresentation( + LConstantOperand* operand) const { + return graph_->LookupValue(operand->index())->representation(); +} + + +LChunk* LChunkBuilder::Build() { + ASSERT(is_unused()); + chunk_ = new LChunk(info(), graph()); + HPhase phase("Building chunk", chunk_); + status_ = BUILDING; + const ZoneList<HBasicBlock*>* blocks = graph()->blocks(); + for (int i = 0; i < blocks->length(); i++) { + HBasicBlock* next = NULL; + if (i < blocks->length() - 1) next = blocks->at(i + 1); + DoBasicBlock(blocks->at(i), next); + if (is_aborted()) return NULL; + } + status_ = DONE; + return chunk_; +} + + +void LChunkBuilder::Abort(const char* format, ...) { + if (FLAG_trace_bailout) { + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); + PrintF("Aborting LChunk building in @\"%s\": ", *name); + va_list arguments; + va_start(arguments, format); + OS::VPrint(format, arguments); + va_end(arguments); + PrintF("\n"); + } + status_ = ABORTED; +} + + +LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { + return new LUnallocated(LUnallocated::FIXED_REGISTER, + Register::ToAllocationIndex(reg)); +} + + +LUnallocated* LChunkBuilder::ToUnallocated(DoubleRegister reg) { + return new LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER, + DoubleRegister::ToAllocationIndex(reg)); +} + + +LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) { + return Use(value, ToUnallocated(fixed_register)); +} + + +LOperand* LChunkBuilder::UseFixedDouble(HValue* value, DoubleRegister reg) { + return Use(value, ToUnallocated(reg)); +} + + +LOperand* LChunkBuilder::UseRegister(HValue* value) { + return Use(value, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); +} + + +LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) { + return Use(value, + new LUnallocated(LUnallocated::MUST_HAVE_REGISTER, + LUnallocated::USED_AT_START)); +} + + +LOperand* LChunkBuilder::UseTempRegister(HValue* value) { + return Use(value, new LUnallocated(LUnallocated::WRITABLE_REGISTER)); +} + + +LOperand* LChunkBuilder::Use(HValue* value) { + return Use(value, new LUnallocated(LUnallocated::NONE)); +} + + +LOperand* LChunkBuilder::UseAtStart(HValue* value) { + return Use(value, new LUnallocated(LUnallocated::NONE, + LUnallocated::USED_AT_START)); +} + + +LOperand* LChunkBuilder::UseOrConstant(HValue* value) { + return value->IsConstant() + ? chunk_->DefineConstantOperand(HConstant::cast(value)) + : Use(value); +} + + +LOperand* LChunkBuilder::UseOrConstantAtStart(HValue* value) { + return value->IsConstant() + ? chunk_->DefineConstantOperand(HConstant::cast(value)) + : UseAtStart(value); +} + + +LOperand* LChunkBuilder::UseRegisterOrConstant(HValue* value) { + return value->IsConstant() + ? chunk_->DefineConstantOperand(HConstant::cast(value)) + : UseRegister(value); +} + + +LOperand* LChunkBuilder::UseRegisterOrConstantAtStart(HValue* value) { + return value->IsConstant() + ? chunk_->DefineConstantOperand(HConstant::cast(value)) + : UseRegisterAtStart(value); +} + + +LOperand* LChunkBuilder::UseAny(HValue* value) { + return value->IsConstant() + ? chunk_->DefineConstantOperand(HConstant::cast(value)) + : Use(value, new LUnallocated(LUnallocated::ANY)); +} + + +LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { + if (value->EmitAtUses()) { + HInstruction* instr = HInstruction::cast(value); + VisitInstruction(instr); + } + operand->set_virtual_register(value->id()); + return operand; +} + + +template<int I, int T> +LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr, + LUnallocated* result) { + result->set_virtual_register(current_instruction_->id()); + instr->set_result(result); + return instr; +} + + +template<int I, int T> +LInstruction* LChunkBuilder::DefineAsRegister( + LTemplateInstruction<1, I, T>* instr) { + return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); +} + + +template<int I, int T> +LInstruction* LChunkBuilder::DefineAsSpilled( + LTemplateInstruction<1, I, T>* instr, int index) { + return Define(instr, new LUnallocated(LUnallocated::FIXED_SLOT, index)); +} + + +template<int I, int T> +LInstruction* LChunkBuilder::DefineSameAsFirst( + LTemplateInstruction<1, I, T>* instr) { + return Define(instr, new LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT)); +} + + +template<int I, int T> +LInstruction* LChunkBuilder::DefineFixed( + LTemplateInstruction<1, I, T>* instr, Register reg) { + return Define(instr, ToUnallocated(reg)); +} + + +template<int I, int T> +LInstruction* LChunkBuilder::DefineFixedDouble( + LTemplateInstruction<1, I, T>* instr, DoubleRegister reg) { + return Define(instr, ToUnallocated(reg)); +} + + +LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { + HEnvironment* hydrogen_env = current_block_->last_environment(); + int argument_index_accumulator = 0; + instr->set_environment(CreateEnvironment(hydrogen_env, + &argument_index_accumulator)); + return instr; +} + + +LInstruction* LChunkBuilder::SetInstructionPendingDeoptimizationEnvironment( + LInstruction* instr, int ast_id) { + ASSERT(instruction_pending_deoptimization_environment_ == NULL); + ASSERT(pending_deoptimization_ast_id_ == AstNode::kNoNumber); + instruction_pending_deoptimization_environment_ = instr; + pending_deoptimization_ast_id_ = ast_id; + return instr; +} + + +void LChunkBuilder::ClearInstructionPendingDeoptimizationEnvironment() { + instruction_pending_deoptimization_environment_ = NULL; + pending_deoptimization_ast_id_ = AstNode::kNoNumber; +} + + +LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, + HInstruction* hinstr, + CanDeoptimize can_deoptimize) { +#ifdef DEBUG + instr->VerifyCall(); +#endif + instr->MarkAsCall(); + instr = AssignPointerMap(instr); + + if (hinstr->HasObservableSideEffects()) { + ASSERT(hinstr->next()->IsSimulate()); + HSimulate* sim = HSimulate::cast(hinstr->next()); + instr = SetInstructionPendingDeoptimizationEnvironment( + instr, sim->ast_id()); + } + + // If instruction does not have side-effects lazy deoptimization + // after the call will try to deoptimize to the point before the call. + // Thus we still need to attach environment to this call even if + // call sequence can not deoptimize eagerly. + bool needs_environment = + (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || + !hinstr->HasObservableSideEffects(); + if (needs_environment && !instr->HasEnvironment()) { + instr = AssignEnvironment(instr); + } + + return instr; +} + + +LInstruction* LChunkBuilder::MarkAsSaveDoubles(LInstruction* instr) { + instr->MarkAsSaveDoubles(); + return instr; +} + + +LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { + ASSERT(!instr->HasPointerMap()); + instr->set_pointer_map(new LPointerMap(position_)); + return instr; +} + + +LUnallocated* LChunkBuilder::TempRegister() { + LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER); + operand->set_virtual_register(allocator_->GetVirtualRegister()); + if (!allocator_->AllocationOk()) Abort("Not enough virtual registers."); + return operand; +} + + +LOperand* LChunkBuilder::FixedTemp(Register reg) { + LUnallocated* operand = ToUnallocated(reg); + ASSERT(operand->HasFixedPolicy()); + return operand; +} + + +LOperand* LChunkBuilder::FixedTemp(DoubleRegister reg) { + LUnallocated* operand = ToUnallocated(reg); + ASSERT(operand->HasFixedPolicy()); + return operand; +} + + +LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { + return new LLabel(instr->block()); +} + + +LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) { + return AssignEnvironment(new LDeoptimize); +} + + +LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { + return AssignEnvironment(new LDeoptimize); +} + + +LInstruction* LChunkBuilder::DoShift(Token::Value op, + HBitwiseBinaryOperation* instr) { + if (instr->representation().IsTagged()) { + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + + LOperand* left = UseFixed(instr->left(), a1); + LOperand* right = UseFixed(instr->right(), a0); + LArithmeticT* result = new LArithmeticT(op, left, right); + return MarkAsCall(DefineFixed(result, v0), instr); + } + + ASSERT(instr->representation().IsInteger32()); + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + LOperand* left = UseRegisterAtStart(instr->left()); + + HValue* right_value = instr->right(); + LOperand* right = NULL; + int constant_value = 0; + if (right_value->IsConstant()) { + HConstant* constant = HConstant::cast(right_value); + right = chunk_->DefineConstantOperand(constant); + constant_value = constant->Integer32Value() & 0x1f; + } else { + right = UseRegisterAtStart(right_value); + } + + // Shift operations can only deoptimize if we do a logical shift + // by 0 and the result cannot be truncated to int32. + bool may_deopt = (op == Token::SHR && constant_value == 0); + bool does_deopt = false; + if (may_deopt) { + for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) { + if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) { + does_deopt = true; + break; + } + } + } + + LInstruction* result = + DefineAsRegister(new LShiftI(op, left, right, does_deopt)); + return does_deopt ? AssignEnvironment(result) : result; +} + + +LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op, + HArithmeticBinaryOperation* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->left()->representation().IsDouble()); + ASSERT(instr->right()->representation().IsDouble()); + ASSERT(op != Token::MOD); + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LArithmeticD* result = new LArithmeticD(op, left, right); + return DefineAsRegister(result); +} + + +LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op, + HArithmeticBinaryOperation* instr) { + ASSERT(op == Token::ADD || + op == Token::DIV || + op == Token::MOD || + op == Token::MUL || + op == Token::SUB); + HValue* left = instr->left(); + HValue* right = instr->right(); + ASSERT(left->representation().IsTagged()); + ASSERT(right->representation().IsTagged()); + LOperand* left_operand = UseFixed(left, a1); + LOperand* right_operand = UseFixed(right, a0); + LArithmeticT* result = new LArithmeticT(op, left_operand, right_operand); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { + ASSERT(is_building()); + current_block_ = block; + next_block_ = next_block; + if (block->IsStartBlock()) { + block->UpdateEnvironment(graph_->start_environment()); + argument_count_ = 0; + } else if (block->predecessors()->length() == 1) { + // We have a single predecessor => copy environment and outgoing + // argument count from the predecessor. + ASSERT(block->phis()->length() == 0); + HBasicBlock* pred = block->predecessors()->at(0); + HEnvironment* last_environment = pred->last_environment(); + ASSERT(last_environment != NULL); + // Only copy the environment, if it is later used again. + if (pred->end()->SecondSuccessor() == NULL) { + ASSERT(pred->end()->FirstSuccessor() == block); + } else { + if (pred->end()->FirstSuccessor()->block_id() > block->block_id() || + pred->end()->SecondSuccessor()->block_id() > block->block_id()) { + last_environment = last_environment->Copy(); + } + } + block->UpdateEnvironment(last_environment); + ASSERT(pred->argument_count() >= 0); + argument_count_ = pred->argument_count(); + } else { + // We are at a state join => process phis. + HBasicBlock* pred = block->predecessors()->at(0); + // No need to copy the environment, it cannot be used later. + HEnvironment* last_environment = pred->last_environment(); + for (int i = 0; i < block->phis()->length(); ++i) { + HPhi* phi = block->phis()->at(i); + last_environment->SetValueAt(phi->merged_index(), phi); + } + for (int i = 0; i < block->deleted_phis()->length(); ++i) { + last_environment->SetValueAt(block->deleted_phis()->at(i), + graph_->GetConstantUndefined()); + } + block->UpdateEnvironment(last_environment); + // Pick up the outgoing argument count of one of the predecessors. + argument_count_ = pred->argument_count(); + } + HInstruction* current = block->first(); + int start = chunk_->instructions()->length(); + while (current != NULL && !is_aborted()) { + // Code for constants in registers is generated lazily. + if (!current->EmitAtUses()) { + VisitInstruction(current); + } + current = current->next(); + } + int end = chunk_->instructions()->length() - 1; + if (end >= start) { + block->set_first_instruction_index(start); + block->set_last_instruction_index(end); + } + block->set_argument_count(argument_count_); + next_block_ = NULL; + current_block_ = NULL; +} + + +void LChunkBuilder::VisitInstruction(HInstruction* current) { + HInstruction* old_current = current_instruction_; + current_instruction_ = current; + if (current->has_position()) position_ = current->position(); + LInstruction* instr = current->CompileToLithium(this); + + if (instr != NULL) { + if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { + instr = AssignPointerMap(instr); + } + if (FLAG_stress_environments && !instr->HasEnvironment()) { + instr = AssignEnvironment(instr); + } + instr->set_hydrogen_value(current); + chunk_->AddInstruction(instr, current_block_); + } + current_instruction_ = old_current; +} + + +LEnvironment* LChunkBuilder::CreateEnvironment( + HEnvironment* hydrogen_env, + int* argument_index_accumulator) { + if (hydrogen_env == NULL) return NULL; + + LEnvironment* outer = + CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + int ast_id = hydrogen_env->ast_id(); + ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); + int value_count = hydrogen_env->length(); + LEnvironment* result = new LEnvironment(hydrogen_env->closure(), + hydrogen_env->is_arguments_adaptor(), + ast_id, + hydrogen_env->parameter_count(), + argument_count_, + value_count, + outer); + int argument_index = *argument_index_accumulator; + for (int i = 0; i < value_count; ++i) { + if (hydrogen_env->is_special_index(i)) continue; + + HValue* value = hydrogen_env->values()->at(i); + LOperand* op = NULL; + if (value->IsArgumentsObject()) { + op = NULL; + } else if (value->IsPushArgument()) { + op = new LArgument(argument_index++); + } else { + op = UseAny(value); + } + result->AddValue(op, value->representation()); + } + + if (!hydrogen_env->is_arguments_adaptor()) { + *argument_index_accumulator = argument_index; + } + + return result; +} + + +LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { + return new LGoto(instr->FirstSuccessor()->block_id()); +} + + +LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { + HValue* value = instr->value(); + if (value->EmitAtUses()) { + HBasicBlock* successor = HConstant::cast(value)->ToBoolean() + ? instr->FirstSuccessor() + : instr->SecondSuccessor(); + return new LGoto(successor->block_id()); + } + + LBranch* result = new LBranch(UseRegister(value)); + // Tagged values that are not known smis or booleans require a + // deoptimization environment. + Representation rep = value->representation(); + HType type = value->type(); + if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean()) { + return AssignEnvironment(result); + } + return result; +} + + +LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* value = UseRegisterAtStart(instr->value()); + LOperand* temp = TempRegister(); + return new LCmpMapAndBranch(value, temp); +} + + +LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) { + return DefineAsRegister(new LArgumentsLength(UseRegister(length->value()))); +} + + +LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) { + return DefineAsRegister(new LArgumentsElements); +} + + +LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { + LInstanceOf* result = + new LInstanceOf(UseFixed(instr->left(), a0), + UseFixed(instr->right(), a1)); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( + HInstanceOfKnownGlobal* instr) { + LInstanceOfKnownGlobal* result = + new LInstanceOfKnownGlobal(UseFixed(instr->left(), a0), FixedTemp(t0)); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { + LOperand* function = UseFixed(instr->function(), a1); + LOperand* receiver = UseFixed(instr->receiver(), a0); + LOperand* length = UseFixed(instr->length(), a2); + LOperand* elements = UseFixed(instr->elements(), a3); + LApplyArguments* result = new LApplyArguments(function, + receiver, + length, + elements); + return MarkAsCall(DefineFixed(result, v0), instr, CAN_DEOPTIMIZE_EAGERLY); +} + + +LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) { + ++argument_count_; + LOperand* argument = Use(instr->argument()); + return new LPushArgument(argument); +} + + +LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) { + return instr->HasNoUses() ? NULL : DefineAsRegister(new LThisFunction); +} + + +LInstruction* LChunkBuilder::DoContext(HContext* instr) { + return instr->HasNoUses() ? NULL : DefineAsRegister(new LContext); +} + + +LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { + LOperand* context = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LOuterContext(context)); +} + + +LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) { + LOperand* context = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LGlobalObject(context)); +} + + +LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) { + LOperand* global_object = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LGlobalReceiver(global_object)); +} + + +LInstruction* LChunkBuilder::DoCallConstantFunction( + HCallConstantFunction* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallConstantFunction, v0), instr); +} + + +LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { + LOperand* function = UseFixed(instr->function(), a1); + argument_count_ -= instr->argument_count(); + LInvokeFunction* result = new LInvokeFunction(function); + return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY); +} + + +LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { + BuiltinFunctionId op = instr->op(); + if (op == kMathLog || op == kMathSin || op == kMathCos) { + LOperand* input = UseFixedDouble(instr->value(), f4); + LUnaryMathOperation* result = new LUnaryMathOperation(input, NULL); + return MarkAsCall(DefineFixedDouble(result, f4), instr); + } else if (op == kMathPowHalf) { + // Input cannot be the same as the result. + // See lithium-codegen-mips.cc::DoMathPowHalf. + LOperand* input = UseFixedDouble(instr->value(), f8); + LOperand* temp = FixedTemp(f6); + LUnaryMathOperation* result = new LUnaryMathOperation(input, temp); + return DefineFixedDouble(result, f4); + } else { + LOperand* input = UseRegisterAtStart(instr->value()); + LOperand* temp = (op == kMathFloor) ? TempRegister() : NULL; + LUnaryMathOperation* result = new LUnaryMathOperation(input, temp); + switch (op) { + case kMathAbs: + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); + case kMathFloor: + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); + case kMathSqrt: + return DefineAsRegister(result); + case kMathRound: + return AssignEnvironment(DefineAsRegister(result)); + default: + UNREACHABLE(); + return NULL; + } + } +} + + +LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) { + ASSERT(instr->key()->representation().IsTagged()); + argument_count_ -= instr->argument_count(); + LOperand* key = UseFixed(instr->key(), a2); + return MarkAsCall(DefineFixed(new LCallKeyed(key), v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallNamed, v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallGlobal, v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallKnownGlobal, v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) { + LOperand* constructor = UseFixed(instr->constructor(), a1); + argument_count_ -= instr->argument_count(); + LCallNew* result = new LCallNew(constructor); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { + LOperand* function = UseFixed(instr->function(), a1); + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallFunction(function), v0), instr); +} + + +LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallRuntime, v0), instr); +} + + +LInstruction* LChunkBuilder::DoShr(HShr* instr) { + return DoShift(Token::SHR, instr); +} + + +LInstruction* LChunkBuilder::DoSar(HSar* instr) { + return DoShift(Token::SAR, instr); +} + + +LInstruction* LChunkBuilder::DoShl(HShl* instr) { + return DoShift(Token::SHL, instr); +} + + +LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + + LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); + LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); + return DefineAsRegister(new LBitI(left, right)); + } else { + ASSERT(instr->representation().IsTagged()); + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + + LOperand* left = UseFixed(instr->left(), a1); + LOperand* right = UseFixed(instr->right(), a0); + LArithmeticT* result = new LArithmeticT(instr->op(), left, right); + return MarkAsCall(DefineFixed(result, v0), instr); + } +} + + +LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) { + ASSERT(instr->value()->representation().IsInteger32()); + ASSERT(instr->representation().IsInteger32()); + return DefineAsRegister(new LBitNotI(UseRegisterAtStart(instr->value()))); +} + + +LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { + if (instr->representation().IsDouble()) { + return DoArithmeticD(Token::DIV, instr); + } else if (instr->representation().IsInteger32()) { + // TODO(1042) The fixed register allocation + // is needed because we call TypeRecordingBinaryOpStub from + // the generated code, which requires registers a0 + // and a1 to be used. We should remove that + // when we provide a native implementation. + LOperand* dividend = UseFixed(instr->left(), a0); + LOperand* divisor = UseFixed(instr->right(), a1); + return AssignEnvironment(AssignPointerMap( + DefineFixed(new LDivI(dividend, divisor), v0))); + } else { + return DoArithmeticT(Token::DIV, instr); + } +} + + +LInstruction* LChunkBuilder::DoMod(HMod* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + + LModI* mod; + if (instr->HasPowerOf2Divisor()) { + ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); + LOperand* value = UseRegisterAtStart(instr->left()); + mod = new LModI(value, UseOrConstant(instr->right())); + } else { + LOperand* dividend = UseRegister(instr->left()); + LOperand* divisor = UseRegister(instr->right()); + mod = new LModI(dividend, + divisor, + TempRegister(), + FixedTemp(f20), + FixedTemp(f22)); + } + + if (instr->CheckFlag(HValue::kBailoutOnMinusZero) || + instr->CheckFlag(HValue::kCanBeDivByZero)) { + return AssignEnvironment(DefineAsRegister(mod)); + } else { + return DefineAsRegister(mod); + } + } else if (instr->representation().IsTagged()) { + return DoArithmeticT(Token::MOD, instr); + } else { + ASSERT(instr->representation().IsDouble()); + // We call a C function for double modulo. It can't trigger a GC. + // We need to use fixed result register for the call. + // TODO(fschneider): Allow any register as input registers. + LOperand* left = UseFixedDouble(instr->left(), f2); + LOperand* right = UseFixedDouble(instr->right(), f4); + LArithmeticD* result = new LArithmeticD(Token::MOD, left, right); + return MarkAsCall(DefineFixedDouble(result, f2), instr); + } +} + + +LInstruction* LChunkBuilder::DoMul(HMul* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + LOperand* left; + LOperand* right = UseOrConstant(instr->MostConstantOperand()); + LOperand* temp = NULL; + if (instr->CheckFlag(HValue::kBailoutOnMinusZero) && + (instr->CheckFlag(HValue::kCanOverflow) || + !right->IsConstantOperand())) { + left = UseRegister(instr->LeastConstantOperand()); + temp = TempRegister(); + } else { + left = UseRegisterAtStart(instr->LeastConstantOperand()); + } + LMulI* mul = new LMulI(left, right, temp); + if (instr->CheckFlag(HValue::kCanOverflow) || + instr->CheckFlag(HValue::kBailoutOnMinusZero)) { + AssignEnvironment(mul); + } + return DefineAsRegister(mul); + + } else if (instr->representation().IsDouble()) { + return DoArithmeticD(Token::MUL, instr); + + } else { + return DoArithmeticT(Token::MUL, instr); + } +} + + +LInstruction* LChunkBuilder::DoSub(HSub* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseOrConstantAtStart(instr->right()); + LSubI* sub = new LSubI(left, right); + LInstruction* result = DefineAsRegister(sub); + if (instr->CheckFlag(HValue::kCanOverflow)) { + result = AssignEnvironment(result); + } + return result; + } else if (instr->representation().IsDouble()) { + return DoArithmeticD(Token::SUB, instr); + } else { + return DoArithmeticT(Token::SUB, instr); + } +} + + +LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); + LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); + LAddI* add = new LAddI(left, right); + LInstruction* result = DefineAsRegister(add); + if (instr->CheckFlag(HValue::kCanOverflow)) { + result = AssignEnvironment(result); + } + return result; + } else if (instr->representation().IsDouble()) { + return DoArithmeticD(Token::ADD, instr); + } else { + ASSERT(instr->representation().IsTagged()); + return DoArithmeticT(Token::ADD, instr); + } +} + + +LInstruction* LChunkBuilder::DoPower(HPower* instr) { + ASSERT(instr->representation().IsDouble()); + // We call a C function for double power. It can't trigger a GC. + // We need to use fixed result register for the call. + Representation exponent_type = instr->right()->representation(); + ASSERT(instr->left()->representation().IsDouble()); + LOperand* left = UseFixedDouble(instr->left(), f2); + LOperand* right = exponent_type.IsDouble() ? + UseFixedDouble(instr->right(), f4) : + UseFixed(instr->right(), a2); + LPower* result = new LPower(left, right); + return MarkAsCall(DefineFixedDouble(result, f0), + instr, + CAN_DEOPTIMIZE_EAGERLY); +} + + +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseFixed(instr->global_object(), a0); + LRandom* result = new LRandom(global_object); + return MarkAsCall(DefineFixedDouble(result, f0), instr); +} + + +LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { + Representation r = instr->GetInputRepresentation(); + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + LOperand* left = UseFixed(instr->left(), a1); + LOperand* right = UseFixed(instr->right(), a0); + LCmpT* result = new LCmpT(left, right); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoCompareIDAndBranch( + HCompareIDAndBranch* instr) { + Representation r = instr->GetInputRepresentation(); + if (r.IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + LOperand* left = UseRegisterOrConstantAtStart(instr->left()); + LOperand* right = UseRegisterOrConstantAtStart(instr->right()); + return new LCmpIDAndBranch(left, right); + } else { + ASSERT(r.IsDouble()); + ASSERT(instr->left()->representation().IsDouble()); + ASSERT(instr->right()->representation().IsDouble()); + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + return new LCmpIDAndBranch(left, right); + } +} + + +LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( + HCompareObjectEqAndBranch* instr) { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + return new LCmpObjectEqAndBranch(left, right); +} + + +LInstruction* LChunkBuilder::DoCompareConstantEqAndBranch( + HCompareConstantEqAndBranch* instr) { + return new LCmpConstantEqAndBranch(UseRegisterAtStart(instr->value())); +} + + +LInstruction* LChunkBuilder::DoIsNilAndBranch(HIsNilAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LIsNilAndBranch(UseRegisterAtStart(instr->value())); +} + + +LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* temp = TempRegister(); + return new LIsObjectAndBranch(UseRegisterAtStart(instr->value()), temp); +} + + +LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* temp = TempRegister(); + return new LIsStringAndBranch(UseRegisterAtStart(instr->value()), temp); +} + + +LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LIsSmiAndBranch(Use(instr->value())); +} + + +LInstruction* LChunkBuilder::DoIsUndetectableAndBranch( + HIsUndetectableAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LIsUndetectableAndBranch(UseRegisterAtStart(instr->value()), + TempRegister()); +} + + +LInstruction* LChunkBuilder::DoStringCompareAndBranch( + HStringCompareAndBranch* instr) { + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + LOperand* left = UseFixed(instr->left(), a1); + LOperand* right = UseFixed(instr->right(), a0); + LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right); + return MarkAsCall(result, instr); +} + + +LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch( + HHasInstanceTypeAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LHasInstanceTypeAndBranch(UseRegisterAtStart(instr->value())); +} + + +LInstruction* LChunkBuilder::DoGetCachedArrayIndex( + HGetCachedArrayIndex* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* value = UseRegisterAtStart(instr->value()); + + return DefineAsRegister(new LGetCachedArrayIndex(value)); +} + + +LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( + HHasCachedArrayIndexAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LHasCachedArrayIndexAndBranch( + UseRegisterAtStart(instr->value())); +} + + +LInstruction* LChunkBuilder::DoClassOfTestAndBranch( + HClassOfTestAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + return new LClassOfTestAndBranch(UseRegister(instr->value()), + TempRegister()); +} + + +LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) { + LOperand* array = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LJSArrayLength(array)); +} + + +LInstruction* LChunkBuilder::DoFixedArrayBaseLength( + HFixedArrayBaseLength* instr) { + LOperand* array = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LFixedArrayBaseLength(array)); +} + + +LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { + LOperand* object = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LElementsKind(object)); +} + + +LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { + LOperand* object = UseRegister(instr->value()); + LValueOf* result = new LValueOf(object, TempRegister()); + return DefineAsRegister(result); +} + + +LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { + return AssignEnvironment(new LBoundsCheck(UseRegisterAtStart(instr->index()), + UseRegister(instr->length()))); +} + + +LInstruction* LChunkBuilder::DoAbnormalExit(HAbnormalExit* instr) { + // The control instruction marking the end of a block that completed + // abruptly (e.g., threw an exception). There is nothing specific to do. + return NULL; +} + + +LInstruction* LChunkBuilder::DoThrow(HThrow* instr) { + LOperand* value = UseFixed(instr->value(), a0); + return MarkAsCall(new LThrow(value), instr); +} + + +LInstruction* LChunkBuilder::DoUseConst(HUseConst* instr) { + return NULL; +} + + +LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) { + // All HForceRepresentation instructions should be eliminated in the + // representation change phase of Hydrogen. + UNREACHABLE(); + return NULL; +} + + +LInstruction* LChunkBuilder::DoChange(HChange* instr) { + Representation from = instr->from(); + Representation to = instr->to(); + if (from.IsTagged()) { + if (to.IsDouble()) { + LOperand* value = UseRegister(instr->value()); + LNumberUntagD* res = new LNumberUntagD(value); + return AssignEnvironment(DefineAsRegister(res)); + } else { + ASSERT(to.IsInteger32()); + LOperand* value = UseRegisterAtStart(instr->value()); + bool needs_check = !instr->value()->type().IsSmi(); + LInstruction* res = NULL; + if (!needs_check) { + res = DefineAsRegister(new LSmiUntag(value, needs_check)); + } else { + LOperand* temp1 = TempRegister(); + LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister() + : NULL; + LOperand* temp3 = instr->CanTruncateToInt32() ? FixedTemp(f22) + : NULL; + res = DefineSameAsFirst(new LTaggedToI(value, temp1, temp2, temp3)); + res = AssignEnvironment(res); + } + return res; + } + } else if (from.IsDouble()) { + if (to.IsTagged()) { + LOperand* value = UseRegister(instr->value()); + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + + // Make sure that the temp and result_temp registers are + // different. + LUnallocated* result_temp = TempRegister(); + LNumberTagD* result = new LNumberTagD(value, temp1, temp2); + Define(result, result_temp); + return AssignPointerMap(result); + } else { + ASSERT(to.IsInteger32()); + LOperand* value = UseRegister(instr->value()); + LDoubleToI* res = + new LDoubleToI(value, + TempRegister(), + instr->CanTruncateToInt32() ? TempRegister() : NULL); + return AssignEnvironment(DefineAsRegister(res)); + } + } else if (from.IsInteger32()) { + if (to.IsTagged()) { + HValue* val = instr->value(); + LOperand* value = UseRegisterAtStart(val); + if (val->HasRange() && val->range()->IsInSmiRange()) { + return DefineAsRegister(new LSmiTag(value)); + } else { + LNumberTagI* result = new LNumberTagI(value); + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); + } + } else { + ASSERT(to.IsDouble()); + LOperand* value = Use(instr->value()); + return DefineAsRegister(new LInteger32ToDouble(value)); + } + } + UNREACHABLE(); + return NULL; +} + + +LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + return AssignEnvironment(new LCheckNonSmi(value)); +} + + +LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + LInstruction* result = new LCheckInstanceType(value); + return AssignEnvironment(result); +} + + +LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) { + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + LInstruction* result = new LCheckPrototypeMaps(temp1, temp2); + return AssignEnvironment(result); +} + + +LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + return AssignEnvironment(new LCheckSmi(value)); +} + + +LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + return AssignEnvironment(new LCheckFunction(value)); +} + + +LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + LInstruction* result = new LCheckMap(value); + return AssignEnvironment(result); +} + + +LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { + HValue* value = instr->value(); + Representation input_rep = value->representation(); + LOperand* reg = UseRegister(value); + if (input_rep.IsDouble()) { + // Revisit this decision, here and 8 lines below. + return DefineAsRegister(new LClampDToUint8(reg, FixedTemp(f22))); + } else if (input_rep.IsInteger32()) { + return DefineAsRegister(new LClampIToUint8(reg)); + } else { + ASSERT(input_rep.IsTagged()); + // Register allocator doesn't (yet) support allocation of double + // temps. Reserve f22 explicitly. + LClampTToUint8* result = new LClampTToUint8(reg, FixedTemp(f22)); + return AssignEnvironment(DefineAsRegister(result)); + } +} + + +LInstruction* LChunkBuilder::DoToInt32(HToInt32* instr) { + HValue* value = instr->value(); + Representation input_rep = value->representation(); + LOperand* reg = UseRegister(value); + if (input_rep.IsDouble()) { + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + LDoubleToI* res = new LDoubleToI(reg, temp1, temp2); + return AssignEnvironment(DefineAsRegister(res)); + } else if (input_rep.IsInteger32()) { + // Canonicalization should already have removed the hydrogen instruction in + // this case, since it is a noop. + UNREACHABLE(); + return NULL; + } else { + ASSERT(input_rep.IsTagged()); + LOperand* temp1 = TempRegister(); + LOperand* temp2 = TempRegister(); + LOperand* temp3 = FixedTemp(f22); + LTaggedToI* res = new LTaggedToI(reg, temp1, temp2, temp3); + return AssignEnvironment(DefineSameAsFirst(res)); + } +} + + +LInstruction* LChunkBuilder::DoReturn(HReturn* instr) { + return new LReturn(UseFixed(instr->value(), v0)); +} + + +LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { + Representation r = instr->representation(); + if (r.IsInteger32()) { + return DefineAsRegister(new LConstantI); + } else if (r.IsDouble()) { + return DefineAsRegister(new LConstantD); + } else if (r.IsTagged()) { + return DefineAsRegister(new LConstantT); + } else { + UNREACHABLE(); + return NULL; + } +} + + +LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { + LLoadGlobalCell* result = new LLoadGlobalCell; + return instr->RequiresHoleCheck() + ? AssignEnvironment(DefineAsRegister(result)) + : DefineAsRegister(result); +} + + +LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) { + LOperand* global_object = UseFixed(instr->global_object(), a0); + LLoadGlobalGeneric* result = new LLoadGlobalGeneric(global_object); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) { + LOperand* value = UseRegister(instr->value()); + // Use a temp to check the value in the cell in the case where we perform + // a hole check. + return instr->RequiresHoleCheck() + ? AssignEnvironment(new LStoreGlobalCell(value, TempRegister())) + : new LStoreGlobalCell(value, NULL); +} + + +LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) { + LOperand* global_object = UseFixed(instr->global_object(), a1); + LOperand* value = UseFixed(instr->value(), a0); + LStoreGlobalGeneric* result = + new LStoreGlobalGeneric(global_object, value); + return MarkAsCall(result, instr); +} + + +LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { + LOperand* context = UseRegisterAtStart(instr->value()); + LInstruction* result = DefineAsRegister(new LLoadContextSlot(context)); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; +} + + +LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { + LOperand* context; + LOperand* value; + if (instr->NeedsWriteBarrier()) { + context = UseTempRegister(instr->context()); + value = UseTempRegister(instr->value()); + } else { + context = UseRegister(instr->context()); + value = UseRegister(instr->value()); + } + LInstruction* result = new LStoreContextSlot(context, value); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; +} + + +LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { + return DefineAsRegister( + new LLoadNamedField(UseRegisterAtStart(instr->object()))); +} + + +LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( + HLoadNamedFieldPolymorphic* instr) { + ASSERT(instr->representation().IsTagged()); + if (instr->need_generic()) { + LOperand* obj = UseFixed(instr->object(), a0); + LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj); + return MarkAsCall(DefineFixed(result, v0), instr); + } else { + LOperand* obj = UseRegisterAtStart(instr->object()); + LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj); + return AssignEnvironment(DefineAsRegister(result)); + } +} + + +LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { + LOperand* object = UseFixed(instr->object(), a0); + LInstruction* result = DefineFixed(new LLoadNamedGeneric(object), v0); + return MarkAsCall(result, instr); +} + + +LInstruction* LChunkBuilder::DoLoadFunctionPrototype( + HLoadFunctionPrototype* instr) { + return AssignEnvironment(DefineAsRegister( + new LLoadFunctionPrototype(UseRegister(instr->function())))); +} + + +LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) { + LOperand* input = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LLoadElements(input)); +} + + +LInstruction* LChunkBuilder::DoLoadExternalArrayPointer( + HLoadExternalArrayPointer* instr) { + LOperand* input = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LLoadExternalArrayPointer(input)); +} + + +LInstruction* LChunkBuilder::DoLoadKeyedFastElement( + HLoadKeyedFastElement* instr) { + ASSERT(instr->representation().IsTagged()); + ASSERT(instr->key()->representation().IsInteger32()); + LOperand* obj = UseRegisterAtStart(instr->object()); + LOperand* key = UseRegisterAtStart(instr->key()); + LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); + if (instr->RequiresHoleCheck()) AssignEnvironment(result); + return DefineAsRegister(result); +} + + +LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( + HLoadKeyedFastDoubleElement* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->key()->representation().IsInteger32()); + LOperand* elements = UseTempRegister(instr->elements()); + LOperand* key = UseRegisterOrConstantAtStart(instr->key()); + LLoadKeyedFastDoubleElement* result = + new LLoadKeyedFastDoubleElement(elements, key); + return AssignEnvironment(DefineAsRegister(result)); +} + + +LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( + HLoadKeyedSpecializedArrayElement* instr) { + ElementsKind elements_kind = instr->elements_kind(); + Representation representation(instr->representation()); + ASSERT( + (representation.IsInteger32() && + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || + (representation.IsDouble() && + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); + ASSERT(instr->key()->representation().IsInteger32()); + LOperand* external_pointer = UseRegister(instr->external_pointer()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LLoadKeyedSpecializedArrayElement* result = + new LLoadKeyedSpecializedArrayElement(external_pointer, key); + LInstruction* load_instr = DefineAsRegister(result); + // An unsigned int array load might overflow and cause a deopt, make sure it + // has an environment. + return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ? + AssignEnvironment(load_instr) : load_instr; +} + + +LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { + LOperand* object = UseFixed(instr->object(), a1); + LOperand* key = UseFixed(instr->key(), a0); + + LInstruction* result = + DefineFixed(new LLoadKeyedGeneric(object, key), v0); + return MarkAsCall(result, instr); +} + + +LInstruction* LChunkBuilder::DoStoreKeyedFastElement( + HStoreKeyedFastElement* instr) { + bool needs_write_barrier = instr->NeedsWriteBarrier(); + ASSERT(instr->value()->representation().IsTagged()); + ASSERT(instr->object()->representation().IsTagged()); + ASSERT(instr->key()->representation().IsInteger32()); + + LOperand* obj = UseTempRegister(instr->object()); + LOperand* val = needs_write_barrier + ? UseTempRegister(instr->value()) + : UseRegisterAtStart(instr->value()); + LOperand* key = needs_write_barrier + ? UseTempRegister(instr->key()) + : UseRegisterOrConstantAtStart(instr->key()); + return new LStoreKeyedFastElement(obj, key, val); +} + + +LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( + HStoreKeyedFastDoubleElement* instr) { + ASSERT(instr->value()->representation().IsDouble()); + ASSERT(instr->elements()->representation().IsTagged()); + ASSERT(instr->key()->representation().IsInteger32()); + + LOperand* elements = UseRegisterAtStart(instr->elements()); + LOperand* val = UseTempRegister(instr->value()); + LOperand* key = UseRegisterOrConstantAtStart(instr->key()); + + return new LStoreKeyedFastDoubleElement(elements, key, val); +} + + +LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( + HStoreKeyedSpecializedArrayElement* instr) { + Representation representation(instr->value()->representation()); + ElementsKind elements_kind = instr->elements_kind(); + ASSERT( + (representation.IsInteger32() && + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || + (representation.IsDouble() && + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); + ASSERT(instr->external_pointer()->representation().IsExternal()); + ASSERT(instr->key()->representation().IsInteger32()); + + LOperand* external_pointer = UseRegister(instr->external_pointer()); + bool val_is_temp_register = + elements_kind == EXTERNAL_PIXEL_ELEMENTS || + elements_kind == EXTERNAL_FLOAT_ELEMENTS; + LOperand* val = val_is_temp_register + ? UseTempRegister(instr->value()) + : UseRegister(instr->value()); + LOperand* key = UseRegisterOrConstant(instr->key()); + + return new LStoreKeyedSpecializedArrayElement(external_pointer, + key, + val); +} + + +LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { + LOperand* obj = UseFixed(instr->object(), a2); + LOperand* key = UseFixed(instr->key(), a1); + LOperand* val = UseFixed(instr->value(), a0); + + ASSERT(instr->object()->representation().IsTagged()); + ASSERT(instr->key()->representation().IsTagged()); + ASSERT(instr->value()->representation().IsTagged()); + + return MarkAsCall(new LStoreKeyedGeneric(obj, key, val), instr); +} + + +LInstruction* LChunkBuilder::DoTransitionElementsKind( + HTransitionElementsKind* instr) { + if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + LOperand* object = UseRegister(instr->object()); + LOperand* new_map_reg = TempRegister(); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, NULL); + return DefineSameAsFirst(result); + } else { + LOperand* object = UseFixed(instr->object(), a0); + LOperand* fixed_object_reg = FixedTemp(a2); + LOperand* new_map_reg = FixedTemp(a3); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, fixed_object_reg); + return MarkAsCall(DefineFixed(result, v0), instr); + } +} + + +LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { + bool needs_write_barrier = instr->NeedsWriteBarrier(); + + LOperand* obj = needs_write_barrier + ? UseTempRegister(instr->object()) + : UseRegisterAtStart(instr->object()); + + LOperand* val = needs_write_barrier + ? UseTempRegister(instr->value()) + : UseRegister(instr->value()); + + return new LStoreNamedField(obj, val); +} + + +LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { + LOperand* obj = UseFixed(instr->object(), a1); + LOperand* val = UseFixed(instr->value(), a0); + + LInstruction* result = new LStoreNamedGeneric(obj, val); + return MarkAsCall(result, instr); +} + + +LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + return MarkAsCall(DefineFixed(new LStringAdd(left, right), v0), instr); +} + + +LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { + LOperand* string = UseTempRegister(instr->string()); + LOperand* index = UseTempRegister(instr->index()); + LStringCharCodeAt* result = new LStringCharCodeAt(string, index); + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); +} + + +LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) { + LOperand* char_code = UseRegister(instr->value()); + LStringCharFromCode* result = new LStringCharFromCode(char_code); + return AssignPointerMap(DefineAsRegister(result)); +} + + +LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { + LOperand* string = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LStringLength(string)); +} + + +LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { + return MarkAsCall(DefineFixed(new LArrayLiteral, v0), instr); +} + + +LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralFast, v0), instr); +} + + +LInstruction* LChunkBuilder::DoObjectLiteralGeneric( + HObjectLiteralGeneric* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, v0), instr); +} + + +LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) { + return MarkAsCall(DefineFixed(new LRegExpLiteral, v0), instr); +} + + +LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) { + return MarkAsCall(DefineFixed(new LFunctionLiteral, v0), instr); +} + + +LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { + LOperand* object = UseFixed(instr->object(), a0); + LOperand* key = UseFixed(instr->key(), a1); + LDeleteProperty* result = new LDeleteProperty(object, key); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) { + allocator_->MarkAsOsrEntry(); + current_block_->last_environment()->set_ast_id(instr->ast_id()); + return AssignEnvironment(new LOsrEntry); +} + + +LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { + int spill_index = chunk()->GetParameterStackSlot(instr->index()); + return DefineAsSpilled(new LParameter, spill_index); +} + + +LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { + int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width. + if (spill_index > LUnallocated::kMaxFixedIndex) { + Abort("Too many spill slots needed for OSR"); + spill_index = 0; + } + return DefineAsSpilled(new LUnknownOSRValue, spill_index); +} + + +LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) { + argument_count_ -= instr->argument_count(); + return MarkAsCall(DefineFixed(new LCallStub, v0), instr); +} + + +LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { + // There are no real uses of the arguments object. + // arguments.length and element access are supported directly on + // stack arguments, and any real arguments object use causes a bailout. + // So this value is never used. + return NULL; +} + + +LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { + LOperand* arguments = UseRegister(instr->arguments()); + LOperand* length = UseTempRegister(instr->length()); + LOperand* index = UseRegister(instr->index()); + LAccessArgumentsAt* result = new LAccessArgumentsAt(arguments, length, index); + return AssignEnvironment(DefineAsRegister(result)); +} + + +LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { + LOperand* object = UseFixed(instr->value(), a0); + LToFastProperties* result = new LToFastProperties(object); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { + LTypeof* result = new LTypeof(UseFixed(instr->value(), a0)); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) { + return new LTypeofIsAndBranch(UseTempRegister(instr->value())); +} + + +LInstruction* LChunkBuilder::DoIsConstructCallAndBranch( + HIsConstructCallAndBranch* instr) { + return new LIsConstructCallAndBranch(TempRegister()); +} + + +LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { + HEnvironment* env = current_block_->last_environment(); + ASSERT(env != NULL); + + env->set_ast_id(instr->ast_id()); + + env->Drop(instr->pop_count()); + for (int i = 0; i < instr->values()->length(); ++i) { + HValue* value = instr->values()->at(i); + if (instr->HasAssignedIndexAt(i)) { + env->Bind(instr->GetAssignedIndexAt(i), value); + } else { + env->Push(value); + } + } + + // If there is an instruction pending deoptimization environment create a + // lazy bailout instruction to capture the environment. + if (pending_deoptimization_ast_id_ == instr->ast_id()) { + LInstruction* result = new LLazyBailout; + result = AssignEnvironment(result); + instruction_pending_deoptimization_environment_-> + set_deoptimization_environment(result->environment()); + ClearInstructionPendingDeoptimizationEnvironment(); + return result; + } + + return NULL; +} + + +LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) { + if (instr->is_function_entry()) { + return MarkAsCall(new LStackCheck, instr); + } else { + ASSERT(instr->is_backwards_branch()); + return AssignEnvironment(AssignPointerMap(new LStackCheck)); + } +} + + +LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { + HEnvironment* outer = current_block_->last_environment(); + HConstant* undefined = graph()->GetConstantUndefined(); + HEnvironment* inner = outer->CopyForInlining(instr->closure(), + instr->arguments_count(), + instr->function(), + undefined, + instr->call_kind()); + current_block_->UpdateEnvironment(inner); + chunk_->AddInlinedClosure(instr->closure()); + return NULL; +} + + +LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { + HEnvironment* outer = current_block_->last_environment()-> + DiscardInlined(false); + current_block_->UpdateEnvironment(outer); + return NULL; +} + + +LInstruction* LChunkBuilder::DoIn(HIn* instr) { + LOperand* key = UseRegisterAtStart(instr->key()); + LOperand* object = UseRegisterAtStart(instr->object()); + LIn* result = new LIn(key, object); + return MarkAsCall(DefineFixed(result, v0), instr); +} + + +} } // namespace v8::internal diff --git a/deps/v8/src/mips/lithium-mips.h b/deps/v8/src/mips/lithium-mips.h index ebc1e43bf6..0a21649fa8 100644 --- a/deps/v8/src/mips/lithium-mips.h +++ b/deps/v8/src/mips/lithium-mips.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -32,275 +32,2246 @@ #include "lithium-allocator.h" #include "lithium.h" #include "safepoint-table.h" - -// Note: this file was taken from the X64 version. ARM has a partially working -// lithium implementation, but for now it is not ported to mips. +#include "utils.h" namespace v8 { namespace internal { // Forward declarations. class LCodeGen; -class LEnvironment; -class Translation; + +#define LITHIUM_ALL_INSTRUCTION_LIST(V) \ + V(ControlInstruction) \ + V(Call) \ + LITHIUM_CONCRETE_INSTRUCTION_LIST(V) + + +#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ + V(AccessArgumentsAt) \ + V(AddI) \ + V(ApplyArguments) \ + V(ArgumentsElements) \ + V(ArgumentsLength) \ + V(ArithmeticD) \ + V(ArithmeticT) \ + V(ArrayLiteral) \ + V(BitI) \ + V(BitNotI) \ + V(BoundsCheck) \ + V(Branch) \ + V(CallConstantFunction) \ + V(CallFunction) \ + V(CallGlobal) \ + V(CallKeyed) \ + V(CallKnownGlobal) \ + V(CallNamed) \ + V(CallNew) \ + V(CallRuntime) \ + V(CallStub) \ + V(CheckFunction) \ + V(CheckInstanceType) \ + V(CheckMap) \ + V(CheckNonSmi) \ + V(CheckPrototypeMaps) \ + V(CheckSmi) \ + V(ClampDToUint8) \ + V(ClampIToUint8) \ + V(ClampTToUint8) \ + V(ClassOfTestAndBranch) \ + V(CmpConstantEqAndBranch) \ + V(CmpIDAndBranch) \ + V(CmpObjectEqAndBranch) \ + V(CmpMapAndBranch) \ + V(CmpT) \ + V(ConstantD) \ + V(ConstantI) \ + V(ConstantT) \ + V(Context) \ + V(DeleteProperty) \ + V(Deoptimize) \ + V(DivI) \ + V(DoubleToI) \ + V(ElementsKind) \ + V(FixedArrayBaseLength) \ + V(FunctionLiteral) \ + V(GetCachedArrayIndex) \ + V(GlobalObject) \ + V(GlobalReceiver) \ + V(Goto) \ + V(HasCachedArrayIndexAndBranch) \ + V(HasInstanceTypeAndBranch) \ + V(In) \ + V(InstanceOf) \ + V(InstanceOfKnownGlobal) \ + V(InstructionGap) \ + V(Integer32ToDouble) \ + V(InvokeFunction) \ + V(IsConstructCallAndBranch) \ + V(IsNilAndBranch) \ + V(IsObjectAndBranch) \ + V(IsStringAndBranch) \ + V(IsSmiAndBranch) \ + V(IsUndetectableAndBranch) \ + V(StringCompareAndBranch) \ + V(JSArrayLength) \ + V(Label) \ + V(LazyBailout) \ + V(LoadContextSlot) \ + V(LoadElements) \ + V(LoadExternalArrayPointer) \ + V(LoadFunctionPrototype) \ + V(LoadGlobalCell) \ + V(LoadGlobalGeneric) \ + V(LoadKeyedFastDoubleElement) \ + V(LoadKeyedFastElement) \ + V(LoadKeyedGeneric) \ + V(LoadKeyedSpecializedArrayElement) \ + V(LoadNamedField) \ + V(LoadNamedFieldPolymorphic) \ + V(LoadNamedGeneric) \ + V(ModI) \ + V(MulI) \ + V(NumberTagD) \ + V(NumberTagI) \ + V(NumberUntagD) \ + V(ObjectLiteralFast) \ + V(ObjectLiteralGeneric) \ + V(OsrEntry) \ + V(OuterContext) \ + V(Parameter) \ + V(Power) \ + V(PushArgument) \ + V(Random) \ + V(RegExpLiteral) \ + V(Return) \ + V(ShiftI) \ + V(SmiTag) \ + V(SmiUntag) \ + V(StackCheck) \ + V(StoreContextSlot) \ + V(StoreGlobalCell) \ + V(StoreGlobalGeneric) \ + V(StoreKeyedFastDoubleElement) \ + V(StoreKeyedFastElement) \ + V(StoreKeyedGeneric) \ + V(StoreKeyedSpecializedArrayElement) \ + V(StoreNamedField) \ + V(StoreNamedGeneric) \ + V(StringAdd) \ + V(StringCharCodeAt) \ + V(StringCharFromCode) \ + V(StringLength) \ + V(SubI) \ + V(TaggedToI) \ + V(ThisFunction) \ + V(Throw) \ + V(ToFastProperties) \ + V(TransitionElementsKind) \ + V(Typeof) \ + V(TypeofIsAndBranch) \ + V(UnaryMathOperation) \ + V(UnknownOSRValue) \ + V(ValueOf) + + +#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ + virtual Opcode opcode() const { return LInstruction::k##type; } \ + virtual void CompileToNative(LCodeGen* generator); \ + virtual const char* Mnemonic() const { return mnemonic; } \ + static L##type* cast(LInstruction* instr) { \ + ASSERT(instr->Is##type()); \ + return reinterpret_cast<L##type*>(instr); \ + } + + +#define DECLARE_HYDROGEN_ACCESSOR(type) \ + H##type* hydrogen() const { \ + return H##type::cast(hydrogen_value()); \ + } + class LInstruction: public ZoneObject { public: - LInstruction() { } + LInstruction() + : environment_(NULL), + hydrogen_value_(NULL), + is_call_(false), + is_save_doubles_(false) { } virtual ~LInstruction() { } - // Predicates should be generated by macro as in lithium-ia32.h. - virtual bool IsLabel() const { - UNIMPLEMENTED(); - return false; + virtual void CompileToNative(LCodeGen* generator) = 0; + virtual const char* Mnemonic() const = 0; + virtual void PrintTo(StringStream* stream); + virtual void PrintDataTo(StringStream* stream) = 0; + virtual void PrintOutputOperandTo(StringStream* stream) = 0; + + enum Opcode { + // Declare a unique enum value for each instruction. +#define DECLARE_OPCODE(type) k##type, + LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE) + kNumberOfInstructions +#undef DECLARE_OPCODE + }; + + virtual Opcode opcode() const = 0; + + // Declare non-virtual type testers for all leaf IR classes. +#define DECLARE_PREDICATE(type) \ + bool Is##type() const { return opcode() == k##type; } + LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE) +#undef DECLARE_PREDICATE + + // Declare virtual predicates for instructions that don't have + // an opcode. + virtual bool IsGap() const { return false; } + + virtual bool IsControl() const { return false; } + + void set_environment(LEnvironment* env) { environment_ = env; } + LEnvironment* environment() const { return environment_; } + bool HasEnvironment() const { return environment_ != NULL; } + + void set_pointer_map(LPointerMap* p) { pointer_map_.set(p); } + LPointerMap* pointer_map() const { return pointer_map_.get(); } + bool HasPointerMap() const { return pointer_map_.is_set(); } + + void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } + HValue* hydrogen_value() const { return hydrogen_value_; } + + void set_deoptimization_environment(LEnvironment* env) { + deoptimization_environment_.set(env); } - virtual bool IsOsrEntry() const { - UNIMPLEMENTED(); - return false; + LEnvironment* deoptimization_environment() const { + return deoptimization_environment_.get(); } + bool HasDeoptimizationEnvironment() const { + return deoptimization_environment_.is_set(); + } + + void MarkAsCall() { is_call_ = true; } + void MarkAsSaveDoubles() { is_save_doubles_ = true; } + + // Interface to the register allocator and iterators. + bool IsMarkedAsCall() const { return is_call_; } + bool IsMarkedAsSaveDoubles() const { return is_save_doubles_; } + + virtual bool HasResult() const = 0; + virtual LOperand* result() = 0; + + virtual int InputCount() = 0; + virtual LOperand* InputAt(int i) = 0; + virtual int TempCount() = 0; + virtual LOperand* TempAt(int i) = 0; + + LOperand* FirstInput() { return InputAt(0); } + LOperand* Output() { return HasResult() ? result() : NULL; } - LPointerMap* pointer_map() const { - UNIMPLEMENTED(); - return NULL; +#ifdef DEBUG + void VerifyCall(); +#endif + + private: + LEnvironment* environment_; + SetOncePointer<LPointerMap> pointer_map_; + HValue* hydrogen_value_; + SetOncePointer<LEnvironment> deoptimization_environment_; + bool is_call_; + bool is_save_doubles_; +}; + + +// R = number of result operands (0 or 1). +// I = number of input operands. +// T = number of temporary operands. +template<int R, int I, int T> +class LTemplateInstruction: public LInstruction { + public: + // Allow 0 or 1 output operands. + STATIC_ASSERT(R == 0 || R == 1); + virtual bool HasResult() const { return R != 0; } + void set_result(LOperand* operand) { results_[0] = operand; } + LOperand* result() { return results_[0]; } + + int InputCount() { return I; } + LOperand* InputAt(int i) { return inputs_[i]; } + + int TempCount() { return T; } + LOperand* TempAt(int i) { return temps_[i]; } + + virtual void PrintDataTo(StringStream* stream); + virtual void PrintOutputOperandTo(StringStream* stream); + + protected: + EmbeddedContainer<LOperand*, R> results_; + EmbeddedContainer<LOperand*, I> inputs_; + EmbeddedContainer<LOperand*, T> temps_; +}; + + +class LGap: public LTemplateInstruction<0, 0, 0> { + public: + explicit LGap(HBasicBlock* block) + : block_(block) { + parallel_moves_[BEFORE] = NULL; + parallel_moves_[START] = NULL; + parallel_moves_[END] = NULL; + parallel_moves_[AFTER] = NULL; } - bool HasPointerMap() const { - UNIMPLEMENTED(); - return false; + // Can't use the DECLARE-macro here because of sub-classes. + virtual bool IsGap() const { return true; } + virtual void PrintDataTo(StringStream* stream); + static LGap* cast(LInstruction* instr) { + ASSERT(instr->IsGap()); + return reinterpret_cast<LGap*>(instr); } - void set_environment(LEnvironment* env) { UNIMPLEMENTED(); } + bool IsRedundant() const; + + HBasicBlock* block() const { return block_; } - LEnvironment* environment() const { - UNIMPLEMENTED(); - return NULL; + enum InnerPosition { + BEFORE, + START, + END, + AFTER, + FIRST_INNER_POSITION = BEFORE, + LAST_INNER_POSITION = AFTER + }; + + LParallelMove* GetOrCreateParallelMove(InnerPosition pos) { + if (parallel_moves_[pos] == NULL) parallel_moves_[pos] = new LParallelMove; + return parallel_moves_[pos]; } - bool HasEnvironment() const { - UNIMPLEMENTED(); - return false; + LParallelMove* GetParallelMove(InnerPosition pos) { + return parallel_moves_[pos]; } - virtual void PrintTo(StringStream* stream) const { UNIMPLEMENTED(); } + private: + LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1]; + HBasicBlock* block_; +}; - virtual bool IsControl() const { - UNIMPLEMENTED(); - return false; + +class LInstructionGap: public LGap { + public: + explicit LInstructionGap(HBasicBlock* block) : LGap(block) { } + + DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap") +}; + + +class LGoto: public LTemplateInstruction<0, 0, 0> { + public: + explicit LGoto(int block_id) : block_id_(block_id) { } + + DECLARE_CONCRETE_INSTRUCTION(Goto, "goto") + virtual void PrintDataTo(StringStream* stream); + virtual bool IsControl() const { return true; } + + int block_id() const { return block_id_; } + + private: + int block_id_; +}; + + +class LLazyBailout: public LTemplateInstruction<0, 0, 0> { + public: + LLazyBailout() : gap_instructions_size_(0) { } + + DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout") + + void set_gap_instructions_size(int gap_instructions_size) { + gap_instructions_size_ = gap_instructions_size; } + int gap_instructions_size() { return gap_instructions_size_; } - void MarkAsCall() { UNIMPLEMENTED(); } - void MarkAsSaveDoubles() { UNIMPLEMENTED(); } + private: + int gap_instructions_size_; +}; - // Interface to the register allocator and iterators. - bool IsMarkedAsCall() const { - UNIMPLEMENTED(); - return false; + +class LDeoptimize: public LTemplateInstruction<0, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize") +}; + + +class LLabel: public LGap { + public: + explicit LLabel(HBasicBlock* block) + : LGap(block), replacement_(NULL) { } + + DECLARE_CONCRETE_INSTRUCTION(Label, "label") + + virtual void PrintDataTo(StringStream* stream); + + int block_id() const { return block()->block_id(); } + bool is_loop_header() const { return block()->IsLoopHeader(); } + Label* label() { return &label_; } + LLabel* replacement() const { return replacement_; } + void set_replacement(LLabel* label) { replacement_ = label; } + bool HasReplacement() const { return replacement_ != NULL; } + + private: + Label label_; + LLabel* replacement_; +}; + + +class LParameter: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter") +}; + + +class LCallStub: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub") + DECLARE_HYDROGEN_ACCESSOR(CallStub) + + TranscendentalCache::Type transcendental_type() { + return hydrogen()->transcendental_type(); } +}; + - bool IsMarkedAsSaveDoubles() const { - UNIMPLEMENTED(); - return false; +class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value") +}; + + +template<int I, int T> +class LControlInstruction: public LTemplateInstruction<0, I, T> { + public: + virtual bool IsControl() const { return true; } + + int SuccessorCount() { return hydrogen()->SuccessorCount(); } + HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); } + int true_block_id() { return hydrogen()->SuccessorAt(0)->block_id(); } + int false_block_id() { return hydrogen()->SuccessorAt(1)->block_id(); } + + private: + HControlInstruction* hydrogen() { + return HControlInstruction::cast(this->hydrogen_value()); } +}; - virtual bool HasResult() const { - UNIMPLEMENTED(); - return false; + +class LApplyArguments: public LTemplateInstruction<1, 4, 0> { + public: + LApplyArguments(LOperand* function, + LOperand* receiver, + LOperand* length, + LOperand* elements) { + inputs_[0] = function; + inputs_[1] = receiver; + inputs_[2] = length; + inputs_[3] = elements; } - virtual LOperand* result() { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") + + LOperand* function() { return inputs_[0]; } + LOperand* receiver() { return inputs_[1]; } + LOperand* length() { return inputs_[2]; } + LOperand* elements() { return inputs_[3]; } +}; + + +class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> { + public: + LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) { + inputs_[0] = arguments; + inputs_[1] = length; + inputs_[2] = index; } - virtual int InputCount() { - UNIMPLEMENTED(); - return 0; + DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at") + + LOperand* arguments() { return inputs_[0]; } + LOperand* length() { return inputs_[1]; } + LOperand* index() { return inputs_[2]; } + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LArgumentsLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LArgumentsLength(LOperand* elements) { + inputs_[0] = elements; } - virtual LOperand* InputAt(int i) { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length") +}; + + +class LArgumentsElements: public LTemplateInstruction<1, 0, 0> { + public: + LArgumentsElements() { } + + DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements") +}; + + +class LModI: public LTemplateInstruction<1, 2, 3> { + public: + // Used when the right hand is a constant power of 2. + LModI(LOperand* left, + LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + temps_[0] = NULL; + temps_[1] = NULL; + temps_[2] = NULL; } - virtual int TempCount() { - UNIMPLEMENTED(); - return 0; + // Used for the standard case. + LModI(LOperand* left, + LOperand* right, + LOperand* temp1, + LOperand* temp2, + LOperand* temp3) { + inputs_[0] = left; + inputs_[1] = right; + temps_[0] = temp1; + temps_[1] = temp2; + temps_[2] = temp3; } - virtual LOperand* TempAt(int i) { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i") + DECLARE_HYDROGEN_ACCESSOR(Mod) +}; + + +class LDivI: public LTemplateInstruction<1, 2, 0> { + public: + LDivI(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - LOperand* FirstInput() { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i") + DECLARE_HYDROGEN_ACCESSOR(Div) +}; + + +class LMulI: public LTemplateInstruction<1, 2, 1> { + public: + LMulI(LOperand* left, LOperand* right, LOperand* temp) { + inputs_[0] = left; + inputs_[1] = right; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i") + DECLARE_HYDROGEN_ACCESSOR(Mul) +}; + + +class LCmpIDAndBranch: public LControlInstruction<2, 0> { + public: + LCmpIDAndBranch(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - LOperand* Output() { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(CmpIDAndBranch, "cmp-id-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareIDAndBranch) + + Token::Value op() const { return hydrogen()->token(); } + bool is_double() const { + return hydrogen()->GetInputRepresentation().IsDouble(); } -#ifdef DEBUG - void VerifyCall() { UNIMPLEMENTED(); } -#endif + virtual void PrintDataTo(StringStream* stream); }; -class LGap: public LInstruction { +class LUnaryMathOperation: public LTemplateInstruction<1, 1, 1> { public: - explicit LGap(HBasicBlock* block) { } + LUnaryMathOperation(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } - HBasicBlock* block() const { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation") + DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) + + virtual void PrintDataTo(StringStream* stream); + BuiltinFunctionId op() const { return hydrogen()->op(); } +}; + + +class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { + public: + LCmpObjectEqAndBranch(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - enum InnerPosition { - BEFORE, - START, - END, - AFTER, - FIRST_INNER_POSITION = BEFORE, - LAST_INNER_POSITION = AFTER - }; + DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, + "cmp-object-eq-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch) +}; - LParallelMove* GetOrCreateParallelMove(InnerPosition pos) { - UNIMPLEMENTED(); - return NULL; + +class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> { + public: + explicit LCmpConstantEqAndBranch(LOperand* left) { + inputs_[0] = left; } - LParallelMove* GetParallelMove(InnerPosition pos) { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch, + "cmp-constant-eq-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareConstantEqAndBranch) +}; + + +class LIsNilAndBranch: public LControlInstruction<1, 0> { + public: + explicit LIsNilAndBranch(LOperand* value) { + inputs_[0] = value; } + + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch) + + EqualityKind kind() const { return hydrogen()->kind(); } + NilValue nil() const { return hydrogen()->nil(); } + + virtual void PrintDataTo(StringStream* stream); }; -class LLabel: public LGap { +class LIsObjectAndBranch: public LControlInstruction<1, 1> { public: - explicit LLabel(HBasicBlock* block) : LGap(block) { } + LIsObjectAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch) + + virtual void PrintDataTo(StringStream* stream); }; -class LOsrEntry: public LInstruction { +class LIsStringAndBranch: public LControlInstruction<1, 1> { public: - // Function could be generated by a macro as in lithium-ia32.h. - static LOsrEntry* cast(LInstruction* instr) { - UNIMPLEMENTED(); - return NULL; + LIsStringAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; } - LOperand** SpilledRegisterArray() { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LIsSmiAndBranch: public LControlInstruction<1, 0> { + public: + explicit LIsSmiAndBranch(LOperand* value) { + inputs_[0] = value; } - LOperand** SpilledDoubleRegisterArray() { - UNIMPLEMENTED(); - return NULL; + + DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsSmiAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LIsUndetectableAndBranch: public LControlInstruction<1, 1> { + public: + explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; } - void MarkSpilledRegister(int allocation_index, LOperand* spill_operand) { - UNIMPLEMENTED(); + DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch, + "is-undetectable-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LStringCompareAndBranch: public LControlInstruction<2, 0> { + public: + LStringCompareAndBranch(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - void MarkSpilledDoubleRegister(int allocation_index, - LOperand* spill_operand) { - UNIMPLEMENTED(); + + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, + "string-compare-and-branch") + DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) + + Token::Value op() const { return hydrogen()->token(); } + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> { + public: + explicit LHasInstanceTypeAndBranch(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch, + "has-instance-type-and-branch") + DECLARE_HYDROGEN_ACCESSOR(HasInstanceTypeAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LGetCachedArrayIndex: public LTemplateInstruction<1, 1, 0> { + public: + explicit LGetCachedArrayIndex(LOperand* value) { + inputs_[0] = value; } + + DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index") + DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex) }; -class LChunk: public ZoneObject { +class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> { + public: + explicit LHasCachedArrayIndexAndBranch(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch, + "has-cached-array-index-and-branch") + DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndexAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LClassOfTestAndBranch: public LControlInstruction<1, 1> { + public: + LClassOfTestAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch, + "class-of-test-and-branch") + DECLARE_HYDROGEN_ACCESSOR(ClassOfTestAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LCmpT: public LTemplateInstruction<1, 2, 0> { public: - explicit LChunk(HGraph* graph) { } + LCmpT(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } - HGraph* graph() const { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t") + DECLARE_HYDROGEN_ACCESSOR(CompareGeneric) + + Token::Value op() const { return hydrogen()->token(); } +}; + + +class LInstanceOf: public LTemplateInstruction<1, 2, 0> { + public: + LInstanceOf(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - const ZoneList<LPointerMap*>* pointer_maps() const { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of") +}; + + +class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 1, 1> { + public: + LInstanceOfKnownGlobal(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; } - LOperand* GetNextSpillSlot(bool double_slot) { - UNIMPLEMENTED(); - return NULL; + DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal, + "instance-of-known-global") + DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal) + + Handle<JSFunction> function() const { return hydrogen()->function(); } +}; + + +class LBoundsCheck: public LTemplateInstruction<0, 2, 0> { + public: + LBoundsCheck(LOperand* index, LOperand* length) { + inputs_[0] = index; + inputs_[1] = length; } - LConstantOperand* DefineConstantOperand(HConstant* constant) { - UNIMPLEMENTED(); - return NULL; + LOperand* index() { return inputs_[0]; } + LOperand* length() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check") +}; + + +class LBitI: public LTemplateInstruction<1, 2, 0> { + public: + LBitI(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - LLabel* GetLabel(int block_id) const { - UNIMPLEMENTED(); - return NULL; + Token::Value op() const { return hydrogen()->op(); } + + DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") + DECLARE_HYDROGEN_ACCESSOR(Bitwise) +}; + + +class LShiftI: public LTemplateInstruction<1, 2, 0> { + public: + LShiftI(Token::Value op, LOperand* left, LOperand* right, bool can_deopt) + : op_(op), can_deopt_(can_deopt) { + inputs_[0] = left; + inputs_[1] = right; } - const ZoneList<LInstruction*>* instructions() const { - UNIMPLEMENTED(); - return NULL; + Token::Value op() const { return op_; } + + bool can_deopt() const { return can_deopt_; } + + DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i") + + private: + Token::Value op_; + bool can_deopt_; +}; + + +class LSubI: public LTemplateInstruction<1, 2, 0> { + public: + LSubI(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; } - int GetParameterStackSlot(int index) const { - UNIMPLEMENTED(); - return 0; + DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i") + DECLARE_HYDROGEN_ACCESSOR(Sub) +}; + + +class LConstantI: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ConstantI, "constant-i") + DECLARE_HYDROGEN_ACCESSOR(Constant) + + int32_t value() const { return hydrogen()->Integer32Value(); } +}; + + +class LConstantD: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d") + DECLARE_HYDROGEN_ACCESSOR(Constant) + + double value() const { return hydrogen()->DoubleValue(); } +}; + + +class LConstantT: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ConstantT, "constant-t") + DECLARE_HYDROGEN_ACCESSOR(Constant) + + Handle<Object> value() const { return hydrogen()->handle(); } +}; + + +class LBranch: public LControlInstruction<1, 0> { + public: + explicit LBranch(LOperand* value) { + inputs_[0] = value; } - void AddGapMove(int index, LOperand* from, LOperand* to) { UNIMPLEMENTED(); } + DECLARE_CONCRETE_INSTRUCTION(Branch, "branch") + DECLARE_HYDROGEN_ACCESSOR(Branch) + + virtual void PrintDataTo(StringStream* stream); +}; - LGap* GetGapAt(int index) const { - UNIMPLEMENTED(); - return NULL; + +class LCmpMapAndBranch: public LTemplateInstruction<0, 1, 1> { + public: + LCmpMapAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; } - bool IsGapAt(int index) const { - UNIMPLEMENTED(); - return false; + DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareMap) + + virtual bool IsControl() const { return true; } + + Handle<Map> map() const { return hydrogen()->map(); } + int true_block_id() const { + return hydrogen()->FirstSuccessor()->block_id(); } + int false_block_id() const { + return hydrogen()->SecondSuccessor()->block_id(); + } +}; + - int NearestGapPos(int index) const { - UNIMPLEMENTED(); - return 0; +class LJSArrayLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LJSArrayLength(LOperand* value) { + inputs_[0] = value; } - void MarkEmptyBlocks() { UNIMPLEMENTED(); } + DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length") + DECLARE_HYDROGEN_ACCESSOR(JSArrayLength) +}; + - CompilationInfo* info() const { - UNIMPLEMENTED(); - return NULL; +class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LFixedArrayBaseLength(LOperand* value) { + inputs_[0] = value; } -#ifdef DEBUG - void Verify() { UNIMPLEMENTED(); } -#endif + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength, + "fixed-array-base-length") + DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength) +}; + + +class LElementsKind: public LTemplateInstruction<1, 1, 0> { + public: + explicit LElementsKind(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind") + DECLARE_HYDROGEN_ACCESSOR(ElementsKind) +}; + + +class LValueOf: public LTemplateInstruction<1, 1, 1> { + public: + LValueOf(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of") + DECLARE_HYDROGEN_ACCESSOR(ValueOf) +}; + + +class LThrow: public LTemplateInstruction<0, 1, 0> { + public: + explicit LThrow(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(Throw, "throw") +}; + + +class LBitNotI: public LTemplateInstruction<1, 1, 0> { + public: + explicit LBitNotI(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(BitNotI, "bit-not-i") +}; + + +class LAddI: public LTemplateInstruction<1, 2, 0> { + public: + LAddI(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i") + DECLARE_HYDROGEN_ACCESSOR(Add) +}; + + +class LPower: public LTemplateInstruction<1, 2, 0> { + public: + LPower(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(Power, "power") + DECLARE_HYDROGEN_ACCESSOR(Power) +}; + + +class LRandom: public LTemplateInstruction<1, 1, 0> { + public: + explicit LRandom(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + +class LArithmeticD: public LTemplateInstruction<1, 2, 0> { + public: + LArithmeticD(Token::Value op, LOperand* left, LOperand* right) + : op_(op) { + inputs_[0] = left; + inputs_[1] = right; + } + + Token::Value op() const { return op_; } + + virtual Opcode opcode() const { return LInstruction::kArithmeticD; } + virtual void CompileToNative(LCodeGen* generator); + virtual const char* Mnemonic() const; + + private: + Token::Value op_; +}; + + +class LArithmeticT: public LTemplateInstruction<1, 2, 0> { + public: + LArithmeticT(Token::Value op, LOperand* left, LOperand* right) + : op_(op) { + inputs_[0] = left; + inputs_[1] = right; + } + + virtual Opcode opcode() const { return LInstruction::kArithmeticT; } + virtual void CompileToNative(LCodeGen* generator); + virtual const char* Mnemonic() const; + + Token::Value op() const { return op_; } + + private: + Token::Value op_; +}; + + +class LReturn: public LTemplateInstruction<0, 1, 0> { + public: + explicit LReturn(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(Return, "return") +}; + + +class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadNamedField(LOperand* object) { + inputs_[0] = object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field") + DECLARE_HYDROGEN_ACCESSOR(LoadNamedField) +}; + + +class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadNamedFieldPolymorphic(LOperand* object) { + inputs_[0] = object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic") + DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic) + + LOperand* object() { return inputs_[0]; } +}; + + +class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadNamedGeneric(LOperand* object) { + inputs_[0] = object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic") + DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric) + + LOperand* object() { return inputs_[0]; } + Handle<Object> name() const { return hydrogen()->name(); } +}; + + +class LLoadFunctionPrototype: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadFunctionPrototype(LOperand* function) { + inputs_[0] = function; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype") + DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype) + + LOperand* function() { return inputs_[0]; } +}; + + +class LLoadElements: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadElements(LOperand* object) { + inputs_[0] = object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements") +}; + + +class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadExternalArrayPointer(LOperand* object) { + inputs_[0] = object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer, + "load-external-array-pointer") +}; + + +class LLoadKeyedFastElement: public LTemplateInstruction<1, 2, 0> { + public: + LLoadKeyedFastElement(LOperand* elements, LOperand* key) { + inputs_[0] = elements; + inputs_[1] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement, "load-keyed-fast-element") + DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement) + + LOperand* elements() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } +}; + + +class LLoadKeyedFastDoubleElement: public LTemplateInstruction<1, 2, 0> { + public: + LLoadKeyedFastDoubleElement(LOperand* elements, LOperand* key) { + inputs_[0] = elements; + inputs_[1] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastDoubleElement, + "load-keyed-fast-double-element") + DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastDoubleElement) + + LOperand* elements() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } +}; + + +class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> { + public: + LLoadKeyedSpecializedArrayElement(LOperand* external_pointer, + LOperand* key) { + inputs_[0] = external_pointer; + inputs_[1] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement, + "load-keyed-specialized-array-element") + DECLARE_HYDROGEN_ACCESSOR(LoadKeyedSpecializedArrayElement) + + LOperand* external_pointer() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + ElementsKind elements_kind() const { + return hydrogen()->elements_kind(); + } +}; + + +class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> { + public: + LLoadKeyedGeneric(LOperand* obj, LOperand* key) { + inputs_[0] = obj; + inputs_[1] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic") + + LOperand* object() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } +}; + + +class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell") + DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell) +}; + + +class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadGlobalGeneric(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic") + DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric) + + LOperand* global_object() { return inputs_[0]; } + Handle<Object> name() const { return hydrogen()->name(); } + bool for_typeof() const { return hydrogen()->for_typeof(); } +}; + + +class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> { + public: + LStoreGlobalCell(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") + DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) + + LOperand* value() { return inputs_[0]; } +}; + + +class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> { + public: + explicit LStoreGlobalGeneric(LOperand* global_object, + LOperand* value) { + inputs_[0] = global_object; + inputs_[1] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic") + DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric) + + LOperand* global_object() { return InputAt(0); } + Handle<Object> name() const { return hydrogen()->name(); } + LOperand* value() { return InputAt(1); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } +}; + + +class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> { + public: + explicit LLoadContextSlot(LOperand* context) { + inputs_[0] = context; + } + + DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot") + DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot) + + LOperand* context() { return InputAt(0); } + int slot_index() { return hydrogen()->slot_index(); } + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LStoreContextSlot: public LTemplateInstruction<0, 2, 0> { + public: + LStoreContextSlot(LOperand* context, LOperand* value) { + inputs_[0] = context; + inputs_[1] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot") + DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot) + + LOperand* context() { return InputAt(0); } + LOperand* value() { return InputAt(1); } + int slot_index() { return hydrogen()->slot_index(); } + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LPushArgument: public LTemplateInstruction<0, 1, 0> { + public: + explicit LPushArgument(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument") +}; + + +class LThisFunction: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function") + DECLARE_HYDROGEN_ACCESSOR(ThisFunction) +}; + + +class LContext: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(Context, "context") +}; + + +class LOuterContext: public LTemplateInstruction<1, 1, 0> { + public: + explicit LOuterContext(LOperand* context) { + inputs_[0] = context; + } + + DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context") + + LOperand* context() { return InputAt(0); } +}; + + +class LGlobalObject: public LTemplateInstruction<1, 1, 0> { + public: + explicit LGlobalObject(LOperand* context) { + inputs_[0] = context; + } + + DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object") + + LOperand* context() { return InputAt(0); } +}; + + +class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> { + public: + explicit LGlobalReceiver(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver") + + LOperand* global() { return InputAt(0); } +}; + + +class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction, "call-constant-function") + DECLARE_HYDROGEN_ACCESSOR(CallConstantFunction) + + virtual void PrintDataTo(StringStream* stream); + + Handle<JSFunction> function() { return hydrogen()->function(); } + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LInvokeFunction: public LTemplateInstruction<1, 1, 0> { + public: + explicit LInvokeFunction(LOperand* function) { + inputs_[0] = function; + } + + DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function") + DECLARE_HYDROGEN_ACCESSOR(InvokeFunction) + + LOperand* function() { return inputs_[0]; } + + virtual void PrintDataTo(StringStream* stream); + + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallKeyed: public LTemplateInstruction<1, 1, 0> { + public: + explicit LCallKeyed(LOperand* key) { + inputs_[0] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed") + DECLARE_HYDROGEN_ACCESSOR(CallKeyed) + + virtual void PrintDataTo(StringStream* stream); + + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + + +class LCallNamed: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call-named") + DECLARE_HYDROGEN_ACCESSOR(CallNamed) + + virtual void PrintDataTo(StringStream* stream); + + Handle<String> name() const { return hydrogen()->name(); } + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallFunction: public LTemplateInstruction<1, 1, 0> { + public: + explicit LCallFunction(LOperand* function) { + inputs_[0] = function; + } + + DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") + DECLARE_HYDROGEN_ACCESSOR(CallFunction) + + LOperand* function() { return inputs_[0]; } + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallGlobal: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call-global") + DECLARE_HYDROGEN_ACCESSOR(CallGlobal) + + virtual void PrintDataTo(StringStream* stream); + + Handle<String> name() const {return hydrogen()->name(); } + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallKnownGlobal: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call-known-global") + DECLARE_HYDROGEN_ACCESSOR(CallKnownGlobal) + + virtual void PrintDataTo(StringStream* stream); + + Handle<JSFunction> target() const { return hydrogen()->target(); } + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallNew: public LTemplateInstruction<1, 1, 0> { + public: + explicit LCallNew(LOperand* constructor) { + inputs_[0] = constructor; + } + + DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new") + DECLARE_HYDROGEN_ACCESSOR(CallNew) + + virtual void PrintDataTo(StringStream* stream); + + int arity() const { return hydrogen()->argument_count() - 1; } +}; + + +class LCallRuntime: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call-runtime") + DECLARE_HYDROGEN_ACCESSOR(CallRuntime) + + const Runtime::Function* function() const { return hydrogen()->function(); } + int arity() const { return hydrogen()->argument_count(); } +}; + + +class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> { + public: + explicit LInteger32ToDouble(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double") +}; + + +class LNumberTagI: public LTemplateInstruction<1, 1, 0> { + public: + explicit LNumberTagI(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i") +}; + + +class LNumberTagD: public LTemplateInstruction<1, 1, 2> { + public: + LNumberTagD(LOperand* value, LOperand* temp1, LOperand* temp2) { + inputs_[0] = value; + temps_[0] = temp1; + temps_[1] = temp2; + } + + DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d") +}; + + +// Sometimes truncating conversion from a tagged value to an int32. +class LDoubleToI: public LTemplateInstruction<1, 1, 2> { + public: + LDoubleToI(LOperand* value, LOperand* temp1, LOperand* temp2) { + inputs_[0] = value; + temps_[0] = temp1; + temps_[1] = temp2; + } + + DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i") + DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) + + bool truncating() { return hydrogen()->CanTruncateToInt32(); } +}; + + +// Truncating conversion from a tagged value to an int32. +class LTaggedToI: public LTemplateInstruction<1, 1, 3> { + public: + LTaggedToI(LOperand* value, + LOperand* temp1, + LOperand* temp2, + LOperand* temp3) { + inputs_[0] = value; + temps_[0] = temp1; + temps_[1] = temp2; + temps_[2] = temp3; + } + + DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i") + DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) + + bool truncating() { return hydrogen()->CanTruncateToInt32(); } +}; + + +class LSmiTag: public LTemplateInstruction<1, 1, 0> { + public: + explicit LSmiTag(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag") +}; + + +class LNumberUntagD: public LTemplateInstruction<1, 1, 0> { + public: + explicit LNumberUntagD(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; + + +class LSmiUntag: public LTemplateInstruction<1, 1, 0> { + public: + LSmiUntag(LOperand* value, bool needs_check) + : needs_check_(needs_check) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag") + + bool needs_check() const { return needs_check_; } + + private: + bool needs_check_; +}; + + +class LStoreNamedField: public LTemplateInstruction<0, 2, 0> { + public: + LStoreNamedField(LOperand* obj, LOperand* val) { + inputs_[0] = obj; + inputs_[1] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") + DECLARE_HYDROGEN_ACCESSOR(StoreNamedField) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + + Handle<Object> name() const { return hydrogen()->name(); } + bool is_in_object() { return hydrogen()->is_in_object(); } + int offset() { return hydrogen()->offset(); } + Handle<Map> transition() const { return hydrogen()->transition(); } +}; + + +class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> { + public: + LStoreNamedGeneric(LOperand* obj, LOperand* val) { + inputs_[0] = obj; + inputs_[1] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic") + DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + Handle<Object> name() const { return hydrogen()->name(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } +}; + + +class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> { + public: + LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) { + inputs_[0] = obj; + inputs_[1] = key; + inputs_[2] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement, + "store-keyed-fast-element") + DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } +}; + + +class LStoreKeyedFastDoubleElement: public LTemplateInstruction<0, 3, 0> { + public: + LStoreKeyedFastDoubleElement(LOperand* elements, + LOperand* key, + LOperand* val) { + inputs_[0] = elements; + inputs_[1] = key; + inputs_[2] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastDoubleElement, + "store-keyed-fast-double-element") + DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastDoubleElement) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* elements() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } +}; + + +class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> { + public: + LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val) { + inputs_[0] = obj; + inputs_[1] = key; + inputs_[2] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") + DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } +}; + +class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { + public: + LStoreKeyedSpecializedArrayElement(LOperand* external_pointer, + LOperand* key, + LOperand* val) { + inputs_[0] = external_pointer; + inputs_[1] = key; + inputs_[2] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement, + "store-keyed-specialized-array-element") + DECLARE_HYDROGEN_ACCESSOR(StoreKeyedSpecializedArrayElement) + + LOperand* external_pointer() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } + ElementsKind elements_kind() const { + return hydrogen()->elements_kind(); + } +}; + + +class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> { + public: + LTransitionElementsKind(LOperand* object, + LOperand* new_map_temp, + LOperand* temp_reg) { + inputs_[0] = object; + temps_[0] = new_map_temp; + temps_[1] = temp_reg; + } + + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, + "transition-elements-kind") + DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* new_map_reg() { return temps_[0]; } + LOperand* temp_reg() { return temps_[1]; } + Handle<Map> original_map() { return hydrogen()->original_map(); } + Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); } +}; + + +class LStringAdd: public LTemplateInstruction<1, 2, 0> { + public: + LStringAdd(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add") + DECLARE_HYDROGEN_ACCESSOR(StringAdd) + + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } +}; + + + +class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> { + public: + LStringCharCodeAt(LOperand* string, LOperand* index) { + inputs_[0] = string; + inputs_[1] = index; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") + DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) + + LOperand* string() { return inputs_[0]; } + LOperand* index() { return inputs_[1]; } +}; + + +class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> { + public: + explicit LStringCharFromCode(LOperand* char_code) { + inputs_[0] = char_code; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code") + DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode) + + LOperand* char_code() { return inputs_[0]; } +}; + + +class LStringLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LStringLength(LOperand* string) { + inputs_[0] = string; + } + + DECLARE_CONCRETE_INSTRUCTION(StringLength, "string-length") + DECLARE_HYDROGEN_ACCESSOR(StringLength) + + LOperand* string() { return inputs_[0]; } +}; + + +class LCheckFunction: public LTemplateInstruction<0, 1, 0> { + public: + explicit LCheckFunction(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return InputAt(0); } + + DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function") + DECLARE_HYDROGEN_ACCESSOR(CheckFunction) +}; + + +class LCheckInstanceType: public LTemplateInstruction<0, 1, 0> { + public: + explicit LCheckInstanceType(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type") + DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType) +}; + + +class LCheckMap: public LTemplateInstruction<0, 1, 0> { + public: + explicit LCheckMap(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(CheckMap, "check-map") + DECLARE_HYDROGEN_ACCESSOR(CheckMap) +}; + + +class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 2> { + public: + LCheckPrototypeMaps(LOperand* temp1, LOperand* temp2) { + temps_[0] = temp1; + temps_[1] = temp2; + } + + DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps") + DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps) + + Handle<JSObject> prototype() const { return hydrogen()->prototype(); } + Handle<JSObject> holder() const { return hydrogen()->holder(); } +}; + + +class LCheckSmi: public LTemplateInstruction<0, 1, 0> { + public: + explicit LCheckSmi(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi") +}; + + +class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { + public: + explicit LCheckNonSmi(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi") +}; + + +class LClampDToUint8: public LTemplateInstruction<1, 1, 1> { + public: + LClampDToUint8(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* unclamped() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8") +}; + + +class LClampIToUint8: public LTemplateInstruction<1, 1, 0> { + public: + explicit LClampIToUint8(LOperand* value) { + inputs_[0] = value; + } + + LOperand* unclamped() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8") +}; + + +class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { + public: + LClampTToUint8(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* unclamped() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8") +}; + + +class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal") + DECLARE_HYDROGEN_ACCESSOR(ArrayLiteral) +}; + + +class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) +}; + + +class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) +}; + + +class LRegExpLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp-literal") + DECLARE_HYDROGEN_ACCESSOR(RegExpLiteral) +}; + + +class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal") + DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral) + + Handle<SharedFunctionInfo> shared_info() { return hydrogen()->shared_info(); } +}; + + +class LToFastProperties: public LTemplateInstruction<1, 1, 0> { + public: + explicit LToFastProperties(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") + DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) +}; + + +class LTypeof: public LTemplateInstruction<1, 1, 0> { + public: + explicit LTypeof(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof") +}; + + +class LTypeofIsAndBranch: public LControlInstruction<1, 0> { + public: + explicit LTypeofIsAndBranch(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch") + DECLARE_HYDROGEN_ACCESSOR(TypeofIsAndBranch) + + Handle<String> type_literal() { return hydrogen()->type_literal(); } + + virtual void PrintDataTo(StringStream* stream); +}; + + +class LIsConstructCallAndBranch: public LControlInstruction<0, 1> { + public: + explicit LIsConstructCallAndBranch(LOperand* temp) { + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, + "is-construct-call-and-branch") +}; + + +class LDeleteProperty: public LTemplateInstruction<1, 2, 0> { + public: + LDeleteProperty(LOperand* obj, LOperand* key) { + inputs_[0] = obj; + inputs_[1] = key; + } + + DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property") + + LOperand* object() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } +}; + + +class LOsrEntry: public LTemplateInstruction<0, 0, 0> { + public: + LOsrEntry(); + + DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry") + + LOperand** SpilledRegisterArray() { return register_spills_; } + LOperand** SpilledDoubleRegisterArray() { return double_register_spills_; } + + void MarkSpilledRegister(int allocation_index, LOperand* spill_operand); + void MarkSpilledDoubleRegister(int allocation_index, + LOperand* spill_operand); + + private: + // Arrays of spill slot operands for registers with an assigned spill + // slot, i.e., that must also be restored to the spill slot on OSR entry. + // NULL if the register has no assigned spill slot. Indexed by allocation + // index. + LOperand* register_spills_[Register::kNumAllocatableRegisters]; + LOperand* double_register_spills_[DoubleRegister::kNumAllocatableRegisters]; +}; + + +class LStackCheck: public LTemplateInstruction<0, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check") + DECLARE_HYDROGEN_ACCESSOR(StackCheck) + + Label* done_label() { return &done_label_; } + + private: + Label done_label_; +}; + + +class LIn: public LTemplateInstruction<1, 2, 0> { + public: + LIn(LOperand* key, LOperand* object) { + inputs_[0] = key; + inputs_[1] = object; + } + + LOperand* key() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(In, "in") +}; + + +class LChunkBuilder; +class LChunk: public ZoneObject { + public: + explicit LChunk(CompilationInfo* info, HGraph* graph); + + void AddInstruction(LInstruction* instruction, HBasicBlock* block); + LConstantOperand* DefineConstantOperand(HConstant* constant); + Handle<Object> LookupLiteral(LConstantOperand* operand) const; + Representation LookupLiteralRepresentation(LConstantOperand* operand) const; + + int GetNextSpillIndex(bool is_double); + LOperand* GetNextSpillSlot(bool is_double); + + int ParameterAt(int index); + int GetParameterStackSlot(int index) const; + int spill_slot_count() const { return spill_slot_count_; } + CompilationInfo* info() const { return info_; } + HGraph* graph() const { return graph_; } + const ZoneList<LInstruction*>* instructions() const { return &instructions_; } + void AddGapMove(int index, LOperand* from, LOperand* to); + LGap* GetGapAt(int index) const; + bool IsGapAt(int index) const; + int NearestGapPos(int index) const; + void MarkEmptyBlocks(); + const ZoneList<LPointerMap*>* pointer_maps() const { return &pointer_maps_; } + LLabel* GetLabel(int block_id) const { + HBasicBlock* block = graph_->blocks()->at(block_id); + int first_instruction = block->first_instruction_index(); + return LLabel::cast(instructions_[first_instruction]); + } + int LookupDestination(int block_id) const { + LLabel* cur = GetLabel(block_id); + while (cur->replacement() != NULL) { + cur = cur->replacement(); + } + return cur->block_id(); + } + Label* GetAssemblyLabel(int block_id) const { + LLabel* label = GetLabel(block_id); + ASSERT(!label->HasReplacement()); + return label->label(); + } + + const ZoneList<Handle<JSFunction> >* inlined_closures() const { + return &inlined_closures_; + } + + void AddInlinedClosure(Handle<JSFunction> closure) { + inlined_closures_.Add(closure); + } + + private: + int spill_slot_count_; + CompilationInfo* info_; + HGraph* const graph_; + ZoneList<LInstruction*> instructions_; + ZoneList<LPointerMap*> pointer_maps_; + ZoneList<Handle<JSFunction> > inlined_closures_; }; class LChunkBuilder BASE_EMBEDDED { public: - LChunkBuilder(CompilationInfo*&, HGraph* graph, LAllocator* allocator) { } + LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) + : chunk_(NULL), + info_(info), + graph_(graph), + status_(UNUSED), + current_instruction_(NULL), + current_block_(NULL), + next_block_(NULL), + argument_count_(0), + allocator_(allocator), + position_(RelocInfo::kNoPosition), + instruction_pending_deoptimization_environment_(NULL), + pending_deoptimization_ast_id_(AstNode::kNoNumber) { } // Build the sequence for the graph. - LChunk* Build() { - UNIMPLEMENTED(); - return NULL; - }; + LChunk* Build(); // Declare methods that deal with the individual node types. -#define DECLARE_DO(type) LInstruction* Do##type(H##type* node) { \ - UNIMPLEMENTED(); \ - return NULL; \ - } +#define DECLARE_DO(type) LInstruction* Do##type(H##type* node); HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) #undef DECLARE_DO + private: + enum Status { + UNUSED, + BUILDING, + DONE, + ABORTED + }; + + LChunk* chunk() const { return chunk_; } + CompilationInfo* info() const { return info_; } + HGraph* graph() const { return graph_; } + + bool is_unused() const { return status_ == UNUSED; } + bool is_building() const { return status_ == BUILDING; } + bool is_done() const { return status_ == DONE; } + bool is_aborted() const { return status_ == ABORTED; } + + void Abort(const char* format, ...); + + // Methods for getting operands for Use / Define / Temp. + LUnallocated* ToUnallocated(Register reg); + LUnallocated* ToUnallocated(DoubleRegister reg); + + // Methods for setting up define-use relationships. + MUST_USE_RESULT LOperand* Use(HValue* value, LUnallocated* operand); + MUST_USE_RESULT LOperand* UseFixed(HValue* value, Register fixed_register); + MUST_USE_RESULT LOperand* UseFixedDouble(HValue* value, + DoubleRegister fixed_register); + + // A value that is guaranteed to be allocated to a register. + // Operand created by UseRegister is guaranteed to be live until the end of + // instruction. This means that register allocator will not reuse it's + // register for any other operand inside instruction. + // Operand created by UseRegisterAtStart is guaranteed to be live only at + // instruction start. Register allocator is free to assign the same register + // to some other operand used inside instruction (i.e. temporary or + // output). + MUST_USE_RESULT LOperand* UseRegister(HValue* value); + MUST_USE_RESULT LOperand* UseRegisterAtStart(HValue* value); + + // An input operand in a register that may be trashed. + MUST_USE_RESULT LOperand* UseTempRegister(HValue* value); + + // An input operand in a register or stack slot. + MUST_USE_RESULT LOperand* Use(HValue* value); + MUST_USE_RESULT LOperand* UseAtStart(HValue* value); + + // An input operand in a register, stack slot or a constant operand. + MUST_USE_RESULT LOperand* UseOrConstant(HValue* value); + MUST_USE_RESULT LOperand* UseOrConstantAtStart(HValue* value); + + // An input operand in a register or a constant operand. + MUST_USE_RESULT LOperand* UseRegisterOrConstant(HValue* value); + MUST_USE_RESULT LOperand* UseRegisterOrConstantAtStart(HValue* value); + + // An input operand in register, stack slot or a constant operand. + // Will not be moved to a register even if one is freely available. + MUST_USE_RESULT LOperand* UseAny(HValue* value); + + // Temporary operand that must be in a register. + MUST_USE_RESULT LUnallocated* TempRegister(); + MUST_USE_RESULT LOperand* FixedTemp(Register reg); + MUST_USE_RESULT LOperand* FixedTemp(DoubleRegister reg); + + // Methods for setting up define-use relationships. + // Return the same instruction that they are passed. + template<int I, int T> + LInstruction* Define(LTemplateInstruction<1, I, T>* instr, + LUnallocated* result); + template<int I, int T> + LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr); + template<int I, int T> + LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr, + int index); + template<int I, int T> + LInstruction* DefineSameAsFirst(LTemplateInstruction<1, I, T>* instr); + template<int I, int T> + LInstruction* DefineFixed(LTemplateInstruction<1, I, T>* instr, + Register reg); + template<int I, int T> + LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr, + DoubleRegister reg); + LInstruction* AssignEnvironment(LInstruction* instr); + LInstruction* AssignPointerMap(LInstruction* instr); + + enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY }; + + // By default we assume that instruction sequences generated for calls + // cannot deoptimize eagerly and we do not attach environment to this + // instruction. + LInstruction* MarkAsCall( + LInstruction* instr, + HInstruction* hinstr, + CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); + LInstruction* MarkAsSaveDoubles(LInstruction* instr); + + LInstruction* SetInstructionPendingDeoptimizationEnvironment( + LInstruction* instr, int ast_id); + void ClearInstructionPendingDeoptimizationEnvironment(); + + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, + int* argument_index_accumulator); + + void VisitInstruction(HInstruction* current); + + void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); + LInstruction* DoBit(Token::Value op, HBitwiseBinaryOperation* instr); + LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr); + LInstruction* DoArithmeticD(Token::Value op, + HArithmeticBinaryOperation* instr); + LInstruction* DoArithmeticT(Token::Value op, + HArithmeticBinaryOperation* instr); + + LChunk* chunk_; + CompilationInfo* info_; + HGraph* const graph_; + Status status_; + HInstruction* current_instruction_; + HBasicBlock* current_block_; + HBasicBlock* next_block_; + int argument_count_; + LAllocator* allocator_; + int position_; + LInstruction* instruction_pending_deoptimization_environment_; + int pending_deoptimization_ast_id_; + DISALLOW_COPY_AND_ASSIGN(LChunkBuilder); }; +#undef DECLARE_HYDROGEN_ACCESSOR +#undef DECLARE_CONCRETE_INSTRUCTION } } // namespace v8::internal diff --git a/deps/v8/src/mips/macro-assembler-mips.cc b/deps/v8/src/mips/macro-assembler-mips.cc index 1c0af5d629..f4e043a7b2 100644 --- a/deps/v8/src/mips/macro-assembler-mips.cc +++ b/deps/v8/src/mips/macro-assembler-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -42,7 +42,8 @@ namespace internal { MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) : Assembler(arg_isolate, buffer, size), generating_stub_(false), - allow_stub_calls_(true) { + allow_stub_calls_(true), + has_frame_(false) { if (isolate() != NULL) { code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), isolate()); @@ -80,36 +81,16 @@ void MacroAssembler::StoreRoot(Register source, } -void MacroAssembler::RecordWriteHelper(Register object, - Register address, - Register scratch) { - if (emit_debug_code()) { - // Check that the object is not in new space. - Label not_in_new_space; - InNewSpace(object, scratch, ne, ¬_in_new_space); - Abort("new-space object passed to RecordWriteHelper"); - bind(¬_in_new_space); - } - - // Calculate page address: Clear bits from 0 to kPageSizeBits. - if (mips32r2) { - Ins(object, zero_reg, 0, kPageSizeBits); +void MacroAssembler::LoadHeapObject(Register result, + Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + li(result, Operand(cell)); + lw(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset)); } else { - // The Ins macro is slow on r1, so use shifts instead. - srl(object, object, kPageSizeBits); - sll(object, object, kPageSizeBits); + li(result, Operand(object)); } - - // Calculate region number. - Ext(address, address, Page::kRegionSizeLog2, - kPageSizeBits - Page::kRegionSizeLog2); - - // Mark region dirty. - lw(scratch, MemOperand(object, Page::kDirtyFlagOffset)); - li(at, Operand(1)); - sllv(at, at, address); - or_(scratch, scratch, at); - sw(scratch, MemOperand(object, Page::kDirtyFlagOffset)); } @@ -119,7 +100,9 @@ void MacroAssembler::PushSafepointRegisters() { // stack, so adjust the stack for unsaved registers. const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; ASSERT(num_unsaved >= 0); - Subu(sp, sp, Operand(num_unsaved * kPointerSize)); + if (num_unsaved > 0) { + Subu(sp, sp, Operand(num_unsaved * kPointerSize)); + } MultiPush(kSafepointSavedRegisters); } @@ -127,7 +110,9 @@ void MacroAssembler::PushSafepointRegisters() { void MacroAssembler::PopSafepointRegisters() { const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; MultiPop(kSafepointSavedRegisters); - Addu(sp, sp, Operand(num_unsaved * kPointerSize)); + if (num_unsaved > 0) { + Addu(sp, sp, Operand(num_unsaved * kPointerSize)); + } } @@ -180,6 +165,7 @@ MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) { MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) { + UNIMPLEMENTED_MIPS(); // General purpose registers are pushed last on the stack. int doubles_size = FPURegister::kNumAllocatableRegisters * kDoubleSize; int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize; @@ -187,8 +173,6 @@ MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) { } - - void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cc, @@ -200,38 +184,53 @@ void MacroAssembler::InNewSpace(Register object, } -// Will clobber 4 registers: object, scratch0, scratch1, at. The -// register 'object' contains a heap object pointer. The heap object -// tag is shifted away. -void MacroAssembler::RecordWrite(Register object, - Operand offset, - Register scratch0, - Register scratch1) { - // The compiled code assumes that record write doesn't change the - // context register, so we check that none of the clobbered - // registers are cp. - ASSERT(!object.is(cp) && !scratch0.is(cp) && !scratch1.is(cp)); - +void MacroAssembler::RecordWriteField( + Register object, + int offset, + Register value, + Register dst, + RAStatus ra_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + ASSERT(!AreAliased(value, dst, t8, object)); + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis. Label done; - // First, test that the object is not in the new space. We cannot set - // region marks for new space pages. - InNewSpace(object, scratch0, eq, &done); + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done); + } - // Add offset into the object. - Addu(scratch0, object, offset); + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + ASSERT(IsAligned(offset, kPointerSize)); - // Record the actual write. - RecordWriteHelper(object, scratch0, scratch1); + Addu(dst, object, Operand(offset - kHeapObjectTag)); + if (emit_debug_code()) { + Label ok; + And(t8, dst, Operand((1 << kPointerSizeLog2) - 1)); + Branch(&ok, eq, t8, Operand(zero_reg)); + stop("Unaligned cell in write barrier"); + bind(&ok); + } + + RecordWrite(object, + dst, + value, + ra_status, + save_fp, + remembered_set_action, + OMIT_SMI_CHECK); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered input registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - li(object, Operand(BitCast<int32_t>(kZapValue))); - li(scratch0, Operand(BitCast<int32_t>(kZapValue))); - li(scratch1, Operand(BitCast<int32_t>(kZapValue))); + li(value, Operand(BitCast<int32_t>(kZapValue + 4))); + li(dst, Operand(BitCast<int32_t>(kZapValue + 8))); } } @@ -241,29 +240,102 @@ void MacroAssembler::RecordWrite(Register object, // tag is shifted away. void MacroAssembler::RecordWrite(Register object, Register address, - Register scratch) { + Register value, + RAStatus ra_status, + SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + ASSERT(!AreAliased(object, address, value, t8)); + ASSERT(!AreAliased(object, address, value, t9)); // The compiled code assumes that record write doesn't change the // context register, so we check that none of the clobbered // registers are cp. - ASSERT(!object.is(cp) && !address.is(cp) && !scratch.is(cp)); + ASSERT(!address.is(cp) && !value.is(cp)); + + if (emit_debug_code()) { + lw(at, MemOperand(address)); + Assert( + eq, "Wrong address or value passed to RecordWrite", at, Operand(value)); + } Label done; - // First, test that the object is not in the new space. We cannot set - // region marks for new space pages. - InNewSpace(object, scratch, eq, &done); + if (smi_check == INLINE_SMI_CHECK) { + ASSERT_EQ(0, kSmiTag); + JumpIfSmi(value, &done); + } + + CheckPageFlag(value, + value, // Used as scratch. + MemoryChunk::kPointersToHereAreInterestingMask, + eq, + &done); + CheckPageFlag(object, + value, // Used as scratch. + MemoryChunk::kPointersFromHereAreInterestingMask, + eq, + &done); // Record the actual write. - RecordWriteHelper(object, address, scratch); + if (ra_status == kRAHasNotBeenSaved) { + push(ra); + } + RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); + CallStub(&stub); + if (ra_status == kRAHasNotBeenSaved) { + pop(ra); + } bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - li(object, Operand(BitCast<int32_t>(kZapValue))); - li(address, Operand(BitCast<int32_t>(kZapValue))); - li(scratch, Operand(BitCast<int32_t>(kZapValue))); + li(address, Operand(BitCast<int32_t>(kZapValue + 12))); + li(value, Operand(BitCast<int32_t>(kZapValue + 16))); + } +} + + +void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. + Register address, + Register scratch, + SaveFPRegsMode fp_mode, + RememberedSetFinalAction and_then) { + Label done; + if (emit_debug_code()) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok); + stop("Remembered set pointer is in new space"); + bind(&ok); + } + // Load store buffer top. + ExternalReference store_buffer = + ExternalReference::store_buffer_top(isolate()); + li(t8, Operand(store_buffer)); + lw(scratch, MemOperand(t8)); + // Store pointer to buffer and increment buffer top. + sw(address, MemOperand(scratch)); + Addu(scratch, scratch, kPointerSize); + // Write back new top of buffer. + sw(scratch, MemOperand(t8)); + // Call stub on end of buffer. + // Check for end of buffer. + And(t8, scratch, Operand(StoreBuffer::kStoreBufferOverflowBit)); + if (and_then == kFallThroughAtEnd) { + Branch(&done, eq, t8, Operand(zero_reg)); + } else { + ASSERT(and_then == kReturnAtEnd); + Ret(eq, t8, Operand(zero_reg)); + } + push(ra); + StoreBufferOverflowStub store_buffer_overflow = + StoreBufferOverflowStub(fp_mode); + CallStub(&store_buffer_overflow); + pop(ra); + bind(&done); + if (and_then == kReturnAtEnd) { + Ret(); } } @@ -372,8 +444,10 @@ void MacroAssembler::GetNumberHash(Register reg0, Register scratch) { xor_(reg0, reg0, at); // hash = hash * 2057; - li(scratch, Operand(2057)); - mul(reg0, reg0, scratch); + sll(scratch, reg0, 11); + sll(at, reg0, 3); + addu(reg0, reg0, at); + addu(reg0, reg0, scratch); // hash = hash ^ (hash >> 16); srl(at, reg0, 16); @@ -697,18 +771,18 @@ void MacroAssembler::li(Register rd, Operand j, bool gen2instr) { } else if (!(j.imm32_ & kHiMask)) { ori(rd, zero_reg, j.imm32_); } else if (!(j.imm32_ & kImm16Mask)) { - lui(rd, (j.imm32_ & kHiMask) >> kLuiShift); + lui(rd, (j.imm32_ >> kLuiShift) & kImm16Mask); } else { - lui(rd, (j.imm32_ & kHiMask) >> kLuiShift); + lui(rd, (j.imm32_ >> kLuiShift) & kImm16Mask); ori(rd, rd, (j.imm32_ & kImm16Mask)); } } else if (MustUseReg(j.rmode_) || gen2instr) { if (MustUseReg(j.rmode_)) { RecordRelocInfo(j.rmode_, j.imm32_); } - // We need always the same number of instructions as we may need to patch + // We always need the same number of instructions as we may need to patch // this code to load another value which may need 2 instructions to load. - lui(rd, (j.imm32_ & kHiMask) >> kLuiShift); + lui(rd, (j.imm32_ >> kLuiShift) & kImm16Mask); ori(rd, rd, (j.imm32_ & kImm16Mask)); } } @@ -719,7 +793,7 @@ void MacroAssembler::MultiPush(RegList regs) { int16_t stack_offset = num_to_push * kPointerSize; Subu(sp, sp, Operand(stack_offset)); - for (int16_t i = kNumRegisters; i > 0; i--) { + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { if ((regs & (1 << i)) != 0) { stack_offset -= kPointerSize; sw(ToRegister(i), MemOperand(sp, stack_offset)); @@ -758,7 +832,7 @@ void MacroAssembler::MultiPop(RegList regs) { void MacroAssembler::MultiPopReversed(RegList regs) { int16_t stack_offset = 0; - for (int16_t i = kNumRegisters; i > 0; i--) { + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { if ((regs & (1 << i)) != 0) { lw(ToRegister(i), MemOperand(sp, stack_offset)); stack_offset += kPointerSize; @@ -774,7 +848,7 @@ void MacroAssembler::MultiPushFPU(RegList regs) { int16_t stack_offset = num_to_push * kDoubleSize; Subu(sp, sp, Operand(stack_offset)); - for (int16_t i = kNumRegisters; i > 0; i--) { + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { if ((regs & (1 << i)) != 0) { stack_offset -= kDoubleSize; sdc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); @@ -816,7 +890,7 @@ void MacroAssembler::MultiPopReversedFPU(RegList regs) { CpuFeatures::Scope scope(FPU); int16_t stack_offset = 0; - for (int16_t i = kNumRegisters; i > 0; i--) { + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { if ((regs & (1 << i)) != 0) { ldc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); stack_offset += kDoubleSize; @@ -826,6 +900,21 @@ void MacroAssembler::MultiPopReversedFPU(RegList regs) { } +void MacroAssembler::FlushICache(Register address, unsigned instructions) { + RegList saved_regs = kJSCallerSaved | ra.bit(); + MultiPush(saved_regs); + AllowExternalCallThatCantCauseGC scope(this); + + // Save to a0 in case address == t0. + Move(a0, address); + PrepareCallCFunction(2, t0); + + li(a1, instructions * kInstrSize); + CallCFunction(ExternalReference::flush_icache_function(isolate()), 2); + MultiPop(saved_regs); +} + + void MacroAssembler::Ext(Register rt, Register rs, uint16_t pos, @@ -854,34 +943,21 @@ void MacroAssembler::Ins(Register rt, uint16_t pos, uint16_t size) { ASSERT(pos < 32); - ASSERT(pos + size < 32); + ASSERT(pos + size <= 32); + ASSERT(size != 0); if (mips32r2) { ins_(rt, rs, pos, size); } else { ASSERT(!rt.is(t8) && !rs.is(t8)); - - srl(t8, rt, pos + size); - // The left chunk from rt that needs to - // be saved is on the right side of t8. - sll(at, t8, pos + size); - // The 'at' register now contains the left chunk on - // the left (proper position) and zeroes. - sll(t8, rt, 32 - pos); - // t8 now contains the right chunk on the left and zeroes. - srl(t8, t8, 32 - pos); - // t8 now contains the right chunk on - // the right (proper position) and zeroes. - or_(rt, at, t8); - // rt now contains the left and right chunks from the original rt - // in their proper position and zeroes in the middle. - sll(t8, rs, 32 - size); - // t8 now contains the chunk from rs on the left and zeroes. - srl(t8, t8, 32 - size - pos); - // t8 now contains the original chunk from rs in - // the middle (proper position). - or_(rt, rt, t8); - // rt now contains the result of the ins instruction in R2 mode. + Subu(at, zero_reg, Operand(1)); + srl(at, at, 32 - size); + and_(t8, rs, at); + sll(t8, t8, pos); + sll(at, at, pos); + nor(at, at, zero_reg); + and_(at, rt, at); + or_(rt, t8, at); } } @@ -952,11 +1028,9 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd, mtc1(at, FPURegister::from_code(scratch.code() + 1)); mtc1(zero_reg, scratch); // Test if scratch > fd. - c(OLT, D, fd, scratch); - - Label simple_convert; // If fd < 2^31 we can convert it normally. - bc1t(&simple_convert); + Label simple_convert; + BranchF(&simple_convert, NULL, lt, fd, scratch); // First we subtract 2^31 from fd, then trunc it to rs // and add 2^31 to rs. @@ -976,6 +1050,102 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd, } +void MacroAssembler::BranchF(Label* target, + Label* nan, + Condition cc, + FPURegister cmp1, + FPURegister cmp2, + BranchDelaySlot bd) { + if (cc == al) { + Branch(bd, target); + return; + } + + ASSERT(nan || target); + // Check for unordered (NaN) cases. + if (nan) { + c(UN, D, cmp1, cmp2); + bc1t(nan); + } + + if (target) { + // Here NaN cases were either handled by this function or are assumed to + // have been handled by the caller. + // Unsigned conditions are treated as their signed counterpart. + switch (cc) { + case Uless: + case less: + c(OLT, D, cmp1, cmp2); + bc1t(target); + break; + case Ugreater: + case greater: + c(ULE, D, cmp1, cmp2); + bc1f(target); + break; + case Ugreater_equal: + case greater_equal: + c(ULT, D, cmp1, cmp2); + bc1f(target); + break; + case Uless_equal: + case less_equal: + c(OLE, D, cmp1, cmp2); + bc1t(target); + break; + case eq: + c(EQ, D, cmp1, cmp2); + bc1t(target); + break; + case ne: + c(EQ, D, cmp1, cmp2); + bc1f(target); + break; + default: + CHECK(0); + }; + } + + if (bd == PROTECT) { + nop(); + } +} + + +void MacroAssembler::Move(FPURegister dst, double imm) { + ASSERT(CpuFeatures::IsEnabled(FPU)); + static const DoubleRepresentation minus_zero(-0.0); + static const DoubleRepresentation zero(0.0); + DoubleRepresentation value(imm); + // Handle special values first. + bool force_load = dst.is(kDoubleRegZero); + if (value.bits == zero.bits && !force_load) { + mov_d(dst, kDoubleRegZero); + } else if (value.bits == minus_zero.bits && !force_load) { + neg_d(dst, kDoubleRegZero); + } else { + uint32_t lo, hi; + DoubleAsTwoUInt32(imm, &lo, &hi); + // Move the low part of the double into the lower of the corresponding FPU + // register of FPU register pair. + if (lo != 0) { + li(at, Operand(lo)); + mtc1(at, dst); + } else { + mtc1(zero_reg, dst); + } + // Move the high part of the double into the higher of the corresponding FPU + // register of FPU register pair. + if (hi != 0) { + li(at, Operand(hi)); + mtc1(at, dst.high()); + } else { + mtc1(zero_reg, dst.high()); + } + } +} + + // Tries to get a signed int32 out of a double precision floating point heap // number. Rounds towards 0. Branch to 'not_int32' if the double is out of the // 32bits signed integer range. @@ -1008,7 +1178,7 @@ void MacroAssembler::ConvertToInt32(Register source, Branch(not_int32, gt, scratch2, Operand(non_smi_exponent)); // We know the exponent is smaller than 30 (biased). If it is less than - // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie + // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, i.e. // it rounds to zero. const uint32_t zero_exponent = (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift; @@ -1074,6 +1244,53 @@ void MacroAssembler::ConvertToInt32(Register source, } +void MacroAssembler::EmitFPUTruncate(FPURoundingMode rounding_mode, + FPURegister result, + DoubleRegister double_input, + Register scratch1, + Register except_flag, + CheckForInexactConversion check_inexact) { + ASSERT(CpuFeatures::IsSupported(FPU)); + CpuFeatures::Scope scope(FPU); + + int32_t except_mask = kFCSRFlagMask; // Assume interested in all exceptions. + + if (check_inexact == kDontCheckForInexactConversion) { + // Ingore inexact exceptions. + except_mask &= ~kFCSRInexactFlagMask; + } + + // Save FCSR. + cfc1(scratch1, FCSR); + // Disable FPU exceptions. + ctc1(zero_reg, FCSR); + + // Do operation based on rounding mode. + switch (rounding_mode) { + case kRoundToNearest: + round_w_d(result, double_input); + break; + case kRoundToZero: + trunc_w_d(result, double_input); + break; + case kRoundToPlusInf: + ceil_w_d(result, double_input); + break; + case kRoundToMinusInf: + floor_w_d(result, double_input); + break; + } // End of switch-statement. + + // Retrieve FCSR. + cfc1(except_flag, FCSR); + // Restore FCSR. + ctc1(scratch1, FCSR); + + // Check for fpu exceptions. + And(except_flag, except_flag, Operand(except_mask)); +} + + void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result, Register input_high, Register input_low, @@ -1160,22 +1377,21 @@ void MacroAssembler::EmitECMATruncate(Register result, FPURegister double_input, FPURegister single_scratch, Register scratch, - Register input_high, - Register input_low) { + Register scratch2, + Register scratch3) { CpuFeatures::Scope scope(FPU); - ASSERT(!input_high.is(result)); - ASSERT(!input_low.is(result)); - ASSERT(!input_low.is(input_high)); + ASSERT(!scratch2.is(result)); + ASSERT(!scratch3.is(result)); + ASSERT(!scratch3.is(scratch2)); ASSERT(!scratch.is(result) && - !scratch.is(input_high) && - !scratch.is(input_low)); + !scratch.is(scratch2) && + !scratch.is(scratch3)); ASSERT(!single_scratch.is(double_input)); Label done; Label manual; // Clear cumulative exception flags and save the FCSR. - Register scratch2 = input_high; cfc1(scratch2, FCSR); ctc1(zero_reg, FCSR); // Try a conversion to a signed integer. @@ -1192,6 +1408,8 @@ void MacroAssembler::EmitECMATruncate(Register result, Branch(&done, eq, scratch, Operand(zero_reg)); // Load the double value and perform a manual truncation. + Register input_high = scratch2; + Register input_low = scratch3; Move(input_low, input_high, double_input); EmitOutOfInt32RangeTruncate(result, input_high, @@ -1223,15 +1441,6 @@ void MacroAssembler::GetLeastBitsFromInt32(Register dst, (cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg)))) -bool MacroAssembler::UseAbsoluteCodePointers() { - if (is_trampoline_emitted()) { - return true; - } else { - return false; - } -} - - void MacroAssembler::Branch(int16_t offset, BranchDelaySlot bdslot) { BranchShort(offset, bdslot); } @@ -1245,11 +1454,18 @@ void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs, void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) { - bool is_label_near = is_near(L); - if (UseAbsoluteCodePointers() && !is_label_near) { - Jr(L, bdslot); + if (L->is_bound()) { + if (is_near(L)) { + BranchShort(L, bdslot); + } else { + Jr(L, bdslot); + } } else { - BranchShort(L, bdslot); + if (is_trampoline_emitted()) { + Jr(L, bdslot); + } else { + BranchShort(L, bdslot); + } } } @@ -1257,15 +1473,26 @@ void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) { void MacroAssembler::Branch(Label* L, Condition cond, Register rs, const Operand& rt, BranchDelaySlot bdslot) { - bool is_label_near = is_near(L); - if (UseAbsoluteCodePointers() && !is_label_near) { - Label skip; - Condition neg_cond = NegateCondition(cond); - BranchShort(&skip, neg_cond, rs, rt); - Jr(L, bdslot); - bind(&skip); + if (L->is_bound()) { + if (is_near(L)) { + BranchShort(L, cond, rs, rt, bdslot); + } else { + Label skip; + Condition neg_cond = NegateCondition(cond); + BranchShort(&skip, neg_cond, rs, rt); + Jr(L, bdslot); + bind(&skip); + } } else { - BranchShort(L, cond, rs, rt, bdslot); + if (is_trampoline_emitted()) { + Label skip; + Condition neg_cond = NegateCondition(cond); + BranchShort(&skip, neg_cond, rs, rt); + Jr(L, bdslot); + bind(&skip); + } else { + BranchShort(L, cond, rs, rt, bdslot); + } } } @@ -1288,8 +1515,8 @@ void MacroAssembler::BranchShort(int16_t offset, Condition cond, Register rs, Register scratch = at; if (rt.is_reg()) { - // We don't want any other register but scratch clobbered. - ASSERT(!scratch.is(rs) && !scratch.is(rt.rm_)); + // NOTE: 'at' can be clobbered by Branch but it is legal to use it as rs or + // rt. r2 = rt.rm_; switch (cond) { case cc_always: @@ -1791,11 +2018,18 @@ void MacroAssembler::BranchAndLink(int16_t offset, Condition cond, Register rs, void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) { - bool is_label_near = is_near(L); - if (UseAbsoluteCodePointers() && !is_label_near) { - Jalr(L, bdslot); + if (L->is_bound()) { + if (is_near(L)) { + BranchAndLinkShort(L, bdslot); + } else { + Jalr(L, bdslot); + } } else { - BranchAndLinkShort(L, bdslot); + if (is_trampoline_emitted()) { + Jalr(L, bdslot); + } else { + BranchAndLinkShort(L, bdslot); + } } } @@ -1803,15 +2037,26 @@ void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) { void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs, const Operand& rt, BranchDelaySlot bdslot) { - bool is_label_near = is_near(L); - if (UseAbsoluteCodePointers() && !is_label_near) { - Label skip; - Condition neg_cond = NegateCondition(cond); - BranchShort(&skip, neg_cond, rs, rt); - Jalr(L, bdslot); - bind(&skip); + if (L->is_bound()) { + if (is_near(L)) { + BranchAndLinkShort(L, cond, rs, rt, bdslot); + } else { + Label skip; + Condition neg_cond = NegateCondition(cond); + BranchShort(&skip, neg_cond, rs, rt); + Jalr(L, bdslot); + bind(&skip); + } } else { - BranchAndLinkShort(L, cond, rs, rt, bdslot); + if (is_trampoline_emitted()) { + Label skip; + Condition neg_cond = NegateCondition(cond); + BranchShort(&skip, neg_cond, rs, rt); + Jalr(L, bdslot); + bind(&skip); + } else { + BranchAndLinkShort(L, cond, rs, rt, bdslot); + } } } @@ -2318,10 +2563,10 @@ void MacroAssembler::Push(Handle<Object> handle) { #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::DebugBreak() { - ASSERT(allow_stub_calls()); mov(a0, zero_reg); li(a1, Operand(ExternalReference(Runtime::kDebugBreak, isolate()))); CEntryStub ces(1); + ASSERT(AllowThisStubCall(&ces)); Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); } @@ -2331,61 +2576,43 @@ void MacroAssembler::DebugBreak() { // --------------------------------------------------------------------------- // Exception handling. -void MacroAssembler::PushTryHandler(CodeLocation try_location, - HandlerType type) { +void MacroAssembler::PushTryHandler(StackHandler::Kind kind, + int handler_index) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - - // The return address is passed in register ra. - if (try_location == IN_JAVASCRIPT) { - if (type == TRY_CATCH_HANDLER) { - li(t0, Operand(StackHandler::TRY_CATCH)); - } else { - li(t0, Operand(StackHandler::TRY_FINALLY)); - } - // Save the current handler as the next handler. - li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - lw(t1, MemOperand(t2)); - - addiu(sp, sp, -StackHandlerConstants::kSize); - sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset)); - sw(fp, MemOperand(sp, StackHandlerConstants::kFPOffset)); - sw(cp, MemOperand(sp, StackHandlerConstants::kContextOffset)); - sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset)); - sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset)); - - // Link this handler as the new current one. - sw(sp, MemOperand(t2)); - + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // For the JSEntry handler, we must preserve a0-a3 and s0. + // t1-t3 are available. We will build up the handler from the bottom by + // pushing on the stack. + // Set up the code object (t1) and the state (t2) for pushing. + unsigned state = + StackHandler::IndexField::encode(handler_index) | + StackHandler::KindField::encode(kind); + li(t1, Operand(CodeObject())); + li(t2, Operand(state)); + + // Push the frame pointer, context, state, and code object. + if (kind == StackHandler::JS_ENTRY) { + ASSERT_EQ(Smi::FromInt(0), 0); + // The second zero_reg indicates no context. + // The first zero_reg is the NULL frame pointer. + // The operands are reversed to match the order of MultiPush/Pop. + Push(zero_reg, zero_reg, t2, t1); } else { - // Must preserve a0-a3, and s0 (argv). - ASSERT(try_location == IN_JS_ENTRY); - // The frame pointer does not point to a JS frame so we save NULL - // for fp. We expect the code throwing an exception to check fp - // before dereferencing it to restore the context. - li(t0, Operand(StackHandler::ENTRY)); - - // Save the current handler as the next handler. - li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - lw(t1, MemOperand(t2)); - - ASSERT(Smi::FromInt(0) == 0); // Used for no context. - - addiu(sp, sp, -StackHandlerConstants::kSize); - sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset)); - sw(zero_reg, MemOperand(sp, StackHandlerConstants::kFPOffset)); - sw(zero_reg, MemOperand(sp, StackHandlerConstants::kContextOffset)); - sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset)); - sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset)); - - // Link this handler as the new current one. - sw(sp, MemOperand(t2)); + MultiPush(t1.bit() | t2.bit() | cp.bit() | fp.bit()); } + + // Link the current handler as the next handler. + li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); + lw(t1, MemOperand(t2)); + push(t1); + // Set this new handler as the current one. + sw(sp, MemOperand(t2)); } @@ -2398,19 +2625,36 @@ void MacroAssembler::PopTryHandler() { } -void MacroAssembler::Throw(Register value) { - // v0 is expected to hold the exception. - Move(v0, value); +void MacroAssembler::JumpToHandlerEntry() { + // Compute the handler entry address and jump to it. The handler table is + // a fixed array of (smi-tagged) code offsets. + // v0 = exception, a1 = code object, a2 = state. + lw(a3, FieldMemOperand(a1, Code::kHandlerTableOffset)); // Handler table. + Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + srl(a2, a2, StackHandler::kKindWidth); // Handler index. + sll(a2, a2, kPointerSizeLog2); + Addu(a2, a3, a2); + lw(a2, MemOperand(a2)); // Smi-tagged offset. + Addu(a1, a1, Operand(Code::kHeaderSize - kHeapObjectTag)); // Code start. + sra(t9, a2, kSmiTagSize); + Addu(t9, t9, a1); + Jump(t9); // Jump. +} + +void MacroAssembler::Throw(Register value) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // The exception is expected in v0. + Move(v0, value); - // Drop the sp to the top of the handler. + // Drop the stack pointer to the top of the top handler. li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); lw(sp, MemOperand(a3)); @@ -2419,44 +2663,19 @@ void MacroAssembler::Throw(Register value) { pop(a2); sw(a2, MemOperand(a3)); - // Restore context and frame pointer, discard state (a3). - MultiPop(a3.bit() | cp.bit() | fp.bit()); + // Get the code object (a1) and state (a2). Restore the context and frame + // pointer. + MultiPop(a1.bit() | a2.bit() | cp.bit() | fp.bit()); // If the handler is a JS frame, restore the context to the frame. - // (a3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any - // of them. + // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp + // or cp. Label done; - Branch(&done, eq, fp, Operand(zero_reg)); + Branch(&done, eq, cp, Operand(zero_reg)); sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); bind(&done); -#ifdef DEBUG - // When emitting debug_code, set ra as return address for the jump. - // 5 instructions: add: 1, pop: 2, jump: 2. - const int kOffsetRaInstructions = 5; - Label find_ra; - - if (emit_debug_code()) { - // Compute ra for the Jump(t9). - const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize; - - // This branch-and-link sequence is needed to get the current PC on mips, - // saved to the ra register. Then adjusted for instruction count. - bal(&find_ra); // bal exposes branch-delay. - nop(); // Branch delay slot nop. - bind(&find_ra); - addiu(ra, ra, kOffsetRaBytes); - } -#endif - - pop(t9); // 2 instructions: lw, add sp. - Jump(t9); // 2 instructions: jr, nop (in delay slot). - - if (emit_debug_code()) { - // Make sure that the expected number of instructions were generated. - ASSERT_EQ(kOffsetRaInstructions, - InstructionsGeneratedSince(&find_ra)); - } + JumpToHandlerEntry(); } @@ -2465,39 +2684,16 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - - // v0 is expected to hold the exception. - Move(v0, value); - - // Drop sp to the top stack handler. - li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - lw(sp, MemOperand(a3)); - - // Unwind the handlers until the ENTRY handler is found. - Label loop, done; - bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - lw(a2, MemOperand(sp, kStateOffset)); - Branch(&done, eq, a2, Operand(StackHandler::ENTRY)); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - lw(sp, MemOperand(sp, kNextOffset)); - jmp(&loop); - bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - pop(a2); - sw(a2, MemOperand(a3)); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + // The exception is expected in v0. if (type == OUT_OF_MEMORY) { // Set external caught exception to false. - ExternalReference external_caught( - Isolate::kExternalCaughtExceptionAddress, isolate()); + ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress, + isolate()); li(a0, Operand(false, RelocInfo::NONE)); li(a2, Operand(external_caught)); sw(a0, MemOperand(a2)); @@ -2506,45 +2702,37 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Failure* out_of_memory = Failure::OutOfMemoryException(); li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory))); li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate()))); + isolate()))); sw(v0, MemOperand(a2)); + } else if (!value.is(v0)) { + mov(v0, value); } - // Stack layout at this point. See also StackHandlerConstants. - // sp -> state (ENTRY) - // cp - // fp - // ra - - // Restore context and frame pointer, discard state (r2). - MultiPop(a2.bit() | cp.bit() | fp.bit()); - -#ifdef DEBUG - // When emitting debug_code, set ra as return address for the jump. - // 5 instructions: add: 1, pop: 2, jump: 2. - const int kOffsetRaInstructions = 5; - Label find_ra; + // Drop the stack pointer to the top of the top stack handler. + li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); + lw(sp, MemOperand(a3)); - if (emit_debug_code()) { - // Compute ra for the Jump(t9). - const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize; + // Unwind the handlers until the ENTRY handler is found. + Label fetch_next, check_kind; + jmp(&check_kind); + bind(&fetch_next); + lw(sp, MemOperand(sp, StackHandlerConstants::kNextOffset)); + + bind(&check_kind); + STATIC_ASSERT(StackHandler::JS_ENTRY == 0); + lw(a2, MemOperand(sp, StackHandlerConstants::kStateOffset)); + And(a2, a2, Operand(StackHandler::KindField::kMask)); + Branch(&fetch_next, ne, a2, Operand(zero_reg)); + + // Set the top handler address to next handler past the top ENTRY handler. + pop(a2); + sw(a2, MemOperand(a3)); - // This branch-and-link sequence is needed to get the current PC on mips, - // saved to the ra register. Then adjusted for instruction count. - bal(&find_ra); // bal exposes branch-delay slot. - nop(); // Branch delay slot nop. - bind(&find_ra); - addiu(ra, ra, kOffsetRaBytes); - } -#endif - pop(t9); // 2 instructions: lw, add sp. - Jump(t9); // 2 instructions: jr, nop (in delay slot). + // Get the code object (a1) and state (a2). Clear the context and frame + // pointer (0 was saved in the handler). + MultiPop(a1.bit() | a2.bit() | cp.bit() | fp.bit()); - if (emit_debug_code()) { - // Make sure that the expected number of instructions were generated. - ASSERT_EQ(kOffsetRaInstructions, - InstructionsGeneratedSince(&find_ra)); - } + JumpToHandlerEntry(); } @@ -2647,6 +2835,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, ASSERT(!result.is(scratch1)); ASSERT(!result.is(scratch2)); ASSERT(!scratch1.is(scratch2)); + ASSERT(!object_size.is(t9)); ASSERT(!scratch1.is(t9) && !scratch2.is(t9) && !result.is(t9)); // Check relative positions of allocation top and limit addresses. @@ -2984,26 +3173,185 @@ void MacroAssembler::CopyBytes(Register src, } +void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler) { + Label loop, entry; + Branch(&entry); + bind(&loop); + sw(filler, MemOperand(start_offset)); + Addu(start_offset, start_offset, kPointerSize); + bind(&entry); + Branch(&loop, lt, start_offset, Operand(end_offset)); +} + + void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue)); } +void MacroAssembler::CheckFastObjectElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + Branch(fail, ls, scratch, + Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Branch(fail, hi, scratch, + Operand(Map::kMaximumBitField2FastElementValue)); +} + + +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + Branch(fail, hi, scratch, + Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); +} + + +void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, + Register key_reg, + Register receiver_reg, + Register elements_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Label* fail) { + Label smi_value, maybe_nan, have_double_value, is_nan, done; + Register mantissa_reg = scratch2; + Register exponent_reg = scratch3; + + // Handle smi values specially. + JumpIfSmi(value_reg, &smi_value); + + // Ensure that the object is a heap number + CheckMap(value_reg, + scratch1, + isolate()->factory()->heap_number_map(), + fail, + DONT_DO_SMI_CHECK); + + // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000 + // in the exponent. + li(scratch1, Operand(kNaNOrInfinityLowerBoundUpper32)); + lw(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset)); + Branch(&maybe_nan, ge, exponent_reg, Operand(scratch1)); + + lw(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + + bind(&have_double_value); + sll(scratch1, key_reg, kDoubleSizeLog2 - kSmiTagSize); + Addu(scratch1, scratch1, elements_reg); + sw(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize)); + uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); + sw(exponent_reg, FieldMemOperand(scratch1, offset)); + jmp(&done); + + bind(&maybe_nan); + // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise + // it's an Infinity, and the non-NaN code path applies. + Branch(&is_nan, gt, exponent_reg, Operand(scratch1)); + lw(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + Branch(&have_double_value, eq, mantissa_reg, Operand(zero_reg)); + bind(&is_nan); + // Load canonical NaN for storing into the double array. + uint64_t nan_int64 = BitCast<uint64_t>( + FixedDoubleArray::canonical_not_the_hole_nan_as_double()); + li(mantissa_reg, Operand(static_cast<uint32_t>(nan_int64))); + li(exponent_reg, Operand(static_cast<uint32_t>(nan_int64 >> 32))); + jmp(&have_double_value); + + bind(&smi_value); + Addu(scratch1, elements_reg, + Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + sll(scratch2, key_reg, kDoubleSizeLog2 - kSmiTagSize); + Addu(scratch1, scratch1, scratch2); + // scratch1 is now effective address of the double element + + FloatingPointHelper::Destination destination; + if (CpuFeatures::IsSupported(FPU)) { + destination = FloatingPointHelper::kFPURegisters; + } else { + destination = FloatingPointHelper::kCoreRegisters; + } + + Register untagged_value = receiver_reg; + SmiUntag(untagged_value, value_reg); + FloatingPointHelper::ConvertIntToDouble(this, + untagged_value, + destination, + f0, + mantissa_reg, + exponent_reg, + scratch4, + f2); + if (destination == FloatingPointHelper::kFPURegisters) { + CpuFeatures::Scope scope(FPU); + sdc1(f0, MemOperand(scratch1, 0)); + } else { + sw(mantissa_reg, MemOperand(scratch1, 0)); + sw(exponent_reg, MemOperand(scratch1, Register::kSizeInBytes)); + } + bind(&done); +} + + +void MacroAssembler::CompareMapAndBranch(Register obj, + Register scratch, + Handle<Map> map, + Label* early_success, + Condition cond, + Label* branch_to, + CompareMapMode mode) { + lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); + Operand right = Operand(map); + if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { + Map* transitioned_fast_element_map( + map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); + ASSERT(transitioned_fast_element_map == NULL || + map->elements_kind() != FAST_ELEMENTS); + if (transitioned_fast_element_map != NULL) { + Branch(early_success, eq, scratch, right); + right = Operand(Handle<Map>(transitioned_fast_element_map)); + } + + Map* transitioned_double_map( + map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); + ASSERT(transitioned_double_map == NULL || + map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); + if (transitioned_double_map != NULL) { + Branch(early_success, eq, scratch, right); + right = Operand(Handle<Map>(transitioned_double_map)); + } + } + + Branch(branch_to, cond, scratch, right); +} + + void MacroAssembler::CheckMap(Register obj, Register scratch, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type) { + SmiCheckType smi_check_type, + CompareMapMode mode) { if (smi_check_type == DO_SMI_CHECK) { JumpIfSmi(obj, fail); } - lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - li(at, Operand(map)); - Branch(fail, ne, scratch, Operand(at)); + Label success; + CompareMapAndBranch(obj, scratch, map, &success, ne, fail, mode); + bind(&success); } @@ -3110,10 +3458,12 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, Handle<Code> code_constant, Register code_reg, Label* done, + bool* definitely_mismatches, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { bool definitely_matches = false; + *definitely_mismatches = false; Label regular_invoke; // Check whether the expected and actual arguments count match. If not, @@ -3144,6 +3494,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, // arguments. definitely_matches = true; } else { + *definitely_mismatches = true; li(a2, Operand(expected.immediate())); } } @@ -3167,7 +3518,9 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, SetCallKind(t1, call_kind); Call(adaptor); call_wrapper.AfterCall(); - jmp(done); + if (!*definitely_mismatches) { + Branch(done); + } } else { SetCallKind(t1, call_kind); Jump(adaptor, RelocInfo::CODE_TARGET); @@ -3183,21 +3536,30 @@ void MacroAssembler::InvokeCode(Register code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; - InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag, + bool definitely_mismatches = false; + InvokePrologue(expected, actual, Handle<Code>::null(), code, + &done, &definitely_mismatches, flag, call_wrapper, call_kind); - if (flag == CALL_FUNCTION) { - SetCallKind(t1, call_kind); - Call(code); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(t1, call_kind); - Jump(code); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + SetCallKind(t1, call_kind); + Call(code); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(t1, call_kind); + Jump(code); + } + // Continue here if InvokePrologue does handle the invocation due to + // mismatched parameter counts. + bind(&done); } - // Continue here if InvokePrologue does handle the invocation due to - // mismatched parameter counts. - bind(&done); } @@ -3207,20 +3569,27 @@ void MacroAssembler::InvokeCode(Handle<Code> code, RelocInfo::Mode rmode, InvokeFlag flag, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; - InvokePrologue(expected, actual, code, no_reg, &done, flag, + bool definitely_mismatches = false; + InvokePrologue(expected, actual, code, no_reg, + &done, &definitely_mismatches, flag, NullCallWrapper(), call_kind); - if (flag == CALL_FUNCTION) { - SetCallKind(t1, call_kind); - Call(code, rmode); - } else { - SetCallKind(t1, call_kind); - Jump(code, rmode); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + SetCallKind(t1, call_kind); + Call(code, rmode); + } else { + SetCallKind(t1, call_kind); + Jump(code, rmode); + } + // Continue here if InvokePrologue does handle the invocation due to + // mismatched parameter counts. + bind(&done); } - // Continue here if InvokePrologue does handle the invocation due to - // mismatched parameter counts. - bind(&done); } @@ -3229,6 +3598,9 @@ void MacroAssembler::InvokeFunction(Register function, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + // Contract with called JS functions requires that function is passed in a1. ASSERT(function.is(a1)); Register expected_reg = a2; @@ -3247,24 +3619,24 @@ void MacroAssembler::InvokeFunction(Register function, } -void MacroAssembler::InvokeFunction(JSFunction* function, +void MacroAssembler::InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper, CallKind call_kind) { - ASSERT(function->is_compiled()); + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); // Get the function and setup the context. - li(a1, Operand(Handle<JSFunction>(function))); + LoadHeapObject(a1, function); lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); - // Invoke the cached code. - Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); - if (V8::UseCrankshaft()) { - UNIMPLEMENTED_MIPS(); - } else { - InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag, call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + lw(a3, FieldMemOperand(a1, JSFunction::kCodeEntryOffset)); + InvokeCode(a3, expected, actual, flag, call_wrapper, call_kind); } @@ -3305,7 +3677,8 @@ void MacroAssembler::IsObjectJSStringType(Register object, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. JumpIfSmi(function, miss); @@ -3313,6 +3686,16 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, GetObjectType(function, result, scratch); Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE)); + if (miss_on_bound_function) { + lw(scratch, + FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); + lw(scratch, + FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); + And(scratch, scratch, + Operand(Smi::FromInt(1 << SharedFunctionInfo::kBoundFunction))); + Branch(miss, ne, scratch, Operand(zero_reg)); + } + // Make sure that the function has an instance prototype. Label non_instance; lbu(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); @@ -3361,51 +3744,24 @@ void MacroAssembler::GetObjectType(Register object, void MacroAssembler::CallStub(CodeStub* stub, Condition cond, Register r1, const Operand& r2) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. Call(stub->GetCode(), RelocInfo::CODE_TARGET, kNoASTId, cond, r1, r2); } -MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond, - Register r1, const Operand& r2) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Call(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, - kNoASTId, cond, r1, r2); - return result; -} - - void MacroAssembler::TailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe()); Jump(stub->GetCode(), RelocInfo::CODE_TARGET); } -MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub, - Condition cond, - Register r1, - const Operand& r2) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. - Object* result; - { MaybeObject* maybe_result = stub->TryGetCode(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Jump(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond, r1, r2); - return result; -} - - static int AddressOffset(ExternalReference ref0, ExternalReference ref1) { return ref0.address() - ref1.address(); } -MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( - ExternalReference function, int stack_space) { +void MacroAssembler::CallApiFunctionAndReturn(ExternalReference function, + int stack_space) { ExternalReference next_address = ExternalReference::handle_scope_next_address(); const int kNextOffset = 0; @@ -3476,11 +3832,10 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( Ret(); bind(&promote_scheduled_exception); - MaybeObject* result = TryTailCallExternalReference( - ExternalReference(Runtime::kPromoteScheduledException, isolate()), 0, 1); - if (result->IsFailure()) { - return result; - } + TailCallExternalReference( + ExternalReference(Runtime::kPromoteScheduledException, isolate()), + 0, + 1); // HandleScope limit has changed. Delete allocated extensions. bind(&delete_allocated_handles); @@ -3493,8 +3848,12 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( 1); mov(v0, s0); jmp(&leave_exit_frame); +} + - return result; +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { + if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false; + return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(); } @@ -3578,7 +3937,16 @@ void MacroAssembler::AdduAndCheckForOverflow(Register dst, ASSERT(!overflow_dst.is(scratch)); ASSERT(!overflow_dst.is(left)); ASSERT(!overflow_dst.is(right)); - ASSERT(!left.is(right)); + + if (left.is(right) && dst.is(left)) { + ASSERT(!dst.is(t9)); + ASSERT(!scratch.is(t9)); + ASSERT(!left.is(t9)); + ASSERT(!right.is(t9)); + ASSERT(!overflow_dst.is(t9)); + mov(t9, right); + right = t9; + } if (dst.is(left)) { mov(scratch, left); // Preserve left. @@ -3611,10 +3979,17 @@ void MacroAssembler::SubuAndCheckForOverflow(Register dst, ASSERT(!overflow_dst.is(scratch)); ASSERT(!overflow_dst.is(left)); ASSERT(!overflow_dst.is(right)); - ASSERT(!left.is(right)); ASSERT(!scratch.is(left)); ASSERT(!scratch.is(right)); + // This happens with some crankshaft code. Since Subu works fine if + // left == right, let's not make that restriction here. + if (left.is(right)) { + mov(dst, zero_reg); + mov(overflow_dst, zero_reg); + return; + } + if (dst.is(left)) { mov(scratch, left); // Preserve left. subu(dst, left, right); // Left is overwritten. @@ -3663,8 +4038,7 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { const Runtime::Function* function = Runtime::FunctionForId(id); li(a0, Operand(function->nargs)); li(a1, Operand(ExternalReference(function, isolate()))); - CEntryStub stub(1); - stub.SaveDoubles(); + CEntryStub stub(1, kSaveFPRegs); CallStub(&stub); } @@ -3696,17 +4070,6 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } -MaybeObject* MacroAssembler::TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size) { - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - li(a0, num_arguments); - return TryJumpToExternalReference(ext); -} - - void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -3723,17 +4086,12 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) { } -MaybeObject* MacroAssembler::TryJumpToExternalReference( - const ExternalReference& builtin) { - li(a1, Operand(builtin)); - CEntryStub stub(1); - return TryTailCallStub(&stub); -} - - void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, const CallWrapper& call_wrapper) { + // You can't call a builtin without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + GetBuiltinEntry(t9, id); if (flag == CALL_FUNCTION) { call_wrapper.BeforeCall(CallSize(t9)); @@ -3866,14 +4224,20 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif - // Disable stub call restrictions to always allow calls to abort. - AllowStubCallsScope allow_scope(this, true); li(a0, Operand(p0)); push(a0); li(a0, Operand(Smi::FromInt(p1 - p0))); push(a0); - CallRuntime(Runtime::kAbort, 2); + // Disable stub call restrictions to always allow calls to abort. + if (!has_frame_) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + CallRuntime(Runtime::kAbort, 2); + } else { + CallRuntime(Runtime::kAbort, 2); + } // Will not return here. if (is_trampoline_pool_blocked()) { // If the calling code cares about the exact number of @@ -3907,6 +4271,46 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match) { + // Load the global or builtins object from the current context. + lw(scratch, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + lw(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); + + // Check that the function's map is the same as the expected cached map. + int expected_index = + Context::GetContextMapIndexFromElementsKind(expected_kind); + lw(at, MemOperand(scratch, Context::SlotOffset(expected_index))); + Branch(no_map_match, ne, map_in_out, Operand(at)); + + // Use the transitioned cached map. + int trans_index = + Context::GetContextMapIndexFromElementsKind(transitioned_kind); + lw(map_in_out, MemOperand(scratch, Context::SlotOffset(trans_index))); +} + + +void MacroAssembler::LoadInitialArrayMap( + Register function_in, Register scratch, Register map_out) { + ASSERT(!function_in.is(map_out)); + Label done; + lw(map_out, FieldMemOperand(function_in, + JSFunction::kPrototypeOrInitialMapOffset)); + if (!FLAG_smi_only_arrays) { + LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + map_out, + scratch, + &done); + } + bind(&done); +} + + void MacroAssembler::LoadGlobalFunction(int index, Register function) { // Load the global or builtins object from the current context. lw(function, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); @@ -3957,7 +4361,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) { - // Setup the frame structure on the stack. + // Set up the frame structure on the stack. STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement); STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset); STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset); @@ -3975,7 +4379,7 @@ void MacroAssembler::EnterExitFrame(bool save_doubles, addiu(sp, sp, -4 * kPointerSize); sw(ra, MemOperand(sp, 3 * kPointerSize)); sw(fp, MemOperand(sp, 2 * kPointerSize)); - addiu(fp, sp, 2 * kPointerSize); // Setup new frame pointer. + addiu(fp, sp, 2 * kPointerSize); // Set up new frame pointer. if (emit_debug_code()) { sw(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset)); @@ -4120,14 +4524,71 @@ void MacroAssembler::JumpIfNotPowerOfTwoOrZero( } +void MacroAssembler::SmiTagCheckOverflow(Register reg, Register overflow) { + ASSERT(!reg.is(overflow)); + mov(overflow, reg); // Save original value. + SmiTag(reg); + xor_(overflow, overflow, reg); // Overflow if (value ^ 2 * value) < 0. +} + + +void MacroAssembler::SmiTagCheckOverflow(Register dst, + Register src, + Register overflow) { + if (dst.is(src)) { + // Fall back to slower case. + SmiTagCheckOverflow(dst, overflow); + } else { + ASSERT(!dst.is(src)); + ASSERT(!dst.is(overflow)); + ASSERT(!src.is(overflow)); + SmiTag(dst, src); + xor_(overflow, dst, src); // Overflow if (value ^ 2 * value) < 0. + } +} + + +void MacroAssembler::UntagAndJumpIfSmi(Register dst, + Register src, + Label* smi_case) { + JumpIfSmi(src, smi_case, at, USE_DELAY_SLOT); + SmiUntag(dst, src); +} + + +void MacroAssembler::UntagAndJumpIfNotSmi(Register dst, + Register src, + Label* non_smi_case) { + JumpIfNotSmi(src, non_smi_case, at, USE_DELAY_SLOT); + SmiUntag(dst, src); +} + +void MacroAssembler::JumpIfSmi(Register value, + Label* smi_label, + Register scratch, + BranchDelaySlot bd) { + ASSERT_EQ(0, kSmiTag); + andi(scratch, value, kSmiTagMask); + Branch(bd, smi_label, eq, scratch, Operand(zero_reg)); +} + +void MacroAssembler::JumpIfNotSmi(Register value, + Label* not_smi_label, + Register scratch, + BranchDelaySlot bd) { + ASSERT_EQ(0, kSmiTag); + andi(scratch, value, kSmiTagMask); + Branch(bd, not_smi_label, ne, scratch, Operand(zero_reg)); +} + + void MacroAssembler::JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi) { STATIC_ASSERT(kSmiTag == 0); ASSERT_EQ(1, kSmiTagMask); or_(at, reg1, reg2); - andi(at, at, kSmiTagMask); - Branch(on_not_both_smi, ne, at, Operand(zero_reg)); + JumpIfNotSmi(at, on_not_both_smi); } @@ -4138,8 +4599,7 @@ void MacroAssembler::JumpIfEitherSmi(Register reg1, ASSERT_EQ(1, kSmiTagMask); // Both Smi tags must be 1 (not Smi). and_(at, reg1, reg2); - andi(at, at, kSmiTagMask); - Branch(on_either_smi, eq, at, Operand(zero_reg)); + JumpIfSmi(at, on_either_smi); } @@ -4217,8 +4677,7 @@ void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first, // Check that neither is a smi. STATIC_ASSERT(kSmiTag == 0); And(scratch1, first, Operand(second)); - And(scratch1, scratch1, Operand(kSmiTagMask)); - Branch(failure, eq, scratch1, Operand(zero_reg)); + JumpIfSmi(scratch1, failure); JumpIfNonSmisNotBothSequentialAsciiStrings(first, second, scratch1, @@ -4257,7 +4716,23 @@ void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type, static const int kRegisterPassedArguments = 4; -void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { +int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments, + int num_double_arguments) { + int stack_passed_words = 0; + num_reg_arguments += 2 * num_double_arguments; + + // Up to four simple arguments are passed in registers a0..a3. + if (num_reg_arguments > kRegisterPassedArguments) { + stack_passed_words += num_reg_arguments - kRegisterPassedArguments; + } + stack_passed_words += kCArgSlotCount; + return stack_passed_words; +} + + +void MacroAssembler::PrepareCallCFunction(int num_reg_arguments, + int num_double_arguments, + Register scratch) { int frame_alignment = ActivationFrameAlignment(); // Up to four simple arguments are passed in registers a0..a3. @@ -4265,9 +4740,8 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { // mips, even though those argument slots are not normally used. // Remaining arguments are pushed on the stack, above (higher address than) // the argument slots. - int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ? - 0 : num_arguments - kRegisterPassedArguments) + - kCArgSlotCount; + int stack_passed_arguments = CalculateStackPassedWords( + num_reg_arguments, num_double_arguments); if (frame_alignment > kPointerSize) { // Make stack end at alignment and make room for num_arguments - 4 words // and the original value of sp. @@ -4282,26 +4756,43 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { } +void MacroAssembler::PrepareCallCFunction(int num_reg_arguments, + Register scratch) { + PrepareCallCFunction(num_reg_arguments, 0, scratch); +} + + +void MacroAssembler::CallCFunction(ExternalReference function, + int num_reg_arguments, + int num_double_arguments) { + li(t8, Operand(function)); + CallCFunctionHelper(t8, num_reg_arguments, num_double_arguments); +} + + +void MacroAssembler::CallCFunction(Register function, + int num_reg_arguments, + int num_double_arguments) { + CallCFunctionHelper(function, num_reg_arguments, num_double_arguments); +} + + void MacroAssembler::CallCFunction(ExternalReference function, int num_arguments) { - CallCFunctionHelper(no_reg, function, t8, num_arguments); + CallCFunction(function, num_arguments, 0); } void MacroAssembler::CallCFunction(Register function, - Register scratch, int num_arguments) { - CallCFunctionHelper(function, - ExternalReference::the_hole_value_location(isolate()), - scratch, - num_arguments); + CallCFunction(function, num_arguments, 0); } void MacroAssembler::CallCFunctionHelper(Register function, - ExternalReference function_reference, - Register scratch, - int num_arguments) { + int num_reg_arguments, + int num_double_arguments) { + ASSERT(has_frame()); // Make sure that the stack is aligned before calling a C function unless // running in the simulator. The simulator has its own alignment check which // provides more information. @@ -4329,19 +4820,15 @@ void MacroAssembler::CallCFunctionHelper(Register function, // allow preemption, so the return address in the link register // stays correct. - if (function.is(no_reg)) { - function = t9; - li(function, Operand(function_reference)); - } else if (!function.is(t9)) { + if (!function.is(t9)) { mov(t9, function); function = t9; } Call(function); - int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ? - 0 : num_arguments - kRegisterPassedArguments) + - kCArgSlotCount; + int stack_passed_arguments = CalculateStackPassedWords( + num_reg_arguments, num_double_arguments); if (OS::ActivationFrameAlignment() > kPointerSize) { lw(sp, MemOperand(sp, stack_passed_arguments * kPointerSize)); @@ -4354,6 +4841,263 @@ void MacroAssembler::CallCFunctionHelper(Register function, #undef BRANCH_ARGS_CHECK +void MacroAssembler::PatchRelocatedValue(Register li_location, + Register scratch, + Register new_value) { + lw(scratch, MemOperand(li_location)); + // At this point scratch is a lui(at, ...) instruction. + if (emit_debug_code()) { + And(scratch, scratch, kOpcodeMask); + Check(eq, "The instruction to patch should be a lui.", + scratch, Operand(LUI)); + lw(scratch, MemOperand(li_location)); + } + srl(t9, new_value, kImm16Bits); + Ins(scratch, t9, 0, kImm16Bits); + sw(scratch, MemOperand(li_location)); + + lw(scratch, MemOperand(li_location, kInstrSize)); + // scratch is now ori(at, ...). + if (emit_debug_code()) { + And(scratch, scratch, kOpcodeMask); + Check(eq, "The instruction to patch should be an ori.", + scratch, Operand(ORI)); + lw(scratch, MemOperand(li_location, kInstrSize)); + } + Ins(scratch, new_value, 0, kImm16Bits); + sw(scratch, MemOperand(li_location, kInstrSize)); + + // Update the I-cache so the new lui and ori can be executed. + FlushICache(li_location, 2); +} + +void MacroAssembler::GetRelocatedValue(Register li_location, + Register value, + Register scratch) { + lw(value, MemOperand(li_location)); + if (emit_debug_code()) { + And(value, value, kOpcodeMask); + Check(eq, "The instruction should be a lui.", + value, Operand(LUI)); + lw(value, MemOperand(li_location)); + } + + // value now holds a lui instruction. Extract the immediate. + sll(value, value, kImm16Bits); + + lw(scratch, MemOperand(li_location, kInstrSize)); + if (emit_debug_code()) { + And(scratch, scratch, kOpcodeMask); + Check(eq, "The instruction should be an ori.", + scratch, Operand(ORI)); + lw(scratch, MemOperand(li_location, kInstrSize)); + } + // "scratch" now holds an ori instruction. Extract the immediate. + andi(scratch, scratch, kImm16Mask); + + // Merge the results. + or_(value, value, scratch); +} + + +void MacroAssembler::CheckPageFlag( + Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met) { + And(scratch, object, Operand(~Page::kPageAlignmentMask)); + lw(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); + And(scratch, scratch, Operand(mask)); + Branch(condition_met, cc, scratch, Operand(zero_reg)); +} + + +void MacroAssembler::JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black) { + HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern. + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); +} + + +void MacroAssembler::HasColor(Register object, + Register bitmap_scratch, + Register mask_scratch, + Label* has_color, + int first_bit, + int second_bit) { + ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, t8)); + ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, t9)); + + GetMarkBits(object, bitmap_scratch, mask_scratch); + + Label other_color, word_boundary; + lw(t9, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + And(t8, t9, Operand(mask_scratch)); + Branch(&other_color, first_bit == 1 ? eq : ne, t8, Operand(zero_reg)); + // Shift left 1 by adding. + Addu(mask_scratch, mask_scratch, Operand(mask_scratch)); + Branch(&word_boundary, eq, mask_scratch, Operand(zero_reg)); + And(t8, t9, Operand(mask_scratch)); + Branch(has_color, second_bit == 1 ? ne : eq, t8, Operand(zero_reg)); + jmp(&other_color); + + bind(&word_boundary); + lw(t9, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize)); + And(t9, t9, Operand(1)); + Branch(has_color, second_bit == 1 ? ne : eq, t9, Operand(zero_reg)); + bind(&other_color); +} + + +// Detect some, but not all, common pointer-free objects. This is used by the +// incremental write barrier which doesn't care about oddballs (they are always +// marked black immediately so this code is not hit). +void MacroAssembler::JumpIfDataObject(Register value, + Register scratch, + Label* not_data_object) { + ASSERT(!AreAliased(value, scratch, t8, no_reg)); + Label is_data_object; + lw(scratch, FieldMemOperand(value, HeapObject::kMapOffset)); + LoadRoot(t8, Heap::kHeapNumberMapRootIndex); + Branch(&is_data_object, eq, t8, Operand(scratch)); + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + lbu(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); + And(t8, scratch, Operand(kIsIndirectStringMask | kIsNotStringMask)); + Branch(not_data_object, ne, t8, Operand(zero_reg)); + bind(&is_data_object); +} + + +void MacroAssembler::GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg) { + ASSERT(!AreAliased(addr_reg, bitmap_reg, mask_reg, no_reg)); + And(bitmap_reg, addr_reg, Operand(~Page::kPageAlignmentMask)); + Ext(mask_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); + const int kLowBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; + Ext(t8, addr_reg, kLowBits, kPageSizeBits - kLowBits); + sll(t8, t8, kPointerSizeLog2); + Addu(bitmap_reg, bitmap_reg, t8); + li(t8, Operand(1)); + sllv(mask_reg, t8, mask_reg); +} + + +void MacroAssembler::EnsureNotWhite( + Register value, + Register bitmap_scratch, + Register mask_scratch, + Register load_scratch, + Label* value_is_white_and_not_data) { + ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, t8)); + GetMarkBits(value, bitmap_scratch, mask_scratch); + + // If the value is black or grey we don't need to do anything. + ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); + ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + Label done; + + // Since both black and grey have a 1 in the first position and white does + // not have a 1 there we only need to check one bit. + lw(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + And(t8, mask_scratch, load_scratch); + Branch(&done, ne, t8, Operand(zero_reg)); + + if (emit_debug_code()) { + // Check for impossible bit pattern. + Label ok; + // sll may overflow, making the check conservative. + sll(t8, mask_scratch, 1); + And(t8, load_scratch, t8); + Branch(&ok, eq, t8, Operand(zero_reg)); + stop("Impossible marking bit pattern"); + bind(&ok); + } + + // Value is white. We check whether it is data that doesn't need scanning. + // Currently only checks for HeapNumber and non-cons strings. + Register map = load_scratch; // Holds map while checking type. + Register length = load_scratch; // Holds length of object after testing type. + Label is_data_object; + + // Check for heap-number + lw(map, FieldMemOperand(value, HeapObject::kMapOffset)); + LoadRoot(t8, Heap::kHeapNumberMapRootIndex); + { + Label skip; + Branch(&skip, ne, t8, Operand(map)); + li(length, HeapNumber::kSize); + Branch(&is_data_object); + bind(&skip); + } + + // Check for strings. + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + Register instance_type = load_scratch; + lbu(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); + And(t8, instance_type, Operand(kIsIndirectStringMask | kIsNotStringMask)); + Branch(value_is_white_and_not_data, ne, t8, Operand(zero_reg)); + // It's a non-indirect (non-cons and non-slice) string. + // If it's external, the length is just ExternalString::kSize. + // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). + // External strings are the only ones with the kExternalStringTag bit + // set. + ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); + ASSERT_EQ(0, kConsStringTag & kExternalStringTag); + And(t8, instance_type, Operand(kExternalStringTag)); + { + Label skip; + Branch(&skip, eq, t8, Operand(zero_reg)); + li(length, ExternalString::kSize); + Branch(&is_data_object); + bind(&skip); + } + + // Sequential string, either ASCII or UC16. + // For ASCII (char-size of 1) we shift the smi tag away to get the length. + // For UC16 (char-size of 2) we just leave the smi tag in place, thereby + // getting the length multiplied by 2. + ASSERT(kAsciiStringTag == 4 && kStringEncodingMask == 4); + ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + lw(t9, FieldMemOperand(value, String::kLengthOffset)); + And(t8, instance_type, Operand(kStringEncodingMask)); + { + Label skip; + Branch(&skip, eq, t8, Operand(zero_reg)); + srl(t9, t9, 1); + bind(&skip); + } + Addu(length, t9, Operand(SeqString::kHeaderSize + kObjectAlignmentMask)); + And(length, length, Operand(~kObjectAlignmentMask)); + + bind(&is_data_object); + // Value is a data object, and it is white. Mark it black. Since we know + // that the object is white we can make it black by flipping one bit. + lw(t8, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + Or(t8, t8, Operand(mask_scratch)); + sw(t8, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + + And(bitmap_scratch, bitmap_scratch, Operand(~Page::kPageAlignmentMask)); + lw(t8, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + Addu(t8, t8, Operand(length)); + sw(t8, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + + bind(&done); +} + + void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors) { lw(descriptors, @@ -4365,6 +5109,60 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) { + ASSERT(!output_reg.is(input_reg)); + Label done; + li(output_reg, Operand(255)); + // Normal branch: nop in delay slot. + Branch(&done, gt, input_reg, Operand(output_reg)); + // Use delay slot in this branch. + Branch(USE_DELAY_SLOT, &done, lt, input_reg, Operand(zero_reg)); + mov(output_reg, zero_reg); // In delay slot. + mov(output_reg, input_reg); // Value is in range 0..255. + bind(&done); +} + + +void MacroAssembler::ClampDoubleToUint8(Register result_reg, + DoubleRegister input_reg, + DoubleRegister temp_double_reg) { + Label above_zero; + Label done; + Label in_bounds; + + Move(temp_double_reg, 0.0); + BranchF(&above_zero, NULL, gt, input_reg, temp_double_reg); + + // Double value is less than zero, NaN or Inf, return 0. + mov(result_reg, zero_reg); + Branch(&done); + + // Double value is >= 255, return 255. + bind(&above_zero); + Move(temp_double_reg, 255.0); + BranchF(&in_bounds, NULL, le, input_reg, temp_double_reg); + li(result_reg, Operand(255)); + Branch(&done); + + // In 0-255 range, round and truncate. + bind(&in_bounds); + round_w_d(temp_double_reg, input_reg); + mfc1(result_reg, temp_double_reg); + bind(&done); +} + + +bool AreAliased(Register r1, Register r2, Register r3, Register r4) { + if (r1.is(r2)) return true; + if (r1.is(r3)) return true; + if (r1.is(r4)) return true; + if (r2.is(r3)) return true; + if (r2.is(r4)) return true; + if (r3.is(r4)) return true; + return false; +} + + CodePatcher::CodePatcher(byte* address, int instructions) : address_(address), instructions_(instructions), diff --git a/deps/v8/src/mips/macro-assembler-mips.h b/deps/v8/src/mips/macro-assembler-mips.h index c968ffcd15..69b3f9d63a 100644 --- a/deps/v8/src/mips/macro-assembler-mips.h +++ b/deps/v8/src/mips/macro-assembler-mips.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -50,15 +50,6 @@ class JumpTarget; // trying to update gp register for position-independent-code. Whenever // MIPS generated code calls C code, it must be via t9 register. -// Registers aliases -// cp is assumed to be a callee saved register. -const Register roots = s6; // Roots array pointer. -const Register cp = s7; // JavaScript context pointer. -const Register fp = s8_fp; // Alias for fp. -// Registers used for condition evaluation. -const Register condReg1 = s4; -const Register condReg2 = s5; - // Flags used for the AllocateInNewSpace functions. enum AllocationFlags { @@ -90,6 +81,43 @@ enum BranchDelaySlot { PROTECT }; + +enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; +enum RAStatus { kRAHasNotBeenSaved, kRAHasBeenSaved }; + +bool AreAliased(Register r1, Register r2, Register r3, Register r4); + + +// ----------------------------------------------------------------------------- +// Static helper functions. + +inline MemOperand ContextOperand(Register context, int index) { + return MemOperand(context, Context::SlotOffset(index)); +} + + +inline MemOperand GlobalObjectOperand() { + return ContextOperand(cp, Context::GLOBAL_INDEX); +} + + +// Generate a MemOperand for loading a field from an object. +inline MemOperand FieldMemOperand(Register object, int offset) { + return MemOperand(object, offset - kHeapObjectTag); +} + + +// Generate a MemOperand for storing arguments 5..N on the stack +// when calling CallCFunction(). +inline MemOperand CFunctionArgumentOperand(int index) { + ASSERT(index > kCArgSlotCount); + // Argument 5 takes the slot just past the four Arg-slots. + int offset = (index - 5) * kPointerSize + kCArgsSlotsSize; + return MemOperand(sp, offset); +} + + // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: @@ -138,21 +166,22 @@ class MacroAssembler: public Assembler { void Jump(intptr_t target, RelocInfo::Mode rmode, COND_ARGS); void Jump(Address target, RelocInfo::Mode rmode, COND_ARGS); void Jump(Handle<Code> code, RelocInfo::Mode rmode, COND_ARGS); - int CallSize(Register target, COND_ARGS); + static int CallSize(Register target, COND_ARGS); void Call(Register target, COND_ARGS); - int CallSize(Address target, RelocInfo::Mode rmode, COND_ARGS); + static int CallSize(Address target, RelocInfo::Mode rmode, COND_ARGS); void Call(Address target, RelocInfo::Mode rmode, COND_ARGS); - int CallSize(Handle<Code> code, - RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, - unsigned ast_id = kNoASTId, - COND_ARGS); + static int CallSize(Handle<Code> code, + RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, + unsigned ast_id = kNoASTId, + COND_ARGS); void Call(Handle<Code> code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, unsigned ast_id = kNoASTId, COND_ARGS); void Ret(COND_ARGS); - inline void Ret(BranchDelaySlot bd) { - Ret(al, zero_reg, Operand(zero_reg), bd); + inline void Ret(BranchDelaySlot bd, Condition cond = al, + Register rs = zero_reg, const Operand& rt = Operand(zero_reg)) { + Ret(cond, rs, rt, bd); } #undef COND_ARGS @@ -197,6 +226,8 @@ class MacroAssembler: public Assembler { mtc1(src_high, FPURegister::from_code(dst.code() + 1)); } + void Move(FPURegister dst, double imm); + // Jump unconditionally to given label. // We NEED a nop in the branch delay slot, as it used by v8, for example in // CodeGenerator::ProcessDeferred(). @@ -206,6 +237,7 @@ class MacroAssembler: public Assembler { Branch(L); } + // Load an object from the root table. void LoadRoot(Register destination, Heap::RootListIndex index); @@ -220,40 +252,137 @@ class MacroAssembler: public Assembler { Heap::RootListIndex index, Condition cond, Register src1, const Operand& src2); + void LoadHeapObject(Register dst, Handle<HeapObject> object); - // Check if object is in new space. - // scratch can be object itself, but it will be clobbered. - void InNewSpace(Register object, - Register scratch, - Condition cc, // eq for new space, ne otherwise. - Label* branch); + void LoadObject(Register result, Handle<Object> object) { + if (object->IsHeapObject()) { + LoadHeapObject(result, Handle<HeapObject>::cast(object)); + } else { + li(result, object); + } + } + // --------------------------------------------------------------------------- + // GC Support - // For the page containing |object| mark the region covering [address] - // dirty. The object address must be in the first 8K of an allocated page. - void RecordWriteHelper(Register object, - Register address, - Register scratch); + void IncrementalMarkingRecordWriteHelper(Register object, + Register value, + Register address); + + enum RememberedSetFinalAction { + kReturnAtEnd, + kFallThroughAtEnd + }; + + + // Record in the remembered set the fact that we have a pointer to new space + // at the address pointed to by the addr register. Only works if addr is not + // in new space. + void RememberedSetHelper(Register object, // Used for debug code. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then); + + void CheckPageFlag(Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met); + + // Check if object is in new space. Jumps if the object is not in new space. + // The register scratch can be object itself, but it will be clobbered. + void JumpIfNotInNewSpace(Register object, + Register scratch, + Label* branch) { + InNewSpace(object, scratch, ne, branch); + } - // For the page containing |object| mark the region covering - // [object+offset] dirty. The object address must be in the first 8K - // of an allocated page. The 'scratch' registers are used in the - // implementation and all 3 registers are clobbered by the - // operation, as well as the 'at' register. RecordWrite updates the - // write barrier even when storing smis. - void RecordWrite(Register object, - Operand offset, + // Check if object is in new space. Jumps if the object is in new space. + // The register scratch can be object itself, but scratch will be clobbered. + void JumpIfInNewSpace(Register object, + Register scratch, + Label* branch) { + InNewSpace(object, scratch, eq, branch); + } + + // Check if an object has a given incremental marking color. + void HasColor(Register object, + Register scratch0, + Register scratch1, + Label* has_color, + int first_bit, + int second_bit); + + void JumpIfBlack(Register object, Register scratch0, - Register scratch1); + Register scratch1, + Label* on_black); + + // Checks the color of an object. If the object is already grey or black + // then we just fall through, since it is already live. If it is white and + // we can determine that it doesn't need to be scanned, then we just mark it + // black and fall through. For the rest we jump to the label so the + // incremental marker can fix its assumptions. + void EnsureNotWhite(Register object, + Register scratch1, + Register scratch2, + Register scratch3, + Label* object_is_white_and_not_data); + + // Detects conservatively whether an object is data-only, i.e. it does need to + // be scanned by the garbage collector. + void JumpIfDataObject(Register value, + Register scratch, + Label* not_data_object); + + // Notify the garbage collector that we wrote a pointer into an object. + // |object| is the object being stored into, |value| is the object being + // stored. value and scratch registers are clobbered by the operation. + // The offset is the offset from the start of the object, not the offset from + // the tagged HeapObject pointer. For use with FieldOperand(reg, off). + void RecordWriteField( + Register object, + int offset, + Register value, + Register scratch, + RAStatus ra_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // As above, but the offset has the tag presubtracted. For use with + // MemOperand(reg, off). + inline void RecordWriteContextSlot( + Register context, + int offset, + Register value, + Register scratch, + RAStatus ra_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK) { + RecordWriteField(context, + offset + kHeapObjectTag, + value, + scratch, + ra_status, + save_fp, + remembered_set_action, + smi_check); + } - // For the page containing |object| mark the region covering - // [address] dirty. The object address must be in the first 8K of an - // allocated page. All 3 registers are clobbered by the operation, - // as well as the ip register. RecordWrite updates the write barrier - // even when storing smis. - void RecordWrite(Register object, - Register address, - Register scratch); + // For a given |object| notify the garbage collector that the slot |address| + // has been written. |value| is the object being stored. The value and + // address registers are clobbered by the operation. + void RecordWrite( + Register object, + Register address, + Register value, + RAStatus ra_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); // --------------------------------------------------------------------------- @@ -282,7 +411,7 @@ class MacroAssembler: public Assembler { } // Check if the given instruction is a 'type' marker. - // ie. check if it is a sll zero_reg, zero_reg, <type> (referenced as + // i.e. check if it is a sll zero_reg, zero_reg, <type> (referenced as // nop(type)). These instructions are generated to mark special location in // the code, like some special IC code. static inline bool IsMarkedCode(Instr instr, int type) { @@ -518,6 +647,14 @@ class MacroAssembler: public Assembler { Addu(sp, sp, 2 * kPointerSize); } + // Pop three registers. Pops rightmost register first (from lower address). + void Pop(Register src1, Register src2, Register src3) { + lw(src3, MemOperand(sp, 0 * kPointerSize)); + lw(src2, MemOperand(sp, 1 * kPointerSize)); + lw(src1, MemOperand(sp, 2 * kPointerSize)); + Addu(sp, sp, 3 * kPointerSize); + } + void Pop(uint32_t count = 1) { Addu(sp, sp, Operand(count * kPointerSize)); } @@ -536,10 +673,17 @@ class MacroAssembler: public Assembler { // into register dst. void LoadFromSafepointRegisterSlot(Register dst, Register src); + // Flush the I-cache from asm code. You should use CPU::FlushICache from C. + // Does not handle errors. + void FlushICache(Register address, unsigned instructions); + // MIPS32 R2 instruction macro. void Ins(Register rt, Register rs, uint16_t pos, uint16_t size); void Ext(Register rt, Register rs, uint16_t pos, uint16_t size); + // --------------------------------------------------------------------------- + // FPU macros. These do not handle special cases like NaN or +- inf. + // Convert unsigned word to double. void Cvt_d_uw(FPURegister fd, FPURegister fs, FPURegister scratch); void Cvt_d_uw(FPURegister fd, Register rs, FPURegister scratch); @@ -548,6 +692,24 @@ class MacroAssembler: public Assembler { void Trunc_uw_d(FPURegister fd, FPURegister fs, FPURegister scratch); void Trunc_uw_d(FPURegister fd, Register rs, FPURegister scratch); + // Wrapper function for the different cmp/branch types. + void BranchF(Label* target, + Label* nan, + Condition cc, + FPURegister cmp1, + FPURegister cmp2, + BranchDelaySlot bd = PROTECT); + + // Alternate (inline) version for better readability with USE_DELAY_SLOT. + inline void BranchF(BranchDelaySlot bd, + Label* target, + Label* nan, + Condition cc, + FPURegister cmp1, + FPURegister cmp2) { + BranchF(target, nan, cc, cmp1, cmp2, bd); + }; + // Convert the HeapNumber pointed to by source to a 32bits signed integer // dest. If the HeapNumber does not fit into a 32bits signed integer branch // to not_int32 label. If FPU is available double_scratch is used but not @@ -559,6 +721,18 @@ class MacroAssembler: public Assembler { FPURegister double_scratch, Label *not_int32); + // Truncates a double using a specific rounding mode. + // The except_flag will contain any exceptions caused by the instruction. + // If check_inexact is kDontCheckForInexactConversion, then the inexacat + // exception is masked. + void EmitFPUTruncate(FPURoundingMode rounding_mode, + FPURegister result, + DoubleRegister double_input, + Register scratch1, + Register except_flag, + CheckForInexactConversion check_inexact + = kDontCheckForInexactConversion); + // Helper for EmitECMATruncate. // This will truncate a floating-point value outside of the singed 32bit // integer range to a 32bit signed integer. @@ -580,15 +754,6 @@ class MacroAssembler: public Assembler { Register scratch2, Register scratch3); - // ------------------------------------------------------------------------- - // Activation frames. - - void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } - void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); } - - void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } - void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter exit frame. // argc - argument count to be dropped by LeaveExitFrame. // save_doubles - saves FPU registers on stack, currently disabled. @@ -607,6 +772,22 @@ class MacroAssembler: public Assembler { void LoadContext(Register dst, int context_chain_length); + // Conditionally load the cached Array transitioned map of type + // transitioned_kind from the global context if the map in register + // map_in_out is the cached Array map in the global context of + // expected_kind. + void LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match); + + // Load the initial map for new Arrays from a JSFunction. + void LoadInitialArrayMap(Register function_in, + Register scratch, + Register map_out); + void LoadGlobalFunction(int index, Register function); // Load the initial map from the global function. The registers @@ -615,10 +796,16 @@ class MacroAssembler: public Assembler { Register map, Register scratch); + void InitializeRootRegister() { + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + li(kRootRegister, Operand(roots_array_start)); + } + // ------------------------------------------------------------------------- // JavaScript invokes. - // Setup call kind marking in t1. The method takes t1 as an + // Set up call kind marking in t1. The method takes t1 as an // explicit first parameter to make the code more readable at the // call sites. void SetCallKind(Register dst, CallKind kind); @@ -646,9 +833,10 @@ class MacroAssembler: public Assembler { const CallWrapper& call_wrapper, CallKind call_kind); - void InvokeFunction(JSFunction* function, + void InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper, CallKind call_kind); @@ -672,19 +860,12 @@ class MacroAssembler: public Assembler { void DebugBreak(); #endif - void InitializeRootRegister() { - ExternalReference roots_address = - ExternalReference::roots_address(isolate()); - li(kRootRegister, Operand(roots_address)); - } // ------------------------------------------------------------------------- // Exception handling. // Push a new try handler and link into try handler chain. - // The return address must be passed in register ra. - // Clobber t0, t1, t2. - void PushTryHandler(CodeLocation try_location, HandlerType type); + void PushTryHandler(StackHandler::Kind kind, int handler_index); // Unlink the stack handler on top of the stack from the try handler chain. // Must preserve the result register. @@ -708,6 +889,13 @@ class MacroAssembler: public Assembler { Register length, Register scratch); + // Initialize fields with filler values. Fields starting at |start_offset| + // not including end_offset are overwritten with the value in |filler|. At + // the end the loop, |start_offset| takes the value of |end_offset|. + void InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler); + // ------------------------------------------------------------------------- // Support functions. @@ -719,7 +907,8 @@ class MacroAssembler: public Assembler { void TryGetFunctionPrototype(Register function, Register result, Register scratch, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); void GetObjectType(Register function, Register map, @@ -731,15 +920,54 @@ class MacroAssembler: public Assembler { Register scratch, Label* fail); - // Check if the map of an object is equal to a specified map (either - // given directly or as an index into the root list) and branch to - // label if not. Skip the smi check if not required (object is known - // to be a heap object). + // Check if a map for a JSObject indicates that the object can have both smi + // and HeapObject elements. Jump to the specified label if it does not. + void CheckFastObjectElements(Register map, + Register scratch, + Label* fail); + + // Check if a map for a JSObject indicates that the object has fast smi only + // elements. Jump to the specified label if it does not. + void CheckFastSmiOnlyElements(Register map, + Register scratch, + Label* fail); + + // Check to see if maybe_number can be stored as a double in + // FastDoubleElements. If it can, store it at the index specified by key in + // the FastDoubleElements array elements, otherwise jump to fail. + void StoreNumberToDoubleElements(Register value_reg, + Register key_reg, + Register receiver_reg, + Register elements_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Label* fail); + + // Compare an object's map with the specified map and its transitioned + // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. Jumps to + // "branch_to" if the result of the comparison is "cond". If multiple map + // compares are required, the compare sequences branches to early_success. + void CompareMapAndBranch(Register obj, + Register scratch, + Handle<Map> map, + Label* early_success, + Condition cond, + Label* branch_to, + CompareMapMode mode = REQUIRE_EXACT_MAP); + + // Check if the map of an object is equal to a specified map and branch to + // label if not. Skip the smi check if not required (object is known to be a + // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match + // against maps that are ElementsKind transition maps of the specificed map. void CheckMap(Register obj, Register scratch, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type); + SmiCheckType smi_check_type, + CompareMapMode mode = REQUIRE_EXACT_MAP); + void CheckMap(Register obj, Register scratch, @@ -760,6 +988,21 @@ class MacroAssembler: public Assembler { // occurred. void IllegalOperation(int num_arguments); + + // Load and check the instance type of an object for being a string. + // Loads the type into the second argument register. + // Returns a condition that will be enabled if the object was a string. + Condition IsObjectStringType(Register obj, + Register type, + Register result) { + lw(type, FieldMemOperand(obj, HeapObject::kMapOffset)); + lbu(type, FieldMemOperand(type, Map::kInstanceTypeOffset)); + And(type, type, Operand(kIsNotStringMask)); + ASSERT_EQ(0, kStringTag); + return eq; + } + + // Picks out an array index from the hash field. // Register use: // hash - holds the index's hash. Clobbered. @@ -833,27 +1076,9 @@ class MacroAssembler: public Assembler { void CallStub(CodeStub* stub, Condition cond = cc_always, Register r1 = zero_reg, const Operand& r2 = Operand(zero_reg)); - // Call a code stub and return the code object called. Try to generate - // the code if necessary. Do not perform a GC but instead return a retry - // after GC failure. - MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub, - Condition cond = cc_always, - Register r1 = zero_reg, - const Operand& r2 = - Operand(zero_reg)); - // Tail call a code stub (jump). void TailCallStub(CodeStub* stub); - // Tail call a code stub (jump) and return the code object called. Try to - // generate the code if necessary. Do not perform a GC but instead return - // a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallStub(CodeStub* stub, - Condition cond = cc_always, - Register r1 = zero_reg, - const Operand& r2 = - Operand(zero_reg)); - void CallJSExitStub(CodeStub* stub); // Call a runtime routine. @@ -874,17 +1099,14 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); - // Tail call of a runtime routine (jump). Try to generate the code if - // necessary. Do not perform a GC but instead return a retry after GC - // failure. - MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size); - // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); + int CalculateStackPassedWords(int num_reg_arguments, + int num_double_arguments); + // Before calling a C-function from generated code, align arguments on stack // and add space for the four mips argument slots. // After aligning the frame, non-register arguments must be stored on the @@ -894,7 +1116,11 @@ class MacroAssembler: public Assembler { // C++ code. // Needs a scratch register to do some arithmetic. This register will be // trashed. - void PrepareCallCFunction(int num_arguments, Register scratch); + void PrepareCallCFunction(int num_reg_arguments, + int num_double_registers, + Register scratch); + void PrepareCallCFunction(int num_reg_arguments, + Register scratch); // Arguments 1-4 are placed in registers a0 thru a3 respectively. // Arguments 5..n are stored to stack using following: @@ -906,7 +1132,13 @@ class MacroAssembler: public Assembler { // return address (unless this is somehow accounted for by the called // function). void CallCFunction(ExternalReference function, int num_arguments); - void CallCFunction(Register function, Register scratch, int num_arguments); + void CallCFunction(Register function, int num_arguments); + void CallCFunction(ExternalReference function, + int num_reg_arguments, + int num_double_arguments); + void CallCFunction(Register function, + int num_reg_arguments, + int num_double_arguments); void GetCFunctionDoubleResult(const DoubleRegister dst); // There are two ways of passing double arguments on MIPS, depending on @@ -917,16 +1149,15 @@ class MacroAssembler: public Assembler { void SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2); void SetCallCDoubleArguments(DoubleRegister dreg, Register reg); - // Calls an API function. Allocates HandleScope, extracts returned value - // from handle and propagates exceptions. Restores context. - MaybeObject* TryCallApiFunctionAndReturn(ExternalReference function, - int stack_space); + // Calls an API function. Allocates HandleScope, extracts returned value + // from handle and propagates exceptions. Restores context. stack_space + // - space to be unwound on exit (includes the call JS arguments space and + // the additional space allocated for the fast call). + void CallApiFunctionAndReturn(ExternalReference function, int stack_space); // Jump to the builtin routine. void JumpToExternalReference(const ExternalReference& builtin); - MaybeObject* TryJumpToExternalReference(const ExternalReference& ext); - // Invoke specified builtin JavaScript function. Adds an entry to // the unresolved list if the name does not resolve. void InvokeBuiltin(Builtins::JavaScript id, @@ -982,6 +1213,9 @@ class MacroAssembler: public Assembler { bool generating_stub() { return generating_stub_; } void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } bool allow_stub_calls() { return allow_stub_calls_; } + void set_has_frame(bool value) { has_frame_ = value; } + bool has_frame() { return has_frame_; } + inline bool AllowThisStubCall(CodeStub* stub); // --------------------------------------------------------------------------- // Number utilities. @@ -997,18 +1231,14 @@ class MacroAssembler: public Assembler { // ------------------------------------------------------------------------- // Smi utilities. - // Try to convert int32 to smi. If the value is to large, preserve - // the original value and jump to not_a_smi. Destroys scratch and - // sets flags. - // This is only used by crankshaft atm so it is unimplemented on MIPS. - void TrySmiTag(Register reg, Label* not_a_smi, Register scratch) { - UNIMPLEMENTED_MIPS(); - } - void SmiTag(Register reg) { Addu(reg, reg, reg); } + // Test for overflow < 0: use BranchOnOverflow() or BranchOnNoOverflow(). + void SmiTagCheckOverflow(Register reg, Register overflow); + void SmiTagCheckOverflow(Register dst, Register src, Register overflow); + void SmiTag(Register dst, Register src) { Addu(dst, src, src); } @@ -1021,21 +1251,25 @@ class MacroAssembler: public Assembler { sra(dst, src, kSmiTagSize); } + // Untag the source value into destination and jump if source is a smi. + // Souce and destination can be the same register. + void UntagAndJumpIfSmi(Register dst, Register src, Label* smi_case); + + // Untag the source value into destination and jump if source is not a smi. + // Souce and destination can be the same register. + void UntagAndJumpIfNotSmi(Register dst, Register src, Label* non_smi_case); + // Jump the register contains a smi. - inline void JumpIfSmi(Register value, Label* smi_label, - Register scratch = at) { - ASSERT_EQ(0, kSmiTag); - andi(scratch, value, kSmiTagMask); - Branch(smi_label, eq, scratch, Operand(zero_reg)); - } + void JumpIfSmi(Register value, + Label* smi_label, + Register scratch = at, + BranchDelaySlot bd = PROTECT); // Jump if the register contains a non-smi. - inline void JumpIfNotSmi(Register value, Label* not_smi_label, - Register scratch = at) { - ASSERT_EQ(0, kSmiTag); - andi(scratch, value, kSmiTagMask); - Branch(not_smi_label, ne, scratch, Operand(zero_reg)); - } + void JumpIfNotSmi(Register value, + Label* not_smi_label, + Register scratch = at, + BranchDelaySlot bd = PROTECT); // Jump if either of the registers contain a non-smi. void JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi); @@ -1096,13 +1330,33 @@ class MacroAssembler: public Assembler { Register scratch2, Label* failure); + void ClampUint8(Register output_reg, Register input_reg); + + void ClampDoubleToUint8(Register result_reg, + DoubleRegister input_reg, + DoubleRegister temp_double_reg); + + void LoadInstanceDescriptors(Register map, Register descriptors); + + // Activation support. + void EnterFrame(StackFrame::Type type); + void LeaveFrame(StackFrame::Type type); + + // Patch the relocated value (lui/ori pair). + void PatchRelocatedValue(Register li_location, + Register scratch, + Register new_value); + // Get the relocatad value (loaded data) from the lui/ori pair. + void GetRelocatedValue(Register li_location, + Register value, + Register scratch); + private: void CallCFunctionHelper(Register function, - ExternalReference function_reference, - Register scratch, - int num_arguments); + int num_reg_arguments, + int num_double_arguments); void BranchShort(int16_t offset, BranchDelaySlot bdslot = PROTECT); void BranchShort(int16_t offset, Condition cond, Register rs, @@ -1130,6 +1384,7 @@ class MacroAssembler: public Assembler { Handle<Code> code_constant, Register code_reg, Label* done, + bool* definitely_mismatches, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind); @@ -1138,25 +1393,37 @@ class MacroAssembler: public Assembler { // the function in the 'resolved' flag. Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved); - // Activation support. - void EnterFrame(StackFrame::Type type); - void LeaveFrame(StackFrame::Type type); - void InitializeNewString(Register string, Register length, Heap::RootListIndex map_index, Register scratch1, Register scratch2); + // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. + void InNewSpace(Register object, + Register scratch, + Condition cond, // eq for new space, ne otherwise. + Label* branch); + + // Helper for finding the mark bits for an address. Afterwards, the + // bitmap register points at the word with the mark bits and the mask + // the position of the first bit. Leaves addr_reg unchanged. + inline void GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg); + + // Helper for throwing exceptions. Compute a handler address and jump to + // it. See the implementation for register usage. + void JumpToHandlerEntry(); + // Compute memory operands for safepoint stack slots. static int SafepointRegisterStackIndex(int reg_code); MemOperand SafepointRegisterSlot(Register reg); MemOperand SafepointRegistersAndDoublesSlot(Register reg); - bool UseAbsoluteCodePointers(); - bool generating_stub_; bool allow_stub_calls_; + bool has_frame_; // This handle will be patched with the code object on installation. Handle<Object> code_object_; @@ -1197,34 +1464,6 @@ class CodePatcher { }; -// ----------------------------------------------------------------------------- -// Static helper functions. - -static MemOperand ContextOperand(Register context, int index) { - return MemOperand(context, Context::SlotOffset(index)); -} - - -static inline MemOperand GlobalObjectOperand() { - return ContextOperand(cp, Context::GLOBAL_INDEX); -} - - -// Generate a MemOperand for loading a field from an object. -static inline MemOperand FieldMemOperand(Register object, int offset) { - return MemOperand(object, offset - kHeapObjectTag); -} - - -// Generate a MemOperand for storing arguments 5..N on the stack -// when calling CallCFunction(). -static inline MemOperand CFunctionArgumentOperand(int index) { - ASSERT(index > kCArgSlotCount); - // Argument 5 takes the slot just past the four Arg-slots. - int offset = (index - 5) * kPointerSize + kCArgsSlotsSize; - return MemOperand(sp, offset); -} - #ifdef GENERATED_CODE_COVERAGE #define CODE_COVERAGE_STRINGIFY(x) #x diff --git a/deps/v8/src/mips/regexp-macro-assembler-mips.cc b/deps/v8/src/mips/regexp-macro-assembler-mips.cc index 63e836f22f..cb210fed04 100644 --- a/deps/v8/src/mips/regexp-macro-assembler-mips.cc +++ b/deps/v8/src/mips/regexp-macro-assembler-mips.cc @@ -377,9 +377,12 @@ void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase( // Isolate. __ li(a3, Operand(ExternalReference::isolate_address())); - ExternalReference function = - ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); - __ CallCFunction(function, argument_count); + { + AllowExternalCallThatCantCauseGC scope(masm_); + ExternalReference function = + ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate()); + __ CallCFunction(function, argument_count); + } // Restore regexp engine registers. __ MultiPop(regexp_registers_to_retain); @@ -607,6 +610,12 @@ Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) { // Entry code: __ bind(&entry_label_); + + // Tell the system that we have a stack frame. Because the type is MANUAL, + // no is generated. + FrameScope scope(masm_, StackFrame::MANUAL); + + // Actually emit code to start a new stack frame. // Push arguments // Save callee-save registers. // Start new stack frame. @@ -1103,6 +1112,11 @@ int RegExpMacroAssemblerMIPS::CheckStackGuardState(Address* return_address, frame_entry<const String*>(re_frame, kInputString) = *subject; frame_entry<const byte*>(re_frame, kInputStart) = new_address; frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length; + } else if (frame_entry<const String*>(re_frame, kInputString) != *subject) { + // Subject string might have been a ConsString that underwent + // short-circuiting during GC. That will not change start_address but + // will change pointer inside the subject handle. + frame_entry<const String*>(re_frame, kInputString) = *subject; } return 0; @@ -1244,13 +1258,14 @@ void RegExpCEntryStub::Generate(MacroAssembler* masm_) { if (stack_alignment < kPointerSize) stack_alignment = kPointerSize; // Stack is already aligned for call, so decrement by alignment // to make room for storing the return address. - __ Subu(sp, sp, Operand(stack_alignment)); - __ sw(ra, MemOperand(sp, 0)); - __ mov(a0, sp); + __ Subu(sp, sp, Operand(stack_alignment + kCArgsSlotsSize)); + const int return_address_offset = kCArgsSlotsSize; + __ Addu(a0, sp, return_address_offset); + __ sw(ra, MemOperand(a0, 0)); __ mov(t9, t1); __ Call(t9); - __ lw(ra, MemOperand(sp, 0)); - __ Addu(sp, sp, Operand(stack_alignment)); + __ lw(ra, MemOperand(sp, return_address_offset)); + __ Addu(sp, sp, Operand(stack_alignment + kCArgsSlotsSize)); __ Jump(ra); } diff --git a/deps/v8/src/mips/simulator-mips.cc b/deps/v8/src/mips/simulator-mips.cc index 17c18977c7..a158f045f5 100644 --- a/deps/v8/src/mips/simulator-mips.cc +++ b/deps/v8/src/mips/simulator-mips.cc @@ -72,7 +72,7 @@ uint32_t get_fcsr_condition_bit(uint32_t cc) { // code. class MipsDebugger { public: - explicit MipsDebugger(Simulator* sim); + explicit MipsDebugger(Simulator* sim) : sim_(sim) { } ~MipsDebugger(); void Stop(Instruction* instr); @@ -105,10 +105,6 @@ class MipsDebugger { void RedoBreakpoints(); }; -MipsDebugger::MipsDebugger(Simulator* sim) { - sim_ = sim; -} - MipsDebugger::~MipsDebugger() { } @@ -391,6 +387,13 @@ void MipsDebugger::Debug() { if (line == NULL) { break; } else { + char* last_input = sim_->last_debugger_input(); + if (strcmp(line, "\n") == 0 && last_input != NULL) { + line = last_input; + } else { + // Ownership is transferred to sim_; + sim_->set_last_debugger_input(line); + } // Use sscanf to parse the individual parts of the command line. At the // moment no command expects more than two parameters. int argc = SScanF(line, @@ -757,7 +760,6 @@ void MipsDebugger::Debug() { PrintF("Unknown command: %s\n", cmd); } } - DeleteArray(line); } // Add all the breakpoints back to stop execution and enter the debugger @@ -791,6 +793,12 @@ static bool AllOnOnePage(uintptr_t start, int size) { } +void Simulator::set_last_debugger_input(char* input) { + DeleteArray(last_debugger_input_); + last_debugger_input_ = input; +} + + void Simulator::FlushICache(v8::internal::HashMap* i_cache, void* start_addr, size_t size) { @@ -880,7 +888,7 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { isolate_->set_simulator_i_cache(i_cache_); } Initialize(isolate); - // Setup simulator support first. Some of this information is needed to + // Set up simulator support first. Some of this information is needed to // setup the architecture state. stack_ = reinterpret_cast<char*>(malloc(stack_size_)); pc_modified_ = false; @@ -889,7 +897,7 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { break_pc_ = NULL; break_instr_ = 0; - // Setup architecture state. + // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < kNumSimuRegisters; i++) { registers_[i] = 0; @@ -911,6 +919,8 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { for (int i = 0; i < kNumExceptions; i++) { exceptions[i] = 0; } + + last_debugger_input_ = NULL; } @@ -1359,9 +1369,9 @@ void Simulator::WriteB(int32_t addr, int8_t value) { // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit() const { - // Leave a safety margin of 256 bytes to prevent overrunning the stack when + // Leave a safety margin of 512 bytes to prevent overrunning the stack when // pushing values. - return reinterpret_cast<uintptr_t>(stack_) + 256; + return reinterpret_cast<uintptr_t>(stack_) + 512; } @@ -1934,7 +1944,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { // Next pc int32_t next_pc = 0; - // Setup the variables if needed before executing the instruction. + // Set up the variables if needed before executing the instruction. ConfigureTypeRegister(instr, alu_out, i64hilo, @@ -2281,7 +2291,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { } -// Type 2: instructions using a 16 bytes immediate. (eg: addi, beq). +// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq). void Simulator::DecodeTypeImmediate(Instruction* instr) { // Instruction fields. Opcode op = instr->OpcodeFieldRaw(); @@ -2604,7 +2614,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { } -// Type 3: instructions using a 26 bytes immediate. (eg: j, jal). +// Type 3: instructions using a 26 bytes immediate. (e.g. j, jal). void Simulator::DecodeTypeJump(Instruction* instr) { // Get current pc. int32_t current_pc = get_pc(); @@ -2701,7 +2711,7 @@ void Simulator::Execute() { int32_t Simulator::Call(byte* entry, int argument_count, ...) { va_list parameters; va_start(parameters, argument_count); - // Setup arguments. + // Set up arguments. // First four arguments passed in registers. ASSERT(argument_count >= 4); @@ -2748,7 +2758,7 @@ int32_t Simulator::Call(byte* entry, int argument_count, ...) { int32_t sp_val = get_register(sp); int32_t fp_val = get_register(fp); - // Setup the callee-saved registers with a known value. To be able to check + // Set up the callee-saved registers with a known value. To be able to check // that they are preserved properly across JS execution. int32_t callee_saved_value = icount_; set_register(s0, callee_saved_value); diff --git a/deps/v8/src/mips/simulator-mips.h b/deps/v8/src/mips/simulator-mips.h index 69dddfad3a..ba625f45ba 100644 --- a/deps/v8/src/mips/simulator-mips.h +++ b/deps/v8/src/mips/simulator-mips.h @@ -221,6 +221,10 @@ class Simulator { // Pop an address from the JS stack. uintptr_t PopAddress(); + // Debugger input. + void set_last_debugger_input(char* input); + char* last_debugger_input() { return last_debugger_input_; } + // ICache checking. static void FlushICache(v8::internal::HashMap* i_cache, void* start, size_t size); @@ -358,6 +362,9 @@ class Simulator { int icount_; int break_count_; + // Debugger input. + char* last_debugger_input_; + // Icache simulation. v8::internal::HashMap* i_cache_; diff --git a/deps/v8/src/mips/stub-cache-mips.cc b/deps/v8/src/mips/stub-cache-mips.cc index 5b949734fb..ae563069f8 100644 --- a/deps/v8/src/mips/stub-cache-mips.cc +++ b/deps/v8/src/mips/stub-cache-mips.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -99,13 +99,12 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( - MacroAssembler* masm, - Label* miss_label, - Register receiver, - String* name, - Register scratch0, - Register scratch1) { +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle<String> name, + Register scratch0, + Register scratch1) { ASSERT(name->IsSymbol()); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); @@ -120,9 +119,8 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( Register map = scratch1; __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); __ lbu(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); - __ And(at, scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); - __ Branch(miss_label, ne, at, Operand(zero_reg)); - + __ And(scratch0, scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); + __ Branch(miss_label, ne, scratch0, Operand(zero_reg)); // Check that receiver is a JSObject. __ lbu(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); @@ -140,20 +138,16 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( // Restore the temporarily used register. __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup( - masm, - miss_label, - &done, - receiver, - properties, - name, - scratch1); - if (result->IsFailure()) return result; + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + receiver, + properties, + name, + scratch1); __ bind(&done); __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); - - return result; } @@ -240,7 +234,10 @@ void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, int index, Register prototype, Label* miss) { + MacroAssembler* masm, + int index, + Register prototype, + Label* miss) { Isolate* isolate = masm->isolate(); // Check we're still in the same context. __ lw(prototype, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); @@ -248,8 +245,8 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( __ li(at, isolate->global()); __ Branch(miss, ne, prototype, Operand(at)); // Get the global function with the given index. - JSFunction* function = - JSFunction::cast(isolate->global_context()->get(index)); + Handle<JSFunction> function( + JSFunction::cast(isolate->global_context()->get(index))); // Load its initial map. The global functions all have initial maps. __ li(prototype, Handle<Map>(function->initial_map())); // Load the prototype from the initial map. @@ -261,8 +258,10 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle<JSObject> holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -283,8 +282,7 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, Register scratch, Label* miss_label) { // Check that the receiver isn't a smi. - __ And(scratch, receiver, Operand(kSmiTagMask)); - __ Branch(miss_label, eq, scratch, Operand(zero_reg)); + __ JumpIfSmi(receiver, miss_label); // Check that the object is a JS array. __ GetObjectType(receiver, scratch, scratch); @@ -370,22 +368,18 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // After executing generated code, the receiver_reg and name_reg // may be clobbered. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, int index, - Map* transition, + Handle<Map> transition, Register receiver_reg, Register name_reg, Register scratch, Label* miss_label) { // a0 : value. Label exit; - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver_reg, miss_label, scratch); - - // Check that the map of the receiver hasn't changed. - __ lw(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); - __ Branch(miss_label, ne, scratch, Operand(Handle<Map>(object->map()))); + // Check that the map of the object hasn't changed. + __ CheckMap(receiver_reg, scratch, Handle<Map>(object->map()), miss_label, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -397,11 +391,11 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); // Perform map transition for the receiver if necessary. - if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { + if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. __ push(receiver_reg); - __ li(a2, Operand(Handle<Map>(transition))); + __ li(a2, Operand(transition)); __ Push(a2, a0); __ TailCallExternalReference( ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), @@ -410,10 +404,10 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, return; } - if (transition != NULL) { + if (!transition.is_null()) { // Update the map of the object; no write barrier updating is // needed because the map is never in new space. - __ li(t0, Operand(Handle<Map>(transition))); + __ li(t0, Operand(transition)); __ sw(t0, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); } @@ -432,7 +426,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the now unused name_reg as a scratch register. - __ RecordWrite(receiver_reg, Operand(offset), name_reg, scratch); + __ mov(name_reg, a0); + __ RecordWriteField(receiver_reg, + offset, + name_reg, + scratch, + kRAHasNotBeenSaved, + kDontSaveFPRegs); } else { // Write to the properties array. int offset = index * kPointerSize + FixedArray::kHeaderSize; @@ -445,7 +445,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Ok to clobber receiver_reg and name_reg, since we return. - __ RecordWrite(scratch, Operand(offset), name_reg, receiver_reg); + __ mov(name_reg, a0); + __ RecordWriteField(scratch, + offset, + name_reg, + receiver_reg, + kRAHasNotBeenSaved, + kDontSaveFPRegs); } // Return the value (register v0). @@ -457,20 +463,15 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); - Code* code = NULL; - if (kind == Code::LOAD_IC) { - code = masm->isolate()->builtins()->builtin(Builtins::kLoadIC_Miss); - } else { - code = masm->isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Miss); - } - - Handle<Code> ic(code); - __ Jump(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = (kind == Code::LOAD_IC) + ? masm->isolate()->builtins()->LoadIC_Miss() + : masm->isolate()->builtins()->KeyedLoadIC_Miss(); + __ Jump(code, RelocInfo::CODE_TARGET); } static void GenerateCallFunction(MacroAssembler* masm, - Object* object, + Handle<Object> object, const ParameterCount& arguments, Label* miss, Code::ExtraICState extra_ic_state) { @@ -502,23 +503,24 @@ static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, Register name, - JSObject* holder_obj) { + Handle<JSObject> holder_obj) { __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!masm->isolate()->heap()->InNewSpace(interceptor)); + Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); + ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); Register scratch = name; - __ li(scratch, Operand(Handle<Object>(interceptor))); + __ li(scratch, Operand(interceptor)); __ Push(scratch, receiver, holder); __ lw(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset)); __ push(scratch); } -static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { +static void CompileCallLoadPropertyWithInterceptor( + MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + Handle<JSObject> holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); ExternalReference ref = @@ -554,34 +556,34 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm) { } -static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, +static void GenerateFastApiDirectCall(MacroAssembler* masm, const CallOptimization& optimization, int argc) { // ----------- S t a t e ------------- // -- sp[0] : holder (set by CheckPrototypes) - // -- sp[4] : callee js function + // -- sp[4] : callee JS function // -- sp[8] : call data - // -- sp[12] : last js argument + // -- sp[12] : last JS argument // -- ... - // -- sp[(argc + 3) * 4] : first js argument + // -- sp[(argc + 3) * 4] : first JS argument // -- sp[(argc + 4) * 4] : receiver // ----------------------------------- // Get the function and setup the context. - JSFunction* function = optimization.constant_function(); - __ li(t1, Operand(Handle<JSFunction>(function))); + Handle<JSFunction> function = optimization.constant_function(); + __ LoadHeapObject(t1, function); __ lw(cp, FieldMemOperand(t1, JSFunction::kContextOffset)); // Pass the additional arguments FastHandleApiCall expects. - Object* call_data = optimization.api_call_info()->data(); - Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); - if (masm->isolate()->heap()->InNewSpace(call_data)) { - __ li(a0, api_call_info_handle); + Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); + Handle<Object> call_data(api_call_info->data()); + if (masm->isolate()->heap()->InNewSpace(*call_data)) { + __ li(a0, api_call_info); __ lw(t2, FieldMemOperand(a0, CallHandlerInfo::kDataOffset)); } else { - __ li(t2, Operand(Handle<Object>(call_data))); + __ li(t2, call_data); } - // Store js function and call data. + // Store JS function and call data. __ sw(t1, MemOperand(sp, 1 * kPointerSize)); __ sw(t2, MemOperand(sp, 2 * kPointerSize)); @@ -589,12 +591,9 @@ static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, // (refer to layout above). __ Addu(a2, sp, Operand(2 * kPointerSize)); - Object* callback = optimization.api_call_info()->callback(); - Address api_function_address = v8::ToCData<Address>(callback); - ApiFunction fun(api_function_address); - const int kApiStackSpace = 4; + FrameScope frame_scope(masm, StackFrame::MANUAL); __ EnterExitFrame(false, kApiStackSpace); // NOTE: the O32 abi requires a0 to hold a special pointer when returning a @@ -617,16 +616,15 @@ static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, // v8::Arguments::is_construct_call = 0 __ sw(zero_reg, MemOperand(a1, 3 * kPointerSize)); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. const int kStackUnwindSpace = argc + kFastApiCallArguments + 1; + Address function_address = v8::ToCData<Address>(api_call_info->callback()); + ApiFunction fun(function_address); ExternalReference ref = ExternalReference(&fun, ExternalReference::DIRECT_API_CALL, masm->isolate()); - return masm->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace); + AllowExternalCallThatCantCauseGC scope(masm); + __ CallApiFunctionAndReturn(ref, kStackUnwindSpace); } class CallInterceptorCompiler BASE_EMBEDDED { @@ -640,86 +638,63 @@ class CallInterceptorCompiler BASE_EMBEDDED { name_(name), extra_ic_state_(extra_ic_state) {} - MaybeObject* Compile(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - LookupResult* lookup, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Label* miss) { + void Compile(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Label* miss) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); - CallOptimization optimization(lookup); - if (optimization.is_constant_call()) { - return CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, + holder, lookup, name, optimization, miss); } else { - CompileRegular(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - name, - holder, - miss); - return masm->isolate()->heap()->undefined_value(); + CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, + name, holder, miss); } } private: - MaybeObject* CompileCacheable(MacroAssembler* masm, - JSObject* object, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - JSObject* interceptor_holder, - LookupResult* lookup, - String* name, - const CallOptimization& optimization, - Label* miss_label) { + void CompileCacheable(MacroAssembler* masm, + Handle<JSObject> object, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<JSObject> interceptor_holder, + LookupResult* lookup, + Handle<String> name, + const CallOptimization& optimization, + Label* miss_label) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); - Counters* counters = masm->isolate()->counters(); - int depth1 = kInvalidProtoDepth; int depth2 = kInvalidProtoDepth; bool can_do_fast_api_call = false; if (optimization.is_simple_api_call() && - !lookup->holder()->IsGlobalObject()) { - depth1 = - optimization.GetPrototypeDepthOfExpectedType(object, - interceptor_holder); + !lookup->holder()->IsGlobalObject()) { + depth1 = optimization.GetPrototypeDepthOfExpectedType( + object, interceptor_holder); if (depth1 == kInvalidProtoDepth) { - depth2 = - optimization.GetPrototypeDepthOfExpectedType(interceptor_holder, - lookup->holder()); + depth2 = optimization.GetPrototypeDepthOfExpectedType( + interceptor_holder, Handle<JSObject>(lookup->holder())); } - can_do_fast_api_call = (depth1 != kInvalidProtoDepth) || - (depth2 != kInvalidProtoDepth); + can_do_fast_api_call = + depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth; } __ IncrementCounter(counters->call_const_interceptor(), 1, - scratch1, scratch2); + scratch1, scratch2); if (can_do_fast_api_call) { __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1, @@ -732,9 +707,9 @@ class CallInterceptorCompiler BASE_EMBEDDED { Label miss_cleanup; Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; Register holder = - stub_compiler_->CheckPrototypes(object, receiver, - interceptor_holder, scratch1, - scratch2, scratch3, name, depth1, miss); + stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, depth1, miss); // Invoke an interceptor and if it provides a value, // branch to |regular_invoke|. @@ -747,10 +722,11 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Check that the maps from interceptor's holder to constant function's // holder haven't changed and thus we can use cached constant function. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { stub_compiler_->CheckPrototypes(interceptor_holder, receiver, - lookup->holder(), scratch1, - scratch2, scratch3, name, depth2, miss); + Handle<JSObject>(lookup->holder()), + scratch1, scratch2, scratch3, + name, depth2, miss); } else { // CheckPrototypes has a side effect of fetching a 'holder' // for API (object which is instanceof for the signature). It's @@ -761,16 +737,13 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - MaybeObject* result = GenerateFastApiDirectCall(masm, - optimization, - arguments_.immediate()); - if (result->IsFailure()) return result; + GenerateFastApiDirectCall(masm, optimization, arguments_.immediate()); } else { CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(optimization.constant_function(), arguments_, - JUMP_FUNCTION, call_kind); + JUMP_FUNCTION, NullCallWrapper(), call_kind); } // Deferred code for fast API call case---clean preallocated space. @@ -785,66 +758,57 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm); } - - return masm->isolate()->heap()->undefined_value(); } void CompileRegular(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, Register receiver, Register scratch1, Register scratch2, Register scratch3, - String* name, - JSObject* interceptor_holder, + Handle<String> name, + Handle<JSObject> interceptor_holder, Label* miss_label) { Register holder = stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, name, - miss_label); + scratch1, scratch2, scratch3, + name, miss_label); // Call a runtime function to load the interceptor property. - __ EnterInternalFrame(); + FrameScope scope(masm, StackFrame::INTERNAL); // Save the name_ register across the call. __ push(name_); - PushInterceptorArguments(masm, - receiver, - holder, - name_, - interceptor_holder); + PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder); __ CallExternalReference( ExternalReference( IC_Utility(IC::kLoadPropertyWithInterceptorForCall), masm->isolate()), 5); - // Restore the name_ register. __ pop(name_); - __ LeaveInternalFrame(); + // Leave the internal frame. } void LoadWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, - JSObject* holder_obj, + Handle<JSObject> holder_obj, Register scratch, Label* interceptor_succeeded) { - __ EnterInternalFrame(); - - __ Push(holder, name_); - - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); - - __ pop(name_); // Restore the name. - __ pop(receiver); // Restore the holder. - __ LeaveInternalFrame(); - + { + FrameScope scope(masm, StackFrame::INTERNAL); + + __ Push(holder, name_); + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. + } // If interceptor returns no-result sentinel, call the constant function. __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); __ Branch(interceptor_succeeded, ne, v0, Operand(scratch)); @@ -861,52 +825,41 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( - MacroAssembler* masm, - GlobalObject* global, - String* name, - Register scratch, - Label* miss) { - Object* probe; - { MaybeObject* maybe_probe = global->EnsurePropertyCell(name); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle<GlobalObject> global, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSGlobalPropertyCell> cell = + GlobalObject::EnsurePropertyCell(global, name); ASSERT(cell->value()->IsTheHole()); - __ li(scratch, Operand(Handle<Object>(cell))); + __ li(scratch, Operand(cell)); __ lw(scratch, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ Branch(miss, ne, scratch, Operand(at)); - return cell; } // Calls GenerateCheckPropertyCell for each global object in the prototype chain // from object to (but not including) holder. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( - MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - Register scratch, - Label* miss) { - JSObject* current = object; - while (current != holder) { +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { if (current->IsGlobalObject()) { - // Returns a cell or a failure. - MaybeObject* result = GenerateCheckPropertyCell( - masm, - GlobalObject::cast(current), - name, - scratch, - miss); - if (result->IsFailure()) return result; + GenerateCheckPropertyCell(masm, + Handle<GlobalObject>::cast(current), + name, + scratch, + miss); } - ASSERT(current->IsJSObject()); - current = JSObject::cast(current->GetPrototype()); + current = Handle<JSObject>(JSObject::cast(current->GetPrototype())); } - return NULL; } @@ -1030,13 +983,13 @@ static void GenerateUInt2Double(MacroAssembler* masm, #define __ ACCESS_MASM(masm()) -Register StubCompiler::CheckPrototypes(JSObject* object, +Register StubCompiler::CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, int save_at_depth, Label* miss) { // Make sure there's no overlap between holder and object registers. @@ -1054,81 +1007,50 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Check the maps in the prototype chain. // Traverse the prototype chain from the object and do map checks. - JSObject* current = object; - while (current != holder) { - depth++; + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { + ++depth; // Only global objects and objects that do not require access // checks are allowed in stubs. ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - ASSERT(current->GetPrototype()->IsJSObject()); - JSObject* prototype = JSObject::cast(current->GetPrototype()); + Handle<JSObject> prototype(JSObject::cast(current->GetPrototype())); if (!current->HasFastProperties() && !current->IsJSGlobalObject() && !current->IsJSGlobalProxy()) { if (!name->IsSymbol()) { - MaybeObject* maybe_lookup_result = heap()->LookupSymbol(name); - Object* lookup_result = NULL; // Initialization to please compiler. - if (!maybe_lookup_result->ToObject(&lookup_result)) { - set_failure(Failure::cast(maybe_lookup_result)); - return reg; - } - name = String::cast(lookup_result); + name = factory()->LookupSymbol(name); } - ASSERT(current->property_dictionary()->FindEntry(name) == + ASSERT(current->property_dictionary()->FindEntry(*name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); - if (negative_lookup->IsFailure()) { - set_failure(Failure::cast(negative_lookup)); - return reg; - } + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // From now the object is in holder_reg. + reg = holder_reg; // From now on the object will be in holder_reg. __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); - } else if (heap()->InNewSpace(prototype)) { - // Get the map of the current object. - __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - - // Branch on the result of the map check. - __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map()))); - - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. + } else { + Handle<Map> current_map(current->map()); + __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK, + ALLOW_ELEMENT_TRANSITION_MAPS); + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - // Restore scratch register to be the map of the object. In the - // new space case below, we load the prototype from the map in - // the scratch register. - __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); + __ CheckAccessGlobalProxy(reg, scratch2, miss); } + reg = holder_reg; // From now on the object will be in holder_reg. - reg = holder_reg; // From now the object is in holder_reg. - // The prototype is in new space; we cannot store a reference - // to it in the code. Load it from the map. - __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); - } else { - // Check the map of the current object. - __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - // Branch on the result of the map check. - __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map()))); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); + if (heap()->InNewSpace(*prototype)) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ li(reg, Operand(prototype)); } - // The prototype is in old space; load it directly. - reg = holder_reg; // From now the object is in holder_reg. - __ li(reg, Operand(Handle<JSObject>(prototype))); } if (save_at_depth == depth) { @@ -1139,65 +1061,57 @@ Register StubCompiler::CheckPrototypes(JSObject* object, current = prototype; } - // Check the holder map. - __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map()))); - // Log the check depth. LOG(masm()->isolate(), IntEvent("check-maps-depth", depth + 1)); + + // Check the holder map. + __ CheckMap(reg, scratch1, Handle<Map>(current->map()), miss, + DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); + // Perform security check for access to the global object. ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); if (holder->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); - }; - - // If we've skipped any global objects, it's not enough to verify - // that their maps haven't changed. We also need to check that the - // property cell for the property is still empty. + } - MaybeObject* result = GenerateCheckPropertyCells(masm(), - object, - holder, - name, - scratch1, - miss); - if (result->IsFailure()) set_failure(Failure::cast(result)); + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); // Return the register containing the holder. return reg; } -void StubCompiler::GenerateLoadField(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadField(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, int index, - String* name, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. - __ And(scratch1, receiver, Operand(kSmiTagMask)); - __ Branch(miss, eq, scratch1, Operand(zero_reg)); + __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, - name, miss); + Register reg = CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); GenerateFastPropertyLoad(masm(), v0, reg, holder, index); __ Ret(); } -void StubCompiler::GenerateLoadConstant(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, - Object* value, - String* name, + Handle<JSFunction> value, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss, scratch1); @@ -1208,83 +1122,77 @@ void StubCompiler::GenerateLoadConstant(JSObject* object, scratch1, scratch2, scratch3, name, miss); // Return the constant value. - __ li(v0, Operand(Handle<Object>(value))); + __ LoadHeapObject(v0, value); __ Ret(); } -MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss) { +void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, + Handle<JSObject> holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss, scratch1); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, - name, miss); + Register reg = CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); // Build AccessorInfo::args_ list on the stack and push property name below // the exit frame to make GC aware of them and store pointers to them. __ push(receiver); __ mov(scratch2, sp); // scratch2 = AccessorInfo::args_ - Handle<AccessorInfo> callback_handle(callback); - if (heap()->InNewSpace(callback_handle->data())) { - __ li(scratch3, callback_handle); + if (heap()->InNewSpace(callback->data())) { + __ li(scratch3, callback); __ lw(scratch3, FieldMemOperand(scratch3, AccessorInfo::kDataOffset)); } else { - __ li(scratch3, Handle<Object>(callback_handle->data())); + __ li(scratch3, Handle<Object>(callback->data())); } __ Push(reg, scratch3, name_reg); __ mov(a2, scratch2); // Saved in case scratch2 == a1. __ mov(a1, sp); // a1 (first argument - see note below) = Handle<String> - Address getter_address = v8::ToCData<Address>(callback->getter()); - ApiFunction fun(getter_address); - // NOTE: the O32 abi requires a0 to hold a special pointer when returning a // struct from the function (which is currently the case). This means we pass // the arguments in a1-a2 instead of a0-a1. TryCallApiFunctionAndReturn // will handle setting up a0. const int kApiStackSpace = 1; - + FrameScope frame_scope(masm(), StackFrame::MANUAL); __ EnterExitFrame(false, kApiStackSpace); + // Create AccessorInfo instance on the stack above the exit frame with - // scratch2 (internal::Object **args_) as the data. + // scratch2 (internal::Object** args_) as the data. __ sw(a2, MemOperand(sp, kPointerSize)); // a2 (second argument - see note above) = AccessorInfo& __ Addu(a2, sp, kPointerSize); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. + const int kStackUnwindSpace = 4; + Address getter_address = v8::ToCData<Address>(callback->getter()); + ApiFunction fun(getter_address); ExternalReference ref = ExternalReference(&fun, ExternalReference::DIRECT_GETTER_CALL, masm()->isolate()); - // 4 args - will be freed later by LeaveExitFrame. - return masm()->TryCallApiFunctionAndReturn(ref, 4); + __ CallApiFunctionAndReturn(ref, kStackUnwindSpace); } -void StubCompiler::GenerateLoadInterceptor(JSObject* object, - JSObject* interceptor_holder, +void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> interceptor_holder, LookupResult* lookup, Register receiver, Register name_reg, Register scratch1, Register scratch2, Register scratch3, - String* name, + Handle<String> name, Label* miss) { ASSERT(interceptor_holder->HasNamedInterceptor()); ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -1296,13 +1204,13 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // and CALLBACKS, so inline only them, other cases may be added // later. bool compile_followup_inline = false; - if (lookup->IsProperty() && lookup->IsCacheable()) { + if (lookup->IsFound() && lookup->IsCacheable()) { if (lookup->type() == FIELD) { compile_followup_inline = true; } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsAccessorInfo() && - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { - compile_followup_inline = true; + lookup->GetCallbackObject()->IsAccessorInfo()) { + compile_followup_inline = + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL; } } @@ -1317,47 +1225,44 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // Save necessary data before invoking an interceptor. // Requires a frame to make GC aware of pushed pointers. - __ EnterInternalFrame(); - - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - // CALLBACKS case needs a receiver to be passed into C++ callback. - __ Push(receiver, holder_reg, name_reg); - } else { - __ Push(holder_reg, name_reg); - } - - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method). - CompileCallLoadPropertyWithInterceptor(masm(), - receiver, - holder_reg, - name_reg, - interceptor_holder); - - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex); - __ Branch(&interceptor_failed, eq, v0, Operand(scratch1)); - __ LeaveInternalFrame(); - __ Ret(); + { + FrameScope frame_scope(masm(), StackFrame::INTERNAL); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + // CALLBACKS case needs a receiver to be passed into C++ callback. + __ Push(receiver, holder_reg, name_reg); + } else { + __ Push(holder_reg, name_reg); + } + // Invoke an interceptor. Note: map checks from receiver to + // interceptor's holder has been compiled before (see a caller + // of this method). + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + interceptor_holder); + // Check if interceptor provided a value for property. If it's + // the case, return immediately. + Label interceptor_failed; + __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex); + __ Branch(&interceptor_failed, eq, v0, Operand(scratch1)); + frame_scope.GenerateLeaveFrame(); + __ Ret(); - __ bind(&interceptor_failed); - __ pop(name_reg); - __ pop(holder_reg); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - __ pop(receiver); + __ bind(&interceptor_failed); + __ pop(name_reg); + __ pop(holder_reg); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + __ pop(receiver); + } + // Leave the internal frame. } - - __ LeaveInternalFrame(); - // Check that the maps from interceptor's holder to lookup's holder // haven't changed. And load lookup's holder into |holder| register. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { holder_reg = CheckPrototypes(interceptor_holder, holder_reg, - lookup->holder(), + Handle<JSObject>(lookup->holder()), scratch1, scratch2, scratch3, @@ -1369,21 +1274,21 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), v0, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle<JSObject>(lookup->holder()), + lookup->GetFieldIndex()); __ Ret(); } else { // We found CALLBACKS property in prototype chain of interceptor's // holder. ASSERT(lookup->type() == CALLBACKS); - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); - ASSERT(callback != NULL); + Handle<AccessorInfo> callback( + AccessorInfo::cast(lookup->GetCallbackObject())); ASSERT(callback->getter() != NULL); // Tail call to runtime. // Important invariant in CALLBACKS case: the code above must be // structured to never clobber |receiver| register. - __ li(scratch2, Handle<AccessorInfo>(callback)); + __ li(scratch2, callback); // holder_reg is either receiver or scratch1. if (!receiver.is(holder_reg)) { ASSERT(scratch1.is(holder_reg)); @@ -1419,16 +1324,16 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ Branch(miss, ne, a2, Operand(Handle<String>(name))); + __ Branch(miss, ne, a2, Operand(name)); } } -void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, +void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, Label* miss) { ASSERT(holder->IsGlobalObject()); @@ -1441,7 +1346,7 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(a0, miss); } @@ -1450,15 +1355,16 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, } -void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, - Label* miss) { +void CallStubCompiler::GenerateLoadFunctionFromCell( + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Label* miss) { // Get the value from the cell. - __ li(a3, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ li(a3, Operand(cell)); __ lw(a1, FieldMemOperand(a3, JSGlobalPropertyCell::kValueOffset)); // Check that the cell contains the same function. - if (heap()->InNewSpace(function)) { + if (heap()->InNewSpace(*function)) { // We can't embed a pointer to a function in new space so we have // to verify that the shared function info is unchanged. This has // the nice side effect that multiple closures based on the same @@ -1473,27 +1379,24 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, __ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); __ Branch(miss, ne, t0, Operand(a3)); } else { - __ Branch(miss, ne, a1, Operand(Handle<JSFunction>(function))); + __ Branch(miss, ne, a1, Operand(function)); } } -MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = +void CallStubCompiler::GenerateMissBranch() { + Handle<Code> code = isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), kind_, - extra_ic_state_); - Object* obj; - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - __ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET); - return obj; + extra_state_); + __ Jump(code, RelocInfo::CODE_TARGET); } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, +Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -1513,23 +1416,23 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, Register reg = CheckPrototypes(object, a0, holder, a1, a3, t0, name, &miss); GenerateFastPropertyLoad(masm(), a1, reg, holder, index); - GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_); + GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); } -MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPushCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -1539,7 +1442,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss; @@ -1555,8 +1458,8 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ JumpIfSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), receiver, - holder, a3, v0, t0, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, a3, v0, t0, + name, &miss); if (argc == 0) { // Nothing to do, just return the length. @@ -1565,22 +1468,20 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ Ret(); } else { Label call_builtin; + if (argc == 1) { // Otherwise fall through to call the builtin. + Label attempt_to_grow_elements; - Register elements = a3; - Register end_elements = t1; - - // Get the elements array of the object. - __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); - - // Check that the elements are in fast mode and writable. - __ CheckMap(elements, - v0, - Heap::kFixedArrayMapRootIndex, - &call_builtin, - DONT_DO_SMI_CHECK); + Register elements = t2; + Register end_elements = t1; + // Get the elements array of the object. + __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); - if (argc == 1) { // Otherwise fall through to call the builtin. - Label exit, with_write_barrier, attempt_to_grow_elements; + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, + v0, + Heap::kFixedArrayMapRootIndex, + &call_builtin, + DONT_DO_SMI_CHECK); // Get the array's length into v0 and calculate new length. __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset)); @@ -1588,35 +1489,77 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, STATIC_ASSERT(kSmiTag == 0); __ Addu(v0, v0, Operand(Smi::FromInt(argc))); - // Get the element's length. + // Get the elements' length. __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); // Check if we could survive without allocation. __ Branch(&attempt_to_grow_elements, gt, v0, Operand(t0)); + // Check if value is a smi. + Label with_write_barrier; + __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize)); + __ JumpIfNotSmi(t0, &with_write_barrier); + // Save new length. __ sw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset)); - // Push the element. - __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize)); + // Store the value. // We may need a register containing the address end_elements below, // so write back the value in end_elements. __ sll(end_elements, v0, kPointerSizeLog2 - kSmiTagSize); __ Addu(end_elements, elements, end_elements); const int kEndElementsOffset = FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; - __ sw(t0, MemOperand(end_elements, kEndElementsOffset)); - __ Addu(end_elements, end_elements, kPointerSize); + __ Addu(end_elements, end_elements, kEndElementsOffset); + __ sw(t0, MemOperand(end_elements)); // Check for a smi. - __ JumpIfNotSmi(t0, &with_write_barrier); - __ bind(&exit); __ Drop(argc + 1); __ Ret(); __ bind(&with_write_barrier); - __ InNewSpace(elements, t0, eq, &exit); - __ RecordWriteHelper(elements, end_elements, t0); + + __ lw(a3, FieldMemOperand(receiver, HeapObject::kMapOffset)); + + if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) { + Label fast_object, not_fast_object; + __ CheckFastObjectElements(a3, t3, ¬_fast_object); + __ jmp(&fast_object); + // In case of fast smi-only, convert to fast object, otherwise bail out. + __ bind(¬_fast_object); + __ CheckFastSmiOnlyElements(a3, t3, &call_builtin); + // edx: receiver + // r3: map + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + a3, + t3, + &call_builtin); + __ mov(a2, receiver); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + __ bind(&fast_object); + } else { + __ CheckFastObjectElements(a3, a3, &call_builtin); + } + + // Save new length. + __ sw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Store the value. + // We may need a register containing the address end_elements below, + // so write back the value in end_elements. + __ sll(end_elements, v0, kPointerSizeLog2 - kSmiTagSize); + __ Addu(end_elements, elements, end_elements); + __ Addu(end_elements, end_elements, kEndElementsOffset); + __ sw(t0, MemOperand(end_elements)); + + __ RecordWrite(elements, + end_elements, + t0, + kRAHasNotBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); __ Drop(argc + 1); __ Ret(); @@ -1628,6 +1571,15 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ Branch(&call_builtin); } + __ lw(a2, MemOperand(sp, (argc - 1) * kPointerSize)); + // Growing elements that are SMI-only requires special handling in case + // the new element is non-Smi. For now, delegate to the builtin. + Label no_fast_elements_check; + __ JumpIfSmi(a2, &no_fast_elements_check); + __ lw(t3, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ CheckFastObjectElements(t3, t3, &call_builtin); + __ bind(&no_fast_elements_check); + ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address( masm()->isolate()); @@ -1641,24 +1593,23 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ Addu(end_elements, elements, end_elements); __ Addu(end_elements, end_elements, Operand(kEndElementsOffset)); __ li(t3, Operand(new_space_allocation_top)); - __ lw(t2, MemOperand(t3)); - __ Branch(&call_builtin, ne, end_elements, Operand(t2)); + __ lw(a3, MemOperand(t3)); + __ Branch(&call_builtin, ne, end_elements, Operand(a3)); __ li(t5, Operand(new_space_allocation_limit)); __ lw(t5, MemOperand(t5)); - __ Addu(t2, t2, Operand(kAllocationDelta * kPointerSize)); - __ Branch(&call_builtin, hi, t2, Operand(t5)); + __ Addu(a3, a3, Operand(kAllocationDelta * kPointerSize)); + __ Branch(&call_builtin, hi, a3, Operand(t5)); // We fit and could grow elements. // Update new_space_allocation_top. - __ sw(t2, MemOperand(t3)); + __ sw(a3, MemOperand(t3)); // Push the argument. - __ lw(t2, MemOperand(sp, (argc - 1) * kPointerSize)); - __ sw(t2, MemOperand(end_elements)); + __ sw(a2, MemOperand(end_elements)); // Fill the rest with holes. - __ LoadRoot(t2, Heap::kTheHoleValueRootIndex); + __ LoadRoot(a3, Heap::kTheHoleValueRootIndex); for (int i = 1; i < kAllocationDelta; i++) { - __ sw(t2, MemOperand(end_elements, i * kPointerSize)); + __ sw(a3, MemOperand(end_elements, i * kPointerSize)); } // Update elements' and array's sizes. @@ -1679,19 +1630,19 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPopCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -1701,25 +1652,22 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss, return_undefined, call_builtin; - Register receiver = a1; Register elements = a3; - GenerateNameCheck(name, &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); __ lw(receiver, MemOperand(sp, argc * kPointerSize)); - // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), - receiver, holder, elements, t0, v0, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, elements, + t0, v0, name, &miss); // Get the elements array of the object. __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); @@ -1768,20 +1716,19 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : function name // -- ra : return address @@ -1791,10 +1738,9 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); - Label miss; Label name_miss; Label index_out_of_range; @@ -1802,7 +1748,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Label* index_out_of_range_label = &index_out_of_range; if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } @@ -1814,13 +1760,12 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Context::STRING_FUNCTION_INDEX, v0, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), v0, holder, - a1, a3, t0, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + v0, holder, a1, a3, t0, name, &miss); Register receiver = a1; Register index = t1; - Register scratch = a3; Register result = v0; __ lw(receiver, MemOperand(sp, argc * kPointerSize)); if (argc > 0) { @@ -1829,20 +1774,19 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharCodeAtGenerator char_code_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_code_at_generator.GenerateFast(masm()); + StringCharCodeAtGenerator generator(receiver, + index, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_code_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1853,22 +1797,21 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ bind(&miss); // Restore function name in a2. - __ li(a2, Handle<String>(name)); + __ li(a2, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : function name // -- ra : return address @@ -1878,21 +1821,18 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); - Label miss; Label name_miss; Label index_out_of_range; Label* index_out_of_range_label = &index_out_of_range; - if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. @@ -1900,14 +1840,13 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( Context::STRING_FUNCTION_INDEX, v0, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), v0, holder, - a1, a3, t0, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + v0, holder, a1, a3, t0, name, &miss); Register receiver = v0; Register index = t1; - Register scratch1 = a1; - Register scratch2 = a3; + Register scratch = a3; Register result = v0; __ lw(receiver, MemOperand(sp, argc * kPointerSize)); if (argc > 0) { @@ -1916,21 +1855,20 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharAtGenerator char_at_generator(receiver, - index, - scratch1, - scratch2, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_at_generator.GenerateFast(masm()); + StringCharAtGenerator generator(receiver, + index, + scratch, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1941,22 +1879,21 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ bind(&miss); // Restore function name in a2. - __ li(a2, Handle<String>(name)); + __ li(a2, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : function name // -- ra : return address @@ -1969,22 +1906,23 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ lw(a1, MemOperand(sp, 1 * kPointerSize)); STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(a1, &miss); - CheckPrototypes(JSObject::cast(object), a1, holder, v0, a3, t0, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, v0, a3, t0, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2000,34 +1938,35 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // Convert the smi code to uint16. __ And(code, code, Operand(Smi::FromInt(0xffff))); - StringCharFromCodeGenerator char_from_code_generator(code, v0); - char_from_code_generator.GenerateFast(masm()); + StringCharFromCodeGenerator generator(code, v0); + generator.GenerateFast(masm()); __ Drop(argc + 1); __ Ret(); StubRuntimeCallHelper call_helper; - char_from_code_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // a2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathFloorCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : function name // -- ra : return address @@ -2036,30 +1975,29 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, // -- sp[argc * 4] : receiver // ----------------------------------- - if (!CpuFeatures::IsSupported(FPU)) - return heap()->undefined_value(); - CpuFeatures::Scope scope_fpu(FPU); + if (!CpuFeatures::IsSupported(FPU)) { + return Handle<Code>::null(); + } + CpuFeatures::Scope scope_fpu(FPU); const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss, slow; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ lw(a1, MemOperand(sp, 1 * kPointerSize)); - STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(a1, &miss); - - CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2145,23 +2083,24 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ bind(&slow); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // a2: function name. - MaybeObject* obj = GenerateMissBranch(); - if (obj->IsFailure()) return obj; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathAbsCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : function name // -- ra : return address @@ -2171,25 +2110,23 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // ----------------------------------- const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; - GenerateNameCheck(name, &miss); - if (cell == NULL) { + GenerateNameCheck(name, &miss); + if (cell.is_null()) { __ lw(a1, MemOperand(sp, 1 * kPointerSize)); - STATIC_ASSERT(kSmiTag == 0); __ JumpIfSmi(a1, &miss); - - CheckPrototypes(JSObject::cast(object), a1, holder, v0, a3, t0, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, v0, a3, t0, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -2247,37 +2184,37 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ bind(&miss); // a2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileFastApiCall( +Handle<Code> CallStubCompiler::CompileFastApiCall( const CallOptimization& optimization, - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { Counters* counters = isolate()->counters(); ASSERT(optimization.is_simple_api_call()); // Bail out if object is a global object as we don't want to // repatch it to global receiver. - if (object->IsGlobalObject()) return heap()->undefined_value(); - if (cell != NULL) return heap()->undefined_value(); - if (!object->IsJSObject()) return heap()->undefined_value(); + if (object->IsGlobalObject()) return Handle<Code>::null(); + if (!cell.is_null()) return Handle<Code>::null(); + if (!object->IsJSObject()) return Handle<Code>::null(); int depth = optimization.GetPrototypeDepthOfExpectedType( - JSObject::cast(object), holder); - if (depth == kInvalidProtoDepth) return heap()->undefined_value(); + Handle<JSObject>::cast(object), holder); + if (depth == kInvalidProtoDepth) return Handle<Code>::null(); Label miss, miss_before_stack_reserved; @@ -2296,40 +2233,37 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( ReserveSpaceForFastApiCall(masm(), a0); // Check that the maps haven't changed and find a Holder as a side effect. - CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name, + CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0, name, depth, &miss); - MaybeObject* result = GenerateFastApiDirectCall(masm(), optimization, argc); - if (result->IsFailure()) return result; + GenerateFastApiDirectCall(masm(), optimization, argc); __ bind(&miss); FreeSpaceForFastApiCall(masm()); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, - JSObject* holder, - JSFunction* function, - String* name, +Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function, + Handle<String> name, CheckType check) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, NULL, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // Undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, + Handle<JSGlobalPropertyCell>::null(), + function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; @@ -2342,23 +2276,20 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the receiver isn't a smi. if (check != NUMBER_CHECK) { - __ And(t1, a1, Operand(kSmiTagMask)); - __ Branch(&miss, eq, t1, Operand(zero_reg)); + __ JumpIfSmi(a1, &miss); } // Make sure that it's okay not to patch the on stack receiver // unless we're doing a receiver map check. ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); - - SharedFunctionInfo* function_info = function->shared(); switch (check) { case RECEIVER_MAP_CHECK: __ IncrementCounter(masm()->isolate()->counters()->call_const(), 1, a0, a3); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0, + name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -2369,50 +2300,46 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, break; case STRING_CHECK: - if (!function->IsBuiltin() && !function_info->strict_mode()) { - // Calling non-strict non-builtins with a value as the receiver - // requires boxing. - __ jmp(&miss); - } else { + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { // Check that the object is a two-byte string or a symbol. __ GetObjectType(a1, a3, a3); __ Branch(&miss, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE)); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::STRING_FUNCTION_INDEX, a0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3, - a1, t0, name, &miss); - } - break; - - case NUMBER_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + a0, holder, a3, a1, t0, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case NUMBER_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a smi or a heap number. - __ And(t1, a1, Operand(kSmiTagMask)); - __ Branch(&fast, eq, t1, Operand(zero_reg)); + __ JumpIfSmi(a1, &fast); __ GetObjectType(a1, a0, a0); __ Branch(&miss, ne, a0, Operand(HEAP_NUMBER_TYPE)); __ bind(&fast); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::NUMBER_FUNCTION_INDEX, a0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3, - a1, t0, name, &miss); - } - break; - } - - case BOOLEAN_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + a0, holder, a3, a1, t0, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case BOOLEAN_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a boolean. __ LoadRoot(t0, Heap::kTrueValueRootIndex); @@ -2423,35 +2350,36 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::BOOLEAN_FUNCTION_INDEX, a0, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3, - a1, t0, name, &miss); + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + a0, holder, a3, a1, t0, name, &miss); + } else { + // Calling non-strict non-builtins with a value as the receiver + // requires boxing. + __ jmp(&miss); } break; } - default: - UNREACHABLE(); - } - - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - __ InvokeFunction(function, arguments(), JUMP_FUNCTION, call_kind); + __ InvokeFunction( + function, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address @@ -2463,71 +2391,54 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Get the number of arguments. const int argc = arguments().immediate(); - - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // Get the receiver from the stack. __ lw(a1, MemOperand(sp, argc * kPointerSize)); - CallInterceptorCompiler compiler(this, arguments(), a2, extra_ic_state_); - MaybeObject* result = compiler.Compile(masm(), - object, - holder, - name, - &lookup, - a1, - a3, - t0, - a0, - &miss); - if (result->IsFailure()) { - return result; - } + CallInterceptorCompiler compiler(this, arguments(), a2, extra_state_); + compiler.Compile(masm(), object, holder, name, &lookup, a1, a3, t0, a0, + &miss); // Move returned value, the function to call, to a1. __ mov(a1, v0); // Restore receiver. __ lw(a0, MemOperand(sp, argc * kPointerSize)); - GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_); + GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(INTERCEPTOR, name); } -MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileCallGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- a2 : name // -- ra : return address // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, cell, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // Undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, cell, function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - GenerateGlobalReceiverCheck(object, holder, name, &miss); GenerateLoadFunctionFromCell(cell, function, &miss); @@ -2538,40 +2449,37 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, __ sw(a3, MemOperand(sp, argc * kPointerSize)); } - // Setup the context (function already in r1). + // Set up the context (function already in r1). __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); // Jump to the cached code (tail call). Counters* counters = masm()->isolate()->counters(); __ IncrementCounter(counters->call_global_inline(), 1, a3, t0); - ASSERT(function->is_compiled()); - Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - if (V8::UseCrankshaft()) { - UNIMPLEMENTED_MIPS(); - } else { - __ InvokeCode(code, expected, arguments(), RelocInfo::CODE_TARGET, - JUMP_FUNCTION, call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + __ lw(a3, FieldMemOperand(a1, JSFunction::kCodeEntryOffset)); + __ InvokeCode(a3, expected, arguments(), JUMP_FUNCTION, + NullCallWrapper(), call_kind); // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1, a1, a3); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); } -MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : receiver @@ -2581,25 +2489,21 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, Label miss; // Name register might be clobbered. - GenerateStoreField(masm(), - object, - index, - transition, - a1, a2, a3, - &miss); + GenerateStoreField(masm(), object, index, transition, a1, a2, a3, &miss); __ bind(&miss); __ li(a2, Operand(Handle<String>(name))); // Restore name. Handle<Code> ic = masm()->isolate()->builtins()->Builtins::StoreIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, - AccessorInfo* callback, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreCallback( + Handle<JSObject> object, + Handle<AccessorInfo> callback, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : receiver @@ -2608,12 +2512,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(a1, &miss); - // Check that the map of the object hasn't changed. - __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset)); - __ Branch(&miss, ne, a3, Operand(Handle<Map>(object->map()))); + __ CheckMap(a1, a3, Handle<Map>(object->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -2625,7 +2526,7 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); __ push(a1); // Receiver. - __ li(a3, Operand(Handle<AccessorInfo>(callback))); // Callback info. + __ li(a3, Operand(callback)); // Callback info. __ Push(a3, a2, a0); // Do tail-call to the runtime system. @@ -2644,8 +2545,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, } -MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreInterceptor( + Handle<JSObject> receiver, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : receiver @@ -2654,12 +2556,9 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(a1, &miss); - // Check that the map of the object hasn't changed. - __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset)); - __ Branch(&miss, ne, a3, Operand(Handle<Map>(receiver->map()))); + __ CheckMap(a1, a3, Handle<Map>(receiver->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (receiver->IsJSGlobalProxy()) { @@ -2691,9 +2590,10 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, } -MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, - JSGlobalPropertyCell* cell, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreGlobal( + Handle<GlobalObject> object, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : receiver @@ -2710,7 +2610,7 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, // cell could have been deleted and reintroducing the global needs // to update the property details in the property dictionary of the // global object. We bail out to the runtime system to do that. - __ li(t0, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ li(t0, Operand(cell)); __ LoadRoot(t1, Heap::kTheHoleValueRootIndex); __ lw(t2, FieldMemOperand(t0, JSGlobalPropertyCell::kValueOffset)); __ Branch(&miss, eq, t1, Operand(t2)); @@ -2718,6 +2618,8 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, // Store the value in the cell. __ sw(a0, FieldMemOperand(t0, JSGlobalPropertyCell::kValueOffset)); __ mov(v0, a0); // Stored value must be returned in v0. + // Cells are always rescanned, so no write barrier here. + Counters* counters = masm()->isolate()->counters(); __ IncrementCounter(counters->named_store_global_inline(), 1, a1, a3); __ Ret(); @@ -2733,9 +2635,9 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last) { +Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> last) { // ----------- S t a t e ------------- // -- a0 : receiver // -- ra : return address @@ -2751,15 +2653,8 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - a1, - &miss); - if (cell->IsFailure()) { - miss.Unuse(); - return cell; - } + GenerateCheckPropertyCell( + masm(), Handle<GlobalObject>::cast(last), name, a1, &miss); } // Return undefined if maps of the full prototype chain is still the same. @@ -2770,14 +2665,14 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NONEXISTENT, heap()->empty_string()); + return GetCode(NONEXISTENT, factory()->empty_string()); } -MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, - JSObject* holder, +Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : receiver // -- a2 : name @@ -2796,24 +2691,19 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> LoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- a0 : receiver // -- a2 : name // -- ra : return address // ----------------------------------- Label miss; - - MaybeObject* result = GenerateLoadCallback(object, holder, a0, a2, a3, a1, t0, - callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(object, holder, a0, a2, a3, a1, t0, callback, name, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2822,10 +2712,10 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, } -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : receiver // -- a2 : name @@ -2842,9 +2732,9 @@ MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : receiver // -- a2 : name @@ -2853,17 +2743,9 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, // ----------------------------------- Label miss; - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(object, - holder, - &lookup, - a0, - a2, - a3, - a1, - t0, - name, + GenerateLoadInterceptor(object, holder, &lookup, a0, a2, a3, a1, t0, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2873,11 +2755,12 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete) { +Handle<Code> LoadStubCompiler::CompileLoadGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name, + bool is_dont_delete) { // ----------- S t a t e ------------- // -- a0 : receiver // -- a2 : name @@ -2888,16 +2771,15 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { - __ And(t0, a0, Operand(kSmiTagMask)); - __ Branch(&miss, eq, t0, Operand(zero_reg)); + if (!object.is_identical_to(holder)) { + __ JumpIfSmi(a0, &miss); } // Check that the map of the global has not changed. CheckPrototypes(object, a0, holder, a3, t0, a1, name, &miss); // Get the value from the cell. - __ li(a3, Operand(Handle<JSGlobalPropertyCell>(cell))); + __ li(a3, Operand(cell)); __ lw(t0, FieldMemOperand(a3, JSGlobalPropertyCell::kValueOffset)); // Check for deleted property if property can actually be deleted. @@ -2920,9 +2802,9 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int index) { // ----------- S t a t e ------------- // -- ra : return address @@ -2932,7 +2814,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, Label miss; // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); GenerateLoadField(receiver, holder, a1, a2, a3, t0, index, name, &miss); __ bind(&miss); @@ -2942,11 +2824,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -2955,15 +2837,10 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( Label miss; // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); - - MaybeObject* result = GenerateLoadCallback(receiver, holder, a1, a0, a2, a3, - t0, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } + __ Branch(&miss, ne, a0, Operand(name)); + GenerateLoadCallback(receiver, holder, a1, a0, a2, a3, t0, callback, name, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2971,10 +2848,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( } -MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -2983,7 +2861,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, Label miss; // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); GenerateLoadConstant(receiver, holder, a1, a2, a3, t0, value, name, &miss); __ bind(&miss); @@ -2994,9 +2872,10 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor( + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -3005,19 +2884,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, Label miss; // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(receiver, - holder, - &lookup, - a1, - a0, - a2, - a3, - t0, - name, + GenerateLoadInterceptor(receiver, holder, &lookup, a1, a0, a2, a3, t0, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -3026,7 +2897,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -3035,7 +2907,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { Label miss; // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); GenerateLoadArrayLength(masm(), a1, a2, &miss); __ bind(&miss); @@ -3045,7 +2917,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -3057,7 +2930,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ IncrementCounter(counters->keyed_load_string_length(), 1, a2, a3); // Check the key is the cached one. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); GenerateLoadStringLength(masm(), a1, a2, a3, &miss, true); __ bind(&miss); @@ -3069,7 +2942,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype( + Handle<String> name) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -3081,7 +2955,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { __ IncrementCounter(counters->keyed_load_function_prototype(), 1, a2, a3); // Check the name hasn't changed. - __ Branch(&miss, ne, a0, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a0, Operand(name)); GenerateLoadFunctionPrototype(masm(), a1, a2, a3, &miss); __ bind(&miss); @@ -3092,33 +2966,29 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key // -- a1 : receiver // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); - MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(a1, - a2, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode(); + + __ DispatchMap(a1, a2, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_ics) { // ----------- S t a t e ------------- // -- ra : return address // -- a0 : key @@ -3130,9 +3000,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( int receiver_count = receiver_maps->length(); __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - Handle<Code> code(handler_ics->at(current)); - __ Jump(code, RelocInfo::CODE_TARGET, eq, a2, Operand(map)); + __ Jump(handler_ics->at(current), RelocInfo::CODE_TARGET, + eq, a2, Operand(receiver_maps->at(current))); } __ bind(&miss); @@ -3140,14 +3009,14 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( __ Jump(miss_ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : key @@ -3161,16 +3030,11 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ IncrementCounter(counters->keyed_store_field(), 1, a3, t0); // Check that the name has not changed. - __ Branch(&miss, ne, a1, Operand(Handle<String>(name))); + __ Branch(&miss, ne, a1, Operand(name)); // a3 is used as scratch register. a1 and a2 keep their values if a jump to // the miss label is generated. - GenerateStoreField(masm(), - object, - index, - transition, - a2, a1, a3, - &miss); + GenerateStoreField(masm(), object, index, transition, a2, a1, a3, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_store_field(), 1, a3, t0); @@ -3178,11 +3042,12 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { +Handle<Code> KeyedStoreStubCompiler::CompileStoreElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : key @@ -3190,29 +3055,25 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- ra : return address // -- a3 : scratch // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - MaybeObject* maybe_stub = - KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(a2, - a3, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = + KeyedStoreElementStub(is_js_array, elements_kind).GetCode(); + + __ DispatchMap(a2, a3, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss(); __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_stubs, + MapHandleList* transitioned_maps) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : key @@ -3225,10 +3086,17 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( int receiver_count = receiver_maps->length(); __ lw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); - for (int current = 0; current < receiver_count; ++current) { - Handle<Map> map(receiver_maps->at(current)); - Handle<Code> code(handler_ics->at(current)); - __ Jump(code, RelocInfo::CODE_TARGET, eq, a3, Operand(map)); + for (int i = 0; i < receiver_count; ++i) { + if (transitioned_maps->at(i).is_null()) { + __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, eq, + a3, Operand(receiver_maps->at(i))); + } else { + Label next_map; + __ Branch(&next_map, ne, a3, Operand(receiver_maps->at(i))); + __ li(a3, Operand(transitioned_maps->at(i))); + __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET); + __ bind(&next_map); + } } __ bind(&miss); @@ -3236,11 +3104,12 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( __ Jump(miss_ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { +Handle<Code> ConstructStubCompiler::CompileConstructStub( + Handle<JSFunction> function) { // a0 : argc // a1 : constructor // ra : return address @@ -3263,8 +3132,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // a1: constructor function // t7: undefined __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset)); - __ And(t0, a2, Operand(kSmiTagMask)); - __ Branch(&generic_stub_call, eq, t0, Operand(zero_reg)); + __ JumpIfSmi(a2, &generic_stub_call); __ GetObjectType(a2, a3, t0); __ Branch(&generic_stub_call, ne, t0, Operand(MAP_TYPE)); @@ -3285,12 +3153,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // a2: initial map // t7: undefined __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset)); - __ AllocateInNewSpace(a3, - t4, - t5, - t6, - &generic_stub_call, - SIZE_IN_WORDS); + __ AllocateInNewSpace(a3, t4, t5, t6, &generic_stub_call, SIZE_IN_WORDS); // Allocated the JSObject, now initialize the fields. Map is set to initial // map and properties and elements are set to empty fixed array. @@ -3325,7 +3188,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // t7: undefined // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. - SharedFunctionInfo* shared = function->shared(); + Handle<SharedFunctionInfo> shared(function->shared()); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { Label not_passed, next; @@ -3457,6 +3320,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) { case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -3553,6 +3417,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3795,9 +3660,9 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1); __ bind(&miss_force_generic); - Code* stub = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET); + Handle<Code> stub = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(stub, RelocInfo::CODE_TARGET); } @@ -3828,7 +3693,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset)); // Check that the index is in range. - __ SmiUntag(t0, key); __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ Branch(&miss_force_generic, Ugreater_equal, key, Operand(t1)); @@ -3836,7 +3700,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Handle both smis and HeapNumbers in the fast path. Go to the // runtime for all other kinds of values. // a3: external array. - // t0: key (integer). if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) { // Double to pixel conversion is only implemented in the runtime for now. @@ -3848,7 +3711,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset)); // a3: base pointer of external storage. - // t0: key (integer). // t1: value (integer). switch (elements_kind) { @@ -3865,33 +3727,36 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ mov(v0, t1); // Value is in range 0..255. __ bind(&done); __ mov(t1, v0); - __ addu(t8, a3, t0); + + __ srl(t8, key, 1); + __ addu(t8, a3, t8); __ sb(t1, MemOperand(t8, 0)); } break; case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ addu(t8, a3, t0); + __ srl(t8, key, 1); + __ addu(t8, a3, t8); __ sb(t1, MemOperand(t8, 0)); break; case EXTERNAL_SHORT_ELEMENTS: case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ sll(t8, t0, 1); - __ addu(t8, a3, t8); + __ addu(t8, a3, key); __ sh(t1, MemOperand(t8, 0)); break; case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ sll(t8, t0, 2); + __ sll(t8, key, 1); __ addu(t8, a3, t8); __ sw(t1, MemOperand(t8, 0)); break; case EXTERNAL_FLOAT_ELEMENTS: // Perform int-to-float conversion and store to memory. + __ SmiUntag(t0, key); StoreIntAsFloat(masm, a3, t0, t1, t2, t3, t4); break; case EXTERNAL_DOUBLE_ELEMENTS: - __ sll(t8, t0, 3); + __ sll(t8, key, 2); __ addu(a3, a3, t8); // a3: effective address of the double element FloatingPointHelper::Destination destination; @@ -3913,6 +3778,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3921,12 +3787,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } // Entry registers are intact, a0 holds the value which is the return value. - __ mov(v0, value); + __ mov(v0, a0); __ Ret(); if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) { // a3: external array. - // t0: index (integer). __ bind(&check_heap_number); __ GetObjectType(value, t1, t2); __ Branch(&slow, ne, t2, Operand(HEAP_NUMBER_TYPE)); @@ -3934,7 +3799,6 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset)); // a3: base pointer of external storage. - // t0: key (integer). // The WebGL specification leaves the behavior of storing NaN and // +/-Infinity into integer arrays basically undefined. For more @@ -3947,11 +3811,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ cvt_s_d(f0, f0); - __ sll(t8, t0, 2); + __ sll(t8, key, 1); __ addu(t8, a3, t8); __ swc1(f0, MemOperand(t8, 0)); } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { - __ sll(t8, t0, 3); + __ sll(t8, key, 2); __ addu(t8, a3, t8); __ sdc1(f0, MemOperand(t8, 0)); } else { @@ -3960,18 +3824,18 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( switch (elements_kind) { case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ addu(t8, a3, t0); + __ srl(t8, key, 1); + __ addu(t8, a3, t8); __ sb(t3, MemOperand(t8, 0)); break; case EXTERNAL_SHORT_ELEMENTS: case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ sll(t8, t0, 1); - __ addu(t8, a3, t8); + __ addu(t8, a3, key); __ sh(t3, MemOperand(t8, 0)); break; case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ sll(t8, t0, 2); + __ sll(t8, key, 1); __ addu(t8, a3, t8); __ sw(t3, MemOperand(t8, 0)); break; @@ -3979,6 +3843,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3989,7 +3854,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Entry registers are intact, a0 holds the value // which is the return value. - __ mov(v0, value); + __ mov(v0, a0); __ Ret(); } else { // FPU is not available, do manual conversions. @@ -4044,13 +3909,13 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ or_(t3, t7, t6); __ bind(&done); - __ sll(t9, a1, 2); + __ sll(t9, key, 1); __ addu(t9, a2, t9); __ sw(t3, MemOperand(t9, 0)); // Entry registers are intact, a0 holds the value which is the return // value. - __ mov(v0, value); + __ mov(v0, a0); __ Ret(); __ bind(&nan_or_infinity_or_zero); @@ -4068,6 +3933,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // t8: effective address of destination element. __ sw(t4, MemOperand(t8, 0)); __ sw(t3, MemOperand(t8, Register::kSizeInBytes)); + __ mov(v0, a0); __ Ret(); } else { bool is_signed_type = IsElementTypeSigned(elements_kind); @@ -4130,18 +3996,18 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( switch (elements_kind) { case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - __ addu(t8, a3, t0); + __ srl(t8, key, 1); + __ addu(t8, a3, t8); __ sb(t3, MemOperand(t8, 0)); break; case EXTERNAL_SHORT_ELEMENTS: case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - __ sll(t8, t0, 1); - __ addu(t8, a3, t8); + __ addu(t8, a3, key); __ sh(t3, MemOperand(t8, 0)); break; case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - __ sll(t8, t0, 2); + __ sll(t8, key, 1); __ addu(t8, a3, t8); __ sw(t3, MemOperand(t8, 0)); break; @@ -4149,6 +4015,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -4223,9 +4090,9 @@ void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) { __ Ret(); __ bind(&miss_force_generic); - Code* stub = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET); + Handle<Code> stub = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(stub, RelocInfo::CODE_TARGET); } @@ -4298,8 +4165,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- a0 : value // -- a1 : key @@ -4308,7 +4177,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, // -- a3 : scratch // -- a4 : scratch (elements) // ----------------------------------- - Label miss_force_generic; + Label miss_force_generic, transition_elements_kind; Register value_reg = a0; Register key_reg = a1; @@ -4342,14 +4211,32 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, // Compare smis. __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch)); - __ Addu(scratch, - elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); - __ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize); - __ Addu(scratch3, scratch2, scratch); - __ sw(value_reg, MemOperand(scratch3)); - __ RecordWrite(scratch, Operand(scratch2), receiver_reg , elements_reg); - + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + __ JumpIfNotSmi(value_reg, &transition_elements_kind); + __ Addu(scratch, + elements_reg, + Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + __ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize); + __ Addu(scratch, scratch, scratch2); + __ sw(value_reg, MemOperand(scratch)); + } else { + ASSERT(elements_kind == FAST_ELEMENTS); + __ Addu(scratch, + elements_reg, + Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + __ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize); + __ Addu(scratch, scratch, scratch2); + __ sw(value_reg, MemOperand(scratch)); + __ mov(receiver_reg, value_reg); + ASSERT(elements_kind == FAST_ELEMENTS); + __ RecordWrite(elements_reg, // Object. + scratch, // Address. + receiver_reg, // Value. + kRAHasNotBeenSaved, + kDontSaveFPRegs); + } // value_reg (a0) is preserved. // Done. __ Ret(); @@ -4358,6 +4245,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ Jump(ic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ Jump(ic_miss, RelocInfo::CODE_TARGET); } @@ -4375,15 +4266,15 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // -- t2 : scratch (exponent_reg) // -- t3 : scratch4 // ----------------------------------- - Label miss_force_generic, smi_value, is_nan, maybe_nan, have_double_value; + Label miss_force_generic, transition_elements_kind; Register value_reg = a0; Register key_reg = a1; Register receiver_reg = a2; - Register scratch = a3; - Register elements_reg = t0; - Register mantissa_reg = t1; - Register exponent_reg = t2; + Register elements_reg = a3; + Register scratch1 = t0; + Register scratch2 = t1; + Register scratch3 = t2; Register scratch4 = t3; // This stub is meant to be tail-jumped to, the receiver must already @@ -4395,90 +4286,25 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // Check that the key is within bounds. if (is_js_array) { - __ lw(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset)); + __ lw(scratch1, FieldMemOperand(receiver_reg, JSArray::kLengthOffset)); } else { - __ lw(scratch, + __ lw(scratch1, FieldMemOperand(elements_reg, FixedArray::kLengthOffset)); } // Compare smis, unsigned compare catches both negative and out-of-bound // indexes. - __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch)); - - // Handle smi values specially. - __ JumpIfSmi(value_reg, &smi_value); - - // Ensure that the object is a heap number - __ CheckMap(value_reg, - scratch, - masm->isolate()->factory()->heap_number_map(), - &miss_force_generic, - DONT_DO_SMI_CHECK); - - // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000 - // in the exponent. - __ li(scratch, Operand(kNaNOrInfinityLowerBoundUpper32)); - __ lw(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset)); - __ Branch(&maybe_nan, ge, exponent_reg, Operand(scratch)); - - __ lw(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); - - __ bind(&have_double_value); - __ sll(scratch4, key_reg, kDoubleSizeLog2 - kSmiTagSize); - __ Addu(scratch, elements_reg, Operand(scratch4)); - __ sw(mantissa_reg, FieldMemOperand(scratch, FixedDoubleArray::kHeaderSize)); - uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); - __ sw(exponent_reg, FieldMemOperand(scratch, offset)); - __ Ret(USE_DELAY_SLOT); - __ mov(v0, value_reg); // In delay slot. - - __ bind(&maybe_nan); - // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise - // it's an Infinity, and the non-NaN code path applies. - __ li(scratch, Operand(kNaNOrInfinityLowerBoundUpper32)); - __ Branch(&is_nan, gt, exponent_reg, Operand(scratch)); - __ lw(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); - __ Branch(&have_double_value, eq, mantissa_reg, Operand(zero_reg)); - - __ bind(&is_nan); - // Load canonical NaN for storing into the double array. - uint64_t nan_int64 = BitCast<uint64_t>( - FixedDoubleArray::canonical_not_the_hole_nan_as_double()); - __ li(mantissa_reg, Operand(static_cast<uint32_t>(nan_int64))); - __ li(exponent_reg, Operand(static_cast<uint32_t>(nan_int64 >> 32))); - __ jmp(&have_double_value); - - __ bind(&smi_value); - __ Addu(scratch, elements_reg, - Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); - __ sll(scratch4, key_reg, kDoubleSizeLog2 - kSmiTagSize); - __ Addu(scratch, scratch, scratch4); - // scratch is now effective address of the double element - - FloatingPointHelper::Destination destination; - if (CpuFeatures::IsSupported(FPU)) { - destination = FloatingPointHelper::kFPURegisters; - } else { - destination = FloatingPointHelper::kCoreRegisters; - } + __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch1)); + + __ StoreNumberToDoubleElements(value_reg, + key_reg, + receiver_reg, + elements_reg, + scratch1, + scratch2, + scratch3, + scratch4, + &transition_elements_kind); - Register untagged_value = receiver_reg; - __ SmiUntag(untagged_value, value_reg); - FloatingPointHelper::ConvertIntToDouble( - masm, - untagged_value, - destination, - f0, - mantissa_reg, - exponent_reg, - scratch4, - f2); - if (destination == FloatingPointHelper::kFPURegisters) { - CpuFeatures::Scope scope(FPU); - __ sdc1(f0, MemOperand(scratch, 0)); - } else { - __ sw(mantissa_reg, MemOperand(scratch, 0)); - __ sw(exponent_reg, MemOperand(scratch, Register::kSizeInBytes)); - } __ Ret(USE_DELAY_SLOT); __ mov(v0, value_reg); // In delay slot. @@ -4487,6 +4313,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ Jump(ic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ Jump(ic_miss, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/mirror-debugger.js b/deps/v8/src/mirror-debugger.js index e3f3c48bb5..0944b719f1 100644 --- a/deps/v8/src/mirror-debugger.js +++ b/deps/v8/src/mirror-debugger.js @@ -225,7 +225,7 @@ ScopeType = { Global: 0, */ function Mirror(type) { this.type_ = type; -}; +} Mirror.prototype.type = function() { @@ -239,7 +239,7 @@ Mirror.prototype.type = function() { */ Mirror.prototype.isValue = function() { return this instanceof ValueMirror; -} +}; /** @@ -248,7 +248,7 @@ Mirror.prototype.isValue = function() { */ Mirror.prototype.isUndefined = function() { return this instanceof UndefinedMirror; -} +}; /** @@ -257,7 +257,7 @@ Mirror.prototype.isUndefined = function() { */ Mirror.prototype.isNull = function() { return this instanceof NullMirror; -} +}; /** @@ -266,7 +266,7 @@ Mirror.prototype.isNull = function() { */ Mirror.prototype.isBoolean = function() { return this instanceof BooleanMirror; -} +}; /** @@ -275,7 +275,7 @@ Mirror.prototype.isBoolean = function() { */ Mirror.prototype.isNumber = function() { return this instanceof NumberMirror; -} +}; /** @@ -284,7 +284,7 @@ Mirror.prototype.isNumber = function() { */ Mirror.prototype.isString = function() { return this instanceof StringMirror; -} +}; /** @@ -293,7 +293,7 @@ Mirror.prototype.isString = function() { */ Mirror.prototype.isObject = function() { return this instanceof ObjectMirror; -} +}; /** @@ -302,7 +302,7 @@ Mirror.prototype.isObject = function() { */ Mirror.prototype.isFunction = function() { return this instanceof FunctionMirror; -} +}; /** @@ -311,7 +311,7 @@ Mirror.prototype.isFunction = function() { */ Mirror.prototype.isUnresolvedFunction = function() { return this instanceof UnresolvedFunctionMirror; -} +}; /** @@ -320,7 +320,7 @@ Mirror.prototype.isUnresolvedFunction = function() { */ Mirror.prototype.isArray = function() { return this instanceof ArrayMirror; -} +}; /** @@ -329,7 +329,7 @@ Mirror.prototype.isArray = function() { */ Mirror.prototype.isDate = function() { return this instanceof DateMirror; -} +}; /** @@ -338,7 +338,7 @@ Mirror.prototype.isDate = function() { */ Mirror.prototype.isRegExp = function() { return this instanceof RegExpMirror; -} +}; /** @@ -347,7 +347,7 @@ Mirror.prototype.isRegExp = function() { */ Mirror.prototype.isError = function() { return this instanceof ErrorMirror; -} +}; /** @@ -356,7 +356,7 @@ Mirror.prototype.isError = function() { */ Mirror.prototype.isProperty = function() { return this instanceof PropertyMirror; -} +}; /** @@ -365,7 +365,7 @@ Mirror.prototype.isProperty = function() { */ Mirror.prototype.isFrame = function() { return this instanceof FrameMirror; -} +}; /** @@ -374,7 +374,7 @@ Mirror.prototype.isFrame = function() { */ Mirror.prototype.isScript = function() { return this instanceof ScriptMirror; -} +}; /** @@ -383,7 +383,7 @@ Mirror.prototype.isScript = function() { */ Mirror.prototype.isContext = function() { return this instanceof ContextMirror; -} +}; /** @@ -392,7 +392,7 @@ Mirror.prototype.isContext = function() { */ Mirror.prototype.isScope = function() { return this instanceof ScopeMirror; -} +}; /** @@ -400,7 +400,7 @@ Mirror.prototype.isScope = function() { */ Mirror.prototype.allocateHandle_ = function() { this.handle_ = next_handle_++; -} +}; /** @@ -409,13 +409,13 @@ Mirror.prototype.allocateHandle_ = function() { */ Mirror.prototype.allocateTransientHandle_ = function() { this.handle_ = next_transient_handle_--; -} +}; Mirror.prototype.toText = function() { // Simpel to text which is used when on specialization in subclass. return "#<" + this.constructor.name + ">"; -} +}; /** @@ -480,7 +480,7 @@ inherits(UndefinedMirror, ValueMirror); UndefinedMirror.prototype.toText = function() { return 'undefined'; -} +}; /** @@ -496,7 +496,7 @@ inherits(NullMirror, ValueMirror); NullMirror.prototype.toText = function() { return 'null'; -} +}; /** @@ -513,7 +513,7 @@ inherits(BooleanMirror, ValueMirror); BooleanMirror.prototype.toText = function() { return this.value_ ? 'true' : 'false'; -} +}; /** @@ -530,7 +530,7 @@ inherits(NumberMirror, ValueMirror); NumberMirror.prototype.toText = function() { return %NumberToString(this.value_); -} +}; /** @@ -555,11 +555,11 @@ StringMirror.prototype.getTruncatedValue = function(maxLength) { '... (length: ' + this.length() + ')'; } return this.value_; -} +}; StringMirror.prototype.toText = function() { return this.getTruncatedValue(kMaxProtocolStringLength); -} +}; /** @@ -898,7 +898,7 @@ FunctionMirror.prototype.constructedBy = function(opt_max_instances) { FunctionMirror.prototype.toText = function() { return this.source(); -} +}; /** @@ -951,7 +951,7 @@ UnresolvedFunctionMirror.prototype.inferredName = function() { UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) { return []; -} +}; /** @@ -971,7 +971,8 @@ ArrayMirror.prototype.length = function() { }; -ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index, opt_to_index) { +ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index, + opt_to_index) { var from_index = opt_from_index || 0; var to_index = opt_to_index || this.length() - 1; if (from_index > to_index) return new Array(); @@ -987,7 +988,7 @@ ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index, opt_ values[i - from_index] = value; } return values; -} +}; /** @@ -1005,7 +1006,7 @@ inherits(DateMirror, ObjectMirror); DateMirror.prototype.toText = function() { var s = JSON.stringify(this.value_); return s.substring(1, s.length - 1); // cut quotes -} +}; /** @@ -1059,7 +1060,7 @@ RegExpMirror.prototype.multiline = function() { RegExpMirror.prototype.toText = function() { // Simpel to text which is used when on specialization in subclass. return "/" + this.source() + "/"; -} +}; /** @@ -1087,12 +1088,12 @@ ErrorMirror.prototype.toText = function() { // Use the same text representation as in messages.js. var text; try { - str = %_CallFunction(this.value_, builtins.errorToString); + str = %_CallFunction(this.value_, builtins.ErrorToString); } catch (e) { str = '#<Error>'; } return str; -} +}; /** @@ -1110,7 +1111,7 @@ function PropertyMirror(mirror, name, details) { this.value_ = details[0]; this.details_ = details[1]; if (details.length > 2) { - this.exception_ = details[2] + this.exception_ = details[2]; this.getter_ = details[3]; this.setter_ = details[4]; } @@ -1120,22 +1121,22 @@ inherits(PropertyMirror, Mirror); PropertyMirror.prototype.isReadOnly = function() { return (this.attributes() & PropertyAttribute.ReadOnly) != 0; -} +}; PropertyMirror.prototype.isEnum = function() { return (this.attributes() & PropertyAttribute.DontEnum) == 0; -} +}; PropertyMirror.prototype.canDelete = function() { return (this.attributes() & PropertyAttribute.DontDelete) == 0; -} +}; PropertyMirror.prototype.name = function() { return this.name_; -} +}; PropertyMirror.prototype.isIndexed = function() { @@ -1145,12 +1146,12 @@ PropertyMirror.prototype.isIndexed = function() { } } return true; -} +}; PropertyMirror.prototype.value = function() { return MakeMirror(this.value_, false); -} +}; /** @@ -1159,22 +1160,22 @@ PropertyMirror.prototype.value = function() { */ PropertyMirror.prototype.isException = function() { return this.exception_ ? true : false; -} +}; PropertyMirror.prototype.attributes = function() { return %DebugPropertyAttributesFromDetails(this.details_); -} +}; PropertyMirror.prototype.propertyType = function() { return %DebugPropertyTypeFromDetails(this.details_); -} +}; PropertyMirror.prototype.insertionIndex = function() { return %DebugPropertyIndexFromDetails(this.details_); -} +}; /** @@ -1183,7 +1184,7 @@ PropertyMirror.prototype.insertionIndex = function() { */ PropertyMirror.prototype.hasGetter = function() { return this.getter_ ? true : false; -} +}; /** @@ -1192,7 +1193,7 @@ PropertyMirror.prototype.hasGetter = function() { */ PropertyMirror.prototype.hasSetter = function() { return this.setter_ ? true : false; -} +}; /** @@ -1206,7 +1207,7 @@ PropertyMirror.prototype.getter = function() { } else { return GetUndefinedMirror(); } -} +}; /** @@ -1220,7 +1221,7 @@ PropertyMirror.prototype.setter = function() { } else { return GetUndefinedMirror(); } -} +}; /** @@ -1233,7 +1234,7 @@ PropertyMirror.prototype.isNative = function() { return (this.propertyType() == PropertyType.Interceptor) || ((this.propertyType() == PropertyType.Callbacks) && !this.hasGetter() && !this.hasSetter()); -} +}; const kFrameDetailsFrameIdIndex = 0; @@ -1284,63 +1285,63 @@ function FrameDetails(break_id, index) { FrameDetails.prototype.frameId = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsFrameIdIndex]; -} +}; FrameDetails.prototype.receiver = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsReceiverIndex]; -} +}; FrameDetails.prototype.func = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsFunctionIndex]; -} +}; FrameDetails.prototype.isConstructCall = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsConstructCallIndex]; -} +}; FrameDetails.prototype.isAtReturn = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsAtReturnIndex]; -} +}; FrameDetails.prototype.isDebuggerFrame = function() { %CheckExecutionState(this.break_id_); var f = kFrameDetailsFlagDebuggerFrameMask; return (this.details_[kFrameDetailsFlagsIndex] & f) == f; -} +}; FrameDetails.prototype.isOptimizedFrame = function() { %CheckExecutionState(this.break_id_); var f = kFrameDetailsFlagOptimizedFrameMask; return (this.details_[kFrameDetailsFlagsIndex] & f) == f; -} +}; FrameDetails.prototype.isInlinedFrame = function() { return this.inlinedFrameIndex() > 0; -} +}; FrameDetails.prototype.inlinedFrameIndex = function() { %CheckExecutionState(this.break_id_); var f = kFrameDetailsFlagInlinedFrameIndexMask; - return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2 -} + return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2; +}; FrameDetails.prototype.argumentCount = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsArgumentCountIndex]; -} +}; FrameDetails.prototype.argumentName = function(index) { @@ -1348,9 +1349,9 @@ FrameDetails.prototype.argumentName = function(index) { if (index >= 0 && index < this.argumentCount()) { return this.details_[kFrameDetailsFirstDynamicIndex + index * kFrameDetailsNameValueSize + - kFrameDetailsNameIndex] + kFrameDetailsNameIndex]; } -} +}; FrameDetails.prototype.argumentValue = function(index) { @@ -1358,45 +1359,45 @@ FrameDetails.prototype.argumentValue = function(index) { if (index >= 0 && index < this.argumentCount()) { return this.details_[kFrameDetailsFirstDynamicIndex + index * kFrameDetailsNameValueSize + - kFrameDetailsValueIndex] + kFrameDetailsValueIndex]; } -} +}; FrameDetails.prototype.localCount = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsLocalCountIndex]; -} +}; FrameDetails.prototype.sourcePosition = function() { %CheckExecutionState(this.break_id_); return this.details_[kFrameDetailsSourcePositionIndex]; -} +}; FrameDetails.prototype.localName = function(index) { %CheckExecutionState(this.break_id_); if (index >= 0 && index < this.localCount()) { var locals_offset = kFrameDetailsFirstDynamicIndex + - this.argumentCount() * kFrameDetailsNameValueSize + this.argumentCount() * kFrameDetailsNameValueSize; return this.details_[locals_offset + index * kFrameDetailsNameValueSize + - kFrameDetailsNameIndex] + kFrameDetailsNameIndex]; } -} +}; FrameDetails.prototype.localValue = function(index) { %CheckExecutionState(this.break_id_); if (index >= 0 && index < this.localCount()) { var locals_offset = kFrameDetailsFirstDynamicIndex + - this.argumentCount() * kFrameDetailsNameValueSize + this.argumentCount() * kFrameDetailsNameValueSize; return this.details_[locals_offset + index * kFrameDetailsNameValueSize + - kFrameDetailsValueIndex] + kFrameDetailsValueIndex]; } -} +}; FrameDetails.prototype.returnValue = function() { @@ -1407,12 +1408,12 @@ FrameDetails.prototype.returnValue = function() { if (this.details_[kFrameDetailsAtReturnIndex]) { return this.details_[return_value_offset]; } -} +}; FrameDetails.prototype.scopeCount = function() { return %GetScopeCount(this.break_id_, this.frameId()); -} +}; /** @@ -1575,7 +1576,8 @@ FrameMirror.prototype.scope = function(index) { }; -FrameMirror.prototype.evaluate = function(source, disable_break, opt_context_object) { +FrameMirror.prototype.evaluate = function(source, disable_break, + opt_context_object) { var result = %DebugEvaluate(this.break_id_, this.details_.frameId(), this.details_.inlinedFrameIndex(), @@ -1599,7 +1601,8 @@ FrameMirror.prototype.invocationText = function() { result += '[debugger]'; } else { // If the receiver has a className which is 'global' don't display it. - var display_receiver = !receiver.className || receiver.className() != 'global'; + var display_receiver = + !receiver.className || (receiver.className() != 'global'); if (display_receiver) { result += receiver.toText(); } @@ -1661,7 +1664,7 @@ FrameMirror.prototype.invocationText = function() { } return result; -} +}; FrameMirror.prototype.sourceAndPositionText = function() { @@ -1693,13 +1696,13 @@ FrameMirror.prototype.sourceAndPositionText = function() { } return result; -} +}; FrameMirror.prototype.localsText = function() { // Format local variables. var result = ''; - var locals_count = this.localCount() + var locals_count = this.localCount(); if (locals_count > 0) { for (var i = 0; i < locals_count; ++i) { result += ' var '; @@ -1711,7 +1714,7 @@ FrameMirror.prototype.localsText = function() { } return result; -} +}; FrameMirror.prototype.toText = function(opt_locals) { @@ -1726,7 +1729,7 @@ FrameMirror.prototype.toText = function(opt_locals) { result += this.localsText(); } return result; -} +}; const kScopeDetailsTypeIndex = 0; @@ -1744,13 +1747,13 @@ function ScopeDetails(frame, index) { ScopeDetails.prototype.type = function() { %CheckExecutionState(this.break_id_); return this.details_[kScopeDetailsTypeIndex]; -} +}; ScopeDetails.prototype.object = function() { %CheckExecutionState(this.break_id_); return this.details_[kScopeDetailsObjectIndex]; -} +}; /** @@ -1862,12 +1865,12 @@ ScriptMirror.prototype.lineCount = function() { ScriptMirror.prototype.locationFromPosition = function( position, include_resource_offset) { return this.script_.locationFromPosition(position, include_resource_offset); -} +}; ScriptMirror.prototype.sourceSlice = function (opt_from_line, opt_to_line) { return this.script_.sourceSlice(opt_from_line, opt_to_line); -} +}; ScriptMirror.prototype.context = function() { @@ -1907,7 +1910,7 @@ ScriptMirror.prototype.toText = function() { } result += ')'; return result; -} +}; /** @@ -1965,7 +1968,7 @@ function JSONProtocolSerializer(details, options) { */ JSONProtocolSerializer.prototype.serializeReference = function(mirror) { return this.serialize_(mirror, true, true); -} +}; /** @@ -1978,7 +1981,7 @@ JSONProtocolSerializer.prototype.serializeReference = function(mirror) { JSONProtocolSerializer.prototype.serializeValue = function(mirror) { var json = this.serialize_(mirror, false, true); return json; -} +}; /** @@ -2000,17 +2003,17 @@ JSONProtocolSerializer.prototype.serializeReferencedObjects = function() { } return content; -} +}; JSONProtocolSerializer.prototype.includeSource_ = function() { return this.options_ && this.options_.includeSource; -} +}; JSONProtocolSerializer.prototype.inlineRefs_ = function() { return this.options_ && this.options_.inlineRefs; -} +}; JSONProtocolSerializer.prototype.maxStringLength_ = function() { @@ -2019,7 +2022,7 @@ JSONProtocolSerializer.prototype.maxStringLength_ = function() { return kMaxProtocolStringLength; } return this.options_.maxStringLength; -} +}; JSONProtocolSerializer.prototype.add_ = function(mirror) { @@ -2032,7 +2035,7 @@ JSONProtocolSerializer.prototype.add_ = function(mirror) { // Add the mirror to the list of mirrors to be serialized. this.mirrors_.push(mirror); -} +}; /** @@ -2139,7 +2142,7 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, break; case PROPERTY_TYPE: - throw new Error('PropertyMirror cannot be serialized independeltly') + throw new Error('PropertyMirror cannot be serialized independeltly'); break; case FRAME_TYPE: @@ -2179,7 +2182,7 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, mirror.evalFromScript()) { content.evalFromScript = this.serializeReference(mirror.evalFromScript()); - var evalFromLocation = mirror.evalFromLocation() + var evalFromLocation = mirror.evalFromLocation(); if (evalFromLocation) { content.evalFromLocation = { line: evalFromLocation.line, column: evalFromLocation.column }; @@ -2203,7 +2206,7 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, // Create and return the JSON string. return content; -} +}; /** @@ -2278,7 +2281,7 @@ JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, } } content.properties = p; -} +}; /** @@ -2342,7 +2345,7 @@ JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) { result.ref = propertyValue.handle(); } return result; -} +}; JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { @@ -2362,7 +2365,7 @@ JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { var x = new Array(mirror.argumentCount()); for (var i = 0; i < mirror.argumentCount(); i++) { var arg = {}; - var argument_name = mirror.argumentName(i) + var argument_name = mirror.argumentName(i); if (argument_name) { arg.name = argument_name; } @@ -2392,7 +2395,7 @@ JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { index: i }); } -} +}; JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) { @@ -2402,7 +2405,7 @@ JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) { content.object = this.inlineRefs_() ? this.serializeValue(mirror.scopeObject()) : this.serializeReference(mirror.scopeObject()); -} +}; /** diff --git a/deps/v8/src/mksnapshot.cc b/deps/v8/src/mksnapshot.cc index a791dbba28..d1620bfff5 100644 --- a/deps/v8/src/mksnapshot.cc +++ b/deps/v8/src/mksnapshot.cc @@ -109,7 +109,7 @@ class PartialSnapshotSink : public i::SnapshotByteSink { if (j != 0) { fprintf(fp, ","); } - fprintf(fp, "%d", at(j)); + fprintf(fp, "%u", static_cast<unsigned char>(at(j))); } } char at(int i) { return data_[i]; } @@ -312,7 +312,7 @@ int main(int argc, char** argv) { } // If we don't do this then we end up with a stray root pointing at the // context even after we have disposed of the context. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags, "mksnapshot"); i::Object* raw_context = *(v8::Utils::OpenHandle(*context)); context.Dispose(); CppByteSink sink(argv[1]); diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index e9ca6c0907..3a667a4398 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -94,6 +94,9 @@ void HeapObject::HeapObjectVerify() { case BYTE_ARRAY_TYPE: ByteArray::cast(this)->ByteArrayVerify(); break; + case FREE_SPACE_TYPE: + FreeSpace::cast(this)->FreeSpaceVerify(); + break; case EXTERNAL_PIXEL_ARRAY_TYPE: ExternalPixelArray::cast(this)->ExternalPixelArrayVerify(); break; @@ -153,6 +156,12 @@ void HeapObject::HeapObjectVerify() { case JS_ARRAY_TYPE: JSArray::cast(this)->JSArrayVerify(); break; + case JS_SET_TYPE: + JSSet::cast(this)->JSSetVerify(); + break; + case JS_MAP_TYPE: + JSMap::cast(this)->JSMapVerify(); + break; case JS_WEAK_MAP_TYPE: JSWeakMap::cast(this)->JSWeakMapVerify(); break; @@ -207,6 +216,11 @@ void ByteArray::ByteArrayVerify() { } +void FreeSpace::FreeSpaceVerify() { + ASSERT(IsFreeSpace()); +} + + void ExternalPixelArray::ExternalPixelArrayVerify() { ASSERT(IsExternalPixelArray()); } @@ -255,12 +269,18 @@ void ExternalDoubleArray::ExternalDoubleArrayVerify() { void JSObject::JSObjectVerify() { VerifyHeapPointer(properties()); VerifyHeapPointer(elements()); + + if (GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) { + ASSERT(this->elements()->IsFixedArray()); + ASSERT(this->elements()->length() >= 2); + } + if (HasFastProperties()) { CHECK_EQ(map()->unused_property_fields(), (map()->inobject_properties() + properties()->length() - map()->NextFreePropertyIndex())); } - ASSERT_EQ(map()->has_fast_elements(), + ASSERT_EQ((map()->has_fast_elements() || map()->has_fast_smi_only_elements()), (elements()->map() == GetHeap()->fixed_array_map() || elements()->map() == GetHeap()->fixed_cow_array_map())); ASSERT(map()->has_fast_elements() == HasFastElements()); @@ -322,7 +342,8 @@ void FixedDoubleArray::FixedDoubleArrayVerify() { double value = get_scalar(i); ASSERT(!isnan(value) || (BitCast<uint64_t>(value) == - BitCast<uint64_t>(canonical_not_the_hole_nan_as_double()))); + BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())) || + ((BitCast<uint64_t>(value) & Double::kSignMask) != 0)); } } } @@ -367,7 +388,7 @@ void ConsString::ConsStringVerify() { CHECK(this->first()->IsString()); CHECK(this->second() == GetHeap()->empty_string() || this->second()->IsString()); - CHECK(this->length() >= String::kMinNonFlatLength); + CHECK(this->length() >= ConsString::kMinLength); if (this->IsFlat()) { // A flat cons can only be created by String::SlowTryFlatten. // Afterwards, the first part may be externalized. @@ -387,6 +408,7 @@ void JSFunction::JSFunctionVerify() { CHECK(IsJSFunction()); VerifyObjectField(kPrototypeOrInitialMapOffset); VerifyObjectField(kNextFunctionLinkOffset); + CHECK(code()->IsCode()); CHECK(next_function_link()->IsUndefined() || next_function_link()->IsJSFunction()); } @@ -446,9 +468,8 @@ void Oddball::OddballVerify() { } else { ASSERT(number->IsSmi()); int value = Smi::cast(number)->value(); - // Hidden oddballs have negative smis. - const int kLeastHiddenOddballNumber = -4; ASSERT(value <= 1); + // Hidden oddballs have negative smis. ASSERT(value >= kLeastHiddenOddballNumber); } } @@ -463,6 +484,7 @@ void JSGlobalPropertyCell::JSGlobalPropertyCellVerify() { void Code::CodeVerify() { CHECK(IsAligned(reinterpret_cast<intptr_t>(instruction_start()), kCodeAlignment)); + relocation_info()->Verify(); Address last_gc_pc = NULL; for (RelocIterator it(this); !it.done(); it.next()) { it.rinfo()->Verify(); @@ -484,11 +506,27 @@ void JSArray::JSArrayVerify() { } +void JSSet::JSSetVerify() { + CHECK(IsJSSet()); + JSObjectVerify(); + VerifyHeapPointer(table()); + ASSERT(table()->IsHashTable() || table()->IsUndefined()); +} + + +void JSMap::JSMapVerify() { + CHECK(IsJSMap()); + JSObjectVerify(); + VerifyHeapPointer(table()); + ASSERT(table()->IsHashTable() || table()->IsUndefined()); +} + + void JSWeakMap::JSWeakMapVerify() { CHECK(IsJSWeakMap()); JSObjectVerify(); VerifyHeapPointer(table()); - ASSERT(table()->IsHashTable()); + ASSERT(table()->IsHashTable() || table()->IsUndefined()); } @@ -535,13 +573,14 @@ void JSRegExp::JSRegExpVerify() { void JSProxy::JSProxyVerify() { - ASSERT(IsJSProxy()); + CHECK(IsJSProxy()); VerifyPointer(handler()); + ASSERT(hash()->IsSmi() || hash()->IsUndefined()); } void JSFunctionProxy::JSFunctionProxyVerify() { - ASSERT(IsJSFunctionProxy()); + CHECK(IsJSFunctionProxy()); JSProxyVerify(); VerifyPointer(call_trap()); VerifyPointer(construct_trap()); @@ -563,6 +602,13 @@ void AccessorInfo::AccessorInfoVerify() { } +void AccessorPair::AccessorPairVerify() { + CHECK(IsAccessorPair()); + VerifyPointer(getter()); + VerifyPointer(setter()); +} + + void AccessCheckInfo::AccessCheckInfoVerify() { CHECK(IsAccessCheckInfo()); VerifyPointer(named_callback()); diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index e7b6a34296..a5ea659b60 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -43,7 +43,10 @@ #include "isolate.h" #include "property.h" #include "spaces.h" +#include "store-buffer.h" #include "v8memory.h" +#include "factory.h" +#include "incremental-marking.h" namespace v8 { namespace internal { @@ -64,6 +67,13 @@ PropertyDetails PropertyDetails::AsDeleted() { } +#define TYPE_CHECKER(type, instancetype) \ + bool Object::Is##type() { \ + return Object::IsHeapObject() && \ + HeapObject::cast(this)->map()->instance_type() == instancetype; \ + } + + #define CAST_ACCESSOR(type) \ type* type::cast(Object* object) { \ ASSERT(object->Is##type()); \ @@ -80,16 +90,7 @@ PropertyDetails PropertyDetails::AsDeleted() { type* holder::name() { return type::cast(READ_FIELD(this, offset)); } \ void holder::set_##name(type* value, WriteBarrierMode mode) { \ WRITE_FIELD(this, offset, value); \ - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, mode); \ - } - - -// GC-safe accessors do not use HeapObject::GetHeap(), but access TLS instead. -#define ACCESSORS_GCSAFE(holder, name, type, offset) \ - type* holder::name() { return type::cast(READ_FIELD(this, offset)); } \ - void holder::set_##name(type* value, WriteBarrierMode mode) { \ - WRITE_FIELD(this, offset, value); \ - CONDITIONAL_WRITE_BARRIER(HEAP, this, offset, mode); \ + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode); \ } @@ -118,6 +119,23 @@ PropertyDetails PropertyDetails::AsDeleted() { } +bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, + ElementsKind to_kind) { + if (to_kind == FAST_ELEMENTS) { + return from_kind == FAST_SMI_ONLY_ELEMENTS || + from_kind == FAST_DOUBLE_ELEMENTS; + } else { + return to_kind == FAST_DOUBLE_ELEMENTS && + from_kind == FAST_SMI_ONLY_ELEMENTS; + } +} + + +bool Object::IsFixedArrayBase() { + return IsFixedArray() || IsFixedDoubleArray(); +} + + bool Object::IsInstanceOf(FunctionTemplateInfo* expected) { // There is a constraint on the object; check. if (!this->IsJSObject()) return false; @@ -147,12 +165,15 @@ bool Object::IsHeapObject() { } -bool Object::IsHeapNumber() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == HEAP_NUMBER_TYPE; +bool Object::NonFailureIsHeapObject() { + ASSERT(!this->IsFailure()); + return (reinterpret_cast<intptr_t>(this) & kSmiTagMask) != 0; } +TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE) + + bool Object::IsString() { return Object::IsHeapObject() && HeapObject::cast(this)->map()->instance_type() < FIRST_NONSTRING_TYPE; @@ -165,6 +186,13 @@ bool Object::IsSpecObject() { } +bool Object::IsSpecFunction() { + if (!Object::IsHeapObject()) return false; + InstanceType type = HeapObject::cast(this)->map()->instance_type(); + return type == JS_FUNCTION_TYPE || type == JS_FUNCTION_PROXY_TYPE; +} + + bool Object::IsSymbol() { if (!this->IsHeapObject()) return false; uint32_t type = HeapObject::cast(this)->map()->instance_type(); @@ -396,19 +424,20 @@ bool Object::IsNumber() { } -bool Object::IsByteArray() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == BYTE_ARRAY_TYPE; -} +TYPE_CHECKER(ByteArray, BYTE_ARRAY_TYPE) +TYPE_CHECKER(FreeSpace, FREE_SPACE_TYPE) -bool Object::IsExternalPixelArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_PIXEL_ARRAY_TYPE; +bool Object::IsFiller() { + if (!Object::IsHeapObject()) return false; + InstanceType instance_type = HeapObject::cast(this)->map()->instance_type(); + return instance_type == FREE_SPACE_TYPE || instance_type == FILLER_TYPE; } +TYPE_CHECKER(ExternalPixelArray, EXTERNAL_PIXEL_ARRAY_TYPE) + + bool Object::IsExternalArray() { if (!Object::IsHeapObject()) return false; @@ -419,60 +448,14 @@ bool Object::IsExternalArray() { } -bool Object::IsExternalByteArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_BYTE_ARRAY_TYPE; -} - - -bool Object::IsExternalUnsignedByteArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE; -} - - -bool Object::IsExternalShortArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_SHORT_ARRAY_TYPE; -} - - -bool Object::IsExternalUnsignedShortArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE; -} - - -bool Object::IsExternalIntArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_INT_ARRAY_TYPE; -} - - -bool Object::IsExternalUnsignedIntArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_UNSIGNED_INT_ARRAY_TYPE; -} - - -bool Object::IsExternalFloatArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_FLOAT_ARRAY_TYPE; -} - - -bool Object::IsExternalDoubleArray() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == - EXTERNAL_DOUBLE_ARRAY_TYPE; -} +TYPE_CHECKER(ExternalByteArray, EXTERNAL_BYTE_ARRAY_TYPE) +TYPE_CHECKER(ExternalUnsignedByteArray, EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) +TYPE_CHECKER(ExternalShortArray, EXTERNAL_SHORT_ARRAY_TYPE) +TYPE_CHECKER(ExternalUnsignedShortArray, EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) +TYPE_CHECKER(ExternalIntArray, EXTERNAL_INT_ARRAY_TYPE) +TYPE_CHECKER(ExternalUnsignedIntArray, EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) +TYPE_CHECKER(ExternalFloatArray, EXTERNAL_FLOAT_ARRAY_TYPE) +TYPE_CHECKER(ExternalDoubleArray, EXTERNAL_DOUBLE_ARRAY_TYPE) bool MaybeObject::IsFailure() { @@ -509,59 +492,34 @@ Failure* Failure::cast(MaybeObject* obj) { bool Object::IsJSReceiver() { + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); return IsHeapObject() && HeapObject::cast(this)->map()->instance_type() >= FIRST_JS_RECEIVER_TYPE; } bool Object::IsJSObject() { - return IsJSReceiver() && !IsJSProxy(); + STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); + return IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() >= FIRST_JS_OBJECT_TYPE; } bool Object::IsJSProxy() { - return Object::IsHeapObject() && - (HeapObject::cast(this)->map()->instance_type() == JS_PROXY_TYPE || - HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_PROXY_TYPE); -} - - -bool Object::IsJSFunctionProxy() { - return Object::IsHeapObject() && - HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_PROXY_TYPE; -} - - -bool Object::IsJSWeakMap() { - return Object::IsJSObject() && - HeapObject::cast(this)->map()->instance_type() == JS_WEAK_MAP_TYPE; -} - - -bool Object::IsJSContextExtensionObject() { - return IsHeapObject() - && (HeapObject::cast(this)->map()->instance_type() == - JS_CONTEXT_EXTENSION_OBJECT_TYPE); -} - - -bool Object::IsMap() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == MAP_TYPE; -} - - -bool Object::IsFixedArray() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == FIXED_ARRAY_TYPE; + if (!Object::IsHeapObject()) return false; + InstanceType type = HeapObject::cast(this)->map()->instance_type(); + return FIRST_JS_PROXY_TYPE <= type && type <= LAST_JS_PROXY_TYPE; } -bool Object::IsFixedDoubleArray() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == - FIXED_DOUBLE_ARRAY_TYPE; -} +TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE) +TYPE_CHECKER(JSSet, JS_SET_TYPE) +TYPE_CHECKER(JSMap, JS_MAP_TYPE) +TYPE_CHECKER(JSWeakMap, JS_WEAK_MAP_TYPE) +TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE) +TYPE_CHECKER(Map, MAP_TYPE) +TYPE_CHECKER(FixedArray, FIXED_ARRAY_TYPE) +TYPE_CHECKER(FixedDoubleArray, FIXED_DOUBLE_ARRAY_TYPE) bool Object::IsDescriptorArray() { @@ -596,6 +554,16 @@ bool Object::IsDeoptimizationOutputData() { } +bool Object::IsTypeFeedbackCells() { + if (!IsFixedArray()) return false; + // There's actually no way to see the difference between a fixed array and + // a cache cells array. Since this is used for asserts we can check that + // the length is plausible though. + if (FixedArray::cast(this)->length() % 2 != 0) return false; + return true; +} + + bool Object::IsContext() { if (Object::IsHeapObject()) { Map* map = HeapObject::cast(this)->map(); @@ -617,17 +585,14 @@ bool Object::IsGlobalContext() { } -bool Object::IsSerializedScopeInfo() { +bool Object::IsScopeInfo() { return Object::IsHeapObject() && HeapObject::cast(this)->map() == - HeapObject::cast(this)->GetHeap()->serialized_scope_info_map(); + HeapObject::cast(this)->GetHeap()->scope_info_map(); } -bool Object::IsJSFunction() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_TYPE; -} +TYPE_CHECKER(JSFunction, JS_FUNCTION_TYPE) template <> inline bool Is<JSFunction>(Object* obj) { @@ -635,44 +600,12 @@ template <> inline bool Is<JSFunction>(Object* obj) { } -bool Object::IsCode() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == CODE_TYPE; -} - - -bool Object::IsOddball() { - ASSERT(HEAP->is_safe_to_read_maps()); - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == ODDBALL_TYPE; -} - - -bool Object::IsJSGlobalPropertyCell() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() - == JS_GLOBAL_PROPERTY_CELL_TYPE; -} - - -bool Object::IsSharedFunctionInfo() { - return Object::IsHeapObject() && - (HeapObject::cast(this)->map()->instance_type() == - SHARED_FUNCTION_INFO_TYPE); -} - - -bool Object::IsJSValue() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == JS_VALUE_TYPE; -} - - -bool Object::IsJSMessageObject() { - return Object::IsHeapObject() - && (HeapObject::cast(this)->map()->instance_type() == - JS_MESSAGE_OBJECT_TYPE); -} +TYPE_CHECKER(Code, CODE_TYPE) +TYPE_CHECKER(Oddball, ODDBALL_TYPE) +TYPE_CHECKER(JSGlobalPropertyCell, JS_GLOBAL_PROPERTY_CELL_TYPE) +TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE) +TYPE_CHECKER(JSValue, JS_VALUE_TYPE) +TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE) bool Object::IsStringWrapper() { @@ -680,10 +613,7 @@ bool Object::IsStringWrapper() { } -bool Object::IsForeign() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == FOREIGN_TYPE; -} +TYPE_CHECKER(Foreign, FOREIGN_TYPE) bool Object::IsBoolean() { @@ -692,16 +622,8 @@ bool Object::IsBoolean() { } -bool Object::IsJSArray() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == JS_ARRAY_TYPE; -} - - -bool Object::IsJSRegExp() { - return Object::IsHeapObject() - && HeapObject::cast(this)->map()->instance_type() == JS_REGEXP_TYPE; -} +TYPE_CHECKER(JSArray, JS_ARRAY_TYPE) +TYPE_CHECKER(JSRegExp, JS_REGEXP_TYPE) template <> inline bool Is<JSArray>(Object* obj) { @@ -738,7 +660,10 @@ bool Object::IsJSFunctionResultCache() { return false; } #ifdef DEBUG - reinterpret_cast<JSFunctionResultCache*>(this)->JSFunctionResultCacheVerify(); + if (FLAG_verify_heap) { + reinterpret_cast<JSFunctionResultCache*>(this)-> + JSFunctionResultCacheVerify(); + } #endif return true; } @@ -750,7 +675,9 @@ bool Object::IsNormalizedMapCache() { return false; } #ifdef DEBUG - reinterpret_cast<NormalizedMapCache*>(this)->NormalizedMapCacheVerify(); + if (FLAG_verify_heap) { + reinterpret_cast<NormalizedMapCache*>(this)->NormalizedMapCacheVerify(); + } #endif return true; } @@ -799,18 +726,8 @@ bool Object::IsGlobalObject() { } -bool Object::IsJSGlobalObject() { - return IsHeapObject() && - (HeapObject::cast(this)->map()->instance_type() == - JS_GLOBAL_OBJECT_TYPE); -} - - -bool Object::IsJSBuiltinsObject() { - return IsHeapObject() && - (HeapObject::cast(this)->map()->instance_type() == - JS_BUILTINS_OBJECT_TYPE); -} +TYPE_CHECKER(JSGlobalObject, JS_GLOBAL_OBJECT_TYPE) +TYPE_CHECKER(JSBuiltinsObject, JS_BUILTINS_OBJECT_TYPE) bool Object::IsUndetectableObject() { @@ -939,21 +856,20 @@ MaybeObject* Object::GetProperty(String* key, PropertyAttributes* attributes) { #define WRITE_FIELD(p, offset, value) \ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value) -// TODO(isolates): Pass heap in to these macros. -#define WRITE_BARRIER(object, offset) \ - object->GetHeap()->RecordWrite(object->address(), offset); - -// CONDITIONAL_WRITE_BARRIER must be issued after the actual -// write due to the assert validating the written value. -#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, mode) \ - if (mode == UPDATE_WRITE_BARRIER) { \ - heap->RecordWrite(object->address(), offset); \ - } else { \ - ASSERT(mode == SKIP_WRITE_BARRIER); \ - ASSERT(heap->InNewSpace(object) || \ - !heap->InNewSpace(READ_FIELD(object, offset)) || \ - Page::FromAddress(object->address())-> \ - IsRegionDirty(object->address() + offset)); \ +#define WRITE_BARRIER(heap, object, offset, value) \ + heap->incremental_marking()->RecordWrite( \ + object, HeapObject::RawField(object, offset), value); \ + if (heap->InNewSpace(value)) { \ + heap->RecordWrite(object->address(), offset); \ + } + +#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, value, mode) \ + if (mode == UPDATE_WRITE_BARRIER) { \ + heap->incremental_marking()->RecordWrite( \ + object, HeapObject::RawField(object, offset), value); \ + if (heap->InNewSpace(value)) { \ + heap->RecordWrite(object->address(), offset); \ + } \ } #ifndef V8_TARGET_ARCH_MIPS @@ -974,7 +890,6 @@ MaybeObject* Object::GetProperty(String* key, PropertyAttributes* attributes) { #define READ_DOUBLE_FIELD(p, offset) read_double_field(p, offset) #endif // V8_TARGET_ARCH_MIPS - #ifndef V8_TARGET_ARCH_MIPS #define WRITE_DOUBLE_FIELD(p, offset, value) \ (*reinterpret_cast<double*>(FIELD_ADDR(p, offset)) = value) @@ -1169,91 +1084,6 @@ HeapObject* MapWord::ToForwardingAddress() { } -bool MapWord::IsMarked() { - return (value_ & kMarkingMask) == 0; -} - - -void MapWord::SetMark() { - value_ &= ~kMarkingMask; -} - - -void MapWord::ClearMark() { - value_ |= kMarkingMask; -} - - -bool MapWord::IsOverflowed() { - return (value_ & kOverflowMask) != 0; -} - - -void MapWord::SetOverflow() { - value_ |= kOverflowMask; -} - - -void MapWord::ClearOverflow() { - value_ &= ~kOverflowMask; -} - - -MapWord MapWord::EncodeAddress(Address map_address, int offset) { - // Offset is the distance in live bytes from the first live object in the - // same page. The offset between two objects in the same page should not - // exceed the object area size of a page. - ASSERT(0 <= offset && offset < Page::kObjectAreaSize); - - uintptr_t compact_offset = offset >> kObjectAlignmentBits; - ASSERT(compact_offset < (1 << kForwardingOffsetBits)); - - Page* map_page = Page::FromAddress(map_address); - ASSERT_MAP_PAGE_INDEX(map_page->mc_page_index); - - uintptr_t map_page_offset = - map_page->Offset(map_address) >> kMapAlignmentBits; - - uintptr_t encoding = - (compact_offset << kForwardingOffsetShift) | - (map_page_offset << kMapPageOffsetShift) | - (map_page->mc_page_index << kMapPageIndexShift); - return MapWord(encoding); -} - - -Address MapWord::DecodeMapAddress(MapSpace* map_space) { - int map_page_index = - static_cast<int>((value_ & kMapPageIndexMask) >> kMapPageIndexShift); - ASSERT_MAP_PAGE_INDEX(map_page_index); - - int map_page_offset = static_cast<int>( - ((value_ & kMapPageOffsetMask) >> kMapPageOffsetShift) << - kMapAlignmentBits); - - return (map_space->PageAddress(map_page_index) + map_page_offset); -} - - -int MapWord::DecodeOffset() { - // The offset field is represented in the kForwardingOffsetBits - // most-significant bits. - uintptr_t offset = (value_ >> kForwardingOffsetShift) << kObjectAlignmentBits; - ASSERT(offset < static_cast<uintptr_t>(Page::kObjectAreaSize)); - return static_cast<int>(offset); -} - - -MapWord MapWord::FromEncodedAddress(Address address) { - return MapWord(reinterpret_cast<uintptr_t>(address)); -} - - -Address MapWord::ToEncodedAddress() { - return reinterpret_cast<Address>(value_); -} - - #ifdef DEBUG void HeapObject::VerifyObjectField(int offset) { VerifyPointer(READ_FIELD(this, offset)); @@ -1266,12 +1096,11 @@ void HeapObject::VerifySmiField(int offset) { Heap* HeapObject::GetHeap() { - // During GC, the map pointer in HeapObject is used in various ways that - // prevent us from retrieving Heap from the map. - // Assert that we are not in GC, implement GC code in a way that it doesn't - // pull heap from the map. - ASSERT(HEAP->is_safe_to_read_maps()); - return map()->heap(); + Heap* heap = + MemoryChunk::FromAddress(reinterpret_cast<Address>(this))->heap(); + ASSERT(heap != NULL); + ASSERT(heap->isolate() == Isolate::Current()); + return heap; } @@ -1287,6 +1116,17 @@ Map* HeapObject::map() { void HeapObject::set_map(Map* value) { set_map_word(MapWord::FromMap(value)); + if (value != NULL) { + // TODO(1600) We are passing NULL as a slot because maps can never be on + // evacuation candidate. + value->GetHeap()->incremental_marking()->RecordWrite(this, NULL, value); + } +} + + +// Unsafe accessor omitting write barrier. +void HeapObject::set_map_no_write_barrier(Map* value) { + set_map_word(MapWord::FromMap(value)); } @@ -1329,87 +1169,188 @@ void HeapObject::IteratePointer(ObjectVisitor* v, int offset) { } -bool HeapObject::IsMarked() { - return map_word().IsMarked(); +double HeapNumber::value() { + return READ_DOUBLE_FIELD(this, kValueOffset); } -void HeapObject::SetMark() { - ASSERT(!IsMarked()); - MapWord first_word = map_word(); - first_word.SetMark(); - set_map_word(first_word); +void HeapNumber::set_value(double value) { + WRITE_DOUBLE_FIELD(this, kValueOffset, value); } -void HeapObject::ClearMark() { - ASSERT(IsMarked()); - MapWord first_word = map_word(); - first_word.ClearMark(); - set_map_word(first_word); +int HeapNumber::get_exponent() { + return ((READ_INT_FIELD(this, kExponentOffset) & kExponentMask) >> + kExponentShift) - kExponentBias; } -bool HeapObject::IsOverflowed() { - return map_word().IsOverflowed(); +int HeapNumber::get_sign() { + return READ_INT_FIELD(this, kExponentOffset) & kSignMask; } -void HeapObject::SetOverflow() { - MapWord first_word = map_word(); - first_word.SetOverflow(); - set_map_word(first_word); +ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset) + + +Object** FixedArray::GetFirstElementAddress() { + return reinterpret_cast<Object**>(FIELD_ADDR(this, OffsetOfElementAt(0))); } -void HeapObject::ClearOverflow() { - ASSERT(IsOverflowed()); - MapWord first_word = map_word(); - first_word.ClearOverflow(); - set_map_word(first_word); +bool FixedArray::ContainsOnlySmisOrHoles() { + Object* the_hole = GetHeap()->the_hole_value(); + Object** current = GetFirstElementAddress(); + for (int i = 0; i < length(); ++i) { + Object* candidate = *current++; + if (!candidate->IsSmi() && candidate != the_hole) return false; + } + return true; } -double HeapNumber::value() { - return READ_DOUBLE_FIELD(this, kValueOffset); +FixedArrayBase* JSObject::elements() { + Object* array = READ_FIELD(this, kElementsOffset); + return static_cast<FixedArrayBase*>(array); } +void JSObject::ValidateSmiOnlyElements() { +#if DEBUG + if (map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) { + Heap* heap = GetHeap(); + // Don't use elements, since integrity checks will fail if there + // are filler pointers in the array. + FixedArray* fixed_array = + reinterpret_cast<FixedArray*>(READ_FIELD(this, kElementsOffset)); + Map* map = fixed_array->map(); + // Arrays that have been shifted in place can't be verified. + if (map != heap->raw_unchecked_one_pointer_filler_map() && + map != heap->raw_unchecked_two_pointer_filler_map() && + map != heap->free_space_map()) { + for (int i = 0; i < fixed_array->length(); i++) { + Object* current = fixed_array->get(i); + ASSERT(current->IsSmi() || current->IsTheHole()); + } + } + } +#endif +} -void HeapNumber::set_value(double value) { - WRITE_DOUBLE_FIELD(this, kValueOffset, value); + +MaybeObject* JSObject::EnsureCanContainHeapObjectElements() { +#if DEBUG + ValidateSmiOnlyElements(); +#endif + if ((map()->elements_kind() != FAST_ELEMENTS)) { + return TransitionElementsKind(FAST_ELEMENTS); + } + return this; } -int HeapNumber::get_exponent() { - return ((READ_INT_FIELD(this, kExponentOffset) & kExponentMask) >> - kExponentShift) - kExponentBias; +MaybeObject* JSObject::EnsureCanContainElements(Object** objects, + uint32_t count, + EnsureElementsMode mode) { + ElementsKind current_kind = map()->elements_kind(); + ElementsKind target_kind = current_kind; + ASSERT(mode != ALLOW_COPIED_DOUBLE_ELEMENTS); + if (current_kind == FAST_ELEMENTS) return this; + + Heap* heap = GetHeap(); + Object* the_hole = heap->the_hole_value(); + Object* heap_number_map = heap->heap_number_map(); + for (uint32_t i = 0; i < count; ++i) { + Object* current = *objects++; + if (!current->IsSmi() && current != the_hole) { + if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && + HeapObject::cast(current)->map() == heap_number_map) { + target_kind = FAST_DOUBLE_ELEMENTS; + } else { + target_kind = FAST_ELEMENTS; + break; + } + } + } + + if (target_kind != current_kind) { + return TransitionElementsKind(target_kind); + } + return this; } -int HeapNumber::get_sign() { - return READ_INT_FIELD(this, kExponentOffset) & kSignMask; +MaybeObject* JSObject::EnsureCanContainElements(FixedArrayBase* elements, + EnsureElementsMode mode) { + if (elements->map() != GetHeap()->fixed_double_array_map()) { + ASSERT(elements->map() == GetHeap()->fixed_array_map() || + elements->map() == GetHeap()->fixed_cow_array_map()); + if (mode == ALLOW_COPIED_DOUBLE_ELEMENTS) { + mode = DONT_ALLOW_DOUBLE_ELEMENTS; + } + Object** objects = FixedArray::cast(elements)->GetFirstElementAddress(); + return EnsureCanContainElements(objects, elements->length(), mode); + } + + ASSERT(mode == ALLOW_COPIED_DOUBLE_ELEMENTS); + if (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS) { + return TransitionElementsKind(FAST_DOUBLE_ELEMENTS); + } + + return this; } -ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset) +MaybeObject* JSObject::GetElementsTransitionMap(Isolate* isolate, + ElementsKind to_kind) { + Map* current_map = map(); + ElementsKind from_kind = current_map->elements_kind(); + if (from_kind == to_kind) return current_map; -FixedArrayBase* JSObject::elements() { - Object* array = READ_FIELD(this, kElementsOffset); - ASSERT(array->HasValidElements()); - return static_cast<FixedArrayBase*>(array); + Context* global_context = isolate->context()->global_context(); + if (current_map == global_context->smi_js_array_map()) { + if (to_kind == FAST_ELEMENTS) { + return global_context->object_js_array_map(); + } else { + if (to_kind == FAST_DOUBLE_ELEMENTS) { + return global_context->double_js_array_map(); + } else { + ASSERT(to_kind == DICTIONARY_ELEMENTS); + } + } + } + return GetElementsTransitionMapSlow(to_kind); } -void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) { - ASSERT(map()->has_fast_elements() == +void JSObject::set_map_and_elements(Map* new_map, + FixedArrayBase* value, + WriteBarrierMode mode) { + ASSERT(value->HasValidElements()); +#ifdef DEBUG + ValidateSmiOnlyElements(); +#endif + if (new_map != NULL) { + if (mode == UPDATE_WRITE_BARRIER) { + set_map(new_map); + } else { + ASSERT(mode == SKIP_WRITE_BARRIER); + set_map_no_write_barrier(new_map); + } + } + ASSERT((map()->has_fast_elements() || + map()->has_fast_smi_only_elements()) == (value->map() == GetHeap()->fixed_array_map() || value->map() == GetHeap()->fixed_cow_array_map())); ASSERT(map()->has_fast_double_elements() == value->IsFixedDoubleArray()); - ASSERT(value->HasValidElements()); WRITE_FIELD(this, kElementsOffset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, mode); + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, value, mode); +} + + +void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) { + set_map_and_elements(NULL, value, mode); } @@ -1420,7 +1361,7 @@ void JSObject::initialize_properties() { void JSObject::initialize_elements() { - ASSERT(map()->has_fast_elements()); + ASSERT(map()->has_fast_elements() || map()->has_fast_smi_only_elements()); ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array())); WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array()); } @@ -1428,9 +1369,12 @@ void JSObject::initialize_elements() { MaybeObject* JSObject::ResetElements() { Object* obj; - { MaybeObject* maybe_obj = map()->GetFastElementsMap(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + ElementsKind elements_kind = FLAG_smi_only_arrays + ? FAST_SMI_ONLY_ELEMENTS + : FAST_ELEMENTS; + MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), + elements_kind); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; set_map(Map::cast(obj)); initialize_elements(); return this; @@ -1442,12 +1386,12 @@ ACCESSORS(Oddball, to_number, Object, kToNumberOffset) byte Oddball::kind() { - return READ_BYTE_FIELD(this, kKindOffset); + return Smi::cast(READ_FIELD(this, kKindOffset))->value(); } void Oddball::set_kind(byte value) { - WRITE_BYTE_FIELD(this, kKindOffset, value); + WRITE_FIELD(this, kKindOffset, Smi::FromInt(value)); } @@ -1481,11 +1425,11 @@ int JSObject::GetHeaderSize() { case JS_VALUE_TYPE: return JSValue::kSize; case JS_ARRAY_TYPE: - return JSValue::kSize; + return JSArray::kSize; case JS_WEAK_MAP_TYPE: return JSWeakMap::kSize; case JS_REGEXP_TYPE: - return JSValue::kSize; + return JSRegExp::kSize; case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return JSObject::kHeaderSize; case JS_MESSAGE_OBJECT_TYPE: @@ -1528,7 +1472,17 @@ void JSObject::SetInternalField(int index, Object* value) { // to adjust the index here. int offset = GetHeaderSize() + (kPointerSize * index); WRITE_FIELD(this, offset, value); - WRITE_BARRIER(this, offset); + WRITE_BARRIER(GetHeap(), this, offset, value); +} + + +void JSObject::SetInternalField(int index, Smi* value) { + ASSERT(index < GetInternalFieldCount() && index >= 0); + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. + int offset = GetHeaderSize() + (kPointerSize * index); + WRITE_FIELD(this, offset, value); } @@ -1554,7 +1508,7 @@ Object* JSObject::FastPropertyAtPut(int index, Object* value) { if (index < 0) { int offset = map()->instance_size() + (index * kPointerSize); WRITE_FIELD(this, offset, value); - WRITE_BARRIER(this, offset); + WRITE_BARRIER(GetHeap(), this, offset, value); } else { ASSERT(index < properties()->length()); properties()->set(index, value); @@ -1588,16 +1542,32 @@ Object* JSObject::InObjectPropertyAtPut(int index, ASSERT(index < 0); int offset = map()->instance_size() + (index * kPointerSize); WRITE_FIELD(this, offset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, mode); + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode); return value; } -void JSObject::InitializeBody(int object_size, Object* value) { - ASSERT(!value->IsHeapObject() || !GetHeap()->InNewSpace(value)); - for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) { - WRITE_FIELD(this, offset, value); +void JSObject::InitializeBody(Map* map, + Object* pre_allocated_value, + Object* filler_value) { + ASSERT(!filler_value->IsHeapObject() || + !GetHeap()->InNewSpace(filler_value)); + ASSERT(!pre_allocated_value->IsHeapObject() || + !GetHeap()->InNewSpace(pre_allocated_value)); + int size = map->instance_size(); + int offset = kHeaderSize; + if (filler_value != pre_allocated_value) { + int pre_allocated = map->pre_allocated_property_fields(); + ASSERT(pre_allocated * kPointerSize + kHeaderSize <= size); + for (int i = 0; i < pre_allocated; i++) { + WRITE_FIELD(this, offset, pre_allocated_value); + offset += kPointerSize; + } + } + while (offset < size) { + WRITE_FIELD(this, offset, filler_value); + offset += kPointerSize; } } @@ -1683,7 +1653,7 @@ void FixedArray::set(int index, Object* value) { ASSERT(index >= 0 && index < this->length()); int offset = kHeaderSize + index * kPointerSize; WRITE_FIELD(this, offset, value); - WRITE_BARRIER(this, offset); + WRITE_BARRIER(GetHeap(), this, offset, value); } @@ -1772,7 +1742,7 @@ void FixedDoubleArray::Initialize(FixedDoubleArray* from) { void FixedDoubleArray::Initialize(FixedArray* from) { int old_length = from->length(); - ASSERT(old_length < length()); + ASSERT(old_length <= length()); for (int i = 0; i < old_length; i++) { Object* hole_or_object = from->get(i); if (hole_or_object->IsTheHole()) { @@ -1806,7 +1776,9 @@ void FixedDoubleArray::Initialize(SeededNumberDictionary* from) { WriteBarrierMode HeapObject::GetWriteBarrierMode(const AssertNoAllocation&) { - if (GetHeap()->InNewSpace(this)) return SKIP_WRITE_BARRIER; + Heap* heap = GetHeap(); + if (heap->incremental_marking()->IsMarking()) return UPDATE_WRITE_BARRIER; + if (heap->InNewSpace(this)) return SKIP_WRITE_BARRIER; return UPDATE_WRITE_BARRIER; } @@ -1818,11 +1790,27 @@ void FixedArray::set(int index, ASSERT(index >= 0 && index < this->length()); int offset = kHeaderSize + index * kPointerSize; WRITE_FIELD(this, offset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, mode); + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode); +} + + +void FixedArray::NoIncrementalWriteBarrierSet(FixedArray* array, + int index, + Object* value) { + ASSERT(array->map() != HEAP->raw_unchecked_fixed_cow_array_map()); + ASSERT(index >= 0 && index < array->length()); + int offset = kHeaderSize + index * kPointerSize; + WRITE_FIELD(array, offset, value); + Heap* heap = array->GetHeap(); + if (heap->InNewSpace(value)) { + heap->RecordWrite(array->address(), offset); + } } -void FixedArray::fast_set(FixedArray* array, int index, Object* value) { +void FixedArray::NoWriteBarrierSet(FixedArray* array, + int index, + Object* value) { ASSERT(array->map() != HEAP->raw_unchecked_fixed_cow_array_map()); ASSERT(index >= 0 && index < array->length()); ASSERT(!HEAP->InNewSpace(value)); @@ -1879,7 +1867,7 @@ void FixedArray::set_unchecked(Heap* heap, WriteBarrierMode mode) { int offset = kHeaderSize + index * kPointerSize; WRITE_FIELD(this, offset, value); - CONDITIONAL_WRITE_BARRIER(heap, this, offset, mode); + CONDITIONAL_WRITE_BARRIER(heap, this, offset, value, mode); } @@ -1914,10 +1902,12 @@ void DescriptorArray::set_bit_field3_storage(int value) { } -void DescriptorArray::fast_swap(FixedArray* array, int first, int second) { +void DescriptorArray::NoIncrementalWriteBarrierSwap(FixedArray* array, + int first, + int second) { Object* tmp = array->get(first); - fast_set(array, first, array->get(second)); - fast_set(array, second, tmp); + NoIncrementalWriteBarrierSet(array, first, array->get(second)); + NoIncrementalWriteBarrierSet(array, second, tmp); } @@ -1992,19 +1982,37 @@ Object* DescriptorArray::GetCallbacksObject(int descriptor_number) { AccessorDescriptor* DescriptorArray::GetCallbacks(int descriptor_number) { ASSERT(GetType(descriptor_number) == CALLBACKS); Foreign* p = Foreign::cast(GetCallbacksObject(descriptor_number)); - return reinterpret_cast<AccessorDescriptor*>(p->address()); + return reinterpret_cast<AccessorDescriptor*>(p->foreign_address()); } bool DescriptorArray::IsProperty(int descriptor_number) { - return GetType(descriptor_number) < FIRST_PHANTOM_PROPERTY_TYPE; + return IsRealProperty(GetType(descriptor_number)); } -bool DescriptorArray::IsTransition(int descriptor_number) { - PropertyType t = GetType(descriptor_number); - return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || - t == ELEMENTS_TRANSITION; +bool DescriptorArray::IsTransitionOnly(int descriptor_number) { + switch (GetType(descriptor_number)) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: + return true; + case CALLBACKS: { + Object* value = GetValue(descriptor_number); + if (!value->IsAccessorPair()) return false; + AccessorPair* accessors = AccessorPair::cast(value); + return accessors->getter()->IsMap() && accessors->setter()->IsMap(); + } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + return false; + } + UNREACHABLE(); // Keep the compiler happy. + return false; } @@ -2025,34 +2033,60 @@ void DescriptorArray::Get(int descriptor_number, Descriptor* desc) { } -void DescriptorArray::Set(int descriptor_number, Descriptor* desc) { +void DescriptorArray::Set(int descriptor_number, + Descriptor* desc, + const WhitenessWitness&) { // Range check. ASSERT(descriptor_number < number_of_descriptors()); - // Make sure none of the elements in desc are in new space. - ASSERT(!HEAP->InNewSpace(desc->GetKey())); - ASSERT(!HEAP->InNewSpace(desc->GetValue())); - - fast_set(this, ToKeyIndex(descriptor_number), desc->GetKey()); + NoIncrementalWriteBarrierSet(this, + ToKeyIndex(descriptor_number), + desc->GetKey()); FixedArray* content_array = GetContentArray(); - fast_set(content_array, ToValueIndex(descriptor_number), desc->GetValue()); - fast_set(content_array, ToDetailsIndex(descriptor_number), - desc->GetDetails().AsSmi()); + NoIncrementalWriteBarrierSet(content_array, + ToValueIndex(descriptor_number), + desc->GetValue()); + NoIncrementalWriteBarrierSet(content_array, + ToDetailsIndex(descriptor_number), + desc->GetDetails().AsSmi()); } -void DescriptorArray::CopyFrom(int index, DescriptorArray* src, int src_index) { +void DescriptorArray::CopyFrom(int index, + DescriptorArray* src, + int src_index, + const WhitenessWitness& witness) { Descriptor desc; src->Get(src_index, &desc); - Set(index, &desc); + Set(index, &desc, witness); } -void DescriptorArray::Swap(int first, int second) { - fast_swap(this, ToKeyIndex(first), ToKeyIndex(second)); +void DescriptorArray::NoIncrementalWriteBarrierSwapDescriptors( + int first, int second) { + NoIncrementalWriteBarrierSwap(this, ToKeyIndex(first), ToKeyIndex(second)); FixedArray* content_array = GetContentArray(); - fast_swap(content_array, ToValueIndex(first), ToValueIndex(second)); - fast_swap(content_array, ToDetailsIndex(first), ToDetailsIndex(second)); + NoIncrementalWriteBarrierSwap(content_array, + ToValueIndex(first), + ToValueIndex(second)); + NoIncrementalWriteBarrierSwap(content_array, + ToDetailsIndex(first), + ToDetailsIndex(second)); +} + + +DescriptorArray::WhitenessWitness::WhitenessWitness(DescriptorArray* array) + : marking_(array->GetHeap()->incremental_marking()) { + marking_->EnterNoMarkingScope(); + if (array->number_of_descriptors() > 0) { + ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT); + ASSERT(Marking::Color(array->GetContentArray()) == Marking::WHITE_OBJECT); + } +} + + +DescriptorArray::WhitenessWitness::~WhitenessWitness() { + marking_->LeaveNoMarkingScope(); } @@ -2084,7 +2118,7 @@ int HashTable<Shape, Key>::FindEntry(Isolate* isolate, Key key) { Object* element = KeyAt(entry); // Empty entry. if (element == isolate->heap()->raw_unchecked_undefined_value()) break; - if (element != isolate->heap()->raw_unchecked_null_value() && + if (element != isolate->heap()->raw_unchecked_the_hole_value() && Shape::IsMatch(key, element)) return entry; entry = NextProbe(entry, count++, capacity); } @@ -2121,9 +2155,11 @@ CAST_ACCESSOR(FixedDoubleArray) CAST_ACCESSOR(DescriptorArray) CAST_ACCESSOR(DeoptimizationInputData) CAST_ACCESSOR(DeoptimizationOutputData) +CAST_ACCESSOR(TypeFeedbackCells) CAST_ACCESSOR(SymbolTable) CAST_ACCESSOR(JSFunctionResultCache) CAST_ACCESSOR(NormalizedMapCache) +CAST_ACCESSOR(ScopeInfo) CAST_ACCESSOR(CompilationCacheTable) CAST_ACCESSOR(CodeCacheHashTable) CAST_ACCESSOR(PolymorphicCodeCacheHashTable) @@ -2156,9 +2192,12 @@ CAST_ACCESSOR(JSArray) CAST_ACCESSOR(JSRegExp) CAST_ACCESSOR(JSProxy) CAST_ACCESSOR(JSFunctionProxy) +CAST_ACCESSOR(JSSet) +CAST_ACCESSOR(JSMap) CAST_ACCESSOR(JSWeakMap) CAST_ACCESSOR(Foreign) CAST_ACCESSOR(ByteArray) +CAST_ACCESSOR(FreeSpace) CAST_ACCESSOR(ExternalArray) CAST_ACCESSOR(ExternalByteArray) CAST_ACCESSOR(ExternalUnsignedByteArray) @@ -2185,6 +2224,7 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) { SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset) +SMI_ACCESSORS(FreeSpace, size, kSizeOffset) SMI_ACCESSORS(String, length, kLengthOffset) @@ -2341,7 +2381,7 @@ String* SlicedString::parent() { void SlicedString::set_parent(String* parent) { - ASSERT(parent->IsSeqString()); + ASSERT(parent->IsSeqString() || parent->IsExternalString()); WRITE_FIELD(this, kParentOffset, parent); } @@ -2361,7 +2401,7 @@ Object* ConsString::unchecked_first() { void ConsString::set_first(String* value, WriteBarrierMode mode) { WRITE_FIELD(this, kFirstOffset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kFirstOffset, mode); + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kFirstOffset, value, mode); } @@ -2377,29 +2417,83 @@ Object* ConsString::unchecked_second() { void ConsString::set_second(String* value, WriteBarrierMode mode) { WRITE_FIELD(this, kSecondOffset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, mode); + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, value, mode); +} + + +bool ExternalString::is_short() { + InstanceType type = map()->instance_type(); + return (type & kShortExternalStringMask) == kShortExternalStringTag; } -ExternalAsciiString::Resource* ExternalAsciiString::resource() { +const ExternalAsciiString::Resource* ExternalAsciiString::resource() { return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)); } +void ExternalAsciiString::update_data_cache() { + if (is_short()) return; + const char** data_field = + reinterpret_cast<const char**>(FIELD_ADDR(this, kResourceDataOffset)); + *data_field = resource()->data(); +} + + void ExternalAsciiString::set_resource( - ExternalAsciiString::Resource* resource) { - *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)) = resource; + const ExternalAsciiString::Resource* resource) { + *reinterpret_cast<const Resource**>( + FIELD_ADDR(this, kResourceOffset)) = resource; + if (resource != NULL) update_data_cache(); +} + + +const char* ExternalAsciiString::GetChars() { + return resource()->data(); +} + + +uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) { + ASSERT(index >= 0 && index < length()); + return GetChars()[index]; } -ExternalTwoByteString::Resource* ExternalTwoByteString::resource() { +const ExternalTwoByteString::Resource* ExternalTwoByteString::resource() { return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)); } +void ExternalTwoByteString::update_data_cache() { + if (is_short()) return; + const uint16_t** data_field = + reinterpret_cast<const uint16_t**>(FIELD_ADDR(this, kResourceDataOffset)); + *data_field = resource()->data(); +} + + void ExternalTwoByteString::set_resource( - ExternalTwoByteString::Resource* resource) { - *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)) = resource; + const ExternalTwoByteString::Resource* resource) { + *reinterpret_cast<const Resource**>( + FIELD_ADDR(this, kResourceOffset)) = resource; + if (resource != NULL) update_data_cache(); +} + + +const uint16_t* ExternalTwoByteString::GetChars() { + return resource()->data(); +} + + +uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) { + ASSERT(index >= 0 && index < length()); + return GetChars()[index]; +} + + +const uint16_t* ExternalTwoByteString::ExternalTwoByteStringGetData( + unsigned start) { + return GetChars() + start; } @@ -2699,6 +2793,9 @@ int HeapObject::SizeFromMap(Map* map) { if (instance_type == BYTE_ARRAY_TYPE) { return reinterpret_cast<ByteArray*>(this)->ByteArraySize(); } + if (instance_type == FREE_SPACE_TYPE) { + return reinterpret_cast<FreeSpace*>(this)->size(); + } if (instance_type == STRING_TYPE) { return SeqTwoByteString::SizeFor( reinterpret_cast<SeqTwoByteString*>(this)->length()); @@ -2860,12 +2957,6 @@ JSFunction* Map::unchecked_constructor() { } -FixedArray* Map::unchecked_prototype_transitions() { - return reinterpret_cast<FixedArray*>( - READ_FIELD(this, kPrototypeTransitionsOffset)); -} - - Code::Flags Code::flags() { return static_cast<Flags>(READ_INT_FIELD(this, kFlagsOffset)); } @@ -2937,6 +3028,19 @@ void Code::set_major_key(int major) { } +bool Code::is_pregenerated() { + return kind() == STUB && IsPregeneratedField::decode(flags()); +} + + +void Code::set_is_pregenerated(bool value) { + ASSERT(kind() == STUB); + Flags f = flags(); + f = static_cast<Flags>(IsPregeneratedField::update(f, value)); + set_flags(f); +} + + bool Code::optimizable() { ASSERT(kind() == FUNCTION); return READ_BYTE_FIELD(this, kOptimizableOffset) == 1; @@ -2979,6 +3083,21 @@ void Code::set_has_debug_break_slots(bool value) { } +bool Code::is_compiled_optimizable() { + ASSERT(kind() == FUNCTION); + byte flags = READ_BYTE_FIELD(this, kFullCodeFlags); + return FullCodeFlagsIsCompiledOptimizable::decode(flags); +} + + +void Code::set_compiled_optimizable(bool value) { + ASSERT(kind() == FUNCTION); + byte flags = READ_BYTE_FIELD(this, kFullCodeFlags); + flags = FullCodeFlagsIsCompiledOptimizable::update(flags, value); + WRITE_BYTE_FIELD(this, kFullCodeFlags, flags); +} + + int Code::allow_osr_at_loop_nesting_level() { ASSERT(kind() == FUNCTION); return READ_BYTE_FIELD(this, kAllowOSRAtLoopNestingLevelOffset); @@ -3102,6 +3221,19 @@ void Code::set_to_boolean_state(byte value) { WRITE_BYTE_FIELD(this, kToBooleanTypeOffset, value); } + +bool Code::has_function_cache() { + ASSERT(kind() == STUB); + return READ_BYTE_FIELD(this, kHasFunctionCacheOffset) != 0; +} + + +void Code::set_has_function_cache(bool flag) { + ASSERT(kind() == STUB); + WRITE_BYTE_FIELD(this, kHasFunctionCacheOffset, flag); +} + + bool Code::is_inline_cache_stub() { Kind kind = this->kind(); return kind >= FIRST_IC_KIND && kind <= LAST_IC_KIND; @@ -3187,48 +3319,6 @@ Code* Code::GetCodeFromTargetAddress(Address address) { } -Isolate* Map::isolate() { - return heap()->isolate(); -} - - -Heap* Map::heap() { - // NOTE: address() helper is not used to save one instruction. - Heap* heap = Page::FromAddress(reinterpret_cast<Address>(this))->heap_; - ASSERT(heap != NULL); - ASSERT(heap->isolate() == Isolate::Current()); - return heap; -} - - -Heap* Code::heap() { - // NOTE: address() helper is not used to save one instruction. - Heap* heap = Page::FromAddress(reinterpret_cast<Address>(this))->heap_; - ASSERT(heap != NULL); - ASSERT(heap->isolate() == Isolate::Current()); - return heap; -} - - -Isolate* Code::isolate() { - return heap()->isolate(); -} - - -Heap* JSGlobalPropertyCell::heap() { - // NOTE: address() helper is not used to save one instruction. - Heap* heap = Page::FromAddress(reinterpret_cast<Address>(this))->heap_; - ASSERT(heap != NULL); - ASSERT(heap->isolate() == Isolate::Current()); - return heap; -} - - -Isolate* JSGlobalPropertyCell::isolate() { - return heap()->isolate(); -} - - Object* Code::GetObjectFromEntryAddress(Address location_of_address) { return HeapObject:: FromAddress(Memory::Address_at(location_of_address) - Code::kHeaderSize); @@ -3243,46 +3333,7 @@ Object* Map::prototype() { void Map::set_prototype(Object* value, WriteBarrierMode mode) { ASSERT(value->IsNull() || value->IsJSReceiver()); WRITE_FIELD(this, kPrototypeOffset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kPrototypeOffset, mode); -} - - -MaybeObject* Map::GetFastElementsMap() { - if (has_fast_elements()) return this; - Object* obj; - { MaybeObject* maybe_obj = CopyDropTransitions(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - Map* new_map = Map::cast(obj); - new_map->set_elements_kind(FAST_ELEMENTS); - isolate()->counters()->map_to_fast_elements()->Increment(); - return new_map; -} - - -MaybeObject* Map::GetFastDoubleElementsMap() { - if (has_fast_double_elements()) return this; - Object* obj; - { MaybeObject* maybe_obj = CopyDropTransitions(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - Map* new_map = Map::cast(obj); - new_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); - isolate()->counters()->map_to_fast_double_elements()->Increment(); - return new_map; -} - - -MaybeObject* Map::GetSlowElementsMap() { - if (!has_fast_elements() && !has_fast_double_elements()) return this; - Object* obj; - { MaybeObject* maybe_obj = CopyDropTransitions(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - Map* new_map = Map::cast(obj); - new_map->set_elements_kind(DICTIONARY_ELEMENTS); - isolate()->counters()->map_to_slow_elements()->Increment(); - return new_map; + CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kPrototypeOffset, value, mode); } @@ -3317,7 +3368,8 @@ void Map::set_instance_descriptors(DescriptorArray* value, WriteBarrierMode mode) { Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset); - if (value == isolate()->heap()->empty_descriptor_array()) { + Heap* heap = GetHeap(); + if (value == heap->empty_descriptor_array()) { clear_instance_descriptors(); return; } else { @@ -3330,10 +3382,8 @@ void Map::set_instance_descriptors(DescriptorArray* value, } ASSERT(!is_shared()); WRITE_FIELD(this, kInstanceDescriptorsOrBitField3Offset, value); - CONDITIONAL_WRITE_BARRIER(GetHeap(), - this, - kInstanceDescriptorsOrBitField3Offset, - mode); + CONDITIONAL_WRITE_BARRIER( + heap, this, kInstanceDescriptorsOrBitField3Offset, value, mode); } @@ -3362,14 +3412,22 @@ void Map::set_bit_field3(int value) { } +FixedArray* Map::unchecked_prototype_transitions() { + return reinterpret_cast<FixedArray*>( + READ_FIELD(this, kPrototypeTransitionsOffset)); +} + + ACCESSORS(Map, code_cache, Object, kCodeCacheOffset) ACCESSORS(Map, prototype_transitions, FixedArray, kPrototypeTransitionsOffset) ACCESSORS(Map, constructor, Object, kConstructorOffset) ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset) -ACCESSORS(JSFunction, literals, FixedArray, kLiteralsOffset) -ACCESSORS_GCSAFE(JSFunction, next_function_link, Object, - kNextFunctionLinkOffset) +ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset) +ACCESSORS(JSFunction, + next_function_link, + Object, + kNextFunctionLinkOffset) ACCESSORS(GlobalObject, builtins, JSBuiltinsObject, kBuiltinsOffset) ACCESSORS(GlobalObject, global_context, Context, kGlobalContextOffset) @@ -3383,6 +3441,9 @@ ACCESSORS(AccessorInfo, data, Object, kDataOffset) ACCESSORS(AccessorInfo, name, Object, kNameOffset) ACCESSORS(AccessorInfo, flag, Smi, kFlagOffset) +ACCESSORS(AccessorPair, getter, Object, kGetterOffset) +ACCESSORS(AccessorPair, setter, Object, kSetterOffset) + ACCESSORS(AccessCheckInfo, named_callback, Object, kNamedCallbackOffset) ACCESSORS(AccessCheckInfo, indexed_callback, Object, kIndexedCallbackOffset) ACCESSORS(AccessCheckInfo, data, Object, kDataOffset) @@ -3458,8 +3519,8 @@ ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex) #endif ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset) -ACCESSORS_GCSAFE(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset) -ACCESSORS_GCSAFE(SharedFunctionInfo, initial_map, Object, kInitialMapOffset) +ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset) +ACCESSORS(SharedFunctionInfo, initial_map, Object, kInitialMapOffset) ACCESSORS(SharedFunctionInfo, instance_class_name, Object, kInstanceClassNameOffset) ACCESSORS(SharedFunctionInfo, function_data, Object, kFunctionDataOffset) @@ -3469,6 +3530,8 @@ ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset) ACCESSORS(SharedFunctionInfo, this_property_assignments, Object, kThisPropertyAssignmentsOffset) +SMI_ACCESSORS(SharedFunctionInfo, profiler_ticks, kProfilerTicksOffset) + BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype, kHiddenPrototypeBit) BOOL_ACCESSORS(FunctionTemplateInfo, flag, undetectable, kUndetectableBit) @@ -3515,6 +3578,8 @@ SMI_ACCESSORS(SharedFunctionInfo, compiler_hints, SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count, kThisPropertyAssignmentsCountOffset) SMI_ACCESSORS(SharedFunctionInfo, opt_count, kOptCountOffset) +SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset) +SMI_ACCESSORS(SharedFunctionInfo, deopt_counter, kDeoptCounterOffset) #else #define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \ @@ -3565,6 +3630,9 @@ PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, this_property_assignments_count, kThisPropertyAssignmentsCountOffset) PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, opt_count, kOptCountOffset) + +PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset) +PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, deopt_counter, kDeoptCounterOffset) #endif @@ -3608,14 +3676,48 @@ void SharedFunctionInfo::set_optimization_disabled(bool disable) { } -BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, strict_mode, - kStrictModeFunction) +LanguageMode SharedFunctionInfo::language_mode() { + int hints = compiler_hints(); + if (BooleanBit::get(hints, kExtendedModeFunction)) { + ASSERT(BooleanBit::get(hints, kStrictModeFunction)); + return EXTENDED_MODE; + } + return BooleanBit::get(hints, kStrictModeFunction) + ? STRICT_MODE : CLASSIC_MODE; +} + + +void SharedFunctionInfo::set_language_mode(LanguageMode language_mode) { + // We only allow language mode transitions that go set the same language mode + // again or go up in the chain: + // CLASSIC_MODE -> STRICT_MODE -> EXTENDED_MODE. + ASSERT(this->language_mode() == CLASSIC_MODE || + this->language_mode() == language_mode || + language_mode == EXTENDED_MODE); + int hints = compiler_hints(); + hints = BooleanBit::set( + hints, kStrictModeFunction, language_mode != CLASSIC_MODE); + hints = BooleanBit::set( + hints, kExtendedModeFunction, language_mode == EXTENDED_MODE); + set_compiler_hints(hints); +} + + +bool SharedFunctionInfo::is_classic_mode() { + return !BooleanBit::get(compiler_hints(), kStrictModeFunction); +} + +BOOL_GETTER(SharedFunctionInfo, compiler_hints, is_extended_mode, + kExtendedModeFunction) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, native, kNative) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, name_should_print_as_anonymous, kNameShouldPrintAsAnonymous) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, bound, kBoundFunction) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_anonymous, kIsAnonymous) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_crankshaft, + kDontCrankshaft) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_inline, kDontInline) ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset) ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset) @@ -3665,30 +3767,23 @@ Code* SharedFunctionInfo::unchecked_code() { void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) { WRITE_FIELD(this, kCodeOffset, value); - ASSERT(!Isolate::Current()->heap()->InNewSpace(value)); + CONDITIONAL_WRITE_BARRIER(value->GetHeap(), this, kCodeOffset, value, mode); } -SerializedScopeInfo* SharedFunctionInfo::scope_info() { - return reinterpret_cast<SerializedScopeInfo*>( - READ_FIELD(this, kScopeInfoOffset)); +ScopeInfo* SharedFunctionInfo::scope_info() { + return reinterpret_cast<ScopeInfo*>(READ_FIELD(this, kScopeInfoOffset)); } -void SharedFunctionInfo::set_scope_info(SerializedScopeInfo* value, +void SharedFunctionInfo::set_scope_info(ScopeInfo* value, WriteBarrierMode mode) { WRITE_FIELD(this, kScopeInfoOffset, reinterpret_cast<Object*>(value)); - CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kScopeInfoOffset, mode); -} - - -Smi* SharedFunctionInfo::deopt_counter() { - return reinterpret_cast<Smi*>(READ_FIELD(this, kDeoptCounterOffset)); -} - - -void SharedFunctionInfo::set_deopt_counter(Smi* value) { - WRITE_FIELD(this, kDeoptCounterOffset, value); + CONDITIONAL_WRITE_BARRIER(GetHeap(), + this, + kScopeInfoOffset, + reinterpret_cast<Object*>(value), + mode); } @@ -3726,8 +3821,8 @@ int SharedFunctionInfo::code_age() { void SharedFunctionInfo::set_code_age(int code_age) { - set_compiler_hints(compiler_hints() | - ((code_age & kCodeAgeMask) << kCodeAgeShift)); + int hints = compiler_hints() & ~(kCodeAgeMask << kCodeAgeShift); + set_compiler_hints(hints | ((code_age & kCodeAgeMask) << kCodeAgeShift)); } @@ -3775,10 +3870,13 @@ Code* JSFunction::unchecked_code() { void JSFunction::set_code(Code* value) { - // Skip the write barrier because code is never in new space. ASSERT(!HEAP->InNewSpace(value)); Address entry = value->entry(); WRITE_INTPTR_FIELD(this, kCodeEntryOffset, reinterpret_cast<intptr_t>(entry)); + GetHeap()->incremental_marking()->RecordWriteOfCodeEntry( + this, + HeapObject::RawField(this, kCodeEntryOffset), + value); } @@ -3818,7 +3916,7 @@ SharedFunctionInfo* JSFunction::unchecked_shared() { void JSFunction::set_context(Object* value) { ASSERT(value->IsUndefined() || value->IsContext()); WRITE_FIELD(this, kContextOffset, value); - WRITE_BARRIER(this, kContextOffset); + WRITE_BARRIER(GetHeap(), this, kContextOffset, value); } ACCESSORS(JSFunction, prototype_or_initial_map, Object, @@ -3835,6 +3933,36 @@ void JSFunction::set_initial_map(Map* value) { } +MaybeObject* JSFunction::set_initial_map_and_cache_transitions( + Map* initial_map) { + Context* global_context = context()->global_context(); + Object* array_function = + global_context->get(Context::ARRAY_FUNCTION_INDEX); + if (array_function->IsJSFunction() && + this == JSFunction::cast(array_function)) { + ASSERT(initial_map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); + + MaybeObject* maybe_map = initial_map->CopyDropTransitions(); + Map* new_double_map = NULL; + if (!maybe_map->To<Map>(&new_double_map)) return maybe_map; + new_double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); + initial_map->AddElementsTransition(FAST_DOUBLE_ELEMENTS, new_double_map); + + maybe_map = new_double_map->CopyDropTransitions(); + Map* new_object_map = NULL; + if (!maybe_map->To<Map>(&new_object_map)) return maybe_map; + new_object_map->set_elements_kind(FAST_ELEMENTS); + new_double_map->AddElementsTransition(FAST_ELEMENTS, new_object_map); + + global_context->set_smi_js_array_map(initial_map); + global_context->set_double_js_array_map(new_double_map); + global_context->set_object_js_array_map(new_object_map); + } + set_initial_map(initial_map); + return this; +} + + bool JSFunction::has_initial_map() { return prototype_or_initial_map()->IsMap(); } @@ -3877,7 +4005,36 @@ bool JSFunction::is_compiled() { } +FixedArray* JSFunction::literals() { + ASSERT(!shared()->bound()); + return literals_or_bindings(); +} + + +void JSFunction::set_literals(FixedArray* literals) { + ASSERT(!shared()->bound()); + set_literals_or_bindings(literals); +} + + +FixedArray* JSFunction::function_bindings() { + ASSERT(shared()->bound()); + return literals_or_bindings(); +} + + +void JSFunction::set_function_bindings(FixedArray* bindings) { + ASSERT(shared()->bound()); + // Bound function literal may be initialized to the empty fixed array + // before the bindings are set. + ASSERT(bindings == GetHeap()->empty_fixed_array() || + bindings->map() == GetHeap()->fixed_cow_array_map()); + set_literals_or_bindings(bindings); +} + + int JSFunction::NumberOfLiterals() { + ASSERT(!shared()->bound()); return literals()->length(); } @@ -3892,7 +4049,7 @@ void JSBuiltinsObject::set_javascript_builtin(Builtins::JavaScript id, Object* value) { ASSERT(id < kJSBuiltinsCount); // id is unsigned. WRITE_FIELD(this, OffsetOfFunctionWithId(id), value); - WRITE_BARRIER(this, OffsetOfFunctionWithId(id)); + WRITE_BARRIER(GetHeap(), this, OffsetOfFunctionWithId(id), value); } @@ -3911,6 +4068,7 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id, ACCESSORS(JSProxy, handler, Object, kHandlerOffset) +ACCESSORS(JSProxy, hash, Object, kHashOffset) ACCESSORS(JSFunctionProxy, call_trap, Object, kCallTrapOffset) ACCESSORS(JSFunctionProxy, construct_trap, Object, kConstructTrapOffset) @@ -3923,22 +4081,19 @@ void JSProxy::InitializeBody(int object_size, Object* value) { } -ACCESSORS(JSWeakMap, table, ObjectHashTable, kTableOffset) -ACCESSORS_GCSAFE(JSWeakMap, next, Object, kNextOffset) +ACCESSORS(JSSet, table, Object, kTableOffset) +ACCESSORS(JSMap, table, Object, kTableOffset) +ACCESSORS(JSWeakMap, table, Object, kTableOffset) +ACCESSORS(JSWeakMap, next, Object, kNextOffset) -ObjectHashTable* JSWeakMap::unchecked_table() { - return reinterpret_cast<ObjectHashTable*>(READ_FIELD(this, kTableOffset)); +Address Foreign::foreign_address() { + return AddressFrom<Address>(READ_INTPTR_FIELD(this, kForeignAddressOffset)); } -Address Foreign::address() { - return AddressFrom<Address>(READ_INTPTR_FIELD(this, kAddressOffset)); -} - - -void Foreign::set_address(Address value) { - WRITE_INTPTR_FIELD(this, kAddressOffset, OffsetFrom(value)); +void Foreign::set_foreign_address(Address value) { + WRITE_INTPTR_FIELD(this, kForeignAddressOffset, OffsetFrom(value)); } @@ -3970,9 +4125,11 @@ JSMessageObject* JSMessageObject::cast(Object* obj) { INT_ACCESSORS(Code, instruction_size, kInstructionSizeOffset) ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset) +ACCESSORS(Code, handler_table, FixedArray, kHandlerTableOffset) ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset) -ACCESSORS(Code, next_code_flushing_candidate, - Object, kNextCodeFlushingCandidateOffset) +ACCESSORS(Code, type_feedback_cells, TypeFeedbackCells, + kTypeFeedbackCellsOffset) +ACCESSORS(Code, gc_metadata, Object, kGCMetadataOffset) byte* Code::instruction_start() { @@ -4016,9 +4173,8 @@ byte* Code::entry() { } -bool Code::contains(byte* pc) { - return (instruction_start() <= pc) && - (pc <= instruction_start() + instruction_size()); +bool Code::contains(byte* inner_pointer) { + return (address() <= inner_pointer) && (inner_pointer <= address() + Size()); } @@ -4097,6 +4253,7 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) { if (value->IsSmi()) { fa->set_unchecked(index, Smi::cast(value)); } else { + // We only do this during GC, so we don't need to notify the write barrier. fa->set_unchecked(heap, index, value, SKIP_WRITE_BARRIER); } } @@ -4104,15 +4261,23 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) { ElementsKind JSObject::GetElementsKind() { ElementsKind kind = map()->elements_kind(); - ASSERT((kind == FAST_ELEMENTS && - (elements()->map() == GetHeap()->fixed_array_map() || - elements()->map() == GetHeap()->fixed_cow_array_map())) || - (kind == FAST_DOUBLE_ELEMENTS && - elements()->IsFixedDoubleArray()) || - (kind == DICTIONARY_ELEMENTS && - elements()->IsFixedArray() && - elements()->IsDictionary()) || - (kind > DICTIONARY_ELEMENTS)); +#if DEBUG + FixedArrayBase* fixed_array = + reinterpret_cast<FixedArrayBase*>(READ_FIELD(this, kElementsOffset)); + Map* map = fixed_array->map(); + ASSERT(((kind == FAST_ELEMENTS || kind == FAST_SMI_ONLY_ELEMENTS) && + (map == GetHeap()->fixed_array_map() || + map == GetHeap()->fixed_cow_array_map())) || + (kind == FAST_DOUBLE_ELEMENTS && + (fixed_array->IsFixedDoubleArray() || + fixed_array == GetHeap()->empty_fixed_array())) || + (kind == DICTIONARY_ELEMENTS && + fixed_array->IsFixedArray() && + fixed_array->IsDictionary()) || + (kind > DICTIONARY_ELEMENTS)); + ASSERT((kind != NON_STRICT_ARGUMENTS_ELEMENTS) || + (elements()->IsFixedArray() && elements()->length() >= 2)); +#endif return kind; } @@ -4127,6 +4292,18 @@ bool JSObject::HasFastElements() { } +bool JSObject::HasFastSmiOnlyElements() { + return GetElementsKind() == FAST_SMI_ONLY_ELEMENTS; +} + + +bool JSObject::HasFastTypeElements() { + ElementsKind elements_kind = GetElementsKind(); + return elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_ELEMENTS; +} + + bool JSObject::HasFastDoubleElements() { return GetElementsKind() == FAST_DOUBLE_ELEMENTS; } @@ -4137,6 +4314,11 @@ bool JSObject::HasDictionaryElements() { } +bool JSObject::HasNonStrictArgumentsElements() { + return GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS; +} + + bool JSObject::HasExternalArrayElements() { HeapObject* array = elements(); ASSERT(array != NULL); @@ -4179,16 +4361,8 @@ bool JSObject::HasIndexedInterceptor() { } -bool JSObject::AllowsSetElementsLength() { - bool result = elements()->IsFixedArray() || - elements()->IsFixedDoubleArray(); - ASSERT(result == !HasExternalArrayElements()); - return result; -} - - MaybeObject* JSObject::EnsureWritableFastElements() { - ASSERT(HasFastElements()); + ASSERT(HasFastTypeElements()); FixedArray* elems = FixedArray::cast(elements()); Isolate* isolate = GetIsolate(); if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems; @@ -4366,44 +4540,18 @@ Object* JSObject::BypassGlobalProxy() { } -bool JSObject::HasHiddenPropertiesObject() { - ASSERT(!IsJSGlobalProxy()); - return GetPropertyAttributePostInterceptor(this, - GetHeap()->hidden_symbol(), - false) != ABSENT; -} - - -Object* JSObject::GetHiddenPropertiesObject() { - ASSERT(!IsJSGlobalProxy()); - PropertyAttributes attributes; - // You can't install a getter on a property indexed by the hidden symbol, - // so we can be sure that GetLocalPropertyPostInterceptor returns a real - // object. - Object* result = - GetLocalPropertyPostInterceptor(this, - GetHeap()->hidden_symbol(), - &attributes)->ToObjectUnchecked(); - return result; -} - - -MaybeObject* JSObject::SetHiddenPropertiesObject(Object* hidden_obj) { - ASSERT(!IsJSGlobalProxy()); - return SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), - hidden_obj, - DONT_ENUM, - kNonStrictMode); -} - - -bool JSObject::HasHiddenProperties() { - return !GetHiddenProperties(OMIT_CREATION)->ToObjectChecked()->IsUndefined(); +MaybeObject* JSReceiver::GetIdentityHash(CreationFlag flag) { + return IsJSProxy() + ? JSProxy::cast(this)->GetIdentityHash(flag) + : JSObject::cast(this)->GetIdentityHash(flag); } -bool JSObject::HasElement(uint32_t index) { - return HasElementWithReceiver(this, index); +bool JSReceiver::HasElement(uint32_t index) { + if (IsJSProxy()) { + return JSProxy::cast(this)->HasElementWithHandler(index); + } + return JSObject::cast(this)->HasElementWithReceiver(this, index); } @@ -4466,7 +4614,7 @@ void Dictionary<Shape, Key>::SetEntry(int entry, WriteBarrierMode mode = FixedArray::GetWriteBarrierMode(no_gc); FixedArray::set(index, key, mode); FixedArray::set(index+1, value, mode); - FixedArray::fast_set(this, index+2, details.AsSmi()); + FixedArray::set(index+2, details.AsSmi()); } @@ -4526,36 +4674,33 @@ MaybeObject* StringDictionaryShape::AsObject(String* key) { } -bool ObjectHashTableShape::IsMatch(JSObject* key, Object* other) { - return key == JSObject::cast(other); +template <int entrysize> +bool ObjectHashTableShape<entrysize>::IsMatch(Object* key, Object* other) { + return key->SameValue(other); } -uint32_t ObjectHashTableShape::Hash(JSObject* key) { - MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION); - ASSERT(!maybe_hash->IsFailure()); - return Smi::cast(maybe_hash->ToObjectUnchecked())->value(); +template <int entrysize> +uint32_t ObjectHashTableShape<entrysize>::Hash(Object* key) { + MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); + return Smi::cast(maybe_hash->ToObjectChecked())->value(); } -uint32_t ObjectHashTableShape::HashForObject(JSObject* key, Object* other) { - MaybeObject* maybe_hash = JSObject::cast(other)->GetIdentityHash( - JSObject::OMIT_CREATION); - ASSERT(!maybe_hash->IsFailure()); - return Smi::cast(maybe_hash->ToObjectUnchecked())->value(); +template <int entrysize> +uint32_t ObjectHashTableShape<entrysize>::HashForObject(Object* key, + Object* other) { + MaybeObject* maybe_hash = other->GetHash(OMIT_CREATION); + return Smi::cast(maybe_hash->ToObjectChecked())->value(); } -MaybeObject* ObjectHashTableShape::AsObject(JSObject* key) { +template <int entrysize> +MaybeObject* ObjectHashTableShape<entrysize>::AsObject(Object* key) { return key; } -void ObjectHashTable::RemoveEntry(int entry) { - RemoveEntry(entry, GetHeap()); -} - - void Map::ClearCodeCache(Heap* heap) { // No write barrier is needed since empty_fixed_array is not in new space. // Please note this function is used during marking: @@ -4566,7 +4711,7 @@ void Map::ClearCodeCache(Heap* heap) { void JSArray::EnsureSize(int required_size) { - ASSERT(HasFastElements()); + ASSERT(HasFastTypeElements()); FixedArray* elts = FixedArray::cast(elements()); const int kArraySizeThatFitsComfortablyInNewSpace = 128; if (elts->length() < required_size) { @@ -4584,13 +4729,31 @@ void JSArray::EnsureSize(int required_size) { void JSArray::set_length(Smi* length) { + // Don't need a write barrier for a Smi. set_length(static_cast<Object*>(length), SKIP_WRITE_BARRIER); } -void JSArray::SetContent(FixedArray* storage) { - set_length(Smi::FromInt(storage->length())); +bool JSArray::AllowsSetElementsLength() { + bool result = elements()->IsFixedArray() || elements()->IsFixedDoubleArray(); + ASSERT(result == !HasExternalArrayElements()); + return result; +} + + +MaybeObject* JSArray::SetContent(FixedArrayBase* storage) { + MaybeObject* maybe_result = EnsureCanContainElements( + storage, ALLOW_COPIED_DOUBLE_ELEMENTS); + if (maybe_result->IsFailure()) return maybe_result; + ASSERT((storage->map() == GetHeap()->fixed_double_array_map() && + GetElementsKind() == FAST_DOUBLE_ELEMENTS) || + ((storage->map() != GetHeap()->fixed_double_array_map()) && + ((GetElementsKind() == FAST_ELEMENTS) || + (GetElementsKind() == FAST_SMI_ONLY_ELEMENTS && + FixedArray::cast(storage)->ContainsOnlySmisOrHoles())))); set_elements(storage); + set_length(Smi::FromInt(storage->length())); + return this; } @@ -4600,6 +4763,47 @@ MaybeObject* FixedArray::Copy() { } +MaybeObject* FixedDoubleArray::Copy() { + if (length() == 0) return this; + return GetHeap()->CopyFixedDoubleArray(this); +} + + +void TypeFeedbackCells::SetAstId(int index, Smi* id) { + set(1 + index * 2, id); +} + + +Smi* TypeFeedbackCells::AstId(int index) { + return Smi::cast(get(1 + index * 2)); +} + + +void TypeFeedbackCells::SetCell(int index, JSGlobalPropertyCell* cell) { + set(index * 2, cell); +} + + +JSGlobalPropertyCell* TypeFeedbackCells::Cell(int index) { + return JSGlobalPropertyCell::cast(get(index * 2)); +} + + +Handle<Object> TypeFeedbackCells::UninitializedSentinel(Isolate* isolate) { + return isolate->factory()->the_hole_value(); +} + + +Handle<Object> TypeFeedbackCells::MegamorphicSentinel(Isolate* isolate) { + return isolate->factory()->undefined_value(); +} + + +Object* TypeFeedbackCells::RawUninitializedSentinel(Heap* heap) { + return heap->raw_unchecked_the_hole_value(); +} + + Relocatable::Relocatable(Isolate* isolate) { ASSERT(isolate == Isolate::Current()); isolate_ = isolate; @@ -4622,14 +4826,14 @@ int JSObject::BodyDescriptor::SizeOf(Map* map, HeapObject* object) { void Foreign::ForeignIterateBody(ObjectVisitor* v) { v->VisitExternalReference( - reinterpret_cast<Address *>(FIELD_ADDR(this, kAddressOffset))); + reinterpret_cast<Address*>(FIELD_ADDR(this, kForeignAddressOffset))); } template<typename StaticVisitor> void Foreign::ForeignIterateBody() { StaticVisitor::VisitExternalReference( - reinterpret_cast<Address *>(FIELD_ADDR(this, kAddressOffset))); + reinterpret_cast<Address*>(FIELD_ADDR(this, kForeignAddressOffset))); } diff --git a/deps/v8/src/objects-printer.cc b/deps/v8/src/objects-printer.cc index 0398572f90..67adad6919 100644 --- a/deps/v8/src/objects-printer.cc +++ b/deps/v8/src/objects-printer.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -82,12 +82,18 @@ void HeapObject::HeapObjectPrint(FILE* out) { case HEAP_NUMBER_TYPE: HeapNumber::cast(this)->HeapNumberPrint(out); break; + case FIXED_DOUBLE_ARRAY_TYPE: + FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out); + break; case FIXED_ARRAY_TYPE: FixedArray::cast(this)->FixedArrayPrint(out); break; case BYTE_ARRAY_TYPE: ByteArray::cast(this)->ByteArrayPrint(out); break; + case FREE_SPACE_TYPE: + FreeSpace::cast(this)->FreeSpacePrint(out); + break; case EXTERNAL_PIXEL_ARRAY_TYPE: ExternalPixelArray::cast(this)->ExternalPixelArrayPrint(out); break; @@ -189,6 +195,11 @@ void ByteArray::ByteArrayPrint(FILE* out) { } +void FreeSpace::FreeSpacePrint(FILE* out) { + PrintF(out, "free space, size %d", Size()); +} + + void ExternalPixelArray::ExternalPixelArrayPrint(FILE* out) { PrintF(out, "external pixel array"); } @@ -256,16 +267,37 @@ void JSObject::PrintProperties(FILE* out) { descs->GetCallbacksObject(i)->ShortPrint(out); PrintF(out, " (callback)\n"); break; + case ELEMENTS_TRANSITION: { + PrintF(out, "(elements transition to "); + Object* descriptor_contents = descs->GetValue(i); + if (descriptor_contents->IsMap()) { + Map* map = Map::cast(descriptor_contents); + PrintElementsKind(out, map->elements_kind()); + } else { + FixedArray* map_array = FixedArray::cast(descriptor_contents); + for (int i = 0; i < map_array->length(); ++i) { + Map* map = Map::cast(map_array->get(i)); + if (i != 0) { + PrintF(out, ", "); + } + PrintElementsKind(out, map->elements_kind()); + } + } + PrintF(out, ")\n"); + break; + } case MAP_TRANSITION: - PrintF(out, " (map transition)\n"); + PrintF(out, "(map transition)\n"); break; case CONSTANT_TRANSITION: - PrintF(out, " (constant transition)\n"); + PrintF(out, "(constant transition)\n"); break; case NULL_DESCRIPTOR: - PrintF(out, " (null descriptor)\n"); + PrintF(out, "(null descriptor)\n"); break; - default: + case NORMAL: // only in slow mode + case HANDLER: // only in lookup results, not in descriptors + case INTERCEPTOR: // only in lookup results, not in descriptors UNREACHABLE(); break; } @@ -277,7 +309,10 @@ void JSObject::PrintProperties(FILE* out) { void JSObject::PrintElements(FILE* out) { - switch (GetElementsKind()) { + // Don't call GetElementsKind, its validation code can cause the printer to + // fail when debugging. + switch (map()->elements_kind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { // Print in array notation for non-sparse arrays. FixedArray* p = FixedArray::cast(elements()); @@ -385,8 +420,13 @@ void JSObject::PrintElements(FILE* out) { void JSObject::JSObjectPrint(FILE* out) { PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this)); - PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map())); - PrintF(out, " - prototype = %p\n", reinterpret_cast<void*>(GetPrototype())); + PrintF(out, " - map = %p [", reinterpret_cast<void*>(map())); + // Don't call GetElementsKind, its validation code can cause the printer to + // fail when debugging. + PrintElementsKind(out, this->map()->elements_kind()); + PrintF(out, + "]\n - prototype = %p\n", + reinterpret_cast<void*>(GetPrototype())); PrintF(out, " {\n"); PrintProperties(out); PrintElements(out); @@ -406,6 +446,9 @@ static const char* TypeToString(InstanceType type) { case EXTERNAL_ASCII_SYMBOL_TYPE: case EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE: case EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL"; + case SHORT_EXTERNAL_ASCII_SYMBOL_TYPE: + case SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE: + case SHORT_EXTERNAL_SYMBOL_TYPE: return "SHORT_EXTERNAL_SYMBOL"; case ASCII_STRING_TYPE: return "ASCII_STRING"; case STRING_TYPE: return "TWO_BYTE_STRING"; case CONS_STRING_TYPE: @@ -413,8 +456,12 @@ static const char* TypeToString(InstanceType type) { case EXTERNAL_ASCII_STRING_TYPE: case EXTERNAL_STRING_WITH_ASCII_DATA_TYPE: case EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING"; + case SHORT_EXTERNAL_ASCII_STRING_TYPE: + case SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE: + case SHORT_EXTERNAL_STRING_TYPE: return "SHORT_EXTERNAL_STRING"; case FIXED_ARRAY_TYPE: return "FIXED_ARRAY"; case BYTE_ARRAY_TYPE: return "BYTE_ARRAY"; + case FREE_SPACE_TYPE: return "FREE_SPACE"; case EXTERNAL_PIXEL_ARRAY_TYPE: return "EXTERNAL_PIXEL_ARRAY"; case EXTERNAL_BYTE_ARRAY_TYPE: return "EXTERNAL_BYTE_ARRAY"; case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: @@ -458,7 +505,9 @@ void Map::MapPrint(FILE* out) { PrintF(out, " - type: %s\n", TypeToString(instance_type())); PrintF(out, " - instance size: %d\n", instance_size()); PrintF(out, " - inobject properties: %d\n", inobject_properties()); - PrintF(out, " - pre-allocated property fields: %d\n", + PrintF(out, " - elements kind: "); + PrintElementsKind(out, elements_kind()); + PrintF(out, "\n - pre-allocated property fields: %d\n", pre_allocated_property_fields()); PrintF(out, " - unused property fields: %d\n", unused_property_fields()); if (is_hidden_prototype()) { @@ -516,6 +565,20 @@ void FixedArray::FixedArrayPrint(FILE* out) { } +void FixedDoubleArray::FixedDoubleArrayPrint(FILE* out) { + HeapObject::PrintHeader(out, "FixedDoubleArray"); + PrintF(out, " - length: %d", length()); + for (int i = 0; i < length(); i++) { + if (is_the_hole(i)) { + PrintF(out, "\n [%d]: <the hole>", i); + } else { + PrintF(out, "\n [%d]: %g", i, get_scalar(i)); + } + } + PrintF(out, "\n"); +} + + void JSValue::JSValuePrint(FILE* out) { HeapObject::PrintHeader(out, "ValueObject"); value()->Print(out); @@ -568,7 +631,7 @@ void String::StringPrint(FILE* out) { // This method is only meant to be called from gdb for debugging purposes. -// Since the string can also be in two-byte encoding, non-ascii characters +// Since the string can also be in two-byte encoding, non-ASCII characters // will be ignored in the output. char* String::ToAsciiArray() { // Static so that subsequent calls frees previously allocated space. @@ -587,6 +650,8 @@ void JSProxy::JSProxyPrint(FILE* out) { PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map())); PrintF(out, " - handler = "); handler()->Print(out); + PrintF(out, " - hash = "); + hash()->Print(out); PrintF(out, "\n"); } @@ -607,7 +672,6 @@ void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) { void JSWeakMap::JSWeakMapPrint(FILE* out) { HeapObject::PrintHeader(out, "JSWeakMap"); PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map())); - PrintF(out, " - number of elements = %d\n", table()->NumberOfElements()); PrintF(out, " - table = "); table()->ShortPrint(out); PrintF(out, "\n"); @@ -707,7 +771,7 @@ void Code::CodePrint(FILE* out) { void Foreign::ForeignPrint(FILE* out) { - PrintF(out, "foreign address : %p", address()); + PrintF(out, "foreign address : %p", foreign_address()); } @@ -726,6 +790,15 @@ void AccessorInfo::AccessorInfoPrint(FILE* out) { } +void AccessorPair::AccessorPairPrint(FILE* out) { + HeapObject::PrintHeader(out, "AccessorPair"); + PrintF(out, "\n - getter: "); + getter()->ShortPrint(out); + PrintF(out, "\n - setter: "); + setter()->ShortPrint(out); +} + + void AccessCheckInfo::AccessCheckInfoPrint(FILE* out) { HeapObject::PrintHeader(out, "AccessCheckInfo"); PrintF(out, "\n - named_callback: "); @@ -802,10 +875,15 @@ void FunctionTemplateInfo::FunctionTemplateInfoPrint(FILE* out) { void ObjectTemplateInfo::ObjectTemplateInfoPrint(FILE* out) { HeapObject::PrintHeader(out, "ObjectTemplateInfo"); + PrintF(out, " - tag: "); + tag()->ShortPrint(out); + PrintF(out, "\n - property_list: "); + property_list()->ShortPrint(out); PrintF(out, "\n - constructor: "); constructor()->ShortPrint(out); PrintF(out, "\n - internal_field_count: "); internal_field_count()->ShortPrint(out); + PrintF(out, "\n"); } diff --git a/deps/v8/src/objects-visiting-inl.h b/deps/v8/src/objects-visiting-inl.h new file mode 100644 index 0000000000..880b44b50e --- /dev/null +++ b/deps/v8/src/objects-visiting-inl.h @@ -0,0 +1,155 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_OBJECTS_VISITING_INL_H_ +#define V8_OBJECTS_VISITING_INL_H_ + + +namespace v8 { +namespace internal { + +template<typename StaticVisitor> +void StaticNewSpaceVisitor<StaticVisitor>::Initialize() { + table_.Register(kVisitShortcutCandidate, + &FixedBodyVisitor<StaticVisitor, + ConsString::BodyDescriptor, + int>::Visit); + + table_.Register(kVisitConsString, + &FixedBodyVisitor<StaticVisitor, + ConsString::BodyDescriptor, + int>::Visit); + + table_.Register(kVisitSlicedString, + &FixedBodyVisitor<StaticVisitor, + SlicedString::BodyDescriptor, + int>::Visit); + + table_.Register(kVisitFixedArray, + &FlexibleBodyVisitor<StaticVisitor, + FixedArray::BodyDescriptor, + int>::Visit); + + table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray); + + table_.Register(kVisitGlobalContext, + &FixedBodyVisitor<StaticVisitor, + Context::ScavengeBodyDescriptor, + int>::Visit); + + table_.Register(kVisitByteArray, &VisitByteArray); + + table_.Register(kVisitSharedFunctionInfo, + &FixedBodyVisitor<StaticVisitor, + SharedFunctionInfo::BodyDescriptor, + int>::Visit); + + table_.Register(kVisitSeqAsciiString, &VisitSeqAsciiString); + + table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString); + + table_.Register(kVisitJSFunction, + &JSObjectVisitor:: + template VisitSpecialized<JSFunction::kSize>); + + table_.Register(kVisitFreeSpace, &VisitFreeSpace); + + table_.Register(kVisitJSWeakMap, &JSObjectVisitor::Visit); + + table_.Register(kVisitJSRegExp, &JSObjectVisitor::Visit); + + table_.template RegisterSpecializations<DataObjectVisitor, + kVisitDataObject, + kVisitDataObjectGeneric>(); + + table_.template RegisterSpecializations<JSObjectVisitor, + kVisitJSObject, + kVisitJSObjectGeneric>(); + table_.template RegisterSpecializations<StructVisitor, + kVisitStruct, + kVisitStructGeneric>(); +} + + +void Code::CodeIterateBody(ObjectVisitor* v) { + int mode_mask = RelocInfo::kCodeTargetMask | + RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | + RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | + RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | + RelocInfo::ModeMask(RelocInfo::JS_RETURN) | + RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | + RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + + // There are two places where we iterate code bodies: here and the + // templated CodeIterateBody (below). They should be kept in sync. + IteratePointer(v, kRelocationInfoOffset); + IteratePointer(v, kHandlerTableOffset); + IteratePointer(v, kDeoptimizationDataOffset); + IteratePointer(v, kTypeFeedbackCellsOffset); + + RelocIterator it(this, mode_mask); + for (; !it.done(); it.next()) { + it.rinfo()->Visit(v); + } +} + + +template<typename StaticVisitor> +void Code::CodeIterateBody(Heap* heap) { + int mode_mask = RelocInfo::kCodeTargetMask | + RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | + RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | + RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | + RelocInfo::ModeMask(RelocInfo::JS_RETURN) | + RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | + RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + + // There are two places where we iterate code bodies: here and the + // non-templated CodeIterateBody (above). They should be kept in sync. + StaticVisitor::VisitPointer( + heap, + reinterpret_cast<Object**>(this->address() + kRelocationInfoOffset)); + StaticVisitor::VisitPointer( + heap, + reinterpret_cast<Object**>(this->address() + kHandlerTableOffset)); + StaticVisitor::VisitPointer( + heap, + reinterpret_cast<Object**>(this->address() + kDeoptimizationDataOffset)); + StaticVisitor::VisitPointer( + heap, + reinterpret_cast<Object**>(this->address() + kTypeFeedbackCellsOffset)); + + RelocIterator it(this, mode_mask); + for (; !it.done(); it.next()) { + it.rinfo()->template Visit<StaticVisitor>(heap); + } +} + + +} } // namespace v8::internal + +#endif // V8_OBJECTS_VISITING_INL_H_ diff --git a/deps/v8/src/objects-visiting.cc b/deps/v8/src/objects-visiting.cc index 0aa21dd6ed..9ca102b2fd 100644 --- a/deps/v8/src/objects-visiting.cc +++ b/deps/v8/src/objects-visiting.cc @@ -64,7 +64,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case kExternalStringTag: return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric, - ExternalString::kSize); + instance_size); } UNREACHABLE(); } @@ -73,6 +73,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case BYTE_ARRAY_TYPE: return kVisitByteArray; + case FREE_SPACE_TYPE: + return kVisitFreeSpace; + case FIXED_ARRAY_TYPE: return kVisitFixedArray; @@ -91,6 +94,16 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case JS_GLOBAL_PROPERTY_CELL_TYPE: return kVisitPropertyCell; + case JS_SET_TYPE: + return GetVisitorIdForSize(kVisitStruct, + kVisitStructGeneric, + JSSet::kSize); + + case JS_MAP_TYPE: + return GetVisitorIdForSize(kVisitStruct, + kVisitStructGeneric, + JSMap::kSize); + case JS_WEAK_MAP_TYPE: return kVisitJSWeakMap; diff --git a/deps/v8/src/objects-visiting.h b/deps/v8/src/objects-visiting.h index 4ce1bd077b..e6ddfed4a7 100644 --- a/deps/v8/src/objects-visiting.h +++ b/deps/v8/src/objects-visiting.h @@ -30,22 +30,6 @@ #include "allocation.h" -#if V8_TARGET_ARCH_IA32 -#include "ia32/assembler-ia32.h" -#include "ia32/assembler-ia32-inl.h" -#elif V8_TARGET_ARCH_X64 -#include "x64/assembler-x64.h" -#include "x64/assembler-x64-inl.h" -#elif V8_TARGET_ARCH_ARM -#include "arm/assembler-arm.h" -#include "arm/assembler-arm-inl.h" -#elif V8_TARGET_ARCH_MIPS -#include "mips/assembler-mips.h" -#include "mips/assembler-mips-inl.h" -#else -#error Unsupported target architecture. -#endif - // This file provides base classes and auxiliary methods for defining // static object visitors used during GC. // Visiting HeapObject body with a normal ObjectVisitor requires performing @@ -67,6 +51,7 @@ class StaticVisitorBase : public AllStatic { kVisitSeqTwoByteString, kVisitShortcutCandidate, kVisitByteArray, + kVisitFreeSpace, kVisitFixedArray, kVisitFixedDoubleArray, kVisitGlobalContext, @@ -172,6 +157,10 @@ class VisitorDispatchTable { } } + inline Callback GetVisitorById(StaticVisitorBase::VisitorId id) { + return reinterpret_cast<Callback>(callbacks_[id]); + } + inline Callback GetVisitor(Map* map) { return reinterpret_cast<Callback>(callbacks_[map->visitor_id()]); } @@ -236,7 +225,7 @@ class FlexibleBodyVisitor : public BodyVisitorBase<StaticVisitor> { static inline ReturnType Visit(Map* map, HeapObject* object) { int object_size = BodyDescriptor::SizeOf(map, object); BodyVisitorBase<StaticVisitor>::IteratePointers( - map->heap(), + map->GetHeap(), object, BodyDescriptor::kStartOffset, object_size); @@ -247,7 +236,7 @@ class FlexibleBodyVisitor : public BodyVisitorBase<StaticVisitor> { static inline ReturnType VisitSpecialized(Map* map, HeapObject* object) { ASSERT(BodyDescriptor::SizeOf(map, object) == object_size); BodyVisitorBase<StaticVisitor>::IteratePointers( - map->heap(), + map->GetHeap(), object, BodyDescriptor::kStartOffset, object_size); @@ -261,7 +250,7 @@ class FixedBodyVisitor : public BodyVisitorBase<StaticVisitor> { public: static inline ReturnType Visit(Map* map, HeapObject* object) { BodyVisitorBase<StaticVisitor>::IteratePointers( - map->heap(), + map->GetHeap(), object, BodyDescriptor::kStartOffset, BodyDescriptor::kEndOffset); @@ -289,63 +278,7 @@ class FixedBodyVisitor : public BodyVisitorBase<StaticVisitor> { template<typename StaticVisitor> class StaticNewSpaceVisitor : public StaticVisitorBase { public: - static void Initialize() { - table_.Register(kVisitShortcutCandidate, - &FixedBodyVisitor<StaticVisitor, - ConsString::BodyDescriptor, - int>::Visit); - - table_.Register(kVisitConsString, - &FixedBodyVisitor<StaticVisitor, - ConsString::BodyDescriptor, - int>::Visit); - - table_.Register(kVisitSlicedString, - &FixedBodyVisitor<StaticVisitor, - SlicedString::BodyDescriptor, - int>::Visit); - - table_.Register(kVisitFixedArray, - &FlexibleBodyVisitor<StaticVisitor, - FixedArray::BodyDescriptor, - int>::Visit); - - table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray); - - table_.Register(kVisitGlobalContext, - &FixedBodyVisitor<StaticVisitor, - Context::ScavengeBodyDescriptor, - int>::Visit); - - table_.Register(kVisitByteArray, &VisitByteArray); - - table_.Register(kVisitSharedFunctionInfo, - &FixedBodyVisitor<StaticVisitor, - SharedFunctionInfo::BodyDescriptor, - int>::Visit); - - table_.Register(kVisitJSWeakMap, &VisitJSObject); - - table_.Register(kVisitJSRegExp, &VisitJSObject); - - table_.Register(kVisitSeqAsciiString, &VisitSeqAsciiString); - - table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString); - - table_.Register(kVisitJSFunction, - &JSObjectVisitor:: - template VisitSpecialized<JSFunction::kSize>); - - table_.RegisterSpecializations<DataObjectVisitor, - kVisitDataObject, - kVisitDataObjectGeneric>(); - table_.RegisterSpecializations<JSObjectVisitor, - kVisitJSObject, - kVisitJSObjectGeneric>(); - table_.RegisterSpecializations<StructVisitor, - kVisitStruct, - kVisitStructGeneric>(); - } + static void Initialize(); static inline int IterateBody(Map* map, HeapObject* obj) { return table_.GetVisitor(map)(map, obj); @@ -379,6 +312,10 @@ class StaticNewSpaceVisitor : public StaticVisitorBase { SeqTwoByteStringSize(map->instance_type()); } + static inline int VisitFreeSpace(Map* map, HeapObject* object) { + return FreeSpace::cast(object)->Size(); + } + class DataObjectVisitor { public: template<int object_size> @@ -410,55 +347,6 @@ VisitorDispatchTable<typename StaticNewSpaceVisitor<StaticVisitor>::Callback> StaticNewSpaceVisitor<StaticVisitor>::table_; -void Code::CodeIterateBody(ObjectVisitor* v) { - int mode_mask = RelocInfo::kCodeTargetMask | - RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | - RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | - RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | - RelocInfo::ModeMask(RelocInfo::JS_RETURN) | - RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | - RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); - - // Use the relocation info pointer before it is visited by - // the heap compaction in the next statement. - RelocIterator it(this, mode_mask); - - IteratePointer(v, kRelocationInfoOffset); - IteratePointer(v, kDeoptimizationDataOffset); - - for (; !it.done(); it.next()) { - it.rinfo()->Visit(v); - } -} - - -template<typename StaticVisitor> -void Code::CodeIterateBody(Heap* heap) { - int mode_mask = RelocInfo::kCodeTargetMask | - RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | - RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | - RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | - RelocInfo::ModeMask(RelocInfo::JS_RETURN) | - RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | - RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); - - // Use the relocation info pointer before it is visited by - // the heap compaction in the next statement. - RelocIterator it(this, mode_mask); - - StaticVisitor::VisitPointer( - heap, - reinterpret_cast<Object**>(this->address() + kRelocationInfoOffset)); - StaticVisitor::VisitPointer( - heap, - reinterpret_cast<Object**>(this->address() + kDeoptimizationDataOffset)); - - for (; !it.done(); it.next()) { - it.rinfo()->template Visit<StaticVisitor>(heap); - } -} - - } } // namespace v8::internal #endif // V8_OBJECTS_VISITING_H_ diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 88ebbf4e9d..284b631b13 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -39,7 +39,9 @@ #include "hydrogen.h" #include "objects-inl.h" #include "objects-visiting.h" +#include "objects-visiting-inl.h" #include "macro-assembler.h" +#include "mark-compact.h" #include "safepoint-table.h" #include "string-stream.h" #include "utils.h" @@ -53,10 +55,53 @@ namespace v8 { namespace internal { -// Getters and setters are stored in a fixed array property. These are -// constants for their indices. -const int kGetterIndex = 0; -const int kSetterIndex = 1; +void PrintElementsKind(FILE* out, ElementsKind kind) { + switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: + PrintF(out, "FAST_SMI_ONLY_ELEMENTS"); + break; + case FAST_ELEMENTS: + PrintF(out, "FAST_ELEMENTS"); + break; + case FAST_DOUBLE_ELEMENTS: + PrintF(out, "FAST_DOUBLE_ELEMENTS"); + break; + case DICTIONARY_ELEMENTS: + PrintF(out, "DICTIONARY_ELEMENTS"); + break; + case NON_STRICT_ARGUMENTS_ELEMENTS: + PrintF(out, "NON_STRICT_ARGUMENTS_ELEMENTS"); + break; + case EXTERNAL_BYTE_ELEMENTS: + PrintF(out, "EXTERNAL_BYTE_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_BYTE_ELEMENTS"); + break; + case EXTERNAL_SHORT_ELEMENTS: + PrintF(out, "EXTERNAL_SHORT_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_SHORT_ELEMENTS"); + break; + case EXTERNAL_INT_ELEMENTS: + PrintF(out, "EXTERNAL_INT_ELEMENTS"); + break; + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + PrintF(out, "EXTERNAL_UNSIGNED_INT_ELEMENTS"); + break; + case EXTERNAL_FLOAT_ELEMENTS: + PrintF(out, "EXTERNAL_FLOAT_ELEMENTS"); + break; + case EXTERNAL_DOUBLE_ELEMENTS: + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS"); + break; + case EXTERNAL_PIXEL_ELEMENTS: + PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS"); + break; + } +} + MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor, Object* value) { @@ -132,34 +177,27 @@ Object* Object::ToBoolean() { void Object::Lookup(String* name, LookupResult* result) { Object* holder = NULL; - if (IsSmi()) { - Context* global_context = Isolate::Current()->context()->global_context(); - holder = global_context->number_function()->instance_prototype(); + if (IsJSReceiver()) { + holder = this; } else { - HeapObject* heap_object = HeapObject::cast(this); - if (heap_object->IsJSObject()) { - return JSObject::cast(this)->Lookup(name, result); - } else if (heap_object->IsJSProxy()) { - return result->HandlerResult(); - } Context* global_context = Isolate::Current()->context()->global_context(); - if (heap_object->IsString()) { - holder = global_context->string_function()->instance_prototype(); - } else if (heap_object->IsHeapNumber()) { + if (IsNumber()) { holder = global_context->number_function()->instance_prototype(); - } else if (heap_object->IsBoolean()) { + } else if (IsString()) { + holder = global_context->string_function()->instance_prototype(); + } else if (IsBoolean()) { holder = global_context->boolean_function()->instance_prototype(); } } ASSERT(holder != NULL); // Cannot handle null or undefined. - JSObject::cast(holder)->Lookup(name, result); + JSReceiver::cast(holder)->Lookup(name, result); } MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, String* name, PropertyAttributes* attributes) { - LookupResult result; + LookupResult result(name->GetIsolate()); Lookup(name, &result); MaybeObject* value = GetProperty(receiver, &result, name, attributes); ASSERT(*attributes <= ABSENT); @@ -167,10 +205,9 @@ MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, } -MaybeObject* Object::GetPropertyWithCallback(Object* receiver, - Object* structure, - String* name, - Object* holder) { +MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, + Object* structure, + String* name) { Isolate* isolate = name->GetIsolate(); // To accommodate both the old and the new api we switch on the // data structure used to store the callbacks. Eventually foreign @@ -178,7 +215,7 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver, if (structure->IsForeign()) { AccessorDescriptor* callback = reinterpret_cast<AccessorDescriptor*>( - Foreign::cast(structure)->address()); + Foreign::cast(structure)->foreign_address()); MaybeObject* value = (callback->getter)(receiver, callback->data); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return value; @@ -191,10 +228,9 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver, v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); HandleScope scope(isolate); JSObject* self = JSObject::cast(receiver); - JSObject* holder_handle = JSObject::cast(holder); Handle<String> key(name); LOG(isolate, ApiNamedPropertyAccess("load", self, name)); - CustomArguments args(isolate, data->data(), self, holder_handle); + CustomArguments args(isolate, data->data(), self, this); v8::AccessorInfo info(args.end()); v8::Handle<v8::Value> result; { @@ -210,11 +246,11 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver, } // __defineGetter__ callback - if (structure->IsFixedArray()) { - Object* getter = FixedArray::cast(structure)->get(kGetterIndex); - if (getter->IsJSFunction()) { - return Object::GetPropertyWithDefinedGetter(receiver, - JSFunction::cast(getter)); + if (structure->IsAccessorPair()) { + Object* getter = AccessorPair::cast(structure)->getter(); + if (getter->IsSpecFunction()) { + // TODO(rossberg): nicer would be to cast to some JSCallable here... + return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); } // Getter is not a function. return isolate->heap()->undefined_value(); @@ -225,47 +261,72 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver, } -MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw, - String* name_raw, - Object* handler_raw) { - Isolate* isolate = name_raw->GetIsolate(); +MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, + String* name_raw) { + Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<Object> receiver(receiver_raw); Handle<Object> name(name_raw); - Handle<Object> handler(handler_raw); - // Extract trap function. - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get"); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<Object> args[] = { receiver, name }; + Handle<Object> result = CallTrap( + "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return Failure::Exception(); - if (trap->IsUndefined()) { - // Get the derived `get' property. - trap = isolate->derived_get_trap(); - } - - // Call trap function. - Object** args[] = { receiver.location(), name.location() }; - bool has_exception; - Handle<Object> result = - Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); - if (has_exception) return Failure::Exception(); return *result; } +Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) { + Isolate* isolate = object->IsHeapObject() + ? Handle<HeapObject>::cast(object)->GetIsolate() + : Isolate::Current(); + CALL_HEAP_FUNCTION(isolate, object->GetElement(index), Object); +} + + +MaybeObject* JSProxy::GetElementWithHandler(Object* receiver, + uint32_t index) { + String* name; + MaybeObject* maybe = GetHeap()->Uint32ToString(index); + if (!maybe->To<String>(&name)) return maybe; + return GetPropertyWithHandler(receiver, name); +} + + +MaybeObject* JSProxy::SetElementWithHandler(uint32_t index, + Object* value, + StrictModeFlag strict_mode) { + String* name; + MaybeObject* maybe = GetHeap()->Uint32ToString(index); + if (!maybe->To<String>(&name)) return maybe; + return SetPropertyWithHandler(name, value, NONE, strict_mode); +} + + +bool JSProxy::HasElementWithHandler(uint32_t index) { + String* name; + MaybeObject* maybe = GetHeap()->Uint32ToString(index); + if (!maybe->To<String>(&name)) return maybe; + return HasPropertyWithHandler(name); +} + + MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, - JSFunction* getter) { + JSReceiver* getter) { HandleScope scope; - Handle<JSFunction> fun(JSFunction::cast(getter)); + Handle<JSReceiver> fun(getter); Handle<Object> self(receiver); #ifdef ENABLE_DEBUGGER_SUPPORT Debug* debug = fun->GetHeap()->isolate()->debug(); // Handle stepping into a getter if step into is active. - if (debug->StepInActive()) { - debug->HandleStepIn(fun, Handle<Object>::null(), 0, false); + // TODO(rossberg): should this apply to getters that are function proxies? + if (debug->StepInActive() && fun->IsJSFunction()) { + debug->HandleStepIn( + Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); } #endif + bool has_pending_exception; Handle<Object> result = Execution::Call(fun, self, 0, NULL, &has_pending_exception); @@ -290,10 +351,8 @@ MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( AccessorInfo* info = AccessorInfo::cast(obj); if (info->all_can_read()) { *attributes = result->GetAttributes(); - return GetPropertyWithCallback(receiver, - result->GetCallbackObject(), - name, - result->holder()); + return result->holder()->GetPropertyWithCallback( + receiver, result->GetCallbackObject(), name); } } break; @@ -302,7 +361,7 @@ MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( case FIELD: case CONSTANT_FUNCTION: { // Search ALL_CAN_READ accessors in prototype chain. - LookupResult r; + LookupResult r(GetIsolate()); result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); if (r.IsProperty()) { return GetPropertyWithFailedAccessCheck(receiver, @@ -315,7 +374,7 @@ MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( case INTERCEPTOR: { // If the object has an interceptor, try real named properties. // No access check in GetPropertyAttributeWithInterceptor. - LookupResult r; + LookupResult r(GetIsolate()); result->holder()->LookupRealNamedProperty(name, &r); if (r.IsProperty()) { return GetPropertyWithFailedAccessCheck(receiver, @@ -362,7 +421,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( case CONSTANT_FUNCTION: { if (!continue_search) break; // Search ALL_CAN_READ accessors in prototype chain. - LookupResult r; + LookupResult r(GetIsolate()); result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); if (r.IsProperty()) { return GetPropertyAttributeWithFailedAccessCheck(receiver, @@ -376,7 +435,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( case INTERCEPTOR: { // If the object has an interceptor, try real named properties. // No access check in GetPropertyAttributeWithInterceptor. - LookupResult r; + LookupResult r(GetIsolate()); if (continue_search) { result->holder()->LookupRealNamedProperty(name, &r); } else { @@ -396,7 +455,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( } } - GetHeap()->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); + GetIsolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); return ABSENT; } @@ -426,6 +485,16 @@ Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) { } +Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyDetails details) { + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->SetNormalizedProperty(*key, *value, details), + Object); +} + + MaybeObject* JSObject::SetNormalizedProperty(String* name, Object* value, PropertyDetails details) { @@ -486,7 +555,7 @@ MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) { } JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(dictionary->ValueAt(entry)); - cell->set_value(cell->heap()->the_hole_value()); + cell->set_value(cell->GetHeap()->the_hole_value()); dictionary->DetailsAtPut(entry, details.AsDeleted()); } else { Object* deleted = dictionary->DeleteProperty(entry, mode); @@ -520,6 +589,21 @@ bool JSObject::IsDirty() { } +Handle<Object> Object::GetProperty(Handle<Object> object, + Handle<Object> receiver, + LookupResult* result, + Handle<String> key, + PropertyAttributes* attributes) { + Isolate* isolate = object->IsHeapObject() + ? Handle<HeapObject>::cast(object)->GetIsolate() + : Isolate::Current(); + CALL_HEAP_FUNCTION( + isolate, + object->GetProperty(*receiver, result, *key, attributes), + Object); +} + + MaybeObject* Object::GetProperty(Object* receiver, LookupResult* result, String* name, @@ -537,7 +621,9 @@ MaybeObject* Object::GetProperty(Object* receiver, // holder in the prototype chain. // Proxy handlers do not use the proxy's prototype, so we can skip this. if (!result->IsHandler()) { - Object* last = result->IsProperty() ? result->holder() : heap->null_value(); + Object* last = result->IsProperty() + ? result->holder() + : Object::cast(heap->null_value()); ASSERT(this != this->GetPrototype()); for (Object* current = this; true; current = current->GetPrototype()) { if (current->IsAccessCheckNeeded()) { @@ -566,30 +652,26 @@ MaybeObject* Object::GetProperty(Object* receiver, } *attributes = result->GetAttributes(); Object* value; - JSObject* holder = result->holder(); switch (result->type()) { case NORMAL: - value = holder->GetNormalizedProperty(result); + value = result->holder()->GetNormalizedProperty(result); ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case FIELD: - value = holder->FastPropertyAt(result->GetFieldIndex()); + value = result->holder()->FastPropertyAt(result->GetFieldIndex()); ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case CONSTANT_FUNCTION: return result->GetConstantFunction(); case CALLBACKS: - return GetPropertyWithCallback(receiver, - result->GetCallbackObject(), - name, - holder); - case HANDLER: { - JSProxy* proxy = JSProxy::cast(this); - return GetPropertyWithHandler(receiver, name, proxy->handler()); - } + return result->holder()->GetPropertyWithCallback( + receiver, result->GetCallbackObject(), name); + case HANDLER: + return result->proxy()->GetPropertyWithHandler(receiver, name); case INTERCEPTOR: { JSObject* recvr = JSObject::cast(receiver); - return holder->GetPropertyWithInterceptor(recvr, name, attributes); + return result->holder()->GetPropertyWithInterceptor( + recvr, name, attributes); } case MAP_TRANSITION: case ELEMENTS_TRANSITION: @@ -613,28 +695,21 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { for (holder = this; holder != heap->null_value(); holder = holder->GetPrototype()) { - if (holder->IsSmi()) { - Context* global_context = Isolate::Current()->context()->global_context(); - holder = global_context->number_function()->instance_prototype(); - } else { - HeapObject* heap_object = HeapObject::cast(holder); - if (!heap_object->IsJSObject()) { - Isolate* isolate = heap->isolate(); - Context* global_context = isolate->context()->global_context(); - if (heap_object->IsString()) { - holder = global_context->string_function()->instance_prototype(); - } else if (heap_object->IsHeapNumber()) { - holder = global_context->number_function()->instance_prototype(); - } else if (heap_object->IsBoolean()) { - holder = global_context->boolean_function()->instance_prototype(); - } else if (heap_object->IsJSProxy()) { - // TODO(rossberg): do something - return heap->undefined_value(); // For now... - } else { - // Undefined and null have no indexed properties. - ASSERT(heap_object->IsUndefined() || heap_object->IsNull()); - return heap->undefined_value(); - } + if (!holder->IsJSObject()) { + Isolate* isolate = heap->isolate(); + Context* global_context = isolate->context()->global_context(); + if (holder->IsNumber()) { + holder = global_context->number_function()->instance_prototype(); + } else if (holder->IsString()) { + holder = global_context->string_function()->instance_prototype(); + } else if (holder->IsBoolean()) { + holder = global_context->boolean_function()->instance_prototype(); + } else if (holder->IsJSProxy()) { + return JSProxy::cast(holder)->GetElementWithHandler(receiver, index); + } else { + // Undefined and null have no indexed properties. + ASSERT(holder->IsUndefined() || holder->IsNull()); + return heap->undefined_value(); } } @@ -701,6 +776,49 @@ Object* Object::GetPrototype() { } +MaybeObject* Object::GetHash(CreationFlag flag) { + // The object is either a number, a string, an odd-ball, + // a real JS object, or a Harmony proxy. + if (IsNumber()) { + uint32_t hash = ComputeLongHash(double_to_uint64(Number())); + return Smi::FromInt(hash & Smi::kMaxValue); + } + if (IsString()) { + uint32_t hash = String::cast(this)->Hash(); + return Smi::FromInt(hash); + } + if (IsOddball()) { + uint32_t hash = Oddball::cast(this)->to_string()->Hash(); + return Smi::FromInt(hash); + } + if (IsJSReceiver()) { + return JSReceiver::cast(this)->GetIdentityHash(flag); + } + + UNREACHABLE(); + return Smi::FromInt(0); +} + + +bool Object::SameValue(Object* other) { + if (other == this) return true; + if (!IsHeapObject() || !other->IsHeapObject()) return false; + + // The object is either a number, a string, an odd-ball, + // a real JS object, or a Harmony proxy. + if (IsNumber() && other->IsNumber()) { + double this_value = Number(); + double other_value = other->Number(); + return (this_value == other_value) || + (isnan(this_value) && isnan(other_value)); + } + if (IsString() && other->IsString()) { + return String::cast(this)->Equals(String::cast(other)); + } + return false; +} + + void Object::ShortPrint(FILE* out) { HeapStringAllocator allocator; StringStream accumulator(&allocator); @@ -818,7 +936,7 @@ MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { len - first_length); } cs->set_first(result); - cs->set_second(heap->empty_string()); + cs->set_second(heap->empty_string(), SKIP_WRITE_BARRIER); return result; } default: @@ -844,39 +962,40 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { #endif // DEBUG Heap* heap = GetHeap(); int size = this->Size(); // Byte size of the original string. - if (size < ExternalString::kSize) { - // The string is too small to fit an external String in its place. This can - // only happen for zero length strings. + if (size < ExternalString::kShortSize) { return false; } - ASSERT(size >= ExternalString::kSize); bool is_ascii = this->IsAsciiRepresentation(); bool is_symbol = this->IsSymbol(); - int length = this->length(); - int hash_field = this->hash_field(); // Morph the object to an external string by adjusting the map and // reinitializing the fields. - this->set_map(is_ascii ? - heap->external_string_with_ascii_data_map() : - heap->external_string_map()); + if (size >= ExternalString::kSize) { + this->set_map_no_write_barrier( + is_symbol + ? (is_ascii ? heap->external_symbol_with_ascii_data_map() + : heap->external_symbol_map()) + : (is_ascii ? heap->external_string_with_ascii_data_map() + : heap->external_string_map())); + } else { + this->set_map_no_write_barrier( + is_symbol + ? (is_ascii ? heap->short_external_symbol_with_ascii_data_map() + : heap->short_external_symbol_map()) + : (is_ascii ? heap->short_external_string_with_ascii_data_map() + : heap->short_external_string_map())); + } ExternalTwoByteString* self = ExternalTwoByteString::cast(this); - self->set_length(length); - self->set_hash_field(hash_field); self->set_resource(resource); - // Additionally make the object into an external symbol if the original string - // was a symbol to start with. - if (is_symbol) { - self->Hash(); // Force regeneration of the hash value. - // Now morph this external string into a external symbol. - this->set_map(is_ascii ? - heap->external_symbol_with_ascii_data_map() : - heap->external_symbol_map()); - } + if (is_symbol) self->Hash(); // Force regeneration of the hash value. // Fill the remainder of the string with dead wood. int new_size = this->Size(); // Byte size of the external String object. heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); + if (Marking::IsBlack(Marking::MarkBitFrom(this))) { + MemoryChunk::IncrementLiveBytesFromMutator(this->address(), + new_size - size); + } return true; } @@ -895,34 +1014,33 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { #endif // DEBUG Heap* heap = GetHeap(); int size = this->Size(); // Byte size of the original string. - if (size < ExternalString::kSize) { - // The string is too small to fit an external String in its place. This can - // only happen for zero length strings. + if (size < ExternalString::kShortSize) { return false; } - ASSERT(size >= ExternalString::kSize); bool is_symbol = this->IsSymbol(); - int length = this->length(); - int hash_field = this->hash_field(); // Morph the object to an external string by adjusting the map and - // reinitializing the fields. - this->set_map(heap->external_ascii_string_map()); + // reinitializing the fields. Use short version if space is limited. + if (size >= ExternalString::kSize) { + this->set_map_no_write_barrier( + is_symbol ? heap->external_ascii_symbol_map() + : heap->external_ascii_string_map()); + } else { + this->set_map_no_write_barrier( + is_symbol ? heap->short_external_ascii_symbol_map() + : heap->short_external_ascii_string_map()); + } ExternalAsciiString* self = ExternalAsciiString::cast(this); - self->set_length(length); - self->set_hash_field(hash_field); self->set_resource(resource); - // Additionally make the object into an external symbol if the original string - // was a symbol to start with. - if (is_symbol) { - self->Hash(); // Force regeneration of the hash value. - // Now morph this external string into a external symbol. - this->set_map(heap->external_ascii_symbol_map()); - } + if (is_symbol) self->Hash(); // Force regeneration of the hash value. // Fill the remainder of the string with dead wood. int new_size = this->Size(); // Byte size of the external String object. heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); + if (Marking::IsBlack(Marking::MarkBitFrom(this))) { + MemoryChunk::IncrementLiveBytesFromMutator(this->address(), + new_size - size); + } return true; } @@ -998,8 +1116,7 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { break; } case JS_WEAK_MAP_TYPE: { - int elements = JSWeakMap::cast(this)->table()->NumberOfElements(); - accumulator->Add("<JS WeakMap[%d]>", elements); + accumulator->Add("<JS WeakMap>"); break; } case JS_REGEXP_TYPE: { @@ -1027,7 +1144,7 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). default: { Map* map_of_this = map(); - Heap* heap = map_of_this->heap(); + Heap* heap = GetHeap(); Object* constructor = map_of_this->constructor(); bool printed = false; if (constructor->IsHeapObject() && @@ -1049,7 +1166,6 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { global_object ? "Global Object: " : "", vowel ? "n" : ""); accumulator->Put(str); - accumulator->Put('>'); printed = true; } } @@ -1070,8 +1186,28 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { } +void JSObject::PrintElementsTransition( + FILE* file, ElementsKind from_kind, FixedArrayBase* from_elements, + ElementsKind to_kind, FixedArrayBase* to_elements) { + if (from_kind != to_kind) { + PrintF(file, "elements transition ["); + PrintElementsKind(file, from_kind); + PrintF(file, " -> "); + PrintElementsKind(file, to_kind); + PrintF(file, "] in "); + JavaScriptFrame::PrintTop(file, false, true); + PrintF(file, " for "); + ShortPrint(file); + PrintF(file, " from "); + from_elements->ShortPrint(file); + PrintF(file, " to "); + to_elements->ShortPrint(file); + PrintF(file, "\n"); + } +} + + void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { - // if (!HEAP->InNewSpace(this)) PrintF("*", this); Heap* heap = GetHeap(); if (!heap->Contains(this)) { accumulator->Add("!!!INVALID POINTER!!!"); @@ -1094,14 +1230,21 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { } switch (map()->instance_type()) { case MAP_TYPE: - accumulator->Add("<Map>"); + accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind()); break; case FIXED_ARRAY_TYPE: accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length()); break; + case FIXED_DOUBLE_ARRAY_TYPE: + accumulator->Add("<FixedDoubleArray[%u]>", + FixedDoubleArray::cast(this)->length()); + break; case BYTE_ARRAY_TYPE: accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length()); break; + case FREE_SPACE_TYPE: + accumulator->Add("<FreeSpace[%u]>", FreeSpace::cast(this)->Size()); + break; case EXTERNAL_PIXEL_ARRAY_TYPE: accumulator->Add("<ExternalPixelArray[%u]>", ExternalPixelArray::cast(this)->length()); @@ -1241,6 +1384,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_VALUE_TYPE: case JS_ARRAY_TYPE: + case JS_SET_TYPE: + case JS_MAP_TYPE: case JS_WEAK_MAP_TYPE: case JS_REGEXP_TYPE: case JS_GLOBAL_PROXY_TYPE: @@ -1277,6 +1422,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case HEAP_NUMBER_TYPE: case FILLER_TYPE: case BYTE_ARRAY_TYPE: + case FREE_SPACE_TYPE: case EXTERNAL_PIXEL_ARRAY_TYPE: case EXTERNAL_BYTE_ARRAY_TYPE: case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: @@ -1508,8 +1654,6 @@ MaybeObject* JSObject::AddConstantFunctionProperty( String* name, JSFunction* function, PropertyAttributes attributes) { - ASSERT(!GetHeap()->InNewSpace(function)); - // Allocate new instance descriptors with (name, function) added ConstantFunctionDescriptor d(name, function, attributes); Object* new_descriptors; @@ -1533,7 +1677,7 @@ MaybeObject* JSObject::AddConstantFunctionProperty( // If the old map is the global object map (from new Object()), // then transitions are not added to it, so we are done. - Heap* heap = old_map->heap(); + Heap* heap = GetHeap(); if (old_map == heap->isolate()->context()->global_context()-> object_function()->map()) { return function; @@ -1609,7 +1753,7 @@ MaybeObject* JSObject::AddProperty(String* name, StrictModeFlag strict_mode) { ASSERT(!IsJSGlobalProxy()); Map* map_of_this = map(); - Heap* heap = map_of_this->heap(); + Heap* heap = GetHeap(); if (!map_of_this->is_extensible()) { if (strict_mode == kNonStrictMode) { return heap->undefined_value(); @@ -1624,7 +1768,7 @@ MaybeObject* JSObject::AddProperty(String* name, // Ensure the descriptor array does not get too big. if (map_of_this->instance_descriptors()->number_of_descriptors() < DescriptorArray::kMaxNumberOfDescriptors) { - if (value->IsJSFunction() && !heap->InNewSpace(value)) { + if (value->IsJSFunction()) { return AddConstantFunctionProperty(name, JSFunction::cast(value), attributes); @@ -1651,13 +1795,21 @@ MaybeObject* JSObject::SetPropertyPostInterceptor( PropertyAttributes attributes, StrictModeFlag strict_mode) { // Check local property, ignore interceptor. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookupRealNamedProperty(name, &result); if (result.IsFound()) { // An existing property, a map transition or a null descriptor was // found. Use set property to handle all these cases. return SetProperty(&result, name, value, attributes, strict_mode); } + bool found = false; + MaybeObject* result_object; + result_object = SetPropertyWithCallbackSetterInPrototypes(name, + value, + attributes, + &found, + strict_mode); + if (found) return result_object; // Add a new real property. return AddProperty(name, value, attributes, strict_mode); } @@ -1671,7 +1823,7 @@ MaybeObject* JSObject::ReplaceSlowProperty(String* name, int new_enumeration_index = 0; // 0 means "Use the next available index." if (old_index != -1) { // All calls to ReplaceSlowProperty have had all transitions removed. - ASSERT(!dictionary->DetailsAt(old_index).IsTransition()); + ASSERT(!dictionary->ContainsTransition(old_index)); new_enumeration_index = dictionary->DetailsAt(old_index).index(); } @@ -1696,7 +1848,7 @@ MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition( return result; } // Do not add transitions to the map of "new Object()". - if (map() == old_map->heap()->isolate()->context()->global_context()-> + if (map() == GetIsolate()->context()->global_context()-> object_function()->map()) { return result; } @@ -1821,11 +1973,22 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( } +Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes, + StrictModeFlag strict_mode) { + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->SetProperty(*key, *value, attributes, strict_mode), + Object); +} + + MaybeObject* JSReceiver::SetProperty(String* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode) { - LookupResult result; + LookupResult result(GetIsolate()); LocalLookup(name, &result); return SetProperty(&result, name, value, attributes, strict_mode); } @@ -1850,7 +2013,7 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, if (structure->IsForeign()) { AccessorDescriptor* callback = reinterpret_cast<AccessorDescriptor*>( - Foreign::cast(structure)->address()); + Foreign::cast(structure)->foreign_address()); MaybeObject* obj = (callback->setter)(this, value, callback->data); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (obj->IsFailure()) return obj; @@ -1878,10 +2041,11 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, return *value_handle; } - if (structure->IsFixedArray()) { - Object* setter = FixedArray::cast(structure)->get(kSetterIndex); - if (setter->IsJSFunction()) { - return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value); + if (structure->IsAccessorPair()) { + Object* setter = AccessorPair::cast(structure)->setter(); + if (setter->IsSpecFunction()) { + // TODO(rossberg): nicer would be to cast to some JSCallable here... + return SetPropertyWithDefinedSetter(JSReceiver::cast(setter), value); } else { if (strict_mode == kNonStrictMode) { return value; @@ -1900,22 +2064,24 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, } -MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter, - Object* value) { +MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSReceiver* setter, + Object* value) { Isolate* isolate = GetIsolate(); Handle<Object> value_handle(value, isolate); - Handle<JSFunction> fun(JSFunction::cast(setter), isolate); - Handle<JSObject> self(this, isolate); + Handle<JSReceiver> fun(setter, isolate); + Handle<JSReceiver> self(this, isolate); #ifdef ENABLE_DEBUGGER_SUPPORT Debug* debug = isolate->debug(); // Handle stepping into a setter if step into is active. - if (debug->StepInActive()) { - debug->HandleStepIn(fun, Handle<Object>::null(), 0, false); + // TODO(rossberg): should this apply to getters that are function proxies? + if (debug->StepInActive() && fun->IsJSFunction()) { + debug->HandleStepIn( + Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); } #endif bool has_pending_exception; - Object** argv[] = { value_handle.location() }; - Execution::Call(fun, self, 1, argv, &has_pending_exception); + Handle<Object> argv[] = { value_handle }; + Execution::Call(fun, self, ARRAY_SIZE(argv), argv, &has_pending_exception); // Check for pending exception and return the result. if (has_pending_exception) return Failure::Exception(); return *value_handle; @@ -1928,6 +2094,9 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name, for (Object* pt = GetPrototype(); pt != heap->null_value(); pt = pt->GetPrototype()) { + if (pt->IsJSProxy()) { + return result->HandlerResult(JSProxy::cast(pt)); + } JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); if (result->IsProperty()) { if (result->type() == CALLBACKS && !result->IsReadOnly()) return; @@ -1948,6 +2117,16 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( for (Object* pt = GetPrototype(); pt != heap->null_value(); pt = pt->GetPrototype()) { + if (pt->IsJSProxy()) { + String* name; + MaybeObject* maybe = GetHeap()->Uint32ToString(index); + if (!maybe->To<String>(&name)) { + *found = true; // Force abort + return maybe; + } + return JSProxy::cast(pt)->SetPropertyWithHandlerIfDefiningSetter( + name, value, NONE, strict_mode, found); + } if (!JSObject::cast(pt)->HasDictionaryElements()) { continue; } @@ -1970,6 +2149,48 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( return heap->the_hole_value(); } +MaybeObject* JSObject::SetPropertyWithCallbackSetterInPrototypes( + String* name, + Object* value, + PropertyAttributes attributes, + bool* found, + StrictModeFlag strict_mode) { + Heap* heap = GetHeap(); + // We could not find a local property so let's check whether there is an + // accessor that wants to handle the property. + LookupResult accessor_result(heap->isolate()); + LookupCallbackSetterInPrototypes(name, &accessor_result); + if (accessor_result.IsFound()) { + *found = true; + if (accessor_result.type() == CALLBACKS) { + return SetPropertyWithCallback(accessor_result.GetCallbackObject(), + name, + value, + accessor_result.holder(), + strict_mode); + } else if (accessor_result.type() == HANDLER) { + // There is a proxy in the prototype chain. Invoke its + // getPropertyDescriptor trap. + bool found = false; + // SetPropertyWithHandlerIfDefiningSetter can cause GC, + // make sure to use the handlified references after calling + // the function. + Handle<JSObject> self(this); + Handle<String> hname(name); + Handle<Object> hvalue(value); + MaybeObject* result = + accessor_result.proxy()->SetPropertyWithHandlerIfDefiningSetter( + name, value, attributes, strict_mode, &found); + if (found) return result; + // The proxy does not define the property as an accessor. + // Consequently, it has no effect on setting the receiver. + return self->AddProperty(*hname, *hvalue, attributes, strict_mode); + } + } + *found = false; + return heap->the_hole_value(); +} + void JSObject::LookupInDescriptor(String* name, LookupResult* result) { DescriptorArray* descriptors = map()->instance_descriptors(); @@ -1986,7 +2207,8 @@ void Map::LookupInDescriptors(JSObject* holder, String* name, LookupResult* result) { DescriptorArray* descriptors = instance_descriptors(); - DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache(); + DescriptorLookupCache* cache = + GetHeap()->isolate()->descriptor_lookup_cache(); int number = cache->Lookup(descriptors, name); if (number == DescriptorLookupCache::kAbsent) { number = descriptors->Search(name); @@ -2000,75 +2222,295 @@ void Map::LookupInDescriptors(JSObject* holder, } -MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind, - bool safe_to_add_transition) { - Heap* current_heap = heap(); +static bool ContainsMap(MapHandleList* maps, Handle<Map> map) { + ASSERT(!map.is_null()); + for (int i = 0; i < maps->length(); ++i) { + if (!maps->at(i).is_null() && maps->at(i).is_identical_to(map)) return true; + } + return false; +} + + +template <class T> +static Handle<T> MaybeNull(T* p) { + if (p == NULL) return Handle<T>::null(); + return Handle<T>(p); +} + + +Handle<Map> Map::FindTransitionedMap(MapHandleList* candidates) { + ElementsKind elms_kind = elements_kind(); + if (elms_kind == FAST_DOUBLE_ELEMENTS) { + bool dummy = true; + Handle<Map> fast_map = + MaybeNull(LookupElementsTransitionMap(FAST_ELEMENTS, &dummy)); + if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { + return fast_map; + } + return Handle<Map>::null(); + } + if (elms_kind == FAST_SMI_ONLY_ELEMENTS) { + bool dummy = true; + Handle<Map> double_map = + MaybeNull(LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, &dummy)); + // In the current implementation, if the DOUBLE map doesn't exist, the + // FAST map can't exist either. + if (double_map.is_null()) return Handle<Map>::null(); + Handle<Map> fast_map = + MaybeNull(double_map->LookupElementsTransitionMap(FAST_ELEMENTS, + &dummy)); + if (!fast_map.is_null() && ContainsMap(candidates, fast_map)) { + return fast_map; + } + if (ContainsMap(candidates, double_map)) return double_map; + } + return Handle<Map>::null(); +} + +static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, + ElementsKind elements_kind) { + if (descriptor_contents->IsMap()) { + Map* map = Map::cast(descriptor_contents); + if (map->elements_kind() == elements_kind) { + return map; + } + return NULL; + } + + FixedArray* map_array = FixedArray::cast(descriptor_contents); + for (int i = 0; i < map_array->length(); ++i) { + Object* current = map_array->get(i); + // Skip undefined slots, they are sentinels for reclaimed maps. + if (!current->IsUndefined()) { + Map* current_map = Map::cast(map_array->get(i)); + if (current_map->elements_kind() == elements_kind) { + return current_map; + } + } + } + + return NULL; +} + + +static MaybeObject* AddElementsTransitionMapToDescriptor( + Object* descriptor_contents, + Map* new_map) { + // Nothing was in the descriptor for an ELEMENTS_TRANSITION, + // simply add the map. + if (descriptor_contents == NULL) { + return new_map; + } + + // There was already a map in the descriptor, create a 2-element FixedArray + // to contain the existing map plus the new one. + FixedArray* new_array; + Heap* heap = new_map->GetHeap(); + if (descriptor_contents->IsMap()) { + // Must tenure, DescriptorArray expects no new-space objects. + MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED); + if (!maybe_new_array->To<FixedArray>(&new_array)) { + return maybe_new_array; + } + new_array->set(0, descriptor_contents); + new_array->set(1, new_map); + return new_array; + } + + // The descriptor already contained a list of maps for different ElementKinds + // of ELEMENTS_TRANSITION, first check the existing array for an undefined + // slot, and if that's not available, create a FixedArray to hold the existing + // maps plus the new one and fill it in. + FixedArray* array = FixedArray::cast(descriptor_contents); + for (int i = 0; i < array->length(); ++i) { + if (array->get(i)->IsUndefined()) { + array->set(i, new_map); + return array; + } + } + + // Must tenure, DescriptorArray expects no new-space objects. + MaybeObject* maybe_new_array = + heap->AllocateFixedArray(array->length() + 1, TENURED); + if (!maybe_new_array->To<FixedArray>(&new_array)) { + return maybe_new_array; + } + int i = 0; + while (i < array->length()) { + new_array->set(i, array->get(i)); + ++i; + } + new_array->set(i, new_map); + return new_array; +} + + +String* Map::elements_transition_sentinel_name() { + return GetHeap()->empty_symbol(); +} + + +Object* Map::GetDescriptorContents(String* sentinel_name, + bool* safe_to_add_transition) { + // Get the cached index for the descriptors lookup, or find and cache it. DescriptorArray* descriptors = instance_descriptors(); - String* elements_transition_sentinel_name = current_heap->empty_symbol(); + DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache(); + int index = cache->Lookup(descriptors, sentinel_name); + if (index == DescriptorLookupCache::kAbsent) { + index = descriptors->Search(sentinel_name); + cache->Update(descriptors, sentinel_name, index); + } + // If the transition already exists, return its descriptor. + if (index != DescriptorArray::kNotFound) { + PropertyDetails details(descriptors->GetDetails(index)); + if (details.type() == ELEMENTS_TRANSITION) { + return descriptors->GetValue(index); + } else { + if (safe_to_add_transition != NULL) { + *safe_to_add_transition = false; + } + } + } + return NULL; +} + + +Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind, + bool* safe_to_add_transition) { + // Special case: indirect SMI->FAST transition (cf. comment in + // AddElementsTransition()). + if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + elements_kind == FAST_ELEMENTS) { + Map* double_map = this->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, + safe_to_add_transition); + if (double_map == NULL) return double_map; + return double_map->LookupElementsTransitionMap(FAST_ELEMENTS, + safe_to_add_transition); + } + Object* descriptor_contents = GetDescriptorContents( + elements_transition_sentinel_name(), safe_to_add_transition); + if (descriptor_contents != NULL) { + Map* maybe_transition_map = + GetElementsTransitionMapFromDescriptor(descriptor_contents, + elements_kind); + ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap()); + return maybe_transition_map; + } + return NULL; +} + + +MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind, + Map* transitioned_map) { + // The map transition graph should be a tree, therefore the transition + // from SMI to FAST elements is not done directly, but by going through + // DOUBLE elements first. + if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + elements_kind == FAST_ELEMENTS) { + bool safe_to_add = true; + Map* double_map = this->LookupElementsTransitionMap( + FAST_DOUBLE_ELEMENTS, &safe_to_add); + // This method is only called when safe_to_add_transition has been found + // to be true earlier. + ASSERT(safe_to_add); + + if (double_map == NULL) { + MaybeObject* maybe_map = this->CopyDropTransitions(); + if (!maybe_map->To(&double_map)) return maybe_map; + double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); + MaybeObject* maybe_double_transition = this->AddElementsTransition( + FAST_DOUBLE_ELEMENTS, double_map); + if (maybe_double_transition->IsFailure()) return maybe_double_transition; + } + return double_map->AddElementsTransition(FAST_ELEMENTS, transitioned_map); + } + + bool safe_to_add_transition = true; + Object* descriptor_contents = GetDescriptorContents( + elements_transition_sentinel_name(), &safe_to_add_transition); + // This method is only called when safe_to_add_transition has been found + // to be true earlier. + ASSERT(safe_to_add_transition); + MaybeObject* maybe_new_contents = + AddElementsTransitionMapToDescriptor(descriptor_contents, + transitioned_map); + Object* new_contents; + if (!maybe_new_contents->ToObject(&new_contents)) { + return maybe_new_contents; + } + + ElementsTransitionDescriptor desc(elements_transition_sentinel_name(), + new_contents); + Object* new_descriptors; + MaybeObject* maybe_new_descriptors = + instance_descriptors()->CopyInsert(&desc, KEEP_TRANSITIONS); + if (!maybe_new_descriptors->ToObject(&new_descriptors)) { + return maybe_new_descriptors; + } + set_instance_descriptors(DescriptorArray::cast(new_descriptors)); + return this; +} + + +Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, + ElementsKind to_kind) { + Isolate* isolate = object->GetIsolate(); + CALL_HEAP_FUNCTION(isolate, + object->GetElementsTransitionMap(isolate, to_kind), + Map); +} + + +MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { + Map* current_map = map(); + ElementsKind from_kind = current_map->elements_kind(); + + if (from_kind == to_kind) return current_map; + + // Only objects with FastProperties can have DescriptorArrays and can track + // element-related maps. Also don't add descriptors to maps that are shared. + bool safe_to_add_transition = HasFastProperties() && + !current_map->IsUndefined() && + !current_map->is_shared(); + + // Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps caused by objects + // with elements that switch back and forth between dictionary and fast + // element mode. + if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) { + safe_to_add_transition = false; + } if (safe_to_add_transition) { // It's only safe to manipulate the descriptor array if it would be // safe to add a transition. - - ASSERT(!is_shared()); // no transitions can be added to shared maps. - // Check if the elements transition already exists. - DescriptorLookupCache* cache = - current_heap->isolate()->descriptor_lookup_cache(); - int index = cache->Lookup(descriptors, elements_transition_sentinel_name); - if (index == DescriptorLookupCache::kAbsent) { - index = descriptors->Search(elements_transition_sentinel_name); - cache->Update(descriptors, - elements_transition_sentinel_name, - index); - } - - // If the transition already exists, check the type. If there is a match, - // return it. - if (index != DescriptorArray::kNotFound) { - PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); - if (details.type() == ELEMENTS_TRANSITION && - details.elements_kind() == elements_kind) { - return descriptors->GetValue(index); - } else { - safe_to_add_transition = false; - } + Map* maybe_transition_map = current_map->LookupElementsTransitionMap( + to_kind, &safe_to_add_transition); + if (maybe_transition_map != NULL) { + return maybe_transition_map; } } + Map* new_map = NULL; + // No transition to an existing map for the given ElementsKind. Make a new // one. - Object* obj; - { MaybeObject* maybe_map = CopyDropTransitions(); - if (!maybe_map->ToObject(&obj)) return maybe_map; + { MaybeObject* maybe_map = current_map->CopyDropTransitions(); + if (!maybe_map->To(&new_map)) return maybe_map; } - Map* new_map = Map::cast(obj); - new_map->set_elements_kind(elements_kind); - GetIsolate()->counters()->map_to_external_array_elements()->Increment(); + new_map->set_elements_kind(to_kind); // Only remember the map transition if the object's map is NOT equal to the // global object_function's map and there is not an already existing // non-matching element transition. - bool allow_map_transition = - safe_to_add_transition && - (GetIsolate()->context()->global_context()->object_function()->map() != - map()); + Context* global_context = GetIsolate()->context()->global_context(); + bool allow_map_transition = safe_to_add_transition && + (global_context->object_function()->map() != map()); if (allow_map_transition) { - // Allocate new instance descriptors for the old map with map transition. - ElementsTransitionDescriptor desc(elements_transition_sentinel_name, - Map::cast(new_map), - elements_kind); - Object* new_descriptors; - MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( - &desc, - KEEP_TRANSITIONS); - if (!maybe_new_descriptors->ToObject(&new_descriptors)) { - return maybe_new_descriptors; - } - descriptors = DescriptorArray::cast(new_descriptors); - set_instance_descriptors(descriptors); + MaybeObject* maybe_transition = + current_map->AddElementsTransition(to_kind, new_map); + if (maybe_transition->IsFailure()) return maybe_transition; } - return new_map; } @@ -2079,6 +2521,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name, Object* proto = GetPrototype(); if (proto->IsNull()) return result->NotFound(); ASSERT(proto->IsJSGlobalObject()); + // A GlobalProxy's prototype should always be a proper JSObject. return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); } @@ -2173,7 +2616,7 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( case INTERCEPTOR: { // Try lookup real named properties. Note that only property can be // set is callbacks marked as ALL_CAN_WRITE on the prototype chain. - LookupResult r; + LookupResult r(GetIsolate()); LookupRealNamedProperty(name, &r); if (r.IsProperty()) { return SetPropertyWithFailedAccessCheck(&r, @@ -2191,10 +2634,10 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( } } - Heap* heap = GetHeap(); - HandleScope scope(heap->isolate()); + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); Handle<Object> value_handle(value); - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET); + isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); return *value_handle; } @@ -2205,7 +2648,7 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result, PropertyAttributes attributes, StrictModeFlag strict_mode) { if (result->IsFound() && result->type() == HANDLER) { - return JSProxy::cast(this)->SetPropertyWithHandler( + return result->proxy()->SetPropertyWithHandler( key, value, attributes, strict_mode); } else { return JSObject::cast(this)->SetPropertyForResult( @@ -2219,22 +2662,11 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) { HandleScope scope(isolate); Handle<Object> receiver(this); Handle<Object> name(name_raw); - Handle<Object> handler(this->handler()); - // Extract trap function. - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has"); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<Object> args[] = { name }; + Handle<Object> result = CallTrap( + "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return Failure::Exception(); - if (trap->IsUndefined()) { - trap = isolate->derived_has_trap(); - } - - // Call trap function. - Object** args[] = { name.location() }; - bool has_exception; - Handle<Object> result = - Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); - if (has_exception) return Failure::Exception(); return result->ToBoolean()->IsTrue(); } @@ -2250,24 +2682,85 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( Handle<Object> receiver(this); Handle<Object> name(name_raw); Handle<Object> value(value_raw); - Handle<Object> handler(this->handler()); - // Extract trap function. - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set"); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<Object> args[] = { receiver, name, value }; + CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return Failure::Exception(); - if (trap->IsUndefined()) { - trap = isolate->derived_set_trap(); - } - // Call trap function. - Object** args[] = { - receiver.location(), name.location(), value.location() - }; - bool has_exception; - Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); - if (has_exception) return Failure::Exception(); + return *value; +} + + +MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandlerIfDefiningSetter( + String* name_raw, + Object* value_raw, + PropertyAttributes attributes, + StrictModeFlag strict_mode, + bool* found) { + *found = true; // except where defined otherwise... + Isolate* isolate = GetHeap()->isolate(); + Handle<JSProxy> proxy(this); + Handle<Object> handler(this->handler()); // Trap might morph proxy. + Handle<String> name(name_raw); + Handle<Object> value(value_raw); + Handle<Object> args[] = { name }; + Handle<Object> result = proxy->CallTrap( + "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); + if (isolate->has_pending_exception()) return Failure::Exception(); + + if (!result->IsUndefined()) { + // The proxy handler cares about this property. + // Check whether it is virtualized as an accessor. + // Emulate [[GetProperty]] semantics for proxies. + bool has_pending_exception; + Handle<Object> argv[] = { result }; + Handle<Object> desc = + Execution::Call(isolate->to_complete_property_descriptor(), result, + ARRAY_SIZE(argv), argv, &has_pending_exception); + if (has_pending_exception) return Failure::Exception(); + + Handle<String> conf_name = + isolate->factory()->LookupAsciiSymbol("configurable_"); + Handle<Object> configurable(v8::internal::GetProperty(desc, conf_name)); + ASSERT(!isolate->has_pending_exception()); + if (configurable->IsFalse()) { + Handle<String> trap = + isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); + Handle<Object> args[] = { handler, trap, name }; + Handle<Object> error = isolate->factory()->NewTypeError( + "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); + return isolate->Throw(*error); + } + ASSERT(configurable->IsTrue()); + + // Check for AccessorDescriptor. + Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); + Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); + ASSERT(!isolate->has_pending_exception()); + if (!setter->IsUndefined()) { + // We have a setter -- invoke it. + // TODO(rossberg): nicer would be to cast to some JSCallable here... + return proxy->SetPropertyWithDefinedSetter( + JSReceiver::cast(*setter), *value); + } else { + Handle<String> get_name = isolate->factory()->LookupAsciiSymbol("get_"); + Handle<Object> getter(v8::internal::GetProperty(desc, get_name)); + ASSERT(!isolate->has_pending_exception()); + if (!getter->IsUndefined()) { + // We have a getter but no setter -- the property may not be + // written. In strict mode, throw an error. + if (strict_mode == kNonStrictMode) return *value; + Handle<Object> args[] = { name, proxy }; + Handle<Object> error = isolate->factory()->NewTypeError( + "no_setter_in_callback", HandleVector(args, ARRAY_SIZE(args))); + return isolate->Throw(*error); + } + } + // Fall-through. + } + // The proxy does not define the property as an accessor. + *found = false; return *value; } @@ -2278,31 +2771,16 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( HandleScope scope(isolate); Handle<Object> receiver(this); Handle<Object> name(name_raw); - Handle<Object> handler(this->handler()); - // Extract trap function. - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<Object> args[] = { name }; + Handle<Object> result = CallTrap( + "delete", Handle<Object>(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return Failure::Exception(); - if (trap->IsUndefined()) { - Handle<Object> args[] = { handler, trap_name }; - Handle<Object> error = isolate->factory()->NewTypeError( - "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); - isolate->Throw(*error); - return Failure::Exception(); - } - - // Call trap function. - Object** args[] = { name.location() }; - bool has_exception; - Handle<Object> result = - Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); - if (has_exception) return Failure::Exception(); Object* bool_result = result->ToBoolean(); - if (mode == STRICT_DELETION && - bool_result == isolate->heap()->false_value()) { - Handle<Object> args[] = { handler, trap_name }; + if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) { + Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); + Handle<Object> args[] = { Handle<Object>(handler()), trap_name }; Handle<Object> error = isolate->factory()->NewTypeError( "handler_failed", HandleVector(args, ARRAY_SIZE(args))); isolate->Throw(*error); @@ -2312,39 +2790,76 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( } +MUST_USE_RESULT MaybeObject* JSProxy::DeleteElementWithHandler( + uint32_t index, + DeleteMode mode) { + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); + Handle<String> name = isolate->factory()->Uint32ToString(index); + return JSProxy::DeletePropertyWithHandler(*name, mode); +} + + MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( JSReceiver* receiver_raw, - String* name_raw, - bool* has_exception) { + String* name_raw) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); + Handle<JSProxy> proxy(this); + Handle<Object> handler(this->handler()); // Trap might morph proxy. Handle<JSReceiver> receiver(receiver_raw); Handle<Object> name(name_raw); - Handle<Object> handler(this->handler()); - // Extract trap function. - Handle<String> trap_name = - isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<Object> args[] = { name }; + Handle<Object> result = CallTrap( + "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return NONE; - if (trap->IsUndefined()) { - Handle<Object> args[] = { handler, trap_name }; + + if (result->IsUndefined()) return ABSENT; + + bool has_pending_exception; + Handle<Object> argv[] = { result }; + Handle<Object> desc = + Execution::Call(isolate->to_complete_property_descriptor(), result, + ARRAY_SIZE(argv), argv, &has_pending_exception); + if (has_pending_exception) return NONE; + + // Convert result to PropertyAttributes. + Handle<String> enum_n = isolate->factory()->LookupAsciiSymbol("enumerable"); + Handle<Object> enumerable(v8::internal::GetProperty(desc, enum_n)); + if (isolate->has_pending_exception()) return NONE; + Handle<String> conf_n = isolate->factory()->LookupAsciiSymbol("configurable"); + Handle<Object> configurable(v8::internal::GetProperty(desc, conf_n)); + if (isolate->has_pending_exception()) return NONE; + Handle<String> writ_n = isolate->factory()->LookupAsciiSymbol("writable"); + Handle<Object> writable(v8::internal::GetProperty(desc, writ_n)); + if (isolate->has_pending_exception()) return NONE; + + if (configurable->IsFalse()) { + Handle<String> trap = + isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); + Handle<Object> args[] = { handler, trap, name }; Handle<Object> error = isolate->factory()->NewTypeError( - "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); + "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); isolate->Throw(*error); - *has_exception = true; return NONE; } - // Call trap function. - Object** args[] = { name.location() }; - Handle<Object> result = - Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception); - if (has_exception) return NONE; + int attributes = NONE; + if (enumerable->ToBoolean()->IsFalse()) attributes |= DONT_ENUM; + if (configurable->ToBoolean()->IsFalse()) attributes |= DONT_DELETE; + if (writable->ToBoolean()->IsFalse()) attributes |= READ_ONLY; + return static_cast<PropertyAttributes>(attributes); +} + - // TODO(rossberg): convert result to PropertyAttributes - USE(result); - return NONE; +MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( + JSReceiver* receiver, + uint32_t index) { + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); + Handle<String> name = isolate->factory()->Uint32ToString(index); + return GetPropertyAttributeWithHandler(receiver, *name); } @@ -2353,6 +2868,9 @@ void JSProxy::Fix() { HandleScope scope(isolate); Handle<JSProxy> self(this); + // Save identity hash. + MaybeObject* maybe_hash = GetIdentityHash(OMIT_CREATION); + if (IsJSFunctionProxy()) { isolate->factory()->BecomeJSFunction(self); // Code will be set on the JavaScript side. @@ -2360,9 +2878,42 @@ void JSProxy::Fix() { isolate->factory()->BecomeJSObject(self); } ASSERT(self->IsJSObject()); + + // Inherit identity, if it was present. + Object* hash; + if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) { + Handle<JSObject> new_self(JSObject::cast(*self)); + isolate->factory()->SetIdentityHash(new_self, hash); + } } +MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, + Handle<Object> derived, + int argc, + Handle<Object> argv[]) { + Isolate* isolate = GetIsolate(); + Handle<Object> handler(this->handler()); + + Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol(name); + Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + if (isolate->has_pending_exception()) return trap; + + if (trap->IsUndefined()) { + if (derived.is_null()) { + Handle<Object> args[] = { handler, trap_name }; + Handle<Object> error = isolate->factory()->NewTypeError( + "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); + isolate->Throw(*error); + return Handle<Object>(); + } + trap = Handle<Object>(derived); + } + + bool threw; + return Execution::Call(trap, handler, argc, argv, &threw); +} + MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, String* name, @@ -2387,48 +2938,46 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, } // Check access rights if needed. - if (IsAccessCheckNeeded() - && !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { - return SetPropertyWithFailedAccessCheck(result, - name, - value, - true, - strict_mode); + if (IsAccessCheckNeeded()) { + if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { + return SetPropertyWithFailedAccessCheck( + result, name, value, true, strict_mode); + } } if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); if (proto->IsNull()) return value; ASSERT(proto->IsJSGlobalObject()); - return JSObject::cast(proto)->SetProperty( + return JSObject::cast(proto)->SetPropertyForResult( result, name, value, attributes, strict_mode); } if (!result->IsProperty() && !IsJSContextExtensionObject()) { - // We could not find a local property so let's check whether there is an - // accessor that wants to handle the property. - LookupResult accessor_result; - LookupCallbackSetterInPrototypes(name, &accessor_result); - if (accessor_result.IsProperty()) { - return SetPropertyWithCallback(accessor_result.GetCallbackObject(), - name, - value, - accessor_result.holder(), - strict_mode); - } + bool found = false; + MaybeObject* result_object; + result_object = SetPropertyWithCallbackSetterInPrototypes(name, + value, + attributes, + &found, + strict_mode); + if (found) return result_object; } + + // At this point, no GC should have happened, as this would invalidate + // 'result', which we cannot handlify! + if (!result->IsFound()) { // Neither properties nor transitions found. return AddProperty(name, value, attributes, strict_mode); } if (result->IsReadOnly() && result->IsProperty()) { if (strict_mode == kStrictMode) { - HandleScope scope(heap->isolate()); - Handle<String> key(name); - Handle<Object> holder(this); - Handle<Object> args[2] = { key, holder }; + Handle<JSObject> self(this); + Handle<String> hname(name); + Handle<Object> args[] = { hname, self }; return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( - "strict_read_only_property", HandleVector(args, 2))); + "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); } else { return value; } @@ -2472,7 +3021,6 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION); JSFunction* function = JSFunction::cast(target_descriptors->GetValue(number)); - ASSERT(!HEAP->InNewSpace(function)); if (value == function) { set_map(target_map); return value; @@ -2484,10 +3032,11 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, case NULL_DESCRIPTOR: case ELEMENTS_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); - default: + case HANDLER: UNREACHABLE(); + return value; } - UNREACHABLE(); + UNREACHABLE(); // keep the compiler happy return value; } @@ -2501,6 +3050,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, // Note that this method cannot be used to set the prototype of a function // because ConvertDescriptorToField() which is called in "case CALLBACKS:" // doesn't handle function prototypes correctly. +Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( + Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes) { + CALL_HEAP_FUNCTION( + object->GetIsolate(), + object->SetLocalPropertyIgnoreAttributes(*key, *value, attributes), + Object); +} + + MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( String* name, Object* value, @@ -2509,12 +3070,12 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; - LookupResult result; + Isolate* isolate = GetIsolate(); + LookupResult result(isolate); LocalLookup(name, &result); // Check access rights if needed. if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { + if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { return SetPropertyWithFailedAccessCheck(&result, name, value, @@ -2572,10 +3133,11 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( case NULL_DESCRIPTOR: case ELEMENTS_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); - default: + case HANDLER: UNREACHABLE(); + return value; } - UNREACHABLE(); + UNREACHABLE(); // keep the compiler happy return value; } @@ -2585,7 +3147,7 @@ PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( String* name, bool continue_search) { // Check local property, ignore interceptor. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookupRealNamedProperty(name, &result); if (result.IsProperty()) return result.GetAttributes(); @@ -2657,12 +3219,11 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( String* key) { uint32_t index = 0; if (IsJSObject() && key->AsArrayIndex(&index)) { - if (JSObject::cast(this)->HasElementWithReceiver(receiver, index)) - return NONE; - return ABSENT; + return JSObject::cast(this)->HasElementWithReceiver(receiver, index) + ? NONE : ABSENT; } // Named property. - LookupResult result; + LookupResult result(GetIsolate()); Lookup(key, &result); return GetPropertyAttribute(receiver, &result, key, true); } @@ -2689,10 +3250,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, case CALLBACKS: return result->GetAttributes(); case HANDLER: { - // TODO(rossberg): propagate exceptions properly. - bool has_exception = false; - return JSProxy::cast(this)->GetPropertyAttributeWithHandler( - receiver, name, &has_exception); + return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( + receiver, name); } case INTERCEPTOR: return result->holder()->GetPropertyAttributeWithInterceptor( @@ -2713,7 +3272,7 @@ PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) { return ABSENT; } // Named property. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookup(name, &result); return GetPropertyAttribute(this, &result, name, false); } @@ -2728,7 +3287,9 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj, if (result->IsMap() && Map::cast(result)->EquivalentToForNormalization(fast, mode)) { #ifdef DEBUG - Map::cast(result)->SharedMapVerify(); + if (FLAG_verify_heap) { + Map::cast(result)->SharedMapVerify(); + } if (FLAG_enable_slow_asserts) { // The cached map should match newly created normalized map bit-by-bit. Object* fresh; @@ -2764,6 +3325,15 @@ void NormalizedMapCache::Clear() { } +void JSObject::UpdateMapCodeCache(Handle<JSObject> object, + Handle<String> name, + Handle<Code> code) { + Isolate* isolate = object->GetIsolate(); + CALL_HEAP_FUNCTION_VOID(isolate, + object->UpdateMapCodeCache(*name, *code)); +} + + MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) { if (map()->is_shared()) { // Fast case maps are never marked as shared. @@ -2782,6 +3352,15 @@ MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) { } +void JSObject::NormalizeProperties(Handle<JSObject> object, + PropertyNormalizationMode mode, + int expected_additional_properties) { + CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), + object->NormalizeProperties( + mode, expected_additional_properties)); +} + + MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, int expected_additional_properties) { if (!HasFastProperties()) return this; @@ -2853,12 +3432,14 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, case INTERCEPTOR: case ELEMENTS_TRANSITION: break; - default: + case HANDLER: + case NORMAL: UNREACHABLE(); + break; } } - Heap* current_heap = map_of_this->heap(); + Heap* current_heap = GetHeap(); // Copy the next enumeration index from instance descriptor. int index = map_of_this->instance_descriptors()->NextEnumerationIndex(); @@ -2880,6 +3461,11 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, ASSERT(instance_size_delta >= 0); current_heap->CreateFillerObjectAt(this->address() + new_instance_size, instance_size_delta); + if (Marking::IsBlack(Marking::MarkBitFrom(this))) { + MemoryChunk::IncrementLiveBytesFromMutator(this->address(), + -instance_size_delta); + } + set_map(new_map); new_map->clear_instance_descriptors(); @@ -2898,6 +3484,14 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, } +void JSObject::TransformToFastProperties(Handle<JSObject> object, + int unused_property_fields) { + CALL_HEAP_FUNCTION_VOID( + object->GetIsolate(), + object->TransformToFastProperties(unused_property_fields)); +} + + MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) { if (HasFastProperties()) return this; ASSERT(!IsGlobalObject()); @@ -2906,6 +3500,14 @@ MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) { } +Handle<SeededNumberDictionary> JSObject::NormalizeElements( + Handle<JSObject> object) { + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->NormalizeElements(), + SeededNumberDictionary); +} + + MaybeObject* JSObject::NormalizeElements() { ASSERT(!HasExternalArrayElements()); @@ -2913,13 +3515,14 @@ MaybeObject* JSObject::NormalizeElements() { FixedArrayBase* array = FixedArrayBase::cast(elements()); Map* old_map = array->map(); bool is_arguments = - (old_map == old_map->heap()->non_strict_arguments_elements_map()); + (old_map == old_map->GetHeap()->non_strict_arguments_elements_map()); if (is_arguments) { array = FixedArrayBase::cast(FixedArray::cast(array)->get(1)); } if (array->IsDictionary()) return array; ASSERT(HasFastElements() || + HasFastSmiOnlyElements() || HasFastDoubleElements() || HasFastArgumentsElements()); // Compute the effective length and allocate a new backing store. @@ -2954,7 +3557,8 @@ MaybeObject* JSObject::NormalizeElements() { if (!maybe_value_object->ToObject(&value)) return maybe_value_object; } } else { - ASSERT(old_map->has_fast_elements()); + ASSERT(old_map->has_fast_elements() || + old_map->has_fast_smi_only_elements()); value = FixedArray::cast(array)->get(i); } PropertyDetails details = PropertyDetails(NONE, NORMAL); @@ -2974,13 +3578,15 @@ MaybeObject* JSObject::NormalizeElements() { // Set the new map first to satify the elements type assert in // set_elements(). Object* new_map; - MaybeObject* maybe = map()->GetSlowElementsMap(); + MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), + DICTIONARY_ELEMENTS); if (!maybe->ToObject(&new_map)) return maybe; set_map(Map::cast(new_map)); set_elements(dictionary); } - old_map->isolate()->counters()->elements_to_dictionary()->Increment(); + old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()-> + Increment(); #ifdef DEBUG if (FLAG_trace_normalization) { @@ -2994,102 +3600,252 @@ MaybeObject* JSObject::NormalizeElements() { } -MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) { +Smi* JSReceiver::GenerateIdentityHash() { Isolate* isolate = GetIsolate(); - Heap* heap = isolate->heap(); - Object* holder = BypassGlobalProxy(); - if (holder->IsUndefined()) return heap->undefined_value(); - JSObject* obj = JSObject::cast(holder); - if (obj->HasFastProperties()) { - // If the object has fast properties, check whether the first slot - // in the descriptor array matches the hidden symbol. Since the - // hidden symbols hash code is zero (and no other string has hash - // code zero) it will always occupy the first entry if present. - DescriptorArray* descriptors = obj->map()->instance_descriptors(); - if ((descriptors->number_of_descriptors() > 0) && - (descriptors->GetKey(0) == heap->hidden_symbol()) && - descriptors->IsProperty(0)) { - ASSERT(descriptors->GetType(0) == FIELD); - return obj->FastPropertyAt(descriptors->GetFieldIndex(0)); - } - } - - // Only attempt to find the hidden properties in the local object and not - // in the prototype chain. - if (!obj->HasHiddenPropertiesObject()) { - // Hidden properties object not found. Allocate a new hidden properties - // object if requested. Otherwise return the undefined value. - if (flag == ALLOW_CREATION) { - Object* hidden_obj; - { MaybeObject* maybe_obj = heap->AllocateJSObject( - isolate->context()->global_context()->object_function()); - if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj; - } - // Don't allow leakage of the hidden object through accessors - // on Object.prototype. - { - MaybeObject* maybe_obj = - JSObject::cast(hidden_obj)->SetPrototype(heap->null_value(), false); - if (maybe_obj->IsFailure()) return maybe_obj; - } - return obj->SetHiddenPropertiesObject(hidden_obj); - } else { - return heap->undefined_value(); - } - } - return obj->GetHiddenPropertiesObject(); -} - - -MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) { - Isolate* isolate = GetIsolate(); - Object* hidden_props_obj; - { MaybeObject* maybe_obj = GetHiddenProperties(flag); - if (!maybe_obj->ToObject(&hidden_props_obj)) return maybe_obj; - } - if (!hidden_props_obj->IsJSObject()) { - // We failed to create hidden properties. That's a detached - // global proxy. - ASSERT(hidden_props_obj->IsUndefined()); - return Smi::FromInt(0); - } - JSObject* hidden_props = JSObject::cast(hidden_props_obj); - String* hash_symbol = isolate->heap()->identity_hash_symbol(); - { - // Note that HasLocalProperty() can cause a GC in the general case in the - // presence of interceptors. - AssertNoAllocation no_alloc; - if (hidden_props->HasLocalProperty(hash_symbol)) { - MaybeObject* hash = hidden_props->GetProperty(hash_symbol); - return Smi::cast(hash->ToObjectChecked()); - } - } int hash_value; int attempts = 0; do { // Generate a random 32-bit hash value but limit range to fit // within a smi. - hash_value = V8::Random(isolate) & Smi::kMaxValue; + hash_value = V8::RandomPrivate(isolate) & Smi::kMaxValue; attempts++; } while (hash_value == 0 && attempts < 30); hash_value = hash_value != 0 ? hash_value : 1; // never return 0 - Smi* hash = Smi::FromInt(hash_value); - { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes( - hash_symbol, - hash, - static_cast<PropertyAttributes>(None)); - if (result->IsFailure()) return result; + return Smi::FromInt(hash_value); +} + + +MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) { + MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(), + hash); + if (maybe->IsFailure()) return maybe; + return this; +} + + +int JSObject::GetIdentityHash(Handle<JSObject> obj) { + CALL_AND_RETRY(obj->GetIsolate(), + obj->GetIdentityHash(ALLOW_CREATION), + return Smi::cast(__object__)->value(), + return 0); +} + + +MaybeObject* JSObject::GetIdentityHash(CreationFlag flag) { + Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol()); + if (stored_value->IsSmi()) return stored_value; + + // Do not generate permanent identity hash code if not requested. + if (flag == OMIT_CREATION) return GetHeap()->undefined_value(); + + Smi* hash = GenerateIdentityHash(); + MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(), + hash); + if (result->IsFailure()) return result; + if (result->ToObjectUnchecked()->IsUndefined()) { + // Trying to get hash of detached proxy. + return Smi::FromInt(0); } return hash; } +MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { + Object* hash = this->hash(); + if (!hash->IsSmi() && flag == ALLOW_CREATION) { + hash = GenerateIdentityHash(); + set_hash(hash); + } + return hash; +} + + +Object* JSObject::GetHiddenProperty(String* key) { + if (IsJSGlobalProxy()) { + // For a proxy, use the prototype as target object. + Object* proxy_parent = GetPrototype(); + // If the proxy is detached, return undefined. + if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); + ASSERT(proxy_parent->IsJSGlobalObject()); + return JSObject::cast(proxy_parent)->GetHiddenProperty(key); + } + ASSERT(!IsJSGlobalProxy()); + MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); + ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. + if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) { + return GetHeap()->undefined_value(); + } + StringDictionary* dictionary = + StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); + int entry = dictionary->FindEntry(key); + if (entry == StringDictionary::kNotFound) return GetHeap()->undefined_value(); + return dictionary->ValueAt(entry); +} + + +Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj, + Handle<String> key, + Handle<Object> value) { + CALL_HEAP_FUNCTION(obj->GetIsolate(), + obj->SetHiddenProperty(*key, *value), + Object); +} + + +MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { + if (IsJSGlobalProxy()) { + // For a proxy, use the prototype as target object. + Object* proxy_parent = GetPrototype(); + // If the proxy is detached, return undefined. + if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); + ASSERT(proxy_parent->IsJSGlobalObject()); + return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value); + } + ASSERT(!IsJSGlobalProxy()); + MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(true); + StringDictionary* dictionary; + if (!hidden_lookup->To<StringDictionary>(&dictionary)) return hidden_lookup; + + // If it was found, check if the key is already in the dictionary. + int entry = dictionary->FindEntry(key); + if (entry != StringDictionary::kNotFound) { + // If key was found, just update the value. + dictionary->ValueAtPut(entry, value); + return this; + } + // Key was not already in the dictionary, so add the entry. + MaybeObject* insert_result = dictionary->Add(key, + value, + PropertyDetails(NONE, NORMAL)); + StringDictionary* new_dict; + if (!insert_result->To<StringDictionary>(&new_dict)) return insert_result; + if (new_dict != dictionary) { + // If adding the key expanded the dictionary (i.e., Add returned a new + // dictionary), store it back to the object. + MaybeObject* store_result = SetHiddenPropertiesDictionary(new_dict); + if (store_result->IsFailure()) return store_result; + } + // Return this to mark success. + return this; +} + + +void JSObject::DeleteHiddenProperty(String* key) { + if (IsJSGlobalProxy()) { + // For a proxy, use the prototype as target object. + Object* proxy_parent = GetPrototype(); + // If the proxy is detached, return immediately. + if (proxy_parent->IsNull()) return; + ASSERT(proxy_parent->IsJSGlobalObject()); + JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); + return; + } + MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); + ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. + if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return; + StringDictionary* dictionary = + StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); + int entry = dictionary->FindEntry(key); + if (entry == StringDictionary::kNotFound) { + // Key wasn't in dictionary. Deletion is a success. + return; + } + // Key was in the dictionary. Remove it. + dictionary->DeleteProperty(entry, JSReceiver::FORCE_DELETION); +} + + +bool JSObject::HasHiddenProperties() { + return GetPropertyAttributePostInterceptor(this, + GetHeap()->hidden_symbol(), + false) != ABSENT; +} + + +MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) { + ASSERT(!IsJSGlobalProxy()); + if (HasFastProperties()) { + // If the object has fast properties, check whether the first slot + // in the descriptor array matches the hidden symbol. Since the + // hidden symbols hash code is zero (and no other string has hash + // code zero) it will always occupy the first entry if present. + DescriptorArray* descriptors = this->map()->instance_descriptors(); + if ((descriptors->number_of_descriptors() > 0) && + (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) { + if (descriptors->GetType(0) == FIELD) { + Object* hidden_store = + this->FastPropertyAt(descriptors->GetFieldIndex(0)); + return StringDictionary::cast(hidden_store); + } else { + ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR || + descriptors->GetType(0) == MAP_TRANSITION); + } + } + } else { + PropertyAttributes attributes; + // You can't install a getter on a property indexed by the hidden symbol, + // so we can be sure that GetLocalPropertyPostInterceptor returns a real + // object. + Object* lookup = + GetLocalPropertyPostInterceptor(this, + GetHeap()->hidden_symbol(), + &attributes)->ToObjectUnchecked(); + if (!lookup->IsUndefined()) { + return StringDictionary::cast(lookup); + } + } + if (!create_if_absent) return GetHeap()->undefined_value(); + const int kInitialSize = 5; + MaybeObject* dict_alloc = StringDictionary::Allocate(kInitialSize); + StringDictionary* dictionary; + if (!dict_alloc->To<StringDictionary>(&dictionary)) return dict_alloc; + MaybeObject* store_result = + SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), + dictionary, + DONT_ENUM, + kNonStrictMode); + if (store_result->IsFailure()) return store_result; + return dictionary; +} + + +MaybeObject* JSObject::SetHiddenPropertiesDictionary( + StringDictionary* dictionary) { + ASSERT(!IsJSGlobalProxy()); + ASSERT(HasHiddenProperties()); + if (HasFastProperties()) { + // If the object has fast properties, check whether the first slot + // in the descriptor array matches the hidden symbol. Since the + // hidden symbols hash code is zero (and no other string has hash + // code zero) it will always occupy the first entry if present. + DescriptorArray* descriptors = this->map()->instance_descriptors(); + if ((descriptors->number_of_descriptors() > 0) && + (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) { + if (descriptors->GetType(0) == FIELD) { + this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary); + return this; + } else { + ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR || + descriptors->GetType(0) == MAP_TRANSITION); + } + } + } + MaybeObject* store_result = + SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), + dictionary, + DONT_ENUM, + kNonStrictMode); + if (store_result->IsFailure()) return store_result; + return this; +} + + MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) { // Check local property, ignore interceptor. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookupRealNamedProperty(name, &result); if (!result.IsProperty()) return GetHeap()->true_value(); @@ -3171,6 +3927,14 @@ MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) { } +Handle<Object> JSObject::DeleteElement(Handle<JSObject> obj, + uint32_t index) { + CALL_HEAP_FUNCTION(obj->GetIsolate(), + obj->DeleteElement(index, JSObject::NORMAL_DELETION), + Object); +} + + MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { Isolate* isolate = GetIsolate(); // Check access rights if needed. @@ -3199,12 +3963,11 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { } -MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { - if (IsJSProxy()) { - return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode); - } else { - return JSObject::cast(this)->DeleteProperty(name, mode); - } +Handle<Object> JSObject::DeleteProperty(Handle<JSObject> obj, + Handle<String> prop) { + CALL_HEAP_FUNCTION(obj->GetIsolate(), + obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION), + Object); } @@ -3231,7 +3994,7 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { if (name->AsArrayIndex(&index)) { return DeleteElement(index, mode); } else { - LookupResult result; + LookupResult result(isolate); LocalLookup(name, &result); if (!result.IsProperty()) return isolate->heap()->true_value(); // Ignore attributes if forcing a deletion. @@ -3265,10 +4028,27 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { } +MaybeObject* JSReceiver::DeleteElement(uint32_t index, DeleteMode mode) { + if (IsJSProxy()) { + return JSProxy::cast(this)->DeleteElementWithHandler(index, mode); + } + return JSObject::cast(this)->DeleteElement(index, mode); +} + + +MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { + if (IsJSProxy()) { + return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode); + } + return JSObject::cast(this)->DeleteProperty(name, mode); +} + + bool JSObject::ReferencesObjectFromElements(FixedArray* elements, ElementsKind kind, Object* object) { - ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS); + ASSERT(kind == FAST_ELEMENTS || + kind == DICTIONARY_ELEMENTS); if (kind == FAST_ELEMENTS) { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() @@ -3289,7 +4069,7 @@ bool JSObject::ReferencesObjectFromElements(FixedArray* elements, // Check whether this object references another object. bool JSObject::ReferencesObject(Object* obj) { Map* map_of_this = map(); - Heap* heap = map_of_this->heap(); + Heap* heap = GetHeap(); AssertNoAllocation no_alloc; // Is the object the constructor for this object? @@ -3324,6 +4104,8 @@ bool JSObject::ReferencesObject(Object* obj) { // Raw pixels and external arrays do not reference other // objects. break; + case FAST_SMI_ONLY_ELEMENTS: + break; case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: { FixedArray* elements = FixedArray::cast(this->elements()); @@ -3389,6 +4171,11 @@ bool JSObject::ReferencesObject(Object* obj) { } +Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { + CALL_HEAP_FUNCTION(object->GetIsolate(), object->PreventExtensions(), Object); +} + + MaybeObject* JSObject::PreventExtensions() { Isolate* isolate = GetIsolate(); if (IsAccessCheckNeeded() && @@ -3440,15 +4227,16 @@ MaybeObject* JSObject::PreventExtensions() { // Tests for the fast common case for property enumeration: -// - This object and all prototypes has an enum cache (which means that it has -// no interceptors and needs no access checks). +// - This object and all prototypes has an enum cache (which means that +// it is no proxy, has no interceptors and needs no access checks). // - This object has no elements. // - No prototype has enumerable properties/elements. -bool JSObject::IsSimpleEnum() { +bool JSReceiver::IsSimpleEnum() { Heap* heap = GetHeap(); for (Object* o = this; o != heap->null_value(); o = JSObject::cast(o)->GetPrototype()) { + if (!o->IsJSObject()) return false; JSObject* curr = JSObject::cast(o); if (!curr->map()->instance_descriptors()->HasEnumCache()) return false; ASSERT(!curr->HasNamedInterceptor()); @@ -3465,11 +4253,14 @@ bool JSObject::IsSimpleEnum() { } -int Map::NumberOfDescribedProperties() { +int Map::NumberOfDescribedProperties(PropertyAttributes filter) { int result = 0; DescriptorArray* descs = instance_descriptors(); for (int i = 0; i < descs->number_of_descriptors(); i++) { - if (descs->IsProperty(i)) result++; + PropertyDetails details(descs->GetDetails(i)); + if (descs->IsProperty(i) && (details.attributes() & filter) == 0) { + result++; + } } return result; } @@ -3511,15 +4302,6 @@ AccessorDescriptor* Map::FindAccessor(String* name) { void JSReceiver::LocalLookup(String* name, LookupResult* result) { - if (IsJSProxy()) { - result->HandlerResult(); - } else { - JSObject::cast(this)->LocalLookup(name, result); - } -} - - -void JSObject::LocalLookup(String* name, LookupResult* result) { ASSERT(name->IsString()); Heap* heap = GetHeap(); @@ -3528,28 +4310,36 @@ void JSObject::LocalLookup(String* name, LookupResult* result) { Object* proto = GetPrototype(); if (proto->IsNull()) return result->NotFound(); ASSERT(proto->IsJSGlobalObject()); - return JSObject::cast(proto)->LocalLookup(name, result); + return JSReceiver::cast(proto)->LocalLookup(name, result); + } + + if (IsJSProxy()) { + result->HandlerResult(JSProxy::cast(this)); + return; } // Do not use inline caching if the object is a non-global object // that requires access checks. - if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) { + if (IsAccessCheckNeeded()) { result->DisallowCaching(); } + JSObject* js_object = JSObject::cast(this); + // Check __proto__ before interceptor. if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) { - result->ConstantResult(this); + result->ConstantResult(js_object); return; } // Check for lookup interceptor except when bootstrapping. - if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { - result->InterceptorResult(this); + if (js_object->HasNamedInterceptor() && + !heap->isolate()->bootstrapper()->IsActive()) { + result->InterceptorResult(js_object); return; } - LocalLookupRealNamedProperty(name, result); + js_object->LocalLookupRealNamedProperty(name, result); } @@ -3559,7 +4349,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { for (Object* current = this; current != heap->null_value(); current = JSObject::cast(current)->GetPrototype()) { - JSObject::cast(current)->LocalLookup(name, result); + JSReceiver::cast(current)->LocalLookup(name, result); if (result->IsProperty()) return; } result->NotFound(); @@ -3570,28 +4360,37 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { void JSObject::LookupCallback(String* name, LookupResult* result) { Heap* heap = GetHeap(); for (Object* current = this; - current != heap->null_value(); + current != heap->null_value() && current->IsJSObject(); current = JSObject::cast(current)->GetPrototype()) { JSObject::cast(current)->LocalLookupRealNamedProperty(name, result); - if (result->IsProperty() && result->type() == CALLBACKS) return; + if (result->IsFound() && result->type() == CALLBACKS) return; } result->NotFound(); } -// Search for a getter or setter in an elements dictionary. Returns either -// undefined if the element is read-only, or the getter/setter pair (fixed -// array) if there is an existing one, or the hole value if the element does -// not exist or is a normal non-getter/setter data element. -static Object* FindGetterSetterInDictionary(SeededNumberDictionary* dictionary, - uint32_t index, - Heap* heap) { +// Search for a getter or setter in an elements dictionary and update its +// attributes. Returns either undefined if the element is non-deletable, or the +// getter/setter pair if there is an existing one, or the hole value if the +// element does not exist or is a normal non-getter/setter data element. +static Object* UpdateGetterSetterInDictionary( + SeededNumberDictionary* dictionary, + uint32_t index, + PropertyAttributes attributes, + Heap* heap) { int entry = dictionary->FindEntry(index); if (entry != SeededNumberDictionary::kNotFound) { Object* result = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); - if (details.IsReadOnly()) return heap->undefined_value(); - if (details.type() == CALLBACKS && result->IsFixedArray()) return result; + // TODO(mstarzinger): We should check for details.IsDontDelete() here once + // we only call into the runtime once to set both getter and setter. + if (details.type() == CALLBACKS && result->IsAccessorPair()) { + if (details.attributes() != attributes) { + dictionary->DetailsAtPut(entry, + PropertyDetails(attributes, CALLBACKS, index)); + } + return result; + } } return heap->the_hole_value(); } @@ -3616,6 +4415,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, if (is_element) { switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: break; @@ -3632,8 +4432,10 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, // elements. return heap->undefined_value(); case DICTIONARY_ELEMENTS: { - Object* probe = - FindGetterSetterInDictionary(element_dictionary(), index, heap); + Object* probe = UpdateGetterSetterInDictionary(element_dictionary(), + index, + attributes, + heap); if (!probe->IsTheHole()) return probe; // Otherwise allow to override it. break; @@ -3651,7 +4453,10 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, if (arguments->IsDictionary()) { SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(arguments); - probe = FindGetterSetterInDictionary(dictionary, index, heap); + probe = UpdateGetterSetterInDictionary(dictionary, + index, + attributes, + heap); if (!probe->IsTheHole()) return probe; } } @@ -3660,14 +4465,15 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, } } else { // Lookup the name. - LookupResult result; - LocalLookup(name, &result); - if (result.IsProperty()) { - if (result.IsReadOnly()) return heap->undefined_value(); + LookupResult result(heap->isolate()); + LocalLookupRealNamedProperty(name, &result); + if (result.IsFound()) { + // TODO(mstarzinger): We should check for result.IsDontDelete() here once + // we only call into the runtime once to set both getter and setter. if (result.type() == CALLBACKS) { Object* obj = result.GetCallbackObject(); // Need to preserve old getters/setters. - if (obj->IsFixedArray()) { + if (obj->IsAccessorPair()) { // Use set to update attributes. return SetPropertyCallback(name, obj, attributes); } @@ -3675,23 +4481,22 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, } } - // Allocate the fixed array to hold getter and setter. - Object* structure; - { MaybeObject* maybe_structure = heap->AllocateFixedArray(2, TENURED); - if (!maybe_structure->ToObject(&structure)) return maybe_structure; + AccessorPair* accessors; + { MaybeObject* maybe_accessors = heap->AllocateAccessorPair(); + if (!maybe_accessors->To<AccessorPair>(&accessors)) return maybe_accessors; } if (is_element) { - return SetElementCallback(index, structure, attributes); + return SetElementCallback(index, accessors, attributes); } else { - return SetPropertyCallback(name, structure, attributes); + return SetPropertyCallback(name, accessors, attributes); } } bool JSObject::CanSetCallback(String* name) { - ASSERT(!IsAccessCheckNeeded() - || Isolate::Current()->MayNamedAccess(this, name, v8::ACCESS_SET)); + ASSERT(!IsAccessCheckNeeded() || + GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); // Check if there is an API defined callback object which prohibits // callback overwriting in this object or it's prototype chain. @@ -3699,7 +4504,7 @@ bool JSObject::CanSetCallback(String* name) { // certain accessors such as window.location should not be allowed // to be overwritten because allowing overwriting could potentially // cause security problems. - LookupResult callback_result; + LookupResult callback_result(GetIsolate()); LookupCallback(name, &callback_result); if (callback_result.IsProperty()) { Object* obj = callback_result.GetCallbackObject(); @@ -3803,7 +4608,7 @@ MaybeObject* JSObject::DefineAccessor(String* name, bool is_getter, Object* fun, PropertyAttributes attributes) { - ASSERT(fun->IsJSFunction() || fun->IsUndefined()); + ASSERT(fun->IsSpecFunction() || fun->IsUndefined()); Isolate* isolate = GetIsolate(); // Check access rights if needed. if (IsAccessCheckNeeded() && @@ -3820,12 +4625,16 @@ MaybeObject* JSObject::DefineAccessor(String* name, fun, attributes); } - Object* array; - { MaybeObject* maybe_array = DefineGetterSetter(name, attributes); - if (!maybe_array->ToObject(&array)) return maybe_array; + Object* accessors; + { MaybeObject* maybe_accessors = DefineGetterSetter(name, attributes); + if (!maybe_accessors->To<Object>(&accessors)) return maybe_accessors; + } + if (accessors->IsUndefined()) return accessors; + if (is_getter) { + AccessorPair::cast(accessors)->set_getter(fun); + } else { + AccessorPair::cast(accessors)->set_setter(fun); } - if (array->IsUndefined()) return array; - FixedArray::cast(array)->set(is_getter ? 0 : 1, fun); return this; } @@ -3866,6 +4675,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { // Accessors overwrite previous callbacks (cf. with getters/setters). switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: break; @@ -3895,7 +4705,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { } } else { // Lookup the name. - LookupResult result; + LookupResult result(isolate); LocalLookup(name, &result); // ES5 forbids turning a property into an accessor if it's not // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5). @@ -3928,7 +4738,6 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { } // Make the lookup and include prototypes. - int accessor_index = is_getter ? kGetterIndex : kSetterIndex; uint32_t index = 0; if (name->AsArrayIndex(&index)) { for (Object* obj = this; @@ -3942,8 +4751,9 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { - if (element->IsFixedArray()) { - return FixedArray::cast(element)->get(accessor_index); + if (element->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(element); + return is_getter ? accessors->getter() : accessors->setter(); } } } @@ -3953,14 +4763,15 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { for (Object* obj = this; obj != heap->null_value(); obj = JSObject::cast(obj)->GetPrototype()) { - LookupResult result; + LookupResult result(heap->isolate()); JSObject::cast(obj)->LocalLookup(name, &result); if (result.IsProperty()) { if (result.IsReadOnly()) return heap->undefined_value(); if (result.type() == CALLBACKS) { Object* obj = result.GetCallbackObject(); - if (obj->IsFixedArray()) { - return FixedArray::cast(obj)->get(accessor_index); + if (obj->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(obj); + return is_getter ? accessors->getter() : accessors->setter(); } } } @@ -4061,7 +4872,7 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP); #ifdef DEBUG - if (Map::cast(result)->is_shared()) { + if (FLAG_verify_heap && Map::cast(result)->is_shared()) { Map::cast(result)->SharedMapVerify(); } #endif @@ -4084,12 +4895,19 @@ MaybeObject* Map::CopyDropTransitions() { return new_map; } +void Map::UpdateCodeCache(Handle<Map> map, + Handle<String> name, + Handle<Code> code) { + Isolate* isolate = map->GetIsolate(); + CALL_HEAP_FUNCTION_VOID(isolate, + map->UpdateCodeCache(*name, *code)); +} MaybeObject* Map::UpdateCodeCache(String* name, Code* code) { // Allocate the code cache if not present. if (code_cache()->IsFixedArray()) { Object* result; - { MaybeObject* maybe_result = code->heap()->AllocateCodeCache(); + { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache(); if (!maybe_result->ToObject(&result)) return maybe_result; } set_code_cache(result); @@ -4127,75 +4945,219 @@ void Map::RemoveFromCodeCache(String* name, Code* code, int index) { } -void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { - // Traverse the transition tree without using a stack. We do this by - // reversing the pointers in the maps and descriptor arrays. - Map* current = this; - Map* meta_map = heap()->meta_map(); - Object** map_or_index_field = NULL; - while (current != meta_map) { - DescriptorArray* d = reinterpret_cast<DescriptorArray*>( - *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset)); - if (!d->IsEmpty()) { - FixedArray* contents = reinterpret_cast<FixedArray*>( - d->get(DescriptorArray::kContentArrayIndex)); - map_or_index_field = RawField(contents, HeapObject::kMapOffset); - Object* map_or_index = *map_or_index_field; - bool map_done = true; // Controls a nested continue statement. - for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0; - i < contents->length(); - i += 2) { - PropertyDetails details(Smi::cast(contents->get(i + 1))); - if (details.IsTransition()) { - // Found a map in the transition array. We record our progress in - // the transition array by recording the current map in the map field - // of the next map and recording the index in the transition array in - // the map field of the array. - Map* next = Map::cast(contents->get(i)); - next->set_map(current); - *map_or_index_field = Smi::FromInt(i + 2); - current = next; - map_done = false; +// An iterator over all map transitions in an descriptor array, reusing the map +// field of the contens array while it is running. +class IntrusiveMapTransitionIterator { + public: + explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array) + : descriptor_array_(descriptor_array) { } + + void Start() { + ASSERT(!IsIterating()); + if (HasContentArray()) *ContentHeader() = Smi::FromInt(0); + } + + bool IsIterating() { + return HasContentArray() && (*ContentHeader())->IsSmi(); + } + + Map* Next() { + ASSERT(IsIterating()); + FixedArray* contents = ContentArray(); + // Attention, tricky index manipulation ahead: Every entry in the contents + // array consists of a value/details pair, so the index is typically even. + // An exception is made for CALLBACKS entries: An even index means we look + // at its getter, and an odd index means we look at its setter. + int index = Smi::cast(*ContentHeader())->value(); + while (index < contents->length()) { + PropertyDetails details(Smi::cast(contents->get(index | 1))); + switch (details.type()) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: + // We definitely have a map transition. + *ContentHeader() = Smi::FromInt(index + 2); + return static_cast<Map*>(contents->get(index)); + case CALLBACKS: { + // We might have a map transition in a getter or in a setter. + AccessorPair* accessors = + static_cast<AccessorPair*>(contents->get(index & ~1)); + Object* accessor = + ((index & 1) == 0) ? accessors->getter() : accessors->setter(); + index++; + if (accessor->IsMap()) { + *ContentHeader() = Smi::FromInt(index); + return static_cast<Map*>(accessor); + } break; } - } - if (!map_done) continue; - } else { - map_or_index_field = NULL; - } - // That was the regular transitions, now for the prototype transitions. - FixedArray* prototype_transitions = - current->unchecked_prototype_transitions(); - Object** proto_map_or_index_field = - RawField(prototype_transitions, HeapObject::kMapOffset); - Object* map_or_index = *proto_map_or_index_field; - const int start = kProtoTransitionHeaderSize + kProtoTransitionMapOffset; - int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : start; - if (i < prototype_transitions->length()) { - // Found a map in the prototype transition array. Record progress in - // an analogous way to the regular transitions array above. - Object* perhaps_map = prototype_transitions->get(i); - if (perhaps_map->IsMap()) { - Map* next = Map::cast(perhaps_map); - next->set_map(current); - *proto_map_or_index_field = - Smi::FromInt(i + kProtoTransitionElementsPerEntry); - current = next; - continue; + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + // We definitely have no map transition. + index += 2; + break; } } - *proto_map_or_index_field = heap()->fixed_array_map(); - if (map_or_index_field != NULL) { - *map_or_index_field = heap()->fixed_array_map(); + *ContentHeader() = descriptor_array_->GetHeap()->fixed_array_map(); + return NULL; + } + + private: + bool HasContentArray() { + return descriptor_array_-> length() > DescriptorArray::kContentArrayIndex; + } + + FixedArray* ContentArray() { + Object* array = descriptor_array_->get(DescriptorArray::kContentArrayIndex); + return static_cast<FixedArray*>(array); + } + + Object** ContentHeader() { + return HeapObject::RawField(ContentArray(), DescriptorArray::kMapOffset); + } + + DescriptorArray* descriptor_array_; +}; + + +// An iterator over all prototype transitions, reusing the map field of the +// underlying array while it is running. +class IntrusivePrototypeTransitionIterator { + public: + explicit IntrusivePrototypeTransitionIterator(FixedArray* proto_trans) + : proto_trans_(proto_trans) { } + + void Start() { + ASSERT(!IsIterating()); + if (HasTransitions()) *Header() = Smi::FromInt(0); + } + + bool IsIterating() { + return HasTransitions() && (*Header())->IsSmi(); + } + + Map* Next() { + ASSERT(IsIterating()); + int transitionNumber = Smi::cast(*Header())->value(); + if (transitionNumber < NumberOfTransitions()) { + *Header() = Smi::FromInt(transitionNumber + 1); + return GetTransition(transitionNumber); } + *Header() = proto_trans_->GetHeap()->fixed_array_map(); + return NULL; + } + + private: + bool HasTransitions() { + return proto_trans_->length() >= Map::kProtoTransitionHeaderSize; + } + + Object** Header() { + return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset); + } + + int NumberOfTransitions() { + Object* num = proto_trans_->get(Map::kProtoTransitionNumberOfEntriesOffset); + return Smi::cast(num)->value(); + } + + Map* GetTransition(int transitionNumber) { + return Map::cast(proto_trans_->get(IndexFor(transitionNumber))); + } - // The callback expects a map to have a real map as its map, so we save - // the map field, which is being used to track the traversal and put the - // correct map (the meta_map) in place while we do the callback. - Map* prev = current->map(); - current->set_map(meta_map); - callback(current, data); - current = prev; + int IndexFor(int transitionNumber) { + return Map::kProtoTransitionHeaderSize + + Map::kProtoTransitionMapOffset + + transitionNumber * Map::kProtoTransitionElementsPerEntry; + } + + FixedArray* proto_trans_; +}; + + +// To traverse the transition tree iteratively, we have to store two kinds of +// information in a map: The parent map in the traversal and which children of a +// node have already been visited. To do this without additional memory, we +// temporarily reuse two maps with known values: +// +// (1) The map of the map temporarily holds the parent, and is restored to the +// meta map afterwards. +// +// (2) The info which children have already been visited depends on which part +// of the map we currently iterate: +// +// (a) If we currently follow normal map transitions, we temporarily store +// the current index in the map of the FixedArray of the desciptor +// array's contents, and restore it to the fixed array map afterwards. +// Note that a single descriptor can have 0, 1, or 2 transitions. +// +// (b) If we currently follow prototype transitions, we temporarily store +// the current index in the map of the FixedArray holding the prototype +// transitions, and restore it to the fixed array map afterwards. +// +// Note that the child iterator is just a concatenation of two iterators: One +// iterating over map transitions and one iterating over prototype transisitons. +class TraversableMap : public Map { + public: + // Record the parent in the traversal within this map. Note that this destroys + // this map's map! + void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); } + + // Reset the current map's map, returning the parent previously stored in it. + TraversableMap* GetAndResetParent() { + TraversableMap* old_parent = static_cast<TraversableMap*>(map()); + set_map_no_write_barrier(GetHeap()->meta_map()); + return old_parent; + } + + // Start iterating over this map's children, possibly destroying a FixedArray + // map (see explanation above). + void ChildIteratorStart() { + IntrusiveMapTransitionIterator(instance_descriptors()).Start(); + IntrusivePrototypeTransitionIterator( + unchecked_prototype_transitions()).Start(); + } + + // If we have an unvisited child map, return that one and advance. If we have + // none, return NULL and reset any destroyed FixedArray maps. + TraversableMap* ChildIteratorNext() { + IntrusiveMapTransitionIterator descriptor_iterator(instance_descriptors()); + if (descriptor_iterator.IsIterating()) { + Map* next = descriptor_iterator.Next(); + if (next != NULL) return static_cast<TraversableMap*>(next); + } + IntrusivePrototypeTransitionIterator + proto_iterator(unchecked_prototype_transitions()); + if (proto_iterator.IsIterating()) { + Map* next = proto_iterator.Next(); + if (next != NULL) return static_cast<TraversableMap*>(next); + } + return NULL; + } +}; + + +// Traverse the transition tree in postorder without using the C++ stack by +// doing pointer reversal. +void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { + TraversableMap* current = static_cast<TraversableMap*>(this); + current->ChildIteratorStart(); + while (true) { + TraversableMap* child = current->ChildIteratorNext(); + if (child != NULL) { + child->ChildIteratorStart(); + child->SetParent(current); + current = child; + } else { + TraversableMap* parent = current->GetAndResetParent(); + callback(current, data); + if (current == this) break; + current = parent; + } } } @@ -4409,7 +5371,7 @@ class CodeCacheHashTableKey : public HashTableKey { MUST_USE_RESULT MaybeObject* AsObject() { ASSERT(code_ != NULL); Object* obj; - { MaybeObject* maybe_obj = code_->heap()->AllocateFixedArray(2); + { MaybeObject* maybe_obj = code_->GetHeap()->AllocateFixedArray(2); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* pair = FixedArray::cast(obj); @@ -4467,13 +5429,22 @@ int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) { void CodeCacheHashTable::RemoveByIndex(int index) { ASSERT(index >= 0); Heap* heap = GetHeap(); - set(EntryToIndex(index), heap->null_value()); - set(EntryToIndex(index) + 1, heap->null_value()); + set(EntryToIndex(index), heap->the_hole_value()); + set(EntryToIndex(index) + 1, heap->the_hole_value()); ElementRemoved(); } -MaybeObject* PolymorphicCodeCache::Update(MapList* maps, +void PolymorphicCodeCache::Update(Handle<PolymorphicCodeCache> cache, + MapHandleList* maps, + Code::Flags flags, + Handle<Code> code) { + Isolate* isolate = cache->GetIsolate(); + CALL_HEAP_FUNCTION_VOID(isolate, cache->Update(maps, flags, *code)); +} + + +MaybeObject* PolymorphicCodeCache::Update(MapHandleList* maps, Code::Flags flags, Code* code) { // Initialize cache if necessary. @@ -4501,13 +5472,14 @@ MaybeObject* PolymorphicCodeCache::Update(MapList* maps, } -Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) { +Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, + Code::Flags flags) { if (!cache()->IsUndefined()) { PolymorphicCodeCacheHashTable* hash_table = PolymorphicCodeCacheHashTable::cast(cache()); - return hash_table->Lookup(maps, flags); + return Handle<Object>(hash_table->Lookup(maps, flags)); } else { - return GetHeap()->undefined_value(); + return GetIsolate()->factory()->undefined_value(); } } @@ -4518,12 +5490,12 @@ Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) { class PolymorphicCodeCacheHashTableKey : public HashTableKey { public: // Callers must ensure that |maps| outlives the newly constructed object. - PolymorphicCodeCacheHashTableKey(MapList* maps, int code_flags) + PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags) : maps_(maps), code_flags_(code_flags) {} bool IsMatch(Object* other) { - MapList other_maps(kDefaultListAllocationSize); + MapHandleList other_maps(kDefaultListAllocationSize); int other_flags; FromObject(other, &other_flags, &other_maps); if (code_flags_ != other_flags) return false; @@ -4539,7 +5511,7 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey { for (int i = 0; i < maps_->length(); ++i) { bool match_found = false; for (int j = 0; j < other_maps.length(); ++j) { - if (maps_->at(i)->EquivalentTo(other_maps.at(j))) { + if (*(maps_->at(i)) == *(other_maps.at(j))) { match_found = true; break; } @@ -4549,7 +5521,7 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey { return true; } - static uint32_t MapsHashHelper(MapList* maps, int code_flags) { + static uint32_t MapsHashHelper(MapHandleList* maps, int code_flags) { uint32_t hash = code_flags; for (int i = 0; i < maps->length(); ++i) { hash ^= maps->at(i)->Hash(); @@ -4562,7 +5534,7 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey { } uint32_t HashForObject(Object* obj) { - MapList other_maps(kDefaultListAllocationSize); + MapHandleList other_maps(kDefaultListAllocationSize); int other_flags; FromObject(obj, &other_flags, &other_maps); return MapsHashHelper(&other_maps, other_flags); @@ -4580,29 +5552,32 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey { FixedArray* list = FixedArray::cast(obj); list->set(0, Smi::FromInt(code_flags_)); for (int i = 0; i < maps_->length(); ++i) { - list->set(i + 1, maps_->at(i)); + list->set(i + 1, *maps_->at(i)); } return list; } private: - static MapList* FromObject(Object* obj, int* code_flags, MapList* maps) { + static MapHandleList* FromObject(Object* obj, + int* code_flags, + MapHandleList* maps) { FixedArray* list = FixedArray::cast(obj); maps->Rewind(0); *code_flags = Smi::cast(list->get(0))->value(); for (int i = 1; i < list->length(); ++i) { - maps->Add(Map::cast(list->get(i))); + maps->Add(Handle<Map>(Map::cast(list->get(i)))); } return maps; } - MapList* maps_; // weak. + MapHandleList* maps_; // weak. int code_flags_; static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1; }; -Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) { +Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps, + int code_flags) { PolymorphicCodeCacheHashTableKey key(maps, code_flags); int entry = FindEntry(&key); if (entry == kNotFound) return GetHeap()->undefined_value(); @@ -4610,7 +5585,7 @@ Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) { } -MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps, +MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps, int code_flags, Code* code) { PolymorphicCodeCacheHashTableKey key(maps, code_flags); @@ -4679,7 +5654,9 @@ MaybeObject* FixedArray::CopySize(int new_length) { AssertNoAllocation no_gc; int len = length(); if (new_length < len) len = new_length; - result->set_map(map()); + // We are taking the map from the old fixed array so the map is sure to + // be an immortal immutable object. + result->set_map_no_write_barrier(map()); WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); for (int i = 0; i < len; i++) { result->set(i, get(i), mode); @@ -4745,14 +5722,19 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage, if (IsEmpty()) return; // Do nothing for empty descriptor array. FixedArray::cast(bridge_storage)-> set(kEnumCacheBridgeCacheIndex, new_cache); - fast_set(FixedArray::cast(bridge_storage), - kEnumCacheBridgeEnumIndex, - get(kEnumerationIndexIndex)); + NoWriteBarrierSet(FixedArray::cast(bridge_storage), + kEnumCacheBridgeEnumIndex, + get(kEnumerationIndexIndex)); set(kEnumerationIndexIndex, bridge_storage); } } +static bool InsertionPointFound(String* key1, String* key2) { + return key1->Hash() > key2->Hash() || key1 == key2; +} + + MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, TransitionFlag transition_flag) { // Transitions are only kept when inserting another transition. @@ -4761,7 +5743,7 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, // Conversely, we filter after replacing, so replacing a transition and // removing all other transitions is not supported. bool remove_transitions = transition_flag == REMOVE_TRANSITIONS; - ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition()); + ASSERT(remove_transitions == !descriptor->ContainsTransition()); ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR); // Ensure the key is a symbol. @@ -4770,29 +5752,18 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, if (!maybe_result->ToObject(&result)) return maybe_result; } - int transitions = 0; - int null_descriptors = 0; - if (remove_transitions) { - for (int i = 0; i < number_of_descriptors(); i++) { - if (IsTransition(i)) transitions++; - if (IsNullDescriptor(i)) null_descriptors++; - } - } else { - for (int i = 0; i < number_of_descriptors(); i++) { - if (IsNullDescriptor(i)) null_descriptors++; - } + int new_size = 0; + for (int i = 0; i < number_of_descriptors(); i++) { + if (IsNullDescriptor(i)) continue; + if (remove_transitions && IsTransitionOnly(i)) continue; + new_size++; } - int new_size = number_of_descriptors() - transitions - null_descriptors; // If key is in descriptor, we replace it in-place when filtering. // Count a null descriptor for key as inserted, not replaced. int index = Search(descriptor->GetKey()); - const bool inserting = (index == kNotFound); - const bool replacing = !inserting; + const bool replacing = (index != kNotFound); bool keep_enumeration_index = false; - if (inserting) { - ++new_size; - } if (replacing) { // We are replacing an existing descriptor. We keep the enumeration // index of a visible property. @@ -4807,15 +5778,23 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, // a transition that will be replaced. Adjust count in this case. ++new_size; } + } else { + ++new_size; } + + DescriptorArray* new_descriptors; { MaybeObject* maybe_result = Allocate(new_size); - if (!maybe_result->ToObject(&result)) return maybe_result; + if (!maybe_result->To<DescriptorArray>(&new_descriptors)) { + return maybe_result; + } } - DescriptorArray* new_descriptors = DescriptorArray::cast(result); + + DescriptorArray::WhitenessWitness witness(new_descriptors); + // Set the enumeration index in the descriptors and set the enumeration index // in the result. int enumeration_index = NextEnumerationIndex(); - if (!descriptor->GetDetails().IsTransition()) { + if (!descriptor->ContainsTransition()) { if (keep_enumeration_index) { descriptor->SetEnumerationIndex( PropertyDetails(GetDetails(index)).index()); @@ -4828,28 +5807,24 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor, // Copy the descriptors, filtering out transitions and null descriptors, // and inserting or replacing a descriptor. - uint32_t descriptor_hash = descriptor->GetKey()->Hash(); - int from_index = 0; int to_index = 0; - - for (; from_index < number_of_descriptors(); from_index++) { - String* key = GetKey(from_index); - if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) { - break; + int insertion_index = -1; + int from_index = 0; + while (from_index < number_of_descriptors()) { + if (insertion_index < 0 && + InsertionPointFound(GetKey(from_index), descriptor->GetKey())) { + insertion_index = to_index++; + if (replacing) from_index++; + } else { + if (!(IsNullDescriptor(from_index) || + (remove_transitions && IsTransitionOnly(from_index)))) { + new_descriptors->CopyFrom(to_index++, this, from_index, witness); + } + from_index++; } - if (IsNullDescriptor(from_index)) continue; - if (remove_transitions && IsTransition(from_index)) continue; - new_descriptors->CopyFrom(to_index++, this, from_index); - } - - new_descriptors->Set(to_index++, descriptor); - if (replacing) from_index++; - - for (; from_index < number_of_descriptors(); from_index++) { - if (IsNullDescriptor(from_index)) continue; - if (remove_transitions && IsTransition(from_index)) continue; - new_descriptors->CopyFrom(to_index++, this, from_index); } + if (insertion_index < 0) insertion_index = to_index++; + new_descriptors->Set(insertion_index, descriptor, witness); ASSERT(to_index == new_descriptors->number_of_descriptors()); SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates()); @@ -4864,22 +5839,27 @@ MaybeObject* DescriptorArray::RemoveTransitions() { // not be allocated. // Compute the size of the map transition entries to be removed. - int num_removed = 0; + int new_number_of_descriptors = 0; for (int i = 0; i < number_of_descriptors(); i++) { - if (!IsProperty(i)) num_removed++; + if (IsProperty(i)) new_number_of_descriptors++; } // Allocate the new descriptor array. - Object* result; - { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed); - if (!maybe_result->ToObject(&result)) return maybe_result; + DescriptorArray* new_descriptors; + { MaybeObject* maybe_result = Allocate(new_number_of_descriptors); + if (!maybe_result->To<DescriptorArray>(&new_descriptors)) { + return maybe_result; + } } - DescriptorArray* new_descriptors = DescriptorArray::cast(result); + + DescriptorArray::WhitenessWitness witness(new_descriptors); // Copy the content. int next_descriptor = 0; for (int i = 0; i < number_of_descriptors(); i++) { - if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i); + if (IsProperty(i)) { + new_descriptors->CopyFrom(next_descriptor++, this, i, witness); + } } ASSERT(next_descriptor == new_descriptors->number_of_descriptors()); @@ -4887,7 +5867,7 @@ MaybeObject* DescriptorArray::RemoveTransitions() { } -void DescriptorArray::SortUnchecked() { +void DescriptorArray::SortUnchecked(const WhitenessWitness& witness) { // In-place heap sort. int len = number_of_descriptors(); @@ -4908,7 +5888,7 @@ void DescriptorArray::SortUnchecked() { } } if (child_hash <= parent_hash) break; - Swap(parent_index, child_index); + NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index); // Now element at child_index could be < its children. parent_index = child_index; // parent_hash remains correct. } @@ -4917,8 +5897,8 @@ void DescriptorArray::SortUnchecked() { // Extract elements and create sorted array. for (int i = len - 1; i > 0; --i) { // Put max element at the back of the array. - Swap(0, i); - // Sift down the new top element. + NoIncrementalWriteBarrierSwapDescriptors(0, i); + // Shift down the new top element. int parent_index = 0; const uint32_t parent_hash = GetKey(parent_index)->Hash(); const int max_parent_index = (i / 2) - 1; @@ -4933,15 +5913,15 @@ void DescriptorArray::SortUnchecked() { } } if (child_hash <= parent_hash) break; - Swap(parent_index, child_index); + NoIncrementalWriteBarrierSwapDescriptors(parent_index, child_index); parent_index = child_index; } } } -void DescriptorArray::Sort() { - SortUnchecked(); +void DescriptorArray::Sort(const WhitenessWitness& witness) { + SortUnchecked(witness); SLOW_ASSERT(IsSortedNoDuplicates()); } @@ -5026,24 +6006,6 @@ bool String::LooksValid() { } -int String::Utf8Length() { - if (IsAsciiRepresentation()) return length(); - // Attempt to flatten before accessing the string. It probably - // doesn't make Utf8Length faster, but it is very likely that - // the string will be accessed later (for example by WriteUtf8) - // so it's still a good idea. - Heap* heap = GetHeap(); - TryFlatten(); - Access<StringInputBuffer> buffer( - heap->isolate()->objects_string_input_buffer()); - buffer->Reset(0, this); - int result = 0; - while (buffer->has_more()) - result += unibrow::Utf8::Length(buffer->GetNext()); - return result; -} - - String::FlatContent String::GetFlatContent() { int length = this->length(); StringShape shape(this); @@ -5070,7 +6032,7 @@ String::FlatContent String::GetFlatContent() { if (shape.representation_tag() == kSeqStringTag) { start = SeqAsciiString::cast(string)->GetChars(); } else { - start = ExternalAsciiString::cast(string)->resource()->data(); + start = ExternalAsciiString::cast(string)->GetChars(); } return FlatContent(Vector<const char>(start + offset, length)); } else { @@ -5079,7 +6041,7 @@ String::FlatContent String::GetFlatContent() { if (shape.representation_tag() == kSeqStringTag) { start = SeqTwoByteString::cast(string)->GetChars(); } else { - start = ExternalTwoByteString::cast(string)->resource()->data(); + start = ExternalTwoByteString::cast(string)->GetChars(); } return FlatContent(Vector<const uc16>(start + offset, length)); } @@ -5105,12 +6067,9 @@ SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, buffer->Reset(offset, this); int character_position = offset; int utf8_bytes = 0; - while (buffer->has_more()) { + while (buffer->has_more() && character_position++ < offset + length) { uint16_t character = buffer->GetNext(); - if (character_position < offset + length) { - utf8_bytes += unibrow::Utf8::Length(character); - } - character_position++; + utf8_bytes += unibrow::Utf8::Length(character); } if (length_return) { @@ -5124,16 +6083,13 @@ SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, buffer->Seek(offset); character_position = offset; int utf8_byte_position = 0; - while (buffer->has_more()) { + while (buffer->has_more() && character_position++ < offset + length) { uint16_t character = buffer->GetNext(); - if (character_position < offset + length) { - if (allow_nulls == DISALLOW_NULLS && character == 0) { - character = ' '; - } - utf8_byte_position += - unibrow::Utf8::Encode(result + utf8_byte_position, character); + if (allow_nulls == DISALLOW_NULLS && character == 0) { + character = ' '; } - character_position++; + utf8_byte_position += + unibrow::Utf8::Encode(result + utf8_byte_position, character); } result[utf8_byte_position] = 0; return SmartArrayPointer<char>(result); @@ -5318,44 +6274,26 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, } -uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) { - ASSERT(index >= 0 && index < length()); - return resource()->data()[index]; -} - - const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock( unsigned* remaining, unsigned* offset_ptr, unsigned max_chars) { // Cast const char* to unibrow::byte* (signedness difference). const unibrow::byte* b = - reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr; + reinterpret_cast<const unibrow::byte*>(GetChars()) + *offset_ptr; *remaining = max_chars; *offset_ptr += max_chars; return b; } -const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData( - unsigned start) { - return resource()->data() + start; -} - - -uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) { - ASSERT(index >= 0 && index < length()); - return resource()->data()[index]; -} - - void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( ReadBlockBuffer* rbb, unsigned* offset_ptr, unsigned max_chars) { unsigned chars_read = 0; unsigned offset = *offset_ptr; - const uint16_t* data = resource()->data(); + const uint16_t* data = GetChars(); while (chars_read < max_chars) { uint16_t c = data[offset]; if (c <= kMaxAsciiCharCode) { @@ -5401,9 +6339,7 @@ void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer( unsigned max_chars) { unsigned capacity = rbb->capacity - rbb->cursor; if (max_chars > capacity) max_chars = capacity; - memcpy(rbb->util_buffer + rbb->cursor, - resource()->data() + *offset_ptr, - max_chars); + memcpy(rbb->util_buffer + rbb->cursor, GetChars() + *offset_ptr, max_chars); rbb->remaining += max_chars; *offset_ptr += max_chars; rbb->cursor += max_chars; @@ -5467,6 +6403,73 @@ const unibrow::byte* String::ReadBlock(String* input, } +// This method determines the type of string involved and then gets the UTF8 +// length of the string. It doesn't flatten the string and has log(n) recursion +// for a string of length n. +int String::Utf8Length(String* input, int from, int to) { + if (from == to) return 0; + int total = 0; + while (true) { + if (input->IsAsciiRepresentation()) return total + to - from; + switch (StringShape(input).representation_tag()) { + case kConsStringTag: { + ConsString* str = ConsString::cast(input); + String* first = str->first(); + String* second = str->second(); + int first_length = first->length(); + if (first_length - from < to - first_length) { + if (first_length > from) { + // Left hand side is shorter. + total += Utf8Length(first, from, first_length); + input = second; + from = 0; + to -= first_length; + } else { + // We only need the right hand side. + input = second; + from -= first_length; + to -= first_length; + } + } else { + if (first_length <= to) { + // Right hand side is shorter. + total += Utf8Length(second, 0, to - first_length); + input = first; + to = first_length; + } else { + // We only need the left hand side. + input = first; + } + } + continue; + } + case kExternalStringTag: + case kSeqStringTag: { + Vector<const uc16> vector = input->GetFlatContent().ToUC16Vector(); + const uc16* p = vector.start(); + for (int i = from; i < to; i++) { + total += unibrow::Utf8::Length(p[i]); + } + return total; + } + case kSlicedStringTag: { + SlicedString* str = SlicedString::cast(input); + int offset = str->offset(); + input = str->parent(); + from += offset; + to += offset; + continue; + } + default: + break; + } + UNREACHABLE(); + return 0; + } + return 0; +} + + void Relocatable::PostGarbageCollectionProcessing() { Isolate* isolate = Isolate::Current(); Relocatable* current = isolate->relocatable_top(); @@ -5778,13 +6781,13 @@ void String::WriteToFlat(String* src, switch (StringShape(source).full_representation_tag()) { case kAsciiStringTag | kExternalStringTag: { CopyChars(sink, - ExternalAsciiString::cast(source)->resource()->data() + from, + ExternalAsciiString::cast(source)->GetChars() + from, to - from); return; } case kTwoByteStringTag | kExternalStringTag: { const uc16* data = - ExternalTwoByteString::cast(source)->resource()->data(); + ExternalTwoByteString::cast(source)->GetChars(); CopyChars(sink, data + from, to - from); @@ -6012,7 +7015,7 @@ bool String::MarkAsUndetectable() { if (StringShape(this).IsSymbol()) return false; Map* map = this->map(); - Heap* heap = map->heap(); + Heap* heap = GetHeap(); if (map == heap->string_map()) { this->set_map(heap->undetectable_string_map()); return true; @@ -6220,66 +7223,155 @@ void String::PrintOn(FILE* file) { } +void Map::CreateOneBackPointer(Object* transition_target) { + if (!transition_target->IsMap()) return; + Map* target = Map::cast(transition_target); +#ifdef DEBUG + // Verify target. + Object* source_prototype = prototype(); + Object* target_prototype = target->prototype(); + ASSERT(source_prototype->IsJSReceiver() || + source_prototype->IsMap() || + source_prototype->IsNull()); + ASSERT(target_prototype->IsJSReceiver() || + target_prototype->IsNull()); + ASSERT(source_prototype->IsMap() || + source_prototype == target_prototype); +#endif + // Point target back to source. set_prototype() will not let us set + // the prototype to a map, as we do here. + *RawField(target, kPrototypeOffset) = this; +} + + void Map::CreateBackPointers() { DescriptorArray* descriptors = instance_descriptors(); for (int i = 0; i < descriptors->number_of_descriptors(); i++) { - if (descriptors->GetType(i) == MAP_TRANSITION || - descriptors->GetType(i) == ELEMENTS_TRANSITION || - descriptors->GetType(i) == CONSTANT_TRANSITION) { - // Get target. - Map* target = Map::cast(descriptors->GetValue(i)); -#ifdef DEBUG - // Verify target. - Object* source_prototype = prototype(); - Object* target_prototype = target->prototype(); - ASSERT(source_prototype->IsJSObject() || - source_prototype->IsMap() || - source_prototype->IsNull()); - ASSERT(target_prototype->IsJSObject() || - target_prototype->IsNull()); - ASSERT(source_prototype->IsMap() || - source_prototype == target_prototype); -#endif - // Point target back to source. set_prototype() will not let us set - // the prototype to a map, as we do here. - *RawField(target, kPrototypeOffset) = this; + switch (descriptors->GetType(i)) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + CreateOneBackPointer(descriptors->GetValue(i)); + break; + case ELEMENTS_TRANSITION: { + Object* object = descriptors->GetValue(i); + if (object->IsMap()) { + CreateOneBackPointer(object); + } else { + FixedArray* array = FixedArray::cast(object); + for (int i = 0; i < array->length(); ++i) { + CreateOneBackPointer(array->get(i)); + } + } + break; + } + case CALLBACKS: { + Object* object = descriptors->GetValue(i); + if (object->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(object); + CreateOneBackPointer(accessors->getter()); + CreateOneBackPointer(accessors->setter()); + } + break; + } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + break; } } } +bool Map::RestoreOneBackPointer(Object* object, + Object* real_prototype, + bool* keep_entry) { + if (!object->IsMap()) return false; + Map* map = Map::cast(object); + if (Marking::MarkBitFrom(map).Get()) { + *keep_entry = true; + return false; + } + ASSERT(map->prototype() == this || map->prototype() == real_prototype); + // Getter prototype() is read-only, set_prototype() has side effects. + *RawField(map, Map::kPrototypeOffset) = real_prototype; + return true; +} + + void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { - // Live DescriptorArray objects will be marked, so we must use - // low-level accessors to get and modify their data. - DescriptorArray* d = reinterpret_cast<DescriptorArray*>( + DescriptorArray* d = DescriptorArray::cast( *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset)); if (d->IsEmpty()) return; Smi* NullDescriptorDetails = PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi(); - FixedArray* contents = reinterpret_cast<FixedArray*>( + FixedArray* contents = FixedArray::cast( d->get(DescriptorArray::kContentArrayIndex)); ASSERT(contents->length() >= 2); for (int i = 0; i < contents->length(); i += 2) { - // If the pair (value, details) is a map transition, - // check if the target is live. If not, null the descriptor. - // Also drop the back pointer for that map transition, so that this - // map is not reached again by following a back pointer from a - // non-live object. + // If the pair (value, details) is a map transition, check if the target is + // live. If not, null the descriptor. Also drop the back pointer for that + // map transition, so that this map is not reached again by following a back + // pointer from a non-live object. + bool keep_entry = false; PropertyDetails details(Smi::cast(contents->get(i + 1))); - if (details.type() == MAP_TRANSITION || - details.type() == ELEMENTS_TRANSITION || - details.type() == CONSTANT_TRANSITION) { - Map* target = reinterpret_cast<Map*>(contents->get(i)); - ASSERT(target->IsHeapObject()); - if (!target->IsMarked()) { - ASSERT(target->IsMap()); - contents->set_unchecked(i + 1, NullDescriptorDetails); - contents->set_null_unchecked(heap, i); - ASSERT(target->prototype() == this || - target->prototype() == real_prototype); - // Getter prototype() is read-only, set_prototype() has side effects. - *RawField(target, Map::kPrototypeOffset) = real_prototype; + switch (details.type()) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + RestoreOneBackPointer(contents->get(i), real_prototype, &keep_entry); + break; + case ELEMENTS_TRANSITION: { + Object* object = contents->get(i); + if (object->IsMap()) { + RestoreOneBackPointer(object, real_prototype, &keep_entry); + } else { + FixedArray* array = FixedArray::cast(object); + for (int j = 0; j < array->length(); ++j) { + if (RestoreOneBackPointer(array->get(j), + real_prototype, + &keep_entry)) { + array->set_undefined(j); + } + } + } + break; + } + case CALLBACKS: { + Object* object = contents->get(i); + if (object->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(object); + if (RestoreOneBackPointer(accessors->getter(), + real_prototype, + &keep_entry)) { + accessors->set_getter(heap->the_hole_value()); + } + if (RestoreOneBackPointer(accessors->setter(), + real_prototype, + &keep_entry)) { + accessors->set_setter(heap->the_hole_value()); + } + } else { + keep_entry = true; + } + break; } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + keep_entry = true; + break; + } + // Make sure that an entry containing only dead transitions gets collected. + // What we *really* want to do here is removing this entry completely, but + // for technical reasons we can't do this, so we zero it out instead. + if (!keep_entry) { + contents->set_unchecked(i + 1, NullDescriptorDetails); + contents->set_null_unchecked(heap, i); } } } @@ -6337,6 +7429,57 @@ void JSFunction::MarkForLazyRecompilation() { } +bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared, + ClearExceptionFlag flag) { + return shared->is_compiled() || CompileLazy(shared, flag); +} + + +static bool CompileLazyHelper(CompilationInfo* info, + ClearExceptionFlag flag) { + // Compile the source information to a code object. + ASSERT(info->IsOptimizing() || !info->shared_info()->is_compiled()); + ASSERT(!info->isolate()->has_pending_exception()); + bool result = Compiler::CompileLazy(info); + ASSERT(result != Isolate::Current()->has_pending_exception()); + if (!result && flag == CLEAR_EXCEPTION) { + info->isolate()->clear_pending_exception(); + } + return result; +} + + +bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared, + ClearExceptionFlag flag) { + CompilationInfo info(shared); + return CompileLazyHelper(&info, flag); +} + + +bool JSFunction::CompileLazy(Handle<JSFunction> function, + ClearExceptionFlag flag) { + bool result = true; + if (function->shared()->is_compiled()) { + function->ReplaceCode(function->shared()->code()); + function->shared()->set_code_age(0); + } else { + CompilationInfo info(function); + result = CompileLazyHelper(&info, flag); + ASSERT(!result || function->is_compiled()); + } + return result; +} + + +bool JSFunction::CompileOptimized(Handle<JSFunction> function, + int osr_ast_id, + ClearExceptionFlag flag) { + CompilationInfo info(function); + info.SetOptimizing(osr_ast_id); + return CompileLazyHelper(&info, flag); +} + + bool JSFunction::IsInlineable() { if (IsBuiltin()) return false; SharedFunctionInfo* shared_info = shared(); @@ -6351,11 +7494,19 @@ bool JSFunction::IsInlineable() { } -Object* JSFunction::SetInstancePrototype(Object* value) { +MaybeObject* JSFunction::SetInstancePrototype(Object* value) { ASSERT(value->IsJSObject()); Heap* heap = GetHeap(); if (has_initial_map()) { - initial_map()->set_prototype(value); + // If the function has allocated the initial map + // replace it with a copy containing the new prototype. + Map* new_map; + MaybeObject* maybe_new_map = initial_map()->CopyDropTransitions(); + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + new_map->set_prototype(value); + MaybeObject* maybe_object = + set_initial_map_and_cache_transitions(new_map); + if (maybe_object->IsFailure()) return maybe_object; } else { // Put the value in the initial map field until an initial map is // needed. At that point, a new initial map is created and the @@ -6384,7 +7535,7 @@ MaybeObject* JSFunction::SetPrototype(Object* value) { if (!maybe_new_map->ToObject(&new_object)) return maybe_new_map; } Map* new_map = Map::cast(new_object); - Heap* heap = new_map->heap(); + Heap* heap = new_map->GetHeap(); set_map(new_map); new_map->set_constructor(value); new_map->set_non_instance_prototype(true); @@ -6401,21 +7552,21 @@ MaybeObject* JSFunction::SetPrototype(Object* value) { Object* JSFunction::RemovePrototype() { Context* global_context = context()->global_context(); - Map* no_prototype_map = shared()->strict_mode() - ? global_context->strict_mode_function_without_prototype_map() - : global_context->function_without_prototype_map(); + Map* no_prototype_map = shared()->is_classic_mode() + ? global_context->function_without_prototype_map() + : global_context->strict_mode_function_without_prototype_map(); if (map() == no_prototype_map) { // Be idempotent. return this; } - ASSERT(!shared()->strict_mode() || - map() == global_context->strict_mode_function_map()); - ASSERT(shared()->strict_mode() || map() == global_context->function_map()); + ASSERT(map() == (shared()->is_classic_mode() + ? global_context->function_map() + : global_context->strict_mode_function_map())); set_map(no_prototype_map); - set_prototype_or_initial_map(no_prototype_map->heap()->the_hole_value()); + set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value()); return this; } @@ -6465,13 +7616,10 @@ bool SharedFunctionInfo::HasSourceCode() { } -Object* SharedFunctionInfo::GetSourceCode() { - Isolate* isolate = GetIsolate(); - if (!HasSourceCode()) return isolate->heap()->undefined_value(); - HandleScope scope(isolate); - Object* source = Script::cast(script())->source(); - return *SubString(Handle<String>(String::cast(source), isolate), - start_position(), end_position()); +Handle<Object> SharedFunctionInfo::GetSourceCode() { + if (!HasSourceCode()) return GetIsolate()->factory()->undefined_value(); + Handle<String> source(String::cast(Script::cast(script())->source())); + return SubString(source, start_position(), end_position()); } @@ -6519,10 +7667,10 @@ bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) { obj = obj->GetPrototype()) { JSObject* js_object = JSObject::cast(obj); for (int i = 0; i < this_property_assignments_count(); i++) { - LookupResult result; + LookupResult result(heap->isolate()); String* name = GetThisPropertyAssignmentName(i); js_object->LocalLookupRealNamedProperty(name, &result); - if (result.IsProperty() && result.type() == CALLBACKS) { + if (result.IsFound() && result.type() == CALLBACKS) { return false; } } @@ -6708,6 +7856,8 @@ bool SharedFunctionInfo::VerifyBailoutId(int id) { void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) { ASSERT(!IsInobjectSlackTrackingInProgress()); + if (!FLAG_clever_optimizations) return; + // Only initiate the tracking the first time. if (live_objects_may_exist()) return; set_live_objects_may_exist(true); @@ -6723,7 +7873,7 @@ void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) { set_construction_count(kGenerousAllocationCount); } set_initial_map(map); - Builtins* builtins = map->heap()->isolate()->builtins(); + Builtins* builtins = map->GetHeap()->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), construct_stub()); set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); @@ -6743,8 +7893,9 @@ void SharedFunctionInfo::DetachInitialMap() { // then StartInobjectTracking will be called again the next time the // constructor is called. The countdown will continue and (possibly after // several more GCs) CompleteInobjectSlackTracking will eventually be called. - set_initial_map(map->heap()->raw_unchecked_undefined_value()); - Builtins* builtins = map->heap()->isolate()->builtins(); + Heap* heap = map->GetHeap(); + set_initial_map(heap->raw_unchecked_undefined_value()); + Builtins* builtins = heap->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), *RawField(this, kConstructStubOffset)); set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); @@ -6760,7 +7911,7 @@ void SharedFunctionInfo::AttachInitialMap(Map* map) { // Resume inobject slack tracking. set_initial_map(map); - Builtins* builtins = map->heap()->isolate()->builtins(); + Builtins* builtins = map->GetHeap()->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), *RawField(this, kConstructStubOffset)); set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); @@ -6792,7 +7943,7 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() { ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress()); Map* map = Map::cast(initial_map()); - Heap* heap = map->heap(); + Heap* heap = map->GetHeap(); set_initial_map(heap->undefined_value()); Builtins* builtins = heap->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), @@ -6812,6 +7963,22 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() { } +#define DECLARE_TAG(ignore1, name, ignore2) name, +const char* const VisitorSynchronization::kTags[ + VisitorSynchronization::kNumberOfSyncTags] = { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) +}; +#undef DECLARE_TAG + + +#define DECLARE_TAG(ignore1, ignore2, name) name, +const char* const VisitorSynchronization::kTagNames[ + VisitorSynchronization::kNumberOfSyncTags] = { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) +}; +#undef DECLARE_TAG + + void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); @@ -6854,8 +8021,18 @@ void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) { } +void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) { + ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); + VisitPointer(rinfo->target_object_address()); +} + +void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) { + Address* p = rinfo->target_reference_address(); + VisitExternalReferences(p, p + 1); +} + void Code::InvalidateRelocation() { - set_relocation_info(heap()->empty_byte_array()); + set_relocation_info(GetHeap()->empty_byte_array()); } @@ -6868,6 +8045,8 @@ void Code::Relocate(intptr_t delta) { void Code::CopyFrom(const CodeDesc& desc) { + ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT); + // copy code memmove(instruction_start(), desc.buffer, desc.instr_size); @@ -6887,16 +8066,17 @@ void Code::CopyFrom(const CodeDesc& desc) { RelocInfo::Mode mode = it.rinfo()->rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { Handle<Object> p = it.rinfo()->target_object_handle(origin); - it.rinfo()->set_target_object(*p); + it.rinfo()->set_target_object(*p, SKIP_WRITE_BARRIER); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { - Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle(); - it.rinfo()->set_target_cell(*cell); + Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle(); + it.rinfo()->set_target_cell(*cell, SKIP_WRITE_BARRIER); } else if (RelocInfo::IsCodeTarget(mode)) { // rewrite code handles in inline cache targets to direct // pointers to the first instruction in the code object Handle<Object> p = it.rinfo()->target_object_handle(origin); Code* code = Code::cast(*p); - it.rinfo()->set_target_address(code->instruction_start()); + it.rinfo()->set_target_address(code->instruction_start(), + SKIP_WRITE_BARRIER); } else { it.rinfo()->apply(delta); } @@ -7015,8 +8195,11 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { static_cast<Translation::Opcode>(iterator.Next()); ASSERT(Translation::BEGIN == opcode); int frame_count = iterator.Next(); - PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode), - frame_count); + int jsframe_count = iterator.Next(); + PrintF(out, " %s {frame count=%d, js frame count=%d}\n", + Translation::StringFor(opcode), + frame_count, + jsframe_count); while (iterator.HasNext() && Translation::BEGIN != @@ -7028,7 +8211,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { UNREACHABLE(); break; - case Translation::FRAME: { + case Translation::JS_FRAME: { int ast_id = iterator.Next(); int function_id = iterator.Next(); JSFunction* function = @@ -7040,6 +8223,12 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { break; } + case Translation::ARGUMENTS_ADAPTOR_FRAME: { + unsigned height = iterator.Next(); + PrintF(out, "{arguments adaptor, height=%d}", height); + break; + } + case Translation::DUPLICATE: break; @@ -7163,7 +8352,7 @@ const char* Code::PropertyType2String(PropertyType type) { case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; } - UNREACHABLE(); + UNREACHABLE(); // keep the compiler happy return NULL; } @@ -7275,9 +8464,20 @@ void Code::Disassemble(const char* name, FILE* out) { static void CopyFastElementsToFast(FixedArray* source, FixedArray* destination, WriteBarrierMode mode) { - uint32_t count = static_cast<uint32_t>(source->length()); - for (uint32_t i = 0; i < count; ++i) { - destination->set(i, source->get(i), mode); + int count = source->length(); + int copy_size = Min(count, destination->length()); + if (mode == SKIP_WRITE_BARRIER || + !Page::FromAddress(destination->address())->IsFlagSet( + MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)) { + Address to = destination->address() + FixedArray::kHeaderSize; + Address from = source->address() + FixedArray::kHeaderSize; + memcpy(reinterpret_cast<void*>(to), + reinterpret_cast<void*>(from), + kPointerSize * copy_size); + } else { + for (int i = 0; i < copy_size; ++i) { + destination->set(i, source->get(i), mode); + } } } @@ -7285,18 +8485,23 @@ static void CopyFastElementsToFast(FixedArray* source, static void CopySlowElementsToFast(SeededNumberDictionary* source, FixedArray* destination, WriteBarrierMode mode) { + int destination_length = destination->length(); for (int i = 0; i < source->Capacity(); ++i) { Object* key = source->KeyAt(i); if (key->IsNumber()) { uint32_t entry = static_cast<uint32_t>(key->Number()); - destination->set(entry, source->ValueAt(i), mode); + if (entry < static_cast<uint32_t>(destination_length)) { + destination->set(entry, source->ValueAt(i), mode); + } } } } -MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, - int length) { +MaybeObject* JSObject::SetFastElementsCapacityAndLength( + int capacity, + int length, + SetFastElementsCapacityMode set_capacity_mode) { Heap* heap = GetHeap(); // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); @@ -7313,28 +8518,40 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, Map* new_map = NULL; if (elements()->map() != heap->non_strict_arguments_elements_map()) { Object* object; - MaybeObject* maybe = map()->GetFastElementsMap(); + // The resized array has FAST_SMI_ONLY_ELEMENTS if the capacity mode forces + // it, or if it's allowed and the old elements array contained only SMIs. + bool has_fast_smi_only_elements = + (set_capacity_mode == kForceSmiOnlyElements) || + ((set_capacity_mode == kAllowSmiOnlyElements) && + (elements()->map()->has_fast_smi_only_elements() || + elements() == heap->empty_fixed_array())); + ElementsKind elements_kind = has_fast_smi_only_elements + ? FAST_SMI_ONLY_ELEMENTS + : FAST_ELEMENTS; + MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), elements_kind); if (!maybe->ToObject(&object)) return maybe; new_map = Map::cast(object); } - switch (GetElementsKind()) { + FixedArrayBase* old_elements_raw = elements(); + ElementsKind elements_kind = GetElementsKind(); + switch (elements_kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { AssertNoAllocation no_gc; - WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); - CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode); - set_map(new_map); - set_elements(new_elements); + WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc)); + CopyFastElementsToFast(FixedArray::cast(old_elements_raw), + new_elements, mode); + set_map_and_elements(new_map, new_elements); break; } case DICTIONARY_ELEMENTS: { AssertNoAllocation no_gc; WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); - CopySlowElementsToFast(SeededNumberDictionary::cast(elements()), + CopySlowElementsToFast(SeededNumberDictionary::cast(old_elements_raw), new_elements, mode); - set_map(new_map); - set_elements(new_elements); + set_map_and_elements(new_map, new_elements); break; } case NON_STRICT_ARGUMENTS_ELEMENTS: { @@ -7342,7 +8559,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); // The object's map and the parameter map are unchanged, the unaliased // arguments are copied to the new backing store. - FixedArray* parameter_map = FixedArray::cast(elements()); + FixedArray* parameter_map = FixedArray::cast(old_elements_raw); FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { CopySlowElementsToFast(SeededNumberDictionary::cast(arguments), @@ -7355,7 +8572,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, break; } case FAST_DOUBLE_ELEMENTS: { - FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements()); + FixedDoubleArray* old_elements = FixedDoubleArray::cast(old_elements_raw); uint32_t old_length = static_cast<uint32_t>(old_elements->length()); // Fill out the new array with this content and array holes. for (uint32_t i = 0; i < old_length; i++) { @@ -7393,6 +8610,11 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, break; } + if (FLAG_trace_elements_transitions) { + PrintElementsTransition(stdout, elements_kind, old_elements_raw, + GetElementsKind(), new_elements); + } + // Update the length if necessary. if (IsJSArray()) { JSArray::cast(this)->set_length(Smi::FromInt(length)); @@ -7416,23 +8638,27 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( } FixedDoubleArray* elems = FixedDoubleArray::cast(obj); - { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap(); + { MaybeObject* maybe_obj = + GetElementsTransitionMap(heap->isolate(), FAST_DOUBLE_ELEMENTS); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); + FixedArrayBase* old_elements = elements(); + ElementsKind elements_kind(GetElementsKind()); AssertNoAllocation no_gc; - switch (GetElementsKind()) { + switch (elements_kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { - elems->Initialize(FixedArray::cast(elements())); + elems->Initialize(FixedArray::cast(old_elements)); break; } case FAST_DOUBLE_ELEMENTS: { - elems->Initialize(FixedDoubleArray::cast(elements())); + elems->Initialize(FixedDoubleArray::cast(old_elements)); break; } case DICTIONARY_ELEMENTS: { - elems->Initialize(SeededNumberDictionary::cast(elements())); + elems->Initialize(SeededNumberDictionary::cast(old_elements)); break; } default: @@ -7440,6 +8666,11 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( break; } + if (FLAG_trace_elements_transitions) { + PrintElementsTransition(stdout, elements_kind, old_elements, + FAST_DOUBLE_ELEMENTS, elems); + } + ASSERT(new_map->has_fast_double_elements()); set_map(new_map); ASSERT(elems->IsFixedDoubleArray()); @@ -7453,53 +8684,6 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( } -MaybeObject* JSObject::SetSlowElements(Object* len) { - // We should never end in here with a pixel or external array. - ASSERT(!HasExternalArrayElements()); - - uint32_t new_length = static_cast<uint32_t>(len->Number()); - - switch (GetElementsKind()) { - case FAST_ELEMENTS: { - case FAST_DOUBLE_ELEMENTS: - // Make sure we never try to shrink dense arrays into sparse arrays. - ASSERT(static_cast<uint32_t>( - FixedArrayBase::cast(elements())->length()) <= new_length); - MaybeObject* result = NormalizeElements(); - if (result->IsFailure()) return result; - - // Update length for JSArrays. - if (IsJSArray()) JSArray::cast(this)->set_length(len); - break; - } - case DICTIONARY_ELEMENTS: { - if (IsJSArray()) { - uint32_t old_length = - static_cast<uint32_t>(JSArray::cast(this)->length()->Number()); - element_dictionary()->RemoveNumberEntries(new_length, old_length), - JSArray::cast(this)->set_length(len); - } - break; - } - case NON_STRICT_ARGUMENTS_ELEMENTS: - UNIMPLEMENTED(); - break; - case EXTERNAL_BYTE_ELEMENTS: - case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case EXTERNAL_SHORT_ELEMENTS: - case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case EXTERNAL_INT_ELEMENTS: - case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: - case EXTERNAL_DOUBLE_ELEMENTS: - case EXTERNAL_PIXEL_ELEMENTS: - UNREACHABLE(); - break; - } - return this; -} - - MaybeObject* JSArray::Initialize(int capacity) { Heap* heap = GetHeap(); ASSERT(capacity >= 0); @@ -7520,162 +8704,15 @@ MaybeObject* JSArray::Initialize(int capacity) { void JSArray::Expand(int required_size) { - Handle<JSArray> self(this); - Handle<FixedArray> old_backing(FixedArray::cast(elements())); - int old_size = old_backing->length(); - int new_size = required_size > old_size ? required_size : old_size; - Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size); - // Can't use this any more now because we may have had a GC! - for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i)); - self->SetContent(*new_backing); -} - - -static Failure* ArrayLengthRangeError(Heap* heap) { - HandleScope scope(heap->isolate()); - return heap->isolate()->Throw( - *FACTORY->NewRangeError("invalid_array_length", - HandleVector<Object>(NULL, 0))); + GetIsolate()->factory()->SetElementsCapacityAndLength( + Handle<JSArray>(this), required_size, required_size); } -MaybeObject* JSObject::SetElementsLength(Object* len) { +MaybeObject* JSArray::SetElementsLength(Object* len) { // We should never end in here with a pixel or external array. ASSERT(AllowsSetElementsLength()); - - MaybeObject* maybe_smi_length = len->ToSmi(); - Object* smi_length = Smi::FromInt(0); - if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) { - const int value = Smi::cast(smi_length)->value(); - if (value < 0) return ArrayLengthRangeError(GetHeap()); - ElementsKind elements_kind = GetElementsKind(); - switch (elements_kind) { - case FAST_ELEMENTS: - case FAST_DOUBLE_ELEMENTS: { - int old_capacity = FixedArrayBase::cast(elements())->length(); - if (value <= old_capacity) { - if (IsJSArray()) { - Object* obj; - if (elements_kind == FAST_ELEMENTS) { - MaybeObject* maybe_obj = EnsureWritableFastElements(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - if (2 * value <= old_capacity) { - // If more than half the elements won't be used, trim the array. - if (value == 0) { - initialize_elements(); - } else { - Address filler_start; - int filler_size; - if (GetElementsKind() == FAST_ELEMENTS) { - FixedArray* fast_elements = FixedArray::cast(elements()); - fast_elements->set_length(value); - filler_start = fast_elements->address() + - FixedArray::OffsetOfElementAt(value); - filler_size = (old_capacity - value) * kPointerSize; - } else { - ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); - FixedDoubleArray* fast_double_elements = - FixedDoubleArray::cast(elements()); - fast_double_elements->set_length(value); - filler_start = fast_double_elements->address() + - FixedDoubleArray::OffsetOfElementAt(value); - filler_size = (old_capacity - value) * kDoubleSize; - } - GetHeap()->CreateFillerObjectAt(filler_start, filler_size); - } - } else { - // Otherwise, fill the unused tail with holes. - int old_length = FastD2I(JSArray::cast(this)->length()->Number()); - if (GetElementsKind() == FAST_ELEMENTS) { - FixedArray* fast_elements = FixedArray::cast(elements()); - for (int i = value; i < old_length; i++) { - fast_elements->set_the_hole(i); - } - } else { - ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); - FixedDoubleArray* fast_double_elements = - FixedDoubleArray::cast(elements()); - for (int i = value; i < old_length; i++) { - fast_double_elements->set_the_hole(i); - } - } - } - JSArray::cast(this)->set_length(Smi::cast(smi_length)); - } - return this; - } - int min = NewElementsCapacity(old_capacity); - int new_capacity = value > min ? value : min; - if (!ShouldConvertToSlowElements(new_capacity)) { - MaybeObject* result; - if (GetElementsKind() == FAST_ELEMENTS) { - result = SetFastElementsCapacityAndLength(new_capacity, value); - } else { - ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); - result = SetFastDoubleElementsCapacityAndLength(new_capacity, - value); - } - if (result->IsFailure()) return result; - return this; - } - break; - } - case DICTIONARY_ELEMENTS: { - if (IsJSArray()) { - if (value == 0) { - // If the length of a slow array is reset to zero, we clear - // the array and flush backing storage. This has the added - // benefit that the array returns to fast mode. - Object* obj; - { MaybeObject* maybe_obj = ResetElements(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - } else { - // Remove deleted elements. - uint32_t old_length = - static_cast<uint32_t>(JSArray::cast(this)->length()->Number()); - element_dictionary()->RemoveNumberEntries(value, old_length); - } - JSArray::cast(this)->set_length(Smi::cast(smi_length)); - } - return this; - } - case NON_STRICT_ARGUMENTS_ELEMENTS: - case EXTERNAL_BYTE_ELEMENTS: - case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case EXTERNAL_SHORT_ELEMENTS: - case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case EXTERNAL_INT_ELEMENTS: - case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: - case EXTERNAL_DOUBLE_ELEMENTS: - case EXTERNAL_PIXEL_ELEMENTS: - UNREACHABLE(); - break; - } - } - - // General slow case. - if (len->IsNumber()) { - uint32_t length; - if (len->ToArrayIndex(&length)) { - return SetSlowElements(len); - } else { - return ArrayLengthRangeError(GetHeap()); - } - } - - // len is not a number so make the array size one and - // set only element to len. - Object* obj; - { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - FixedArray::cast(obj)->set(0, len); - if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1)); - set_elements(FixedArray::cast(obj)); - return this; + return GetElementsAccessor()->SetLength(this, len); } @@ -7718,7 +8755,7 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) { FixedArray* new_cache; // Grow array by factor 2 over and above what we need. { MaybeObject* maybe_cache = - heap()->AllocateFixedArray(transitions * 2 * step + header); + GetHeap()->AllocateFixedArray(transitions * 2 * step + header); if (!maybe_cache->To<FixedArray>(&new_cache)) return maybe_cache; } @@ -7771,7 +8808,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, // It is sufficient to validate that the receiver is not in the new prototype // chain. for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) { - if (JSObject::cast(pt) == this) { + if (JSReceiver::cast(pt) == this) { // Cycle detected. HandleScope scope(heap->isolate()); return heap->isolate()->Throw( @@ -7786,8 +8823,8 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, // hidden and set the new prototype on that object. Object* current_proto = real_receiver->GetPrototype(); while (current_proto->IsJSObject() && - JSObject::cast(current_proto)->map()->is_hidden_prototype()) { - real_receiver = JSObject::cast(current_proto); + JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) { + real_receiver = JSReceiver::cast(current_proto); current_proto = current_proto->GetPrototype(); } } @@ -7820,8 +8857,22 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, } +MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, + uint32_t first_arg, + uint32_t arg_count, + EnsureElementsMode mode) { + // Elements in |Arguments| are ordered backwards (because they're on the + // stack), but the method that's called here iterates over them in forward + // direction. + return EnsureCanContainElements( + args->arguments() - first_arg - (arg_count - 1), + arg_count, mode); +} + + bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast<uint32_t> @@ -7882,6 +8933,11 @@ bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { Object* pt = GetPrototype(); if (pt->IsNull()) return false; + if (pt->IsJSProxy()) { + // We need to follow the spec and simulate a call to [[GetOwnProperty]]. + return JSProxy::cast(pt)->GetElementAttributeWithHandler( + receiver, index) != ABSENT; + } return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); } @@ -7958,6 +9014,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast<uint32_t> @@ -8073,6 +9130,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { ElementsKind kind = GetElementsKind(); switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast<uint32_t> @@ -8139,6 +9197,11 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { Object* pt = GetPrototype(); if (pt->IsNull()) return false; + if (pt->IsJSProxy()) { + // We need to follow the spec and simulate a call to [[GetOwnProperty]]. + return JSProxy::cast(pt)->GetElementAttributeWithHandler( + receiver, index) != ABSENT; + } return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); } @@ -8213,11 +9276,11 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver, } // __defineGetter__ callback - if (structure->IsFixedArray()) { - Object* getter = FixedArray::cast(structure)->get(kGetterIndex); - if (getter->IsJSFunction()) { - return Object::GetPropertyWithDefinedGetter(receiver, - JSFunction::cast(getter)); + if (structure->IsAccessorPair()) { + Object* getter = AccessorPair::cast(structure)->getter(); + if (getter->IsSpecFunction()) { + // TODO(rossberg): nicer would be to cast to some JSCallable here... + return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); } // Getter is not a function. return isolate->heap()->undefined_value(); @@ -8270,10 +9333,11 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure, return *value_handle; } - if (structure->IsFixedArray()) { - Handle<Object> setter(FixedArray::cast(structure)->get(kSetterIndex)); - if (setter->IsJSFunction()) { - return SetPropertyWithDefinedSetter(JSFunction::cast(*setter), value); + if (structure->IsAccessorPair()) { + Handle<Object> setter(AccessorPair::cast(structure)->setter()); + if (setter->IsSpecFunction()) { + // TODO(rossberg): nicer would be to cast to some JSCallable here... + return SetPropertyWithDefinedSetter(JSReceiver::cast(*setter), value); } else { if (strict_mode == kNonStrictMode) { return value; @@ -8323,7 +9387,8 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value, StrictModeFlag strict_mode, bool check_prototype) { - ASSERT(HasFastElements() || HasFastArgumentsElements()); + ASSERT(HasFastTypeElements() || + HasFastArgumentsElements()); FixedArray* backing_store = FixedArray::cast(elements()); if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) { @@ -8334,10 +9399,10 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, if (!maybe->ToObject(&writable)) return maybe; backing_store = FixedArray::cast(writable); } - uint32_t length = static_cast<uint32_t>(backing_store->length()); + uint32_t capacity = static_cast<uint32_t>(backing_store->length()); if (check_prototype && - (index >= length || backing_store->get(index)->IsTheHole())) { + (index >= capacity || backing_store->get(index)->IsTheHole())) { bool found; MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index, value, @@ -8346,39 +9411,76 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, if (found) return result; } - // Check whether there is extra space in fixed array. - if (index < length) { - backing_store->set(index, value); - if (IsJSArray()) { - // Update the length of the array if needed. - uint32_t array_length = 0; - CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); - if (index >= array_length) { - JSArray::cast(this)->set_length(Smi::FromInt(index + 1)); + uint32_t new_capacity = capacity; + // Check if the length property of this object needs to be updated. + uint32_t array_length = 0; + bool must_update_array_length = false; + if (IsJSArray()) { + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length)); + if (index >= array_length) { + must_update_array_length = true; + array_length = index + 1; + } + } + // Check if the capacity of the backing store needs to be increased, or if + // a transition to slow elements is necessary. + if (index >= capacity) { + bool convert_to_slow = true; + if ((index - capacity) < kMaxGap) { + new_capacity = NewElementsCapacity(index + 1); + ASSERT(new_capacity > index); + if (!ShouldConvertToSlowElements(new_capacity)) { + convert_to_slow = false; } } + if (convert_to_slow) { + MaybeObject* result = NormalizeElements(); + if (result->IsFailure()) return result; + return SetDictionaryElement(index, value, strict_mode, check_prototype); + } + } + // Convert to fast double elements if appropriate. + if (HasFastSmiOnlyElements() && !value->IsSmi() && value->IsNumber()) { + MaybeObject* maybe = + SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); + if (maybe->IsFailure()) return maybe; + FixedDoubleArray::cast(elements())->set(index, value->Number()); return value; } - - // Allow gap in fast case. - if ((index - length) < kMaxGap) { - // Try allocating extra space. - int new_capacity = NewElementsCapacity(index + 1); - if (!ShouldConvertToSlowElements(new_capacity)) { - ASSERT(static_cast<uint32_t>(new_capacity) > index); - Object* new_elements; - MaybeObject* maybe = - SetFastElementsCapacityAndLength(new_capacity, index + 1); - if (!maybe->ToObject(&new_elements)) return maybe; - FixedArray::cast(new_elements)->set(index, value); - return value; - } + // Change elements kind from SMI_ONLY to generic FAST if necessary. + if (HasFastSmiOnlyElements() && !value->IsSmi()) { + MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), + FAST_ELEMENTS); + Map* new_map; + if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map; + set_map(new_map); + if (FLAG_trace_elements_transitions) { + PrintElementsTransition(stdout, FAST_SMI_ONLY_ELEMENTS, elements(), + FAST_ELEMENTS, elements()); + } + } + // Increase backing store capacity if that's been decided previously. + if (new_capacity != capacity) { + Object* new_elements; + SetFastElementsCapacityMode set_capacity_mode = + value->IsSmi() && HasFastSmiOnlyElements() + ? kAllowSmiOnlyElements + : kDontAllowSmiOnlyElements; + MaybeObject* maybe = + SetFastElementsCapacityAndLength(new_capacity, + array_length, + set_capacity_mode); + if (!maybe->ToObject(&new_elements)) return maybe; + FixedArray::cast(new_elements)->set(index, value); + return value; } - - // Otherwise default to slow case. - MaybeObject* result = NormalizeElements(); - if (result->IsFailure()) return result; - return SetDictionaryElement(index, value, strict_mode, check_prototype); + // Finally, set the new element and length. + ASSERT(elements()->IsFixedArray()); + backing_store->set(index, value); + if (must_update_array_length) { + JSArray::cast(this)->set_length(Smi::FromInt(array_length)); + } + return value; } @@ -8472,9 +9574,20 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } else { new_length = dictionary->max_number_key() + 1; } - MaybeObject* result = CanConvertToFastDoubleElements() + SetFastElementsCapacityMode set_capacity_mode = FLAG_smi_only_arrays + ? kAllowSmiOnlyElements + : kDontAllowSmiOnlyElements; + bool has_smi_only_elements = false; + bool should_convert_to_fast_double_elements = + ShouldConvertToFastDoubleElements(&has_smi_only_elements); + if (has_smi_only_elements) { + set_capacity_mode = kForceSmiOnlyElements; + } + MaybeObject* result = should_convert_to_fast_double_elements ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) - : SetFastElementsCapacityAndLength(new_length, new_length); + : SetFastElementsCapacityAndLength(new_length, + new_length, + set_capacity_mode); if (result->IsFailure()) return result; #ifdef DEBUG if (FLAG_trace_normalization) { @@ -8518,10 +9631,15 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( if (IsJSArray()) { CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); } - MaybeObject* maybe_obj = - SetFastElementsCapacityAndLength(elms_length, length); + MaybeObject* maybe_obj = SetFastElementsCapacityAndLength( + elms_length, + length, + kDontAllowSmiOnlyElements); if (!maybe_obj->ToObject(&obj)) return maybe_obj; - return SetFastElement(index, value, strict_mode, check_prototype); + return SetFastElement(index, + value, + strict_mode, + check_prototype); } double double_value = value_is_smi @@ -8572,6 +9690,46 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement( } +MaybeObject* JSReceiver::SetElement(uint32_t index, + Object* value, + StrictModeFlag strict_mode, + bool check_proto) { + return IsJSProxy() + ? JSProxy::cast(this)->SetElementWithHandler(index, value, strict_mode) + : JSObject::cast(this)->SetElement(index, value, strict_mode, check_proto) + ; +} + + +Handle<Object> JSObject::SetOwnElement(Handle<JSObject> object, + uint32_t index, + Handle<Object> value, + StrictModeFlag strict_mode) { + ASSERT(!object->HasExternalArrayElements()); + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->SetElement(index, *value, strict_mode, false), + Object); +} + + +Handle<Object> JSObject::SetElement(Handle<JSObject> object, + uint32_t index, + Handle<Object> value, + StrictModeFlag strict_mode) { + if (object->HasExternalArrayElements()) { + if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) { + bool has_exception; + Handle<Object> number = Execution::ToNumber(value, &has_exception); + if (has_exception) return Handle<Object>(); + value = number; + } + } + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->SetElement(index, *value, strict_mode, true), + Object); +} + + MaybeObject* JSObject::SetElement(uint32_t index, Object* value, StrictModeFlag strict_mode, @@ -8618,6 +9776,7 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, bool check_prototype) { Isolate* isolate = GetIsolate(); switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: return SetFastElement(index, value, strict_mode, check_prototype); case FAST_DOUBLE_ELEMENTS: @@ -8693,6 +9852,79 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, } +Handle<Object> JSObject::TransitionElementsKind(Handle<JSObject> object, + ElementsKind to_kind) { + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->TransitionElementsKind(to_kind), + Object); +} + + +MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { + ElementsKind from_kind = map()->elements_kind(); + + Isolate* isolate = GetIsolate(); + if (from_kind == FAST_SMI_ONLY_ELEMENTS && + (to_kind == FAST_ELEMENTS || + elements() == isolate->heap()->empty_fixed_array())) { + MaybeObject* maybe_new_map = GetElementsTransitionMap(isolate, to_kind); + Map* new_map; + if (!maybe_new_map->To(&new_map)) return maybe_new_map; + set_map(new_map); + if (FLAG_trace_elements_transitions) { + FixedArrayBase* elms = FixedArrayBase::cast(elements()); + PrintElementsTransition(stdout, from_kind, elms, to_kind, elms); + } + return this; + } + + FixedArrayBase* elms = FixedArrayBase::cast(elements()); + uint32_t capacity = static_cast<uint32_t>(elms->length()); + uint32_t length = capacity; + + if (IsJSArray()) { + Object* raw_length = JSArray::cast(this)->length(); + if (raw_length->IsUndefined()) { + // If length is undefined, then JSArray is being initialized and has no + // elements, assume a length of zero. + length = 0; + } else { + CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); + } + } + + if (from_kind == FAST_SMI_ONLY_ELEMENTS && + to_kind == FAST_DOUBLE_ELEMENTS) { + MaybeObject* maybe_result = + SetFastDoubleElementsCapacityAndLength(capacity, length); + if (maybe_result->IsFailure()) return maybe_result; + return this; + } + + if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + MaybeObject* maybe_result = SetFastElementsCapacityAndLength( + capacity, length, kDontAllowSmiOnlyElements); + if (maybe_result->IsFailure()) return maybe_result; + return this; + } + + // This method should never be called for any other case than the ones + // handled above. + UNREACHABLE(); + return GetIsolate()->heap()->null_value(); +} + + +// static +bool Map::IsValidElementsTransition(ElementsKind from_kind, + ElementsKind to_kind) { + return + (from_kind == FAST_SMI_ONLY_ELEMENTS && + (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) || + (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS); +} + + MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index, Object* value) { uint32_t old_len = 0; @@ -8781,6 +10013,7 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { break; } // Fall through. + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: backing_store = FixedArray::cast(backing_store_base); *capacity = backing_store->length(); @@ -8874,18 +10107,26 @@ bool JSObject::ShouldConvertToFastElements() { } -bool JSObject::CanConvertToFastDoubleElements() { +bool JSObject::ShouldConvertToFastDoubleElements( + bool* has_smi_only_elements) { + *has_smi_only_elements = false; if (FLAG_unbox_double_arrays) { ASSERT(HasDictionaryElements()); SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(elements()); + bool found_double = false; for (int i = 0; i < dictionary->Capacity(); i++) { Object* key = dictionary->KeyAt(i); if (key->IsNumber()) { - if (!dictionary->ValueAt(i)->IsNumber()) return false; + Object* value = dictionary->ValueAt(i); + if (!value->IsNumber()) return false; + if (!value->IsSmi()) { + found_double = true; + } } } - return true; + *has_smi_only_elements = !found_double; + return found_double; } else { return false; } @@ -8960,7 +10201,7 @@ MaybeObject* JSObject::GetPropertyPostInterceptor( String* name, PropertyAttributes* attributes) { // Check local property in holder, ignore interceptor. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookupRealNamedProperty(name, &result); if (result.IsProperty()) { return GetProperty(receiver, &result, name, attributes); @@ -8978,7 +10219,7 @@ MaybeObject* JSObject::GetLocalPropertyPostInterceptor( String* name, PropertyAttributes* attributes) { // Check local property in holder, ignore interceptor. - LookupResult result; + LookupResult result(GetIsolate()); LocalLookupRealNamedProperty(name, &result); if (result.IsProperty()) { return GetProperty(receiver, &result, name, attributes); @@ -9029,15 +10270,15 @@ MaybeObject* JSObject::GetPropertyWithInterceptor( bool JSObject::HasRealNamedProperty(String* key) { // Check access rights if needed. + Isolate* isolate = GetIsolate(); if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); + if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); return false; } } - LookupResult result; + LookupResult result(isolate); LocalLookupRealNamedProperty(key, &result); return result.IsProperty() && (result.type() != INTERCEPTOR); } @@ -9057,6 +10298,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) { if (this->IsStringObjectWithCharacterAt(index)) return true; switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? static_cast<uint32_t>( @@ -9105,39 +10347,24 @@ bool JSObject::HasRealElementProperty(uint32_t index) { bool JSObject::HasRealNamedCallbackProperty(String* key) { // Check access rights if needed. + Isolate* isolate = GetIsolate(); if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); + if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); return false; } } - LookupResult result; + LookupResult result(isolate); LocalLookupRealNamedProperty(key, &result); - return result.IsProperty() && (result.type() == CALLBACKS); + return result.IsFound() && (result.type() == CALLBACKS); } int JSObject::NumberOfLocalProperties(PropertyAttributes filter) { - if (HasFastProperties()) { - DescriptorArray* descs = map()->instance_descriptors(); - int result = 0; - for (int i = 0; i < descs->number_of_descriptors(); i++) { - PropertyDetails details(descs->GetDetails(i)); - if (details.IsProperty() && (details.attributes() & filter) == 0) { - result++; - } - } - return result; - } else { - return property_dictionary()->NumberOfElementsFilterAttributes(filter); - } -} - - -int JSObject::NumberOfEnumProperties() { - return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM)); + return HasFastProperties() ? + map()->NumberOfDescribedProperties(filter) : + property_dictionary()->NumberOfElementsFilterAttributes(filter); } @@ -9147,8 +10374,8 @@ void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { set(j, temp); if (this != numbers) { temp = numbers->get(i); - numbers->set(i, numbers->get(j)); - numbers->set(j, temp); + numbers->set(i, Smi::cast(numbers->get(j))); + numbers->set(j, Smi::cast(temp)); } } @@ -9258,7 +10485,7 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { // purpose of this function is to provide reflection information for the object // mirrors. void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) { - ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index)); + ASSERT(storage->length() >= (NumberOfLocalProperties() - index)); if (HasFastProperties()) { DescriptorArray* descs = map()->instance_descriptors(); for (int i = 0; i < descs->number_of_descriptors(); i++) { @@ -9296,6 +10523,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, PropertyAttributes filter) { int counter = 0; switch (GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : @@ -9462,70 +10690,87 @@ class StringSharedKey : public HashTableKey { public: StringSharedKey(String* source, SharedFunctionInfo* shared, - StrictModeFlag strict_mode) + LanguageMode language_mode, + int scope_position) : source_(source), shared_(shared), - strict_mode_(strict_mode) { } + language_mode_(language_mode), + scope_position_(scope_position) { } bool IsMatch(Object* other) { if (!other->IsFixedArray()) return false; - FixedArray* pair = FixedArray::cast(other); - SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0)); + FixedArray* other_array = FixedArray::cast(other); + SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); if (shared != shared_) return false; - StrictModeFlag strict_mode = static_cast<StrictModeFlag>( - Smi::cast(pair->get(2))->value()); - if (strict_mode != strict_mode_) return false; - String* source = String::cast(pair->get(1)); + int language_unchecked = Smi::cast(other_array->get(2))->value(); + ASSERT(language_unchecked == CLASSIC_MODE || + language_unchecked == STRICT_MODE || + language_unchecked == EXTENDED_MODE); + LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); + if (language_mode != language_mode_) return false; + int scope_position = Smi::cast(other_array->get(3))->value(); + if (scope_position != scope_position_) return false; + String* source = String::cast(other_array->get(1)); return source->Equals(source_); } static uint32_t StringSharedHashHelper(String* source, SharedFunctionInfo* shared, - StrictModeFlag strict_mode) { + LanguageMode language_mode, + int scope_position) { uint32_t hash = source->Hash(); if (shared->HasSourceCode()) { // Instead of using the SharedFunctionInfo pointer in the hash // code computation, we use a combination of the hash of the - // script source code and the start and end positions. We do - // this to ensure that the cache entries can survive garbage + // script source code and the start position of the calling scope. + // We do this to ensure that the cache entries can survive garbage // collection. Script* script = Script::cast(shared->script()); hash ^= String::cast(script->source())->Hash(); - if (strict_mode == kStrictMode) hash ^= 0x8000; - hash += shared->start_position(); + if (language_mode == STRICT_MODE) hash ^= 0x8000; + if (language_mode == EXTENDED_MODE) hash ^= 0x0080; + hash += scope_position; } return hash; } uint32_t Hash() { - return StringSharedHashHelper(source_, shared_, strict_mode_); + return StringSharedHashHelper( + source_, shared_, language_mode_, scope_position_); } uint32_t HashForObject(Object* obj) { - FixedArray* pair = FixedArray::cast(obj); - SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0)); - String* source = String::cast(pair->get(1)); - StrictModeFlag strict_mode = static_cast<StrictModeFlag>( - Smi::cast(pair->get(2))->value()); - return StringSharedHashHelper(source, shared, strict_mode); + FixedArray* other_array = FixedArray::cast(obj); + SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0)); + String* source = String::cast(other_array->get(1)); + int language_unchecked = Smi::cast(other_array->get(2))->value(); + ASSERT(language_unchecked == CLASSIC_MODE || + language_unchecked == STRICT_MODE || + language_unchecked == EXTENDED_MODE); + LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); + int scope_position = Smi::cast(other_array->get(3))->value(); + return StringSharedHashHelper( + source, shared, language_mode, scope_position); } MUST_USE_RESULT MaybeObject* AsObject() { Object* obj; - { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(3); + { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(4); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - FixedArray* pair = FixedArray::cast(obj); - pair->set(0, shared_); - pair->set(1, source_); - pair->set(2, Smi::FromInt(strict_mode_)); - return pair; + FixedArray* other_array = FixedArray::cast(obj); + other_array->set(0, shared_); + other_array->set(1, source_); + other_array->set(2, Smi::FromInt(language_mode_)); + other_array->set(3, Smi::FromInt(scope_position_)); + return other_array; } private: String* source_; SharedFunctionInfo* shared_; - StrictModeFlag strict_mode_; + LanguageMode language_mode_; + int scope_position_; }; @@ -9778,7 +11023,7 @@ class SymbolKey : public HashTableKey { // Transform string to symbol if possible. Map* map = heap->SymbolMapForString(string_); if (map != NULL) { - string_->set_map(map); + string_->set_map_no_write_barrier(map); ASSERT(string_->IsSymbol()); return string_; } @@ -9839,7 +11084,7 @@ int StringDictionary::FindEntry(String* key) { // Optimized for symbol key. Knowledge of the key type allows: // 1. Move the check if the key is a symbol out of the loop. - // 2. Avoid comparing hash codes in symbol to symbol comparision. + // 2. Avoid comparing hash codes in symbol to symbol comparison. // 3. Detect a case when a dictionary key is not a symbol but the key is. // In case of positive result the dictionary key may be replaced by // the symbol with minimal performance penalty. It gives a chance to @@ -9857,20 +11102,45 @@ int StringDictionary::FindEntry(String* key) { if (element->IsUndefined()) break; // Empty entry. if (key == element) return entry; if (!element->IsSymbol() && - !element->IsNull() && + !element->IsTheHole() && String::cast(element)->Equals(key)) { // Replace a non-symbol key by the equivalent symbol for faster further // lookups. set(index, key); return entry; } - ASSERT(element->IsNull() || !String::cast(element)->Equals(key)); + ASSERT(element->IsTheHole() || !String::cast(element)->Equals(key)); entry = NextProbe(entry, count++, capacity); } return kNotFound; } +bool StringDictionary::ContainsTransition(int entry) { + switch (DetailsAt(entry).type()) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: + return true; + case CALLBACKS: { + Object* value = ValueAt(entry); + if (!value->IsAccessorPair()) return false; + AccessorPair* accessors = AccessorPair::cast(value); + return accessors->getter()->IsMap() || accessors->setter()->IsMap(); + } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + return false; + } + UNREACHABLE(); // Keep the compiler happy. + return false; +} + + template<typename Shape, typename Key> MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) { ASSERT(NumberOfElements() < new_table->Capacity()); @@ -9968,7 +11238,7 @@ uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) { // EnsureCapacity will guarantee the hash table is never full. while (true) { Object* element = KeyAt(entry); - if (element->IsUndefined() || element->IsNull()) break; + if (element->IsUndefined() || element->IsTheHole()) break; entry = NextProbe(entry, count++, capacity); } return entry; @@ -9983,7 +11253,9 @@ template class HashTable<CompilationCacheShape, HashTableKey*>; template class HashTable<MapCacheShape, HashTableKey*>; -template class HashTable<ObjectHashTableShape, JSObject*>; +template class HashTable<ObjectHashTableShape<1>, Object*>; + +template class HashTable<ObjectHashTableShape<2>, Object*>; template class Dictionary<StringDictionaryShape, String*>; @@ -10006,6 +11278,9 @@ template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::AtPut( template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: AtPut(uint32_t, Object*); +template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>:: + SlowReverseLookup(Object* value); + template Object* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: SlowReverseLookup(Object* value); @@ -10184,8 +11459,6 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { // If the object is in dictionary mode, it is converted to fast elements // mode. MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { - ASSERT(!HasExternalArrayElements()); - Heap* heap = GetHeap(); if (HasDictionaryElements()) { @@ -10199,7 +11472,8 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { // Convert to fast elements. Object* obj; - { MaybeObject* maybe_obj = map()->GetFastElementsMap(); + { MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(), + FAST_ELEMENTS); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); @@ -10215,13 +11489,16 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { set_map(new_map); set_elements(fast_elements); + } else if (HasExternalArrayElements()) { + // External arrays cannot have holes or undefined elements. + return Smi::FromInt(ExternalArray::cast(elements())->length()); } else if (!HasFastDoubleElements()) { Object* obj; { MaybeObject* maybe_obj = EnsureWritableFastElements(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } } - ASSERT(HasFastElements() || HasFastDoubleElements()); + ASSERT(HasFastTypeElements() || HasFastDoubleElements()); // Collect holes at the end, undefined before that and the rest at the // start, and return the number of non-hole, non-undefined values. @@ -10490,6 +11767,16 @@ JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) { } +Handle<JSGlobalPropertyCell> GlobalObject::EnsurePropertyCell( + Handle<GlobalObject> global, + Handle<String> name) { + Isolate* isolate = global->GetIsolate(); + CALL_HEAP_FUNCTION(isolate, + global->EnsurePropertyCell(*name), + JSGlobalPropertyCell); +} + + MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { ASSERT(!HasFastProperties()); int entry = property_dictionary()->FindEntry(name); @@ -10691,8 +11978,12 @@ Object* CompilationCacheTable::Lookup(String* src) { Object* CompilationCacheTable::LookupEval(String* src, Context* context, - StrictModeFlag strict_mode) { - StringSharedKey key(src, context->closure()->shared(), strict_mode); + LanguageMode language_mode, + int scope_position) { + StringSharedKey key(src, + context->closure()->shared(), + language_mode, + scope_position); int entry = FindEntry(&key); if (entry == kNotFound) return GetHeap()->undefined_value(); return get(EntryToIndex(entry) + 1); @@ -10727,10 +12018,12 @@ MaybeObject* CompilationCacheTable::Put(String* src, Object* value) { MaybeObject* CompilationCacheTable::PutEval(String* src, Context* context, - SharedFunctionInfo* value) { + SharedFunctionInfo* value, + int scope_position) { StringSharedKey key(src, context->closure()->shared(), - value->strict_mode() ? kStrictMode : kNonStrictMode); + value->language_mode(), + scope_position); Object* obj; { MaybeObject* maybe_obj = EnsureCapacity(1, &key); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -10774,13 +12067,13 @@ MaybeObject* CompilationCacheTable::PutRegExp(String* src, void CompilationCacheTable::Remove(Object* value) { - Object* null_value = GetHeap()->null_value(); + Object* the_hole_value = GetHeap()->the_hole_value(); for (int entry = 0, size = Capacity(); entry < size; entry++) { int entry_index = EntryToIndex(entry); int value_index = entry_index + 1; if (get(value_index) == value) { - fast_set(this, entry_index, null_value); - fast_set(this, value_index, null_value); + NoWriteBarrierSet(this, entry_index, the_hole_value); + NoWriteBarrierSet(this, value_index, the_hole_value); ElementRemoved(); } } @@ -10933,30 +12226,6 @@ MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) { } -void SeededNumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { - // Do nothing if the interval [from, to) is empty. - if (from >= to) return; - - Heap* heap = GetHeap(); - int removed_entries = 0; - Object* sentinel = heap->null_value(); - int capacity = Capacity(); - for (int i = 0; i < capacity; i++) { - Object* key = KeyAt(i); - if (key->IsNumber()) { - uint32_t number = static_cast<uint32_t>(key->Number()); - if (from <= number && number < to) { - SetEntry(i, sentinel, sentinel); - removed_entries++; - } - } - } - - // Update the number of elements. - ElementsRemoved(removed_entries); -} - - template<typename Shape, typename Key> Object* Dictionary<Shape, Key>::DeleteProperty(int entry, JSReceiver::DeleteMode mode) { @@ -10966,7 +12235,7 @@ Object* Dictionary<Shape, Key>::DeleteProperty(int entry, if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) { return heap->false_value(); } - SetEntry(entry, heap->null_value(), heap->null_value()); + SetEntry(entry, heap->the_hole_value(), heap->the_hole_value()); HashTable<Shape, Key>::ElementRemoved(); return heap->true_value(); } @@ -11098,6 +12367,27 @@ MaybeObject* UnseededNumberDictionary::AtNumberPut(uint32_t key, } +Handle<SeededNumberDictionary> SeededNumberDictionary::Set( + Handle<SeededNumberDictionary> dictionary, + uint32_t index, + Handle<Object> value, + PropertyDetails details) { + CALL_HEAP_FUNCTION(dictionary->GetIsolate(), + dictionary->Set(index, *value, details), + SeededNumberDictionary); +} + + +Handle<UnseededNumberDictionary> UnseededNumberDictionary::Set( + Handle<UnseededNumberDictionary> dictionary, + uint32_t index, + Handle<Object> value) { + CALL_HEAP_FUNCTION(dictionary->GetIsolate(), + dictionary->Set(index, *value), + UnseededNumberDictionary); +} + + MaybeObject* SeededNumberDictionary::Set(uint32_t key, Object* value, PropertyDetails details) { @@ -11279,14 +12569,15 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( } // Allocate the instance descriptor. - Object* descriptors_unchecked; - { MaybeObject* maybe_descriptors_unchecked = + DescriptorArray* descriptors; + { MaybeObject* maybe_descriptors = DescriptorArray::Allocate(instance_descriptor_length); - if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) { - return maybe_descriptors_unchecked; + if (!maybe_descriptors->To<DescriptorArray>(&descriptors)) { + return maybe_descriptors; } } - DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked); + + DescriptorArray::WhitenessWitness witness(descriptors); int inobject_props = obj->map()->inobject_properties(); int number_of_allocated_fields = @@ -11324,7 +12615,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( JSFunction::cast(value), details.attributes(), details.index()); - descriptors->Set(next_descriptor++, &d); + descriptors->Set(next_descriptor++, &d, witness); } else if (type == NORMAL) { if (current_offset < inobject_props) { obj->InObjectPropertyAtPut(current_offset, @@ -11338,13 +12629,13 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( current_offset++, details.attributes(), details.index()); - descriptors->Set(next_descriptor++, &d); + descriptors->Set(next_descriptor++, &d, witness); } else if (type == CALLBACKS) { CallbacksDescriptor d(String::cast(key), value, details.attributes(), details.index()); - descriptors->Set(next_descriptor++, &d); + descriptors->Set(next_descriptor++, &d, witness); } else { UNREACHABLE(); } @@ -11352,7 +12643,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( } ASSERT(current_offset == number_of_fields); - descriptors->Sort(); + descriptors->Sort(witness); // Allocate new map. Object* new_map; { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors(); @@ -11375,20 +12666,84 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( } -Object* ObjectHashTable::Lookup(JSObject* key) { +bool ObjectHashSet::Contains(Object* key) { + ASSERT(IsKey(key)); + + // If the object does not have an identity hash, it was never used as a key. + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return false; + } + return (FindEntry(key) != kNotFound); +} + + +MaybeObject* ObjectHashSet::Add(Object* key) { + ASSERT(IsKey(key)); + + // Make sure the key object has an identity hash code. + int hash; + { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION); + if (maybe_hash->IsFailure()) return maybe_hash; + hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); + } + int entry = FindEntry(key); + + // Check whether key is already present. + if (entry != kNotFound) return this; + + // Check whether the hash set should be extended and add entry. + Object* obj; + { MaybeObject* maybe_obj = EnsureCapacity(1, key); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } + ObjectHashSet* table = ObjectHashSet::cast(obj); + entry = table->FindInsertionEntry(hash); + table->set(EntryToIndex(entry), key); + table->ElementAdded(); + return table; +} + + +MaybeObject* ObjectHashSet::Remove(Object* key) { + ASSERT(IsKey(key)); + + // If the object does not have an identity hash, it was never used as a key. + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) return this; + } + int entry = FindEntry(key); + + // Check whether key is actually present. + if (entry == kNotFound) return this; + + // Remove entry and try to shrink this hash set. + set_the_hole(EntryToIndex(entry)); + ElementRemoved(); + return Shrink(key); +} + + +Object* ObjectHashTable::Lookup(Object* key) { + ASSERT(IsKey(key)); + // If the object does not have an identity hash, it was never used as a key. - MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION); - if (maybe_hash->IsFailure()) return GetHeap()->undefined_value(); + { MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION); + if (maybe_hash->ToObjectUnchecked()->IsUndefined()) { + return GetHeap()->undefined_value(); + } + } int entry = FindEntry(key); if (entry == kNotFound) return GetHeap()->undefined_value(); return get(EntryToIndex(entry) + 1); } -MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) { +MaybeObject* ObjectHashTable::Put(Object* key, Object* value) { + ASSERT(IsKey(key)); + // Make sure the key object has an identity hash code. int hash; - { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION); + { MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION); if (maybe_hash->IsFailure()) return maybe_hash; hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); } @@ -11418,16 +12773,16 @@ MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) { } -void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) { +void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) { set(EntryToIndex(entry), key); set(EntryToIndex(entry) + 1, value); ElementAdded(); } -void ObjectHashTable::RemoveEntry(int entry, Heap* heap) { - set_null(heap, EntryToIndex(entry)); - set_null(heap, EntryToIndex(entry) + 1); +void ObjectHashTable::RemoveEntry(int entry) { + set_the_hole(EntryToIndex(entry)); + set_the_hole(EntryToIndex(entry) + 1); ElementRemoved(); } @@ -11682,7 +13037,7 @@ int BreakPointInfo::GetBreakPointCount() { // Multiple break points. return FixedArray::cast(break_point_objects())->length(); } -#endif +#endif // ENABLE_DEBUGGER_SUPPORT } } // namespace v8::internal diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 1245ed0c12..bc18bf8fab 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -31,6 +31,7 @@ #include "allocation.h" #include "builtins.h" #include "list.h" +#include "property-details.h" #include "smart-array-pointer.h" #include "unicode-inl.h" #if V8_TARGET_ARCH_ARM @@ -38,6 +39,8 @@ #elif V8_TARGET_ARCH_MIPS #include "mips/constants-mips.h" #endif +#include "v8checks.h" + // // Most object types in the V8 JavaScript are described in this file. @@ -51,6 +54,8 @@ // - JSReceiver (suitable for property access) // - JSObject // - JSArray +// - JSSet +// - JSMap // - JSWeakMap // - JSRegExp // - JSFunction @@ -74,7 +79,7 @@ // - MapCache // - Context // - JSFunctionResultCache -// - SerializedScopeInfo +// - ScopeInfo // - FixedDoubleArray // - ExternalArray // - ExternalPixelArray @@ -102,6 +107,7 @@ // - SharedFunctionInfo // - Struct // - AccessorInfo +// - AccessorPair // - AccessCheckInfo // - InterceptorInfo // - CallHandlerInfo @@ -120,24 +126,17 @@ // HeapObject: [32 bit direct pointer] (4 byte aligned) | 01 // Failure: [30 bit signed int] 11 -// Ecma-262 3rd 8.6.1 -enum PropertyAttributes { - NONE = v8::None, - READ_ONLY = v8::ReadOnly, - DONT_ENUM = v8::DontEnum, - DONT_DELETE = v8::DontDelete, - ABSENT = 16 // Used in runtime to indicate a property is absent. - // ABSENT can never be stored in or returned from a descriptor's attributes - // bitfield. It is only used as a return value meaning the attributes of - // a non-existent property. -}; - namespace v8 { namespace internal { enum ElementsKind { - // The "fast" kind for tagged values. Must be first to make it possible - // to efficiently check maps if they have fast elements. + // The "fast" kind for elements that only contain SMI values. Must be first + // to make it possible to efficiently check maps for this kind. + FAST_SMI_ONLY_ELEMENTS, + + // The "fast" kind for tagged values. Must be second to make it possible to + // efficiently check maps for this and the FAST_SMI_ONLY_ELEMENTS kind + // together at once. FAST_ELEMENTS, // The "fast" kind for unwrapped, non-tagged double values. @@ -160,101 +159,21 @@ enum ElementsKind { // Derived constants from ElementsKind FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, - FIRST_ELEMENTS_KIND = FAST_ELEMENTS, + FIRST_ELEMENTS_KIND = FAST_SMI_ONLY_ELEMENTS, LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS }; -static const int kElementsKindCount = - LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; - -// PropertyDetails captures type and attributes for a property. -// They are used both in property dictionaries and instance descriptors. -class PropertyDetails BASE_EMBEDDED { - public: - PropertyDetails(PropertyAttributes attributes, - PropertyType type, - int index = 0) { - ASSERT(type != ELEMENTS_TRANSITION); - ASSERT(TypeField::is_valid(type)); - ASSERT(AttributesField::is_valid(attributes)); - ASSERT(StorageField::is_valid(index)); - - value_ = TypeField::encode(type) - | AttributesField::encode(attributes) - | StorageField::encode(index); - - ASSERT(type == this->type()); - ASSERT(attributes == this->attributes()); - ASSERT(index == this->index()); - } - - PropertyDetails(PropertyAttributes attributes, - PropertyType type, - ElementsKind elements_kind) { - ASSERT(type == ELEMENTS_TRANSITION); - ASSERT(TypeField::is_valid(type)); - ASSERT(AttributesField::is_valid(attributes)); - ASSERT(StorageField::is_valid(static_cast<int>(elements_kind))); - - value_ = TypeField::encode(type) - | AttributesField::encode(attributes) - | StorageField::encode(static_cast<int>(elements_kind)); - - ASSERT(type == this->type()); - ASSERT(attributes == this->attributes()); - ASSERT(elements_kind == this->elements_kind()); - } - - // Conversion for storing details as Object*. - explicit inline PropertyDetails(Smi* smi); - inline Smi* AsSmi(); - - PropertyType type() { return TypeField::decode(value_); } - - bool IsTransition() { - PropertyType t = type(); - ASSERT(t != INTERCEPTOR); - return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || - t == ELEMENTS_TRANSITION; - } - - bool IsProperty() { - return type() < FIRST_PHANTOM_PROPERTY_TYPE; - } - - PropertyAttributes attributes() { return AttributesField::decode(value_); } - - int index() { return StorageField::decode(value_); } - - ElementsKind elements_kind() { - ASSERT(type() == ELEMENTS_TRANSITION); - return static_cast<ElementsKind>(StorageField::decode(value_)); - } - - inline PropertyDetails AsDeleted(); - - static bool IsValidIndex(int index) { - return StorageField::is_valid(index); - } - - bool IsReadOnly() { return (attributes() & READ_ONLY) != 0; } - bool IsDontDelete() { return (attributes() & DONT_DELETE) != 0; } - bool IsDontEnum() { return (attributes() & DONT_ENUM) != 0; } - bool IsDeleted() { return DeletedField::decode(value_) != 0;} - - // Bit fields in value_ (type, shift, size). Must be public so the - // constants can be embedded in generated code. - class TypeField: public BitField<PropertyType, 0, 4> {}; - class AttributesField: public BitField<PropertyAttributes, 4, 3> {}; - class DeletedField: public BitField<uint32_t, 7, 1> {}; - class StorageField: public BitField<uint32_t, 8, 32-8> {}; +enum CompareMapMode { + REQUIRE_EXACT_MAP, + ALLOW_ELEMENT_TRANSITION_MAPS +}; - static const int kInitialIndex = 1; +const int kElementsKindCount = LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; - private: - uint32_t value_; -}; +void PrintElementsKind(FILE* out, ElementsKind kind); +inline bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind, + ElementsKind to_kind); // Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER. enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER }; @@ -276,8 +195,15 @@ enum NormalizedMapSharingMode { }; +// Indicates whether a get method should implicitly create the object looked up. +enum CreationFlag { + ALLOW_CREATION, + OMIT_CREATION +}; + + // Instance size sentinel for objects of variable size. -static const int kVariableSizeSentinel = 0; +const int kVariableSizeSentinel = 0; // All Maps have a field instance_type containing a InstanceType. @@ -291,7 +217,7 @@ static const int kVariableSizeSentinel = 0; // encoding is considered TWO_BYTE. It is not mentioned in the name. ASCII // encoding is mentioned explicitly in the name. Likewise, the default // representation is considered sequential. It is not mentioned in the -// name. The other representations (eg, CONS, EXTERNAL) are explicitly +// name. The other representations (e.g. CONS, EXTERNAL) are explicitly // mentioned. Finally, the string is either a SYMBOL_TYPE (if it is a // symbol) or a STRING_TYPE (if it is not a symbol). // @@ -311,6 +237,9 @@ static const int kVariableSizeSentinel = 0; V(EXTERNAL_SYMBOL_TYPE) \ V(EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE) \ V(EXTERNAL_ASCII_SYMBOL_TYPE) \ + V(SHORT_EXTERNAL_SYMBOL_TYPE) \ + V(SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE) \ + V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE) \ V(STRING_TYPE) \ V(ASCII_STRING_TYPE) \ V(CONS_STRING_TYPE) \ @@ -319,6 +248,9 @@ static const int kVariableSizeSentinel = 0; V(EXTERNAL_STRING_TYPE) \ V(EXTERNAL_STRING_WITH_ASCII_DATA_TYPE) \ V(EXTERNAL_ASCII_STRING_TYPE) \ + V(SHORT_EXTERNAL_STRING_TYPE) \ + V(SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE) \ + V(SHORT_EXTERNAL_ASCII_STRING_TYPE) \ V(PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \ \ V(MAP_TYPE) \ @@ -329,6 +261,7 @@ static const int kVariableSizeSentinel = 0; V(HEAP_NUMBER_TYPE) \ V(FOREIGN_TYPE) \ V(BYTE_ARRAY_TYPE) \ + V(FREE_SPACE_TYPE) \ /* Note: the order of these external array */ \ /* types is relied upon in */ \ /* Object::IsExternalArray(). */ \ @@ -343,6 +276,7 @@ static const int kVariableSizeSentinel = 0; V(FILLER_TYPE) \ \ V(ACCESSOR_INFO_TYPE) \ + V(ACCESSOR_PAIR_TYPE) \ V(ACCESS_CHECK_INFO_TYPE) \ V(INTERCEPTOR_INFO_TYPE) \ V(CALL_HANDLER_INFO_TYPE) \ @@ -418,6 +352,18 @@ static const int kVariableSizeSentinel = 0; ExternalAsciiString::kSize, \ external_ascii_symbol, \ ExternalAsciiSymbol) \ + V(SHORT_EXTERNAL_SYMBOL_TYPE, \ + ExternalTwoByteString::kShortSize, \ + short_external_symbol, \ + ShortExternalSymbol) \ + V(SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE, \ + ExternalTwoByteString::kShortSize, \ + short_external_symbol_with_ascii_data, \ + ShortExternalSymbolWithAsciiData) \ + V(SHORT_EXTERNAL_ASCII_SYMBOL_TYPE, \ + ExternalAsciiString::kShortSize, \ + short_external_ascii_symbol, \ + ShortExternalAsciiSymbol) \ V(STRING_TYPE, \ kVariableSizeSentinel, \ string, \ @@ -453,7 +399,19 @@ static const int kVariableSizeSentinel = 0; V(EXTERNAL_ASCII_STRING_TYPE, \ ExternalAsciiString::kSize, \ external_ascii_string, \ - ExternalAsciiString) + ExternalAsciiString) \ + V(SHORT_EXTERNAL_STRING_TYPE, \ + ExternalTwoByteString::kShortSize, \ + short_external_string, \ + ShortExternalString) \ + V(SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE, \ + ExternalTwoByteString::kShortSize, \ + short_external_string_with_ascii_data, \ + ShortExternalStringWithAsciiData) \ + V(SHORT_EXTERNAL_ASCII_STRING_TYPE, \ + ExternalAsciiString::kShortSize, \ + short_external_ascii_string, \ + ShortExternalAsciiString) // A struct is a simple object a set of object-valued fields. Including an // object type in this causes the compiler to generate most of the boilerplate @@ -466,6 +424,7 @@ static const int kVariableSizeSentinel = 0; // manually. #define STRUCT_LIST_ALL(V) \ V(ACCESSOR_INFO, AccessorInfo, accessor_info) \ + V(ACCESSOR_PAIR, AccessorPair, accessor_pair) \ V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \ V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \ V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \ @@ -533,10 +492,15 @@ const uint32_t kSlicedNotConsMask = kSlicedStringTag & ~kConsStringTag; STATIC_ASSERT(IS_POWER_OF_TWO(kSlicedNotConsMask) && kSlicedNotConsMask != 0); // If bit 7 is clear, then bit 3 indicates whether this two-byte -// string actually contains ascii data. +// string actually contains ASCII data. const uint32_t kAsciiDataHintMask = 0x08; const uint32_t kAsciiDataHintTag = 0x08; +// If bit 7 is clear and string representation indicates an external string, +// then bit 4 indicates whether the data pointer is cached. +const uint32_t kShortExternalStringMask = 0x10; +const uint32_t kShortExternalStringTag = 0x10; + // A ConsString with an empty string as the right side is a candidate // for being shortcut by the garbage collector unless it is a @@ -556,6 +520,13 @@ enum InstanceType { ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag, CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag, CONS_ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kConsStringTag, + SHORT_EXTERNAL_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | + kExternalStringTag | kShortExternalStringTag, + SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE = + kTwoByteStringTag | kSymbolTag | kExternalStringTag | + kAsciiDataHintTag | kShortExternalStringTag, + SHORT_EXTERNAL_ASCII_SYMBOL_TYPE = kAsciiStringTag | kExternalStringTag | + kSymbolTag | kShortExternalStringTag, EXTERNAL_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kExternalStringTag, EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE = kTwoByteStringTag | kSymbolTag | kExternalStringTag | kAsciiDataHintTag, @@ -567,6 +538,13 @@ enum InstanceType { CONS_ASCII_STRING_TYPE = kAsciiStringTag | kConsStringTag, SLICED_STRING_TYPE = kTwoByteStringTag | kSlicedStringTag, SLICED_ASCII_STRING_TYPE = kAsciiStringTag | kSlicedStringTag, + SHORT_EXTERNAL_STRING_TYPE = + kTwoByteStringTag | kExternalStringTag | kShortExternalStringTag, + SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE = + kTwoByteStringTag | kExternalStringTag | + kAsciiDataHintTag | kShortExternalStringTag, + SHORT_EXTERNAL_ASCII_STRING_TYPE = + kAsciiStringTag | kExternalStringTag | kShortExternalStringTag, EXTERNAL_STRING_TYPE = kTwoByteStringTag | kExternalStringTag, EXTERNAL_STRING_WITH_ASCII_DATA_TYPE = kTwoByteStringTag | kExternalStringTag | kAsciiDataHintTag, @@ -585,6 +563,7 @@ enum InstanceType { HEAP_NUMBER_TYPE, FOREIGN_TYPE, BYTE_ARRAY_TYPE, + FREE_SPACE_TYPE, EXTERNAL_BYTE_ARRAY_TYPE, // FIRST_EXTERNAL_ARRAY_TYPE EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE, EXTERNAL_SHORT_ARRAY_TYPE, @@ -599,6 +578,7 @@ enum InstanceType { // Structs. ACCESSOR_INFO_TYPE, + ACCESSOR_PAIR_TYPE, ACCESS_CHECK_INFO_TYPE, INTERCEPTOR_INFO_TYPE, CALL_HANDLER_INFO_TYPE, @@ -621,24 +601,32 @@ enum InstanceType { JS_MESSAGE_OBJECT_TYPE, - JS_VALUE_TYPE, // FIRST_NON_CALLABLE_OBJECT_TYPE, FIRST_JS_RECEIVER_TYPE + // All the following types are subtypes of JSReceiver, which corresponds to + // objects in the JS sense. The first and the last type in this range are + // the two forms of function. This organization enables using the same + // compares for checking the JS_RECEIVER/SPEC_OBJECT range and the + // NONCALLABLE_JS_OBJECT range. + JS_FUNCTION_PROXY_TYPE, // FIRST_JS_RECEIVER_TYPE, FIRST_JS_PROXY_TYPE + JS_PROXY_TYPE, // LAST_JS_PROXY_TYPE + + JS_VALUE_TYPE, // FIRST_JS_OBJECT_TYPE JS_OBJECT_TYPE, JS_CONTEXT_EXTENSION_OBJECT_TYPE, JS_GLOBAL_OBJECT_TYPE, JS_BUILTINS_OBJECT_TYPE, JS_GLOBAL_PROXY_TYPE, JS_ARRAY_TYPE, - JS_PROXY_TYPE, + JS_SET_TYPE, + JS_MAP_TYPE, JS_WEAK_MAP_TYPE, - JS_REGEXP_TYPE, // LAST_NONCALLABLE_SPEC_OBJECT_TYPE + JS_REGEXP_TYPE, - JS_FUNCTION_TYPE, // FIRST_CALLABLE_SPEC_OBJECT_TYPE - JS_FUNCTION_PROXY_TYPE, // LAST_CALLABLE_SPEC_OBJECT_TYPE + JS_FUNCTION_TYPE, // LAST_JS_OBJECT_TYPE, LAST_JS_RECEIVER_TYPE // Pseudo-types FIRST_TYPE = 0x0, - LAST_TYPE = JS_FUNCTION_PROXY_TYPE, + LAST_TYPE = JS_FUNCTION_TYPE, INVALID_TYPE = FIRST_TYPE - 1, FIRST_NONSTRING_TYPE = MAP_TYPE, // Boundaries for testing for an external array. @@ -651,21 +639,27 @@ enum InstanceType { // are not continuous in this enum! The enum ranges instead reflect the // external class names, where proxies are treated as either ordinary objects, // or functions. - FIRST_JS_RECEIVER_TYPE = JS_VALUE_TYPE, + FIRST_JS_RECEIVER_TYPE = JS_FUNCTION_PROXY_TYPE, LAST_JS_RECEIVER_TYPE = LAST_TYPE, + // Boundaries for testing the types represented as JSObject + FIRST_JS_OBJECT_TYPE = JS_VALUE_TYPE, + LAST_JS_OBJECT_TYPE = LAST_TYPE, + // Boundaries for testing the types represented as JSProxy + FIRST_JS_PROXY_TYPE = JS_FUNCTION_PROXY_TYPE, + LAST_JS_PROXY_TYPE = JS_PROXY_TYPE, + // Boundaries for testing whether the type is a JavaScript object. + FIRST_SPEC_OBJECT_TYPE = FIRST_JS_RECEIVER_TYPE, + LAST_SPEC_OBJECT_TYPE = LAST_JS_RECEIVER_TYPE, // Boundaries for testing the types for which typeof is "object". - FIRST_NONCALLABLE_SPEC_OBJECT_TYPE = JS_VALUE_TYPE, + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE = JS_PROXY_TYPE, LAST_NONCALLABLE_SPEC_OBJECT_TYPE = JS_REGEXP_TYPE, - // Boundaries for testing the types for which typeof is "function". - FIRST_CALLABLE_SPEC_OBJECT_TYPE = JS_FUNCTION_TYPE, - LAST_CALLABLE_SPEC_OBJECT_TYPE = JS_FUNCTION_PROXY_TYPE, - // Boundaries for testing whether the type is a JavaScript object. - FIRST_SPEC_OBJECT_TYPE = FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, - LAST_SPEC_OBJECT_TYPE = LAST_CALLABLE_SPEC_OBJECT_TYPE + // Note that the types for which typeof is "function" are not continuous. + // Define this so that we can put assertions on discrete checks. + NUM_OF_CALLABLE_SPEC_OBJECT_TYPES = 2 }; -static const int kExternalArrayTypeCount = LAST_EXTERNAL_ARRAY_TYPE - - FIRST_EXTERNAL_ARRAY_TYPE + 1; +const int kExternalArrayTypeCount = + LAST_EXTERNAL_ARRAY_TYPE - FIRST_EXTERNAL_ARRAY_TYPE + 1; STATIC_CHECK(JS_OBJECT_TYPE == Internals::kJSObjectType); STATIC_CHECK(FIRST_NONSTRING_TYPE == Internals::kFirstNonstringType); @@ -697,6 +691,7 @@ class ElementsAccessor; class FixedArrayBase; class ObjectVisitor; class StringStream; +class Failure; struct ValueInfo : public Malloced { ValueInfo() : type(FIRST_TYPE), ptr(NULL), str(NULL), number(0) { } @@ -710,7 +705,6 @@ struct ValueInfo : public Malloced { // A template-ized version of the IsXXX functions. template <class C> static inline bool Is(Object* obj); -class Failure; class MaybeObject BASE_EMBEDDED { public: @@ -748,7 +742,7 @@ class MaybeObject BASE_EMBEDDED { // Prints this object with details. inline void Print() { Print(stdout); - }; + } inline void PrintLn() { PrintLn(stdout); } @@ -791,6 +785,7 @@ class MaybeObject BASE_EMBEDDED { V(ExternalDoubleArray) \ V(ExternalPixelArray) \ V(ByteArray) \ + V(FreeSpace) \ V(JSReceiver) \ V(JSObject) \ V(JSContextExtensionObject) \ @@ -798,11 +793,12 @@ class MaybeObject BASE_EMBEDDED { V(DescriptorArray) \ V(DeoptimizationInputData) \ V(DeoptimizationOutputData) \ + V(TypeFeedbackCells) \ V(FixedArray) \ V(FixedDoubleArray) \ V(Context) \ V(GlobalContext) \ - V(SerializedScopeInfo) \ + V(ScopeInfo) \ V(JSFunction) \ V(Code) \ V(Oddball) \ @@ -815,6 +811,8 @@ class MaybeObject BASE_EMBEDDED { V(JSArray) \ V(JSProxy) \ V(JSFunctionProxy) \ + V(JSSet) \ + V(JSMap) \ V(JSWeakMap) \ V(JSRegExp) \ V(HashTable) \ @@ -835,6 +833,9 @@ class MaybeObject BASE_EMBEDDED { V(AccessCheckNeeded) \ V(JSGlobalPropertyCell) \ + +class JSReceiver; + // Object is the abstract superclass for all classes in the // object hierarchy. // Object does not use any virtual functions to avoid the @@ -849,6 +850,8 @@ class Object : public MaybeObject { HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL) #undef IS_TYPE_FUNCTION_DECL + inline bool IsFixedArrayBase(); + // Returns true if this object is an instance of the specified // function template. inline bool IsInstanceOf(FunctionTemplateInfo* type); @@ -859,6 +862,7 @@ class Object : public MaybeObject { #undef DECLARE_STRUCT_PREDICATE INLINE(bool IsSpecObject()); + INLINE(bool IsSpecFunction()); // Oddball testing. INLINE(bool IsUndefined()); @@ -867,6 +871,10 @@ class Object : public MaybeObject { INLINE(bool IsTrue()); INLINE(bool IsFalse()); inline bool IsArgumentsMarker(); + inline bool NonFailureIsHeapObject(); + + // Filler objects (fillers and free space objects). + inline bool IsFiller(); // Extract the number. inline double Number(); @@ -899,20 +907,22 @@ class Object : public MaybeObject { Object* receiver, String* key, PropertyAttributes* attributes); + + static Handle<Object> GetProperty(Handle<Object> object, + Handle<Object> receiver, + LookupResult* result, + Handle<String> key, + PropertyAttributes* attributes); + MUST_USE_RESULT MaybeObject* GetProperty(Object* receiver, LookupResult* result, String* key, PropertyAttributes* attributes); - MUST_USE_RESULT MaybeObject* GetPropertyWithCallback(Object* receiver, - Object* structure, - String* name, - Object* holder); - MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(Object* receiver, - String* name, - Object* handler); + MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver, - JSFunction* getter); + JSReceiver* getter); + static Handle<Object> GetElement(Handle<Object> object, uint32_t index); inline MaybeObject* GetElement(uint32_t index); // For use when we know that no exception can be thrown. inline Object* GetElementNoExceptionThrown(uint32_t index); @@ -921,6 +931,16 @@ class Object : public MaybeObject { // Return the object's prototype (might be Heap::null_value()). Object* GetPrototype(); + // Returns the permanent hash code associated with this object depending on + // the actual object type. Might return a failure in case no hash was + // created yet or GC was caused by creation. + MUST_USE_RESULT MaybeObject* GetHash(CreationFlag flag); + + // Checks whether this object has the same value as the given one. This + // function is implemented according to ES5, section 9.12 and can be used + // to implement the Harmony "egal" function. + bool SameValue(Object* other); + // Tries to convert an object to an array index. Returns true and sets // the output parameter if it succeeds. inline bool ToArrayIndex(uint32_t* index); @@ -1067,7 +1087,7 @@ class Failure: public MaybeObject { // Heap objects typically have a map pointer in their first word. However, -// during GC other data (eg, mark bits, forwarding addresses) is sometimes +// during GC other data (e.g. mark bits, forwarding addresses) is sometimes // encoded in the first word. The class MapWord is an abstraction of the // value in a heap object's first word. class MapWord BASE_EMBEDDED { @@ -1086,7 +1106,7 @@ class MapWord BASE_EMBEDDED { // True if this map word is a forwarding address for a scavenge // collection. Only valid during a scavenge collection (specifically, - // when all map words are heap object pointers, ie. not during a full GC). + // when all map words are heap object pointers, i.e. not during a full GC). inline bool IsForwardingAddress(); // Create a map word from a forwarding address. @@ -1095,101 +1115,13 @@ class MapWord BASE_EMBEDDED { // View this map word as a forwarding address. inline HeapObject* ToForwardingAddress(); - // Marking phase of full collection: the map word of live objects is - // marked, and may be marked as overflowed (eg, the object is live, its - // children have not been visited, and it does not fit in the marking - // stack). - - // True if this map word's mark bit is set. - inline bool IsMarked(); - - // Return this map word but with its mark bit set. - inline void SetMark(); - - // Return this map word but with its mark bit cleared. - inline void ClearMark(); - - // True if this map word's overflow bit is set. - inline bool IsOverflowed(); - - // Return this map word but with its overflow bit set. - inline void SetOverflow(); - - // Return this map word but with its overflow bit cleared. - inline void ClearOverflow(); - - - // Compacting phase of a full compacting collection: the map word of live - // objects contains an encoding of the original map address along with the - // forwarding address (represented as an offset from the first live object - // in the same page as the (old) object address). - - // Create a map word from a map address and a forwarding address offset. - static inline MapWord EncodeAddress(Address map_address, int offset); - - // Return the map address encoded in this map word. - inline Address DecodeMapAddress(MapSpace* map_space); - - // Return the forwarding offset encoded in this map word. - inline int DecodeOffset(); - - - // During serialization: the map word is used to hold an encoded - // address, and possibly a mark bit (set and cleared with SetMark - // and ClearMark). - - // Create a map word from an encoded address. - static inline MapWord FromEncodedAddress(Address address); - - inline Address ToEncodedAddress(); - - // Bits used by the marking phase of the garbage collector. - // - // The first word of a heap object is normally a map pointer. The last two - // bits are tagged as '01' (kHeapObjectTag). We reuse the last two bits to - // mark an object as live and/or overflowed: - // last bit = 0, marked as alive - // second bit = 1, overflowed - // An object is only marked as overflowed when it is marked as live while - // the marking stack is overflowed. - static const int kMarkingBit = 0; // marking bit - static const int kMarkingMask = (1 << kMarkingBit); // marking mask - static const int kOverflowBit = 1; // overflow bit - static const int kOverflowMask = (1 << kOverflowBit); // overflow mask - - // Forwarding pointers and map pointer encoding. On 32 bit all the bits are - // used. - // +-----------------+------------------+-----------------+ - // |forwarding offset|page offset of map|page index of map| - // +-----------------+------------------+-----------------+ - // ^ ^ ^ - // | | | - // | | kMapPageIndexBits - // | kMapPageOffsetBits - // kForwardingOffsetBits - static const int kMapPageOffsetBits = kPageSizeBits - kMapAlignmentBits; - static const int kForwardingOffsetBits = kPageSizeBits - kObjectAlignmentBits; -#ifdef V8_HOST_ARCH_64_BIT - static const int kMapPageIndexBits = 16; -#else - // Use all the 32-bits to encode on a 32-bit platform. - static const int kMapPageIndexBits = - 32 - (kMapPageOffsetBits + kForwardingOffsetBits); -#endif - - static const int kMapPageIndexShift = 0; - static const int kMapPageOffsetShift = - kMapPageIndexShift + kMapPageIndexBits; - static const int kForwardingOffsetShift = - kMapPageOffsetShift + kMapPageOffsetBits; + static inline MapWord FromRawValue(uintptr_t value) { + return MapWord(value); + } - // Bit masks covering the different parts the encoding. - static const uintptr_t kMapPageIndexMask = - (1 << kMapPageOffsetShift) - 1; - static const uintptr_t kMapPageOffsetMask = - ((1 << kForwardingOffsetShift) - 1) & ~kMapPageIndexMask; - static const uintptr_t kForwardingOffsetMask = - ~(kMapPageIndexMask | kMapPageOffsetMask); + inline uintptr_t ToRawValue() { + return value_; + } private: // HeapObject calls the private constructor and directly reads the value. @@ -1209,6 +1141,10 @@ class HeapObject: public Object { // information. inline Map* map(); inline void set_map(Map* value); + // The no-write-barrier version. This is OK if the object is white and in + // new space, or if the value is an immortal immutable object, like the maps + // of primitive (non-JS) objects like strings, heap numbers etc. + inline void set_map_no_write_barrier(Map* value); // During garbage collection, the map word of a heap object does not // necessarily contain a map pointer. @@ -1216,8 +1152,8 @@ class HeapObject: public Object { inline void set_map_word(MapWord map_word); // The Heap the object was allocated in. Used also to access Isolate. - // This method can not be used during GC, it ASSERTs this. inline Heap* GetHeap(); + // Convenience method to get current isolate. This method can be // accessed only when its result is the same as // Isolate::Current(), it ASSERTs this. See also comment for GetHeap. @@ -1246,31 +1182,6 @@ class HeapObject: public Object { // GC internal. inline int SizeFromMap(Map* map); - // Support for the marking heap objects during the marking phase of GC. - // True if the object is marked live. - inline bool IsMarked(); - - // Mutate this object's map pointer to indicate that the object is live. - inline void SetMark(); - - // Mutate this object's map pointer to remove the indication that the - // object is live (ie, partially restore the map pointer). - inline void ClearMark(); - - // True if this object is marked as overflowed. Overflowed objects have - // been reached and marked during marking of the heap, but their children - // have not necessarily been marked and they have not been pushed on the - // marking stack. - inline bool IsOverflowed(); - - // Mutate this object's map pointer to indicate that the object is - // overflowed. - inline void SetOverflow(); - - // Mutate this object's map pointer to remove the indication that the - // object is overflowed (ie, partially restore the map pointer). - inline void ClearOverflow(); - // Returns the field at offset in obj, as a read/write Object* reference. // Does no checking, and is safe to use during GC, while maps are invalid. // Does not invoke write barrier, so should only be assigned to @@ -1294,18 +1205,14 @@ class HeapObject: public Object { HeapObjectPrint(stdout); } void HeapObjectPrint(FILE* out); + void PrintHeader(FILE* out, const char* id); #endif + #ifdef DEBUG void HeapObjectVerify(); inline void VerifyObjectField(int offset); inline void VerifySmiField(int offset); -#endif - -#ifdef OBJECT_PRINT - void PrintHeader(FILE* out, const char* id); -#endif -#ifdef DEBUG // Verify a pointer is a valid HeapObject pointer that points to object // areas in the heap. static void VerifyHeapPointer(Object* p); @@ -1425,6 +1332,13 @@ class HeapNumber: public HeapObject { }; +enum EnsureElementsMode { + DONT_ALLOW_DOUBLE_ELEMENTS, + ALLOW_COPIED_DOUBLE_ELEMENTS, + ALLOW_CONVERTED_DOUBLE_ELEMENTS +}; + + // JSReceiver includes types on which properties can be defined, i.e., // JSObject and JSProxy. class JSReceiver: public HeapObject { @@ -1438,6 +1352,11 @@ class JSReceiver: public HeapObject { // Casting. static inline JSReceiver* cast(Object* obj); + static Handle<Object> SetProperty(Handle<JSReceiver> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes, + StrictModeFlag strict_mode); // Can cause GC. MUST_USE_RESULT MaybeObject* SetProperty(String* key, Object* value, @@ -1448,8 +1367,21 @@ class JSReceiver: public HeapObject { Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode); + MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSReceiver* setter, + Object* value); MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode); + MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode); + + // Set the index'th array element. + // Can cause GC, or return failure if GC is required. + MUST_USE_RESULT MaybeObject* SetElement(uint32_t index, + Object* value, + StrictModeFlag strict_mode, + bool check_prototype); + + // Tests for the fast common case for property enumeration. + bool IsSimpleEnum(); // Returns the class name ([[Class]] property in the specification). String* class_name(); @@ -1466,6 +1398,7 @@ class JSReceiver: public HeapObject { // Can cause a GC. inline bool HasProperty(String* name); inline bool HasLocalProperty(String* name); + inline bool HasElement(uint32_t index); // Return the object's prototype (might be Heap::null_value()). inline Object* GetPrototype(); @@ -1474,11 +1407,18 @@ class JSReceiver: public HeapObject { MUST_USE_RESULT MaybeObject* SetPrototype(Object* value, bool skip_hidden_prototypes); + // Retrieves a permanent object identity hash code. The undefined value might + // be returned in case no hash was created yet and OMIT_CREATION was used. + inline MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag); + // Lookup a property. If found, the result is valid and has // detailed information. void LocalLookup(String* name, LookupResult* result); void Lookup(String* name, LookupResult* result); + protected: + Smi* GenerateIdentityHash(); + private: PropertyAttributes GetPropertyAttribute(JSReceiver* receiver, LookupResult* result, @@ -1525,8 +1465,14 @@ class JSObject: public JSReceiver { MUST_USE_RESULT inline MaybeObject* ResetElements(); inline ElementsKind GetElementsKind(); inline ElementsAccessor* GetElementsAccessor(); + inline bool HasFastSmiOnlyElements(); inline bool HasFastElements(); + // Returns if an object has either FAST_ELEMENT or FAST_SMI_ONLY_ELEMENT + // elements. TODO(danno): Rename HasFastTypeElements to HasFastElements() and + // HasFastElements to HasFastObjectElements. + inline bool HasFastTypeElements(); inline bool HasFastDoubleElements(); + inline bool HasNonStrictArgumentsElements(); inline bool HasDictionaryElements(); inline bool HasExternalPixelElements(); inline bool HasExternalArrayElements(); @@ -1540,9 +1486,13 @@ class JSObject: public JSReceiver { inline bool HasExternalDoubleElements(); bool HasFastArgumentsElements(); bool HasDictionaryArgumentsElements(); - inline bool AllowsSetElementsLength(); inline SeededNumberDictionary* element_dictionary(); // Gets slow elements. + inline void set_map_and_elements( + Map* map, + FixedArrayBase* value, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + // Requires: HasFastElements(). MUST_USE_RESULT inline MaybeObject* EnsureWritableFastElements(); @@ -1554,6 +1504,11 @@ class JSObject: public JSReceiver { // a dictionary, and it will stay a dictionary. MUST_USE_RESULT MaybeObject* PrepareSlowElementsForSort(uint32_t limit); + MUST_USE_RESULT MaybeObject* GetPropertyWithCallback(Object* receiver, + Object* structure, + String* name); + + // Can cause GC. MUST_USE_RESULT MaybeObject* SetPropertyForResult(LookupResult* result, String* key, Object* value, @@ -1571,8 +1526,6 @@ class JSObject: public JSReceiver { Object* value, JSObject* holder, StrictModeFlag strict_mode); - MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSFunction* setter, - Object* value); MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor( String* name, Object* value, @@ -1583,6 +1536,14 @@ class JSObject: public JSReceiver { Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode); + + static Handle<Object> SetLocalPropertyIgnoreAttributes( + Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes); + + // Can cause GC. MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes( String* key, Object* value, @@ -1598,6 +1559,11 @@ class JSObject: public JSReceiver { // Sets the property value in a normalized object given (key, value, details). // Handles the special representation of JS global objects. + static Handle<Object> SetNormalizedProperty(Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyDetails details); + MUST_USE_RESULT MaybeObject* SetNormalizedProperty(String* name, Object* value, PropertyDetails details); @@ -1660,43 +1626,55 @@ class JSObject: public JSReceiver { // Accessors for hidden properties object. // // Hidden properties are not local properties of the object itself. - // Instead they are stored on an auxiliary JSObject stored as a local + // Instead they are stored in an auxiliary structure kept as a local // property with a special name Heap::hidden_symbol(). But if the // receiver is a JSGlobalProxy then the auxiliary object is a property - // of its prototype. - // - // Has/Get/SetHiddenPropertiesObject methods don't allow the holder to be - // a JSGlobalProxy. Use BypassGlobalProxy method above to get to the real - // holder. - // - // These accessors do not touch interceptors or accessors. - inline bool HasHiddenPropertiesObject(); - inline Object* GetHiddenPropertiesObject(); - MUST_USE_RESULT inline MaybeObject* SetHiddenPropertiesObject( - Object* hidden_obj); + // of its prototype, and if it's a detached proxy, then you can't have + // hidden properties. + + // Sets a hidden property on this object. Returns this object if successful, + // undefined if called on a detached proxy. + static Handle<Object> SetHiddenProperty(Handle<JSObject> obj, + Handle<String> key, + Handle<Object> value); + // Returns a failure if a GC is required. + MUST_USE_RESULT MaybeObject* SetHiddenProperty(String* key, Object* value); + // Gets the value of a hidden property with the given key. Returns undefined + // if the property doesn't exist (or if called on a detached proxy), + // otherwise returns the value set for the key. + Object* GetHiddenProperty(String* key); + // Deletes a hidden property. Deleting a non-existing property is + // considered successful. + void DeleteHiddenProperty(String* key); + // Returns true if the object has a property with the hidden symbol as name. + bool HasHiddenProperties(); + + static int GetIdentityHash(Handle<JSObject> obj); + MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag); + MUST_USE_RESULT MaybeObject* SetIdentityHash(Object* hash, CreationFlag flag); + + static Handle<Object> DeleteProperty(Handle<JSObject> obj, + Handle<String> name); + MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode); - // Indicates whether the hidden properties object should be created. - enum HiddenPropertiesFlag { ALLOW_CREATION, OMIT_CREATION }; + static Handle<Object> DeleteElement(Handle<JSObject> obj, uint32_t index); + MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode); - // Retrieves the hidden properties object. - // - // The undefined value might be returned in case no hidden properties object - // is present and creation was omitted. - inline bool HasHiddenProperties(); - MUST_USE_RESULT MaybeObject* GetHiddenProperties(HiddenPropertiesFlag flag); + inline void ValidateSmiOnlyElements(); - // Retrieves a permanent object identity hash code. - // - // The identity hash is stored as a hidden property. The undefined value might - // be returned in case no hidden properties object is present and creation was - // omitted. - MUST_USE_RESULT MaybeObject* GetIdentityHash(HiddenPropertiesFlag flag); + // Makes sure that this object can contain HeapObject as elements. + inline MaybeObject* EnsureCanContainHeapObjectElements(); - MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode); - MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode); - - // Tests for the fast common case for property enumeration. - bool IsSimpleEnum(); + // Makes sure that this object can contain the specified elements. + inline MaybeObject* EnsureCanContainElements(Object** elements, + uint32_t count, + EnsureElementsMode mode); + inline MaybeObject* EnsureCanContainElements(FixedArrayBase* elements, + EnsureElementsMode mode); + MaybeObject* EnsureCanContainElements(Arguments* arguments, + uint32_t first_arg, + uint32_t arg_count, + EnsureElementsMode mode); // Do we want to keep the elements in fast case when increasing the // capacity? @@ -1707,11 +1685,11 @@ class JSObject: public JSReceiver { // elements. bool ShouldConvertToFastElements(); // Returns true if the elements of JSObject contains only values that can be - // represented in a FixedDoubleArray. - bool CanConvertToFastDoubleElements(); + // represented in a FixedDoubleArray and has at least one value that can only + // be represented as a double and not a Smi. + bool ShouldConvertToFastDoubleElements(bool* has_smi_only_elements); // Tells whether the index'th element is present. - inline bool HasElement(uint32_t index); bool HasElementWithReceiver(JSReceiver* receiver, uint32_t index); // Computes the new capacity when expanding the elements of a JSObject. @@ -1747,6 +1725,7 @@ class JSObject: public JSReceiver { Object* value, StrictModeFlag strict_mode, bool check_prototype); + MUST_USE_RESULT MaybeObject* SetDictionaryElement(uint32_t index, Object* value, StrictModeFlag strict_mode, @@ -1758,7 +1737,18 @@ class JSObject: public JSReceiver { StrictModeFlag strict_mode, bool check_prototype = true); - // Set the index'th array element. + + static Handle<Object> SetOwnElement(Handle<JSObject> object, + uint32_t index, + Handle<Object> value, + StrictModeFlag strict_mode); + + // Empty handle is returned if the element cannot be set to the given value. + static MUST_USE_RESULT Handle<Object> SetElement(Handle<JSObject> object, + uint32_t index, + Handle<Object> value, + StrictModeFlag strict_mode); + // A Failure object is returned if GC is needed. MUST_USE_RESULT MaybeObject* SetElement(uint32_t index, Object* value, @@ -1769,15 +1759,22 @@ class JSObject: public JSReceiver { // The undefined object if index is out of bounds. MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index); + enum SetFastElementsCapacityMode { + kAllowSmiOnlyElements, + kForceSmiOnlyElements, + kDontAllowSmiOnlyElements + }; + // Replace the elements' backing store with fast elements of the given // capacity. Update the length for JSArrays. Returns the new backing // store. - MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity, - int length); + MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength( + int capacity, + int length, + SetFastElementsCapacityMode set_capacity_mode); MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength( int capacity, int length); - MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length); // Lookup interceptors are used for handling properties controlled by host // objects. @@ -1789,9 +1786,6 @@ class JSObject: public JSReceiver { bool HasRealElementProperty(uint32_t index); bool HasRealNamedCallbackProperty(String* key); - // Initializes the array to a certain length - MUST_USE_RESULT MaybeObject* SetElementsLength(Object* length); - // Get the header size for a JSObject. Used to compute the index of // internal fields as well as the number of internal fields. inline int GetHeaderSize(); @@ -1800,10 +1794,7 @@ class JSObject: public JSReceiver { inline int GetInternalFieldOffset(int index); inline Object* GetInternalField(int index); inline void SetInternalField(int index, Object* value); - - // Lookup a property. If found, the result is valid and has - // detailed information. - void LocalLookup(String* name, LookupResult* result); + inline void SetInternalField(int index, Smi* value); // The following lookup functions skip interceptors. void LocalLookupRealNamedProperty(String* name, LookupResult* result); @@ -1816,9 +1807,7 @@ class JSObject: public JSReceiver { // Returns the number of properties on this object filtering out properties // with the specified attributes (ignoring interceptors). - int NumberOfLocalProperties(PropertyAttributes filter); - // Returns the number of enumerable properties (ignoring interceptors). - int NumberOfEnumProperties(); + int NumberOfLocalProperties(PropertyAttributes filter = NONE); // Fill in details for properties into storage starting at the specified // index. void GetLocalPropertyNames(FixedArray* storage, int index); @@ -1860,6 +1849,21 @@ class JSObject: public JSReceiver { Object* value, PropertyAttributes attributes); + // Returns a new map with all transitions dropped from the object's current + // map and the ElementsKind set. + static Handle<Map> GetElementsTransitionMap(Handle<JSObject> object, + ElementsKind to_kind); + inline MUST_USE_RESULT MaybeObject* GetElementsTransitionMap( + Isolate* isolate, + ElementsKind elements_kind); + MUST_USE_RESULT MaybeObject* GetElementsTransitionMapSlow( + ElementsKind elements_kind); + + static Handle<Object> TransitionElementsKind(Handle<JSObject> object, + ElementsKind to_kind); + + MUST_USE_RESULT MaybeObject* TransitionElementsKind(ElementsKind to_kind); + // Converts a descriptor of any other type to a real field, // backed by the properties array. Descriptors of visible // types, such as CONSTANT_FUNCTION, keep their enumeration order. @@ -1898,16 +1902,32 @@ class JSObject: public JSReceiver { // representation. If the object is expected to have additional properties // added this number can be indicated to have the backing store allocated to // an initial capacity for holding these properties. + static void NormalizeProperties(Handle<JSObject> object, + PropertyNormalizationMode mode, + int expected_additional_properties); + MUST_USE_RESULT MaybeObject* NormalizeProperties( PropertyNormalizationMode mode, int expected_additional_properties); + // Convert and update the elements backing store to be a + // SeededNumberDictionary dictionary. Returns the backing after conversion. + static Handle<SeededNumberDictionary> NormalizeElements( + Handle<JSObject> object); + MUST_USE_RESULT MaybeObject* NormalizeElements(); + static void UpdateMapCodeCache(Handle<JSObject> object, + Handle<String> name, + Handle<Code> code); + MUST_USE_RESULT MaybeObject* UpdateMapCodeCache(String* name, Code* code); // Transform slow named properties to fast variants. // Returns failure if allocation failed. + static void TransformToFastProperties(Handle<JSObject> object, + int unused_property_fields); + MUST_USE_RESULT MaybeObject* TransformToFastProperties( int unused_property_fields); @@ -1923,11 +1943,14 @@ class JSObject: public JSReceiver { WriteBarrierMode mode = UPDATE_WRITE_BARRIER); - // initializes the body after properties slot, properties slot is - // initialized by set_properties - // Note: this call does not update write barrier, it is caller's - // reponsibility to ensure that *v* can be collected without WB here. - inline void InitializeBody(int object_size, Object* value); + // Initializes the body after properties slot, properties slot is + // initialized by set_properties. Fill the pre-allocated fields with + // pre_allocated_value and the rest with filler_value. + // Note: this call does not update write barrier, the caller is responsible + // to ensure that |filler_value| can be collected without WB here. + inline void InitializeBody(Map* map, + Object* pre_allocated_value, + Object* filler_value); // Check whether this object references another object bool ReferencesObject(Object* obj); @@ -1936,6 +1959,7 @@ class JSObject: public JSReceiver { static inline JSObject* cast(Object* obj); // Disalow further properties to be added to the object. + static Handle<Object> PreventExtensions(Handle<JSObject> object); MUST_USE_RESULT MaybeObject* PreventExtensions(); @@ -1962,6 +1986,10 @@ class JSObject: public JSReceiver { void PrintElements(FILE* out); #endif + void PrintElementsTransition( + FILE* file, ElementsKind from_kind, FixedArrayBase* from_elements, + ElementsKind to_kind, FixedArrayBase* to_elements); + #ifdef DEBUG // Structure for collecting spill information about JSObjects. class SpillInformation { @@ -2052,6 +2080,18 @@ class JSObject: public JSReceiver { StrictModeFlag strict_mode, bool check_prototype); + // Searches the prototype chain for a callback setter and sets the property + // with the setter if it finds one. The '*found' flag indicates whether + // a setter was found or not. + // This function can cause GC and can return a failure result with + // '*found==true'. + MUST_USE_RESULT MaybeObject* SetPropertyWithCallbackSetterInPrototypes( + String* name, + Object* value, + PropertyAttributes attributes, + bool* found, + StrictModeFlag strict_mode); + MUST_USE_RESULT MaybeObject* DeletePropertyPostInterceptor(String* name, DeleteMode mode); MUST_USE_RESULT MaybeObject* DeletePropertyWithInterceptor(String* name); @@ -2090,6 +2130,15 @@ class JSObject: public JSReceiver { void LookupInDescriptor(String* name, LookupResult* result); + // Returns the hidden properties backing store object, currently + // a StringDictionary, stored on this object. + // If no hidden properties object has been put on this object, + // return undefined, unless create_if_absent is true, in which case + // a new dictionary is created, added to this object, and returned. + MaybeObject* GetHiddenPropertiesDictionary(bool create_if_absent); + // Updates the existing hidden properties dictionary. + MaybeObject* SetHiddenPropertiesDictionary(StringDictionary* dictionary); + DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject); }; @@ -2145,6 +2194,9 @@ class FixedArray: public FixedArrayBase { // Gives access to raw memory which stores the array's data. inline Object** data_start(); + inline Object** GetFirstElementAddress(); + inline bool ContainsOnlySmisOrHoles(); + // Copy operations. MUST_USE_RESULT inline MaybeObject* Copy(); MUST_USE_RESULT MaybeObject* CopySize(int new_length); @@ -2207,7 +2259,16 @@ class FixedArray: public FixedArrayBase { protected: // Set operation on FixedArray without using write barriers. Can // only be used for storing old space objects or smis. - static inline void fast_set(FixedArray* array, int index, Object* value); + static inline void NoWriteBarrierSet(FixedArray* array, + int index, + Object* value); + + // Set operation on FixedArray without incremental write barrier. Can + // only be used if the object is guaranteed to be white (whiteness witness + // is present). + static inline void NoIncrementalWriteBarrierSet(FixedArray* array, + int index, + Object* value); private: DISALLOW_IMPLICIT_CONSTRUCTORS(FixedArray); @@ -2230,6 +2291,9 @@ class FixedDoubleArray: public FixedArrayBase { // Checking for the hole. inline bool is_the_hole(int index); + // Copy operations + MUST_USE_RESULT inline MaybeObject* Copy(); + // Garbage collection support. inline static int SizeFor(int length) { return kHeaderSize + length * kDoubleSize; @@ -2269,6 +2333,9 @@ class FixedDoubleArray: public FixedArrayBase { }; +class IncrementalMarking; + + // DescriptorArrays are fixed arrays used to hold instance descriptors. // The format of the these objects is: // TODO(1399): It should be possible to make room for bit_field3 in the map @@ -2310,7 +2377,7 @@ class DescriptorArray: public FixedArray { // Set next enumeration index and flush any enum cache. void SetNextEnumerationIndex(int value) { if (!IsEmpty()) { - fast_set(this, kEnumerationIndexIndex, Smi::FromInt(value)); + set(kEnumerationIndexIndex, Smi::FromInt(value)); } } bool HasEnumCache() { @@ -2343,17 +2410,31 @@ class DescriptorArray: public FixedArray { inline Object* GetCallbacksObject(int descriptor_number); inline AccessorDescriptor* GetCallbacks(int descriptor_number); inline bool IsProperty(int descriptor_number); - inline bool IsTransition(int descriptor_number); + inline bool IsTransitionOnly(int descriptor_number); inline bool IsNullDescriptor(int descriptor_number); inline bool IsDontEnum(int descriptor_number); + class WhitenessWitness { + public: + inline explicit WhitenessWitness(DescriptorArray* array); + inline ~WhitenessWitness(); + + private: + IncrementalMarking* marking_; + }; + // Accessor for complete descriptor. inline void Get(int descriptor_number, Descriptor* desc); - inline void Set(int descriptor_number, Descriptor* desc); + inline void Set(int descriptor_number, + Descriptor* desc, + const WhitenessWitness&); // Transfer complete descriptor from another descriptor array to // this one. - inline void CopyFrom(int index, DescriptorArray* src, int src_index); + inline void CopyFrom(int index, + DescriptorArray* src, + int src_index, + const WhitenessWitness&); // Copy the descriptor array, insert a new descriptor and optionally // remove map transitions. If the descriptor is already present, it is @@ -2370,11 +2451,11 @@ class DescriptorArray: public FixedArray { // Sort the instance descriptors by the hash codes of their keys. // Does not check for duplicates. - void SortUnchecked(); + void SortUnchecked(const WhitenessWitness&); // Sort the instance descriptors by the hash codes of their keys. // Checks the result for duplicates. - void Sort(); + void Sort(const WhitenessWitness&); // Search the instance descriptors for given name. inline int Search(String* name); @@ -2467,10 +2548,12 @@ class DescriptorArray: public FixedArray { NULL_DESCRIPTOR; } // Swap operation on FixedArray without using write barriers. - static inline void fast_swap(FixedArray* array, int first, int second); + static inline void NoIncrementalWriteBarrierSwap( + FixedArray* array, int first, int second); // Swap descriptor first and second. - inline void Swap(int first, int second); + inline void NoIncrementalWriteBarrierSwapDescriptors( + int first, int second); FixedArray* GetContentArray() { return FixedArray::cast(get(kContentArrayIndex)); @@ -2488,7 +2571,7 @@ class DescriptorArray: public FixedArray { // encountered and stops when unused elements are encountered. // // - Elements with key == undefined have not been used yet. -// - Elements with key == null have been deleted. +// - Elements with key == the_hole have been deleted. // // The hash table class is parameterized with a Shape and a Key. // Shape must be a class with the following interface: @@ -2523,7 +2606,7 @@ class BaseShape { } static uint32_t HashForObject(Key key, Object* object) { return 0; } static uint32_t SeededHashForObject(Key key, uint32_t seed, Object* object) { - // Won't be called if UsesSeed isn't overridden by child class. + ASSERT(UsesSeed); return HashForObject(key, object); } }; @@ -2534,7 +2617,8 @@ class HashTable: public FixedArray { // Wrapper methods inline uint32_t Hash(Key key) { if (Shape::UsesSeed) { - return Shape::SeededHash(key, GetHeap()->HashSeed()); + return Shape::SeededHash(key, + GetHeap()->HashSeed()); } else { return Shape::Hash(key); } @@ -2542,7 +2626,8 @@ class HashTable: public FixedArray { inline uint32_t HashForObject(Key key, Object* object) { if (Shape::UsesSeed) { - return Shape::SeededHashForObject(key, GetHeap()->HashSeed(), object); + return Shape::SeededHashForObject(key, + GetHeap()->HashSeed(), object); } else { return Shape::HashForObject(key, object); } @@ -2590,10 +2675,10 @@ class HashTable: public FixedArray { // Returns the key at entry. Object* KeyAt(int entry) { return get(EntryToIndex(entry)); } - // Tells whether k is a real key. Null and undefined are not allowed + // Tells whether k is a real key. The hole and undefined are not allowed // as keys and can be used to indicate missing or deleted elements. bool IsKey(Object* k) { - return !k->IsNull() && !k->IsUndefined(); + return !k->IsTheHole() && !k->IsUndefined(); } // Garbage collection support. @@ -2645,12 +2730,12 @@ class HashTable: public FixedArray { // Update the number of elements in the hash table. void SetNumberOfElements(int nof) { - fast_set(this, kNumberOfElementsIndex, Smi::FromInt(nof)); + set(kNumberOfElementsIndex, Smi::FromInt(nof)); } // Update the number of deleted elements in the hash table. void SetNumberOfDeletedElements(int nod) { - fast_set(this, kNumberOfDeletedElementsIndex, Smi::FromInt(nod)); + set(kNumberOfDeletedElementsIndex, Smi::FromInt(nod)); } // Sets the capacity of the hash table. @@ -2660,7 +2745,7 @@ class HashTable: public FixedArray { // and non-zero. ASSERT(capacity > 0); ASSERT(capacity <= kMaxCapacity); - fast_set(this, kCapacityIndex, Smi::FromInt(capacity)); + set(kCapacityIndex, Smi::FromInt(capacity)); } @@ -2868,7 +2953,7 @@ class Dictionary: public HashTable<Shape, Key> { // Accessors for next enumeration index. void SetNextEnumerationIndex(int index) { - this->fast_set(this, kNextEnumerationIndexIndex, Smi::FromInt(index)); + this->set(kNextEnumerationIndexIndex, Smi::FromInt(index)); } int NextEnumerationIndex() { @@ -2948,9 +3033,11 @@ class StringDictionary: public Dictionary<StringDictionaryShape, String*> { JSObject* obj, int unused_property_fields); - // Find entry for key otherwise return kNotFound. Optimzed version of + // Find entry for key, otherwise return kNotFound. Optimized version of // HashTable::FindEntry. int FindEntry(String* key); + + bool ContainsTransition(int entry); }; @@ -2999,6 +3086,13 @@ class SeededNumberDictionary PropertyDetails details); // Set an existing entry or add a new one if needed. + // Return the updated dictionary. + MUST_USE_RESULT static Handle<SeededNumberDictionary> Set( + Handle<SeededNumberDictionary> dictionary, + uint32_t index, + Handle<Object> value, + PropertyDetails details); + MUST_USE_RESULT MaybeObject* Set(uint32_t key, Object* value, PropertyDetails details); @@ -3018,9 +3112,6 @@ class SeededNumberDictionary // requires_slow_elements returns false. inline uint32_t max_number_key(); - // Remove all entries were key is a number and (from <= key && key < to). - void RemoveNumberEntries(uint32_t from, uint32_t to); - // Bit masks. static const int kRequiresSlowElementsMask = 1; static const int kRequiresSlowElementsTagSize = 1; @@ -3041,24 +3132,51 @@ class UnseededNumberDictionary MUST_USE_RESULT MaybeObject* AddNumberEntry(uint32_t key, Object* value); // Set an existing entry or add a new one if needed. + // Return the updated dictionary. + MUST_USE_RESULT static Handle<UnseededNumberDictionary> Set( + Handle<UnseededNumberDictionary> dictionary, + uint32_t index, + Handle<Object> value); + MUST_USE_RESULT MaybeObject* Set(uint32_t key, Object* value); }; +template <int entrysize> class ObjectHashTableShape : public BaseShape<Object*> { public: - static inline bool IsMatch(JSObject* key, Object* other); - static inline uint32_t Hash(JSObject* key); - static inline uint32_t HashForObject(JSObject* key, Object* object); - MUST_USE_RESULT static inline MaybeObject* AsObject(JSObject* key); + static inline bool IsMatch(Object* key, Object* other); + static inline uint32_t Hash(Object* key); + static inline uint32_t HashForObject(Object* key, Object* object); + MUST_USE_RESULT static inline MaybeObject* AsObject(Object* key); static const int kPrefixSize = 0; - static const int kEntrySize = 2; + static const int kEntrySize = entrysize; }; -// ObjectHashTable maps keys that are JavaScript objects to object values by +// ObjectHashSet holds keys that are arbitrary objects by using the identity +// hash of the key for hashing purposes. +class ObjectHashSet: public HashTable<ObjectHashTableShape<1>, Object*> { + public: + static inline ObjectHashSet* cast(Object* obj) { + ASSERT(obj->IsHashTable()); + return reinterpret_cast<ObjectHashSet*>(obj); + } + + // Looks up whether the given key is part of this hash set. + bool Contains(Object* key); + + // Adds the given key to this hash set. + MUST_USE_RESULT MaybeObject* Add(Object* key); + + // Removes the given key from this hash set. + MUST_USE_RESULT MaybeObject* Remove(Object* key); +}; + + +// ObjectHashTable maps keys that are arbitrary objects to object values by // using the identity hash of the key for hashing purposes. -class ObjectHashTable: public HashTable<ObjectHashTableShape, JSObject*> { +class ObjectHashTable: public HashTable<ObjectHashTableShape<2>, Object*> { public: static inline ObjectHashTable* cast(Object* obj) { ASSERT(obj->IsHashTable()); @@ -3067,18 +3185,17 @@ class ObjectHashTable: public HashTable<ObjectHashTableShape, JSObject*> { // Looks up the value associated with the given key. The undefined value is // returned in case the key is not present. - Object* Lookup(JSObject* key); + Object* Lookup(Object* key); // Adds (or overwrites) the value associated with the given key. Mapping a // key to the undefined value causes removal of the whole entry. - MUST_USE_RESULT MaybeObject* Put(JSObject* key, Object* value); + MUST_USE_RESULT MaybeObject* Put(Object* key, Object* value); private: friend class MarkCompactCollector; - void AddEntry(int entry, JSObject* key, Object* value); - void RemoveEntry(int entry, Heap* heap); - inline void RemoveEntry(int entry); + void AddEntry(int entry, Object* key, Object* value); + void RemoveEntry(int entry); // Returns the index to the value of an entry. static inline int EntryToValueIndex(int entry) { @@ -3125,6 +3242,207 @@ class JSFunctionResultCache: public FixedArray { }; +// ScopeInfo represents information about different scopes of a source +// program and the allocation of the scope's variables. Scope information +// is stored in a compressed form in ScopeInfo objects and is used +// at runtime (stack dumps, deoptimization, etc.). + +// This object provides quick access to scope info details for runtime +// routines. +class ScopeInfo : public FixedArray { + public: + static inline ScopeInfo* cast(Object* object); + + // Return the type of this scope. + ScopeType Type(); + + // Does this scope call eval? + bool CallsEval(); + + // Return the language mode of this scope. + LanguageMode language_mode(); + + // Does this scope make a non-strict eval call? + bool CallsNonStrictEval() { + return CallsEval() && (language_mode() == CLASSIC_MODE); + } + + // Return the total number of locals allocated on the stack and in the + // context. This includes the parameters that are allocated in the context. + int LocalCount(); + + // Return the number of stack slots for code. This number consists of two + // parts: + // 1. One stack slot per stack allocated local. + // 2. One stack slot for the function name if it is stack allocated. + int StackSlotCount(); + + // Return the number of context slots for code if a context is allocated. This + // number consists of three parts: + // 1. Size of fixed header for every context: Context::MIN_CONTEXT_SLOTS + // 2. One context slot per context allocated local. + // 3. One context slot for the function name if it is context allocated. + // Parameters allocated in the context count as context allocated locals. If + // no contexts are allocated for this scope ContextLength returns 0. + int ContextLength(); + + // Is this scope the scope of a named function expression? + bool HasFunctionName(); + + // Return if this has context allocated locals. + bool HasHeapAllocatedLocals(); + + // Return if contexts are allocated for this scope. + bool HasContext(); + + // Return the function_name if present. + String* FunctionName(); + + // Return the name of the given parameter. + String* ParameterName(int var); + + // Return the name of the given local. + String* LocalName(int var); + + // Return the name of the given stack local. + String* StackLocalName(int var); + + // Return the name of the given context local. + String* ContextLocalName(int var); + + // Return the mode of the given context local. + VariableMode ContextLocalMode(int var); + + // Return the initialization flag of the given context local. + InitializationFlag ContextLocalInitFlag(int var); + + // Lookup support for serialized scope info. Returns the + // the stack slot index for a given slot name if the slot is + // present; otherwise returns a value < 0. The name must be a symbol + // (canonicalized). + int StackSlotIndex(String* name); + + // Lookup support for serialized scope info. Returns the + // context slot index for a given slot name if the slot is present; otherwise + // returns a value < 0. The name must be a symbol (canonicalized). + // If the slot is present and mode != NULL, sets *mode to the corresponding + // mode for that variable. + int ContextSlotIndex(String* name, + VariableMode* mode, + InitializationFlag* init_flag); + + // Lookup support for serialized scope info. Returns the + // parameter index for a given parameter name if the parameter is present; + // otherwise returns a value < 0. The name must be a symbol (canonicalized). + int ParameterIndex(String* name); + + // Lookup support for serialized scope info. Returns the + // function context slot index if the function name is present (named + // function expressions, only), otherwise returns a value < 0. The name + // must be a symbol (canonicalized). + int FunctionContextSlotIndex(String* name, VariableMode* mode); + + static Handle<ScopeInfo> Create(Scope* scope); + + // Serializes empty scope info. + static ScopeInfo* Empty(); + +#ifdef DEBUG + void Print(); +#endif + + // The layout of the static part of a ScopeInfo is as follows. Each entry is + // numeric and occupies one array slot. + // 1. A set of properties of the scope + // 2. The number of parameters. This only applies to function scopes. For + // non-function scopes this is 0. + // 3. The number of non-parameter variables allocated on the stack. + // 4. The number of non-parameter and parameter variables allocated in the + // context. +#define FOR_EACH_NUMERIC_FIELD(V) \ + V(Flags) \ + V(ParameterCount) \ + V(StackLocalCount) \ + V(ContextLocalCount) + +#define FIELD_ACCESSORS(name) \ + void Set##name(int value) { \ + set(k##name, Smi::FromInt(value)); \ + } \ + int name() { \ + if (length() > 0) { \ + return Smi::cast(get(k##name))->value(); \ + } else { \ + return 0; \ + } \ + } + FOR_EACH_NUMERIC_FIELD(FIELD_ACCESSORS) +#undef FIELD_ACCESSORS + + private: + enum { +#define DECL_INDEX(name) k##name, + FOR_EACH_NUMERIC_FIELD(DECL_INDEX) +#undef DECL_INDEX +#undef FOR_EACH_NUMERIC_FIELD + kVariablePartIndex + }; + + // The layout of the variable part of a ScopeInfo is as follows: + // 1. ParameterEntries: + // This part stores the names of the parameters for function scopes. One + // slot is used per parameter, so in total this part occupies + // ParameterCount() slots in the array. For other scopes than function + // scopes ParameterCount() is 0. + // 2. StackLocalEntries: + // Contains the names of local variables that are allocated on the stack, + // in increasing order of the stack slot index. One slot is used per stack + // local, so in total this part occupies StackLocalCount() slots in the + // array. + // 3. ContextLocalNameEntries: + // Contains the names of local variables and parameters that are allocated + // in the context. They are stored in increasing order of the context slot + // index starting with Context::MIN_CONTEXT_SLOTS. One slot is used per + // context local, so in total this part occupies ContextLocalCount() slots + // in the array. + // 4. ContextLocalInfoEntries: + // Contains the variable modes and initialization flags corresponding to + // the context locals in ContextLocalNameEntries. One slot is used per + // context local, so in total this part occupies ContextLocalCount() + // slots in the array. + // 5. FunctionNameEntryIndex: + // If the scope belongs to a named function expression this part contains + // information about the function variable. It always occupies two array + // slots: a. The name of the function variable. + // b. The context or stack slot index for the variable. + int ParameterEntriesIndex(); + int StackLocalEntriesIndex(); + int ContextLocalNameEntriesIndex(); + int ContextLocalInfoEntriesIndex(); + int FunctionNameEntryIndex(); + + // Location of the function variable for named function expressions. + enum FunctionVariableInfo { + NONE, // No function name present. + STACK, // Function + CONTEXT, + UNUSED + }; + + // Properties of scopes. + class TypeField: public BitField<ScopeType, 0, 3> {}; + class CallsEvalField: public BitField<bool, 3, 1> {}; + class LanguageModeField: public BitField<LanguageMode, 4, 2> {}; + class FunctionVariableField: public BitField<FunctionVariableInfo, 6, 2> {}; + class FunctionVariableMode: public BitField<VariableMode, 8, 3> {}; + + // BitFields representing the encoded information for context locals in the + // ContextLocalInfoEntries part. + class ContextLocalMode: public BitField<VariableMode, 0, 3> {}; + class ContextLocalInitFlag: public BitField<InitializationFlag, 3, 1> {}; +}; + + // The cache for maps used by normalized (dictionary mode) objects. // Such maps do not have property descriptors, so a typical program // needs very limited number of distinct normalized maps. @@ -3146,11 +3464,12 @@ class NormalizedMapCache: public FixedArray { }; -// ByteArray represents fixed sized byte arrays. Used by the outside world, -// such as PCRE, and also by the memory allocator and garbage collector to -// fill in free blocks in the heap. +// ByteArray represents fixed sized byte arrays. Used for the relocation info +// that is attached to code objects. class ByteArray: public FixedArrayBase { public: + inline int Size() { return RoundUp(length() + kHeaderSize, kPointerSize); } + // Setter and getter. inline byte get(int index); inline void set(int index, byte value); @@ -3207,6 +3526,41 @@ class ByteArray: public FixedArrayBase { }; +// FreeSpace represents fixed sized areas of the heap that are not currently in +// use. Used by the heap and GC. +class FreeSpace: public HeapObject { + public: + // [size]: size of the free space including the header. + inline int size(); + inline void set_size(int value); + + inline int Size() { return size(); } + + // Casting. + static inline FreeSpace* cast(Object* obj); + +#ifdef OBJECT_PRINT + inline void FreeSpacePrint() { + FreeSpacePrint(stdout); + } + void FreeSpacePrint(FILE* out); +#endif +#ifdef DEBUG + void FreeSpaceVerify(); +#endif + + // Layout description. + // Size is smi tagged when it is stored. + static const int kSizeOffset = HeapObject::kHeaderSize; + static const int kHeaderSize = kSizeOffset + kPointerSize; + + static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(FreeSpace); +}; + + // An ExternalArray represents a fixed-size array of primitive values // which live outside the JavaScript heap. Its subclasses are used to // implement the CanvasArray types being defined in the WebGL @@ -3553,11 +3907,6 @@ class DeoptimizationInputData: public FixedArray { DEFINE_ELEMENT_ACCESSORS(OsrAstId, Smi) DEFINE_ELEMENT_ACCESSORS(OsrPcOffset, Smi) - // Unchecked accessor to be used during GC. - FixedArray* UncheckedLiteralArray() { - return reinterpret_cast<FixedArray*>(get(kLiteralArrayIndex)); - } - #undef DEFINE_ELEMENT_ACCESSORS // Accessors for elements of the ith deoptimization entry. @@ -3632,8 +3981,44 @@ class DeoptimizationOutputData: public FixedArray { }; -class SafepointEntry; +// Forward declaration. +class JSGlobalPropertyCell; + +// TypeFeedbackCells is a fixed array used to hold the association between +// cache cells and AST ids for code generated by the full compiler. +// The format of the these objects is +// [i * 2]: Global property cell of ith cache cell. +// [i * 2 + 1]: Ast ID for ith cache cell. +class TypeFeedbackCells: public FixedArray { + public: + int CellCount() { return length() / 2; } + static int LengthOfFixedArray(int cell_count) { return cell_count * 2; } + // Accessors for AST ids associated with cache values. + inline Smi* AstId(int index); + inline void SetAstId(int index, Smi* id); + + // Accessors for global property cells holding the cache values. + inline JSGlobalPropertyCell* Cell(int index); + inline void SetCell(int index, JSGlobalPropertyCell* cell); + + // The object that indicates an uninitialized cache. + static inline Handle<Object> UninitializedSentinel(Isolate* isolate); + + // The object that indicates a megamorphic state. + static inline Handle<Object> MegamorphicSentinel(Isolate* isolate); + + // A raw version of the uninitialized sentinel that's safe to read during + // garbage collection (e.g., for patching the cache). + static inline Object* RawUninitializedSentinel(Heap* heap); + + // Casting. + static inline TypeFeedbackCells* cast(Object* obj); +}; + + +// Forward declaration. +class SafepointEntry; // Code describes objects with on-the-fly generated machine code. class Code: public HeapObject { @@ -3699,14 +4084,19 @@ class Code: public HeapObject { DECL_ACCESSORS(relocation_info, ByteArray) void InvalidateRelocation(); + // [handler_table]: Fixed array containing offsets of exception handlers. + DECL_ACCESSORS(handler_table, FixedArray) + // [deoptimization_data]: Array containing data for deopt. DECL_ACCESSORS(deoptimization_data, FixedArray) - // [code_flushing_candidate]: Field only used during garbage - // collection to hold code flushing candidates. The contents of this + // [type_feedback_cells]: Array containing cache cells used for type feedback. + DECL_ACCESSORS(type_feedback_cells, TypeFeedbackCells) + + // [gc_metadata]: Field used to hold GC related metadata. The contents of this // field does not have to be traced during garbage collection since // it is only used by the garbage collector itself. - DECL_ACCESSORS(next_code_flushing_candidate, Object) + DECL_ACCESSORS(gc_metadata, Object) // Unchecked accessors to be used during GC. inline ByteArray* unchecked_relocation_info(); @@ -3742,6 +4132,11 @@ class Code: public HeapObject { inline int major_key(); inline void set_major_key(int value); + // For stubs, tells whether they should always exist, so that they can be + // called from other stubs. + inline bool is_pregenerated(); + inline void set_is_pregenerated(bool value); + // [optimizable]: For FUNCTION kind, tells if it is optimizable. inline bool optimizable(); inline void set_optimizable(bool value); @@ -3756,6 +4151,11 @@ class Code: public HeapObject { inline bool has_debug_break_slots(); inline void set_has_debug_break_slots(bool value); + // [compiled_with_optimizing]: For FUNCTION kind, tells if it has + // been compiled with IsOptimizing set to true. + inline bool is_compiled_optimizable(); + inline void set_compiled_optimizable(bool value); + // [allow_osr_at_loop_nesting_level]: For FUNCTION kind, tells for // how long the function has been marked for OSR and therefore which // level of loop nesting we are willing to do on-stack replacement @@ -3801,6 +4201,11 @@ class Code: public HeapObject { inline byte to_boolean_state(); inline void set_to_boolean_state(byte value); + // [has_function_cache]: For kind STUB tells whether there is a function + // cache is passed to the stub. + inline bool has_function_cache(); + inline void set_has_function_cache(bool flag); + // Get the safepoint entry for the given pc. SafepointEntry GetSafepointEntry(Address pc); @@ -3905,10 +4310,6 @@ class Code: public HeapObject { void CodeVerify(); #endif - // Returns the isolate/heap this code object belongs to. - inline Isolate* isolate(); - inline Heap* heap(); - // Max loop nesting marker used to postpose OSR. We don't take loop // nesting that is deeper than 5 levels into account. static const int kMaxLoopNestingMarker = 6; @@ -3916,12 +4317,13 @@ class Code: public HeapObject { // Layout description. static const int kInstructionSizeOffset = HeapObject::kHeaderSize; static const int kRelocationInfoOffset = kInstructionSizeOffset + kIntSize; + static const int kHandlerTableOffset = kRelocationInfoOffset + kPointerSize; static const int kDeoptimizationDataOffset = - kRelocationInfoOffset + kPointerSize; - static const int kNextCodeFlushingCandidateOffset = + kHandlerTableOffset + kPointerSize; + static const int kTypeFeedbackCellsOffset = kDeoptimizationDataOffset + kPointerSize; - static const int kFlagsOffset = - kNextCodeFlushingCandidateOffset + kPointerSize; + static const int kGCMetadataOffset = kTypeFeedbackCellsOffset + kPointerSize; + static const int kFlagsOffset = kGCMetadataOffset + kPointerSize; static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize; static const int kKindSpecificFlagsSize = 2 * kIntSize; @@ -3944,11 +4346,13 @@ class Code: public HeapObject { static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1; static const int kCompareStateOffset = kStubMajorKeyOffset + 1; static const int kToBooleanTypeOffset = kStubMajorKeyOffset + 1; + static const int kHasFunctionCacheOffset = kStubMajorKeyOffset + 1; static const int kFullCodeFlags = kOptimizableOffset + 1; class FullCodeFlagsHasDeoptimizationSupportField: public BitField<bool, 0, 1> {}; // NOLINT class FullCodeFlagsHasDebugBreakSlotsField: public BitField<bool, 1, 1> {}; + class FullCodeFlagsIsCompiledOptimizable: public BitField<bool, 2, 1> {}; static const int kBinaryOpReturnTypeOffset = kBinaryOpTypeOffset + 1; @@ -3960,14 +4364,16 @@ class Code: public HeapObject { // Flags layout. BitField<type, shift, size>. class ICStateField: public BitField<InlineCacheState, 0, 3> {}; class TypeField: public BitField<PropertyType, 3, 4> {}; - class KindField: public BitField<Kind, 7, 4> {}; - class CacheHolderField: public BitField<InlineCacheHolderFlag, 11, 1> {}; + class CacheHolderField: public BitField<InlineCacheHolderFlag, 7, 1> {}; + class KindField: public BitField<Kind, 8, 4> {}; class ExtraICStateField: public BitField<ExtraICState, 12, 2> {}; + class IsPregeneratedField: public BitField<bool, 14, 1> {}; // Signed field cannot be encoded using the BitField class. - static const int kArgumentsCountShift = 14; + static const int kArgumentsCountShift = 15; static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1); + // This constant should be encodable in an ARM instruction. static const int kFlagsNotUsedInLookup = TypeField::kMask | CacheHolderField::kMask; @@ -4101,8 +4507,12 @@ class Map: public HeapObject { (bit_field2() & kElementsKindMask) >> kElementsKindShift); } + // Tells whether the instance has fast elements that are only Smis. + inline bool has_fast_smi_only_elements() { + return elements_kind() == FAST_SMI_ONLY_ELEMENTS; + } + // Tells whether the instance has fast elements. - // Equivalent to instance->GetElementsKind() == FAST_ELEMENTS. inline bool has_fast_elements() { return elements_kind() == FAST_ELEMENTS; } @@ -4111,6 +4521,10 @@ class Map: public HeapObject { return elements_kind() == FAST_DOUBLE_ELEMENTS; } + inline bool has_non_strict_arguments_elements() { + return elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS; + } + inline bool has_external_array_elements() { ElementsKind kind(elements_kind()); return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && @@ -4121,6 +4535,14 @@ class Map: public HeapObject { return elements_kind() == DICTIONARY_ELEMENTS; } + inline bool has_slow_elements_kind() { + return elements_kind() == DICTIONARY_ELEMENTS + || elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS; + } + + static bool IsValidElementsTransition(ElementsKind from_kind, + ElementsKind to_kind); + // Tells whether the map is attached to SharedFunctionInfo // (for inobject slack tracking). inline void set_attached_to_shared_function_info(bool value); @@ -4169,6 +4591,7 @@ class Map: public HeapObject { // 1 + 2 * i: prototype // 2 + 2 * i: target map DECL_ACCESSORS(prototype_transitions, FixedArray) + inline FixedArray* unchecked_prototype_transitions(); static const int kProtoTransitionHeaderSize = 1; @@ -4178,14 +4601,14 @@ class Map: public HeapObject { static const int kProtoTransitionMapOffset = 1; inline int NumberOfProtoTransitions() { - FixedArray* cache = unchecked_prototype_transitions(); + FixedArray* cache = prototype_transitions(); if (cache->length() == 0) return 0; return Smi::cast(cache->get(kProtoTransitionNumberOfEntriesOffset))->value(); } inline void SetNumberOfProtoTransitions(int value) { - FixedArray* cache = unchecked_prototype_transitions(); + FixedArray* cache = prototype_transitions(); ASSERT(cache->length() != 0); cache->set_unchecked(kProtoTransitionNumberOfEntriesOffset, Smi::FromInt(value)); @@ -4207,35 +4630,15 @@ class Map: public HeapObject { // instance descriptors. MUST_USE_RESULT MaybeObject* CopyDropTransitions(); - // Returns this map if it already has elements that are fast, otherwise - // returns a copy of the map, with all transitions dropped from the - // descriptors and the ElementsKind set to FAST_ELEMENTS. - MUST_USE_RESULT inline MaybeObject* GetFastElementsMap(); - - // Returns this map if it already has fast elements that are doubles, - // otherwise returns a copy of the map, with all transitions dropped from the - // descriptors and the ElementsKind set to FAST_DOUBLE_ELEMENTS. - MUST_USE_RESULT inline MaybeObject* GetFastDoubleElementsMap(); - - // Returns this map if already has dictionary elements, otherwise returns a - // copy of the map, with all transitions dropped from the descriptors and the - // ElementsKind set to DICTIONARY_ELEMENTS. - MUST_USE_RESULT inline MaybeObject* GetSlowElementsMap(); - - // Returns a new map with all transitions dropped from the descriptors and the - // ElementsKind set. - MUST_USE_RESULT MaybeObject* GetElementsTransitionMap( - ElementsKind elements_kind, - bool safe_to_add_transition); - // Returns the property index for name (only valid for FAST MODE). int PropertyIndexFor(String* name); // Returns the next free property index (only valid for FAST MODE). int NextFreePropertyIndex(); - // Returns the number of properties described in instance_descriptors. - int NumberOfDescribedProperties(); + // Returns the number of properties described in instance_descriptors + // filtering out properties with the specified attributes. + int NumberOfDescribedProperties(PropertyAttributes filter = NONE); // Casting. static inline Map* cast(Object* obj); @@ -4249,6 +4652,9 @@ class Map: public HeapObject { inline void ClearCodeCache(Heap* heap); // Update code cache. + static void UpdateCodeCache(Handle<Map> map, + Handle<String> name, + Handle<Code> code); MUST_USE_RESULT MaybeObject* UpdateCodeCache(String* name, Code* code); // Returns the found code or undefined if absent. @@ -4266,12 +4672,21 @@ class Map: public HeapObject { // This is undone in MarkCompactCollector::ClearNonLiveTransitions(). void CreateBackPointers(); + void CreateOneBackPointer(Object* transition_target); + // Set all map transitions from this map to dead maps to null. // Also, restore the original prototype on the targets of these // transitions, so that we do not process this map again while // following back pointers. void ClearNonLiveTransitions(Heap* heap, Object* real_prototype); + // Restore a possible back pointer in the prototype field of object. + // Return true in that case and false otherwise. Set *keep_entry to + // true when a live map transition has been found. + bool RestoreOneBackPointer(Object* object, + Object* real_prototype, + bool* keep_entry); + // Computes a hash value for this map, to be used in HashTables and such. int Hash(); @@ -4281,11 +4696,30 @@ class Map: public HeapObject { // The "shared" flags of both this map and |other| are ignored. bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode); - // Returns true if this map and |other| describe equivalent objects. - // The "shared" flags of both this map and |other| are ignored. - bool EquivalentTo(Map* other) { - return EquivalentToForNormalization(other, KEEP_INOBJECT_PROPERTIES); - } + // Returns the contents of this map's descriptor array for the given string. + // May return NULL. |safe_to_add_transition| is set to false and NULL + // is returned if adding transitions is not allowed. + Object* GetDescriptorContents(String* sentinel_name, + bool* safe_to_add_transitions); + + // Returns the map that this map transitions to if its elements_kind + // is changed to |elements_kind|, or NULL if no such map is cached yet. + // |safe_to_add_transitions| is set to false if adding transitions is not + // allowed. + Map* LookupElementsTransitionMap(ElementsKind elements_kind, + bool* safe_to_add_transition); + + // Adds an entry to this map's descriptor array for a transition to + // |transitioned_map| when its elements_kind is changed to |elements_kind|. + MaybeObject* AddElementsTransition(ElementsKind elements_kind, + Map* transitioned_map); + + // Returns the transitioned map for this map with the most generic + // elements_kind that's found in |candidates|, or null handle if no match is + // found at all. + Handle<Map> FindTransitionedMap(MapHandleList* candidates); + Map* FindTransitionedMap(MapList* candidates); + // Dispatched behavior. #ifdef OBJECT_PRINT @@ -4302,10 +4736,6 @@ class Map: public HeapObject { inline int visitor_id(); inline void set_visitor_id(int visitor_id); - // Returns the isolate/heap this map belongs to. - inline Isolate* isolate(); - inline Heap* heap(); - typedef void (*TraverseCallback)(Map* map, void* data); void TraverseTransitionTree(TraverseCallback callback, void* data); @@ -4342,7 +4772,7 @@ class Map: public HeapObject { static const int kSize = MAP_POINTER_ALIGN(kPadStart); // Layout of pointer fields. Heap iteration code relies on them - // being continiously allocated. + // being continuously allocated. static const int kPointerFieldsBeginOffset = Map::kPrototypeOffset; static const int kPointerFieldsEndOffset = Map::kPrototypeTransitionsOffset + kPointerSize; @@ -4382,7 +4812,7 @@ class Map: public HeapObject { static const int kStringWrapperSafeForDefaultValueOf = 2; static const int kAttachedToSharedFunctionInfo = 3; // No bits can be used after kElementsKindFirstBit, they are all reserved for - // storing ElementKind. for anything other than storing the ElementKind. + // storing ElementKind. static const int kElementsKindShift = 4; static const int kElementsKindBitCount = 4; @@ -4391,6 +4821,9 @@ class Map: public HeapObject { ((1 << (kElementsKindShift + kElementsKindBitCount)) - 1); static const int8_t kMaximumBitField2FastElementValue = static_cast<int8_t>( (FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1; + static const int8_t kMaximumBitField2FastSmiOnlyElementValue = + static_cast<int8_t>((FAST_SMI_ONLY_ELEMENTS + 1) << + Map::kElementsKindShift) - 1; // Bit positions for bit field 3 static const int kIsShared = 0; @@ -4405,6 +4838,7 @@ class Map: public HeapObject { kSize> BodyDescriptor; private: + String* elements_transition_sentinel_name(); DISALLOW_IMPLICIT_CONSTRUCTORS(Map); }; @@ -4546,7 +4980,10 @@ class Script: public Struct { V(Math, atan, MathATan) \ V(Math, exp, MathExp) \ V(Math, sqrt, MathSqrt) \ - V(Math, pow, MathPow) + V(Math, pow, MathPow) \ + V(Math, random, MathRandom) \ + V(Math, max, MathMax) \ + V(Math, min, MathMin) enum BuiltinFunctionId { @@ -4572,7 +5009,7 @@ class SharedFunctionInfo: public HeapObject { DECL_ACCESSORS(code, Code) // [scope_info]: Scope info. - DECL_ACCESSORS(scope_info, SerializedScopeInfo) + DECL_ACCESSORS(scope_info, ScopeInfo) // [construct stub]: Code stub for constructing instances of this function. DECL_ACCESSORS(construct_stub, Code) @@ -4760,8 +5197,14 @@ class SharedFunctionInfo: public HeapObject { // A counter used to determine when to stress the deoptimizer with a // deopt. - inline Smi* deopt_counter(); - inline void set_deopt_counter(Smi* counter); + inline int deopt_counter(); + inline void set_deopt_counter(int counter); + + inline int profiler_ticks(); + inline void set_profiler_ticks(int ticks); + + inline int ast_node_count(); + inline void set_ast_node_count(int count); // Add information on assignments of the form this.x = ...; void SetThisPropertyAssignmentsInfo( @@ -4794,8 +5237,20 @@ class SharedFunctionInfo: public HeapObject { // spending time attempting to optimize it again. DECL_BOOLEAN_ACCESSORS(optimization_disabled) - // Indicates whether the function is a strict mode function. - DECL_BOOLEAN_ACCESSORS(strict_mode) + // Indicates the language mode of the function's code as defined by the + // current harmony drafts for the next ES language standard. Possible + // values are: + // 1. CLASSIC_MODE - Unrestricted syntax and semantics, same as in ES5. + // 2. STRICT_MODE - Restricted syntax and semantics, same as in ES5. + // 3. EXTENDED_MODE - Only available under the harmony flag, not part of ES5. + inline LanguageMode language_mode(); + inline void set_language_mode(LanguageMode language_mode); + + // Indicates whether the language mode of this function is CLASSIC_MODE. + inline bool is_classic_mode(); + + // Indicates whether the language mode of this function is EXTENDED_MODE. + inline bool is_extended_mode(); // False if the function definitely does not allocate an arguments object. DECL_BOOLEAN_ACCESSORS(uses_arguments) @@ -4823,6 +5278,12 @@ class SharedFunctionInfo: public HeapObject { // through the API, which does not change this flag). DECL_BOOLEAN_ACCESSORS(is_anonymous) + // Indicates that the function cannot be crankshafted. + DECL_BOOLEAN_ACCESSORS(dont_crankshaft) + + // Indicates that the function cannot be inlined. + DECL_BOOLEAN_ACCESSORS(dont_inline) + // Indicates whether or not the code in the shared function support // deoptimization. inline bool has_deoptimization_support(); @@ -4860,7 +5321,7 @@ class SharedFunctionInfo: public HeapObject { // [source code]: Source code for the function. bool HasSourceCode(); - Object* GetSourceCode(); + Handle<Object> GetSourceCode(); inline int opt_count(); inline void set_opt_count(int opt_count); @@ -4887,6 +5348,13 @@ class SharedFunctionInfo: public HeapObject { void SharedFunctionInfoVerify(); #endif + // Helpers to compile the shared code. Returns true on success, false on + // failure (e.g., stack overflow during compilation). + static bool EnsureCompiled(Handle<SharedFunctionInfo> shared, + ClearExceptionFlag flag); + static bool CompileLazy(Handle<SharedFunctionInfo> shared, + ClearExceptionFlag flag); + // Casting. static inline SharedFunctionInfo* cast(Object* obj); @@ -4910,12 +5378,12 @@ class SharedFunctionInfo: public HeapObject { kInferredNameOffset + kPointerSize; static const int kThisPropertyAssignmentsOffset = kInitialMapOffset + kPointerSize; - static const int kDeoptCounterOffset = + static const int kProfilerTicksOffset = kThisPropertyAssignmentsOffset + kPointerSize; #if V8_HOST_ARCH_32_BIT // Smi fields. static const int kLengthOffset = - kDeoptCounterOffset + kPointerSize; + kProfilerTicksOffset + kPointerSize; static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize; static const int kExpectedNofPropertiesOffset = kFormalParameterCountOffset + kPointerSize; @@ -4933,8 +5401,11 @@ class SharedFunctionInfo: public HeapObject { kCompilerHintsOffset + kPointerSize; static const int kOptCountOffset = kThisPropertyAssignmentsCountOffset + kPointerSize; + static const int kAstNodeCountOffset = kOptCountOffset + kPointerSize; + static const int kDeoptCounterOffset = + kAstNodeCountOffset + kPointerSize; // Total size. - static const int kSize = kOptCountOffset + kPointerSize; + static const int kSize = kDeoptCounterOffset + kPointerSize; #else // The only reason to use smi fields instead of int fields // is to allow iteration without maps decoding during @@ -4946,7 +5417,7 @@ class SharedFunctionInfo: public HeapObject { // word is not set and thus this word cannot be treated as pointer // to HeapObject during old space traversal. static const int kLengthOffset = - kDeoptCounterOffset + kPointerSize; + kProfilerTicksOffset + kPointerSize; static const int kFormalParameterCountOffset = kLengthOffset + kIntSize; @@ -4970,8 +5441,11 @@ class SharedFunctionInfo: public HeapObject { static const int kOptCountOffset = kThisPropertyAssignmentsCountOffset + kIntSize; + static const int kAstNodeCountOffset = kOptCountOffset + kIntSize; + static const int kDeoptCounterOffset = kAstNodeCountOffset + kIntSize; + // Total size. - static const int kSize = kOptCountOffset + kIntSize; + static const int kSize = kDeoptCounterOffset + kIntSize; #endif @@ -5011,12 +5485,15 @@ class SharedFunctionInfo: public HeapObject { kCodeAgeShift, kOptimizationDisabled = kCodeAgeShift + kCodeAgeSize, kStrictModeFunction, + kExtendedModeFunction, kUsesArguments, kHasDuplicateParameters, kNative, kBoundFunction, kIsAnonymous, kNameShouldPrintAsAnonymous, + kDontCrankshaft, + kDontInline, kCompilerHintsCount // Pseudo entry }; @@ -5037,22 +5514,30 @@ class SharedFunctionInfo: public HeapObject { public: // Constants for optimizing codegen for strict mode function and // native tests. - // Allows to use byte-widgh instructions. + // Allows to use byte-width instructions. static const int kStrictModeBitWithinByte = (kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte; + static const int kExtendedModeBitWithinByte = + (kExtendedModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte; + static const int kNativeBitWithinByte = (kNative + kCompilerHintsSmiTagSize) % kBitsPerByte; #if __BYTE_ORDER == __LITTLE_ENDIAN static const int kStrictModeByteOffset = kCompilerHintsOffset + (kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte; + static const int kExtendedModeByteOffset = kCompilerHintsOffset + + (kExtendedModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte; static const int kNativeByteOffset = kCompilerHintsOffset + (kNative + kCompilerHintsSmiTagSize) / kBitsPerByte; #elif __BYTE_ORDER == __BIG_ENDIAN static const int kStrictModeByteOffset = kCompilerHintsOffset + (kCompilerHintsSize - 1) - ((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte); + static const int kExtendedModeByteOffset = kCompilerHintsOffset + + (kCompilerHintsSize - 1) - + ((kExtendedModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte); static const int kNativeByteOffset = kCompilerHintsOffset + (kCompilerHintsSize - 1) - ((kNative + kCompilerHintsSmiTagSize) / kBitsPerByte); @@ -5108,6 +5593,14 @@ class JSFunction: public JSObject { // recompiled the next time it is executed. void MarkForLazyRecompilation(); + // Helpers to compile this function. Returns true on success, false on + // failure (e.g., stack overflow during compilation). + static bool CompileLazy(Handle<JSFunction> function, + ClearExceptionFlag flag); + static bool CompileOptimized(Handle<JSFunction> function, + int osr_ast_id, + ClearExceptionFlag flag); + // Tells whether or not the function is already marked for lazy // recompilation. inline bool IsMarkedForLazyRecompilation(); @@ -5115,7 +5608,8 @@ class JSFunction: public JSObject { // Check whether or not this function is inlineable. bool IsInlineable(); - // [literals]: Fixed array holding the materialized literals. + // [literals_or_bindings]: Fixed array holding either + // the materialized literals or the bindings of a bound function. // // If the function contains object, regexp or array literals, the // literals array prefix contains the object, regexp, and array @@ -5124,11 +5618,22 @@ class JSFunction: public JSObject { // or array functions. Performing a dynamic lookup, we might end up // using the functions from a new context that we should not have // access to. - DECL_ACCESSORS(literals, FixedArray) + // + // On bound functions, the array is a (copy-on-write) fixed-array containing + // the function that was bound, bound this-value and any bound + // arguments. Bound functions never contain literals. + DECL_ACCESSORS(literals_or_bindings, FixedArray) + + inline FixedArray* literals(); + inline void set_literals(FixedArray* literals); + + inline FixedArray* function_bindings(); + inline void set_function_bindings(FixedArray* bindings); // The initial map for an object created by this constructor. inline Map* initial_map(); inline void set_initial_map(Map* value); + inline MaybeObject* set_initial_map_and_cache_transitions(Map* value); inline bool has_initial_map(); // Get and set the prototype property on a JSFunction. If the @@ -5139,7 +5644,7 @@ class JSFunction: public JSObject { inline bool has_instance_prototype(); inline Object* prototype(); inline Object* instance_prototype(); - Object* SetInstancePrototype(Object* value); + MaybeObject* SetInstancePrototype(Object* value); MUST_USE_RESULT MaybeObject* SetPrototype(Object* value); // After prototype is removed, it will not be created when accessed, and @@ -5212,6 +5717,11 @@ class JSFunction: public JSObject { static const int kLiteralsPrefixSize = 1; static const int kLiteralGlobalContextIndex = 0; + // Layout of the bound-function binding array. + static const int kBoundFunctionIndex = 0; + static const int kBoundThisIndex = 1; + static const int kBoundArgumentsStartIndex = 2; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunction); }; @@ -5256,7 +5766,6 @@ class JSGlobalProxy : public JSObject { // Forward declaration. class JSBuiltinsObject; -class JSGlobalPropertyCell; // Common super class for JavaScript global objects and the special // builtins global objects. @@ -5284,6 +5793,11 @@ class GlobalObject: public JSObject { } // Ensure that the global object has a cell for the given property name. + static Handle<JSGlobalPropertyCell> EnsurePropertyCell( + Handle<GlobalObject> global, + Handle<String> name); + // TODO(kmillikin): This function can be eliminated once the stub cache is + // full handlified (and the static helper can be written directly). MUST_USE_RESULT MaybeObject* EnsurePropertyCell(String* name); // Casting. @@ -5296,8 +5810,6 @@ class GlobalObject: public JSObject { static const int kHeaderSize = kGlobalReceiverOffset + kPointerSize; private: - friend class AGCCVersionRequiresThisClassToHaveAFriendSoHereItIs; - DISALLOW_IMPLICIT_CONSTRUCTORS(GlobalObject); }; @@ -5640,12 +6152,16 @@ class CompilationCacheTable: public HashTable<CompilationCacheShape, public: // Find cached value for a string key, otherwise return null. Object* Lookup(String* src); - Object* LookupEval(String* src, Context* context, StrictModeFlag strict_mode); + Object* LookupEval(String* src, + Context* context, + LanguageMode language_mode, + int scope_position); Object* LookupRegExp(String* source, JSRegExp::Flags flags); MaybeObject* Put(String* src, Object* value); MaybeObject* PutEval(String* src, Context* context, - SharedFunctionInfo* value); + SharedFunctionInfo* value, + int scope_position); MaybeObject* PutRegExp(String* src, JSRegExp::Flags flags, FixedArray* value); // Remove given value from cache. @@ -5758,10 +6274,17 @@ class PolymorphicCodeCache: public Struct { public: DECL_ACCESSORS(cache, Object) - MUST_USE_RESULT MaybeObject* Update(MapList* maps, + static void Update(Handle<PolymorphicCodeCache> cache, + MapHandleList* maps, + Code::Flags flags, + Handle<Code> code); + + MUST_USE_RESULT MaybeObject* Update(MapHandleList* maps, Code::Flags flags, Code* code); - Object* Lookup(MapList* maps, Code::Flags flags); + + // Returns an undefined value if the entry is not found. + Handle<Object> Lookup(MapHandleList* maps, Code::Flags flags); static inline PolymorphicCodeCache* cast(Object* obj); @@ -5786,8 +6309,11 @@ class PolymorphicCodeCache: public Struct { class PolymorphicCodeCacheHashTable : public HashTable<CodeCacheHashTableShape, HashTableKey*> { public: - Object* Lookup(MapList* maps, int code_kind); - MUST_USE_RESULT MaybeObject* Put(MapList* maps, int code_kind, Code* code); + Object* Lookup(MapHandleList* maps, int code_kind); + + MUST_USE_RESULT MaybeObject* Put(MapHandleList* maps, + int code_kind, + Code* code); static inline PolymorphicCodeCacheHashTable* cast(Object* obj); @@ -6033,7 +6559,7 @@ class String: public HeapObject { inline String* GetUnderlying(); // Mark the string as an undetectable object. It only applies to - // ascii and two byte string types. + // ASCII and two byte string types. bool MarkAsUndetectable(); // Return a substring. @@ -6065,7 +6591,8 @@ class String: public HeapObject { RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL, int* length_output = 0); - int Utf8Length(); + inline int Utf8Length() { return Utf8Length(this, 0, length()); } + static int Utf8Length(String* input, int from, int to); // Return a 16 bit Unicode representation of the string. // The string should be nearly flat, otherwise the performance of @@ -6129,14 +6656,11 @@ class String: public HeapObject { // value into an array index. static const int kMaxArrayIndexSize = 10; - // Max ascii char code. + // Max ASCII char code. static const int kMaxAsciiCharCode = unibrow::Utf8::kMaxOneByteChar; static const unsigned kMaxAsciiCharCodeU = unibrow::Utf8::kMaxOneByteChar; static const int kMaxUC16CharCode = 0xffff; - // Minimum length for a cons string. - static const int kMinNonFlatLength = 13; - // Mask constant for checking if a string has a computed hash code // and if it is an array index. The least significant bit indicates // whether a hash code has been computed. If the hash code has been @@ -6307,13 +6831,16 @@ class SeqString: public String { // Casting. static inline SeqString* cast(Object* obj); + // Layout description. + static const int kHeaderSize = String::kSize; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(SeqString); }; -// The AsciiString class captures sequential ascii string objects. -// Each character in the AsciiString is an ascii character. +// The AsciiString class captures sequential ASCII string objects. +// Each character in the AsciiString is an ASCII character. class SeqAsciiString: public SeqString { public: static const bool kHasAsciiEncoding = true; @@ -6340,12 +6867,8 @@ class SeqAsciiString: public SeqString { return OBJECT_POINTER_ALIGN(kHeaderSize + length * kCharSize); } - // Layout description. - static const int kHeaderSize = String::kSize; - static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); - // Maximal memory usage for a single sequential ASCII string. - static const int kMaxSize = 512 * MB; + static const int kMaxSize = 512 * MB - 1; // Maximal length of a single sequential ASCII string. // Q.v. String::kMaxLength which is the maximal size of concatenated strings. static const int kMaxLength = (kMaxSize - kHeaderSize); @@ -6394,12 +6917,8 @@ class SeqTwoByteString: public SeqString { return OBJECT_POINTER_ALIGN(kHeaderSize + length * kShortSize); } - // Layout description. - static const int kHeaderSize = String::kSize; - static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); - // Maximal memory usage for a single sequential two-byte string. - static const int kMaxSize = 512 * MB; + static const int kMaxSize = 512 * MB - 1; // Maximal length of a single sequential two-byte string. // Q.v. String::kMaxLength which is the maximal size of concatenated strings. static const int kMaxLength = (kMaxSize - kHeaderSize) / sizeof(uint16_t); @@ -6543,7 +7062,12 @@ class ExternalString: public String { // Layout description. static const int kResourceOffset = POINTER_SIZE_ALIGN(String::kSize); - static const int kSize = kResourceOffset + kPointerSize; + static const int kShortSize = kResourceOffset + kPointerSize; + static const int kResourceDataOffset = kResourceOffset + kPointerSize; + static const int kSize = kResourceDataOffset + kPointerSize; + + // Return whether external string is short (data pointer is not cached). + inline bool is_short(); STATIC_CHECK(kResourceOffset == Internals::kStringResourceOffset); @@ -6561,11 +7085,19 @@ class ExternalAsciiString: public ExternalString { typedef v8::String::ExternalAsciiStringResource Resource; // The underlying resource. - inline Resource* resource(); - inline void set_resource(Resource* buffer); + inline const Resource* resource(); + inline void set_resource(const Resource* buffer); + + // Update the pointer cache to the external character array. + // The cached pointer is always valid, as the external character array does = + // not move during lifetime. Deserialization is the only exception, after + // which the pointer cache has to be refreshed. + inline void update_data_cache(); + + inline const char* GetChars(); // Dispatched behavior. - uint16_t ExternalAsciiStringGet(int index); + inline uint16_t ExternalAsciiStringGet(int index); // Casting. static inline ExternalAsciiString* cast(Object* obj); @@ -6598,14 +7130,22 @@ class ExternalTwoByteString: public ExternalString { typedef v8::String::ExternalStringResource Resource; // The underlying string resource. - inline Resource* resource(); - inline void set_resource(Resource* buffer); + inline const Resource* resource(); + inline void set_resource(const Resource* buffer); + + // Update the pointer cache to the external character array. + // The cached pointer is always valid, as the external character array does = + // not move during lifetime. Deserialization is the only exception, after + // which the pointer cache has to be refreshed. + inline void update_data_cache(); + + inline const uint16_t* GetChars(); // Dispatched behavior. - uint16_t ExternalTwoByteStringGet(int index); + inline uint16_t ExternalTwoByteStringGet(int index); // For regexp code. - const uint16_t* ExternalTwoByteStringGetData(unsigned start); + inline const uint16_t* ExternalTwoByteStringGetData(unsigned start); // Casting. static inline ExternalTwoByteString* cast(Object* obj); @@ -6750,6 +7290,9 @@ class Oddball: public HeapObject { static const byte kUndefined = 5; static const byte kOther = 6; + // The ToNumber value of a hidden oddball is a negative smi. + static const int kLeastHiddenOddballNumber = -5; + typedef FixedBodyDescriptor<kToStringOffset, kToNumberOffset + kPointerSize, kSize> BodyDescriptor; @@ -6785,10 +7328,6 @@ class JSGlobalPropertyCell: public HeapObject { kValueOffset + kPointerSize, kSize> BodyDescriptor; - // Returns the isolate/heap this cell object belongs to. - inline Isolate* isolate(); - inline Heap* heap(); - private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalPropertyCell); }; @@ -6800,25 +7339,56 @@ class JSProxy: public JSReceiver { // [handler]: The handler property. DECL_ACCESSORS(handler, Object) + // [hash]: The hash code property (undefined if not initialized yet). + DECL_ACCESSORS(hash, Object) + // Casting. static inline JSProxy* cast(Object* obj); bool HasPropertyWithHandler(String* name); + bool HasElementWithHandler(uint32_t index); + + MUST_USE_RESULT MaybeObject* GetPropertyWithHandler( + Object* receiver, + String* name); + MUST_USE_RESULT MaybeObject* GetElementWithHandler( + Object* receiver, + uint32_t index); MUST_USE_RESULT MaybeObject* SetPropertyWithHandler( String* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode); + MUST_USE_RESULT MaybeObject* SetElementWithHandler( + uint32_t index, + Object* value, + StrictModeFlag strict_mode); + + // If the handler defines an accessor property, invoke its setter + // (or throw if only a getter exists) and set *found to true. Otherwise false. + MUST_USE_RESULT MaybeObject* SetPropertyWithHandlerIfDefiningSetter( + String* name, + Object* value, + PropertyAttributes attributes, + StrictModeFlag strict_mode, + bool* found); MUST_USE_RESULT MaybeObject* DeletePropertyWithHandler( String* name, DeleteMode mode); + MUST_USE_RESULT MaybeObject* DeleteElementWithHandler( + uint32_t index, + DeleteMode mode); MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler( JSReceiver* receiver, - String* name, - bool* has_exception); + String* name); + MUST_USE_RESULT PropertyAttributes GetElementAttributeWithHandler( + JSReceiver* receiver, + uint32_t index); + + MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag); // Turn this into an (empty) JSObject. void Fix(); @@ -6826,6 +7396,13 @@ class JSProxy: public JSReceiver { // Initializes the body after the handler slot. inline void InitializeBody(int object_size, Object* value); + // Invoke a trap by name. If the trap does not exist on this's handler, + // but derived_trap is non-NULL, invoke that instead. May cause GC. + Handle<Object> CallTrap(const char* name, + Handle<Object> derived_trap, + int argc, + Handle<Object> args[]); + // Dispatched behavior. #ifdef OBJECT_PRINT inline void JSProxyPrint() { @@ -6841,7 +7418,8 @@ class JSProxy: public JSReceiver { // size as a virgin JSObject. This is essential for becoming a JSObject // upon freeze. static const int kHandlerOffset = HeapObject::kHeaderSize; - static const int kPaddingOffset = kHandlerOffset + kPointerSize; + static const int kHashOffset = kHandlerOffset + kPointerSize; + static const int kPaddingOffset = kHashOffset + kPointerSize; static const int kSize = JSObject::kHeaderSize; static const int kHeaderSize = kPaddingOffset; static const int kPaddingSize = kSize - kPaddingOffset; @@ -6849,7 +7427,7 @@ class JSProxy: public JSReceiver { STATIC_CHECK(kPaddingSize >= 0); typedef FixedBodyDescriptor<kHandlerOffset, - kHandlerOffset + kPointerSize, + kPaddingOffset, kSize> BodyDescriptor; private: @@ -6880,7 +7458,7 @@ class JSFunctionProxy: public JSProxy { #endif // Layout description. - static const int kCallTrapOffset = kHandlerOffset + kPointerSize; + static const int kCallTrapOffset = JSProxy::kPaddingOffset; static const int kConstructTrapOffset = kCallTrapOffset + kPointerSize; static const int kPaddingOffset = kConstructTrapOffset + kPointerSize; static const int kSize = JSFunction::kSize; @@ -6897,18 +7475,69 @@ class JSFunctionProxy: public JSProxy { }; +// The JSSet describes EcmaScript Harmony sets +class JSSet: public JSObject { + public: + // [set]: the backing hash set containing keys. + DECL_ACCESSORS(table, Object) + + // Casting. + static inline JSSet* cast(Object* obj); + +#ifdef OBJECT_PRINT + inline void JSSetPrint() { + JSSetPrint(stdout); + } + void JSSetPrint(FILE* out); +#endif +#ifdef DEBUG + void JSSetVerify(); +#endif + + static const int kTableOffset = JSObject::kHeaderSize; + static const int kSize = kTableOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSSet); +}; + + +// The JSMap describes EcmaScript Harmony maps +class JSMap: public JSObject { + public: + // [table]: the backing hash table mapping keys to values. + DECL_ACCESSORS(table, Object) + + // Casting. + static inline JSMap* cast(Object* obj); + +#ifdef OBJECT_PRINT + inline void JSMapPrint() { + JSMapPrint(stdout); + } + void JSMapPrint(FILE* out); +#endif +#ifdef DEBUG + void JSMapVerify(); +#endif + + static const int kTableOffset = JSObject::kHeaderSize; + static const int kSize = kTableOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSMap); +}; + + // The JSWeakMap describes EcmaScript Harmony weak maps class JSWeakMap: public JSObject { public: // [table]: the backing hash table mapping keys to values. - DECL_ACCESSORS(table, ObjectHashTable) + DECL_ACCESSORS(table, Object) // [next]: linked list of encountered weak maps during GC. DECL_ACCESSORS(next, Object) - // Unchecked accessors to be used during GC. - inline ObjectHashTable* unchecked_table(); - // Casting. static inline JSWeakMap* cast(Object* obj); @@ -6937,8 +7566,8 @@ class JSWeakMap: public JSObject { class Foreign: public HeapObject { public: // [address]: field containing the address. - inline Address address(); - inline void set_address(Address value); + inline Address foreign_address(); + inline void set_foreign_address(Address value); // Casting. static inline Foreign* cast(Object* obj); @@ -6961,10 +7590,10 @@ class Foreign: public HeapObject { // Layout description. - static const int kAddressOffset = HeapObject::kHeaderSize; - static const int kSize = kAddressOffset + kPointerSize; + static const int kForeignAddressOffset = HeapObject::kHeaderSize; + static const int kSize = kForeignAddressOffset + kPointerSize; - STATIC_CHECK(kAddressOffset == Internals::kForeignAddressOffset); + STATIC_CHECK(kForeignAddressOffset == Internals::kForeignAddressOffset); private: DISALLOW_IMPLICIT_CONSTRUCTORS(Foreign); @@ -6993,8 +7622,12 @@ class JSArray: public JSObject { // capacity is non-zero. MUST_USE_RESULT MaybeObject* Initialize(int capacity); + // Initializes the array to a certain length. + inline bool AllowsSetElementsLength(); + MUST_USE_RESULT MaybeObject* SetElementsLength(Object* length); + // Set the content of the array to the content of storage. - inline void SetContent(FixedArray* storage); + inline MaybeObject* SetContent(FixedArrayBase* storage); // Casting. static inline JSArray* cast(Object* obj); @@ -7109,6 +7742,35 @@ class AccessorInfo: public Struct { }; +// Support for JavaScript accessors: A pair of a getter and a setter. Each +// accessor can either be +// * a pointer to a JavaScript function or proxy: a real accessor +// * undefined: considered an accessor by the spec, too, strangely enough +// * the hole: an accessor which has not been set +// * a pointer to a map: a transition used to ensure map sharing +class AccessorPair: public Struct { + public: + DECL_ACCESSORS(getter, Object) + DECL_ACCESSORS(setter, Object) + + static inline AccessorPair* cast(Object* obj); + +#ifdef OBJECT_PRINT + void AccessorPairPrint(FILE* out = stdout); +#endif +#ifdef DEBUG + void AccessorPairVerify(); +#endif + + static const int kGetterOffset = HeapObject::kHeaderSize; + static const int kSetterOffset = kGetterOffset + kPointerSize; + static const int kSize = kSetterOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(AccessorPair); +}; + + class AccessCheckInfo: public Struct { public: DECL_ACCESSORS(named_callback, Object) @@ -7209,8 +7871,8 @@ class TemplateInfo: public Struct { static const int kTagOffset = HeapObject::kHeaderSize; static const int kPropertyListOffset = kTagOffset + kPointerSize; static const int kHeaderSize = kPropertyListOffset + kPointerSize; - protected: - friend class AGCCVersionRequiresThisClassToHaveAFriendSoHereItIs; + + private: DISALLOW_IMPLICIT_CONSTRUCTORS(TemplateInfo); }; @@ -7476,6 +8138,34 @@ class BreakPointInfo: public Struct { #undef DECL_BOOLEAN_ACCESSORS #undef DECL_ACCESSORS +#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \ + V(kSymbolTable, "symbol_table", "(Symbols)") \ + V(kExternalStringsTable, "external_strings_table", "(External strings)") \ + V(kStrongRootList, "strong_root_list", "(Strong roots)") \ + V(kSymbol, "symbol", "(Symbol)") \ + V(kBootstrapper, "bootstrapper", "(Bootstrapper)") \ + V(kTop, "top", "(Isolate)") \ + V(kRelocatable, "relocatable", "(Relocatable)") \ + V(kDebug, "debug", "(Debugger)") \ + V(kCompilationCache, "compilationcache", "(Compilation cache)") \ + V(kHandleScope, "handlescope", "(Handle scope)") \ + V(kBuiltins, "builtins", "(Builtins)") \ + V(kGlobalHandles, "globalhandles", "(Global handles)") \ + V(kThreadManager, "threadmanager", "(Thread manager)") \ + V(kExtensions, "Extensions", "(Extensions)") + +class VisitorSynchronization : public AllStatic { + public: +#define DECLARE_ENUM(enum_item, ignore1, ignore2) enum_item, + enum SyncTag { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_ENUM) + kNumberOfSyncTags + }; +#undef DECLARE_ENUM + + static const char* const kTags[kNumberOfSyncTags]; + static const char* const kTagNames[kNumberOfSyncTags]; +}; // Abstract base class for visiting, and optionally modifying, the // pointers contained in Objects. Used in GC and serialization/deserialization. @@ -7514,11 +8204,16 @@ class ObjectVisitor BASE_EMBEDDED { // Handy shorthand for visiting a single pointer. virtual void VisitPointer(Object** p) { VisitPointers(p, p + 1); } + // Visit pointer embedded into a code object. + virtual void VisitEmbeddedPointer(RelocInfo* rinfo); + // Visits a contiguous arrays of external references (references to the C++ // heap) in the half-open range [start, end). Any or all of the values // may be modified on return. virtual void VisitExternalReferences(Address* start, Address* end) {} + virtual void VisitExternalReference(RelocInfo* rinfo); + inline void VisitExternalReference(Address* p) { VisitExternalReferences(p, p + 1); } @@ -7526,13 +8221,10 @@ class ObjectVisitor BASE_EMBEDDED { // Visits a handle that has an embedder-assigned class ID. virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {} -#ifdef DEBUG // Intended for serialization/deserialization checking: insert, or // check for the presence of, a tag at this position in the stream. - virtual void Synchronize(const char* tag) {} -#else - inline void Synchronize(const char* tag) {} -#endif + // Also used for marking up GC roots in heap snapshots. + virtual void Synchronize(VisitorSynchronization::SyncTag tag) {} }; diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 90d5c91458..c02cad93e1 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,7 +28,7 @@ #include "v8.h" #include "api.h" -#include "ast-inl.h" +#include "ast.h" #include "bootstrapper.h" #include "char-predicates-inl.h" #include "codegen.h" @@ -407,9 +407,9 @@ unsigned* ScriptDataImpl::ReadAddress(int position) { } -Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) { +Scope* Parser::NewScope(Scope* parent, ScopeType type) { Scope* result = new(zone()) Scope(parent, type); - result->Initialize(inside_with); + result->Initialize(); return result; } @@ -459,90 +459,51 @@ class TargetScope BASE_EMBEDDED { // ---------------------------------------------------------------------------- -// LexicalScope is a support class to facilitate manipulation of the -// Parser's scope stack. The constructor sets the parser's top scope -// to the incoming scope, and the destructor resets it. -// -// Additionally, it stores transient information used during parsing. -// These scopes are not kept around after parsing or referenced by syntax -// trees so they can be stack-allocated and hence used by the pre-parser. +// FunctionState and BlockState together implement the parser's scope stack. +// The parser's current scope is in top_scope_. The BlockState and +// FunctionState constructors push on the scope stack and the destructors +// pop. They are also used to hold the parser's per-function and per-block +// state. -class LexicalScope BASE_EMBEDDED { +class Parser::BlockState BASE_EMBEDDED { public: - LexicalScope(Parser* parser, Scope* scope, Isolate* isolate); - ~LexicalScope(); - - int NextMaterializedLiteralIndex() { - int next_index = - materialized_literal_count_ + JSFunction::kLiteralsPrefixSize; - materialized_literal_count_++; - return next_index; + BlockState(Parser* parser, Scope* scope) + : parser_(parser), + outer_scope_(parser->top_scope_) { + parser->top_scope_ = scope; } - int materialized_literal_count() { return materialized_literal_count_; } - void SetThisPropertyAssignmentInfo( - bool only_simple_this_property_assignments, - Handle<FixedArray> this_property_assignments) { - only_simple_this_property_assignments_ = - only_simple_this_property_assignments; - this_property_assignments_ = this_property_assignments; - } - bool only_simple_this_property_assignments() { - return only_simple_this_property_assignments_; - } - Handle<FixedArray> this_property_assignments() { - return this_property_assignments_; - } - - void AddProperty() { expected_property_count_++; } - int expected_property_count() { return expected_property_count_; } + ~BlockState() { parser_->top_scope_ = outer_scope_; } private: - // Captures the number of literals that need materialization in the - // function. Includes regexp literals, and boilerplate for object - // and array literals. - int materialized_literal_count_; - - // Properties count estimation. - int expected_property_count_; - - // Keeps track of assignments to properties of this. Used for - // optimizing constructors. - bool only_simple_this_property_assignments_; - Handle<FixedArray> this_property_assignments_; - - // Bookkeeping Parser* parser_; - // Previous values - LexicalScope* lexical_scope_parent_; - Scope* previous_scope_; - int previous_with_nesting_level_; - unsigned previous_ast_node_id_; + Scope* outer_scope_; }; -LexicalScope::LexicalScope(Parser* parser, Scope* scope, Isolate* isolate) - : materialized_literal_count_(0), - expected_property_count_(0), - only_simple_this_property_assignments_(false), - this_property_assignments_(isolate->factory()->empty_fixed_array()), - parser_(parser), - lexical_scope_parent_(parser->lexical_scope_), - previous_scope_(parser->top_scope_), - previous_with_nesting_level_(parser->with_nesting_level_), - previous_ast_node_id_(isolate->ast_node_id()) { +Parser::FunctionState::FunctionState(Parser* parser, + Scope* scope, + Isolate* isolate) + : next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize), + next_handler_index_(0), + expected_property_count_(0), + only_simple_this_property_assignments_(false), + this_property_assignments_(isolate->factory()->empty_fixed_array()), + parser_(parser), + outer_function_state_(parser->current_function_state_), + outer_scope_(parser->top_scope_), + saved_ast_node_id_(isolate->ast_node_id()), + factory_(isolate) { parser->top_scope_ = scope; - parser->lexical_scope_ = this; - parser->with_nesting_level_ = 0; + parser->current_function_state_ = this; isolate->set_ast_node_id(AstNode::kDeclarationsId + 1); } -LexicalScope::~LexicalScope() { - parser_->top_scope_ = previous_scope_; - parser_->lexical_scope_ = lexical_scope_parent_; - parser_->with_nesting_level_ = previous_with_nesting_level_; - parser_->isolate()->set_ast_node_id(previous_ast_node_id_); +Parser::FunctionState::~FunctionState() { + parser_->top_scope_ = outer_scope_; + parser_->current_function_state_ = outer_function_state_; + parser_->isolate()->set_ast_node_id(saved_ast_node_id_); } @@ -570,34 +531,40 @@ LexicalScope::~LexicalScope() { // Implementation of Parser Parser::Parser(Handle<Script> script, - bool allow_natives_syntax, + int parser_flags, v8::Extension* extension, ScriptDataImpl* pre_data) : isolate_(script->GetIsolate()), symbol_cache_(pre_data ? pre_data->symbol_count() : 0), script_(script), scanner_(isolate_->unicode_cache()), + reusable_preparser_(NULL), top_scope_(NULL), - with_nesting_level_(0), - lexical_scope_(NULL), + current_function_state_(NULL), target_stack_(NULL), - allow_natives_syntax_(allow_natives_syntax), extension_(extension), pre_data_(pre_data), fni_(NULL), + allow_natives_syntax_((parser_flags & kAllowNativesSyntax) != 0), + allow_lazy_((parser_flags & kAllowLazy) != 0), + allow_modules_((parser_flags & kAllowModules) != 0), stack_overflow_(false), - parenthesized_function_(false), - harmony_block_scoping_(false) { + parenthesized_function_(false) { AstNode::ResetIds(); + if ((parser_flags & kLanguageModeMask) == EXTENDED_MODE) { + scanner().SetHarmonyScoping(true); + } + if ((parser_flags & kAllowModules) != 0) { + scanner().SetHarmonyModules(true); + } } -FunctionLiteral* Parser::ParseProgram(Handle<String> source, - bool in_global_context, - StrictModeFlag strict_mode) { +FunctionLiteral* Parser::ParseProgram(CompilationInfo* info) { ZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT); HistogramTimerScope timer(isolate()->counters()->parse()); + Handle<String> source(String::cast(script_->source())); isolate()->counters()->total_parse_size()->Increment(source->length()); fni_ = new(zone()) FuncNameInferrer(isolate()); @@ -610,65 +577,66 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, ExternalTwoByteStringUC16CharacterStream stream( Handle<ExternalTwoByteString>::cast(source), 0, source->length()); scanner_.Initialize(&stream); - return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); + return DoParseProgram(info, source, &zone_scope); } else { GenericStringUC16CharacterStream stream(source, 0, source->length()); scanner_.Initialize(&stream); - return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); + return DoParseProgram(info, source, &zone_scope); } } -FunctionLiteral* Parser::DoParseProgram(Handle<String> source, - bool in_global_context, - StrictModeFlag strict_mode, +FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info, + Handle<String> source, ZoneScope* zone_scope) { + ASSERT(top_scope_ == NULL); ASSERT(target_stack_ == NULL); if (pre_data_ != NULL) pre_data_->Initialize(); // Compute the parsing mode. - mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; + mode_ = (FLAG_lazy && allow_lazy_) ? PARSE_LAZILY : PARSE_EAGERLY; if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; - Scope::Type type = - in_global_context - ? Scope::GLOBAL_SCOPE - : Scope::EVAL_SCOPE; Handle<String> no_name = isolate()->factory()->empty_symbol(); FunctionLiteral* result = NULL; - { Scope* scope = NewScope(top_scope_, type, inside_with()); - LexicalScope lexical_scope(this, scope, isolate()); - if (strict_mode == kStrictMode) { - top_scope_->EnableStrictMode(); + { Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE); + info->SetGlobalScope(scope); + if (!info->is_global()) { + scope = Scope::DeserializeScopeChain(*info->calling_context(), scope); + scope = NewScope(scope, EVAL_SCOPE); } + scope->set_start_position(0); + scope->set_end_position(source->length()); + FunctionState function_state(this, scope, isolate()); + top_scope_->SetLanguageMode(info->language_mode()); ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16); bool ok = true; int beg_loc = scanner().location().beg_pos; ParseSourceElements(body, Token::EOS, &ok); - if (ok && top_scope_->is_strict_mode()) { + if (ok && !top_scope_->is_classic_mode()) { CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok); } - if (ok && harmony_block_scoping_) { + if (ok && is_extended_mode()) { CheckConflictingVarDeclarations(scope, &ok); } if (ok) { - result = new(zone()) FunctionLiteral( - isolate(), + result = factory()->NewFunctionLiteral( no_name, top_scope_, body, - lexical_scope.materialized_literal_count(), - lexical_scope.expected_property_count(), - lexical_scope.only_simple_this_property_assignments(), - lexical_scope.this_property_assignments(), - 0, + function_state.materialized_literal_count(), + function_state.expected_property_count(), + function_state.handler_count(), + function_state.only_simple_this_property_assignments(), + function_state.this_property_assignments(), 0, - source->length(), + false, // Does not have duplicate parameters. FunctionLiteral::ANONYMOUS_EXPRESSION, - false); // Does not have duplicate parameters. + false); // Top-level literal doesn't count for the AST's properties. + result->set_ast_properties(factory()->visitor()->ast_properties()); } else if (stack_overflow_) { isolate()->StackOverflow(); } @@ -714,6 +682,7 @@ FunctionLiteral* Parser::ParseLazy(CompilationInfo* info, ZoneScope* zone_scope) { Handle<SharedFunctionInfo> shared_info = info->shared_info(); scanner_.Initialize(source); + ASSERT(top_scope_ == NULL); ASSERT(target_stack_ == NULL); Handle<String> name(String::cast(shared_info->name())); @@ -727,16 +696,17 @@ FunctionLiteral* Parser::ParseLazy(CompilationInfo* info, { // Parse the function literal. - Scope* scope = NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with()); + Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE); + info->SetGlobalScope(scope); if (!info->closure().is_null()) { - scope = Scope::DeserializeScopeChain(info, scope); + scope = Scope::DeserializeScopeChain(info->closure()->context(), scope); } - LexicalScope lexical_scope(this, scope, isolate()); - - if (shared_info->strict_mode()) { - top_scope_->EnableStrictMode(); - } - + FunctionState function_state(this, scope, isolate()); + ASSERT(scope->language_mode() != STRICT_MODE || !info->is_classic_mode()); + ASSERT(scope->language_mode() != EXTENDED_MODE || + info->is_extended_mode()); + ASSERT(info->language_mode() == shared_info->language_mode()); + scope->SetLanguageMode(shared_info->language_mode()); FunctionLiteral::Type type = shared_info->is_expression() ? (shared_info->is_anonymous() ? FunctionLiteral::ANONYMOUS_EXPRESSION @@ -817,10 +787,6 @@ void Parser::ReportMessageAt(Scanner::Location source_location, isolate()->Throw(*result, &location); } -void Parser::SetHarmonyBlockScoping(bool block_scoping) { - scanner().SetHarmonyBlockScoping(block_scoping); - harmony_block_scoping_ = block_scoping; -} // Base class containing common code for the different finder classes used by // the parser. @@ -957,17 +923,18 @@ class InitializationBlockFinder : public ParserFinder { }; -// A ThisNamedPropertyAssigmentFinder finds and marks statements of the form +// A ThisNamedPropertyAssignmentFinder finds and marks statements of the form // this.x = ...;, where x is a named property. It also determines whether a // function contains only assignments of this type. -class ThisNamedPropertyAssigmentFinder : public ParserFinder { +class ThisNamedPropertyAssignmentFinder : public ParserFinder { public: - explicit ThisNamedPropertyAssigmentFinder(Isolate* isolate) + explicit ThisNamedPropertyAssignmentFinder(Isolate* isolate) : isolate_(isolate), only_simple_this_property_assignments_(true), - names_(NULL), - assigned_arguments_(NULL), - assigned_constants_(NULL) {} + names_(0), + assigned_arguments_(0), + assigned_constants_(0) { + } void Update(Scope* scope, Statement* stat) { // Bail out if function already has property assignment that are @@ -994,19 +961,17 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder { // Returns a fixed array containing three elements for each assignment of the // form this.x = y; Handle<FixedArray> GetThisPropertyAssignments() { - if (names_ == NULL) { + if (names_.is_empty()) { return isolate_->factory()->empty_fixed_array(); } - ASSERT(names_ != NULL); - ASSERT(assigned_arguments_ != NULL); - ASSERT_EQ(names_->length(), assigned_arguments_->length()); - ASSERT_EQ(names_->length(), assigned_constants_->length()); + ASSERT_EQ(names_.length(), assigned_arguments_.length()); + ASSERT_EQ(names_.length(), assigned_constants_.length()); Handle<FixedArray> assignments = - isolate_->factory()->NewFixedArray(names_->length() * 3); - for (int i = 0; i < names_->length(); i++) { - assignments->set(i * 3, *names_->at(i)); - assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_->at(i))); - assignments->set(i * 3 + 2, *assigned_constants_->at(i)); + isolate_->factory()->NewFixedArray(names_.length() * 3); + for (int i = 0; i < names_.length(); ++i) { + assignments->set(i * 3, *names_[i]); + assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_[i])); + assignments->set(i * 3 + 2, *assigned_constants_[i]); } return assignments; } @@ -1063,18 +1028,37 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder { AssignmentFromSomethingElse(); } + + + + // We will potentially reorder the property assignments, so they must be + // simple enough that the ordering does not matter. void AssignmentFromParameter(Handle<String> name, int index) { - EnsureAllocation(); - names_->Add(name); - assigned_arguments_->Add(index); - assigned_constants_->Add(isolate_->factory()->undefined_value()); + EnsureInitialized(); + for (int i = 0; i < names_.length(); ++i) { + if (name->Equals(*names_[i])) { + assigned_arguments_[i] = index; + assigned_constants_[i] = isolate_->factory()->undefined_value(); + return; + } + } + names_.Add(name); + assigned_arguments_.Add(index); + assigned_constants_.Add(isolate_->factory()->undefined_value()); } void AssignmentFromConstant(Handle<String> name, Handle<Object> value) { - EnsureAllocation(); - names_->Add(name); - assigned_arguments_->Add(-1); - assigned_constants_->Add(value); + EnsureInitialized(); + for (int i = 0; i < names_.length(); ++i) { + if (name->Equals(*names_[i])) { + assigned_arguments_[i] = -1; + assigned_constants_[i] = value; + return; + } + } + names_.Add(name); + assigned_arguments_.Add(-1); + assigned_constants_.Add(value); } void AssignmentFromSomethingElse() { @@ -1082,41 +1066,42 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder { only_simple_this_property_assignments_ = false; } - void EnsureAllocation() { - if (names_ == NULL) { - ASSERT(assigned_arguments_ == NULL); - ASSERT(assigned_constants_ == NULL); - Zone* zone = isolate_->zone(); - names_ = new(zone) ZoneStringList(4); - assigned_arguments_ = new(zone) ZoneList<int>(4); - assigned_constants_ = new(zone) ZoneObjectList(4); + void EnsureInitialized() { + if (names_.capacity() == 0) { + ASSERT(assigned_arguments_.capacity() == 0); + ASSERT(assigned_constants_.capacity() == 0); + names_.Initialize(4); + assigned_arguments_.Initialize(4); + assigned_constants_.Initialize(4); } } Isolate* isolate_; bool only_simple_this_property_assignments_; - ZoneStringList* names_; - ZoneList<int>* assigned_arguments_; - ZoneObjectList* assigned_constants_; + ZoneStringList names_; + ZoneList<int> assigned_arguments_; + ZoneObjectList assigned_constants_; }; Statement* Parser::ParseSourceElement(ZoneStringList* labels, bool* ok) { + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // + // In harmony mode we allow additionally the following productions + // SourceElement: + // LetDeclaration + // ConstDeclaration + if (peek() == Token::FUNCTION) { - // FunctionDeclaration is only allowed in the context of SourceElements - // (Ecma 262 5th Edition, clause 14): - // SourceElement: - // Statement - // FunctionDeclaration - // Common language extension is to allow function declaration in place - // of any statement. This language extension is disabled in strict mode. return ParseFunctionDeclaration(ok); - } else if (peek() == Token::LET) { + } else if (peek() == Token::LET || peek() == Token::CONST) { return ParseVariableStatement(kSourceElement, ok); - } else { - return ParseStatement(labels, ok); } + return ParseStatement(labels, ok); } @@ -1124,7 +1109,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, int end_token, bool* ok) { // SourceElements :: - // (Statement)* <end_token> + // (SourceElement)* <end_token> // Allocate a target stack to use for this set of source // elements. This way, all scripts and functions get their own @@ -1134,7 +1119,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, ASSERT(processor != NULL); InitializationBlockFinder block_finder(top_scope_, target_stack_); - ThisNamedPropertyAssigmentFinder this_property_assignment_finder(isolate()); + ThisNamedPropertyAssignmentFinder this_property_assignment_finder(isolate()); bool directive_prologue = true; // Parsing directive prologue. while (peek() != end_token) { @@ -1151,8 +1136,8 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, if (directive_prologue) { // A shot at a directive. - ExpressionStatement *e_stat; - Literal *literal; + ExpressionStatement* e_stat; + Literal* literal; // Still processing directive prologue? if ((e_stat = stat->AsExpressionStatement()) != NULL && (literal = e_stat->expression()->AsLiteral()) != NULL && @@ -1160,11 +1145,13 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, Handle<String> directive = Handle<String>::cast(literal->handle()); // Check "use strict" directive (ES5 14.1). - if (!top_scope_->is_strict_mode() && + if (top_scope_->is_classic_mode() && directive->Equals(isolate()->heap()->use_strict()) && token_loc.end_pos - token_loc.beg_pos == isolate()->heap()->use_strict()->length() + 2) { - top_scope_->EnableStrictMode(); + // TODO(ES6): Fix entering extended mode, once it is specified. + top_scope_->SetLanguageMode(FLAG_harmony_scoping + ? EXTENDED_MODE : STRICT_MODE); // "use strict" is the only directive for now. directive_prologue = false; } @@ -1188,7 +1175,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, this_property_assignment_finder.only_simple_this_property_assignments() && top_scope_->declarations()->length() == 0; if (only_simple_this_property_assignments) { - lexical_scope_->SetThisPropertyAssignmentInfo( + current_function_state_->SetThisPropertyAssignmentInfo( only_simple_this_property_assignments, this_property_assignment_finder.GetThisPropertyAssignments()); } @@ -1230,13 +1217,14 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { return ParseBlock(labels, ok); case Token::CONST: // fall through + case Token::LET: case Token::VAR: stmt = ParseVariableStatement(kStatement, ok); break; case Token::SEMICOLON: Next(); - return EmptyStatement(); + return factory()->NewEmptyStatement(); case Token::IF: stmt = ParseIfStatement(labels, ok); @@ -1284,7 +1272,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { // one must take great care not to treat it as a // fall-through. It is much easier just to wrap the entire // try-statement in a statement block and put the labels there - Block* result = new(zone()) Block(isolate(), labels, 1, false); + Block* result = factory()->NewBlock(labels, 1, false); Target target(&this->target_stack_, result); TryStatement* statement = ParseTryStatement(CHECK_OK); if (statement) { @@ -1295,9 +1283,14 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { } case Token::FUNCTION: { - // In strict mode, FunctionDeclaration is only allowed in the context - // of SourceElements. - if (top_scope_->is_strict_mode()) { + // FunctionDeclaration is only allowed in the context of SourceElements + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // Common language extension is to allow function declaration in place + // of any statement. This language extension is disabled in strict mode. + if (!top_scope_->is_classic_mode()) { ReportMessageAt(scanner().peek_location(), "strict_function", Vector<const char*>::empty()); *ok = false; @@ -1321,7 +1314,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { VariableProxy* Parser::Declare(Handle<String> name, - Variable::Mode mode, + VariableMode mode, FunctionLiteral* fun, bool resolve, bool* ok) { @@ -1329,6 +1322,12 @@ VariableProxy* Parser::Declare(Handle<String> name, // If we are inside a function, a declaration of a var/const variable is a // truly local variable, and the scope of the variable is always the function // scope. + // Let/const variables in harmony mode are always added to the immediately + // enclosing scope. + Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY) + ? top_scope_ : top_scope_->DeclarationScope(); + InitializationFlag init_flag = (fun != NULL || mode == VAR) + ? kCreatedInitialized : kNeedsInitialization; // If a function scope exists, then we can statically declare this // variable and also set its mode. In any case, a Declaration node @@ -1338,17 +1337,16 @@ VariableProxy* Parser::Declare(Handle<String> name, // to the calling function context. // Similarly, strict mode eval scope does not leak variable declarations to // the caller's scope so we declare all locals, too. - - Scope* declaration_scope = mode == Variable::LET ? top_scope_ - : top_scope_->DeclarationScope(); + // Also for block scoped let/const bindings the variable can be + // statically declared. if (declaration_scope->is_function_scope() || - declaration_scope->is_strict_mode_eval_scope() || + declaration_scope->is_strict_or_extended_eval_scope() || declaration_scope->is_block_scope()) { // Declare the variable in the function scope. var = declaration_scope->LocalLookup(name); if (var == NULL) { // Declare the name. - var = declaration_scope->DeclareLocal(name, mode); + var = declaration_scope->DeclareLocal(name, mode, init_flag); } else { // The name was declared in this scope before; check for conflicting // re-declarations. We have a conflict if either of the declarations is @@ -1361,12 +1359,13 @@ VariableProxy* Parser::Declare(Handle<String> name, // // because the var declaration is hoisted to the function scope where 'x' // is already bound. - if ((mode != Variable::VAR) || (var->mode() != Variable::VAR)) { + if ((mode != VAR) || (var->mode() != VAR)) { // We only have vars, consts and lets in declarations. - ASSERT(var->mode() == Variable::VAR || - var->mode() == Variable::CONST || - var->mode() == Variable::LET); - if (harmony_block_scoping_) { + ASSERT(var->mode() == VAR || + var->mode() == CONST || + var->mode() == CONST_HARMONY || + var->mode() == LET); + if (is_extended_mode()) { // In harmony mode we treat re-declarations as early errors. See // ES5 16 for a definition of early errors. SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS); @@ -1376,8 +1375,8 @@ VariableProxy* Parser::Declare(Handle<String> name, *ok = false; return NULL; } - const char* type = (var->mode() == Variable::VAR) ? "var" : - (var->mode() == Variable::CONST) ? "const" : "let"; + const char* type = (var->mode() == VAR) + ? "var" : var->is_const_mode() ? "const" : "let"; Handle<String> type_string = isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED); Expression* expression = @@ -1405,19 +1404,35 @@ VariableProxy* Parser::Declare(Handle<String> name, // a performance issue since it may lead to repeated // Runtime::DeclareContextSlot() calls. VariableProxy* proxy = declaration_scope->NewUnresolved( - name, false, scanner().location().beg_pos); + factory(), name, scanner().location().beg_pos); declaration_scope->AddDeclaration( - new(zone()) Declaration(proxy, mode, fun, top_scope_)); + factory()->NewVariableDeclaration(proxy, mode, fun, top_scope_)); - // For global const variables we bind the proxy to a variable. - if (mode == Variable::CONST && declaration_scope->is_global_scope()) { + if ((mode == CONST || mode == CONST_HARMONY) && + declaration_scope->is_global_scope()) { + // For global const variables we bind the proxy to a variable. ASSERT(resolve); // should be set by all callers Variable::Kind kind = Variable::NORMAL; var = new(zone()) Variable(declaration_scope, name, - Variable::CONST, + mode, + true, + kind, + kNeedsInitialization); + } else if (declaration_scope->is_eval_scope() && + declaration_scope->is_classic_mode()) { + // For variable declarations in a non-strict eval scope the proxy is bound + // to a lookup variable to force a dynamic declaration using the + // DeclareContextSlot runtime function. + Variable::Kind kind = Variable::NORMAL; + var = new(zone()) Variable(declaration_scope, + name, + mode, true, - kind); + kind, + init_flag); + var->AllocateTo(Variable::LOOKUP, -1); + resolve = true; } // If requested and we have a local variable, bind the proxy to the variable @@ -1487,7 +1502,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { Handle<Code> construct_stub = Handle<Code>(fun->shared()->construct_stub()); Handle<SharedFunctionInfo> shared = isolate()->factory()->NewSharedFunctionInfo(name, literals, code, - Handle<SerializedScopeInfo>(fun->shared()->scope_info())); + Handle<ScopeInfo>(fun->shared()->scope_info())); shared->set_construct_stub(*construct_stub); // Copy the function data to the shared function info. @@ -1497,12 +1512,13 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { // TODO(1240846): It's weird that native function declarations are // introduced dynamically when we meet their declarations, whereas - // other functions are setup when entering the surrounding scope. + // other functions are set up when entering the surrounding scope. SharedFunctionInfoLiteral* lit = - new(zone()) SharedFunctionInfoLiteral(isolate(), shared); - VariableProxy* var = Declare(name, Variable::VAR, NULL, true, CHECK_OK); - return new(zone()) ExpressionStatement(new(zone()) Assignment( - isolate(), Token::INIT_VAR, var, lit, RelocInfo::kNoPosition)); + factory()->NewSharedFunctionInfoLiteral(shared); + VariableProxy* var = Declare(name, VAR, NULL, true, CHECK_OK); + return factory()->NewExpressionStatement( + factory()->NewAssignment( + Token::INIT_VAR, var, lit, RelocInfo::kNoPosition)); } @@ -1522,14 +1538,14 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) { // Even if we're not at the top-level of the global or a function // scope, we treat is as such and introduce the function with it's // initial value upon entering the corresponding scope. - Variable::Mode mode = harmony_block_scoping_ ? Variable::LET : Variable::VAR; + VariableMode mode = is_extended_mode() ? LET : VAR; Declare(name, mode, fun, true, CHECK_OK); - return EmptyStatement(); + return factory()->NewEmptyStatement(); } Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { - if (harmony_block_scoping_) return ParseScopedBlock(labels, ok); + if (top_scope_->is_extended_mode()) return ParseScopedBlock(labels, ok); // Block :: // '{' Statement* '}' @@ -1538,7 +1554,7 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { // (ECMA-262, 3rd, 12.2) // // Construct block expecting 16 statements. - Block* result = new(zone()) Block(isolate(), labels, 16, false); + Block* result = factory()->NewBlock(labels, 16, false); Target target(&this->target_stack_, result); Expect(Token::LBRACE, CHECK_OK); InitializationBlockFinder block_finder(top_scope_, target_stack_); @@ -1555,22 +1571,21 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { + // The harmony mode uses source elements instead of statements. + // + // Block :: + // '{' SourceElement* '}' + // Construct block expecting 16 statements. - Block* body = new(zone()) Block(isolate(), labels, 16, false); - Scope* saved_scope = top_scope_; - Scope* block_scope = NewScope(top_scope_, - Scope::BLOCK_SCOPE, - inside_with()); - if (top_scope_->is_strict_mode()) { - block_scope->EnableStrictMode(); - } - top_scope_ = block_scope; + Block* body = factory()->NewBlock(labels, 16, false); + Scope* block_scope = NewScope(top_scope_, BLOCK_SCOPE); // Parse the statements and collect escaping labels. - TargetCollector collector; - Target target(&this->target_stack_, &collector); Expect(Token::LBRACE, CHECK_OK); - { + block_scope->set_start_position(scanner().location().beg_pos); + { BlockState block_state(this, block_scope); + TargetCollector collector; + Target target(&this->target_stack_, &collector); Target target_body(&this->target_stack_, body); InitializationBlockFinder block_finder(top_scope_, target_stack_); @@ -1583,8 +1598,7 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { } } Expect(Token::RBRACE, CHECK_OK); - top_scope_ = saved_scope; - + block_scope->set_end_position(scanner().location().end_pos); block_scope = block_scope->FinalizeBlockScope(); body->set_block_scope(block_scope); return body; @@ -1598,6 +1612,7 @@ Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context, Handle<String> ignore; Block* result = ParseVariableDeclarations(var_context, + NULL, &ignore, CHECK_OK); ExpectSemicolon(CHECK_OK); @@ -1612,17 +1627,29 @@ bool Parser::IsEvalOrArguments(Handle<String> string) { // If the variable declaration declares exactly one non-const -// variable, then *var is set to that variable. In all other cases, -// *var is untouched; in particular, it is the caller's responsibility +// variable, then *out is set to that variable. In all other cases, +// *out is untouched; in particular, it is the caller's responsibility // to initialize it properly. This mechanism is used for the parsing // of 'for-in' loops. -Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, - Handle<String>* out, - bool* ok) { +Block* Parser::ParseVariableDeclarations( + VariableDeclarationContext var_context, + VariableDeclarationProperties* decl_props, + Handle<String>* out, + bool* ok) { // VariableDeclarations :: - // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] - - Variable::Mode mode = Variable::VAR; + // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] + // + // The ES6 Draft Rev3 specifies the following grammar for const declarations + // + // ConstDeclaration :: + // const ConstBinding (',' ConstBinding)* ';' + // ConstBinding :: + // Identifier '=' AssignmentExpression + // + // TODO(ES6): + // ConstBinding :: + // BindingPattern '=' AssignmentExpression + VariableMode mode = VAR; // True if the binding needs initialization. 'let' and 'const' declared // bindings are created uninitialized by their declaration nodes and // need initialization. 'var' declared bindings are always initialized @@ -1633,33 +1660,69 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, if (peek() == Token::VAR) { Consume(Token::VAR); } else if (peek() == Token::CONST) { + // TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads: + // + // ConstDeclaration : const ConstBinding (',' ConstBinding)* ';' + // + // * It is a Syntax Error if the code that matches this production is not + // contained in extended code. + // + // However disallowing const in classic mode will break compatibility with + // existing pages. Therefore we keep allowing const with the old + // non-harmony semantics in classic mode. Consume(Token::CONST); - if (top_scope_->is_strict_mode()) { - ReportMessage("strict_const", Vector<const char*>::empty()); - *ok = false; - return NULL; + switch (top_scope_->language_mode()) { + case CLASSIC_MODE: + mode = CONST; + init_op = Token::INIT_CONST; + break; + case STRICT_MODE: + ReportMessage("strict_const", Vector<const char*>::empty()); + *ok = false; + return NULL; + case EXTENDED_MODE: + if (var_context != kSourceElement && + var_context != kForStatement) { + // In extended mode 'const' declarations are only allowed in source + // element positions. + ReportMessage("unprotected_const", Vector<const char*>::empty()); + *ok = false; + return NULL; + } + mode = CONST_HARMONY; + init_op = Token::INIT_CONST_HARMONY; } - mode = Variable::CONST; is_const = true; needs_init = true; - init_op = Token::INIT_CONST; } else if (peek() == Token::LET) { + // ES6 Draft Rev4 section 12.2.1: + // + // LetDeclaration : let LetBindingList ; + // + // * It is a Syntax Error if the code that matches this production is not + // contained in extended code. + if (!is_extended_mode()) { + ReportMessage("illegal_let", Vector<const char*>::empty()); + *ok = false; + return NULL; + } Consume(Token::LET); if (var_context != kSourceElement && var_context != kForStatement) { + // Let declarations are only allowed in source element positions. ASSERT(var_context == kStatement); ReportMessage("unprotected_let", Vector<const char*>::empty()); *ok = false; return NULL; } - mode = Variable::LET; + mode = LET; needs_init = true; init_op = Token::INIT_LET; } else { UNREACHABLE(); // by current callers } - Scope* declaration_scope = mode == Variable::LET + Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY) ? top_scope_ : top_scope_->DeclarationScope(); // The scope of a var/const declared variable anywhere inside a function // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can @@ -1674,7 +1737,7 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // is inside an initializer block, it is ignored. // // Create new block with one expected declaration. - Block* block = new(zone()) Block(isolate(), NULL, 1, true); + Block* block = factory()->NewBlock(NULL, 1, true); int nvars = 0; // the number of variables declared Handle<String> name; do { @@ -1686,7 +1749,7 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, if (fni_ != NULL) fni_->PushVariableName(name); // Strict mode variables may not be named eval or arguments - if (declaration_scope->is_strict_mode() && IsEvalOrArguments(name)) { + if (!declaration_scope->is_classic_mode() && IsEvalOrArguments(name)) { ReportMessage("strict_var_name", Vector<const char*>::empty()); *ok = false; return NULL; @@ -1704,8 +1767,10 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // If we have a const declaration, in an inner scope, the proxy is always // bound to the declared variable (independent of possibly surrounding with // statements). - Declare(name, mode, NULL, is_const /* always bound for CONST! */, - CHECK_OK); + // For let/const declarations in harmony mode, we can also immediately + // pre-resolve the proxy because it resides in the same scope as the + // declaration. + VariableProxy* proxy = Declare(name, mode, NULL, mode != VAR, CHECK_OK); nvars++; if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { ReportMessageAt(scanner().location(), "too_many_variables", @@ -1744,7 +1809,8 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, Scope* initialization_scope = is_const ? declaration_scope : top_scope_; Expression* value = NULL; int position = -1; - if (peek() == Token::ASSIGN) { + // Harmony consts have non-optional initializers. + if (peek() == Token::ASSIGN || mode == CONST_HARMONY) { Expect(Token::ASSIGN, CHECK_OK); position = scanner().location().beg_pos; value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); @@ -1756,6 +1822,12 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, } else { fni_->RemoveLastFunction(); } + if (decl_props != NULL) *decl_props = kHasInitializers; + } + + // Record the end position of the initializer. + if (proxy->var() != NULL) { + proxy->var()->set_initializer_position(scanner().location().end_pos); } // Make sure that 'const x' and 'let x' initialize 'x' to undefined. @@ -1782,12 +1854,11 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // declaration statement has been executed. This is important in // browsers where the global object (window) has lots of // properties defined in prototype objects. - if (initialization_scope->is_global_scope()) { // Compute the arguments for the runtime call. ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3); // We have at least 1 parameter. - arguments->Add(NewLiteral(name)); + arguments->Add(factory()->NewLiteral(name)); CallRuntime* initialize; if (is_const) { @@ -1798,19 +1869,15 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // and add it to the initialization statement block. // Note that the function does different things depending on // the number of arguments (1 or 2). - initialize = - new(zone()) CallRuntime( - isolate(), - isolate()->factory()->InitializeConstGlobal_symbol(), - Runtime::FunctionForId(Runtime::kInitializeConstGlobal), - arguments); + initialize = factory()->NewCallRuntime( + isolate()->factory()->InitializeConstGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeConstGlobal), + arguments); } else { // Add strict mode. // We may want to pass singleton to avoid Literal allocations. - StrictModeFlag flag = initialization_scope->is_strict_mode() - ? kStrictMode - : kNonStrictMode; - arguments->Add(NewNumberLiteral(flag)); + LanguageMode language_mode = initialization_scope->language_mode(); + arguments->Add(factory()->NewNumberLiteral(language_mode)); // Be careful not to assign a value to the global variable if // we're in a with. The initialization value should not @@ -1825,39 +1892,42 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, // and add it to the initialization statement block. // Note that the function does different things depending on // the number of arguments (2 or 3). - initialize = - new(zone()) CallRuntime( - isolate(), - isolate()->factory()->InitializeVarGlobal_symbol(), - Runtime::FunctionForId(Runtime::kInitializeVarGlobal), - arguments); + initialize = factory()->NewCallRuntime( + isolate()->factory()->InitializeVarGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeVarGlobal), + arguments); } - block->AddStatement(new(zone()) ExpressionStatement(initialize)); + block->AddStatement(factory()->NewExpressionStatement(initialize)); + } else if (needs_init) { + // Constant initializations always assign to the declared constant which + // is always at the function scope level. This is only relevant for + // dynamically looked-up variables and constants (the start context for + // constant lookups is always the function context, while it is the top + // context for var declared variables). Sigh... + // For 'let' and 'const' declared variables in harmony mode the + // initialization also always assigns to the declared variable. + ASSERT(proxy != NULL); + ASSERT(proxy->var() != NULL); + ASSERT(value != NULL); + Assignment* assignment = + factory()->NewAssignment(init_op, proxy, value, position); + block->AddStatement(factory()->NewExpressionStatement(assignment)); + value = NULL; } // Add an assignment node to the initialization statement block if we still - // have a pending initialization value. We must distinguish between - // different kinds of declarations: 'var' initializations are simply - // assignments (with all the consequences if they are inside a 'with' - // statement - they may change a 'with' object property). Constant - // initializations always assign to the declared constant which is - // always at the function scope level. This is only relevant for - // dynamically looked-up variables and constants (the start context - // for constant lookups is always the function context, while it is - // the top context for var declared variables). Sigh... - // For 'let' declared variables the initialization is in the same scope - // as the declaration. Thus dynamic lookups are unnecessary even if the - // block scope is inside a with. + // have a pending initialization value. if (value != NULL) { - bool in_with = mode == Variable::VAR ? inside_with() : false; + ASSERT(mode == VAR); + // 'var' initializations are simply assignments (with all the consequences + // if they are inside a 'with' statement - they may change a 'with' object + // property). VariableProxy* proxy = - initialization_scope->NewUnresolved(name, in_with); + initialization_scope->NewUnresolved(factory(), name); Assignment* assignment = - new(zone()) Assignment(isolate(), init_op, proxy, value, position); - if (block) { - block->AddStatement(new(zone()) ExpressionStatement(assignment)); - } + factory()->NewAssignment(init_op, proxy, value, position); + block->AddStatement(factory()->NewExpressionStatement(assignment)); } if (fni_ != NULL) fni_->Leave(); @@ -1937,7 +2007,7 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, // Parsed expression statement. ExpectSemicolon(CHECK_OK); - return new(zone()) ExpressionStatement(expr); + return factory()->NewExpressionStatement(expr); } @@ -1955,10 +2025,9 @@ IfStatement* Parser::ParseIfStatement(ZoneStringList* labels, bool* ok) { Next(); else_statement = ParseStatement(labels, CHECK_OK); } else { - else_statement = EmptyStatement(); + else_statement = factory()->NewEmptyStatement(); } - return new(zone()) IfStatement( - isolate(), condition, then_statement, else_statement); + return factory()->NewIfStatement(condition, then_statement, else_statement); } @@ -1988,7 +2057,7 @@ Statement* Parser::ParseContinueStatement(bool* ok) { return NULL; } ExpectSemicolon(CHECK_OK); - return new(zone()) ContinueStatement(target); + return factory()->NewContinueStatement(target); } @@ -2006,7 +2075,8 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) { // Parse labeled break statements that target themselves into // empty statements, e.g. 'l1: l2: l3: break l2;' if (!label.is_null() && ContainsLabel(labels, label)) { - return EmptyStatement(); + ExpectSemicolon(CHECK_OK); + return factory()->NewEmptyStatement(); } BreakableStatement* target = NULL; target = LookupBreakTarget(label, CHECK_OK); @@ -2023,7 +2093,7 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) { return NULL; } ExpectSemicolon(CHECK_OK); - return new(zone()) BreakStatement(target); + return factory()->NewBreakStatement(target); } @@ -2043,11 +2113,11 @@ Statement* Parser::ParseReturnStatement(bool* ok) { tok == Token::RBRACE || tok == Token::EOS) { ExpectSemicolon(CHECK_OK); - result = new(zone()) ReturnStatement(GetLiteralUndefined()); + result = factory()->NewReturnStatement(GetLiteralUndefined()); } else { Expression* expr = ParseExpression(true, CHECK_OK); ExpectSemicolon(CHECK_OK); - result = new(zone()) ReturnStatement(expr); + result = factory()->NewReturnStatement(expr); } // An ECMAScript program is considered syntactically incorrect if it @@ -2060,7 +2130,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) { declaration_scope->is_eval_scope()) { Handle<String> type = isolate()->factory()->illegal_return_symbol(); Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null()); - return new(zone()) ExpressionStatement(throw_error); + return factory()->NewExpressionStatement(throw_error); } return result; } @@ -2072,7 +2142,7 @@ Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { Expect(Token::WITH, CHECK_OK); - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { ReportMessage("strict_mode_with", Vector<const char*>::empty()); *ok = false; return NULL; @@ -2082,11 +2152,15 @@ Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { Expression* expr = ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - ++with_nesting_level_; top_scope_->DeclarationScope()->RecordWithStatement(); - Statement* stmt = ParseStatement(labels, CHECK_OK); - --with_nesting_level_; - return new(zone()) WithStatement(expr, stmt); + Scope* with_scope = NewScope(top_scope_, WITH_SCOPE); + Statement* stmt; + { BlockState block_state(this, with_scope); + with_scope->set_start_position(scanner().peek_location().beg_pos); + stmt = ParseStatement(labels, CHECK_OK); + with_scope->set_end_position(scanner().location().end_pos); + } + return factory()->NewWithStatement(expr, stmt); } @@ -2128,7 +2202,7 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels, // SwitchStatement :: // 'switch' '(' Expression ')' '{' CaseClause* '}' - SwitchStatement* statement = new(zone()) SwitchStatement(isolate(), labels); + SwitchStatement* statement = factory()->NewSwitchStatement(labels); Target target(&this->target_stack_, statement); Expect(Token::SWITCH, CHECK_OK); @@ -2164,8 +2238,7 @@ Statement* Parser::ParseThrowStatement(bool* ok) { Expression* exception = ParseExpression(true, CHECK_OK); ExpectSemicolon(CHECK_OK); - return new(zone()) ExpressionStatement( - new(zone()) Throw(isolate(), exception, pos)); + return factory()->NewExpressionStatement(factory()->NewThrow(exception, pos)); } @@ -2210,9 +2283,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Consume(Token::CATCH); Expect(Token::LPAREN, CHECK_OK); + catch_scope = NewScope(top_scope_, CATCH_SCOPE); + catch_scope->set_start_position(scanner().location().beg_pos); name = ParseIdentifier(CHECK_OK); - if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) { + if (!top_scope_->is_classic_mode() && IsEvalOrArguments(name)) { ReportMessage("strict_catch_variable", Vector<const char*>::empty()); *ok = false; return NULL; @@ -2222,22 +2297,16 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { if (peek() == Token::LBRACE) { Target target(&this->target_stack_, &catch_collector); - catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); - if (top_scope_->is_strict_mode()) { - catch_scope->EnableStrictMode(); - } - Variable::Mode mode = harmony_block_scoping_ - ? Variable::LET : Variable::VAR; - catch_variable = catch_scope->DeclareLocal(name, mode); + VariableMode mode = is_extended_mode() ? LET : VAR; + catch_variable = + catch_scope->DeclareLocal(name, mode, kCreatedInitialized); - Scope* saved_scope = top_scope_; - top_scope_ = catch_scope; + BlockState block_state(this, catch_scope); catch_block = ParseBlock(NULL, CHECK_OK); - top_scope_ = saved_scope; } else { Expect(Token::LBRACE, CHECK_OK); } - + catch_scope->set_end_position(scanner().location().end_pos); tok = peek(); } @@ -2255,13 +2324,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { if (catch_block != NULL && finally_block != NULL) { // If we have both, create an inner try/catch. ASSERT(catch_scope != NULL && catch_variable != NULL); - TryCatchStatement* statement = - new(zone()) TryCatchStatement(try_block, - catch_scope, - catch_variable, - catch_block); + int index = current_function_state_->NextHandlerIndex(); + TryCatchStatement* statement = factory()->NewTryCatchStatement( + index, try_block, catch_scope, catch_variable, catch_block); statement->set_escaping_targets(try_collector.targets()); - try_block = new(zone()) Block(isolate(), NULL, 1, false); + try_block = factory()->NewBlock(NULL, 1, false); try_block->AddStatement(statement); catch_block = NULL; // Clear to indicate it's been handled. } @@ -2270,14 +2337,13 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { if (catch_block != NULL) { ASSERT(finally_block == NULL); ASSERT(catch_scope != NULL && catch_variable != NULL); - result = - new(zone()) TryCatchStatement(try_block, - catch_scope, - catch_variable, - catch_block); + int index = current_function_state_->NextHandlerIndex(); + result = factory()->NewTryCatchStatement( + index, try_block, catch_scope, catch_variable, catch_block); } else { ASSERT(finally_block != NULL); - result = new(zone()) TryFinallyStatement(try_block, finally_block); + int index = current_function_state_->NextHandlerIndex(); + result = factory()->NewTryFinallyStatement(index, try_block, finally_block); // Combine the jump targets of the try block and the possible catch block. try_collector.targets()->AddAll(*catch_collector.targets()); } @@ -2292,7 +2358,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels, // DoStatement :: // 'do' Statement 'while' '(' Expression ')' ';' - DoWhileStatement* loop = new(zone()) DoWhileStatement(isolate(), labels); + DoWhileStatement* loop = factory()->NewDoWhileStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::DO, CHECK_OK); @@ -2323,7 +2389,7 @@ WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) { // WhileStatement :: // 'while' '(' Expression ')' Statement - WhileStatement* loop = new(zone()) WhileStatement(isolate(), labels); + WhileStatement* loop = factory()->NewWhileStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::WHILE, CHECK_OK); @@ -2343,17 +2409,23 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Statement* init = NULL; + // Create an in-between scope for let-bound iteration variables. + Scope* saved_scope = top_scope_; + Scope* for_scope = NewScope(top_scope_, BLOCK_SCOPE); + top_scope_ = for_scope; + Expect(Token::FOR, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); + for_scope->set_start_position(scanner().location().beg_pos); if (peek() != Token::SEMICOLON) { if (peek() == Token::VAR || peek() == Token::CONST) { Handle<String> name; Block* variable_statement = - ParseVariableDeclarations(kForStatement, &name, CHECK_OK); + ParseVariableDeclarations(kForStatement, NULL, &name, CHECK_OK); if (peek() == Token::IN && !name.is_null()) { - VariableProxy* each = top_scope_->NewUnresolved(name, inside_with()); - ForInStatement* loop = new(zone()) ForInStatement(isolate(), labels); + VariableProxy* each = top_scope_->NewUnresolved(factory(), name); + ForInStatement* loop = factory()->NewForInStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); @@ -2362,15 +2434,73 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Statement* body = ParseStatement(NULL, CHECK_OK); loop->Initialize(each, enumerable, body); - Block* result = new(zone()) Block(isolate(), NULL, 2, false); + Block* result = factory()->NewBlock(NULL, 2, false); result->AddStatement(variable_statement); result->AddStatement(loop); + top_scope_ = saved_scope; + for_scope->set_end_position(scanner().location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + ASSERT(for_scope == NULL); // Parsed for-in loop w/ variable/const declaration. return result; } else { init = variable_statement; } + } else if (peek() == Token::LET) { + Handle<String> name; + VariableDeclarationProperties decl_props = kHasNoInitializers; + Block* variable_statement = + ParseVariableDeclarations(kForStatement, + &decl_props, + &name, + CHECK_OK); + bool accept_IN = !name.is_null() && decl_props != kHasInitializers; + if (peek() == Token::IN && accept_IN) { + // Rewrite a for-in statement of the form + // + // for (let x in e) b + // + // into + // + // <let x' be a temporary variable> + // for (x' in e) { + // let x; + // x = x'; + // b; + // } + + // TODO(keuchel): Move the temporary variable to the block scope, after + // implementing stack allocated block scoped variables. + Variable* temp = top_scope_->DeclarationScope()->NewTemporary(name); + VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); + VariableProxy* each = top_scope_->NewUnresolved(factory(), name); + ForInStatement* loop = factory()->NewForInStatement(labels); + Target target(&this->target_stack_, loop); + + Expect(Token::IN, CHECK_OK); + Expression* enumerable = ParseExpression(true, CHECK_OK); + Expect(Token::RPAREN, CHECK_OK); + + Statement* body = ParseStatement(NULL, CHECK_OK); + Block* body_block = factory()->NewBlock(NULL, 3, false); + Assignment* assignment = factory()->NewAssignment( + Token::ASSIGN, each, temp_proxy, RelocInfo::kNoPosition); + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment); + body_block->AddStatement(variable_statement); + body_block->AddStatement(assignment_statement); + body_block->AddStatement(body); + loop->Initialize(temp_proxy, enumerable, body_block); + top_scope_ = saved_scope; + for_scope->set_end_position(scanner().location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + body_block->set_block_scope(for_scope); + // Parsed for-in loop w/ let declaration. + return loop; + } else { + init = variable_statement; + } } else { Expression* expression = ParseExpression(false, CHECK_OK); if (peek() == Token::IN) { @@ -2383,7 +2513,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { isolate()->factory()->invalid_lhs_in_for_in_symbol(); expression = NewThrowReferenceError(type); } - ForInStatement* loop = new(zone()) ForInStatement(isolate(), labels); + ForInStatement* loop = factory()->NewForInStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); @@ -2392,17 +2522,21 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Statement* body = ParseStatement(NULL, CHECK_OK); if (loop) loop->Initialize(expression, enumerable, body); + top_scope_ = saved_scope; + for_scope->set_end_position(scanner().location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + ASSERT(for_scope == NULL); // Parsed for-in loop. return loop; } else { - init = new(zone()) ExpressionStatement(expression); + init = factory()->NewExpressionStatement(expression); } } } // Standard 'for' loop - ForStatement* loop = new(zone()) ForStatement(isolate(), labels); + ForStatement* loop = factory()->NewForStatement(labels); Target target(&this->target_stack_, loop); // Parsed initializer at this point. @@ -2417,13 +2551,36 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Statement* next = NULL; if (peek() != Token::RPAREN) { Expression* exp = ParseExpression(true, CHECK_OK); - next = new(zone()) ExpressionStatement(exp); + next = factory()->NewExpressionStatement(exp); } Expect(Token::RPAREN, CHECK_OK); Statement* body = ParseStatement(NULL, CHECK_OK); - if (loop) loop->Initialize(init, cond, next, body); - return loop; + top_scope_ = saved_scope; + for_scope->set_end_position(scanner().location().end_pos); + for_scope = for_scope->FinalizeBlockScope(); + if (for_scope != NULL) { + // Rewrite a for statement of the form + // + // for (let x = i; c; n) b + // + // into + // + // { + // let x = i; + // for (; c; n) b + // } + ASSERT(init != NULL); + Block* result = factory()->NewBlock(NULL, 2, false); + result->AddStatement(init); + result->AddStatement(loop); + result->set_block_scope(for_scope); + if (loop) loop->Initialize(NULL, cond, next, body); + return result; + } else { + if (loop) loop->Initialize(init, cond, next, body); + return loop; + } } @@ -2438,8 +2595,8 @@ Expression* Parser::ParseExpression(bool accept_IN, bool* ok) { Expect(Token::COMMA, CHECK_OK); int position = scanner().location().beg_pos; Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); - result = new(zone()) BinaryOperation( - isolate(), Token::COMMA, result, right, position); + result = + factory()->NewBinaryOperation(Token::COMMA, result, right, position); } return result; } @@ -2470,10 +2627,11 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { expression = NewThrowReferenceError(type); } - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { // Assignment to eval or arguments is disallowed in strict mode. CheckStrictModeLValue(expression, "strict_lhs_assignment", CHECK_OK); } + MarkAsLValue(expression); Token::Value op = Next(); // Get assignment operator. int pos = scanner().location().beg_pos; @@ -2489,13 +2647,13 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { property != NULL && property->obj()->AsVariableProxy() != NULL && property->obj()->AsVariableProxy()->is_this()) { - lexical_scope_->AddProperty(); + current_function_state_->AddProperty(); } // If we assign a function literal to a property we pretenure the // literal so it can be added as a constant function property. if (property != NULL && right->AsFunctionLiteral() != NULL) { - right->AsFunctionLiteral()->set_pretenure(true); + right->AsFunctionLiteral()->set_pretenure(); } if (fni_ != NULL) { @@ -2513,7 +2671,7 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { fni_->Leave(); } - return new(zone()) Assignment(isolate(), op, expression, right, pos); + return factory()->NewAssignment(op, expression, right, pos); } @@ -2535,8 +2693,8 @@ Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) { Expect(Token::COLON, CHECK_OK); int right_position = scanner().peek_location().beg_pos; Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); - return new(zone()) Conditional( - isolate(), expression, left, right, left_position, right_position); + return factory()->NewConditional( + expression, left, right, left_position, right_position); } @@ -2567,41 +2725,47 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { switch (op) { case Token::ADD: - x = NewNumberLiteral(x_val + y_val); + x = factory()->NewNumberLiteral(x_val + y_val); continue; case Token::SUB: - x = NewNumberLiteral(x_val - y_val); + x = factory()->NewNumberLiteral(x_val - y_val); continue; case Token::MUL: - x = NewNumberLiteral(x_val * y_val); + x = factory()->NewNumberLiteral(x_val * y_val); continue; case Token::DIV: - x = NewNumberLiteral(x_val / y_val); + x = factory()->NewNumberLiteral(x_val / y_val); continue; - case Token::BIT_OR: - x = NewNumberLiteral(DoubleToInt32(x_val) | DoubleToInt32(y_val)); + case Token::BIT_OR: { + int value = DoubleToInt32(x_val) | DoubleToInt32(y_val); + x = factory()->NewNumberLiteral(value); continue; - case Token::BIT_AND: - x = NewNumberLiteral(DoubleToInt32(x_val) & DoubleToInt32(y_val)); + } + case Token::BIT_AND: { + int value = DoubleToInt32(x_val) & DoubleToInt32(y_val); + x = factory()->NewNumberLiteral(value); continue; - case Token::BIT_XOR: - x = NewNumberLiteral(DoubleToInt32(x_val) ^ DoubleToInt32(y_val)); + } + case Token::BIT_XOR: { + int value = DoubleToInt32(x_val) ^ DoubleToInt32(y_val); + x = factory()->NewNumberLiteral(value); continue; + } case Token::SHL: { int value = DoubleToInt32(x_val) << (DoubleToInt32(y_val) & 0x1f); - x = NewNumberLiteral(value); + x = factory()->NewNumberLiteral(value); continue; } case Token::SHR: { uint32_t shift = DoubleToInt32(y_val) & 0x1f; uint32_t value = DoubleToUint32(x_val) >> shift; - x = NewNumberLiteral(value); + x = factory()->NewNumberLiteral(value); continue; } case Token::SAR: { uint32_t shift = DoubleToInt32(y_val) & 0x1f; int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift); - x = NewNumberLiteral(value); + x = factory()->NewNumberLiteral(value); continue; } default: @@ -2620,15 +2784,15 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { case Token::NE_STRICT: cmp = Token::EQ_STRICT; break; default: break; } - x = NewCompareNode(cmp, x, y, position); + x = factory()->NewCompareOperation(cmp, x, y, position); if (cmp != op) { // The comparison was negated - add a NOT. - x = new(zone()) UnaryOperation(isolate(), Token::NOT, x, position); + x = factory()->NewUnaryOperation(Token::NOT, x, position); } } else { // We have a "normal" binary operation. - x = new(zone()) BinaryOperation(isolate(), op, x, y, position); + x = factory()->NewBinaryOperation(op, x, y, position); } } } @@ -2636,27 +2800,6 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { } -Expression* Parser::NewCompareNode(Token::Value op, - Expression* x, - Expression* y, - int position) { - ASSERT(op != Token::NE && op != Token::NE_STRICT); - if (op == Token::EQ || op == Token::EQ_STRICT) { - bool is_strict = (op == Token::EQ_STRICT); - Literal* x_literal = x->AsLiteral(); - if (x_literal != NULL && x_literal->IsNull()) { - return new(zone()) CompareToNull(isolate(), is_strict, y); - } - - Literal* y_literal = y->AsLiteral(); - if (y_literal != NULL && y_literal->IsNull()) { - return new(zone()) CompareToNull(isolate(), is_strict, x); - } - } - return new(zone()) CompareOperation(isolate(), op, x, y, position); -} - - Expression* Parser::ParseUnaryExpression(bool* ok) { // UnaryExpression :: // PostfixExpression @@ -2682,7 +2825,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { // Convert the literal to a boolean condition and negate it. bool condition = literal->ToBoolean()->IsTrue(); Handle<Object> result(isolate()->heap()->ToBoolean(!condition)); - return NewLiteral(result); + return factory()->NewLiteral(result); } else if (literal->IsNumber()) { // Compute some expressions involving only number literals. double value = literal->Number(); @@ -2690,9 +2833,9 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { case Token::ADD: return expression; case Token::SUB: - return NewNumberLiteral(-value); + return factory()->NewNumberLiteral(-value); case Token::BIT_NOT: - return NewNumberLiteral(~DoubleToInt32(value)); + return factory()->NewNumberLiteral(~DoubleToInt32(value)); default: break; } @@ -2700,7 +2843,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { } // "delete identifier" is a syntax error in strict mode. - if (op == Token::DELETE && top_scope_->is_strict_mode()) { + if (op == Token::DELETE && !top_scope_->is_classic_mode()) { VariableProxy* operand = expression->AsVariableProxy(); if (operand != NULL && !operand->is_this()) { ReportMessage("strict_delete", Vector<const char*>::empty()); @@ -2709,7 +2852,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { } } - return new(zone()) UnaryOperation(isolate(), op, expression, position); + return factory()->NewUnaryOperation(op, expression, position); } else if (Token::IsCountOp(op)) { op = Next(); @@ -2724,17 +2867,17 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { expression = NewThrowReferenceError(type); } - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { // Prefix expression operand in strict mode may not be eval or arguments. CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK); } + MarkAsLValue(expression); int position = scanner().location().beg_pos; - return new(zone()) CountOperation(isolate(), - op, - true /* prefix */, - expression, - position); + return factory()->NewCountOperation(op, + true /* prefix */, + expression, + position); } else { return ParsePostfixExpression(ok); @@ -2759,19 +2902,19 @@ Expression* Parser::ParsePostfixExpression(bool* ok) { expression = NewThrowReferenceError(type); } - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { // Postfix expression operand in strict mode may not be eval or arguments. CheckStrictModeLValue(expression, "strict_lhs_prefix", CHECK_OK); } + MarkAsLValue(expression); Token::Value next = Next(); int position = scanner().location().beg_pos; expression = - new(zone()) CountOperation(isolate(), - next, - false /* postfix */, - expression, - position); + factory()->NewCountOperation(next, + false /* postfix */, + expression, + position); } return expression; } @@ -2794,37 +2937,40 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { Consume(Token::LBRACK); int pos = scanner().location().beg_pos; Expression* index = ParseExpression(true, CHECK_OK); - result = new(zone()) Property(isolate(), result, index, pos); + result = factory()->NewProperty(result, index, pos); Expect(Token::RBRACK, CHECK_OK); break; } case Token::LPAREN: { - int pos = scanner().location().beg_pos; + int pos; + if (scanner().current_token() == Token::IDENTIFIER) { + // For call of an identifier we want to report position of + // the identifier as position of the call in the stack trace. + pos = scanner().location().beg_pos; + } else { + // For other kinds of calls we record position of the parenthesis as + // position of the call. Note that this is extremely important for + // expressions of the form function(){...}() for which call position + // should not point to the closing brace otherwise it will intersect + // with positions recorded for function literal and confuse debugger. + pos = scanner().peek_location().beg_pos; + } ZoneList<Expression*>* args = ParseArguments(CHECK_OK); // Keep track of eval() calls since they disable all local variable // optimizations. // The calls that need special treatment are the - // direct (i.e. not aliased) eval calls. These calls are all of the - // form eval(...) with no explicit receiver object where eval is not - // declared in the current scope chain. + // direct eval calls. These calls are all of the form eval(...), with + // no explicit receiver. // These calls are marked as potentially direct eval calls. Whether // they are actually direct calls to eval is determined at run time. - // TODO(994): In ES5, it doesn't matter if the "eval" var is declared - // in the local scope chain. It only matters that it's called "eval", - // is called without a receiver and it refers to the original eval - // function. VariableProxy* callee = result->AsVariableProxy(); if (callee != NULL && callee->IsVariable(isolate()->factory()->eval_symbol())) { - Handle<String> name = callee->name(); - Variable* var = top_scope_->Lookup(name); - if (var == NULL) { - top_scope_->DeclarationScope()->RecordEvalCall(); - } + top_scope_->DeclarationScope()->RecordEvalCall(); } - result = NewCall(result, args, pos); + result = factory()->NewCall(result, args, pos); break; } @@ -2832,10 +2978,8 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { Consume(Token::PERIOD); int pos = scanner().location().beg_pos; Handle<String> name = ParseIdentifierName(CHECK_OK); - result = new(zone()) Property(isolate(), - result, - NewLiteral(name), - pos); + result = + factory()->NewProperty(result, factory()->NewLiteral(name), pos); if (fni_ != NULL) fni_->PushLiteralName(name); break; } @@ -2871,10 +3015,8 @@ Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { if (!stack->is_empty()) { int last = stack->pop(); - result = new(zone()) CallNew(isolate(), - result, - new(zone()) ZoneList<Expression*>(0), - last); + result = factory()->NewCallNew( + result, new(zone()) ZoneList<Expression*>(0), last); } return result; } @@ -2926,7 +3068,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, Consume(Token::LBRACK); int pos = scanner().location().beg_pos; Expression* index = ParseExpression(true, CHECK_OK); - result = new(zone()) Property(isolate(), result, index, pos); + result = factory()->NewProperty(result, index, pos); if (fni_ != NULL) { if (index->IsPropertyName()) { fni_->PushLiteralName(index->AsLiteral()->AsPropertyName()); @@ -2942,10 +3084,8 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, Consume(Token::PERIOD); int pos = scanner().location().beg_pos; Handle<String> name = ParseIdentifierName(CHECK_OK); - result = new(zone()) Property(isolate(), - result, - NewLiteral(name), - pos); + result = + factory()->NewProperty(result, factory()->NewLiteral(name), pos); if (fni_ != NULL) fni_->PushLiteralName(name); break; } @@ -2954,7 +3094,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, // Consume one of the new prefixes (already parsed). ZoneList<Expression*>* args = ParseArguments(CHECK_OK); int last = stack->pop(); - result = new(zone()) CallNew(isolate(), result, args, last); + result = factory()->NewCallNew(result, args, last); break; } default: @@ -2973,7 +3113,7 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) { Expect(Token::DEBUGGER, CHECK_OK); ExpectSemicolon(CHECK_OK); - return new(zone()) DebuggerStatement(); + return factory()->NewDebuggerStatement(); } @@ -2999,9 +3139,9 @@ void Parser::ReportUnexpectedToken(Token::Value token) { return ReportMessage("unexpected_reserved", Vector<const char*>::empty()); case Token::FUTURE_STRICT_RESERVED_WORD: - return ReportMessage(top_scope_->is_strict_mode() ? - "unexpected_strict_reserved" : - "unexpected_token_identifier", + return ReportMessage(top_scope_->is_classic_mode() ? + "unexpected_token_identifier" : + "unexpected_strict_reserved", Vector<const char*>::empty()); default: const char* name = Token::String(token); @@ -3038,35 +3178,31 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { switch (peek()) { case Token::THIS: { Consume(Token::THIS); - result = new(zone()) VariableProxy(isolate(), top_scope_->receiver()); + result = factory()->NewVariableProxy(top_scope_->receiver()); break; } case Token::NULL_LITERAL: Consume(Token::NULL_LITERAL); - result = new(zone()) Literal( - isolate(), isolate()->factory()->null_value()); + result = factory()->NewLiteral(isolate()->factory()->null_value()); break; case Token::TRUE_LITERAL: Consume(Token::TRUE_LITERAL); - result = new(zone()) Literal( - isolate(), isolate()->factory()->true_value()); + result = factory()->NewLiteral(isolate()->factory()->true_value()); break; case Token::FALSE_LITERAL: Consume(Token::FALSE_LITERAL); - result = new(zone()) Literal( - isolate(), isolate()->factory()->false_value()); + result = factory()->NewLiteral(isolate()->factory()->false_value()); break; case Token::IDENTIFIER: case Token::FUTURE_STRICT_RESERVED_WORD: { Handle<String> name = ParseIdentifier(CHECK_OK); if (fni_ != NULL) fni_->PushVariableName(name); - result = top_scope_->NewUnresolved(name, - inside_with(), - scanner().location().beg_pos); + result = top_scope_->NewUnresolved( + factory(), name, scanner().location().beg_pos); break; } @@ -3076,14 +3212,14 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { double value = StringToDouble(isolate()->unicode_cache(), scanner().literal_ascii_string(), ALLOW_HEX | ALLOW_OCTALS); - result = NewNumberLiteral(value); + result = factory()->NewNumberLiteral(value); break; } case Token::STRING: { Consume(Token::STRING); Handle<String> symbol = GetSymbol(CHECK_OK); - result = NewLiteral(symbol); + result = factory()->NewLiteral(symbol); if (fni_ != NULL) fni_->PushLiteralName(symbol); break; } @@ -3181,11 +3317,14 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { Expect(Token::RBRACK, CHECK_OK); // Update the scope information before the pre-parsing bailout. - int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + int literal_index = current_function_state_->NextMaterializedLiteralIndex(); - // Allocate a fixed array with all the literals. - Handle<FixedArray> literals = + // Allocate a fixed array to hold all the object literals. + Handle<FixedArray> object_literals = isolate()->factory()->NewFixedArray(values->length(), TENURED); + Handle<FixedDoubleArray> double_literals; + ElementsKind elements_kind = FAST_SMI_ONLY_ELEMENTS; + bool has_only_undefined_values = true; // Fill in the literals. bool is_simple = true; @@ -3197,21 +3336,85 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { } Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i)); if (boilerplate_value->IsUndefined()) { - literals->set_the_hole(i); + object_literals->set_the_hole(i); + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + double_literals->set_the_hole(i); + } is_simple = false; } else { - literals->set(i, *boilerplate_value); + // Examine each literal element, and adjust the ElementsKind if the + // literal element is not of a type that can be stored in the current + // ElementsKind. Start with FAST_SMI_ONLY_ELEMENTS, and transition to + // FAST_DOUBLE_ELEMENTS and FAST_ELEMENTS as necessary. Always remember + // the tagged value, no matter what the ElementsKind is in case we + // ultimately end up in FAST_ELEMENTS. + has_only_undefined_values = false; + object_literals->set(i, *boilerplate_value); + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + // Smi only elements. Notice if a transition to FAST_DOUBLE_ELEMENTS or + // FAST_ELEMENTS is required. + if (!boilerplate_value->IsSmi()) { + if (boilerplate_value->IsNumber() && FLAG_smi_only_arrays) { + // Allocate a double array on the FAST_DOUBLE_ELEMENTS transition to + // avoid over-allocating in TENURED space. + double_literals = isolate()->factory()->NewFixedDoubleArray( + values->length(), TENURED); + // Copy the contents of the FAST_SMI_ONLY_ELEMENT array to the + // FAST_DOUBLE_ELEMENTS array so that they are in sync. + for (int j = 0; j < i; ++j) { + Object* smi_value = object_literals->get(j); + if (smi_value->IsTheHole()) { + double_literals->set_the_hole(j); + } else { + double_literals->set(j, Smi::cast(smi_value)->value()); + } + } + double_literals->set(i, boilerplate_value->Number()); + elements_kind = FAST_DOUBLE_ELEMENTS; + } else { + elements_kind = FAST_ELEMENTS; + } + } + } else if (elements_kind == FAST_DOUBLE_ELEMENTS) { + // Continue to store double values in to FAST_DOUBLE_ELEMENTS arrays + // until the first value is seen that can't be stored as a double. + if (boilerplate_value->IsNumber()) { + double_literals->set(i, boilerplate_value->Number()); + } else { + elements_kind = FAST_ELEMENTS; + } + } } } + // Very small array literals that don't have a concrete hint about their type + // from a constant value should default to the slow case to avoid lots of + // elements transitions on really small objects. + if (has_only_undefined_values && values->length() <= 2) { + elements_kind = FAST_ELEMENTS; + } + // Simple and shallow arrays can be lazily copied, we transform the // elements array to a copy-on-write array. - if (is_simple && depth == 1 && values->length() > 0) { - literals->set_map(isolate()->heap()->fixed_cow_array_map()); + if (is_simple && depth == 1 && values->length() > 0 && + elements_kind != FAST_DOUBLE_ELEMENTS) { + object_literals->set_map(isolate()->heap()->fixed_cow_array_map()); } - return new(zone()) ArrayLiteral( - isolate(), literals, values, literal_index, is_simple, depth); + Handle<FixedArrayBase> element_values = elements_kind == FAST_DOUBLE_ELEMENTS + ? Handle<FixedArrayBase>(double_literals) + : Handle<FixedArrayBase>(object_literals); + + // Remember both the literal's constant values as well as the ElementsKind + // in a 2-element FixedArray. + Handle<FixedArray> literals = + isolate()->factory()->NewFixedArray(2, TENURED); + + literals->set(0, Smi::FromInt(elements_kind)); + literals->set(1, *element_values); + + return factory()->NewArrayLiteral( + literals, values, literal_index, is_simple, depth); } @@ -3291,11 +3494,11 @@ bool IsEqualNumber(void* first, void* second); // Validation per 11.1.5 Object Initialiser class ObjectLiteralPropertyChecker { public: - ObjectLiteralPropertyChecker(Parser* parser, bool strict) : + ObjectLiteralPropertyChecker(Parser* parser, LanguageMode language_mode) : props(&IsEqualString), elems(&IsEqualNumber), parser_(parser), - strict_(strict) { + language_mode_(language_mode) { } void CheckProperty( @@ -3325,7 +3528,7 @@ class ObjectLiteralPropertyChecker { HashMap props; HashMap elems; Parser* parser_; - bool strict_; + LanguageMode language_mode_; }; @@ -3336,7 +3539,7 @@ void ObjectLiteralPropertyChecker::CheckProperty( ASSERT(property != NULL); - Literal *lit = property->key(); + Literal* lit = property->key(); Handle<Object> handle = lit->handle(); uint32_t hash; @@ -3374,8 +3577,8 @@ void ObjectLiteralPropertyChecker::CheckProperty( intptr_t prev = reinterpret_cast<intptr_t> (entry->value); intptr_t curr = GetPropertyKind(property); - // Duplicate data properties are illegal in strict mode. - if (strict_ && (curr & prev & kData) != 0) { + // Duplicate data properties are illegal in strict or extended mode. + if (language_mode_ != CLASSIC_MODE && (curr & prev & kData) != 0) { parser_->ReportMessageAt(loc, "strict_duplicate_property", Vector<const char*>::empty()); *ok = false; @@ -3486,11 +3689,9 @@ ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter, RelocInfo::kNoPosition, FunctionLiteral::ANONYMOUS_EXPRESSION, CHECK_OK); - // Allow any number of parameters for compatiabilty with JSC. + // Allow any number of parameters for compatibilty with JSC. // Specification only allows zero parameters for get and one for set. - ObjectLiteral::Property* property = - new(zone()) ObjectLiteral::Property(is_getter, value); - return property; + return factory()->NewObjectLiteralProperty(is_getter, value); } else { ReportUnexpectedToken(next); *ok = false; @@ -3511,7 +3712,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { int number_of_boilerplate_properties = 0; bool has_function = false; - ObjectLiteralPropertyChecker checker(this, top_scope_->is_strict_mode()); + ObjectLiteralPropertyChecker checker(this, top_scope_->language_mode()); Expect(Token::LBRACE, CHECK_OK); @@ -3555,7 +3756,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { } // Failed to parse as get/set property, so it's just a property // called "get" or "set". - key = NewLiteral(id); + key = factory()->NewLiteral(id); break; } case Token::STRING: { @@ -3564,10 +3765,10 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { if (fni_ != NULL) fni_->PushLiteralName(string); uint32_t index; if (!string.is_null() && string->AsArrayIndex(&index)) { - key = NewNumberLiteral(index); + key = factory()->NewNumberLiteral(index); break; } - key = NewLiteral(string); + key = factory()->NewLiteral(string); break; } case Token::NUMBER: { @@ -3576,14 +3777,14 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { double value = StringToDouble(isolate()->unicode_cache(), scanner().literal_ascii_string(), ALLOW_HEX | ALLOW_OCTALS); - key = NewNumberLiteral(value); + key = factory()->NewNumberLiteral(value); break; } default: if (Token::IsKeyword(next)) { Consume(next); Handle<String> string = GetSymbol(CHECK_OK); - key = NewLiteral(string); + key = factory()->NewLiteral(string); } else { // Unexpected token. Token::Value next = Next(); @@ -3599,11 +3800,13 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { ObjectLiteral::Property* property = new(zone()) ObjectLiteral::Property(key, value); - // Mark object literals that contain function literals and pretenure the - // literal so it can be added as a constant function property. - if (value->AsFunctionLiteral() != NULL) { + // Mark top-level object literals that contain function literals and + // pretenure the literal so it can be added as a constant function + // property. + if (top_scope_->DeclarationScope()->is_global_scope() && + value->AsFunctionLiteral() != NULL) { has_function = true; - value->AsFunctionLiteral()->set_pretenure(true); + value->AsFunctionLiteral()->set_pretenure(); } // Count CONSTANT or COMPUTED properties to maintain the enumeration order. @@ -3623,7 +3826,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { Expect(Token::RBRACE, CHECK_OK); // Computation of literal_index must happen before pre parse bailout. - int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + int literal_index = current_function_state_->NextMaterializedLiteralIndex(); Handle<FixedArray> constant_properties = isolate()->factory()->NewFixedArray( number_of_boilerplate_properties * 2, TENURED); @@ -3636,14 +3839,13 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { &is_simple, &fast_elements, &depth); - return new(zone()) ObjectLiteral(isolate(), - constant_properties, - properties, - literal_index, - is_simple, - fast_elements, - depth, - has_function); + return factory()->NewObjectLiteral(constant_properties, + properties, + literal_index, + is_simple, + fast_elements, + depth, + has_function); } @@ -3655,15 +3857,14 @@ Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { return NULL; } - int literal_index = lexical_scope_->NextMaterializedLiteralIndex(); + int literal_index = current_function_state_->NextMaterializedLiteralIndex(); Handle<String> js_pattern = NextLiteralString(TENURED); scanner().ScanRegExpFlags(); Handle<String> js_flags = NextLiteralString(TENURED); Next(); - return new(zone()) RegExpLiteral( - isolate(), js_pattern, js_flags, literal_index); + return factory()->NewRegExpLiteral(js_pattern, js_flags, literal_index); } @@ -3691,6 +3892,98 @@ ZoneList<Expression*>* Parser::ParseArguments(bool* ok) { } +class SingletonLogger : public ParserRecorder { + public: + SingletonLogger() : has_error_(false), start_(-1), end_(-1) { } + virtual ~SingletonLogger() { } + + void Reset() { has_error_ = false; } + + virtual void LogFunction(int start, + int end, + int literals, + int properties, + LanguageMode mode) { + ASSERT(!has_error_); + start_ = start; + end_ = end; + literals_ = literals; + properties_ = properties; + mode_ = mode; + }; + + // Logs a symbol creation of a literal or identifier. + virtual void LogAsciiSymbol(int start, Vector<const char> literal) { } + virtual void LogUC16Symbol(int start, Vector<const uc16> literal) { } + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + virtual void LogMessage(int start, + int end, + const char* message, + const char* argument_opt) { + has_error_ = true; + start_ = start; + end_ = end; + message_ = message; + argument_opt_ = argument_opt; + } + + virtual int function_position() { return 0; } + + virtual int symbol_position() { return 0; } + + virtual int symbol_ids() { return -1; } + + virtual Vector<unsigned> ExtractData() { + UNREACHABLE(); + return Vector<unsigned>(); + } + + virtual void PauseRecording() { } + + virtual void ResumeRecording() { } + + bool has_error() { return has_error_; } + + int start() { return start_; } + int end() { return end_; } + int literals() { + ASSERT(!has_error_); + return literals_; + } + int properties() { + ASSERT(!has_error_); + return properties_; + } + LanguageMode language_mode() { + ASSERT(!has_error_); + return mode_; + } + const char* message() { + ASSERT(has_error_); + return message_; + } + const char* argument_opt() { + ASSERT(has_error_); + return argument_opt_; + } + + private: + bool has_error_; + int start_; + int end_; + // For function entries. + int literals_; + int properties_; + LanguageMode mode_; + // For error messages. + const char* message_; + const char* argument_opt_; +}; + + FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, bool name_is_strict_reserved, int function_token_position, @@ -3713,26 +4006,25 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, // Function declarations are function scoped in normal mode, so they are // hoisted. In harmony block scoping mode they are block scoped, so they // are not hoisted. - Scope* scope = (type == FunctionLiteral::DECLARATION && - !harmony_block_scoping_) - ? NewScope(top_scope_->DeclarationScope(), Scope::FUNCTION_SCOPE, false) - : NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); - ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8); - int materialized_literal_count; - int expected_property_count; - int start_pos; - int end_pos; + Scope* scope = (type == FunctionLiteral::DECLARATION && !is_extended_mode()) + ? NewScope(top_scope_->DeclarationScope(), FUNCTION_SCOPE) + : NewScope(top_scope_, FUNCTION_SCOPE); + ZoneList<Statement*>* body = NULL; + int materialized_literal_count = -1; + int expected_property_count = -1; + int handler_count = 0; bool only_simple_this_property_assignments; Handle<FixedArray> this_property_assignments; bool has_duplicate_parameters = false; + AstProperties ast_properties; // Parse function body. - { LexicalScope lexical_scope(this, scope, isolate()); + { FunctionState function_state(this, scope, isolate()); top_scope_->SetScopeName(function_name); // FormalParameterList :: // '(' (Identifier)*[','] ')' Expect(Token::LPAREN, CHECK_OK); - start_pos = scanner().location().beg_pos; + scope->set_start_position(scanner().location().beg_pos); Scanner::Location name_loc = Scanner::Location::invalid(); Scanner::Location dupe_loc = Scanner::Location::invalid(); Scanner::Location reserved_loc = Scanner::Location::invalid(); @@ -3756,10 +4048,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, reserved_loc = scanner().location(); } - top_scope_->DeclareParameter(param_name, - harmony_block_scoping_ - ? Variable::LET - : Variable::VAR); + top_scope_->DeclareParameter(param_name, is_extended_mode() ? LET : VAR); num_parameters++; if (num_parameters > kMaxNumFunctionParameters) { ReportMessageAt(scanner().location(), "too_many_parameters", @@ -3780,71 +4069,130 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, // NOTE: We create a proxy and resolve it here so that in the // future we can change the AST to only refer to VariableProxies // instead of Variables and Proxis as is the case now. + Variable* fvar = NULL; + Token::Value fvar_init_op = Token::INIT_CONST; if (type == FunctionLiteral::NAMED_EXPRESSION) { - Variable* fvar = top_scope_->DeclareFunctionVar(function_name); - VariableProxy* fproxy = - top_scope_->NewUnresolved(function_name, inside_with()); - fproxy->BindTo(fvar); - body->Add(new(zone()) ExpressionStatement( - new(zone()) Assignment(isolate(), - Token::INIT_CONST, - fproxy, - new(zone()) ThisFunction(isolate()), - RelocInfo::kNoPosition))); + VariableMode fvar_mode; + if (is_extended_mode()) { + fvar_mode = CONST_HARMONY; + fvar_init_op = Token::INIT_CONST_HARMONY; + } else { + fvar_mode = CONST; + } + fvar = + top_scope_->DeclareFunctionVar(function_name, fvar_mode, factory()); } - // Determine if the function will be lazily compiled. The mode can only - // be PARSE_LAZILY if the --lazy flag is true. We will not lazily - // compile if we do not have preparser data for the function. + // Determine whether the function will be lazily compiled. + // The heuristics are: + // - It must not have been prohibited by the caller to Parse (some callers + // need a full AST). + // - The outer scope must be trivial (only global variables in scope). + // - The function mustn't be a function expression with an open parenthesis + // before; we consider that a hint that the function will be called + // immediately, and it would be a waste of time to make it lazily + // compiled. + // These are all things we can know at this point, without looking at the + // function itself. bool is_lazily_compiled = (mode() == PARSE_LAZILY && top_scope_->outer_scope()->is_global_scope() && top_scope_->HasTrivialOuterContext() && - !parenthesized_function_ && - pre_data() != NULL); + !parenthesized_function_); parenthesized_function_ = false; // The bit was set for this function only. if (is_lazily_compiled) { int function_block_pos = scanner().location().beg_pos; - FunctionEntry entry = pre_data()->GetFunctionEntry(function_block_pos); - if (!entry.is_valid()) { - // There is no preparser data for the function, we will not lazily - // compile after all. - is_lazily_compiled = false; + FunctionEntry entry; + if (pre_data_ != NULL) { + // If we have pre_data_, we use it to skip parsing the function body. + // the preparser data contains the information we need to construct the + // lazy function. + entry = pre_data()->GetFunctionEntry(function_block_pos); + if (entry.is_valid()) { + if (entry.end_pos() <= function_block_pos) { + // End position greater than end of stream is safe, and hard + // to check. + ReportInvalidPreparseData(function_name, CHECK_OK); + } + scanner().SeekForward(entry.end_pos() - 1); + + scope->set_end_position(entry.end_pos()); + Expect(Token::RBRACE, CHECK_OK); + isolate()->counters()->total_preparse_skipped()->Increment( + scope->end_position() - function_block_pos); + materialized_literal_count = entry.literal_count(); + expected_property_count = entry.property_count(); + top_scope_->SetLanguageMode(entry.language_mode()); + only_simple_this_property_assignments = false; + this_property_assignments = isolate()->factory()->empty_fixed_array(); + } else { + is_lazily_compiled = false; + } } else { - end_pos = entry.end_pos(); - if (end_pos <= function_block_pos) { - // End position greater than end of stream is safe, and hard to check. - ReportInvalidPreparseData(function_name, CHECK_OK); + // With no preparser data, we partially parse the function, without + // building an AST. This gathers the data needed to build a lazy + // function. + SingletonLogger logger; + preparser::PreParser::PreParseResult result = + LazyParseFunctionLiteral(&logger); + if (result == preparser::PreParser::kPreParseStackOverflow) { + // Propagate stack overflow. + stack_overflow_ = true; + *ok = false; + return NULL; + } + if (logger.has_error()) { + const char* arg = logger.argument_opt(); + Vector<const char*> args; + if (arg != NULL) { + args = Vector<const char*>(&arg, 1); + } + ReportMessageAt(Scanner::Location(logger.start(), logger.end()), + logger.message(), args); + *ok = false; + return NULL; } + scope->set_end_position(logger.end()); + Expect(Token::RBRACE, CHECK_OK); isolate()->counters()->total_preparse_skipped()->Increment( - end_pos - function_block_pos); - // Seek to position just before terminal '}'. - scanner().SeekForward(end_pos - 1); - materialized_literal_count = entry.literal_count(); - expected_property_count = entry.property_count(); - if (entry.strict_mode()) top_scope_->EnableStrictMode(); + scope->end_position() - function_block_pos); + materialized_literal_count = logger.literals(); + expected_property_count = logger.properties(); + top_scope_->SetLanguageMode(logger.language_mode()); only_simple_this_property_assignments = false; this_property_assignments = isolate()->factory()->empty_fixed_array(); - Expect(Token::RBRACE, CHECK_OK); } } if (!is_lazily_compiled) { + body = new(zone()) ZoneList<Statement*>(8); + if (fvar != NULL) { + VariableProxy* fproxy = + top_scope_->NewUnresolved(factory(), function_name); + fproxy->BindTo(fvar); + body->Add(factory()->NewExpressionStatement( + factory()->NewAssignment(fvar_init_op, + fproxy, + factory()->NewThisFunction(), + RelocInfo::kNoPosition))); + } ParseSourceElements(body, Token::RBRACE, CHECK_OK); - materialized_literal_count = lexical_scope.materialized_literal_count(); - expected_property_count = lexical_scope.expected_property_count(); + materialized_literal_count = function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + handler_count = function_state.handler_count(); only_simple_this_property_assignments = - lexical_scope.only_simple_this_property_assignments(); - this_property_assignments = lexical_scope.this_property_assignments(); + function_state.only_simple_this_property_assignments(); + this_property_assignments = function_state.this_property_assignments(); Expect(Token::RBRACE, CHECK_OK); - end_pos = scanner().location().end_pos; + scope->set_end_position(scanner().location().end_pos); } // Validate strict mode. - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { if (IsEvalOrArguments(function_name)) { + int start_pos = scope->start_position(); int position = function_token_position != RelocInfo::kNoPosition ? function_token_position : (start_pos > 0 ? start_pos - 1 : start_pos); @@ -3867,6 +4215,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, return NULL; } if (name_is_strict_reserved) { + int start_pos = scope->start_position(); int position = function_token_position != RelocInfo::kNoPosition ? function_token_position : (start_pos > 0 ? start_pos - 1 : start_pos); @@ -3882,35 +4231,60 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, *ok = false; return NULL; } - CheckOctalLiteral(start_pos, end_pos, CHECK_OK); + CheckOctalLiteral(scope->start_position(), + scope->end_position(), + CHECK_OK); } + ast_properties = *factory()->visitor()->ast_properties(); } - if (harmony_block_scoping_) { + if (is_extended_mode()) { CheckConflictingVarDeclarations(scope, CHECK_OK); } FunctionLiteral* function_literal = - new(zone()) FunctionLiteral(isolate(), - function_name, - scope, - body, - materialized_literal_count, - expected_property_count, - only_simple_this_property_assignments, - this_property_assignments, - num_parameters, - start_pos, - end_pos, - type, - has_duplicate_parameters); + factory()->NewFunctionLiteral(function_name, + scope, + body, + materialized_literal_count, + expected_property_count, + handler_count, + only_simple_this_property_assignments, + this_property_assignments, + num_parameters, + has_duplicate_parameters, + type, + true); function_literal->set_function_token_position(function_token_position); + function_literal->set_ast_properties(&ast_properties); if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal); return function_literal; } +preparser::PreParser::PreParseResult Parser::LazyParseFunctionLiteral( + SingletonLogger* logger) { + HistogramTimerScope preparse_scope(isolate()->counters()->pre_parse()); + ASSERT_EQ(Token::LBRACE, scanner().current_token()); + + if (reusable_preparser_ == NULL) { + intptr_t stack_limit = isolate()->stack_guard()->real_climit(); + bool do_allow_lazy = true; + reusable_preparser_ = new preparser::PreParser(&scanner_, + NULL, + stack_limit, + do_allow_lazy, + allow_natives_syntax_, + allow_modules_); + } + preparser::PreParser::PreParseResult result = + reusable_preparser_->PreParseLazyFunction(top_scope_->language_mode(), + logger); + return result; +} + + Expression* Parser::ParseV8Intrinsic(bool* ok) { // CallRuntime :: // '%' Identifier Arguments @@ -3953,7 +4327,7 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) { } // We have a valid intrinsics call or a call to a builtin. - return new(zone()) CallRuntime(isolate(), name, function, args); + return factory()->NewCallRuntime(name, function, args); } @@ -4009,24 +4383,19 @@ void Parser::ExpectSemicolon(bool* ok) { Literal* Parser::GetLiteralUndefined() { - return NewLiteral(isolate()->factory()->undefined_value()); + return factory()->NewLiteral(isolate()->factory()->undefined_value()); } Literal* Parser::GetLiteralTheHole() { - return NewLiteral(isolate()->factory()->the_hole_value()); -} - - -Literal* Parser::GetLiteralNumber(double value) { - return NewNumberLiteral(value); + return factory()->NewLiteral(isolate()->factory()->the_hole_value()); } // Parses an identifier that is valid for the current scope, in particular it // fails on strict mode future reserved keywords in a strict scope. Handle<String> Parser::ParseIdentifier(bool* ok) { - if (top_scope_->is_strict_mode()) { + if (!top_scope_->is_classic_mode()) { Expect(Token::IDENTIFIER, ok); } else if (!Check(Token::IDENTIFIER)) { Expect(Token::FUTURE_STRICT_RESERVED_WORD, ok); @@ -4064,12 +4433,21 @@ Handle<String> Parser::ParseIdentifierName(bool* ok) { } +void Parser::MarkAsLValue(Expression* expression) { + VariableProxy* proxy = expression != NULL + ? expression->AsVariableProxy() + : NULL; + + if (proxy != NULL) proxy->MarkAsLValue(); +} + + // Checks LHS expression for assignment and prefix/postfix increment/decrement // in strict mode. void Parser::CheckStrictModeLValue(Expression* expression, const char* error, bool* ok) { - ASSERT(top_scope_->is_strict_mode()); + ASSERT(!top_scope_->is_classic_mode()); VariableProxy* lhs = expression != NULL ? expression->AsVariableProxy() : NULL; @@ -4188,11 +4566,6 @@ void Parser::RegisterTargetUse(Label* target, Target* stop) { } -Literal* Parser::NewNumberLiteral(double number) { - return NewLiteral(isolate()->factory()->NewNumber(number, TENURED)); -} - - Expression* Parser::NewThrowReferenceError(Handle<String> type) { return NewThrowError(isolate()->factory()->MakeReferenceError_symbol(), type, HandleVector<Object>(NULL, 0)); @@ -4232,19 +4605,15 @@ Expression* Parser::NewThrowError(Handle<String> constructor, elements->set(i, *element); } } - Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements(elements, - TENURED); + Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements( + elements, FAST_ELEMENTS, TENURED); ZoneList<Expression*>* args = new(zone()) ZoneList<Expression*>(2); - args->Add(NewLiteral(type)); - args->Add(NewLiteral(array)); - CallRuntime* call_constructor = new(zone()) CallRuntime(isolate(), - constructor, - NULL, - args); - return new(zone()) Throw(isolate(), - call_constructor, - scanner().location().beg_pos); + args->Add(factory()->NewLiteral(type)); + args->Add(factory()->NewLiteral(array)); + CallRuntime* call_constructor = + factory()->NewCallRuntime(constructor, NULL, args); + return factory()->NewThrow(call_constructor, scanner().location().beg_pos); } // ---------------------------------------------------------------------------- @@ -5122,18 +5491,20 @@ int ScriptDataImpl::ReadNumber(byte** source) { // Create a Scanner for the preparser to use as input, and preparse the source. static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, - bool allow_lazy, - ParserRecorder* recorder, - bool harmony_block_scoping) { + int flags, + ParserRecorder* recorder) { Isolate* isolate = Isolate::Current(); - JavaScriptScanner scanner(isolate->unicode_cache()); - scanner.SetHarmonyBlockScoping(harmony_block_scoping); + HistogramTimerScope timer(isolate->counters()->pre_parse()); + Scanner scanner(isolate->unicode_cache()); + scanner.SetHarmonyScoping(FLAG_harmony_scoping); scanner.Initialize(source); intptr_t stack_limit = isolate->stack_guard()->real_climit(); - if (!preparser::PreParser::PreParseProgram(&scanner, - recorder, - allow_lazy, - stack_limit)) { + preparser::PreParser::PreParseResult result = + preparser::PreParser::PreParseProgram(&scanner, + recorder, + flags, + stack_limit); + if (result == preparser::PreParser::kPreParseStackOverflow) { isolate->StackOverflow(); return NULL; } @@ -5147,27 +5518,38 @@ static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. -ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, +ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, v8::Extension* extension, - bool harmony_block_scoping) { + int flags) { bool allow_lazy = FLAG_lazy && (extension == NULL); if (!allow_lazy) { // Partial preparsing is only about lazily compiled functions. // If we don't allow lazy compilation, the log data will be empty. return NULL; } + flags |= kAllowLazy; PartialParserRecorder recorder; - return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping); + int source_length = source->length(); + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUC16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), 0, source_length); + return DoPreParse(&stream, flags, &recorder); + } else { + GenericStringUC16CharacterStream stream(source, 0, source_length); + return DoPreParse(&stream, flags, &recorder); + } } ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source, v8::Extension* extension, - bool harmony_block_scoping) { + int flags) { Handle<Script> no_script; - bool allow_lazy = FLAG_lazy && (extension == NULL); + if (FLAG_lazy && (extension == NULL)) { + flags |= kAllowLazy; + } CompleteParserRecorder recorder; - return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping); + return DoPreParse(source, flags, &recorder); } @@ -5193,29 +5575,29 @@ bool RegExpParser::ParseRegExp(FlatStringReader* input, } -bool ParserApi::Parse(CompilationInfo* info) { +bool ParserApi::Parse(CompilationInfo* info, int parsing_flags) { ASSERT(info->function() == NULL); FunctionLiteral* result = NULL; Handle<Script> script = info->script(); - bool harmony_block_scoping = !info->is_native() && - FLAG_harmony_block_scoping; + ASSERT((parsing_flags & kLanguageModeMask) == CLASSIC_MODE); + if (!info->is_native() && FLAG_harmony_scoping) { + // Harmony scoping is requested. + parsing_flags |= EXTENDED_MODE; + } + if (!info->is_native() && FLAG_harmony_modules) { + parsing_flags |= kAllowModules; + } + if (FLAG_allow_natives_syntax || info->is_native()) { + // We require %identifier(..) syntax. + parsing_flags |= kAllowNativesSyntax; + } if (info->is_lazy()) { - bool allow_natives_syntax = - FLAG_allow_natives_syntax || - info->is_native(); - Parser parser(script, allow_natives_syntax, NULL, NULL); - parser.SetHarmonyBlockScoping(harmony_block_scoping); + ASSERT(!info->is_eval()); + Parser parser(script, parsing_flags, NULL, NULL); result = parser.ParseLazy(info); } else { - // Whether we allow %identifier(..) syntax. - bool allow_natives_syntax = - info->is_native() || FLAG_allow_natives_syntax; ScriptDataImpl* pre_data = info->pre_parse_data(); - Parser parser(script, - allow_natives_syntax, - info->extension(), - pre_data); - parser.SetHarmonyBlockScoping(harmony_block_scoping); + Parser parser(script, parsing_flags, info->extension(), pre_data); if (pre_data != NULL && pre_data->has_error()) { Scanner::Location loc = pre_data->MessageLocation(); const char* message = pre_data->BuildMessage(); @@ -5228,10 +5610,7 @@ bool ParserApi::Parse(CompilationInfo* info) { DeleteArray(args.start()); ASSERT(info->isolate()->has_pending_exception()); } else { - Handle<String> source = Handle<String>(String::cast(script->source())); - result = parser.ParseProgram(source, - info->is_global(), - info->StrictMode()); + result = parser.ParseProgram(info); } } info->SetFunction(result); diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 3312f2f56a..fbc4a1529d 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -33,6 +33,7 @@ #include "preparse-data-format.h" #include "preparse-data.h" #include "scopes.h" +#include "preparser.h" namespace v8 { namespace internal { @@ -42,7 +43,6 @@ class FuncNameInferrer; class ParserLog; class PositionStack; class Target; -class LexicalScope; template <typename T> class ZoneListWrapper; @@ -67,26 +67,36 @@ class ParserMessage : public Malloced { class FunctionEntry BASE_EMBEDDED { public: - explicit FunctionEntry(Vector<unsigned> backing) : backing_(backing) { } - FunctionEntry() : backing_(Vector<unsigned>::empty()) { } + enum { + kStartPositionIndex, + kEndPositionIndex, + kLiteralCountIndex, + kPropertyCountIndex, + kLanguageModeIndex, + kSize + }; + + explicit FunctionEntry(Vector<unsigned> backing) + : backing_(backing) { } - int start_pos() { return backing_[kStartPosOffset]; } - int end_pos() { return backing_[kEndPosOffset]; } - int literal_count() { return backing_[kLiteralCountOffset]; } - int property_count() { return backing_[kPropertyCountOffset]; } - bool strict_mode() { return backing_[kStrictModeOffset] != 0; } + FunctionEntry() : backing_() { } - bool is_valid() { return backing_.length() > 0; } + int start_pos() { return backing_[kStartPositionIndex]; } + int end_pos() { return backing_[kEndPositionIndex]; } + int literal_count() { return backing_[kLiteralCountIndex]; } + int property_count() { return backing_[kPropertyCountIndex]; } + LanguageMode language_mode() { + ASSERT(backing_[kLanguageModeIndex] == CLASSIC_MODE || + backing_[kLanguageModeIndex] == STRICT_MODE || + backing_[kLanguageModeIndex] == EXTENDED_MODE); + return static_cast<LanguageMode>(backing_[kLanguageModeIndex]); + } - static const int kSize = 5; + bool is_valid() { return !backing_.is_empty(); } private: Vector<unsigned> backing_; - static const int kStartPosOffset = 0; - static const int kEndPosOffset = 1; - static const int kLiteralCountOffset = 2; - static const int kPropertyCountOffset = 3; - static const int kStrictModeOffset = 4; + bool owns_data_; }; @@ -98,7 +108,7 @@ class ScriptDataImpl : public ScriptData { // Create an empty ScriptDataImpl that is guaranteed to not satisfy // a SanityCheck. - ScriptDataImpl() : store_(Vector<unsigned>()), owns_store_(false) { } + ScriptDataImpl() : owns_store_(false) { } virtual ~ScriptDataImpl(); virtual int Length(); @@ -159,24 +169,24 @@ class ParserApi { // Parses the source code represented by the compilation info and sets its // function literal. Returns false (and deallocates any allocated AST // nodes) if parsing failed. - static bool Parse(CompilationInfo* info); + static bool Parse(CompilationInfo* info, int flags); // Generic preparser generating full preparse data. static ScriptDataImpl* PreParse(UC16CharacterStream* source, v8::Extension* extension, - bool harmony_block_scoping); + int flags); // Preparser that only does preprocessing that makes sense if only used // immediately after. - static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source, + static ScriptDataImpl* PartialPreParse(Handle<String> source, v8::Extension* extension, - bool harmony_block_scoping); + int flags); }; // ---------------------------------------------------------------------------- // REGEXP PARSING -// A BuffferedZoneList is an automatically growing list, just like (and backed +// A BufferedZoneList is an automatically growing list, just like (and backed // by) a ZoneList, that is optimized for the case of adding and removing // a single element. The last element added is stored outside the backing list, // and if no more than one element is ever added, the ZoneList isn't even @@ -415,19 +425,22 @@ class RegExpParser { // ---------------------------------------------------------------------------- // JAVASCRIPT PARSING +// Forward declaration. +class SingletonLogger; + class Parser { public: Parser(Handle<Script> script, - bool allow_natives_syntax, + int parsing_flags, // Combination of ParsingFlags v8::Extension* extension, ScriptDataImpl* pre_data); - virtual ~Parser() { } + virtual ~Parser() { + delete reusable_preparser_; + reusable_preparser_ = NULL; + } // Returns NULL if parsing failed. - FunctionLiteral* ParseProgram(Handle<String> source, - bool in_global_context, - StrictModeFlag strict_mode); - + FunctionLiteral* ParseProgram(CompilationInfo* info); FunctionLiteral* ParseLazy(CompilationInfo* info); void ReportMessageAt(Scanner::Location loc, @@ -436,7 +449,6 @@ class Parser { void ReportMessageAt(Scanner::Location loc, const char* message, Vector<Handle<String> > args); - void SetHarmonyBlockScoping(bool block_scoping); private: // Limit on number of function parameters is chosen arbitrarily. @@ -445,9 +457,7 @@ class Parser { // should be checked. static const int kMaxNumFunctionParameters = 32766; static const int kMaxNumFunctionLocals = 32767; - FunctionLiteral* ParseLazy(CompilationInfo* info, - UC16CharacterStream* source, - ZoneScope* zone_scope); + enum Mode { PARSE_LAZILY, PARSE_EAGERLY @@ -459,13 +469,87 @@ class Parser { kForStatement }; + // If a list of variable declarations includes any initializers. + enum VariableDeclarationProperties { + kHasInitializers, + kHasNoInitializers + }; + + class BlockState; + + class FunctionState BASE_EMBEDDED { + public: + FunctionState(Parser* parser, + Scope* scope, + Isolate* isolate); + ~FunctionState(); + + int NextMaterializedLiteralIndex() { + return next_materialized_literal_index_++; + } + int materialized_literal_count() { + return next_materialized_literal_index_ - JSFunction::kLiteralsPrefixSize; + } + + int NextHandlerIndex() { return next_handler_index_++; } + int handler_count() { return next_handler_index_; } + + void SetThisPropertyAssignmentInfo( + bool only_simple_this_property_assignments, + Handle<FixedArray> this_property_assignments) { + only_simple_this_property_assignments_ = + only_simple_this_property_assignments; + this_property_assignments_ = this_property_assignments; + } + bool only_simple_this_property_assignments() { + return only_simple_this_property_assignments_; + } + Handle<FixedArray> this_property_assignments() { + return this_property_assignments_; + } + + void AddProperty() { expected_property_count_++; } + int expected_property_count() { return expected_property_count_; } + + AstNodeFactory<AstConstructionVisitor>* factory() { return &factory_; } + + private: + // Used to assign an index to each literal that needs materialization in + // the function. Includes regexp literals, and boilerplate for object and + // array literals. + int next_materialized_literal_index_; + + // Used to assign a per-function index to try and catch handlers. + int next_handler_index_; + + // Properties count estimation. + int expected_property_count_; + + // Keeps track of assignments to properties of this. Used for + // optimizing constructors. + bool only_simple_this_property_assignments_; + Handle<FixedArray> this_property_assignments_; + + Parser* parser_; + FunctionState* outer_function_state_; + Scope* outer_scope_; + int saved_ast_node_id_; + AstNodeFactory<AstConstructionVisitor> factory_; + }; + + + + + FunctionLiteral* ParseLazy(CompilationInfo* info, + UC16CharacterStream* source, + ZoneScope* zone_scope); + Isolate* isolate() { return isolate_; } Zone* zone() { return isolate_->zone(); } // Called by ParseProgram after setting up the scanner. - FunctionLiteral* DoParseProgram(Handle<String> source, - bool in_global_context, - StrictModeFlag strict_mode, + FunctionLiteral* DoParseProgram(CompilationInfo* info, + Handle<String> source, ZoneScope* zone_scope); // Report syntax error @@ -473,10 +557,14 @@ class Parser { void ReportInvalidPreparseData(Handle<String> name, bool* ok); void ReportMessage(const char* message, Vector<const char*> args); - bool inside_with() const { return with_nesting_level_ > 0; } - JavaScriptScanner& scanner() { return scanner_; } + bool inside_with() const { return top_scope_->inside_with(); } + Scanner& scanner() { return scanner_; } Mode mode() const { return mode_; } ScriptDataImpl* pre_data() const { return pre_data_; } + bool is_extended_mode() { + ASSERT(top_scope_ != NULL); + return top_scope_->is_extended_mode(); + } // Check if the given string is 'eval' or 'arguments'. bool IsEvalOrArguments(Handle<String> string); @@ -492,10 +580,10 @@ class Parser { Statement* ParseFunctionDeclaration(bool* ok); Statement* ParseNativeDeclaration(bool* ok); Block* ParseBlock(ZoneStringList* labels, bool* ok); - Block* ParseScopedBlock(ZoneStringList* labels, bool* ok); Block* ParseVariableStatement(VariableDeclarationContext var_context, bool* ok); Block* ParseVariableDeclarations(VariableDeclarationContext var_context, + VariableDeclarationProperties* decl_props, Handle<String>* out, bool* ok); Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels, @@ -515,6 +603,9 @@ class Parser { TryStatement* ParseTryStatement(bool* ok); DebuggerStatement* ParseDebuggerStatement(bool* ok); + // Support for hamony block scoped bindings. + Block* ParseScopedBlock(ZoneStringList* labels, bool* ok); + Expression* ParseExpression(bool accept_IN, bool* ok); Expression* ParseAssignmentExpression(bool accept_IN, bool* ok); Expression* ParseConditionalExpression(bool accept_IN, bool* ok); @@ -533,11 +624,6 @@ class Parser { ObjectLiteral::Property* ParseObjectLiteralGetSet(bool is_getter, bool* ok); Expression* ParseRegExpLiteral(bool seen_equal, bool* ok); - Expression* NewCompareNode(Token::Value op, - Expression* x, - Expression* y, - int position); - // Populate the constant properties fixed array for a materialized object // literal. void BuildObjectLiteralConstantProperties( @@ -626,7 +712,6 @@ class Parser { // Get odd-ball literals. Literal* GetLiteralUndefined(); Literal* GetLiteralTheHole(); - Literal* GetLiteralNumber(double value); Handle<String> ParseIdentifier(bool* ok); Handle<String> ParseIdentifierOrStrictReservedWord( @@ -636,6 +721,11 @@ class Parser { bool* is_set, bool* ok); + // Determine if the expression is a variable proxy and mark it as being used + // in an assignment or with a increment/decrement operator. This is currently + // used on for the statically checking assignments to harmony const bindings. + void MarkAsLValue(Expression* expression); + // Strict mode validation of LValue expressions void CheckStrictModeLValue(Expression* expression, const char* error, @@ -656,7 +746,7 @@ class Parser { void CheckConflictingVarDeclarations(Scope* scope, bool* ok); // Parser support - VariableProxy* Declare(Handle<String> name, Variable::Mode mode, + VariableProxy* Declare(Handle<String> name, VariableMode mode, FunctionLiteral* fun, bool resolve, bool* ok); @@ -669,30 +759,12 @@ class Parser { // Factory methods. - Statement* EmptyStatement() { - static v8::internal::EmptyStatement empty; - return ∅ - } - - Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); + Scope* NewScope(Scope* parent, ScopeType type); Handle<String> LookupSymbol(int symbol_id); Handle<String> LookupCachedSymbol(int symbol_id); - Expression* NewCall(Expression* expression, - ZoneList<Expression*>* arguments, - int pos) { - return new(zone()) Call(isolate(), expression, arguments, pos); - } - - inline Literal* NewLiteral(Handle<Object> handle) { - return new(zone()) Literal(isolate(), handle); - } - - // Create a number literal. - Literal* NewNumberLiteral(double value); - // Generate AST node that throw a ReferenceError with the given type. Expression* NewThrowReferenceError(Handle<String> type); @@ -712,33 +784,39 @@ class Parser { Handle<String> type, Vector< Handle<Object> > arguments); + preparser::PreParser::PreParseResult LazyParseFunctionLiteral( + SingletonLogger* logger); + + AstNodeFactory<AstConstructionVisitor>* factory() { + return current_function_state_->factory(); + } + Isolate* isolate_; ZoneList<Handle<String> > symbol_cache_; Handle<Script> script_; - JavaScriptScanner scanner_; - + Scanner scanner_; + preparser::PreParser* reusable_preparser_; Scope* top_scope_; - int with_nesting_level_; - - LexicalScope* lexical_scope_; - Mode mode_; - + FunctionState* current_function_state_; Target* target_stack_; // for break, continue statements - bool allow_natives_syntax_; v8::Extension* extension_; - bool is_pre_parsing_; ScriptDataImpl* pre_data_; FuncNameInferrer* fni_; + + Mode mode_; + bool allow_natives_syntax_; + bool allow_lazy_; + bool allow_modules_; bool stack_overflow_; // If true, the next (and immediately following) function literal is // preceded by a parenthesis. // Heuristically that means that the function will be called immediately, // so never lazily compile it. bool parenthesized_function_; - bool harmony_block_scoping_; - friend class LexicalScope; + friend class BlockState; + friend class FunctionState; }; diff --git a/deps/v8/src/platform-cygwin.cc b/deps/v8/src/platform-cygwin.cc index a72f5da4b7..c27e3c982f 100644 --- a/deps/v8/src/platform-cygwin.cc +++ b/deps/v8/src/platform-cygwin.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -61,7 +61,7 @@ double ceiling(double x) { static Mutex* limit_mutex = NULL; -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. // Convert the current time to a 64-bit integer first, before converting it // to an unsigned. Going directly can cause an overflow and the seed to be @@ -114,7 +114,7 @@ double OS::LocalTimeOffset() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -290,7 +290,7 @@ void OS::LogSharedLibraryAddresses() { } LOG(isolate, SharedLibraryEvent(lib_name, start, end)); } else { - // Entry not describing executable data. Skip to end of line to setup + // Entry not describing executable data. Skip to end of line to set up // reading the next entry. do { c = getc(fp); @@ -722,6 +722,7 @@ class SamplerThread : public Thread { static Mutex* mutex_; static SamplerThread* instance_; + private: DISALLOW_COPY_AND_ASSIGN(SamplerThread); }; diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc index 685ec3c786..a5981b19c3 100644 --- a/deps/v8/src/platform-freebsd.cc +++ b/deps/v8/src/platform-freebsd.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -79,7 +79,7 @@ double ceiling(double x) { static Mutex* limit_mutex = NULL; -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. // Convert the current time to a 64-bit integer first, before converting it // to an unsigned. Going directly can cause an overflow and the seed to be @@ -128,7 +128,7 @@ double OS::LocalTimeOffset() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -333,44 +333,126 @@ int OS::StackWalk(Vector<OS::StackFrame> frames) { static const int kMmapFd = -1; static const int kMmapFdOffset = 0; +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } VirtualMemory::VirtualMemory(size_t size) { - address_ = mmap(NULL, size, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - kMmapFd, kMmapFdOffset); + address_ = ReserveRegion(size); size_ = size; } +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* reservation = mmap(OS::GetRandomMmapAddr(), + request_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + if (reservation == MAP_FAILED) return; + + Address base = static_cast<Address>(reservation); + Address aligned_base = RoundUp(base, alignment); + ASSERT_LE(base, aligned_base); + + // Unmap extra memory reserved before and after the desired block. + if (aligned_base != base) { + size_t prefix_size = static_cast<size_t>(aligned_base - base); + OS::Free(base, prefix_size); + request_size -= prefix_size; + } + + size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); + ASSERT_LE(aligned_size, request_size); + + if (aligned_size != request_size) { + size_t suffix_size = request_size - aligned_size; + OS::Free(aligned_base + aligned_size, suffix_size); + request_size -= suffix_size; + } + + ASSERT(aligned_size == request_size); + + address_ = static_cast<void*>(aligned_base); + size_ = aligned_size; +} + + VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == munmap(address(), size())) address_ = MAP_FAILED; + bool result = ReleaseRegion(address(), size()); + ASSERT(result); + USE(result); } } bool VirtualMemory::IsReserved() { - return address_ != MAP_FAILED; + return address_ != NULL; } -bool VirtualMemory::Commit(void* address, size_t size, bool executable) { - int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); - if (MAP_FAILED == mmap(address, size, prot, +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; +} + + +bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + return CommitRegion(address, size, is_executable); +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + return UncommitRegion(address, size); +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + void* result = mmap(OS::GetRandomMmapAddr(), + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + + if (result == MAP_FAILED) return NULL; + + return result; +} + + +bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { + int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); + if (MAP_FAILED == mmap(base, + size, + prot, MAP_PRIVATE | MAP_ANON | MAP_FIXED, - kMmapFd, kMmapFdOffset)) { + kMmapFd, + kMmapFdOffset)) { return false; } - UpdateAllocatedSpaceLimits(address, size); + UpdateAllocatedSpaceLimits(base, size); return true; } -bool VirtualMemory::Uncommit(void* address, size_t size) { - return mmap(address, size, PROT_NONE, +bool VirtualMemory::UncommitRegion(void* base, size_t size) { + return mmap(base, + size, + PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, - kMmapFd, kMmapFdOffset) != MAP_FAILED; + kMmapFd, + kMmapFdOffset) != MAP_FAILED; +} + + +bool VirtualMemory::ReleaseRegion(void* base, size_t size) { + return munmap(base, size) == 0; } @@ -382,15 +464,8 @@ class Thread::PlatformData : public Malloced { Thread::Thread(const Options& options) : data_(new PlatformData), - stack_size_(options.stack_size) { - set_name(options.name); -} - - -Thread::Thread(const char* name) - : data_(new PlatformData), - stack_size_(0) { - set_name(name); + stack_size_(options.stack_size()) { + set_name(options.name()); } @@ -635,8 +710,10 @@ class SignalSender : public Thread { FULL_INTERVAL }; + static const int kSignalSenderStackSize = 64 * KB; + explicit SignalSender(int interval) - : Thread("SignalSender"), + : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), interval_(interval) {} static void AddActiveSampler(Sampler* sampler) { @@ -758,6 +835,7 @@ class SignalSender : public Thread { static bool signal_handler_installed_; static struct sigaction old_signal_handler_; + private: DISALLOW_COPY_AND_ASSIGN(SignalSender); }; diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index b152dae9a6..14297483c3 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -78,31 +78,7 @@ double ceiling(double x) { static Mutex* limit_mutex = NULL; -static void* GetRandomMmapAddr() { - Isolate* isolate = Isolate::UncheckedCurrent(); - // Note that the current isolate isn't set up in a call path via - // CpuFeatures::Probe. We don't care about randomization in this case because - // the code page is immediately freed. - if (isolate != NULL) { -#ifdef V8_TARGET_ARCH_X64 - uint64_t rnd1 = V8::RandomPrivate(isolate); - uint64_t rnd2 = V8::RandomPrivate(isolate); - uint64_t raw_addr = (rnd1 << 32) ^ rnd2; - raw_addr &= V8_UINT64_C(0x3ffffffff000); -#else - uint32_t raw_addr = V8::RandomPrivate(isolate); - // The range 0x20000000 - 0x60000000 is relatively unpopulated across a - // variety of ASLR modes (PAE kernel, NX compat mode, etc). - raw_addr &= 0x3ffff000; - raw_addr += 0x20000000; -#endif - return reinterpret_cast<void*>(raw_addr); - } - return NULL; -} - - -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. We preserve microsecond resolution. uint64_t seed = Ticks() ^ (getpid() << 16); srandom(static_cast<unsigned int>(seed)); @@ -350,7 +326,7 @@ double OS::LocalTimeOffset() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -381,9 +357,9 @@ size_t OS::AllocateAlignment() { void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { - const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); + const size_t msize = RoundUp(requested, AllocateAlignment()); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); - void* addr = GetRandomMmapAddr(); + void* addr = OS::GetRandomMmapAddr(); void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mbase == MAP_FAILED) { LOG(i::Isolate::Current(), @@ -453,7 +429,12 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { int size = ftell(file); void* memory = - mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + mmap(OS::GetRandomMmapAddr(), + size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fileno(file), + 0); return new PosixMemoryMappedFile(file, memory, size); } @@ -468,13 +449,18 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, return NULL; } void* memory = - mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + mmap(OS::GetRandomMmapAddr(), + size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fileno(file), + 0); return new PosixMemoryMappedFile(file, memory, size); } PosixMemoryMappedFile::~PosixMemoryMappedFile() { - if (memory_) munmap(memory_, size_); + if (memory_) OS::Free(memory_, size_); fclose(file_); } @@ -526,7 +512,7 @@ void OS::LogSharedLibraryAddresses() { } LOG(isolate, SharedLibraryEvent(lib_name, start, end)); } else { - // Entry not describing executable data. Skip to end of line to setup + // Entry not describing executable data. Skip to end of line to set up // reading the next entry. do { c = getc(fp); @@ -553,10 +539,14 @@ void OS::SignalCodeMovingGC() { // kernel log. int size = sysconf(_SC_PAGESIZE); FILE* f = fopen(kGCFakeMmap, "w+"); - void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, - fileno(f), 0); + void* addr = mmap(OS::GetRandomMmapAddr(), + size, + PROT_READ | PROT_EXEC, + MAP_PRIVATE, + fileno(f), + 0); ASSERT(addr != MAP_FAILED); - munmap(addr, size); + OS::Free(addr, size); fclose(f); } @@ -598,44 +588,126 @@ int OS::StackWalk(Vector<OS::StackFrame> frames) { static const int kMmapFd = -1; static const int kMmapFdOffset = 0; +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } VirtualMemory::VirtualMemory(size_t size) { - address_ = mmap(GetRandomMmapAddr(), size, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, - kMmapFd, kMmapFdOffset); + address_ = ReserveRegion(size); size_ = size; } +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* reservation = mmap(OS::GetRandomMmapAddr(), + request_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + if (reservation == MAP_FAILED) return; + + Address base = static_cast<Address>(reservation); + Address aligned_base = RoundUp(base, alignment); + ASSERT_LE(base, aligned_base); + + // Unmap extra memory reserved before and after the desired block. + if (aligned_base != base) { + size_t prefix_size = static_cast<size_t>(aligned_base - base); + OS::Free(base, prefix_size); + request_size -= prefix_size; + } + + size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); + ASSERT_LE(aligned_size, request_size); + + if (aligned_size != request_size) { + size_t suffix_size = request_size - aligned_size; + OS::Free(aligned_base + aligned_size, suffix_size); + request_size -= suffix_size; + } + + ASSERT(aligned_size == request_size); + + address_ = static_cast<void*>(aligned_base); + size_ = aligned_size; +} + + VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == munmap(address(), size())) address_ = MAP_FAILED; + bool result = ReleaseRegion(address(), size()); + ASSERT(result); + USE(result); } } bool VirtualMemory::IsReserved() { - return address_ != MAP_FAILED; + return address_ != NULL; +} + + +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; } bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + return CommitRegion(address, size, is_executable); +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + return UncommitRegion(address, size); +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + void* result = mmap(OS::GetRandomMmapAddr(), + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + + if (result == MAP_FAILED) return NULL; + + return result; +} + + +bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); - if (MAP_FAILED == mmap(address, size, prot, + if (MAP_FAILED == mmap(base, + size, + prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, - kMmapFd, kMmapFdOffset)) { + kMmapFd, + kMmapFdOffset)) { return false; } - UpdateAllocatedSpaceLimits(address, size); + UpdateAllocatedSpaceLimits(base, size); return true; } -bool VirtualMemory::Uncommit(void* address, size_t size) { - return mmap(address, size, PROT_NONE, +bool VirtualMemory::UncommitRegion(void* base, size_t size) { + return mmap(base, + size, + PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, - kMmapFd, kMmapFdOffset) != MAP_FAILED; + kMmapFd, + kMmapFdOffset) != MAP_FAILED; +} + + +bool VirtualMemory::ReleaseRegion(void* base, size_t size) { + return munmap(base, size) == 0; } @@ -648,15 +720,8 @@ class Thread::PlatformData : public Malloced { Thread::Thread(const Options& options) : data_(new PlatformData()), - stack_size_(options.stack_size) { - set_name(options.name); -} - - -Thread::Thread(const char* name) - : data_(new PlatformData()), - stack_size_(0) { - set_name(name); + stack_size_(options.stack_size()) { + set_name(options.name()); } @@ -696,7 +761,8 @@ void Thread::Start() { pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_)); attr_ptr = &attr; } - pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this); + int result = pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this); + CHECK_EQ(0, result); ASSERT(data_->thread_ != kNoThread); } @@ -878,6 +944,38 @@ typedef struct ucontext { } ucontext_t; enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11}; +#elif !defined(__GLIBC__) && defined(__mips__) +// MIPS version of sigcontext, for Android bionic. +struct sigcontext { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + uint64_t fpregs[32]; + uint32_t acx; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +}; +typedef uint32_t __sigset_t; +typedef struct sigcontext mcontext_t; +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + __sigset_t uc_sigmask; +} ucontext_t; + #endif @@ -892,7 +990,6 @@ static int GetThreadID() { static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { -#ifndef V8_HOST_ARCH_MIPS USE(info); if (signal != SIGPROF) return; Isolate* isolate = Isolate::UncheckedCurrent(); @@ -934,15 +1031,14 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample->pc = reinterpret_cast<Address>(mcontext.arm_pc); sample->sp = reinterpret_cast<Address>(mcontext.arm_sp); sample->fp = reinterpret_cast<Address>(mcontext.arm_fp); -#endif +#endif // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) #elif V8_HOST_ARCH_MIPS - sample.pc = reinterpret_cast<Address>(mcontext.pc); - sample.sp = reinterpret_cast<Address>(mcontext.gregs[29]); - sample.fp = reinterpret_cast<Address>(mcontext.gregs[30]); -#endif + sample->pc = reinterpret_cast<Address>(mcontext.pc); + sample->sp = reinterpret_cast<Address>(mcontext.gregs[29]); + sample->fp = reinterpret_cast<Address>(mcontext.gregs[30]); +#endif // V8_HOST_ARCH_* sampler->SampleStack(sample); sampler->Tick(sample); -#endif } @@ -964,8 +1060,10 @@ class SignalSender : public Thread { FULL_INTERVAL }; + static const int kSignalSenderStackSize = 64 * KB; + explicit SignalSender(int interval) - : Thread("SignalSender"), + : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), vm_tgid_(getpid()), interval_(interval) {} @@ -1080,6 +1178,9 @@ class SignalSender : public Thread { // occuring during signal delivery. useconds_t interval = interval_ * 1000 - 100; if (full_or_half == HALF_INTERVAL) interval /= 2; +#if defined(ANDROID) + usleep(interval); +#else int result = usleep(interval); #ifdef DEBUG if (result != 0 && errno != EINTR) { @@ -1089,8 +1190,9 @@ class SignalSender : public Thread { errno); ASSERT(result == 0 || errno == EINTR); } -#endif +#endif // DEBUG USE(result); +#endif // ANDROID } const int vm_tgid_; @@ -1103,6 +1205,7 @@ class SignalSender : public Thread { static bool signal_handler_installed_; static struct sigaction old_signal_handler_; + private: DISALLOW_COPY_AND_ASSIGN(SignalSender); }; diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc index 6be941a08b..e367d21a41 100644 --- a/deps/v8/src/platform-macos.cc +++ b/deps/v8/src/platform-macos.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -75,7 +75,7 @@ extern "C" { namespace v8 { namespace internal { -// 0 is never a valid thread id on MacOSX since a ptread_t is +// 0 is never a valid thread id on MacOSX since a pthread_t is // a pointer. static const pthread_t kNoThread = (pthread_t) 0; @@ -93,13 +93,9 @@ double ceiling(double x) { static Mutex* limit_mutex = NULL; -void OS::Setup() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly will cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); +void OS::SetUp() { + // Seed the random number generator. We preserve microsecond resolution. + uint64_t seed = Ticks() ^ (getpid() << 16); srandom(static_cast<unsigned int>(seed)); limit_mutex = CreateMutex(); } @@ -107,7 +103,7 @@ void OS::Setup() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -148,9 +144,12 @@ void* OS::Allocate(const size_t requested, bool is_executable) { const size_t msize = RoundUp(requested, getpagesize()); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); - void* mbase = mmap(NULL, msize, prot, + void* mbase = mmap(OS::GetRandomMmapAddr(), + msize, + prot, MAP_PRIVATE | MAP_ANON, - kMmapFd, kMmapFdOffset); + kMmapFd, + kMmapFdOffset); if (mbase == MAP_FAILED) { LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed")); return NULL; @@ -207,7 +206,12 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { int size = ftell(file); void* memory = - mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + mmap(OS::GetRandomMmapAddr(), + size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fileno(file), + 0); return new PosixMemoryMappedFile(file, memory, size); } @@ -222,13 +226,18 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, return NULL; } void* memory = - mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + mmap(OS::GetRandomMmapAddr(), + size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fileno(file), + 0); return new PosixMemoryMappedFile(file, memory, size); } PosixMemoryMappedFile::~PosixMemoryMappedFile() { - if (memory_) munmap(memory_, size_); + if (memory_) OS::Free(memory_, size_); fclose(file_); } @@ -334,33 +343,102 @@ int OS::StackWalk(Vector<StackFrame> frames) { } +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } -VirtualMemory::VirtualMemory(size_t size) { - address_ = mmap(NULL, size, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - kMmapFd, kMmapFdOffset); - size_ = size; +VirtualMemory::VirtualMemory(size_t size) + : address_(ReserveRegion(size)), size_(size) { } + + +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* reservation = mmap(OS::GetRandomMmapAddr(), + request_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + if (reservation == MAP_FAILED) return; + + Address base = static_cast<Address>(reservation); + Address aligned_base = RoundUp(base, alignment); + ASSERT_LE(base, aligned_base); + + // Unmap extra memory reserved before and after the desired block. + if (aligned_base != base) { + size_t prefix_size = static_cast<size_t>(aligned_base - base); + OS::Free(base, prefix_size); + request_size -= prefix_size; + } + + size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); + ASSERT_LE(aligned_size, request_size); + + if (aligned_size != request_size) { + size_t suffix_size = request_size - aligned_size; + OS::Free(aligned_base + aligned_size, suffix_size); + request_size -= suffix_size; + } + + ASSERT(aligned_size == request_size); + + address_ = static_cast<void*>(aligned_base); + size_ = aligned_size; } VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == munmap(address(), size())) address_ = MAP_FAILED; + bool result = ReleaseRegion(address(), size()); + ASSERT(result); + USE(result); } } +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + void* result = mmap(OS::GetRandomMmapAddr(), + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + + if (result == MAP_FAILED) return NULL; + + return result; +} + + bool VirtualMemory::IsReserved() { - return address_ != MAP_FAILED; + return address_ != NULL; } bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + return CommitRegion(address, size, is_executable); +} + + +bool VirtualMemory::CommitRegion(void* address, + size_t size, + bool is_executable) { int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); - if (MAP_FAILED == mmap(address, size, prot, + if (MAP_FAILED == mmap(address, + size, + prot, MAP_PRIVATE | MAP_ANON | MAP_FIXED, - kMmapFd, kMmapFdOffset)) { + kMmapFd, + kMmapFdOffset)) { return false; } @@ -370,9 +448,22 @@ bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { bool VirtualMemory::Uncommit(void* address, size_t size) { - return mmap(address, size, PROT_NONE, + return UncommitRegion(address, size); +} + + +bool VirtualMemory::UncommitRegion(void* address, size_t size) { + return mmap(address, + size, + PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, - kMmapFd, kMmapFdOffset) != MAP_FAILED; + kMmapFd, + kMmapFdOffset) != MAP_FAILED; +} + + +bool VirtualMemory::ReleaseRegion(void* address, size_t size) { + return munmap(address, size) == 0; } @@ -382,17 +473,11 @@ class Thread::PlatformData : public Malloced { pthread_t thread_; // Thread handle for pthread. }; -Thread::Thread(const Options& options) - : data_(new PlatformData), - stack_size_(options.stack_size) { - set_name(options.name); -} - -Thread::Thread(const char* name) +Thread::Thread(const Options& options) : data_(new PlatformData), - stack_size_(0) { - set_name(name); + stack_size_(options.stack_size()) { + set_name(options.name()); } @@ -645,10 +730,13 @@ class Sampler::PlatformData : public Malloced { thread_act_t profiled_thread_; }; + class SamplerThread : public Thread { public: + static const int kSamplerThreadStackSize = 64 * KB; + explicit SamplerThread(int interval) - : Thread("SamplerThread"), + : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)), interval_(interval) {} static void AddActiveSampler(Sampler* sampler) { @@ -763,6 +851,7 @@ class SamplerThread : public Thread { static Mutex* mutex_; static SamplerThread* instance_; + private: DISALLOW_COPY_AND_ASSIGN(SamplerThread); }; diff --git a/deps/v8/src/platform-nullos.cc b/deps/v8/src/platform-nullos.cc index 8c2a8633d7..094f950f72 100644 --- a/deps/v8/src/platform-nullos.cc +++ b/deps/v8/src/platform-nullos.cc @@ -56,7 +56,7 @@ double modulo(double x, double y) { // Initialize OS class early in the V8 startup. -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. UNIMPLEMENTED(); } diff --git a/deps/v8/src/platform-openbsd.cc b/deps/v8/src/platform-openbsd.cc index 973329b9b1..7e27a01d45 100644 --- a/deps/v8/src/platform-openbsd.cc +++ b/deps/v8/src/platform-openbsd.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,87 +25,107 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Platform specific code for OpenBSD goes here. For the POSIX comaptible parts -// the implementation is in platform-posix.cc. +// Platform specific code for OpenBSD and NetBSD goes here. For the POSIX +// comaptible parts the implementation is in platform-posix.cc. #include <pthread.h> #include <semaphore.h> #include <signal.h> #include <sys/time.h> #include <sys/resource.h> +#include <sys/syscall.h> #include <sys/types.h> #include <stdlib.h> #include <sys/types.h> // mmap & munmap #include <sys/mman.h> // mmap & munmap #include <sys/stat.h> // open -#include <sys/fcntl.h> // open -#include <unistd.h> // getpagesize +#include <fcntl.h> // open +#include <unistd.h> // sysconf #include <execinfo.h> // backtrace, backtrace_symbols #include <strings.h> // index #include <errno.h> #include <stdarg.h> -#include <limits.h> #undef MAP_TYPE #include "v8.h" -#include "v8threads.h" #include "platform.h" +#include "v8threads.h" #include "vm-state-inl.h" namespace v8 { namespace internal { -// 0 is never a valid thread id on OpenBSD since tids and pids share a -// name space and pid 0 is used to kill the group (see man 2 kill). +// 0 is never a valid thread id on Linux and OpenBSD since tids and pids share a +// name space and pid 0 is reserved (see man 2 kill). static const pthread_t kNoThread = (pthread_t) 0; double ceiling(double x) { - // Correct as on OS X - if (-1.0 < x && x < 0.0) { - return -0.0; - } else { - return ceil(x); - } + return ceil(x); } static Mutex* limit_mutex = NULL; -void OS::Setup() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly can cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); - srandom(static_cast<unsigned int>(seed)); - limit_mutex = CreateMutex(); +static void* GetRandomMmapAddr() { + Isolate* isolate = Isolate::UncheckedCurrent(); + // Note that the current isolate isn't set up in a call path via + // CpuFeatures::Probe. We don't care about randomization in this case because + // the code page is immediately freed. + if (isolate != NULL) { +#ifdef V8_TARGET_ARCH_X64 + uint64_t rnd1 = V8::RandomPrivate(isolate); + uint64_t rnd2 = V8::RandomPrivate(isolate); + uint64_t raw_addr = (rnd1 << 32) ^ rnd2; + // Currently available CPUs have 48 bits of virtual addressing. Truncate + // the hint address to 46 bits to give the kernel a fighting chance of + // fulfilling our placement request. + raw_addr &= V8_UINT64_C(0x3ffffffff000); +#else + uint32_t raw_addr = V8::RandomPrivate(isolate); + // The range 0x20000000 - 0x60000000 is relatively unpopulated across a + // variety of ASLR modes (PAE kernel, NX compat mode, etc). + raw_addr &= 0x3ffff000; + raw_addr += 0x20000000; +#endif + return reinterpret_cast<void*>(raw_addr); + } + return NULL; } -void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { - __asm__ __volatile__("" : : : "memory"); - *ptr = value; +void OS::SetUp() { + // Seed the random number generator. We preserve microsecond resolution. + uint64_t seed = Ticks() ^ (getpid() << 16); + srandom(static_cast<unsigned int>(seed)); + limit_mutex = CreateMutex(); } uint64_t OS::CpuFeaturesImpliedByPlatform() { - return 0; // OpenBSD runs on anything. + return 0; } int OS::ActivationFrameAlignment() { - // 16 byte alignment on OpenBSD + // With gcc 4.4 the tree vectorization optimizer can generate code + // that requires 16 byte alignment such as movdqa on x86. return 16; } +void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { + __asm__ __volatile__("" : : : "memory"); + // An x86 store acts as a release barrier. + *ptr = value; +} + + const char* OS::LocalTimezone(double time) { if (isnan(time)) return ""; time_t tv = static_cast<time_t>(floor(time/msPerSecond)); @@ -126,7 +146,7 @@ double OS::LocalTimeOffset() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -150,19 +170,20 @@ bool OS::IsOutsideAllocatedSpace(void* address) { size_t OS::AllocateAlignment() { - return getpagesize(); + return sysconf(_SC_PAGESIZE); } void* OS::Allocate(const size_t requested, size_t* allocated, - bool executable) { - const size_t msize = RoundUp(requested, getpagesize()); - int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); - void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); - + bool is_executable) { + const size_t msize = RoundUp(requested, AllocateAlignment()); + int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); + void* addr = GetRandomMmapAddr(); + void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); if (mbase == MAP_FAILED) { - LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed")); + LOG(i::Isolate::Current(), + StringEvent("OS::Allocate", "mmap failed")); return NULL; } *allocated = msize; @@ -171,9 +192,9 @@ void* OS::Allocate(const size_t requested, } -void OS::Free(void* buf, const size_t length) { +void OS::Free(void* address, const size_t size) { // TODO(1240712): munmap has a return value which is ignored here. - int result = munmap(buf, length); + int result = munmap(address, size); USE(result); ASSERT(result == 0); } @@ -192,13 +213,7 @@ void OS::Abort() { void OS::DebugBreak() { -#if (defined(__arm__) || defined(__thumb__)) -# if defined(CAN_USE_ARMV5_INSTRUCTIONS) - asm("bkpt 0"); -# endif -#else asm("int $3"); -#endif } @@ -245,61 +260,95 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, PosixMemoryMappedFile::~PosixMemoryMappedFile() { - if (memory_) munmap(memory_, size_); + if (memory_) OS::Free(memory_, size_); fclose(file_); } -static unsigned StringToLong(char* buffer) { - return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT -} - - void OS::LogSharedLibraryAddresses() { - static const int MAP_LENGTH = 1024; - int fd = open("/proc/self/maps", O_RDONLY); - if (fd < 0) return; + // This function assumes that the layout of the file is as follows: + // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] + // If we encounter an unexpected situation we abort scanning further entries. + FILE* fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) return; + + // Allocate enough room to be able to store a full file name. + const int kLibNameLen = FILENAME_MAX + 1; + char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); + + i::Isolate* isolate = ISOLATE; + // This loop will terminate once the scanning hits an EOF. while (true) { - char addr_buffer[11]; - addr_buffer[0] = '0'; - addr_buffer[1] = 'x'; - addr_buffer[10] = 0; - int result = read(fd, addr_buffer + 2, 8); - if (result < 8) break; - unsigned start = StringToLong(addr_buffer); - result = read(fd, addr_buffer + 2, 1); - if (result < 1) break; - if (addr_buffer[2] != '-') break; - result = read(fd, addr_buffer + 2, 8); - if (result < 8) break; - unsigned end = StringToLong(addr_buffer); - char buffer[MAP_LENGTH]; - int bytes_read = -1; - do { - bytes_read++; - if (bytes_read >= MAP_LENGTH - 1) - break; - result = read(fd, buffer + bytes_read, 1); - if (result < 1) break; - } while (buffer[bytes_read] != '\n'); - buffer[bytes_read] = 0; - // Ignore mappings that are not executable. - if (buffer[3] != 'x') continue; - char* start_of_path = index(buffer, '/'); - // There may be no filename in this line. Skip to next. - if (start_of_path == NULL) continue; - buffer[bytes_read] = 0; - LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end)); + uintptr_t start, end; + char attr_r, attr_w, attr_x, attr_p; + // Parse the addresses and permission bits at the beginning of the line. + if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; + if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; + + int c; + if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { + // Found a read-only executable entry. Skip characters until we reach + // the beginning of the filename or the end of the line. + do { + c = getc(fp); + } while ((c != EOF) && (c != '\n') && (c != '/')); + if (c == EOF) break; // EOF: Was unexpected, just exit. + + // Process the filename if found. + if (c == '/') { + ungetc(c, fp); // Push the '/' back into the stream to be read below. + + // Read to the end of the line. Exit if the read fails. + if (fgets(lib_name, kLibNameLen, fp) == NULL) break; + + // Drop the newline character read by fgets. We do not need to check + // for a zero-length string because we know that we at least read the + // '/' character. + lib_name[strlen(lib_name) - 1] = '\0'; + } else { + // No library name found, just record the raw address range. + snprintf(lib_name, kLibNameLen, + "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); + } + LOG(isolate, SharedLibraryEvent(lib_name, start, end)); + } else { + // Entry not describing executable data. Skip to end of line to set up + // reading the next entry. + do { + c = getc(fp); + } while ((c != EOF) && (c != '\n')); + if (c == EOF) break; + } } - close(fd); + free(lib_name); + fclose(fp); } +static const char kGCFakeMmap[] = "/tmp/__v8_gc__"; + + void OS::SignalCodeMovingGC() { + // Support for ll_prof.py. + // + // The Linux profiler built into the kernel logs all mmap's with + // PROT_EXEC so that analysis tools can properly attribute ticks. We + // do a mmap with a name known by ll_prof.py and immediately munmap + // it. This injects a GC marker into the stream of events generated + // by the kernel and allows us to synchronize V8 code log and the + // kernel log. + int size = sysconf(_SC_PAGESIZE); + FILE* f = fopen(kGCFakeMmap, "w+"); + void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, + fileno(f), 0); + ASSERT(addr != MAP_FAILED); + OS::Free(addr, size); + fclose(f); } int OS::StackWalk(Vector<OS::StackFrame> frames) { + // backtrace is a glibc extension. int frames_size = frames.length(); ScopedVector<void*> addresses(frames_size); @@ -331,64 +380,140 @@ int OS::StackWalk(Vector<OS::StackFrame> frames) { static const int kMmapFd = -1; static const int kMmapFdOffset = 0; +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } VirtualMemory::VirtualMemory(size_t size) { - address_ = mmap(NULL, size, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - kMmapFd, kMmapFdOffset); + address_ = ReserveRegion(size); size_ = size; } +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* reservation = mmap(GetRandomMmapAddr(), + request_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + if (reservation == MAP_FAILED) return; + + Address base = static_cast<Address>(reservation); + Address aligned_base = RoundUp(base, alignment); + ASSERT_LE(base, aligned_base); + + // Unmap extra memory reserved before and after the desired block. + if (aligned_base != base) { + size_t prefix_size = static_cast<size_t>(aligned_base - base); + OS::Free(base, prefix_size); + request_size -= prefix_size; + } + + size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); + ASSERT_LE(aligned_size, request_size); + + if (aligned_size != request_size) { + size_t suffix_size = request_size - aligned_size; + OS::Free(aligned_base + aligned_size, suffix_size); + request_size -= suffix_size; + } + + ASSERT(aligned_size == request_size); + + address_ = static_cast<void*>(aligned_base); + size_ = aligned_size; +} + + VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == munmap(address(), size())) address_ = MAP_FAILED; + bool result = ReleaseRegion(address(), size()); + ASSERT(result); + USE(result); } } bool VirtualMemory::IsReserved() { - return address_ != MAP_FAILED; + return address_ != NULL; +} + + +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; +} + + +bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + return CommitRegion(address, size, is_executable); } -bool VirtualMemory::Commit(void* address, size_t size, bool executable) { - int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); - if (MAP_FAILED == mmap(address, size, prot, +bool VirtualMemory::Uncommit(void* address, size_t size) { + return UncommitRegion(address, size); +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + void* result = mmap(GetRandomMmapAddr(), + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + + if (result == MAP_FAILED) return NULL; + + return result; +} + + +bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { + int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); + if (MAP_FAILED == mmap(base, + size, + prot, MAP_PRIVATE | MAP_ANON | MAP_FIXED, - kMmapFd, kMmapFdOffset)) { + kMmapFd, + kMmapFdOffset)) { return false; } - UpdateAllocatedSpaceLimits(address, size); + UpdateAllocatedSpaceLimits(base, size); return true; } -bool VirtualMemory::Uncommit(void* address, size_t size) { - return mmap(address, size, PROT_NONE, +bool VirtualMemory::UncommitRegion(void* base, size_t size) { + return mmap(base, + size, + PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, - kMmapFd, kMmapFdOffset) != MAP_FAILED; + kMmapFd, + kMmapFdOffset) != MAP_FAILED; +} + + +bool VirtualMemory::ReleaseRegion(void* base, size_t size) { + return munmap(base, size) == 0; } class Thread::PlatformData : public Malloced { public: + PlatformData() : thread_(kNoThread) {} + pthread_t thread_; // Thread handle for pthread. }; - Thread::Thread(const Options& options) - : data_(new PlatformData), - stack_size_(options.stack_size) { - set_name(options.name); -} - - -Thread::Thread(const char* name) - : data_(new PlatformData), - stack_size_(0) { - set_name(name); + : data_(new PlatformData()), + stack_size_(options.stack_size()) { + set_name(options.name()); } @@ -402,6 +527,11 @@ static void* ThreadEntry(void* arg) { // This is also initialized by the first argument to pthread_create() but we // don't know which thread will run first (the original thread or the new // one) so we initialize it here too. +#ifdef PR_SET_NAME + prctl(PR_SET_NAME, + reinterpret_cast<unsigned long>(thread->name()), // NOLINT + 0, 0, 0); +#endif thread->data()->thread_ = pthread_self(); ASSERT(thread->data()->thread_ != kNoThread); thread->Run(); @@ -477,6 +607,7 @@ class OpenBSDMutex : public Mutex { ASSERT(result == 0); result = pthread_mutex_init(&mutex_, &attrs); ASSERT(result == 0); + USE(result); } virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); } @@ -533,6 +664,14 @@ void OpenBSDSemaphore::Wait() { } +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} while (false) +#endif + + bool OpenBSDSemaphore::Wait(int timeout) { const long kOneSecondMicros = 1000000; // NOLINT @@ -566,29 +705,15 @@ bool OpenBSDSemaphore::Wait(int timeout) { } } - Semaphore* OS::CreateSemaphore(int count) { return new OpenBSDSemaphore(count); } static pthread_t GetThreadID() { - pthread_t thread_id = pthread_self(); - return thread_id; + return pthread_self(); } - -class Sampler::PlatformData : public Malloced { - public: - PlatformData() : vm_tid_(GetThreadID()) {} - - pthread_t vm_tid() const { return vm_tid_; } - - private: - pthread_t vm_tid_; -}; - - static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { USE(info); if (signal != SIGPROF) return; @@ -610,8 +735,20 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { if (sample == NULL) sample = &sample_obj; // Extracting the sample from the context is extremely machine dependent. - ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); sample->state = isolate->current_vm_state(); + ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); +#ifdef __NetBSD__ + mcontext_t& mcontext = ucontext->uc_mcontext; +#if V8_HOST_ARCH_IA32 + sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]); + sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]); + sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]); +#elif V8_HOST_ARCH_X64 + sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]); + sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]); + sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]); +#endif // V8_HOST_ARCH +#else // OpenBSD #if V8_HOST_ARCH_IA32 sample->pc = reinterpret_cast<Address>(ucontext->sc_eip); sample->sp = reinterpret_cast<Address>(ucontext->sc_esp); @@ -620,16 +757,24 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample->pc = reinterpret_cast<Address>(ucontext->sc_rip); sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp); sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp); -#elif V8_HOST_ARCH_ARM - sample->pc = reinterpret_cast<Address>(ucontext->sc_r15); - sample->sp = reinterpret_cast<Address>(ucontext->sc_r13); - sample->fp = reinterpret_cast<Address>(ucontext->sc_r11); -#endif +#endif // V8_HOST_ARCH +#endif // __NetBSD__ sampler->SampleStack(sample); sampler->Tick(sample); } +class Sampler::PlatformData : public Malloced { + public: + PlatformData() : vm_tid_(GetThreadID()) {} + + pthread_t vm_tid() const { return vm_tid_; } + + private: + pthread_t vm_tid_; +}; + + class SignalSender : public Thread { public: enum SleepInterval { @@ -637,23 +782,35 @@ class SignalSender : public Thread { FULL_INTERVAL }; + static const int kSignalSenderStackSize = 64 * KB; + explicit SignalSender(int interval) - : Thread("SignalSender"), + : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), + vm_tgid_(getpid()), interval_(interval) {} + static void InstallSignalHandler() { + struct sigaction sa; + sa.sa_sigaction = ProfilerSignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + signal_handler_installed_ = + (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); + } + + static void RestoreSignalHandler() { + if (signal_handler_installed_) { + sigaction(SIGPROF, &old_signal_handler_, 0); + signal_handler_installed_ = false; + } + } + static void AddActiveSampler(Sampler* sampler) { ScopedLock lock(mutex_); SamplerRegistry::AddActiveSampler(sampler); if (instance_ == NULL) { - // Install a signal handler. - struct sigaction sa; - sa.sa_sigaction = ProfilerSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_SIGINFO; - signal_handler_installed_ = - (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); - - // Start a thread that sends SIGPROF signal to VM threads. + // Start a thread that will send SIGPROF signal to VM threads, + // when CPU profiling will be enabled. instance_ = new SignalSender(sampler->interval()); instance_->Start(); } else { @@ -668,12 +825,7 @@ class SignalSender : public Thread { RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_); delete instance_; instance_ = NULL; - - // Restore the old signal handler. - if (signal_handler_installed_) { - sigaction(SIGPROF, &old_signal_handler_, 0); - signal_handler_installed_ = false; - } + RestoreSignalHandler(); } } @@ -685,6 +837,11 @@ class SignalSender : public Thread { bool cpu_profiling_enabled = (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); + if (cpu_profiling_enabled && !signal_handler_installed_) { + InstallSignalHandler(); + } else if (!cpu_profiling_enabled && signal_handler_installed_) { + RestoreSignalHandler(); + } // When CPU profiling is enabled both JavaScript and C++ code is // profiled. We must not suspend. if (!cpu_profiling_enabled) { @@ -751,6 +908,7 @@ class SignalSender : public Thread { USE(result); } + const int vm_tgid_; const int interval_; RuntimeProfilerRateLimiter rate_limiter_; @@ -760,9 +918,11 @@ class SignalSender : public Thread { static bool signal_handler_installed_; static struct sigaction old_signal_handler_; + private: DISALLOW_COPY_AND_ASSIGN(SignalSender); }; + Mutex* SignalSender::mutex_ = OS::CreateMutex(); SignalSender* SignalSender::instance_ = NULL; struct sigaction SignalSender::old_signal_handler_; diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index 52cf02963a..34fd5c4498 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -46,9 +46,9 @@ #undef MAP_TYPE -#if defined(ANDROID) +#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT) #define LOG_TAG "v8" -#include <utils/Log.h> // LOG_PRI_VA +#include <android/log.h> #endif #include "v8.h" @@ -70,6 +70,12 @@ intptr_t OS::MaxVirtualMemory() { } +intptr_t OS::CommitPageSize() { + static intptr_t page_size = getpagesize(); + return page_size; +} + + #ifndef __CYGWIN__ // Get rid of writable permission on code allocations. void OS::ProtectCode(void* address, const size_t size) { @@ -84,6 +90,34 @@ void OS::Guard(void* address, const size_t size) { #endif // __CYGWIN__ +void* OS::GetRandomMmapAddr() { + Isolate* isolate = Isolate::UncheckedCurrent(); + // Note that the current isolate isn't set up in a call path via + // CpuFeatures::Probe. We don't care about randomization in this case because + // the code page is immediately freed. + if (isolate != NULL) { +#ifdef V8_TARGET_ARCH_X64 + uint64_t rnd1 = V8::RandomPrivate(isolate); + uint64_t rnd2 = V8::RandomPrivate(isolate); + uint64_t raw_addr = (rnd1 << 32) ^ rnd2; + // Currently available CPUs have 48 bits of virtual addressing. Truncate + // the hint address to 46 bits to give the kernel a fighting chance of + // fulfilling our placement request. + raw_addr &= V8_UINT64_C(0x3ffffffff000); +#else + uint32_t raw_addr = V8::RandomPrivate(isolate); + // The range 0x20000000 - 0x60000000 is relatively unpopulated across a + // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos + // 10.6 and 10.7. + raw_addr &= 0x3ffff000; + raw_addr += 0x20000000; +#endif + return reinterpret_cast<void*>(raw_addr); + } + return NULL; +} + + // ---------------------------------------------------------------------------- // Math functions @@ -182,7 +216,7 @@ void OS::Print(const char* format, ...) { void OS::VPrint(const char* format, va_list args) { #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT) - LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, format, args); + __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args); #else vprintf(format, args); #endif @@ -199,7 +233,7 @@ void OS::FPrint(FILE* out, const char* format, ...) { void OS::VFPrint(FILE* out, const char* format, va_list args) { #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT) - LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, format, args); + __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args); #else vfprintf(out, format, args); #endif @@ -216,7 +250,7 @@ void OS::PrintError(const char* format, ...) { void OS::VPrintError(const char* format, va_list args) { #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT) - LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, format, args); + __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args); #else vfprintf(stderr, format, args); #endif @@ -427,7 +461,7 @@ bool POSIXSocket::SetReuseAddress(bool reuse_address) { } -bool Socket::Setup() { +bool Socket::SetUp() { // Nothing to do on POSIX. return true; } diff --git a/deps/v8/src/platform-solaris.cc b/deps/v8/src/platform-solaris.cc index 035d394453..349da01edc 100644 --- a/deps/v8/src/platform-solaris.cc +++ b/deps/v8/src/platform-solaris.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -53,6 +53,7 @@ #include "v8.h" #include "platform.h" +#include "v8threads.h" #include "vm-state-inl.h" @@ -89,7 +90,7 @@ double ceiling(double x) { static Mutex* limit_mutex = NULL; -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. // Convert the current time to a 64-bit integer first, before converting it // to an unsigned. Going directly will cause an overflow and the seed to be @@ -139,7 +140,7 @@ double OS::LocalTimeOffset() { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -322,43 +323,126 @@ static const int kMmapFd = -1; static const int kMmapFdOffset = 0; +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } + VirtualMemory::VirtualMemory(size_t size) { - address_ = mmap(NULL, size, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - kMmapFd, kMmapFdOffset); + address_ = ReserveRegion(size); size_ = size; } +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* reservation = mmap(OS::GetRandomMmapAddr(), + request_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + if (reservation == MAP_FAILED) return; + + Address base = static_cast<Address>(reservation); + Address aligned_base = RoundUp(base, alignment); + ASSERT_LE(base, aligned_base); + + // Unmap extra memory reserved before and after the desired block. + if (aligned_base != base) { + size_t prefix_size = static_cast<size_t>(aligned_base - base); + OS::Free(base, prefix_size); + request_size -= prefix_size; + } + + size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); + ASSERT_LE(aligned_size, request_size); + + if (aligned_size != request_size) { + size_t suffix_size = request_size - aligned_size; + OS::Free(aligned_base + aligned_size, suffix_size); + request_size -= suffix_size; + } + + ASSERT(aligned_size == request_size); + + address_ = static_cast<void*>(aligned_base); + size_ = aligned_size; +} + + VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == munmap(address(), size())) address_ = MAP_FAILED; + bool result = ReleaseRegion(address(), size()); + ASSERT(result); + USE(result); } } bool VirtualMemory::IsReserved() { - return address_ != MAP_FAILED; + return address_ != NULL; +} + + +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; +} + + +bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + return CommitRegion(address, size, is_executable); +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + return UncommitRegion(address, size); +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + void* result = mmap(OS::GetRandomMmapAddr(), + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, + kMmapFd, + kMmapFdOffset); + + if (result == MAP_FAILED) return NULL; + + return result; } -bool VirtualMemory::Commit(void* address, size_t size, bool executable) { - int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); - if (MAP_FAILED == mmap(address, size, prot, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - kMmapFd, kMmapFdOffset)) { +bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { + int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); + if (MAP_FAILED == mmap(base, + size, + prot, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + kMmapFd, + kMmapFdOffset)) { return false; } - UpdateAllocatedSpaceLimits(address, size); + UpdateAllocatedSpaceLimits(base, size); return true; } -bool VirtualMemory::Uncommit(void* address, size_t size) { - return mmap(address, size, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, - kMmapFd, kMmapFdOffset) != MAP_FAILED; +bool VirtualMemory::UncommitRegion(void* base, size_t size) { + return mmap(base, + size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, + kMmapFd, + kMmapFdOffset) != MAP_FAILED; +} + + +bool VirtualMemory::ReleaseRegion(void* base, size_t size) { + return munmap(base, size) == 0; } @@ -369,17 +453,11 @@ class Thread::PlatformData : public Malloced { pthread_t thread_; // Thread handle for pthread. }; -Thread::Thread(const Options& options) - : data_(new PlatformData()), - stack_size_(options.stack_size) { - set_name(options.name); -} - -Thread::Thread(const char* name) +Thread::Thread(const Options& options) : data_(new PlatformData()), - stack_size_(0) { - set_name(name); + stack_size_(options.stack_size()) { + set_name(options.name()); } @@ -626,8 +704,10 @@ class SignalSender : public Thread { FULL_INTERVAL }; + static const int kSignalSenderStackSize = 64 * KB; + explicit SignalSender(int interval) - : Thread("SignalSender"), + : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), interval_(interval) {} static void InstallSignalHandler() { @@ -759,6 +839,7 @@ class SignalSender : public Thread { static bool signal_handler_installed_; static struct sigaction old_signal_handler_; + private: DISALLOW_COPY_AND_ASSIGN(SignalSender); }; diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc index 97788e2f66..f16b5e74fd 100644 --- a/deps/v8/src/platform-win32.cc +++ b/deps/v8/src/platform-win32.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -198,7 +198,7 @@ double modulo(double x, double y) { // ---------------------------------------------------------------------------- // The Time class represents time on win32. A timestamp is represented as -// a 64-bit integer in 100 nano-seconds since January 1, 1601 (UTC). JavaScript +// a 64-bit integer in 100 nanoseconds since January 1, 1601 (UTC). JavaScript // timestamps are represented as a doubles in milliseconds since 00:00:00 UTC, // January 1, 1970. @@ -528,7 +528,7 @@ char* Time::LocalTimezone() { } -void OS::Setup() { +void OS::SetUp() { // Seed the random number generator. // Convert the current time to a 64-bit integer first, before converting it // to an unsigned. Going directly can cause an overflow and the seed to be @@ -776,7 +776,7 @@ void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) { // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions -// and verification). The estimate is conservative, ie, not all addresses in +// and verification). The estimate is conservative, i.e., not all addresses in // 'allocated' space are actually allocated to our heap. The range is // [lowest, highest), inclusive on the low and and exclusive on the high end. static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); @@ -889,6 +889,11 @@ void OS::Free(void* address, const size_t size) { } +intptr_t OS::CommitPageSize() { + return 4096; +} + + void OS::ProtectCode(void* address, const size_t size) { DWORD old_protect; VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect); @@ -1397,41 +1402,101 @@ void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { } -bool VirtualMemory::IsReserved() { - return address_ != NULL; -} +VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } -VirtualMemory::VirtualMemory(size_t size) { - address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); - size_ = size; +VirtualMemory::VirtualMemory(size_t size) + : address_(ReserveRegion(size)), size_(size) { } + + +VirtualMemory::VirtualMemory(size_t size, size_t alignment) + : address_(NULL), size_(0) { + ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); + size_t request_size = RoundUp(size + alignment, + static_cast<intptr_t>(OS::AllocateAlignment())); + void* address = ReserveRegion(request_size); + if (address == NULL) return; + Address base = RoundUp(static_cast<Address>(address), alignment); + // Try reducing the size by freeing and then reallocating a specific area. + bool result = ReleaseRegion(address, request_size); + USE(result); + ASSERT(result); + address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS); + if (address != NULL) { + request_size = size; + ASSERT(base == static_cast<Address>(address)); + } else { + // Resizing failed, just go with a bigger area. + address = ReserveRegion(request_size); + if (address == NULL) return; + } + address_ = address; + size_ = request_size; } VirtualMemory::~VirtualMemory() { if (IsReserved()) { - if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL; + bool result = ReleaseRegion(address_, size_); + ASSERT(result); + USE(result); } } +bool VirtualMemory::IsReserved() { + return address_ != NULL; +} + + +void VirtualMemory::Reset() { + address_ = NULL; + size_ = 0; +} + + bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + if (CommitRegion(address, size, is_executable)) { + UpdateAllocatedSpaceLimits(address, static_cast<int>(size)); + return true; + } + return false; +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + ASSERT(IsReserved()); + return UncommitRegion(address, size); +} + + +void* VirtualMemory::ReserveRegion(size_t size) { + return VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); +} + + +bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) { + if (NULL == VirtualAlloc(base, size, MEM_COMMIT, prot)) { return false; } - UpdateAllocatedSpaceLimits(address, static_cast<int>(size)); + UpdateAllocatedSpaceLimits(base, static_cast<int>(size)); return true; } -bool VirtualMemory::Uncommit(void* address, size_t size) { - ASSERT(IsReserved()); - return VirtualFree(address, size, MEM_DECOMMIT) != false; +bool VirtualMemory::UncommitRegion(void* base, size_t size) { + return VirtualFree(base, size, MEM_DECOMMIT) != 0; } +bool VirtualMemory::ReleaseRegion(void* base, size_t size) { + return VirtualFree(base, 0, MEM_RELEASE) != 0; +} + + + // ---------------------------------------------------------------------------- // Win32 thread support. @@ -1453,6 +1518,7 @@ class Thread::PlatformData : public Malloced { public: explicit PlatformData(HANDLE thread) : thread_(thread) {} HANDLE thread_; + unsigned thread_id_; }; @@ -1460,16 +1526,9 @@ class Thread::PlatformData : public Malloced { // handle until it is started. Thread::Thread(const Options& options) - : stack_size_(options.stack_size) { + : stack_size_(options.stack_size()) { data_ = new PlatformData(kNoThread); - set_name(options.name); -} - - -Thread::Thread(const char* name) - : stack_size_(0) { - data_ = new PlatformData(kNoThread); - set_name(name); + set_name(options.name()); } @@ -1496,13 +1555,15 @@ void Thread::Start() { ThreadEntry, this, 0, - NULL)); + &data_->thread_id_)); } // Wait for thread to terminate. void Thread::Join() { - WaitForSingleObject(data_->thread_, INFINITE); + if (data_->thread_id_ != GetCurrentThreadId()) { + WaitForSingleObject(data_->thread_, INFINITE); + } } @@ -1757,7 +1818,7 @@ bool Win32Socket::SetReuseAddress(bool reuse_address) { } -bool Socket::Setup() { +bool Socket::SetUp() { // Initialize Winsock32 int err; WSADATA winsock_data; @@ -1833,8 +1894,10 @@ class Sampler::PlatformData : public Malloced { class SamplerThread : public Thread { public: + static const int kSamplerThreadStackSize = 64 * KB; + explicit SamplerThread(int interval) - : Thread("SamplerThread"), + : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)), interval_(interval) {} static void AddActiveSampler(Sampler* sampler) { @@ -1938,6 +2001,7 @@ class SamplerThread : public Thread { static Mutex* mutex_; static SamplerThread* instance_; + private: DISALLOW_COPY_AND_ASSIGN(SamplerThread); }; diff --git a/deps/v8/src/platform.h b/deps/v8/src/platform.h index 034fe3404d..a0186d580f 100644 --- a/deps/v8/src/platform.h +++ b/deps/v8/src/platform.h @@ -109,7 +109,7 @@ class Socket; class OS { public: // Initializes the platform OS support. Called once at VM startup. - static void Setup(); + static void SetUp(); // Returns the accumulated user time for thread. This routine // can be used for profiling. The implementation should @@ -172,12 +172,19 @@ class OS { bool is_executable); static void Free(void* address, const size_t size); + // This is the granularity at which the ProtectCode(...) call can set page + // permissions. + static intptr_t CommitPageSize(); + // Mark code segments non-writable. static void ProtectCode(void* address, const size_t size); // Assign memory as a guard page so that access will cause an exception. static void Guard(void* address, const size_t size); + // Generate a random address to be used for hinting mmap(). + static void* GetRandomMmapAddr(); + // Get the Alignment guaranteed by Allocate(). static size_t AllocateAlignment(); @@ -301,23 +308,46 @@ class OS { DISALLOW_IMPLICIT_CONSTRUCTORS(OS); }; - +// Represents and controls an area of reserved memory. +// Control of the reserved memory can be assigned to another VirtualMemory +// object by assignment or copy-contructing. This removes the reserved memory +// from the original object. class VirtualMemory { public: + // Empty VirtualMemory object, controlling no reserved memory. + VirtualMemory(); + // Reserves virtual memory with size. explicit VirtualMemory(size_t size); + + // Reserves virtual memory containing an area of the given size that + // is aligned per alignment. This may not be at the position returned + // by address(). + VirtualMemory(size_t size, size_t alignment); + + // Releases the reserved memory, if any, controlled by this VirtualMemory + // object. ~VirtualMemory(); // Returns whether the memory has been reserved. bool IsReserved(); + // Initialize or resets an embedded VirtualMemory object. + void Reset(); + // Returns the start address of the reserved memory. + // If the memory was reserved with an alignment, this address is not + // necessarily aligned. The user might need to round it up to a multiple of + // the alignment to get the start of the aligned block. void* address() { ASSERT(IsReserved()); return address_; } - // Returns the size of the reserved memory. + // Returns the size of the reserved memory. The returned value is only + // meaningful when IsReserved() returns true. + // If the memory was reserved with an alignment, this size may be larger + // than the requested size. size_t size() { return size_; } // Commits real memory. Returns whether the operation succeeded. @@ -326,11 +356,43 @@ class VirtualMemory { // Uncommit real memory. Returns whether the operation succeeded. bool Uncommit(void* address, size_t size); + void Release() { + ASSERT(IsReserved()); + // Notice: Order is important here. The VirtualMemory object might live + // inside the allocated region. + void* address = address_; + size_t size = size_; + Reset(); + bool result = ReleaseRegion(address, size); + USE(result); + ASSERT(result); + } + + // Assign control of the reserved region to a different VirtualMemory object. + // The old object is no longer functional (IsReserved() returns false). + void TakeControl(VirtualMemory* from) { + ASSERT(!IsReserved()); + address_ = from->address_; + size_ = from->size_; + from->Reset(); + } + + static void* ReserveRegion(size_t size); + + static bool CommitRegion(void* base, size_t size, bool is_executable); + + static bool UncommitRegion(void* base, size_t size); + + // Must be called with a base pointer that has been returned by ReserveRegion + // and the same size it was reserved with. + static bool ReleaseRegion(void* base, size_t size); + private: void* address_; // Start address of the virtual memory. size_t size_; // Size of the virtual memory. }; + // ---------------------------------------------------------------------------- // Thread // @@ -350,16 +412,22 @@ class Thread { LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt }; - struct Options { - Options() : name("v8:<unknown>"), stack_size(0) {} + class Options { + public: + Options() : name_("v8:<unknown>"), stack_size_(0) {} + Options(const char* name, int stack_size = 0) + : name_(name), stack_size_(stack_size) {} + + const char* name() const { return name_; } + int stack_size() const { return stack_size_; } - const char* name; - int stack_size; + private: + const char* name_; + int stack_size_; }; // Create new thread. explicit Thread(const Options& options); - explicit Thread(const char* name); virtual ~Thread(); // Start new thread by calling the Run() method in the new thread. @@ -415,7 +483,7 @@ class Thread { PlatformData* data() { return data_; } private: - void set_name(const char *name); + void set_name(const char* name); PlatformData* data_; @@ -491,7 +559,7 @@ class Semaphore { virtual void Wait() = 0; // Suspends the calling thread until the counter is non zero or the timeout - // time has passsed. If timeout happens the return value is false and the + // time has passed. If timeout happens the return value is false and the // counter is unchanged. Otherwise the semaphore counter is decremented and // true is returned. The timeout value is specified in microseconds. virtual bool Wait(int timeout) = 0; @@ -531,7 +599,7 @@ class Socket { virtual bool IsValid() const = 0; - static bool Setup(); + static bool SetUp(); static int LastError(); static uint16_t HToN(uint16_t value); static uint16_t NToH(uint16_t value); diff --git a/deps/v8/src/preparse-data.h b/deps/v8/src/preparse-data.h index c6503c4fc1..c77a47a10c 100644 --- a/deps/v8/src/preparse-data.h +++ b/deps/v8/src/preparse-data.h @@ -49,7 +49,7 @@ class ParserRecorder { int end, int literals, int properties, - int strict_mode) = 0; + LanguageMode language_mode) = 0; // Logs a symbol creation of a literal or identifier. virtual void LogAsciiSymbol(int start, Vector<const char> literal) { } @@ -89,12 +89,12 @@ class FunctionLoggingParserRecorder : public ParserRecorder { int end, int literals, int properties, - int strict_mode) { + LanguageMode language_mode) { function_store_.Add(start); function_store_.Add(end); function_store_.Add(literals); function_store_.Add(properties); - function_store_.Add(strict_mode); + function_store_.Add(language_mode); } // Logs an error message and marks the log as containing an error. diff --git a/deps/v8/src/preparser-api.cc b/deps/v8/src/preparser-api.cc index 899489e250..1bca9a3333 100644 --- a/deps/v8/src/preparser-api.cc +++ b/deps/v8/src/preparser-api.cc @@ -182,13 +182,13 @@ PreParserData Preparse(UnicodeInputStream* input, size_t max_stack) { internal::InputStreamUTF16Buffer buffer(input); uintptr_t stack_limit = reinterpret_cast<uintptr_t>(&buffer) - max_stack; internal::UnicodeCache unicode_cache; - internal::JavaScriptScanner scanner(&unicode_cache); + internal::Scanner scanner(&unicode_cache); scanner.Initialize(&buffer); internal::CompleteParserRecorder recorder; preparser::PreParser::PreParseResult result = preparser::PreParser::PreParseProgram(&scanner, &recorder, - true, + internal::kAllowLazy, stack_limit); if (result == preparser::PreParser::kPreParseStackOverflow) { return PreParserData::StackOverflow(); diff --git a/deps/v8/src/preparser.cc b/deps/v8/src/preparser.cc index 6021ebd463..b36f4faca4 100644 --- a/deps/v8/src/preparser.cc +++ b/deps/v8/src/preparser.cc @@ -52,6 +52,34 @@ int isfinite(double value); namespace preparser { +PreParser::PreParseResult PreParser::PreParseLazyFunction( + i::LanguageMode mode, i::ParserRecorder* log) { + log_ = log; + // Lazy functions always have trivial outer scopes (no with/catch scopes). + Scope top_scope(&scope_, kTopLevelScope); + set_language_mode(mode); + Scope function_scope(&scope_, kFunctionScope); + ASSERT_EQ(i::Token::LBRACE, scanner_->current_token()); + bool ok = true; + int start_position = scanner_->peek_location().beg_pos; + ParseLazyFunctionLiteralBody(&ok); + if (stack_overflow_) return kPreParseStackOverflow; + if (!ok) { + ReportUnexpectedToken(scanner_->current_token()); + } else { + ASSERT_EQ(i::Token::RBRACE, scanner_->peek()); + if (!is_classic_mode()) { + int end_pos = scanner_->location().end_pos; + CheckOctalLiteral(start_position, end_pos, &ok); + if (ok) { + CheckDelayedStrictModeViolation(start_position, end_pos, &ok); + } + } + } + return kPreParseSuccess; +} + + // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. // See preparser-data.h for the data. @@ -72,7 +100,7 @@ void PreParser::ReportUnexpectedToken(i::Token::Value token) { if (token == i::Token::ILLEGAL && stack_overflow_) { return; } - i::JavaScriptScanner::Location source_location = scanner_->location(); + i::Scanner::Location source_location = scanner_->location(); // Four of the tokens are treated specially switch (token) { @@ -117,8 +145,21 @@ void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) { PreParser::Statement PreParser::ParseSourceElement(bool* ok) { + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // + // In harmony mode we allow additionally the following productions + // SourceElement: + // LetDeclaration + // ConstDeclaration + switch (peek()) { + case i::Token::FUNCTION: + return ParseFunctionDeclaration(ok); case i::Token::LET: + case i::Token::CONST: return ParseVariableStatement(kSourceElement, ok); default: return ParseStatement(ok); @@ -136,7 +177,8 @@ PreParser::SourceElements PreParser::ParseSourceElements(int end_token, Statement statement = ParseSourceElement(CHECK_OK); if (allow_directive_prologue) { if (statement.IsUseStrictLiteral()) { - set_strict_mode(); + set_language_mode(harmony_scoping_ ? + i::EXTENDED_MODE : i::STRICT_MODE); } else if (!statement.IsStringLiteral()) { allow_directive_prologue = false; } @@ -185,6 +227,7 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { return ParseBlock(ok); case i::Token::CONST: + case i::Token::LET: case i::Token::VAR: return ParseVariableStatement(kStatement, ok); @@ -225,8 +268,19 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { case i::Token::TRY: return ParseTryStatement(ok); - case i::Token::FUNCTION: - return ParseFunctionDeclaration(ok); + case i::Token::FUNCTION: { + i::Scanner::Location start_location = scanner_->peek_location(); + Statement statement = ParseFunctionDeclaration(CHECK_OK); + i::Scanner::Location end_location = scanner_->location(); + if (!is_classic_mode()) { + ReportMessageAt(start_location.beg_pos, end_location.end_pos, + "strict_function", NULL); + *ok = false; + return Statement::Default(); + } else { + return statement; + } + } case i::Token::DEBUGGER: return ParseDebuggerStatement(ok); @@ -271,14 +325,10 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) { // Expect(i::Token::LBRACE, CHECK_OK); while (peek() != i::Token::RBRACE) { - i::Scanner::Location start_location = scanner_->peek_location(); - Statement statement = ParseSourceElement(CHECK_OK); - i::Scanner::Location end_location = scanner_->location(); - if (strict_mode() && statement.IsFunctionDeclaration()) { - ReportMessageAt(start_location.beg_pos, end_location.end_pos, - "strict_function", NULL); - *ok = false; - return Statement::Default(); + if (is_extended_mode()) { + ParseSourceElement(CHECK_OK); + } else { + ParseStatement(CHECK_OK); } } Expect(i::Token::RBRACE, ok); @@ -294,6 +344,7 @@ PreParser::Statement PreParser::ParseVariableStatement( Statement result = ParseVariableDeclarations(var_context, NULL, + NULL, CHECK_OK); ExpectSemicolon(CHECK_OK); return result; @@ -307,22 +358,73 @@ PreParser::Statement PreParser::ParseVariableStatement( // of 'for-in' loops. PreParser::Statement PreParser::ParseVariableDeclarations( VariableDeclarationContext var_context, + VariableDeclarationProperties* decl_props, int* num_decl, bool* ok) { // VariableDeclarations :: // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] - + // + // The ES6 Draft Rev3 specifies the following grammar for const declarations + // + // ConstDeclaration :: + // const ConstBinding (',' ConstBinding)* ';' + // ConstBinding :: + // Identifier '=' AssignmentExpression + // + // TODO(ES6): + // ConstBinding :: + // BindingPattern '=' AssignmentExpression + bool require_initializer = false; if (peek() == i::Token::VAR) { Consume(i::Token::VAR); } else if (peek() == i::Token::CONST) { - if (strict_mode()) { + // TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads: + // + // ConstDeclaration : const ConstBinding (',' ConstBinding)* ';' + // + // * It is a Syntax Error if the code that matches this production is not + // contained in extended code. + // + // However disallowing const in classic mode will break compatibility with + // existing pages. Therefore we keep allowing const with the old + // non-harmony semantics in classic mode. + Consume(i::Token::CONST); + switch (language_mode()) { + case i::CLASSIC_MODE: + break; + case i::STRICT_MODE: { + i::Scanner::Location location = scanner_->peek_location(); + ReportMessageAt(location, "strict_const", NULL); + *ok = false; + return Statement::Default(); + } + case i::EXTENDED_MODE: + if (var_context != kSourceElement && + var_context != kForStatement) { + i::Scanner::Location location = scanner_->peek_location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "unprotected_const", NULL); + *ok = false; + return Statement::Default(); + } + require_initializer = true; + break; + } + } else if (peek() == i::Token::LET) { + // ES6 Draft Rev4 section 12.2.1: + // + // LetDeclaration : let LetBindingList ; + // + // * It is a Syntax Error if the code that matches this production is not + // contained in extended code. + if (!is_extended_mode()) { i::Scanner::Location location = scanner_->peek_location(); - ReportMessageAt(location, "strict_const", NULL); + ReportMessageAt(location.beg_pos, location.end_pos, + "illegal_let", NULL); *ok = false; return Statement::Default(); } - Consume(i::Token::CONST); - } else if (peek() == i::Token::LET) { + Consume(i::Token::LET); if (var_context != kSourceElement && var_context != kForStatement) { i::Scanner::Location location = scanner_->peek_location(); @@ -331,7 +433,6 @@ PreParser::Statement PreParser::ParseVariableDeclarations( *ok = false; return Statement::Default(); } - Consume(i::Token::LET); } else { *ok = false; return Statement::Default(); @@ -346,7 +447,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations( // Parse variable name. if (nvars > 0) Consume(i::Token::COMMA); Identifier identifier = ParseIdentifier(CHECK_OK); - if (strict_mode() && !identifier.IsValidStrictVariable()) { + if (!is_classic_mode() && !identifier.IsValidStrictVariable()) { StrictModeIdentifierViolation(scanner_->location(), "strict_var_name", identifier, @@ -354,9 +455,10 @@ PreParser::Statement PreParser::ParseVariableDeclarations( return Statement::Default(); } nvars++; - if (peek() == i::Token::ASSIGN) { + if (peek() == i::Token::ASSIGN || require_initializer) { Expect(i::Token::ASSIGN, CHECK_OK); ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); + if (decl_props != NULL) *decl_props = kHasInitializers; } } while (peek() == i::Token::COMMA); @@ -372,18 +474,11 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { Expression expr = ParseExpression(true, CHECK_OK); if (expr.IsRawIdentifier()) { - if (peek() == i::Token::COLON && - (!strict_mode() || !expr.AsIdentifier().IsFutureReserved())) { + ASSERT(!expr.AsIdentifier().IsFutureReserved()); + ASSERT(is_classic_mode() || !expr.AsIdentifier().IsFutureStrictReserved()); + if (peek() == i::Token::COLON) { Consume(i::Token::COLON); - i::Scanner::Location start_location = scanner_->peek_location(); - Statement statement = ParseStatement(CHECK_OK); - if (strict_mode() && statement.IsFunctionDeclaration()) { - i::Scanner::Location end_location = scanner_->location(); - ReportMessageAt(start_location.beg_pos, end_location.end_pos, - "strict_function", NULL); - *ok = false; - } - return Statement::Default(); + return ParseStatement(ok); } // Preparsing is disabled for extensions (because the extension details // aren't passed to lazily compiled functions), so we don't @@ -476,7 +571,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) { // WithStatement :: // 'with' '(' Expression ')' Statement Expect(i::Token::WITH, CHECK_OK); - if (strict_mode()) { + if (!is_classic_mode()) { i::Scanner::Location location = scanner_->location(); ReportMessageAt(location, "strict_mode_with", NULL); *ok = false; @@ -513,15 +608,7 @@ PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) { Expect(i::Token::DEFAULT, CHECK_OK); Expect(i::Token::COLON, CHECK_OK); } else { - i::Scanner::Location start_location = scanner_->peek_location(); - Statement statement = ParseStatement(CHECK_OK); - if (strict_mode() && statement.IsFunctionDeclaration()) { - i::Scanner::Location end_location = scanner_->location(); - ReportMessageAt(start_location.beg_pos, end_location.end_pos, - "strict_function", NULL); - *ok = false; - return Statement::Default(); - } + ParseStatement(CHECK_OK); } token = peek(); } @@ -567,9 +654,14 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { if (peek() != i::Token::SEMICOLON) { if (peek() == i::Token::VAR || peek() == i::Token::CONST || peek() == i::Token::LET) { + bool is_let = peek() == i::Token::LET; int decl_count; - ParseVariableDeclarations(kForStatement, &decl_count, CHECK_OK); - if (peek() == i::Token::IN && decl_count == 1) { + VariableDeclarationProperties decl_props = kHasNoInitializers; + ParseVariableDeclarations( + kForStatement, &decl_props, &decl_count, CHECK_OK); + bool accept_IN = decl_count == 1 && + !(is_let && decl_props == kHasInitializers); + if (peek() == i::Token::IN && accept_IN) { Expect(i::Token::IN, CHECK_OK); ParseExpression(true, CHECK_OK); Expect(i::Token::RPAREN, CHECK_OK); @@ -614,7 +706,7 @@ PreParser::Statement PreParser::ParseThrowStatement(bool* ok) { Expect(i::Token::THROW, CHECK_OK); if (scanner_->HasAnyLineTerminatorBeforeNext()) { - i::JavaScriptScanner::Location pos = scanner_->location(); + i::Scanner::Location pos = scanner_->location(); ReportMessageAt(pos, "newline_after_throw", NULL); *ok = false; return Statement::Default(); @@ -649,7 +741,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) { Consume(i::Token::CATCH); Expect(i::Token::LPAREN, CHECK_OK); Identifier id = ParseIdentifier(CHECK_OK); - if (strict_mode() && !id.IsValidStrictVariable()) { + if (!is_classic_mode() && !id.IsValidStrictVariable()) { StrictModeIdentifierViolation(scanner_->location(), "strict_catch_variable", id, @@ -727,7 +819,8 @@ PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN, return expression; } - if (strict_mode() && expression.IsIdentifier() && + if (!is_classic_mode() && + expression.IsIdentifier() && expression.AsIdentifier().IsEvalOrArguments()) { i::Scanner::Location after = scanner_->location(); ReportMessageAt(before.beg_pos, after.end_pos, @@ -815,7 +908,8 @@ PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) { op = Next(); i::Scanner::Location before = scanner_->peek_location(); Expression expression = ParseUnaryExpression(CHECK_OK); - if (strict_mode() && expression.IsIdentifier() && + if (!is_classic_mode() && + expression.IsIdentifier() && expression.AsIdentifier().IsEvalOrArguments()) { i::Scanner::Location after = scanner_->location(); ReportMessageAt(before.beg_pos, after.end_pos, @@ -837,7 +931,8 @@ PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) { Expression expression = ParseLeftHandSideExpression(CHECK_OK); if (!scanner_->HasAnyLineTerminatorBeforeNext() && i::Token::IsCountOp(peek())) { - if (strict_mode() && expression.IsIdentifier() && + if (!is_classic_mode() && + expression.IsIdentifier() && expression.AsIdentifier().IsEvalOrArguments()) { i::Scanner::Location after = scanner_->location(); ReportMessageAt(before.beg_pos, after.end_pos, @@ -1024,7 +1119,7 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) { } case i::Token::FUTURE_STRICT_RESERVED_WORD: - if (strict_mode()) { + if (!is_classic_mode()) { Next(); i::Scanner::Location location = scanner_->location(); ReportMessageAt(location, "strict_reserved_word", NULL); @@ -1124,7 +1219,7 @@ void PreParser::CheckDuplicate(DuplicateFinder* finder, if (HasConflict(old_type, type)) { if (IsDataDataConflict(old_type, type)) { // Both are data properties. - if (!strict_mode()) return; + if (is_classic_mode()) return; ReportMessageAt(scanner_->location(), "strict_duplicate_property", NULL); } else if (IsDataAccessorConflict(old_type, type)) { @@ -1307,9 +1402,6 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) { } Expect(i::Token::RPAREN, CHECK_OK); - Expect(i::Token::LBRACE, CHECK_OK); - int function_block_pos = scanner_->location().beg_pos; - // Determine if the function will be lazily compiled. // Currently only happens to top-level functions. // Optimistically assume that all top-level functions are lazily compiled. @@ -1318,26 +1410,15 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) { !parenthesized_function_); parenthesized_function_ = false; + Expect(i::Token::LBRACE, CHECK_OK); if (is_lazily_compiled) { - log_->PauseRecording(); - ParseSourceElements(i::Token::RBRACE, ok); - log_->ResumeRecording(); - if (!*ok) Expression::Default(); - - Expect(i::Token::RBRACE, CHECK_OK); - - // Position right after terminal '}'. - int end_pos = scanner_->location().end_pos; - log_->LogFunction(function_block_pos, end_pos, - function_scope.materialized_literal_count(), - function_scope.expected_properties(), - strict_mode() ? 1 : 0); + ParseLazyFunctionLiteralBody(CHECK_OK); } else { - ParseSourceElements(i::Token::RBRACE, CHECK_OK); - Expect(i::Token::RBRACE, CHECK_OK); + ParseSourceElements(i::Token::RBRACE, ok); } + Expect(i::Token::RBRACE, CHECK_OK); - if (strict_mode()) { + if (!is_classic_mode()) { int end_position = scanner_->location().end_pos; CheckOctalLiteral(start_position, end_position, CHECK_OK); CheckDelayedStrictModeViolation(start_position, end_position, CHECK_OK); @@ -1348,11 +1429,31 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) { } +void PreParser::ParseLazyFunctionLiteralBody(bool* ok) { + int body_start = scanner_->location().beg_pos; + log_->PauseRecording(); + ParseSourceElements(i::Token::RBRACE, ok); + log_->ResumeRecording(); + if (!*ok) return; + + // Position right after terminal '}'. + ASSERT_EQ(i::Token::RBRACE, scanner_->peek()); + int body_end = scanner_->peek_location().end_pos; + log_->LogFunction(body_start, body_end, + scope_->materialized_literal_count(), + scope_->expected_properties(), + language_mode()); +} + + PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) { // CallRuntime :: // '%' Identifier Arguments - Expect(i::Token::MOD, CHECK_OK); + if (!allow_natives_syntax_) { + *ok = false; + return Expression::Default(); + } ParseIdentifier(CHECK_OK); ParseArguments(ok); @@ -1435,9 +1536,16 @@ PreParser::Identifier PreParser::ParseIdentifier(bool* ok) { ReportMessageAt(location.beg_pos, location.end_pos, "reserved_word", NULL); *ok = false; + return GetIdentifierSymbol(); } - // FALLTHROUGH case i::Token::FUTURE_STRICT_RESERVED_WORD: + if (!is_classic_mode()) { + i::Scanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "strict_reserved_word", NULL); + *ok = false; + } + // FALLTHROUGH case i::Token::IDENTIFIER: return GetIdentifierSymbol(); default: @@ -1450,7 +1558,7 @@ PreParser::Identifier PreParser::ParseIdentifier(bool* ok) { void PreParser::SetStrictModeViolation(i::Scanner::Location location, const char* type, bool* ok) { - if (strict_mode()) { + if (!is_classic_mode()) { ReportMessageAt(location, type, NULL); *ok = false; return; @@ -1490,7 +1598,7 @@ void PreParser::StrictModeIdentifierViolation(i::Scanner::Location location, } else if (identifier.IsFutureStrictReserved()) { type = "strict_reserved_word"; } - if (strict_mode()) { + if (!is_classic_mode()) { ReportMessageAt(location, type, NULL); *ok = false; return; diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h index b97b7cff60..886d81a9e6 100644 --- a/deps/v8/src/preparser.h +++ b/deps/v8/src/preparser.h @@ -110,19 +110,54 @@ class PreParser { kPreParseSuccess }; + + PreParser(i::Scanner* scanner, + i::ParserRecorder* log, + uintptr_t stack_limit, + bool allow_lazy, + bool allow_natives_syntax, + bool allow_modules) + : scanner_(scanner), + log_(log), + scope_(NULL), + stack_limit_(stack_limit), + strict_mode_violation_location_(i::Scanner::Location::invalid()), + strict_mode_violation_type_(NULL), + stack_overflow_(false), + allow_lazy_(allow_lazy), + allow_modules_(allow_modules), + allow_natives_syntax_(allow_natives_syntax), + parenthesized_function_(false), + harmony_scoping_(scanner->HarmonyScoping()) { } + ~PreParser() {} // Pre-parse the program from the character stream; returns true on // success (even if parsing failed, the pre-parse data successfully // captured the syntax error), and false if a stack-overflow happened // during parsing. - static PreParseResult PreParseProgram(i::JavaScriptScanner* scanner, + static PreParseResult PreParseProgram(i::Scanner* scanner, i::ParserRecorder* log, - bool allow_lazy, + int flags, uintptr_t stack_limit) { - return PreParser(scanner, log, stack_limit, allow_lazy).PreParse(); + bool allow_lazy = (flags & i::kAllowLazy) != 0; + bool allow_natives_syntax = (flags & i::kAllowNativesSyntax) != 0; + bool allow_modules = (flags & i::kAllowModules) != 0; + return PreParser(scanner, log, stack_limit, allow_lazy, + allow_natives_syntax, allow_modules).PreParse(); } + // Parses a single function literal, from the opening parentheses before + // parameters to the closing brace after the body. + // Returns a FunctionEntry describing the body of the funciton in enough + // detail that it can be lazily compiled. + // The scanner is expected to have matched the "function" keyword and + // parameters, and have consumed the initial '{'. + // At return, unless an error occured, the scanner is positioned before the + // the final '}'. + PreParseResult PreParseLazyFunction(i::LanguageMode mode, + i::ParserRecorder* log); + private: // Used to detect duplicates in object literals. Each of the values // kGetterProperty, kSetterProperty and kValueProperty represents @@ -179,6 +214,12 @@ class PreParser { kForStatement }; + // If a list of variable declarations includes any initializers. + enum VariableDeclarationProperties { + kHasInitializers, + kHasNoInitializers + }; + class Expression; class Identifier { @@ -408,7 +449,8 @@ class PreParser { materialized_literal_count_(0), expected_properties_(0), with_nesting_count_(0), - strict_((prev_ != NULL) && prev_->is_strict()) { + language_mode_( + (prev_ != NULL) ? prev_->language_mode() : i::CLASSIC_MODE) { *variable = this; } ~Scope() { *variable_ = prev_; } @@ -418,8 +460,15 @@ class PreParser { int expected_properties() { return expected_properties_; } int materialized_literal_count() { return materialized_literal_count_; } bool IsInsideWith() { return with_nesting_count_ != 0; } - bool is_strict() { return strict_; } - void set_strict() { strict_ = true; } + bool is_classic_mode() { + return language_mode_ == i::CLASSIC_MODE; + } + i::LanguageMode language_mode() { + return language_mode_; + } + void set_language_mode(i::LanguageMode language_mode) { + language_mode_ = language_mode; + } void EnterWith() { with_nesting_count_++; } void LeaveWith() { with_nesting_count_--; } @@ -430,25 +479,9 @@ class PreParser { int materialized_literal_count_; int expected_properties_; int with_nesting_count_; - bool strict_; + i::LanguageMode language_mode_; }; - // Private constructor only used in PreParseProgram. - PreParser(i::JavaScriptScanner* scanner, - i::ParserRecorder* log, - uintptr_t stack_limit, - bool allow_lazy) - : scanner_(scanner), - log_(log), - scope_(NULL), - stack_limit_(stack_limit), - strict_mode_violation_location_(i::Scanner::Location::invalid()), - strict_mode_violation_type_(NULL), - stack_overflow_(false), - allow_lazy_(true), - parenthesized_function_(false), - harmony_block_scoping_(scanner->HarmonyBlockScoping()) { } - // Preparse the program. Only called in PreParseProgram after creating // the instance. PreParseResult PreParse() { @@ -459,7 +492,7 @@ class PreParser { if (stack_overflow_) return kPreParseStackOverflow; if (!ok) { ReportUnexpectedToken(scanner_->current_token()); - } else if (scope_->is_strict()) { + } else if (!scope_->is_classic_mode()) { CheckOctalLiteral(start_position, scanner_->location().end_pos, &ok); } return kPreParseSuccess; @@ -493,6 +526,7 @@ class PreParser { Statement ParseVariableStatement(VariableDeclarationContext var_context, bool* ok); Statement ParseVariableDeclarations(VariableDeclarationContext var_context, + VariableDeclarationProperties* decl_props, int* num_decl, bool* ok); Statement ParseExpressionOrLabelledStatement(bool* ok); @@ -527,6 +561,7 @@ class PreParser { Arguments ParseArguments(bool* ok); Expression ParseFunctionLiteral(bool* ok); + void ParseLazyFunctionLiteralBody(bool* ok); Identifier ParseIdentifier(bool* ok); Identifier ParseIdentifierName(bool* ok); @@ -562,11 +597,19 @@ class PreParser { bool peek_any_identifier(); - void set_strict_mode() { - scope_->set_strict(); + void set_language_mode(i::LanguageMode language_mode) { + scope_->set_language_mode(language_mode); + } + + bool is_classic_mode() { + return scope_->language_mode() == i::CLASSIC_MODE; + } + + bool is_extended_mode() { + return scope_->language_mode() == i::EXTENDED_MODE; } - bool strict_mode() { return scope_->is_strict(); } + i::LanguageMode language_mode() { return scope_->language_mode(); } void Consume(i::Token::Value token) { Next(); } @@ -590,7 +633,7 @@ class PreParser { void SetStrictModeViolation(i::Scanner::Location, const char* type, - bool *ok); + bool* ok); void CheckDelayedStrictModeViolation(int beg_pos, int end_pos, bool* ok); @@ -599,7 +642,7 @@ class PreParser { Identifier identifier, bool* ok); - i::JavaScriptScanner* scanner_; + i::Scanner* scanner_; i::ParserRecorder* log_; Scope* scope_; uintptr_t stack_limit_; @@ -607,8 +650,10 @@ class PreParser { const char* strict_mode_violation_type_; bool stack_overflow_; bool allow_lazy_; + bool allow_modules_; + bool allow_natives_syntax_; bool parenthesized_function_; - bool harmony_block_scoping_; + bool harmony_scoping_; }; } } // v8::preparser diff --git a/deps/v8/src/prettyprinter.cc b/deps/v8/src/prettyprinter.cc index 663af284b4..685e443e24 100644 --- a/deps/v8/src/prettyprinter.cc +++ b/deps/v8/src/prettyprinter.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -58,7 +58,7 @@ void PrettyPrinter::VisitBlock(Block* node) { } -void PrettyPrinter::VisitDeclaration(Declaration* node) { +void PrettyPrinter::VisitVariableDeclaration(VariableDeclaration* node) { Print("var "); PrintLiteral(node->proxy()->name(), false); if (node->fun() != NULL) { @@ -69,6 +69,38 @@ void PrettyPrinter::VisitDeclaration(Declaration* node) { } +void PrettyPrinter::VisitModuleDeclaration(ModuleDeclaration* node) { + Print("module "); + PrintLiteral(node->proxy()->name(), false); + Print(" = "); + Visit(node->module()); + Print(";"); +} + + +void PrettyPrinter::VisitModuleLiteral(ModuleLiteral* node) { + VisitBlock(node->body()); +} + + +void PrettyPrinter::VisitModuleVariable(ModuleVariable* node) { + PrintLiteral(node->var()->name(), false); +} + + +void PrettyPrinter::VisitModulePath(ModulePath* node) { + Visit(node->module()); + Print("."); + PrintLiteral(node->name(), false); +} + + +void PrettyPrinter::VisitModuleUrl(ModuleUrl* node) { + Print("at "); + PrintLiteral(node->url(), true); +} + + void PrettyPrinter::VisitExpressionStatement(ExpressionStatement* node) { Visit(node->expression()); Print(";"); @@ -372,13 +404,6 @@ void PrettyPrinter::VisitCompareOperation(CompareOperation* node) { } -void PrettyPrinter::VisitCompareToNull(CompareToNull* node) { - Print("("); - Visit(node->expression()); - Print("%s null)", Token::String(node->op())); -} - - void PrettyPrinter::VisitThisFunction(ThisFunction* node) { Print("<this-function>"); } @@ -454,6 +479,7 @@ void PrettyPrinter::Print(const char* format, ...) { void PrettyPrinter::PrintStatements(ZoneList<Statement*>* statements) { + if (statements == NULL) return; for (int i = 0; i < statements->length(); i++) { if (i != 0) Print(" "); Visit(statements->at(i)); @@ -717,7 +743,7 @@ void AstPrinter::VisitBlock(Block* node) { } -void AstPrinter::VisitDeclaration(Declaration* node) { +void AstPrinter::VisitVariableDeclaration(VariableDeclaration* node) { if (node->fun() == NULL) { // var or const declarations PrintLiteralWithModeIndented(Variable::Mode2String(node->mode()), @@ -734,6 +760,35 @@ void AstPrinter::VisitDeclaration(Declaration* node) { } +void AstPrinter::VisitModuleDeclaration(ModuleDeclaration* node) { + IndentedScope indent(this, "MODULE"); + PrintLiteralIndented("NAME", node->proxy()->name(), true); + Visit(node->module()); +} + + +void AstPrinter::VisitModuleLiteral(ModuleLiteral* node) { + VisitBlock(node->body()); +} + + +void AstPrinter::VisitModuleVariable(ModuleVariable* node) { + PrintLiteralIndented("VARIABLE", node->var()->name(), false); +} + + +void AstPrinter::VisitModulePath(ModulePath* node) { + IndentedScope indent(this, "PATH"); + PrintIndentedVisit("MODULE", node->module()); + PrintLiteralIndented("NAME", node->name(), false); +} + + +void AstPrinter::VisitModuleUrl(ModuleUrl* node) { + PrintLiteralIndented("URL", node->url(), true); +} + + void AstPrinter::VisitExpressionStatement(ExpressionStatement* node) { Visit(node->expression()); } @@ -1020,416 +1075,10 @@ void AstPrinter::VisitCompareOperation(CompareOperation* node) { } -void AstPrinter::VisitCompareToNull(CompareToNull* node) { - const char* name = node->is_strict() - ? "COMPARE-TO-NULL-STRICT" - : "COMPARE-TO-NULL"; - IndentedScope indent(this, name, node); - Visit(node->expression()); -} - - void AstPrinter::VisitThisFunction(ThisFunction* node) { IndentedScope indent(this, "THIS-FUNCTION"); } - -TagScope::TagScope(JsonAstBuilder* builder, const char* name) - : builder_(builder), next_(builder->tag()), has_body_(false) { - if (next_ != NULL) { - next_->use(); - builder->Print(",\n"); - } - builder->set_tag(this); - builder->PrintIndented("["); - builder->Print("\"%s\"", name); - builder->increase_indent(JsonAstBuilder::kTagIndentSize); -} - - -TagScope::~TagScope() { - builder_->decrease_indent(JsonAstBuilder::kTagIndentSize); - if (has_body_) { - builder_->Print("\n"); - builder_->PrintIndented("]"); - } else { - builder_->Print("]"); - } - builder_->set_tag(next_); -} - - -AttributesScope::AttributesScope(JsonAstBuilder* builder) - : builder_(builder), attribute_count_(0) { - builder->set_attributes(this); - builder->tag()->use(); - builder->Print(",\n"); - builder->PrintIndented("{"); - builder->increase_indent(JsonAstBuilder::kAttributesIndentSize); -} - - -AttributesScope::~AttributesScope() { - builder_->decrease_indent(JsonAstBuilder::kAttributesIndentSize); - if (attribute_count_ > 1) { - builder_->Print("\n"); - builder_->PrintIndented("}"); - } else { - builder_->Print("}"); - } - builder_->set_attributes(NULL); -} - - -const char* JsonAstBuilder::BuildProgram(FunctionLiteral* program) { - Init(); - Visit(program); - Print("\n"); - return Output(); -} - - -void JsonAstBuilder::AddAttributePrefix(const char* name) { - if (attributes()->is_used()) { - Print(",\n"); - PrintIndented("\""); - } else { - Print("\""); - } - Print("%s\":", name); - attributes()->use(); -} - - -void JsonAstBuilder::AddAttribute(const char* name, Handle<String> value) { - SmartArrayPointer<char> value_string = value->ToCString(); - AddAttributePrefix(name); - Print("\"%s\"", *value_string); -} - - -void JsonAstBuilder::AddAttribute(const char* name, const char* value) { - AddAttributePrefix(name); - Print("\"%s\"", value); -} - - -void JsonAstBuilder::AddAttribute(const char* name, int value) { - AddAttributePrefix(name); - Print("%d", value); -} - - -void JsonAstBuilder::AddAttribute(const char* name, bool value) { - AddAttributePrefix(name); - Print(value ? "true" : "false"); -} - - -void JsonAstBuilder::VisitBlock(Block* stmt) { - TagScope tag(this, "Block"); - VisitStatements(stmt->statements()); -} - - -void JsonAstBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { - TagScope tag(this, "ExpressionStatement"); - Visit(stmt->expression()); -} - - -void JsonAstBuilder::VisitEmptyStatement(EmptyStatement* stmt) { - TagScope tag(this, "EmptyStatement"); -} - - -void JsonAstBuilder::VisitIfStatement(IfStatement* stmt) { - TagScope tag(this, "IfStatement"); - Visit(stmt->condition()); - Visit(stmt->then_statement()); - Visit(stmt->else_statement()); -} - - -void JsonAstBuilder::VisitContinueStatement(ContinueStatement* stmt) { - TagScope tag(this, "ContinueStatement"); -} - - -void JsonAstBuilder::VisitBreakStatement(BreakStatement* stmt) { - TagScope tag(this, "BreakStatement"); -} - - -void JsonAstBuilder::VisitReturnStatement(ReturnStatement* stmt) { - TagScope tag(this, "ReturnStatement"); - Visit(stmt->expression()); -} - - -void JsonAstBuilder::VisitWithStatement(WithStatement* stmt) { - TagScope tag(this, "WithStatement"); - Visit(stmt->expression()); - Visit(stmt->statement()); -} - - -void JsonAstBuilder::VisitSwitchStatement(SwitchStatement* stmt) { - TagScope tag(this, "SwitchStatement"); -} - - -void JsonAstBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { - TagScope tag(this, "DoWhileStatement"); - Visit(stmt->body()); - Visit(stmt->cond()); -} - - -void JsonAstBuilder::VisitWhileStatement(WhileStatement* stmt) { - TagScope tag(this, "WhileStatement"); - Visit(stmt->cond()); - Visit(stmt->body()); -} - - -void JsonAstBuilder::VisitForStatement(ForStatement* stmt) { - TagScope tag(this, "ForStatement"); - if (stmt->init() != NULL) Visit(stmt->init()); - if (stmt->cond() != NULL) Visit(stmt->cond()); - Visit(stmt->body()); - if (stmt->next() != NULL) Visit(stmt->next()); -} - - -void JsonAstBuilder::VisitForInStatement(ForInStatement* stmt) { - TagScope tag(this, "ForInStatement"); - Visit(stmt->each()); - Visit(stmt->enumerable()); - Visit(stmt->body()); -} - - -void JsonAstBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) { - TagScope tag(this, "TryCatchStatement"); - { AttributesScope attributes(this); - AddAttribute("variable", stmt->variable()->name()); - } - Visit(stmt->try_block()); - Visit(stmt->catch_block()); -} - - -void JsonAstBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) { - TagScope tag(this, "TryFinallyStatement"); - Visit(stmt->try_block()); - Visit(stmt->finally_block()); -} - - -void JsonAstBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { - TagScope tag(this, "DebuggerStatement"); -} - - -void JsonAstBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { - TagScope tag(this, "FunctionLiteral"); - { - AttributesScope attributes(this); - AddAttribute("name", expr->name()); - } - VisitDeclarations(expr->scope()->declarations()); - VisitStatements(expr->body()); -} - - -void JsonAstBuilder::VisitSharedFunctionInfoLiteral( - SharedFunctionInfoLiteral* expr) { - TagScope tag(this, "SharedFunctionInfoLiteral"); -} - - -void JsonAstBuilder::VisitConditional(Conditional* expr) { - TagScope tag(this, "Conditional"); -} - - -void JsonAstBuilder::VisitVariableProxy(VariableProxy* expr) { - TagScope tag(this, "Variable"); - { - AttributesScope attributes(this); - Variable* var = expr->var(); - AddAttribute("name", var->name()); - switch (var->location()) { - case Variable::UNALLOCATED: - AddAttribute("location", "UNALLOCATED"); - break; - case Variable::PARAMETER: - AddAttribute("location", "PARAMETER"); - AddAttribute("index", var->index()); - break; - case Variable::LOCAL: - AddAttribute("location", "LOCAL"); - AddAttribute("index", var->index()); - break; - case Variable::CONTEXT: - AddAttribute("location", "CONTEXT"); - AddAttribute("index", var->index()); - break; - case Variable::LOOKUP: - AddAttribute("location", "LOOKUP"); - break; - } - } -} - - -void JsonAstBuilder::VisitLiteral(Literal* expr) { - TagScope tag(this, "Literal"); - { - AttributesScope attributes(this); - Handle<Object> handle = expr->handle(); - if (handle->IsString()) { - AddAttribute("handle", Handle<String>(String::cast(*handle))); - } else if (handle->IsSmi()) { - AddAttribute("handle", Smi::cast(*handle)->value()); - } - } -} - - -void JsonAstBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { - TagScope tag(this, "RegExpLiteral"); -} - - -void JsonAstBuilder::VisitObjectLiteral(ObjectLiteral* expr) { - TagScope tag(this, "ObjectLiteral"); -} - - -void JsonAstBuilder::VisitArrayLiteral(ArrayLiteral* expr) { - TagScope tag(this, "ArrayLiteral"); -} - - -void JsonAstBuilder::VisitAssignment(Assignment* expr) { - TagScope tag(this, "Assignment"); - { - AttributesScope attributes(this); - AddAttribute("op", Token::Name(expr->op())); - } - Visit(expr->target()); - Visit(expr->value()); -} - - -void JsonAstBuilder::VisitThrow(Throw* expr) { - TagScope tag(this, "Throw"); - Visit(expr->exception()); -} - - -void JsonAstBuilder::VisitProperty(Property* expr) { - TagScope tag(this, "Property"); - Visit(expr->obj()); - Visit(expr->key()); -} - - -void JsonAstBuilder::VisitCall(Call* expr) { - TagScope tag(this, "Call"); - Visit(expr->expression()); - VisitExpressions(expr->arguments()); -} - - -void JsonAstBuilder::VisitCallNew(CallNew* expr) { - TagScope tag(this, "CallNew"); - Visit(expr->expression()); - VisitExpressions(expr->arguments()); -} - - -void JsonAstBuilder::VisitCallRuntime(CallRuntime* expr) { - TagScope tag(this, "CallRuntime"); - { - AttributesScope attributes(this); - AddAttribute("name", expr->name()); - } - VisitExpressions(expr->arguments()); -} - - -void JsonAstBuilder::VisitUnaryOperation(UnaryOperation* expr) { - TagScope tag(this, "UnaryOperation"); - { - AttributesScope attributes(this); - AddAttribute("op", Token::Name(expr->op())); - } - Visit(expr->expression()); -} - - -void JsonAstBuilder::VisitCountOperation(CountOperation* expr) { - TagScope tag(this, "CountOperation"); - { - AttributesScope attributes(this); - AddAttribute("is_prefix", expr->is_prefix()); - AddAttribute("op", Token::Name(expr->op())); - } - Visit(expr->expression()); -} - - -void JsonAstBuilder::VisitBinaryOperation(BinaryOperation* expr) { - TagScope tag(this, "BinaryOperation"); - { - AttributesScope attributes(this); - AddAttribute("op", Token::Name(expr->op())); - } - Visit(expr->left()); - Visit(expr->right()); -} - - -void JsonAstBuilder::VisitCompareOperation(CompareOperation* expr) { - TagScope tag(this, "CompareOperation"); - { - AttributesScope attributes(this); - AddAttribute("op", Token::Name(expr->op())); - } - Visit(expr->left()); - Visit(expr->right()); -} - - -void JsonAstBuilder::VisitCompareToNull(CompareToNull* expr) { - TagScope tag(this, "CompareToNull"); - { - AttributesScope attributes(this); - AddAttribute("is_strict", expr->is_strict()); - } - Visit(expr->expression()); -} - - -void JsonAstBuilder::VisitThisFunction(ThisFunction* expr) { - TagScope tag(this, "ThisFunction"); -} - - -void JsonAstBuilder::VisitDeclaration(Declaration* decl) { - TagScope tag(this, "Declaration"); - { - AttributesScope attributes(this); - AddAttribute("mode", Variable::Mode2String(decl->mode())); - } - Visit(decl->proxy()); - if (decl->fun() != NULL) Visit(decl->fun()); -} - - #endif // DEBUG } } // namespace v8::internal diff --git a/deps/v8/src/prettyprinter.h b/deps/v8/src/prettyprinter.h index a26c48e490..9ac7257640 100644 --- a/deps/v8/src/prettyprinter.h +++ b/deps/v8/src/prettyprinter.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -112,107 +112,6 @@ class AstPrinter: public PrettyPrinter { int indent_; }; - -// Forward declaration of helper classes. -class TagScope; -class AttributesScope; - -// Build a C string containing a JSON representation of a function's -// AST. The representation is based on JsonML (www.jsonml.org). -class JsonAstBuilder: public PrettyPrinter { - public: - JsonAstBuilder() - : indent_(0), top_tag_scope_(NULL), attributes_scope_(NULL) { - } - virtual ~JsonAstBuilder() {} - - // Controls the indentation of subsequent lines of a tag body after - // the first line. - static const int kTagIndentSize = 2; - - // Controls the indentation of subsequent lines of an attributes - // blocks's body after the first line. - static const int kAttributesIndentSize = 1; - - // Construct a JSON representation of a function literal. - const char* BuildProgram(FunctionLiteral* program); - - // Print text indented by the current indentation level. - void PrintIndented(const char* text) { Print("%*s%s", indent_, "", text); } - - // Change the indentation level. - void increase_indent(int amount) { indent_ += amount; } - void decrease_indent(int amount) { indent_ -= amount; } - - // The builder maintains a stack of opened AST node constructors. - // Each node constructor corresponds to a JsonML tag. - TagScope* tag() { return top_tag_scope_; } - void set_tag(TagScope* scope) { top_tag_scope_ = scope; } - - // The builder maintains a pointer to the currently opened attributes - // of current AST node or NULL if the attributes are not opened. - AttributesScope* attributes() { return attributes_scope_; } - void set_attributes(AttributesScope* scope) { attributes_scope_ = scope; } - - // Add an attribute to the currently opened attributes. - void AddAttribute(const char* name, Handle<String> value); - void AddAttribute(const char* name, const char* value); - void AddAttribute(const char* name, int value); - void AddAttribute(const char* name, bool value); - - // AST node visit functions. -#define DECLARE_VISIT(type) virtual void Visit##type(type* node); - AST_NODE_LIST(DECLARE_VISIT) -#undef DECLARE_VISIT - - private: - int indent_; - TagScope* top_tag_scope_; - AttributesScope* attributes_scope_; - - // Utility function used by AddAttribute implementations. - void AddAttributePrefix(const char* name); -}; - - -// The JSON AST builder keeps a stack of open element tags (AST node -// constructors from the current iteration point to the root of the -// AST). TagScope is a helper class to manage the opening and closing -// of tags, the indentation of their bodies, and comma separating their -// contents. -class TagScope BASE_EMBEDDED { - public: - TagScope(JsonAstBuilder* builder, const char* name); - ~TagScope(); - - void use() { has_body_ = true; } - - private: - JsonAstBuilder* builder_; - TagScope* next_; - bool has_body_; -}; - - -// AttributesScope is a helper class to manage the opening and closing -// of attribute blocks, the indentation of their bodies, and comma -// separating their contents. JsonAstBuilder::AddAttribute adds an -// attribute to the currently open AttributesScope. They cannot be -// nested so the builder keeps an optional single scope rather than a -// stack. -class AttributesScope BASE_EMBEDDED { - public: - explicit AttributesScope(JsonAstBuilder* builder); - ~AttributesScope(); - - bool is_used() { return attribute_count_ > 0; } - void use() { ++attribute_count_; } - - private: - JsonAstBuilder* builder_; - int attribute_count_; -}; - #endif // DEBUG } } // namespace v8::internal diff --git a/deps/v8/src/profile-generator-inl.h b/deps/v8/src/profile-generator-inl.h index 88d6e87941..7a70b013bb 100644 --- a/deps/v8/src/profile-generator-inl.h +++ b/deps/v8/src/profile-generator-inl.h @@ -95,6 +95,26 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { } +uint64_t HeapObjectsMap::GetNthGcSubrootId(int delta) { + return kGcRootsFirstSubrootId + delta * kObjectIdStep; +} + + +HeapObject* V8HeapExplorer::GetNthGcSubrootObject(int delta) { + return reinterpret_cast<HeapObject*>( + reinterpret_cast<char*>(kFirstGcSubrootObject) + + delta * HeapObjectsMap::kObjectIdStep); +} + + +int V8HeapExplorer::GetGcSubrootOrder(HeapObject* subroot) { + return static_cast<int>( + (reinterpret_cast<char*>(subroot) - + reinterpret_cast<char*>(kFirstGcSubrootObject)) / + HeapObjectsMap::kObjectIdStep); +} + + uint64_t HeapEntry::id() { union { Id stored_id; diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index e319efb043..c6e6131a71 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -152,9 +152,12 @@ const char* StringsStorage::GetVFormatted(const char* format, va_list args) { const char* StringsStorage::GetName(String* name) { if (name->IsString()) { - return AddOrDisposeString( - name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(), - name->Hash()); + int length = Min(kMaxNameSize, name->length()); + SmartArrayPointer<char> data = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length); + uint32_t hash = + HashSequentialString(*data, length, name->GetHeap()->HashSeed()); + return AddOrDisposeString(data.Detach(), hash); } return ""; } @@ -493,8 +496,6 @@ void CpuProfile::Print() { CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL; const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL; -const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = - CodeMap::CodeEntryInfo(NULL, 0); void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) { @@ -903,7 +904,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { entry++; } - for (const Address *stack_pos = sample.stack, + for (const Address* stack_pos = sample.stack, *stack_end = stack_pos + sample.frames_count; stack_pos != stack_end; ++stack_pos) { @@ -943,7 +944,7 @@ void HeapGraphEdge::Init( void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) { - ASSERT(type == kElement || type == kHidden); + ASSERT(type == kElement || type == kHidden || type == kWeak); child_index_ = child_index; type_ = type; index_ = index; @@ -1058,8 +1059,11 @@ void HeapEntry::PaintAllReachable() { } -void HeapEntry::Print(int max_depth, int indent) { - OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id()); +void HeapEntry::Print( + const char* prefix, const char* edge_name, int max_depth, int indent) { + OS::Print("%6d %7d @%6llu %*c %s%s: ", + self_size(), RetainedSize(false), id(), + indent, ' ', prefix, edge_name); if (type() != kString) { OS::Print("%s %.40s\n", TypeAsString(), name_); } else { @@ -1078,29 +1082,40 @@ void HeapEntry::Print(int max_depth, int indent) { Vector<HeapGraphEdge> ch = children(); for (int i = 0; i < ch.length(); ++i) { HeapGraphEdge& edge = ch[i]; + const char* edge_prefix = ""; + ScopedVector<char> index(64); + const char* edge_name = index.start(); switch (edge.type()) { case HeapGraphEdge::kContextVariable: - OS::Print(" %*c #%s: ", indent, ' ', edge.name()); + edge_prefix = "#"; + edge_name = edge.name(); break; case HeapGraphEdge::kElement: - OS::Print(" %*c %d: ", indent, ' ', edge.index()); + OS::SNPrintF(index, "%d", edge.index()); break; case HeapGraphEdge::kInternal: - OS::Print(" %*c $%s: ", indent, ' ', edge.name()); + edge_prefix = "$"; + edge_name = edge.name(); break; case HeapGraphEdge::kProperty: - OS::Print(" %*c %s: ", indent, ' ', edge.name()); + edge_name = edge.name(); break; case HeapGraphEdge::kHidden: - OS::Print(" %*c $%d: ", indent, ' ', edge.index()); + edge_prefix = "$"; + OS::SNPrintF(index, "%d", edge.index()); break; case HeapGraphEdge::kShortcut: - OS::Print(" %*c ^%s: ", indent, ' ', edge.name()); + edge_prefix = "^"; + edge_name = edge.name(); + break; + case HeapGraphEdge::kWeak: + edge_prefix = "w"; + OS::SNPrintF(index, "%d", edge.index()); break; default: - OS::Print("!!! unknown edge type: %d ", edge.type()); + OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type()); } - edge.to()->Print(max_depth, indent + 2); + edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2); } } @@ -1116,6 +1131,7 @@ const char* HeapEntry::TypeAsString() { case kRegExp: return "/regexp/"; case kHeapNumber: return "/number/"; case kNative: return "/native/"; + case kSynthetic: return "/synthetic/"; default: return "???"; } } @@ -1219,7 +1235,10 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize); STATIC_ASSERT( sizeof(HeapEntry) == - SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT + SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize); + for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { + gc_subroot_entries_[i] = NULL; + } } HeapSnapshot::~HeapSnapshot() { @@ -1275,13 +1294,15 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, } -HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count, - int retainers_count) { - ASSERT(natives_root_entry_ == NULL); - return (natives_root_entry_ = AddEntry( +HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag, + int children_count, + int retainers_count) { + ASSERT(gc_subroot_entries_[tag] == NULL); + ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags); + return (gc_subroot_entries_[tag] = AddEntry( HeapEntry::kObject, - "(Native objects)", - HeapObjectsMap::kNativesRootObjectId, + VisitorSynchronization::kTagNames[tag], + HeapObjectsMap::GetNthGcSubrootId(tag), 0, children_count, retainers_count)); @@ -1360,17 +1381,20 @@ List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { void HeapSnapshot::Print(int max_depth) { - root()->Print(max_depth, 0); + root()->Print("", "", max_depth, 0); } // We split IDs on evens for embedder objects (see // HeapObjectsMap::GenerateId) and odds for native objects. const uint64_t HeapObjectsMap::kInternalRootObjectId = 1; -const uint64_t HeapObjectsMap::kGcRootsObjectId = 3; -const uint64_t HeapObjectsMap::kNativesRootObjectId = 5; -// Increase kFirstAvailableObjectId if new 'special' objects appear. -const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7; +const uint64_t HeapObjectsMap::kGcRootsObjectId = + HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId = + HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = + HeapObjectsMap::kGcRootsFirstSubrootId + + VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; HeapObjectsMap::HeapObjectsMap() : initial_fill_mode_(true), @@ -1396,7 +1420,7 @@ uint64_t HeapObjectsMap::FindObject(Address addr) { if (existing != 0) return existing; } uint64_t id = next_id_; - next_id_ += 2; + next_id_ += kObjectIdStep; AddEntry(addr, id); return id; } @@ -1408,10 +1432,12 @@ void HeapObjectsMap::MoveObject(Address from, Address to) { if (entry != NULL) { void* value = entry->value; entries_map_.Remove(from, AddressHash(from)); - entry = entries_map_.Lookup(to, AddressHash(to), true); - // We can have an entry at the new location, it is OK, as GC can overwrite - // dead objects with alive objects being moved. - entry->value = value; + if (to != NULL) { + entry = entries_map_.Lookup(to, AddressHash(to), true); + // We can have an entry at the new location, it is OK, as GC can overwrite + // dead objects with alive objects being moved. + entry->value = value; + } } } @@ -1536,6 +1562,9 @@ void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) { Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) { + // First perform a full GC in order to avoid dead objects. + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "HeapSnapshotsCollection::FindHeapObjectById"); AssertNoAllocation no_allocation; HeapObject* object = NULL; HeapIterator iterator(HeapIterator::kFilterUnreachable); @@ -1553,7 +1582,7 @@ Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) { } -HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder = +HeapEntry* const HeapEntriesMap::kHeapEntryPlaceholder = reinterpret_cast<HeapEntry*>(1); HeapEntriesMap::HeapEntriesMap() @@ -1682,12 +1711,18 @@ void HeapObjectsSet::SetTag(Object* obj, const char* tag) { } -HeapObject *const V8HeapExplorer::kInternalRootObject = +HeapObject* const V8HeapExplorer::kInternalRootObject = reinterpret_cast<HeapObject*>( static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId)); -HeapObject *const V8HeapExplorer::kGcRootsObject = +HeapObject* const V8HeapExplorer::kGcRootsObject = reinterpret_cast<HeapObject*>( static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId)); +HeapObject* const V8HeapExplorer::kFirstGcSubrootObject = + reinterpret_cast<HeapObject*>( + static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId)); +HeapObject* const V8HeapExplorer::kLastGcSubrootObject = + reinterpret_cast<HeapObject*>( + static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId)); V8HeapExplorer::V8HeapExplorer( @@ -1720,6 +1755,11 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, return snapshot_->AddRootEntry(children_count); } else if (object == kGcRootsObject) { return snapshot_->AddGcRootsEntry(children_count, retainers_count); + } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) { + return snapshot_->AddGcSubrootEntry( + GetGcSubrootOrder(object), + children_count, + retainers_count); } else if (object->IsJSGlobalObject()) { const char* tag = objects_tags_.GetTag(object); const char* name = collection_->names()->GetName( @@ -1783,6 +1823,18 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, : "", children_count, retainers_count); + } else if (object->IsGlobalContext()) { + return AddEntry(object, + HeapEntry::kHidden, + "system / GlobalContext", + children_count, + retainers_count); + } else if (object->IsContext()) { + return AddEntry(object, + HeapEntry::kHidden, + "system / Context", + children_count, + retainers_count); } else if (object->IsFixedArray() || object->IsFixedDoubleArray() || object->IsByteArray() || @@ -1822,9 +1874,38 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, } +class GcSubrootsEnumerator : public ObjectVisitor { + public: + GcSubrootsEnumerator( + SnapshotFillerInterface* filler, V8HeapExplorer* explorer) + : filler_(filler), + explorer_(explorer), + previous_object_count_(0), + object_count_(0) { + } + void VisitPointers(Object** start, Object** end) { + object_count_ += end - start; + } + void Synchronize(VisitorSynchronization::SyncTag tag) { + // Skip empty subroots. + if (previous_object_count_ != object_count_) { + previous_object_count_ = object_count_; + filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_); + } + } + private: + SnapshotFillerInterface* filler_; + V8HeapExplorer* explorer_; + intptr_t previous_object_count_; + intptr_t object_count_; +}; + + void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { filler->AddEntry(kInternalRootObject, this); filler->AddEntry(kGcRootsObject, this); + GcSubrootsEnumerator enumerator(filler, this); + heap_->IterateRoots(&enumerator, VISIT_ALL); } @@ -1843,12 +1924,13 @@ const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) { } -int V8HeapExplorer::EstimateObjectsCount() { - HeapIterator iterator(HeapIterator::kFilterUnreachable); +int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) { int objects_count = 0; - for (HeapObject* obj = iterator.next(); + for (HeapObject* obj = iterator->next(); obj != NULL; - obj = iterator.next(), ++objects_count) {} + obj = iterator->next()) { + objects_count++; + } return objects_count; } @@ -1921,6 +2003,7 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetPropertyReference( obj, entry, heap_->prototype_symbol(), proto_or_map, + NULL, JSFunction::kPrototypeOrInitialMapOffset); } else { SetPropertyReference( @@ -1935,10 +2018,17 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(js_fun, entry, "context", js_fun->unchecked_context(), JSFunction::kContextOffset); - TagObject(js_fun->literals(), "(function literals)"); + TagObject(js_fun->literals_or_bindings(), + "(function literals_or_bindings)"); SetInternalReference(js_fun, entry, - "literals", js_fun->literals(), + "literals_or_bindings", + js_fun->literals_or_bindings(), JSFunction::kLiteralsOffset); + for (int i = JSFunction::kNonWeakFieldsEndOffset; + i < JSFunction::kSize; + i += kPointerSize) { + SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i); + } } TagObject(js_obj->properties(), "(object properties)"); SetInternalReference(obj, entry, @@ -1954,6 +2044,10 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(obj, entry, 1, cs->first()); SetInternalReference(obj, entry, 2, cs->second()); } + if (obj->IsSlicedString()) { + SlicedString* ss = SlicedString::cast(obj); + SetInternalReference(obj, entry, "parent", ss->parent()); + } extract_indexed_refs = false; } else if (obj->IsGlobalContext()) { Context* context = Context::cast(obj); @@ -1961,8 +2055,14 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { "(context func. result caches)"); TagObject(context->normalized_map_cache(), "(context norm. map cache)"); TagObject(context->runtime_context(), "(runtime context)"); - TagObject(context->map_cache(), "(context map cache)"); TagObject(context->data(), "(context data)"); + for (int i = Context::FIRST_WEAK_SLOT; + i < Context::GLOBAL_CONTEXT_SLOTS; + ++i) { + SetWeakReference(obj, entry, + i, context->get(i), + FixedArray::OffsetOfElementAt(i)); + } } else if (obj->IsMap()) { Map* map = Map::cast(obj); SetInternalReference(obj, entry, @@ -1976,6 +2076,14 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { "descriptors", map->instance_descriptors(), Map::kInstanceDescriptorsOrBitField3Offset); } + if (map->prototype_transitions() != heap_->empty_fixed_array()) { + TagObject(map->prototype_transitions(), "(prototype transitions)"); + SetInternalReference(obj, + entry, + "prototype_transitions", + map->prototype_transitions(), + Map::kPrototypeTransitionsOffset); + } SetInternalReference(obj, entry, "code_cache", map->code_cache(), Map::kCodeCacheOffset); @@ -1997,6 +2105,9 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(obj, entry, "script", shared->script(), SharedFunctionInfo::kScriptOffset); + SetWeakReference(obj, entry, + 1, shared->initial_map(), + SharedFunctionInfo::kInitialMapOffset); } else if (obj->IsScript()) { Script* script = Script::cast(obj); SetInternalReference(obj, entry, @@ -2052,20 +2163,27 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry) { if (js_obj->IsJSFunction()) { - HandleScope hs; JSFunction* func = JSFunction::cast(js_obj); Context* context = func->context(); - ZoneScope zscope(Isolate::Current(), DELETE_ON_EXIT); - SerializedScopeInfo* serialized_scope_info = - context->closure()->shared()->scope_info(); - ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info); - int locals_number = zone_scope_info.NumberOfLocals(); - for (int i = 0; i < locals_number; ++i) { - String* local_name = *zone_scope_info.LocalName(i); - int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL); - if (idx >= 0 && idx < context->length()) { - SetClosureReference(js_obj, entry, local_name, context->get(idx)); - } + ScopeInfo* scope_info = context->closure()->shared()->scope_info(); + + // Add context allocated locals. + int context_locals = scope_info->ContextLocalCount(); + for (int i = 0; i < context_locals; ++i) { + String* local_name = scope_info->ContextLocalName(i); + int idx = Context::MIN_CONTEXT_SLOTS + i; + SetClosureReference(js_obj, entry, local_name, context->get(idx)); + } + + // Add function variable. + if (scope_info->HasFunctionName()) { + String* name = scope_info->FunctionName(); + int idx = Context::MIN_CONTEXT_SLOTS + context_locals; +#ifdef DEBUG + VariableMode mode; + ASSERT(idx == scope_info->FunctionContextSlotIndex(name, &mode)); +#endif + SetClosureReference(js_obj, entry, name, context->get(idx)); } } } @@ -2083,6 +2201,7 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, SetPropertyReference( js_obj, entry, descs->GetKey(i), js_obj->InObjectPropertyAt(index), + NULL, js_obj->GetInObjectPropertyOffset(index)); } else { SetPropertyReference( @@ -2096,7 +2215,29 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, js_obj, entry, descs->GetKey(i), descs->GetConstantFunction(i)); break; - default: ; + case CALLBACKS: { + Object* callback_obj = descs->GetValue(i); + if (callback_obj->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(callback_obj); + if (Object* getter = accessors->getter()) { + SetPropertyReference(js_obj, entry, descs->GetKey(i), + getter, "get-%s"); + } + if (Object* setter = accessors->setter()) { + SetPropertyReference(js_obj, entry, descs->GetKey(i), + setter, "set-%s"); + } + } + break; + } + case NORMAL: // only in slow mode + case HANDLER: // only in lookup results, not in descriptors + case INTERCEPTOR: // only in lookup results, not in descriptors + case MAP_TRANSITION: // we do not care about transitions here... + case ELEMENTS_TRANSITION: + case CONSTANT_TRANSITION: + case NULL_DESCRIPTOR: // ... and not about "holes" + break; } } } else { @@ -2161,15 +2302,16 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, String* V8HeapExplorer::GetConstructorName(JSObject* object) { - if (object->IsJSFunction()) return HEAP->closure_symbol(); + Heap* heap = object->GetHeap(); + if (object->IsJSFunction()) return heap->closure_symbol(); String* constructor_name = object->constructor_name(); - if (constructor_name == HEAP->Object_symbol()) { + if (constructor_name == heap->Object_symbol()) { // Look up an immediate "constructor" property, if it is a function, // return its name. This is for instances of binding objects, which // have prototype constructor type "Object". Object* constructor_prop = NULL; - LookupResult result; - object->LocalLookupRealNamedProperty(HEAP->constructor_symbol(), &result); + LookupResult result(heap->isolate()); + object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result); if (result.IsProperty()) { constructor_prop = result.GetLazyValue(); } @@ -2192,23 +2334,76 @@ HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { class RootsReferencesExtractor : public ObjectVisitor { + private: + struct IndexTag { + IndexTag(int index, VisitorSynchronization::SyncTag tag) + : index(index), tag(tag) { } + int index; + VisitorSynchronization::SyncTag tag; + }; + public: - explicit RootsReferencesExtractor(V8HeapExplorer* explorer) - : explorer_(explorer) { + RootsReferencesExtractor() + : collecting_all_references_(false), + previous_reference_count_(0) { } + void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p); + if (collecting_all_references_) { + for (Object** p = start; p < end; p++) all_references_.Add(*p); + } else { + for (Object** p = start; p < end; p++) strong_references_.Add(*p); + } + } + + void SetCollectingAllReferences() { collecting_all_references_ = true; } + + void FillReferences(V8HeapExplorer* explorer) { + ASSERT(strong_references_.length() <= all_references_.length()); + for (int i = 0; i < reference_tags_.length(); ++i) { + explorer->SetGcRootsReference(reference_tags_[i].tag); + } + int strong_index = 0, all_index = 0, tags_index = 0; + while (all_index < all_references_.length()) { + if (strong_index < strong_references_.length() && + strong_references_[strong_index] == all_references_[all_index]) { + explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, + false, + all_references_[all_index++]); + ++strong_index; + } else { + explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, + true, + all_references_[all_index++]); + } + if (reference_tags_[tags_index].index == all_index) ++tags_index; + } } + + void Synchronize(VisitorSynchronization::SyncTag tag) { + if (collecting_all_references_ && + previous_reference_count_ != all_references_.length()) { + previous_reference_count_ = all_references_.length(); + reference_tags_.Add(IndexTag(previous_reference_count_, tag)); + } + } + private: - V8HeapExplorer* explorer_; + bool collecting_all_references_; + List<Object*> strong_references_; + List<Object*> all_references_; + int previous_reference_count_; + List<IndexTag> reference_tags_; }; bool V8HeapExplorer::IterateAndExtractReferences( SnapshotFillerInterface* filler) { - filler_ = filler; HeapIterator iterator(HeapIterator::kFilterUnreachable); + + filler_ = filler; bool interrupted = false; + // Heap iteration with filtering must be finished in any case. for (HeapObject* obj = iterator.next(); obj != NULL; @@ -2223,8 +2418,11 @@ bool V8HeapExplorer::IterateAndExtractReferences( return false; } SetRootGcRootsReference(); - RootsReferencesExtractor extractor(this); + RootsReferencesExtractor extractor; + heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG); + extractor.SetCollectingAllReferences(); heap_->IterateRoots(&extractor, VISIT_ALL); + extractor.FillReferences(this); filler_ = NULL; return progress_->ProgressReport(false); } @@ -2314,19 +2512,45 @@ void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj, } +void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj, + int field_offset) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetIndexedReference(HeapGraphEdge::kWeak, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); + IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); + } +} + + void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent_entry, String* reference_name, Object* child_obj, + const char* name_format_string, int field_offset) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { HeapGraphEdge::Type type = reference_name->length() > 0 ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal; + const char* name = name_format_string != NULL ? + collection_->names()->GetFormatted( + name_format_string, + *reference_name->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL)) : + collection_->names()->GetName(reference_name); + filler_->SetNamedReference(type, parent_obj, parent_entry, - collection_->names()->GetName(reference_name), + name, child_obj, child_entry); IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); @@ -2368,12 +2592,21 @@ void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) { } -void V8HeapExplorer::SetGcRootsReference(Object* child_obj) { +void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + kGcRootsObject, snapshot_->gc_roots(), + GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag)); +} + + +void V8HeapExplorer::SetGcSubrootReference( + VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - kGcRootsObject, snapshot_->gc_roots(), + is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement, + GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag), child_obj, child_entry); } } @@ -2417,6 +2650,7 @@ class GlobalObjectsEnumerator : public ObjectVisitor { // Modifies heap. Must not be run during heap traversal. void V8HeapExplorer::TagGlobalObjects() { + HandleScope scope; Isolate* isolate = Isolate::Current(); GlobalObjectsEnumerator enumerator; isolate->global_handles()->IterateAllRoots(&enumerator); @@ -2427,6 +2661,7 @@ void V8HeapExplorer::TagGlobalObjects() { const char** urls = NewArray<const char*>(enumerator.count()); for (int i = 0, l = enumerator.count(); i < l; ++i) { urls[i] = NULL; + HandleScope scope; Handle<JSGlobalObject> global_obj = enumerator.at(i); Object* obj_document; if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) && @@ -2464,9 +2699,43 @@ class GlobalHandlesExtractor : public ObjectVisitor { NativeObjectsExplorer* explorer_; }; -HeapThing const NativeObjectsExplorer::kNativesRootObject = - reinterpret_cast<HeapThing>( - static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId)); + +class BasicHeapEntriesAllocator : public HeapEntriesAllocator { + public: + BasicHeapEntriesAllocator( + HeapSnapshot* snapshot, + HeapEntry::Type entries_type) + : snapshot_(snapshot), + collection_(snapshot_->collection()), + entries_type_(entries_type) { + } + virtual HeapEntry* AllocateEntry( + HeapThing ptr, int children_count, int retainers_count); + private: + HeapSnapshot* snapshot_; + HeapSnapshotsCollection* collection_; + HeapEntry::Type entries_type_; +}; + + +HeapEntry* BasicHeapEntriesAllocator::AllocateEntry( + HeapThing ptr, int children_count, int retainers_count) { + v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr); + intptr_t elements = info->GetElementCount(); + intptr_t size = info->GetSizeInBytes(); + return snapshot_->AddEntry( + entries_type_, + elements != -1 ? + collection_->names()->GetFormatted( + "%s / %" V8_PTR_PREFIX "d entries", + info->GetLabel(), + info->GetElementCount()) : + collection_->names()->GetCopy(info->GetLabel()), + HeapObjectsMap::GenerateId(info), + size != -1 ? static_cast<int>(size) : 0, + children_count, + retainers_count); +} NativeObjectsExplorer::NativeObjectsExplorer( @@ -2476,7 +2745,12 @@ NativeObjectsExplorer::NativeObjectsExplorer( progress_(progress), embedder_queried_(false), objects_by_info_(RetainedInfosMatch), + native_groups_(StringsMatch), filler_(NULL) { + synthetic_entries_allocator_ = + new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic); + native_entries_allocator_ = + new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative); } @@ -2491,37 +2765,15 @@ NativeObjectsExplorer::~NativeObjectsExplorer() { reinterpret_cast<List<HeapObject*>* >(p->value); delete objects; } -} - - -HeapEntry* NativeObjectsExplorer::AllocateEntry( - HeapThing ptr, int children_count, int retainers_count) { - if (ptr == kNativesRootObject) { - return snapshot_->AddNativesRootEntry(children_count, retainers_count); - } else { + for (HashMap::Entry* p = native_groups_.Start(); + p != NULL; + p = native_groups_.Next(p)) { v8::RetainedObjectInfo* info = - reinterpret_cast<v8::RetainedObjectInfo*>(ptr); - intptr_t elements = info->GetElementCount(); - intptr_t size = info->GetSizeInBytes(); - return snapshot_->AddEntry( - HeapEntry::kNative, - elements != -1 ? - collection_->names()->GetFormatted( - "%s / %" V8_PTR_PREFIX "d entries", - info->GetLabel(), - info->GetElementCount()) : - collection_->names()->GetCopy(info->GetLabel()), - HeapObjectsMap::GenerateId(info), - size != -1 ? static_cast<int>(size) : 0, - children_count, - retainers_count); + reinterpret_cast<v8::RetainedObjectInfo*>(p->value); + info->Dispose(); } -} - - -void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) { - if (EstimateObjectsCount() <= 0) return; - filler->AddEntry(kNativesRootObject, this); + delete synthetic_entries_allocator_; + delete native_entries_allocator_; } @@ -2556,6 +2808,29 @@ void NativeObjectsExplorer::FillRetainedObjects() { embedder_queried_ = true; } +void NativeObjectsExplorer::FillImplicitReferences() { + Isolate* isolate = Isolate::Current(); + List<ImplicitRefGroup*>* groups = + isolate->global_handles()->implicit_ref_groups(); + for (int i = 0; i < groups->length(); ++i) { + ImplicitRefGroup* group = groups->at(i); + HeapObject* parent = *group->parent_; + HeapEntry* parent_entry = + filler_->FindOrAddEntry(parent, native_entries_allocator_); + ASSERT(parent_entry != NULL); + Object*** children = group->children_; + for (size_t j = 0; j < group->length_; ++j) { + Object* child = *children[j]; + HeapEntry* child_entry = + filler_->FindOrAddEntry(child, native_entries_allocator_); + filler_->SetNamedReference( + HeapGraphEdge::kInternal, + parent, parent_entry, + "native", + child, child_entry); + } + } +} List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( v8::RetainedObjectInfo* info) { @@ -2572,34 +2847,82 @@ List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( bool NativeObjectsExplorer::IterateAndExtractReferences( SnapshotFillerInterface* filler) { - if (EstimateObjectsCount() <= 0) return true; filler_ = filler; FillRetainedObjects(); - for (HashMap::Entry* p = objects_by_info_.Start(); - p != NULL; - p = objects_by_info_.Next(p)) { - v8::RetainedObjectInfo* info = - reinterpret_cast<v8::RetainedObjectInfo*>(p->key); - SetNativeRootReference(info); - List<HeapObject*>* objects = - reinterpret_cast<List<HeapObject*>* >(p->value); - for (int i = 0; i < objects->length(); ++i) { - SetWrapperNativeReferences(objects->at(i), info); + FillImplicitReferences(); + if (EstimateObjectsCount() > 0) { + for (HashMap::Entry* p = objects_by_info_.Start(); + p != NULL; + p = objects_by_info_.Next(p)) { + v8::RetainedObjectInfo* info = + reinterpret_cast<v8::RetainedObjectInfo*>(p->key); + SetNativeRootReference(info); + List<HeapObject*>* objects = + reinterpret_cast<List<HeapObject*>* >(p->value); + for (int i = 0; i < objects->length(); ++i) { + SetWrapperNativeReferences(objects->at(i), info); + } } + SetRootNativeRootsReference(); } - SetRootNativesRootReference(); filler_ = NULL; return true; } +class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo { + public: + explicit NativeGroupRetainedObjectInfo(const char* label) + : disposed_(false), + hash_(reinterpret_cast<intptr_t>(label)), + label_(label) { + } + + virtual ~NativeGroupRetainedObjectInfo() {} + virtual void Dispose() { + CHECK(!disposed_); + disposed_ = true; + delete this; + } + virtual bool IsEquivalent(RetainedObjectInfo* other) { + return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel()); + } + virtual intptr_t GetHash() { return hash_; } + virtual const char* GetLabel() { return label_; } + + private: + bool disposed_; + intptr_t hash_; + const char* label_; +}; + + +NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo( + const char* label) { + const char* label_copy = collection_->names()->GetCopy(label); + uint32_t hash = HashSequentialString(label_copy, + static_cast<int>(strlen(label_copy)), + HEAP->HashSeed()); + HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy), + hash, true); + if (entry->value == NULL) + entry->value = new NativeGroupRetainedObjectInfo(label); + return static_cast<NativeGroupRetainedObjectInfo*>(entry->value); +} + + void NativeObjectsExplorer::SetNativeRootReference( v8::RetainedObjectInfo* info) { - HeapEntry* child_entry = filler_->FindOrAddEntry(info, this); + HeapEntry* child_entry = + filler_->FindOrAddEntry(info, native_entries_allocator_); ASSERT(child_entry != NULL); - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - kNativesRootObject, snapshot_->natives_root(), + NativeGroupRetainedObjectInfo* group_info = + FindOrAddGroupInfo(info->GetGroupLabel()); + HeapEntry* group_entry = + filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_); + filler_->SetNamedAutoIndexReference( + HeapGraphEdge::kInternal, + group_info, group_entry, info, child_entry); } @@ -2608,7 +2931,8 @@ void NativeObjectsExplorer::SetWrapperNativeReferences( HeapObject* wrapper, v8::RetainedObjectInfo* info) { HeapEntry* wrapper_entry = filler_->FindEntry(wrapper); ASSERT(wrapper_entry != NULL); - HeapEntry* info_entry = filler_->FindOrAddEntry(info, this); + HeapEntry* info_entry = + filler_->FindOrAddEntry(info, native_entries_allocator_); ASSERT(info_entry != NULL); filler_->SetNamedReference(HeapGraphEdge::kInternal, wrapper, wrapper_entry, @@ -2620,11 +2944,20 @@ void NativeObjectsExplorer::SetWrapperNativeReferences( } -void NativeObjectsExplorer::SetRootNativesRootReference() { - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - V8HeapExplorer::kInternalRootObject, snapshot_->root(), - kNativesRootObject, snapshot_->natives_root()); +void NativeObjectsExplorer::SetRootNativeRootsReference() { + for (HashMap::Entry* entry = native_groups_.Start(); + entry; + entry = native_groups_.Next(entry)) { + NativeGroupRetainedObjectInfo* group_info = + static_cast<NativeGroupRetainedObjectInfo*>(entry->value); + HeapEntry* group_entry = + filler_->FindOrAddEntry(group_info, native_entries_allocator_); + ASSERT(group_entry != NULL); + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + V8HeapExplorer::kInternalRootObject, snapshot_->root(), + group_info, group_entry); + } } @@ -2774,13 +3107,47 @@ class SnapshotFiller : public SnapshotFillerInterface { bool HeapSnapshotGenerator::GenerateSnapshot() { v8_heap_explorer_.TagGlobalObjects(); + // TODO(1562) Profiler assumes that any object that is in the heap after + // full GC is reachable from the root when computing dominators. + // This is not true for weakly reachable objects. + // As a temporary solution we call GC twice. + Isolate::Current()->heap()->CollectAllGarbage( + Heap::kMakeHeapIterableMask, + "HeapSnapshotGenerator::GenerateSnapshot"); + Isolate::Current()->heap()->CollectAllGarbage( + Heap::kMakeHeapIterableMask, + "HeapSnapshotGenerator::GenerateSnapshot"); + +#ifdef DEBUG + Heap* debug_heap = Isolate::Current()->heap(); + ASSERT(!debug_heap->old_data_space()->was_swept_conservatively()); + ASSERT(!debug_heap->old_pointer_space()->was_swept_conservatively()); + ASSERT(!debug_heap->code_space()->was_swept_conservatively()); + ASSERT(!debug_heap->cell_space()->was_swept_conservatively()); + ASSERT(!debug_heap->map_space()->was_swept_conservatively()); +#endif + + // The following code uses heap iterators, so we want the heap to be + // stable. It should follow TagGlobalObjects as that can allocate. AssertNoAllocation no_alloc; +#ifdef DEBUG + debug_heap->Verify(); +#endif + SetProgressTotal(4); // 2 passes + dominators + sizes. +#ifdef DEBUG + debug_heap->Verify(); +#endif + // Pass 1. Iterate heap contents to count entries and references. if (!CountEntriesAndReferences()) return false; +#ifdef DEBUG + debug_heap->Verify(); +#endif + // Allocate and fill entries in the snapshot, allocate references. snapshot_->AllocateEntries(entries_.entries_count(), entries_.total_children_count(), @@ -2818,8 +3185,9 @@ bool HeapSnapshotGenerator::ProgressReport(bool force) { void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { if (control_ == NULL) return; + HeapIterator iterator(HeapIterator::kFilterUnreachable); progress_total_ = ( - v8_heap_explorer_.EstimateObjectsCount() + + v8_heap_explorer_.EstimateObjectsCount(&iterator) + dom_explorer_.EstimateObjectsCount()) * iterations_count; progress_counter_ = 0; } @@ -2828,7 +3196,6 @@ void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { bool HeapSnapshotGenerator::CountEntriesAndReferences() { SnapshotCounter counter(&entries_); v8_heap_explorer_.AddRootEntries(&counter); - dom_explorer_.AddRootEntries(&counter); return v8_heap_explorer_.IterateAndExtractReferences(&counter) && dom_explorer_.IterateAndExtractReferences(&counter); @@ -2869,7 +3236,7 @@ void HeapSnapshotGenerator::FillReversePostorderIndexes( nodes_to_visit.RemoveLast(); } } - entries->Truncate(current_entry); + ASSERT_EQ(current_entry, entries->length()); } @@ -3149,7 +3516,8 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { writer_->AddNumber(edge->type()); writer_->AddCharacter(','); if (edge->type() == HeapGraphEdge::kElement - || edge->type() == HeapGraphEdge::kHidden) { + || edge->type() == HeapGraphEdge::kHidden + || edge->type() == HeapGraphEdge::kWeak) { writer_->AddNumber(edge->index()); } else { writer_->AddNumber(GetStringId(edge->name())); @@ -3210,7 +3578,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("closure") "," JSON_S("regexp") "," JSON_S("number") - "," JSON_S("native")) + "," JSON_S("native") + "," JSON_S("synthetic")) "," JSON_S("string") "," JSON_S("number") "," JSON_S("number") @@ -3229,7 +3598,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("property") "," JSON_S("internal") "," JSON_S("hidden") - "," JSON_S("shortcut")) + "," JSON_S("shortcut") + "," JSON_S("weak")) "," JSON_S("string_or_number") "," JSON_S("node")))))); #undef JSON_S diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index 0beb109e24..a0dea588fe 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -74,6 +74,8 @@ class StringsStorage { inline const char* GetFunctionName(const char* name); private: + static const int kMaxNameSize = 1024; + INLINE(static bool StringsMatch(void* key1, void* key2)) { return strcmp(reinterpret_cast<char*>(key1), reinterpret_cast<char*>(key2)) == 0; @@ -257,7 +259,7 @@ class CodeMap { typedef Address Key; typedef CodeEntryInfo Value; static const Key kNoKey; - static const Value kNoValue; + static const Value NoValue() { return CodeEntryInfo(NULL, 0); } static int Compare(const Key& a, const Key& b) { return a < b ? -1 : (a > b ? 1 : 0); } @@ -453,7 +455,8 @@ class HeapGraphEdge BASE_EMBEDDED { kProperty = v8::HeapGraphEdge::kProperty, kInternal = v8::HeapGraphEdge::kInternal, kHidden = v8::HeapGraphEdge::kHidden, - kShortcut = v8::HeapGraphEdge::kShortcut + kShortcut = v8::HeapGraphEdge::kShortcut, + kWeak = v8::HeapGraphEdge::kWeak }; HeapGraphEdge() { } @@ -463,7 +466,7 @@ class HeapGraphEdge BASE_EMBEDDED { Type type() { return static_cast<Type>(type_); } int index() { - ASSERT(type_ == kElement || type_ == kHidden); + ASSERT(type_ == kElement || type_ == kHidden || type_ == kWeak); return index_; } const char* name() { @@ -522,7 +525,8 @@ class HeapEntry BASE_EMBEDDED { kClosure = v8::HeapGraphNode::kClosure, kRegExp = v8::HeapGraphNode::kRegExp, kHeapNumber = v8::HeapGraphNode::kHeapNumber, - kNative = v8::HeapGraphNode::kNative + kNative = v8::HeapGraphNode::kNative, + kSynthetic = v8::HeapGraphNode::kSynthetic }; HeapEntry() { } @@ -550,7 +554,10 @@ class HeapEntry BASE_EMBEDDED { Vector<HeapGraphEdge*> retainers() { return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); } HeapEntry* dominator() { return dominator_; } - void set_dominator(HeapEntry* entry) { dominator_ = entry; } + void set_dominator(HeapEntry* entry) { + ASSERT(entry != NULL); + dominator_ = entry; + } void clear_paint() { painted_ = kUnpainted; } bool painted_reachable() { return painted_ == kPainted; } @@ -583,7 +590,8 @@ class HeapEntry BASE_EMBEDDED { int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); } int RetainedSize(bool exact); - void Print(int max_depth, int indent); + void Print( + const char* prefix, const char* edge_name, int max_depth, int indent); Handle<HeapObject> GetHeapObject(); @@ -656,6 +664,7 @@ class HeapSnapshot { HeapEntry* root() { return root_entry_; } HeapEntry* gc_roots() { return gc_roots_entry_; } HeapEntry* natives_root() { return natives_root_entry_; } + HeapEntry* gc_subroot(int index) { return gc_subroot_entries_[index]; } List<HeapEntry*>* entries() { return &entries_; } int raw_entries_size() { return raw_entries_size_; } @@ -669,6 +678,9 @@ class HeapSnapshot { int retainers_count); HeapEntry* AddRootEntry(int children_count); HeapEntry* AddGcRootsEntry(int children_count, int retainers_count); + HeapEntry* AddGcSubrootEntry(int tag, + int children_count, + int retainers_count); HeapEntry* AddNativesRootEntry(int children_count, int retainers_count); void ClearPaint(); HeapEntry* GetEntryById(uint64_t id); @@ -690,6 +702,7 @@ class HeapSnapshot { HeapEntry* root_entry_; HeapEntry* gc_roots_entry_; HeapEntry* natives_root_entry_; + HeapEntry* gc_subroot_entries_[VisitorSynchronization::kNumberOfSyncTags]; char* raw_entries_; List<HeapEntry*> entries_; bool entries_sorted_; @@ -711,10 +724,13 @@ class HeapObjectsMap { void MoveObject(Address from, Address to); static uint64_t GenerateId(v8::RetainedObjectInfo* info); + static inline uint64_t GetNthGcSubrootId(int delta); + static const int kObjectIdStep = 2; static const uint64_t kInternalRootObjectId; static const uint64_t kGcRootsObjectId; static const uint64_t kNativesRootObjectId; + static const uint64_t kGcRootsFirstSubrootId; static const uint64_t kFirstAvailableObjectId; private: @@ -819,7 +835,7 @@ class HeapEntriesMap { int total_children_count() { return total_children_count_; } int total_retainers_count() { return total_retainers_count_; } - static HeapEntry *const kHeapEntryPlaceholder; + static HeapEntry* const kHeapEntryPlaceholder; private: struct EntryInfo { @@ -922,7 +938,7 @@ class V8HeapExplorer : public HeapEntriesAllocator { virtual HeapEntry* AllocateEntry( HeapThing ptr, int children_count, int retainers_count); void AddRootEntries(SnapshotFillerInterface* filler); - int EstimateObjectsCount(); + int EstimateObjectsCount(HeapIterator* iterator); bool IterateAndExtractReferences(SnapshotFillerInterface* filler); void TagGlobalObjects(); @@ -966,10 +982,16 @@ class V8HeapExplorer : public HeapEntriesAllocator { HeapEntry* parent, int index, Object* child); + void SetWeakReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj, + int field_offset); void SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, Object* child, + const char* name_format_string = NULL, int field_offset = -1); void SetPropertyShortcutReference(HeapObject* parent_obj, HeapEntry* parent, @@ -977,11 +999,16 @@ class V8HeapExplorer : public HeapEntriesAllocator { Object* child); void SetRootShortcutReference(Object* child); void SetRootGcRootsReference(); - void SetGcRootsReference(Object* child); + void SetGcRootsReference(VisitorSynchronization::SyncTag tag); + void SetGcSubrootReference( + VisitorSynchronization::SyncTag tag, bool is_weak, Object* child); void TagObject(Object* obj, const char* tag); HeapEntry* GetEntry(Object* obj); + static inline HeapObject* GetNthGcSubrootObject(int delta); + static inline int GetGcSubrootOrder(HeapObject* subroot); + Heap* heap_; HeapSnapshot* snapshot_; HeapSnapshotsCollection* collection_; @@ -990,31 +1017,36 @@ class V8HeapExplorer : public HeapEntriesAllocator { HeapObjectsSet objects_tags_; static HeapObject* const kGcRootsObject; + static HeapObject* const kFirstGcSubrootObject; + static HeapObject* const kLastGcSubrootObject; friend class IndexedReferencesExtractor; + friend class GcSubrootsEnumerator; friend class RootsReferencesExtractor; DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer); }; +class NativeGroupRetainedObjectInfo; + + // An implementation of retained native objects extractor. -class NativeObjectsExplorer : public HeapEntriesAllocator { +class NativeObjectsExplorer { public: NativeObjectsExplorer(HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress); virtual ~NativeObjectsExplorer(); - virtual HeapEntry* AllocateEntry( - HeapThing ptr, int children_count, int retainers_count); void AddRootEntries(SnapshotFillerInterface* filler); int EstimateObjectsCount(); bool IterateAndExtractReferences(SnapshotFillerInterface* filler); private: void FillRetainedObjects(); + void FillImplicitReferences(); List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info); void SetNativeRootReference(v8::RetainedObjectInfo* info); - void SetRootNativesRootReference(); + void SetRootNativeRootsReference(); void SetWrapperNativeReferences(HeapObject* wrapper, v8::RetainedObjectInfo* info); void VisitSubtreeWrapper(Object** p, uint16_t class_id); @@ -1028,6 +1060,12 @@ class NativeObjectsExplorer : public HeapEntriesAllocator { (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent( reinterpret_cast<v8::RetainedObjectInfo*>(key2)); } + INLINE(static bool StringsMatch(void* key1, void* key2)) { + return strcmp(reinterpret_cast<char*>(key1), + reinterpret_cast<char*>(key2)) == 0; + } + + NativeGroupRetainedObjectInfo* FindOrAddGroupInfo(const char* label); HeapSnapshot* snapshot_; HeapSnapshotsCollection* collection_; @@ -1036,6 +1074,9 @@ class NativeObjectsExplorer : public HeapEntriesAllocator { HeapObjectsSet in_groups_; // RetainedObjectInfo* -> List<HeapObject*>* HashMap objects_by_info_; + HashMap native_groups_; + HeapEntriesAllocator* synthetic_entries_allocator_; + HeapEntriesAllocator* native_entries_allocator_; // Used during references extraction. SnapshotFillerInterface* filler_; diff --git a/deps/v8/src/property-details.h b/deps/v8/src/property-details.h new file mode 100644 index 0000000000..81f521a627 --- /dev/null +++ b/deps/v8/src/property-details.h @@ -0,0 +1,152 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_PROPERTY_DETAILS_H_ +#define V8_PROPERTY_DETAILS_H_ + +#include "../include/v8.h" +#include "allocation.h" +#include "utils.h" + +// Ecma-262 3rd 8.6.1 +enum PropertyAttributes { + NONE = v8::None, + READ_ONLY = v8::ReadOnly, + DONT_ENUM = v8::DontEnum, + DONT_DELETE = v8::DontDelete, + ABSENT = 16 // Used in runtime to indicate a property is absent. + // ABSENT can never be stored in or returned from a descriptor's attributes + // bitfield. It is only used as a return value meaning the attributes of + // a non-existent property. +}; + + +namespace v8 { +namespace internal { + +class Smi; + +// Type of properties. +// Order of properties is significant. +// Must fit in the BitField PropertyDetails::TypeField. +// A copy of this is in mirror-debugger.js. +enum PropertyType { + NORMAL = 0, // only in slow mode + FIELD = 1, // only in fast mode + CONSTANT_FUNCTION = 2, // only in fast mode + CALLBACKS = 3, + HANDLER = 4, // only in lookup results, not in descriptors + INTERCEPTOR = 5, // only in lookup results, not in descriptors + // All properties before MAP_TRANSITION are real. + MAP_TRANSITION = 6, // only in fast mode + ELEMENTS_TRANSITION = 7, + CONSTANT_TRANSITION = 8, // only in fast mode + NULL_DESCRIPTOR = 9, // only in fast mode + // There are no IC stubs for NULL_DESCRIPTORS. Therefore, + // NULL_DESCRIPTOR can be used as the type flag for IC stubs for + // nonexistent properties. + NONEXISTENT = NULL_DESCRIPTOR +}; + + +inline bool IsRealProperty(PropertyType type) { + switch (type) { + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case CALLBACKS: + case HANDLER: + case INTERCEPTOR: + return true; + case MAP_TRANSITION: + case ELEMENTS_TRANSITION: + case CONSTANT_TRANSITION: + case NULL_DESCRIPTOR: + return false; + } + UNREACHABLE(); // keep the compiler happy + return false; +} + + +// PropertyDetails captures type and attributes for a property. +// They are used both in property dictionaries and instance descriptors. +class PropertyDetails BASE_EMBEDDED { + public: + PropertyDetails(PropertyAttributes attributes, + PropertyType type, + int index = 0) { + ASSERT(TypeField::is_valid(type)); + ASSERT(AttributesField::is_valid(attributes)); + ASSERT(StorageField::is_valid(index)); + + value_ = TypeField::encode(type) + | AttributesField::encode(attributes) + | StorageField::encode(index); + + ASSERT(type == this->type()); + ASSERT(attributes == this->attributes()); + ASSERT(index == this->index()); + } + + // Conversion for storing details as Object*. + explicit inline PropertyDetails(Smi* smi); + inline Smi* AsSmi(); + + PropertyType type() { return TypeField::decode(value_); } + + PropertyAttributes attributes() { return AttributesField::decode(value_); } + + int index() { return StorageField::decode(value_); } + + inline PropertyDetails AsDeleted(); + + static bool IsValidIndex(int index) { + return StorageField::is_valid(index); + } + + bool IsReadOnly() { return (attributes() & READ_ONLY) != 0; } + bool IsDontDelete() { return (attributes() & DONT_DELETE) != 0; } + bool IsDontEnum() { return (attributes() & DONT_ENUM) != 0; } + bool IsDeleted() { return DeletedField::decode(value_) != 0;} + + // Bit fields in value_ (type, shift, size). Must be public so the + // constants can be embedded in generated code. + class TypeField: public BitField<PropertyType, 0, 4> {}; + class AttributesField: public BitField<PropertyAttributes, 4, 3> {}; + class DeletedField: public BitField<uint32_t, 7, 1> {}; + class StorageField: public BitField<uint32_t, 8, 32-8> {}; + + static const int kInitialIndex = 1; + + private: + uint32_t value_; +}; + +} } // namespace v8::internal + +#endif // V8_PROPERTY_DETAILS_H_ diff --git a/deps/v8/src/property.cc b/deps/v8/src/property.cc index 7cc2df5a3c..78f237d6c7 100644 --- a/deps/v8/src/property.cc +++ b/deps/v8/src/property.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -31,6 +31,15 @@ namespace v8 { namespace internal { +void LookupResult::Iterate(ObjectVisitor* visitor) { + LookupResult* current = this; // Could be NULL. + while (current != NULL) { + visitor->VisitPointer(BitCast<Object**>(¤t->holder_)); + current = current->next_; + } +} + + #ifdef OBJECT_PRINT void LookupResult::Print(FILE* out) { if (!IsFound()) { @@ -82,6 +91,9 @@ void LookupResult::Print(FILE* out) { break; case CONSTANT_TRANSITION: PrintF(out, " -type = constant property transition\n"); + PrintF(out, " -map:\n"); + GetTransitionMap()->Print(out); + PrintF(out, "\n"); break; case NULL_DESCRIPTOR: PrintF(out, " =type = null descriptor\n"); @@ -102,4 +114,28 @@ void Descriptor::Print(FILE* out) { #endif +bool Descriptor::ContainsTransition() { + switch (details_.type()) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + case ELEMENTS_TRANSITION: + return true; + case CALLBACKS: { + if (!value_->IsAccessorPair()) return false; + AccessorPair* accessors = AccessorPair::cast(value_); + return accessors->getter()->IsMap() || accessors->setter()->IsMap(); + } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + return false; + } + UNREACHABLE(); // Keep the compiler happy. + return false; +} + + } } // namespace v8::internal diff --git a/deps/v8/src/property.h b/deps/v8/src/property.h index e7d9fc5345..d5efb7f351 100644 --- a/deps/v8/src/property.h +++ b/deps/v8/src/property.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -71,6 +71,8 @@ class Descriptor BASE_EMBEDDED { details_ = PropertyDetails(details_.attributes(), details_.type(), index); } + bool ContainsTransition(); + private: String* key_; Object* value_; @@ -115,11 +117,9 @@ class MapTransitionDescriptor: public Descriptor { class ElementsTransitionDescriptor: public Descriptor { public: ElementsTransitionDescriptor(String* key, - Map* map, - ElementsKind elements_kind) - : Descriptor(key, map, PropertyDetails(NONE, - ELEMENTS_TRANSITION, - elements_kind)) { } + Object* map_or_array) + : Descriptor(key, map_or_array, PropertyDetails(NONE, + ELEMENTS_TRANSITION)) { } }; // Marks a field name in a map so that adding the field is guaranteed @@ -166,10 +166,20 @@ class CallbacksDescriptor: public Descriptor { class LookupResult BASE_EMBEDDED { public: - LookupResult() - : lookup_type_(NOT_FOUND), + explicit LookupResult(Isolate* isolate) + : isolate_(isolate), + next_(isolate->top_lookup_result()), + lookup_type_(NOT_FOUND), + holder_(NULL), cacheable_(true), - details_(NONE, NORMAL) {} + details_(NONE, NORMAL) { + isolate->SetTopLookupResult(this); + } + + ~LookupResult() { + ASSERT(isolate_->top_lookup_result() == this); + isolate_->SetTopLookupResult(next_); + } void DescriptorResult(JSObject* holder, PropertyDetails details, int number) { lookup_type_ = DESCRIPTOR_TYPE; @@ -202,9 +212,9 @@ class LookupResult BASE_EMBEDDED { number_ = entry; } - void HandlerResult() { + void HandlerResult(JSProxy* proxy) { lookup_type_ = HANDLER_TYPE; - holder_ = NULL; + holder_ = proxy; details_ = PropertyDetails(NONE, HANDLER); cacheable_ = false; } @@ -217,11 +227,17 @@ class LookupResult BASE_EMBEDDED { void NotFound() { lookup_type_ = NOT_FOUND; + holder_ = NULL; } JSObject* holder() { ASSERT(IsFound()); - return holder_; + return JSObject::cast(holder_); + } + + JSProxy* proxy() { + ASSERT(IsFound()); + return JSProxy::cast(holder_); } PropertyType type() { @@ -248,12 +264,7 @@ class LookupResult BASE_EMBEDDED { // Is the result is a property excluding transitions and the null // descriptor? bool IsProperty() { - return IsFound() && (type() < FIRST_PHANTOM_PROPERTY_TYPE); - } - - // Is the result a property or a transition? - bool IsPropertyOrTransition() { - return IsFound() && (type() != NULL_DESCRIPTOR); + return IsFound() && IsRealProperty(GetPropertyDetails().type()); } bool IsCacheable() { return cacheable_; } @@ -278,10 +289,12 @@ class LookupResult BASE_EMBEDDED { } } + Map* GetTransitionMap() { ASSERT(lookup_type_ == DESCRIPTOR_TYPE); - ASSERT(type() == MAP_TRANSITION || type() == CONSTANT_TRANSITION || - type() == ELEMENTS_TRANSITION); + ASSERT(type() == MAP_TRANSITION || + type() == ELEMENTS_TRANSITION || + type() == CONSTANT_TRANSITION); return Map::cast(GetValue()); } @@ -343,7 +356,12 @@ class LookupResult BASE_EMBEDDED { return holder()->GetNormalizedProperty(this); } + void Iterate(ObjectVisitor* visitor); + private: + Isolate* isolate_; + LookupResult* next_; + // Where did we find the result; enum { NOT_FOUND, @@ -354,7 +372,7 @@ class LookupResult BASE_EMBEDDED { CONSTANT_TYPE } lookup_type_; - JSObject* holder_; + JSReceiver* holder_; int number_; bool cacheable_; PropertyDetails details_; diff --git a/deps/v8/src/proxy.js b/deps/v8/src/proxy.js index 4e44cd4ef3..3cd467faf2 100644 --- a/deps/v8/src/proxy.js +++ b/deps/v8/src/proxy.js @@ -32,7 +32,10 @@ var $Proxy = global.Proxy $Proxy.create = function(handler, proto) { if (!IS_SPEC_OBJECT(handler)) throw MakeTypeError("handler_non_object", ["create"]) - if (!IS_SPEC_OBJECT(proto)) proto = null // Mozilla does this... + if (IS_UNDEFINED(proto)) + proto = null + else if (!(IS_SPEC_OBJECT(proto) || proto === null)) + throw MakeTypeError("proto_non_object", ["create"]) return %CreateJSProxy(handler, proto) } @@ -42,8 +45,14 @@ $Proxy.createFunction = function(handler, callTrap, constructTrap) { if (!IS_SPEC_FUNCTION(callTrap)) throw MakeTypeError("trap_function_expected", ["createFunction", "call"]) if (IS_UNDEFINED(constructTrap)) { - constructTrap = callTrap - } else if (!IS_SPEC_FUNCTION(constructTrap)) { + constructTrap = DerivedConstructTrap(callTrap) + } else if (IS_SPEC_FUNCTION(constructTrap)) { + // Make sure the trap receives 'undefined' as this. + var construct = constructTrap + constructTrap = function() { + return %Apply(construct, void 0, arguments, 0, %_ArgumentsLength()); + } + } else { throw MakeTypeError("trap_function_expected", ["createFunction", "construct"]) } @@ -57,6 +66,17 @@ $Proxy.createFunction = function(handler, callTrap, constructTrap) { // Builtins //////////////////////////////////////////////////////////////////////////////// +function DerivedConstructTrap(callTrap) { + return function() { + var proto = this.prototype + if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype + var obj = new $Object() + obj.__proto__ = proto + var result = %Apply(callTrap, obj, arguments, 0, %_ArgumentsLength()); + return IS_SPEC_OBJECT(result) ? result : obj + } +} + function DelegateCallAndConstruct(callTrap, constructTrap) { return function() { return %Apply(%_IsConstructCall() ? constructTrap : callTrap, @@ -136,9 +156,32 @@ function DerivedKeysTrap() { var enumerableNames = [] for (var i = 0, count = 0; i < names.length; ++i) { var name = names[i] - if (this.getOwnPropertyDescriptor(TO_STRING_INLINE(name)).enumerable) { + var desc = this.getOwnPropertyDescriptor(TO_STRING_INLINE(name)) + if (!IS_UNDEFINED(desc) && desc.enumerable) { enumerableNames[count++] = names[i] } } return enumerableNames } + +function DerivedEnumerateTrap() { + var names = this.getPropertyNames() + var enumerableNames = [] + for (var i = 0, count = 0; i < names.length; ++i) { + var name = names[i] + var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name)) + if (!IS_UNDEFINED(desc) && desc.enumerable) { + enumerableNames[count++] = names[i] + } + } + return enumerableNames +} + +function ProxyEnumerate(proxy) { + var handler = %GetHandler(proxy) + if (IS_UNDEFINED(handler.enumerate)) { + return %Apply(DerivedEnumerateTrap, handler, [], 0, 0) + } else { + return ToStringArray(handler.enumerate(), "enumerate") + } +} diff --git a/deps/v8/src/regexp-macro-assembler-tracer.cc b/deps/v8/src/regexp-macro-assembler-tracer.cc index b32d71dba5..f8432784f2 100644 --- a/deps/v8/src/regexp-macro-assembler-tracer.cc +++ b/deps/v8/src/regexp-macro-assembler-tracer.cc @@ -37,8 +37,8 @@ RegExpMacroAssemblerTracer::RegExpMacroAssemblerTracer( RegExpMacroAssembler* assembler) : assembler_(assembler) { unsigned int type = assembler->Implementation(); - ASSERT(type < 4); - const char* impl_names[4] = {"IA32", "ARM", "X64", "Bytecode"}; + ASSERT(type < 5); + const char* impl_names[] = {"IA32", "ARM", "MIPS", "X64", "Bytecode"}; PrintF("RegExpMacroAssembler%s();\n", impl_names[type]); } diff --git a/deps/v8/src/regexp-macro-assembler.cc b/deps/v8/src/regexp-macro-assembler.cc index f91ea9348f..b6fb3c5214 100644 --- a/deps/v8/src/regexp-macro-assembler.cc +++ b/deps/v8/src/regexp-macro-assembler.cc @@ -81,7 +81,7 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition( if (subject->IsAsciiRepresentation()) { const byte* address; if (StringShape(subject).IsExternal()) { - const char* data = ExternalAsciiString::cast(subject)->resource()->data(); + const char* data = ExternalAsciiString::cast(subject)->GetChars(); address = reinterpret_cast<const byte*>(data); } else { ASSERT(subject->IsSeqAsciiString()); @@ -92,7 +92,7 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition( } const uc16* data; if (StringShape(subject).IsExternal()) { - data = ExternalTwoByteString::cast(subject)->resource()->data(); + data = ExternalTwoByteString::cast(subject)->GetChars(); } else { ASSERT(subject->IsSeqTwoByteString()); data = SeqTwoByteString::cast(subject)->GetChars(); @@ -133,7 +133,7 @@ NativeRegExpMacroAssembler::Result NativeRegExpMacroAssembler::Match( subject_ptr = slice->parent(); slice_offset = slice->offset(); } - // Ensure that an underlying string has the same ascii-ness. + // Ensure that an underlying string has the same ASCII-ness. bool is_ascii = subject_ptr->IsAsciiRepresentation(); ASSERT(subject_ptr->IsExternalString() || subject_ptr->IsSeqString()); // String is now either Sequential or External diff --git a/deps/v8/src/regexp.js b/deps/v8/src/regexp.js index 38d4496153..00dd7f15b7 100644 --- a/deps/v8/src/regexp.js +++ b/deps/v8/src/regexp.js @@ -95,12 +95,11 @@ function RegExpConstructor(pattern, flags) { } } - // Deprecated RegExp.prototype.compile method. We behave like the constructor // were called again. In SpiderMonkey, this method returns the regexp object. // In JSC, it returns undefined. For compatibility with JSC, we match their // behavior. -function CompileRegExp(pattern, flags) { +function RegExpCompile(pattern, flags) { // Both JSC and SpiderMonkey treat a missing pattern argument as the // empty subject string, and an actual undefined value passed as the // pattern as the string 'undefined'. Note that JSC is inconsistent @@ -108,6 +107,11 @@ function CompileRegExp(pattern, flags) { // RegExp.prototype.compile and in the constructor, where they are // the empty string. For compatibility with JSC, we match their // behavior. + if (this == $RegExp.prototype) { + // We don't allow recompiling RegExp.prototype. + throw MakeTypeError('incompatible_method_receiver', + ['RegExp.prototype.compile', this]); + } if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { DoConstructRegExp(this, 'undefined', flags); } else { @@ -170,13 +174,6 @@ function RegExpExec(string) { ['RegExp.prototype.exec', this]); } - if (%_ArgumentsLength() === 0) { - var regExpInput = LAST_INPUT(lastMatchInfo); - if (IS_UNDEFINED(regExpInput)) { - throw MakeError('no_input_to_regexp', [this]); - } - string = regExpInput; - } string = TO_STRING_INLINE(string); var lastIndex = this.lastIndex; @@ -225,14 +222,6 @@ function RegExpTest(string) { throw MakeTypeError('incompatible_method_receiver', ['RegExp.prototype.test', this]); } - if (%_ArgumentsLength() == 0) { - var regExpInput = LAST_INPUT(lastMatchInfo); - if (IS_UNDEFINED(regExpInput)) { - throw MakeError('no_input_to_regexp', [this]); - } - string = regExpInput; - } - string = TO_STRING_INLINE(string); var lastIndex = this.lastIndex; @@ -408,7 +397,6 @@ var lastMatchInfoOverride = null; function SetUpRegExp() { %CheckIsBootstrapping(); %FunctionSetInstanceClassName($RegExp, 'RegExp'); - %FunctionSetPrototype($RegExp, new $Object()); %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM); %SetCode($RegExp, RegExpConstructor); @@ -416,7 +404,7 @@ function SetUpRegExp() { "exec", RegExpExec, "test", RegExpTest, "toString", RegExpToString, - "compile", CompileRegExp + "compile", RegExpCompile )); // The length of compile is 1 in SpiderMonkey. @@ -431,56 +419,73 @@ function SetUpRegExp() { } function RegExpSetInput(string) { LAST_INPUT(lastMatchInfo) = ToString(string); - }; + } %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); - %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, + DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, + DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, + DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, + DONT_ENUM | DONT_DELETE); // The properties multiline and $* are aliases for each other. When this // value is set in SpiderMonkey, the value it is set to is coerced to a // boolean. We mimic that behavior with a slight difference: in SpiderMonkey // the value of the expression 'RegExp.multiline = null' (for instance) is the - // boolean false (ie, the value after coercion), while in V8 it is the value - // null (ie, the value before coercion). + // boolean false (i.e., the value after coercion), while in V8 it is the value + // null (i.e., the value before coercion). // Getter and setter for multiline. var multiline = false; - function RegExpGetMultiline() { return multiline; }; - function RegExpSetMultiline(flag) { multiline = flag ? true : false; }; + function RegExpGetMultiline() { return multiline; } + function RegExpSetMultiline(flag) { multiline = flag ? true : false; } - %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE); - %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE); - %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, + DONT_DELETE); + %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, + DONT_DELETE); + %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, + DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, + DONT_ENUM | DONT_DELETE); function NoOpSetter(ignored) {} // Static properties set by a successful match. - %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE); + %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, + DONT_DELETE); %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, + DONT_ENUM | DONT_DELETE); %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE); + %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, + DONT_DELETE); %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, + DONT_ENUM | DONT_DELETE); %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE); + %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, + DONT_DELETE); %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, + DONT_ENUM | DONT_DELETE); %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE); + %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, + DONT_DELETE); %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE); + %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, + DONT_ENUM | DONT_DELETE); %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); for (var i = 1; i < 10; ++i) { - %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE); + %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), + DONT_DELETE); %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE); } } diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc index 3d4c2dcc12..8308792baa 100644 --- a/deps/v8/src/rewriter.cc +++ b/deps/v8/src/rewriter.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -42,12 +42,18 @@ class Processor: public AstVisitor { : result_(result), result_assigned_(false), is_set_(false), - in_try_(false) { - } + in_try_(false), + factory_(isolate()) { } + + virtual ~Processor() { } void Process(ZoneList<Statement*>* statements); bool result_assigned() const { return result_assigned_; } + AstNodeFactory<AstNullVisitor>* factory() { + return &factory_; + } + private: Variable* result_; @@ -64,15 +70,13 @@ class Processor: public AstVisitor { bool is_set_; bool in_try_; + AstNodeFactory<AstNullVisitor> factory_; + Expression* SetResult(Expression* value) { result_assigned_ = true; - Zone* zone = isolate()->zone(); - VariableProxy* result_proxy = new(zone) VariableProxy(isolate(), result_); - return new(zone) Assignment(isolate(), - Token::ASSIGN, - result_proxy, - value, - RelocInfo::kNoPosition); + VariableProxy* result_proxy = factory()->NewVariableProxy(result_); + return factory()->NewAssignment( + Token::ASSIGN, result_proxy, value, RelocInfo::kNoPosition); } // Node visitors. @@ -205,7 +209,12 @@ void Processor::VisitWithStatement(WithStatement* node) { // Do nothing: -void Processor::VisitDeclaration(Declaration* node) {} +void Processor::VisitVariableDeclaration(VariableDeclaration* node) {} +void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {} +void Processor::VisitModuleLiteral(ModuleLiteral* node) {} +void Processor::VisitModuleVariable(ModuleVariable* node) {} +void Processor::VisitModulePath(ModulePath* node) {} +void Processor::VisitModuleUrl(ModuleUrl* node) {} void Processor::VisitEmptyStatement(EmptyStatement* node) {} void Processor::VisitReturnStatement(ReturnStatement* node) {} void Processor::VisitDebuggerStatement(DebuggerStatement* node) {} @@ -236,10 +245,21 @@ bool Rewriter::Rewrite(CompilationInfo* info) { if (processor.HasStackOverflow()) return false; if (processor.result_assigned()) { - Isolate* isolate = info->isolate(); - Zone* zone = isolate->zone(); - VariableProxy* result_proxy = new(zone) VariableProxy(isolate, result); - body->Add(new(zone) ReturnStatement(result_proxy)); + ASSERT(function->end_position() != RelocInfo::kNoPosition); + // Set the position of the assignment statement one character past the + // source code, such that it definitely is not in the source code range + // of an immediate inner scope. For example in + // eval('with ({x:1}) x = 1'); + // the end position of the function generated for executing the eval code + // coincides with the end of the with scope which is the position of '1'. + int position = function->end_position(); + VariableProxy* result_proxy = processor.factory()->NewVariableProxy( + result->name(), false, position); + result_proxy->BindTo(result); + Statement* result_statement = + processor.factory()->NewReturnStatement(result_proxy); + result_statement->set_statement_pos(position); + body->Add(result_statement); } } diff --git a/deps/v8/src/runtime-profiler.cc b/deps/v8/src/runtime-profiler.cc index 26d8846107..3e719cd3e1 100644 --- a/deps/v8/src/runtime-profiler.cc +++ b/deps/v8/src/runtime-profiler.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -35,6 +35,7 @@ #include "deoptimizer.h" #include "execution.h" #include "global-handles.h" +#include "isolate-inl.h" #include "mark-compact.h" #include "platform.h" #include "scopeinfo.h" @@ -45,6 +46,8 @@ namespace internal { // Optimization sampler constants. static const int kSamplerFrameCount = 2; + +// Constants for statistical profiler. static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 }; static const int kSamplerTicksBetweenThresholdAdjustment = 32; @@ -57,6 +60,16 @@ static const int kSamplerThresholdSizeFactorInit = 3; static const int kSizeLimit = 1500; +// Constants for counter based profiler. + +// Number of times a function has to be seen on the stack before it is +// optimized. +static const int kProfilerTicksBeforeOptimization = 2; + +// Maximum size in bytes of generated code for a function to be optimized +// the very first time it is seen on the stack. +static const int kMaxSizeEarlyOpt = 500; + Atomic32 RuntimeProfiler::state_ = 0; // TODO(isolates): Create the semaphore lazily and clean it up when no @@ -64,7 +77,7 @@ Atomic32 RuntimeProfiler::state_ = 0; Semaphore* RuntimeProfiler::semaphore_ = OS::CreateSemaphore(0); #ifdef DEBUG -bool RuntimeProfiler::has_been_globally_setup_ = false; +bool RuntimeProfiler::has_been_globally_set_up_ = false; #endif bool RuntimeProfiler::enabled_ = false; @@ -81,21 +94,21 @@ RuntimeProfiler::RuntimeProfiler(Isolate* isolate) void RuntimeProfiler::GlobalSetup() { - ASSERT(!has_been_globally_setup_); + ASSERT(!has_been_globally_set_up_); enabled_ = V8::UseCrankshaft() && FLAG_opt; #ifdef DEBUG - has_been_globally_setup_ = true; + has_been_globally_set_up_ = true; #endif } -void RuntimeProfiler::Optimize(JSFunction* function) { +void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) { ASSERT(function->IsOptimizable()); if (FLAG_trace_opt) { PrintF("[marking "); function->PrintName(); PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address())); - PrintF(" for recompilation"); + PrintF(" for recompilation, reason: %s", reason); PrintF("]\n"); } @@ -135,14 +148,13 @@ void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) { // Get the stack check stub code object to match against. We aren't // prepared to generate it, but we don't expect to have to. StackCheckStub check_stub; - Object* check_code; - MaybeObject* maybe_check_code = check_stub.TryGetCode(); - if (maybe_check_code->ToObject(&check_code)) { + Code* stack_check_code = NULL; + if (check_stub.FindCodeInCache(&stack_check_code)) { Code* replacement_code = isolate_->builtins()->builtin(Builtins::kOnStackReplacement); Code* unoptimized_code = shared->code(); Deoptimizer::PatchStackCheckCode(unoptimized_code, - Code::cast(check_code), + stack_check_code, replacement_code); } } @@ -192,17 +204,19 @@ void RuntimeProfiler::OptimizeNow() { JavaScriptFrame* frame = it.frame(); JSFunction* function = JSFunction::cast(frame->function()); - // Adjust threshold each time we have processed - // a certain number of ticks. - if (sampler_ticks_until_threshold_adjustment_ > 0) { - sampler_ticks_until_threshold_adjustment_--; - if (sampler_ticks_until_threshold_adjustment_ <= 0) { - // If the threshold is not already at the minimum - // modify and reset the ticks until next adjustment. - if (sampler_threshold_ > kSamplerThresholdMin) { - sampler_threshold_ -= kSamplerThresholdDelta; - sampler_ticks_until_threshold_adjustment_ = - kSamplerTicksBetweenThresholdAdjustment; + if (!FLAG_watch_ic_patching) { + // Adjust threshold each time we have processed + // a certain number of ticks. + if (sampler_ticks_until_threshold_adjustment_ > 0) { + sampler_ticks_until_threshold_adjustment_--; + if (sampler_ticks_until_threshold_adjustment_ <= 0) { + // If the threshold is not already at the minimum + // modify and reset the ticks until next adjustment. + if (sampler_threshold_ > kSamplerThresholdMin) { + sampler_threshold_ -= kSamplerThresholdDelta; + sampler_ticks_until_threshold_adjustment_ = + kSamplerTicksBetweenThresholdAdjustment; + } } } } @@ -217,25 +231,55 @@ void RuntimeProfiler::OptimizeNow() { // Do not record non-optimizable functions. if (!function->IsOptimizable()) continue; - samples[sample_count++] = function; - int function_size = function->shared()->SourceSize(); - int threshold_size_factor = (function_size > kSizeLimit) - ? sampler_threshold_size_factor_ - : 1; + if (FLAG_watch_ic_patching) { + int ticks = function->shared()->profiler_ticks(); + + if (ticks >= kProfilerTicksBeforeOptimization) { + // If this particular function hasn't had any ICs patched for enough + // ticks, optimize it now. + Optimize(function, "hot and stable"); + } else if (!any_ic_changed_ && + function->shared()->code()->instruction_size() < kMaxSizeEarlyOpt) { + // If no IC was patched since the last tick and this function is very + // small, optimistically optimize it now. + Optimize(function, "small function"); + } else if (!code_generated_ && + !any_ic_changed_ && + total_code_generated_ > 0 && + total_code_generated_ < 2000) { + // If no code was generated and no IC was patched since the last tick, + // but a little code has already been generated since last Reset(), + // then type info might already be stable and we can optimize now. + Optimize(function, "stable on startup"); + } else { + function->shared()->set_profiler_ticks(ticks + 1); + } + } else { // !FLAG_counting_profiler + samples[sample_count++] = function; + + int function_size = function->shared()->SourceSize(); + int threshold_size_factor = (function_size > kSizeLimit) + ? sampler_threshold_size_factor_ + : 1; - int threshold = sampler_threshold_ * threshold_size_factor; + int threshold = sampler_threshold_ * threshold_size_factor; - if (LookupSample(function) >= threshold) { - Optimize(function); + if (LookupSample(function) >= threshold) { + Optimize(function, "sampler window lookup"); + } } } - - // Add the collected functions as samples. It's important not to do - // this as part of collecting them because this will interfere with - // the sample lookup in case of recursive functions. - for (int i = 0; i < sample_count; i++) { - AddSample(samples[i], kSamplerFrameWeight[i]); + if (FLAG_watch_ic_patching) { + any_ic_changed_ = false; + code_generated_ = false; + } else { // !FLAG_counting_profiler + // Add the collected functions as samples. It's important not to do + // this as part of collecting them because this will interfere with + // the sample lookup in case of recursive functions. + for (int i = 0; i < sample_count; i++) { + AddSample(samples[i], kSamplerFrameWeight[i]); + } } } @@ -245,9 +289,11 @@ void RuntimeProfiler::NotifyTick() { } -void RuntimeProfiler::Setup() { - ASSERT(has_been_globally_setup_); - ClearSampleBuffer(); +void RuntimeProfiler::SetUp() { + ASSERT(has_been_globally_set_up_); + if (!FLAG_watch_ic_patching) { + ClearSampleBuffer(); + } // If the ticker hasn't already started, make sure to do so to get // the ticks for the runtime profiler. if (IsEnabled()) isolate_->logger()->EnsureTickerStarted(); @@ -255,10 +301,14 @@ void RuntimeProfiler::Setup() { void RuntimeProfiler::Reset() { - sampler_threshold_ = kSamplerThresholdInit; - sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit; - sampler_ticks_until_threshold_adjustment_ = - kSamplerTicksBetweenThresholdAdjustment; + if (FLAG_watch_ic_patching) { + total_code_generated_ = 0; + } else { // !FLAG_counting_profiler + sampler_threshold_ = kSamplerThresholdInit; + sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit; + sampler_ticks_until_threshold_adjustment_ = + kSamplerTicksBetweenThresholdAdjustment; + } } @@ -338,7 +388,8 @@ void RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(Thread* thread) { void RuntimeProfiler::RemoveDeadSamples() { for (int i = 0; i < kSamplerWindowSize; i++) { Object* function = sampler_window_[i]; - if (function != NULL && !HeapObject::cast(function)->IsMarked()) { + if (function != NULL && + !Marking::MarkBitFrom(HeapObject::cast(function)).Get()) { sampler_window_[i] = NULL; } } diff --git a/deps/v8/src/runtime-profiler.h b/deps/v8/src/runtime-profiler.h index 15c209713e..f37456653b 100644 --- a/deps/v8/src/runtime-profiler.h +++ b/deps/v8/src/runtime-profiler.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -46,7 +46,7 @@ class RuntimeProfiler { static void GlobalSetup(); static inline bool IsEnabled() { - ASSERT(has_been_globally_setup_); + ASSERT(has_been_globally_set_up_); return enabled_; } @@ -54,13 +54,22 @@ class RuntimeProfiler { void NotifyTick(); - void Setup(); + void SetUp(); void Reset(); void TearDown(); Object** SamplerWindowAddress(); int SamplerWindowSize(); + void NotifyICChanged() { any_ic_changed_ = true; } + + void NotifyCodeGenerated(int generated_code_size) { + if (FLAG_watch_ic_patching) { + code_generated_ = true; + total_code_generated_ += generated_code_size; + } + } + // Rate limiting support. // VM thread interface. @@ -97,7 +106,7 @@ class RuntimeProfiler { static void HandleWakeUp(Isolate* isolate); - void Optimize(JSFunction* function); + void Optimize(JSFunction* function, const char* reason); void AttemptOnStackReplacement(JSFunction* function); @@ -119,6 +128,10 @@ class RuntimeProfiler { int sampler_window_position_; int sampler_window_weight_[kSamplerWindowSize]; + bool any_ic_changed_; + bool code_generated_; + int total_code_generated_; + // Possible state values: // -1 => the profiler thread is waiting on the semaphore // 0 or positive => the number of isolates running JavaScript code. @@ -126,7 +139,7 @@ class RuntimeProfiler { static Semaphore* semaphore_; #ifdef DEBUG - static bool has_been_globally_setup_; + static bool has_been_globally_set_up_; #endif static bool enabled_; }; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index b1c4c102ec..80ea7f4b6f 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -42,6 +42,7 @@ #include "deoptimizer.h" #include "execution.h" #include "global-handles.h" +#include "isolate-inl.h" #include "jsregexp.h" #include "json-parser.h" #include "liveedit.h" @@ -105,6 +106,27 @@ namespace internal { type name = NumberTo##Type(obj); +// Assert that the given argument has a valid value for a StrictModeFlag +// and store it in a StrictModeFlag variable with the given name. +#define CONVERT_STRICT_MODE_ARG(name, index) \ + ASSERT(args[index]->IsSmi()); \ + ASSERT(args.smi_at(index) == kStrictMode || \ + args.smi_at(index) == kNonStrictMode); \ + StrictModeFlag name = \ + static_cast<StrictModeFlag>(args.smi_at(index)); + + +// Assert that the given argument has a valid value for a LanguageMode +// and store it in a LanguageMode variable with the given name. +#define CONVERT_LANGUAGE_MODE_ARG(name, index) \ + ASSERT(args[index]->IsSmi()); \ + ASSERT(args.smi_at(index) == CLASSIC_MODE || \ + args.smi_at(index) == STRICT_MODE || \ + args.smi_at(index) == EXTENDED_MODE); \ + LanguageMode name = \ + static_cast<LanguageMode>(args.smi_at(index)); + + MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, JSObject* boilerplate) { StackLimitCheck check(isolate); @@ -143,7 +165,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } } else { { MaybeObject* maybe_result = - heap->AllocateFixedArray(copy->NumberOfLocalProperties(NONE)); + heap->AllocateFixedArray(copy->NumberOfLocalProperties()); if (!maybe_result->ToObject(&result)) return maybe_result; } FixedArray* names = FixedArray::cast(result); @@ -177,6 +199,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, // Pixel elements cannot be created using an object literal. ASSERT(!copy->HasExternalArrayElements()); switch (copy->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); if (elements->map() == heap->fixed_cow_array_map()) { @@ -189,6 +212,9 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } else { for (int i = 0; i < elements->length(); i++) { Object* value = elements->get(i); + ASSERT(value->IsSmi() || + value->IsTheHole() || + (copy->GetElementsKind() == FAST_ELEMENTS)); if (value->IsJSObject()) { JSObject* js_object = JSObject::cast(value); { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, @@ -240,18 +266,6 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } -RUNTIME_FUNCTION(MaybeObject*, Runtime_CloneLiteralBoilerplate) { - CONVERT_CHECKED(JSObject, boilerplate, args[0]); - return DeepCopyBoilerplate(isolate, boilerplate); -} - - -RUNTIME_FUNCTION(MaybeObject*, Runtime_CloneShallowLiteralBoilerplate) { - CONVERT_CHECKED(JSObject, boilerplate, args[0]); - return isolate->heap()->CopyJSObject(boilerplate); -} - - static Handle<Map> ComputeObjectLiteralMap( Handle<Context> context, Handle<FixedArray> constant_properties, @@ -259,45 +273,43 @@ static Handle<Map> ComputeObjectLiteralMap( Isolate* isolate = context->GetIsolate(); int properties_length = constant_properties->length(); int number_of_properties = properties_length / 2; - if (FLAG_canonicalize_object_literal_maps) { - // Check that there are only symbols and array indices among keys. - int number_of_symbol_keys = 0; - for (int p = 0; p != properties_length; p += 2) { - Object* key = constant_properties->get(p); - uint32_t element_index = 0; - if (key->IsSymbol()) { - number_of_symbol_keys++; - } else if (key->ToArrayIndex(&element_index)) { - // An index key does not require space in the property backing store. - number_of_properties--; - } else { - // Bail out as a non-symbol non-index key makes caching impossible. - // ASSERT to make sure that the if condition after the loop is false. - ASSERT(number_of_symbol_keys != number_of_properties); - break; - } + // Check that there are only symbols and array indices among keys. + int number_of_symbol_keys = 0; + for (int p = 0; p != properties_length; p += 2) { + Object* key = constant_properties->get(p); + uint32_t element_index = 0; + if (key->IsSymbol()) { + number_of_symbol_keys++; + } else if (key->ToArrayIndex(&element_index)) { + // An index key does not require space in the property backing store. + number_of_properties--; + } else { + // Bail out as a non-symbol non-index key makes caching impossible. + // ASSERT to make sure that the if condition after the loop is false. + ASSERT(number_of_symbol_keys != number_of_properties); + break; } - // If we only have symbols and array indices among keys then we can - // use the map cache in the global context. - const int kMaxKeys = 10; - if ((number_of_symbol_keys == number_of_properties) && - (number_of_symbol_keys < kMaxKeys)) { - // Create the fixed array with the key. - Handle<FixedArray> keys = - isolate->factory()->NewFixedArray(number_of_symbol_keys); - if (number_of_symbol_keys > 0) { - int index = 0; - for (int p = 0; p < properties_length; p += 2) { - Object* key = constant_properties->get(p); - if (key->IsSymbol()) { - keys->set(index++, key); - } + } + // If we only have symbols and array indices among keys then we can + // use the map cache in the global context. + const int kMaxKeys = 10; + if ((number_of_symbol_keys == number_of_properties) && + (number_of_symbol_keys < kMaxKeys)) { + // Create the fixed array with the key. + Handle<FixedArray> keys = + isolate->factory()->NewFixedArray(number_of_symbol_keys); + if (number_of_symbol_keys > 0) { + int index = 0; + for (int p = 0; p < properties_length; p += 2) { + Object* key = constant_properties->get(p); + if (key->IsSymbol()) { + keys->set(index++, key); } - ASSERT(index == number_of_symbol_keys); } - *is_result_from_cache = true; - return isolate->factory()->ObjectLiteralMapFromCache(context, keys); + ASSERT(index == number_of_symbol_keys); } + *is_result_from_cache = true; + return isolate->factory()->ObjectLiteralMapFromCache(context, keys); } *is_result_from_cache = false; return isolate->factory()->CopyMap( @@ -341,7 +353,7 @@ static Handle<Object> CreateObjectLiteralBoilerplate( Handle<JSObject> boilerplate = isolate->factory()->NewJSObjectFromMap(map); // Normalize the elements of the boilerplate to save space if needed. - if (!should_have_fast_elements) NormalizeElements(boilerplate); + if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate); // Add the constant properties to the boilerplate. int length = constant_properties->length(); @@ -351,7 +363,8 @@ static Handle<Object> CreateObjectLiteralBoilerplate( // Normalize the properties of object to avoid n^2 behavior // when extending the object multiple properties. Indicate the number of // properties to be added. - NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2); + JSObject::NormalizeProperties( + boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2); } for (int index = 0; index < length; index +=2) { @@ -369,22 +382,18 @@ static Handle<Object> CreateObjectLiteralBoilerplate( if (key->IsSymbol()) { if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) { // Array index as string (uint32). - result = SetOwnElement(boilerplate, - element_index, - value, - kNonStrictMode); + result = JSObject::SetOwnElement( + boilerplate, element_index, value, kNonStrictMode); } else { Handle<String> name(String::cast(*key)); ASSERT(!name->AsArrayIndex(&element_index)); - result = SetLocalPropertyIgnoreAttributes(boilerplate, name, - value, NONE); + result = JSObject::SetLocalPropertyIgnoreAttributes( + boilerplate, name, value, NONE); } } else if (key->ToArrayIndex(&element_index)) { // Array index (uint32). - result = SetOwnElement(boilerplate, - element_index, - value, - kNonStrictMode); + result = JSObject::SetOwnElement( + boilerplate, element_index, value, kNonStrictMode); } else { // Non-uint32 number. ASSERT(key->IsNumber()); @@ -394,8 +403,8 @@ static Handle<Object> CreateObjectLiteralBoilerplate( const char* str = DoubleToCString(num, buffer); Handle<String> name = isolate->factory()->NewStringFromAscii(CStrVector(str)); - result = SetLocalPropertyIgnoreAttributes(boilerplate, name, - value, NONE); + result = JSObject::SetLocalPropertyIgnoreAttributes( + boilerplate, name, value, NONE); } // If setting the property on the boilerplate throws an // exception, the exception is converted to an empty handle in @@ -409,52 +418,110 @@ static Handle<Object> CreateObjectLiteralBoilerplate( // computed properties have been assigned so that we can generate // constant function properties. if (should_transform && !has_function_literal) { - TransformToFastProperties(boilerplate, - boilerplate->map()->unused_property_fields()); + JSObject::TransformToFastProperties( + boilerplate, boilerplate->map()->unused_property_fields()); } return boilerplate; } -static Handle<Object> CreateArrayLiteralBoilerplate( +MaybeObject* TransitionElements(Handle<Object> object, + ElementsKind to_kind, + Isolate* isolate) { + HandleScope scope(isolate); + if (!object->IsJSObject()) return isolate->ThrowIllegalOperation(); + ElementsKind from_kind = + Handle<JSObject>::cast(object)->map()->elements_kind(); + if (Map::IsValidElementsTransition(from_kind, to_kind)) { + Handle<Object> result = JSObject::TransitionElementsKind( + Handle<JSObject>::cast(object), to_kind); + if (result.is_null()) return isolate->ThrowIllegalOperation(); + return *result; + } + return isolate->ThrowIllegalOperation(); +} + + +static const int kSmiOnlyLiteralMinimumLength = 1024; + + +Handle<Object> Runtime::CreateArrayLiteralBoilerplate( Isolate* isolate, Handle<FixedArray> literals, Handle<FixedArray> elements) { // Create the JSArray. Handle<JSFunction> constructor( JSFunction::GlobalContextFromLiterals(*literals)->array_function()); - Handle<Object> object = isolate->factory()->NewJSObject(constructor); - - const bool is_cow = - (elements->map() == isolate->heap()->fixed_cow_array_map()); - Handle<FixedArray> copied_elements = - is_cow ? elements : isolate->factory()->CopyFixedArray(elements); + Handle<JSArray> object = + Handle<JSArray>::cast(isolate->factory()->NewJSObject(constructor)); + + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(elements->get(0))->value()); + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(elements->get(1))); + + Context* global_context = isolate->context()->global_context(); + if (constant_elements_kind == FAST_SMI_ONLY_ELEMENTS) { + object->set_map(Map::cast(global_context->smi_js_array_map())); + } else if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { + object->set_map(Map::cast(global_context->double_js_array_map())); + } else { + object->set_map(Map::cast(global_context->object_js_array_map())); + } - Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements); - if (is_cow) { -#ifdef DEBUG - // Copy-on-write arrays must be shallow (and simple). - for (int i = 0; i < content->length(); i++) { - ASSERT(!content->get(i)->IsFixedArray()); - } -#endif + Handle<FixedArrayBase> copied_elements_values; + if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { + ASSERT(FLAG_smi_only_arrays); + copied_elements_values = isolate->factory()->CopyFixedDoubleArray( + Handle<FixedDoubleArray>::cast(constant_elements_values)); } else { - for (int i = 0; i < content->length(); i++) { - if (content->get(i)->IsFixedArray()) { - // The value contains the constant_properties of a - // simple object or array literal. - Handle<FixedArray> fa(FixedArray::cast(content->get(i))); - Handle<Object> result = - CreateLiteralBoilerplate(isolate, literals, fa); - if (result.is_null()) return result; - content->set(i, *result); + ASSERT(constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + constant_elements_kind == FAST_ELEMENTS); + const bool is_cow = + (constant_elements_values->map() == + isolate->heap()->fixed_cow_array_map()); + if (is_cow) { + copied_elements_values = constant_elements_values; +#if DEBUG + Handle<FixedArray> fixed_array_values = + Handle<FixedArray>::cast(copied_elements_values); + for (int i = 0; i < fixed_array_values->length(); i++) { + ASSERT(!fixed_array_values->get(i)->IsFixedArray()); + } +#endif + } else { + Handle<FixedArray> fixed_array_values = + Handle<FixedArray>::cast(constant_elements_values); + Handle<FixedArray> fixed_array_values_copy = + isolate->factory()->CopyFixedArray(fixed_array_values); + copied_elements_values = fixed_array_values_copy; + for (int i = 0; i < fixed_array_values->length(); i++) { + Object* current = fixed_array_values->get(i); + if (current->IsFixedArray()) { + // The value contains the constant_properties of a + // simple object or array literal. + Handle<FixedArray> fa(FixedArray::cast(fixed_array_values->get(i))); + Handle<Object> result = + CreateLiteralBoilerplate(isolate, literals, fa); + if (result.is_null()) return result; + fixed_array_values_copy->set(i, *result); + } } } } + object->set_elements(*copied_elements_values); + object->set_length(Smi::FromInt(copied_elements_values->length())); + + // Ensure that the boilerplate object has FAST_ELEMENTS, unless the flag is + // on or the object is larger than the threshold. + if (!FLAG_smi_only_arrays && + constant_elements_values->length() < kSmiOnlyLiteralMinimumLength) { + if (object->GetElementsKind() != FAST_ELEMENTS) { + CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); + } + } - // Set the elements. - Handle<JSArray>::cast(object)->SetContent(*content); return object; } @@ -479,7 +546,8 @@ static Handle<Object> CreateLiteralBoilerplate( false, kHasNoFunctionLiteral); case CompileTimeValue::ARRAY_LITERAL: - return CreateArrayLiteralBoilerplate(isolate, literals, elements); + return Runtime::CreateArrayLiteralBoilerplate( + isolate, literals, elements); default: UNREACHABLE(); return Handle<Object>::null(); @@ -487,28 +555,6 @@ static Handle<Object> CreateLiteralBoilerplate( } -RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralBoilerplate) { - // Takes a FixedArray of elements containing the literal elements of - // the array literal and produces JSArray with those elements. - // Additionally takes the literals array of the surrounding function - // which contains the context from which to get the Array function - // to use for creating the array literal. - HandleScope scope(isolate); - ASSERT(args.length() == 3); - CONVERT_ARG_CHECKED(FixedArray, literals, 0); - CONVERT_SMI_ARG_CHECKED(literals_index, 1); - CONVERT_ARG_CHECKED(FixedArray, elements, 2); - - Handle<Object> object = - CreateArrayLiteralBoilerplate(isolate, literals, elements); - if (object.is_null()) return Failure::Exception(); - - // Update the functions literal and return the boilerplate. - literals->set(literals_index, *object); - return *object; -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteral) { HandleScope scope(isolate); ASSERT(args.length() == 4); @@ -571,7 +617,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteral) { // Check if boilerplate exists. If not, create it first. Handle<Object> boilerplate(literals->get(literals_index), isolate); if (*boilerplate == isolate->heap()->undefined_value()) { - boilerplate = CreateArrayLiteralBoilerplate(isolate, literals, elements); + boilerplate = + Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements); if (boilerplate.is_null()) return Failure::Exception(); // Update the functions literal and return the boilerplate. literals->set(literals_index, *boilerplate); @@ -590,7 +637,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) { // Check if boilerplate exists. If not, create it first. Handle<Object> boilerplate(literals->get(literals_index), isolate); if (*boilerplate == isolate->heap()->undefined_value()) { - boilerplate = CreateArrayLiteralBoilerplate(isolate, literals, elements); + ASSERT(*elements != isolate->heap()->empty_fixed_array()); + boilerplate = + Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements); if (boilerplate.is_null()) return Failure::Exception(); // Update the functions literal and return the boilerplate. literals->set(literals_index, *boilerplate); @@ -669,6 +718,82 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) { + HandleScope scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSSet, holder, 0); + Handle<ObjectHashSet> table = isolate->factory()->NewObjectHashSet(0); + holder->set_table(*table); + return *holder; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAdd) { + HandleScope scope(isolate); + ASSERT(args.length() == 2); + CONVERT_ARG_CHECKED(JSSet, holder, 0); + Handle<Object> key(args[1]); + Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); + table = ObjectHashSetAdd(table, key); + holder->set_table(*table); + return isolate->heap()->undefined_symbol(); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHas) { + HandleScope scope(isolate); + ASSERT(args.length() == 2); + CONVERT_ARG_CHECKED(JSSet, holder, 0); + Handle<Object> key(args[1]); + Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); + return isolate->heap()->ToBoolean(table->Contains(*key)); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) { + HandleScope scope(isolate); + ASSERT(args.length() == 2); + CONVERT_ARG_CHECKED(JSSet, holder, 0); + Handle<Object> key(args[1]); + Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); + table = ObjectHashSetRemove(table, key); + holder->set_table(*table); + return isolate->heap()->undefined_symbol(); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) { + HandleScope scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSMap, holder, 0); + Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0); + holder->set_table(*table); + return *holder; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGet) { + HandleScope scope(isolate); + ASSERT(args.length() == 2); + CONVERT_ARG_CHECKED(JSMap, holder, 0); + Handle<Object> key(args[1]); + return ObjectHashTable::cast(holder->table())->Lookup(*key); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) { + HandleScope scope(isolate); + ASSERT(args.length() == 3); + CONVERT_ARG_CHECKED(JSMap, holder, 0); + Handle<Object> key(args[1]); + Handle<Object> value(args[2]); + Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table())); + Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value); + holder->set_table(*new_table); + return *value; +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) { HandleScope scope(isolate); ASSERT(args.length() == 1); @@ -685,10 +810,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapGet) { NoHandleAllocation ha; ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0); - // TODO(mstarzinger): Currently we cannot use JSProxy objects as keys - // because they cannot be cast to JSObject to get an identity hash code. - CONVERT_ARG_CHECKED(JSObject, key, 1); - return weakmap->table()->Lookup(*key); + CONVERT_ARG_CHECKED(JSReceiver, key, 1); + return ObjectHashTable::cast(weakmap->table())->Lookup(*key); } @@ -696,10 +819,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapSet) { HandleScope scope(isolate); ASSERT(args.length() == 3); CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0); - // TODO(mstarzinger): See Runtime_WeakMapGet above. - CONVERT_ARG_CHECKED(JSObject, key, 1); + CONVERT_ARG_CHECKED(JSReceiver, key, 1); Handle<Object> value(args[2]); - Handle<ObjectHashTable> table(weakmap->table()); + Handle<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table())); Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value); weakmap->set_table(*new_table); return *value; @@ -752,49 +874,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) { } -// Inserts an object as the hidden prototype of another object. -RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHiddenPrototype) { - NoHandleAllocation ha; - ASSERT(args.length() == 2); - CONVERT_CHECKED(JSObject, jsobject, args[0]); - CONVERT_CHECKED(JSObject, proto, args[1]); - - // Sanity checks. The old prototype (that we are replacing) could - // theoretically be null, but if it is not null then check that we - // didn't already install a hidden prototype here. - RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() || - !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype()); - RUNTIME_ASSERT(!proto->map()->is_hidden_prototype()); - - // Allocate up front before we start altering state in case we get a GC. - Object* map_or_failure; - { MaybeObject* maybe_map_or_failure = proto->map()->CopyDropTransitions(); - if (!maybe_map_or_failure->ToObject(&map_or_failure)) { - return maybe_map_or_failure; - } - } - Map* new_proto_map = Map::cast(map_or_failure); - - { MaybeObject* maybe_map_or_failure = jsobject->map()->CopyDropTransitions(); - if (!maybe_map_or_failure->ToObject(&map_or_failure)) { - return maybe_map_or_failure; - } - } - Map* new_map = Map::cast(map_or_failure); - - // Set proto's prototype to be the old prototype of the object. - new_proto_map->set_prototype(jsobject->GetPrototype()); - proto->set_map(new_proto_map); - new_proto_map->set_is_hidden_prototype(); - - // Set the object's prototype to proto. - new_map->set_prototype(proto); - jsobject->set_map(new_map); - - return isolate->heap()->undefined_value(); -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) { NoHandleAllocation ha; ASSERT(args.length() == 0); @@ -929,7 +1008,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { HandleScope scope(isolate); Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms); - LookupResult result; + LookupResult result(isolate); CONVERT_ARG_CHECKED(JSObject, obj, 0); CONVERT_ARG_CHECKED(String, name, 1); @@ -960,7 +1039,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { case JSObject::INTERCEPTED_ELEMENT: case JSObject::FAST_ELEMENT: { elms->set(IS_ACCESSOR_INDEX, heap->false_value()); - Handle<Object> value = GetElement(obj, index); + Handle<Object> value = Object::GetElement(obj, index); RETURN_IF_EMPTY_HANDLE(isolate, value); elms->set(VALUE_INDEX, *value); elms->set(WRITABLE_INDEX, heap->true_value()); @@ -990,21 +1069,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { switch (details.type()) { case CALLBACKS: { // This is an accessor property with getter and/or setter. - FixedArray* callbacks = - FixedArray::cast(dictionary->ValueAt(entry)); + AccessorPair* accessors = + AccessorPair::cast(dictionary->ValueAt(entry)); elms->set(IS_ACCESSOR_INDEX, heap->true_value()); if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) { - elms->set(GETTER_INDEX, callbacks->get(0)); + elms->set(GETTER_INDEX, accessors->getter()); } if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) { - elms->set(SETTER_INDEX, callbacks->get(1)); + elms->set(SETTER_INDEX, accessors->setter()); } break; } case NORMAL: { // This is a data property. elms->set(IS_ACCESSOR_INDEX, heap->false_value()); - Handle<Object> value = GetElement(obj, index); + Handle<Object> value = Object::GetElement(obj, index); ASSERT(!value.is_null()); elms->set(VALUE_INDEX, *value); elms->set(WRITABLE_INDEX, heap->ToBoolean(!details.IsReadOnly())); @@ -1036,18 +1115,18 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete())); bool is_js_accessor = (result.type() == CALLBACKS) && - (result.GetCallbackObject()->IsFixedArray()); + (result.GetCallbackObject()->IsAccessorPair()); if (is_js_accessor) { // __defineGetter__/__defineSetter__ callback. elms->set(IS_ACCESSOR_INDEX, heap->true_value()); - FixedArray* structure = FixedArray::cast(result.GetCallbackObject()); + AccessorPair* accessors = AccessorPair::cast(result.GetCallbackObject()); if (CheckAccess(*obj, *name, &result, v8::ACCESS_GET)) { - elms->set(GETTER_INDEX, structure->get(0)); + elms->set(GETTER_INDEX, accessors->getter()); } if (CheckAccess(*obj, *name, &result, v8::ACCESS_SET)) { - elms->set(SETTER_INDEX, structure->get(1)); + elms->set(SETTER_INDEX, accessors->setter()); } } else { elms->set(IS_ACCESSOR_INDEX, heap->false_value()); @@ -1208,49 +1287,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { if (value->IsUndefined() || is_const_property) { // Lookup the property in the global object, and don't set the // value of the variable if the property is already there. - LookupResult lookup; + LookupResult lookup(isolate); global->Lookup(*name, &lookup); if (lookup.IsProperty()) { - // Determine if the property is local by comparing the holder - // against the global object. The information will be used to - // avoid throwing re-declaration errors when declaring - // variables or constants that exist in the prototype chain. - bool is_local = (*global == lookup.holder()); - // Get the property attributes and determine if the property is - // read-only. + // We found an existing property. Unless it was an interceptor + // that claims the property is absent, skip this declaration. + if (lookup.type() != INTERCEPTOR) { + continue; + } PropertyAttributes attributes = global->GetPropertyAttribute(*name); - bool is_read_only = (attributes & READ_ONLY) != 0; - if (lookup.type() == INTERCEPTOR) { - // If the interceptor says the property is there, we - // just return undefined without overwriting the property. - // Otherwise, we continue to setting the property. - if (attributes != ABSENT) { - // Check if the existing property conflicts with regards to const. - if (is_local && (is_read_only || is_const_property)) { - const char* type = (is_read_only) ? "const" : "var"; - return ThrowRedeclarationError(isolate, type, name); - }; - // The property already exists without conflicting: Go to - // the next declaration. - continue; - } - // Fall-through and introduce the absent property by using - // SetProperty. - } else { - // For const properties, we treat a callback with this name - // even in the prototype as a conflicting declaration. - if (is_const_property && (lookup.type() == CALLBACKS)) { - return ThrowRedeclarationError(isolate, "const", name); - } - // Otherwise, we check for locally conflicting declarations. - if (is_local && (is_read_only || is_const_property)) { - const char* type = (is_read_only) ? "const" : "var"; - return ThrowRedeclarationError(isolate, type, name); - } - // The property already exists without conflicting: Go to - // the next declaration. + if (attributes != ABSENT) { continue; } + // Fall-through and introduce the absent property by using + // SetProperty. } } else { is_function_declaration = true; @@ -1264,32 +1314,18 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { value = function; } - LookupResult lookup; + LookupResult lookup(isolate); global->LocalLookup(*name, &lookup); - // There's a local property that we need to overwrite because - // we're either declaring a function or there's an interceptor - // that claims the property is absent. - // - // Check for conflicting re-declarations. We cannot have - // conflicting types in case of intercepted properties because - // they are absent. - if (lookup.IsProperty() && - (lookup.type() != INTERCEPTOR) && - (lookup.IsReadOnly() || is_const_property)) { - const char* type = (lookup.IsReadOnly()) ? "const" : "var"; - return ThrowRedeclarationError(isolate, type, name); - } - // Compute the property attributes. According to ECMA-262, section // 13, page 71, the property must be read-only and // non-deletable. However, neither SpiderMonkey nor KJS creates the // property as read-only, so we don't either. int attr = NONE; - if ((flags & kDeclareGlobalsEvalFlag) == 0) { + if (!DeclareGlobalsEvalFlag::decode(flags)) { attr |= DONT_DELETE; } - bool is_native = (flags & kDeclareGlobalsNativeFlag) != 0; + bool is_native = DeclareGlobalsNativeFlag::decode(flags); if (is_const_property || (is_native && is_function_declaration)) { attr |= READ_ONLY; } @@ -1308,21 +1344,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { } PropertyAttributes attributes = static_cast<PropertyAttributes>(attr); - RETURN_IF_EMPTY_HANDLE(isolate, - SetLocalPropertyIgnoreAttributes(global, - name, - value, - attributes)); + RETURN_IF_EMPTY_HANDLE( + isolate, + JSObject::SetLocalPropertyIgnoreAttributes(global, name, value, + attributes)); } else { - StrictModeFlag strict_mode = - ((flags & kDeclareGlobalsStrictModeFlag) != 0) ? kStrictMode - : kNonStrictMode; - RETURN_IF_EMPTY_HANDLE(isolate, - SetProperty(global, - name, - value, - static_cast<PropertyAttributes>(attr), - strict_mode)); + LanguageMode language_mode = DeclareGlobalsLanguageMode::decode(flags); + StrictModeFlag strict_mode_flag = (language_mode == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + RETURN_IF_EMPTY_HANDLE( + isolate, + JSReceiver::SetProperty(global, name, value, + static_cast<PropertyAttributes>(attr), + strict_mode_flag)); } } @@ -1335,15 +1369,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { HandleScope scope(isolate); ASSERT(args.length() == 4); - CONVERT_ARG_CHECKED(Context, context, 0); + // Declarations are always made in a function or global context. In the + // case of eval code, the context passed is the context of the caller, + // which may be some nested context and not the declaration context. + RUNTIME_ASSERT(args[0]->IsContext()); + Handle<Context> context(Context::cast(args[0])->declaration_context()); + Handle<String> name(String::cast(args[1])); PropertyAttributes mode = static_cast<PropertyAttributes>(args.smi_at(2)); RUNTIME_ASSERT(mode == READ_ONLY || mode == NONE); Handle<Object> initial_value(args[3], isolate); - // Declarations are always done in a function or global context. - context = Handle<Context>(context->declaration_context()); - int index; PropertyAttributes attributes; ContextLookupFlags flags = DONT_FOLLOW_CHAINS; @@ -1352,9 +1388,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { context->Lookup(name, flags, &index, &attributes, &binding_flags); if (attributes != ABSENT) { - // The name was declared before; check for conflicting - // re-declarations: This is similar to the code in parser.cc in - // the AstBuildingParser::Declare function. + // The name was declared before; check for conflicting re-declarations. if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) { // Functions are not read-only. ASSERT(mode != READ_ONLY || initial_value->IsTheHole()); @@ -1365,53 +1399,42 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { // Initialize it if necessary. if (*initial_value != NULL) { if (index >= 0) { - // The variable or constant context slot should always be in - // the function context or the arguments object. - if (holder->IsContext()) { - ASSERT(holder.is_identical_to(context)); - if (((attributes & READ_ONLY) == 0) || - context->get(index)->IsTheHole()) { - context->set(index, *initial_value); - } - } else { - // The holder is an arguments object. - Handle<JSObject> arguments(Handle<JSObject>::cast(holder)); - Handle<Object> result = SetElement(arguments, index, initial_value, - kNonStrictMode); - if (result.is_null()) return Failure::Exception(); + ASSERT(holder.is_identical_to(context)); + if (((attributes & READ_ONLY) == 0) || + context->get(index)->IsTheHole()) { + context->set(index, *initial_value); } } else { - // Slow case: The property is not in the FixedArray part of the context. - Handle<JSObject> context_ext = Handle<JSObject>::cast(holder); + // Slow case: The property is in the context extension object of a + // function context or the global object of a global context. + Handle<JSObject> object = Handle<JSObject>::cast(holder); RETURN_IF_EMPTY_HANDLE( isolate, - SetProperty(context_ext, name, initial_value, - mode, kNonStrictMode)); + JSReceiver::SetProperty(object, name, initial_value, mode, + kNonStrictMode)); } } } else { // The property is not in the function context. It needs to be - // "declared" in the function context's extension context, or in the - // global context. - Handle<JSObject> context_ext; + // "declared" in the function context's extension context or as a + // property of the the global object. + Handle<JSObject> object; if (context->has_extension()) { - // The function context's extension context exists - use it. - context_ext = Handle<JSObject>(JSObject::cast(context->extension())); + object = Handle<JSObject>(JSObject::cast(context->extension())); } else { - // The function context's extension context does not exists - allocate - // it. - context_ext = isolate->factory()->NewJSObject( + // Context extension objects are allocated lazily. + ASSERT(context->IsFunctionContext()); + object = isolate->factory()->NewJSObject( isolate->context_extension_function()); - // And store it in the extension slot. - context->set_extension(*context_ext); + context->set_extension(*object); } - ASSERT(*context_ext != NULL); + ASSERT(*object != NULL); // Declare the property by setting it to the initial value if provided, // or undefined, and use the correct mode (e.g. READ_ONLY attribute for // constant declarations). - ASSERT(!context_ext->HasLocalProperty(*name)); + ASSERT(!object->HasLocalProperty(*name)); Handle<Object> value(isolate->heap()->undefined_value(), isolate); if (*initial_value != NULL) value = initial_value; // Declaring a const context slot is a conflicting declaration if @@ -1421,16 +1444,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { // SetProperty and no setters are invoked for those since they are // not real JSObjects. if (initial_value->IsTheHole() && - !context_ext->IsJSContextExtensionObject()) { - LookupResult lookup; - context_ext->Lookup(*name, &lookup); - if (lookup.IsProperty() && (lookup.type() == CALLBACKS)) { + !object->IsJSContextExtensionObject()) { + LookupResult lookup(isolate); + object->Lookup(*name, &lookup); + if (lookup.IsFound() && (lookup.type() == CALLBACKS)) { return ThrowRedeclarationError(isolate, "const", name); } } - RETURN_IF_EMPTY_HANDLE(isolate, - SetProperty(context_ext, name, value, mode, - kNonStrictMode)); + RETURN_IF_EMPTY_HANDLE( + isolate, + JSReceiver::SetProperty(object, name, value, mode, kNonStrictMode)); } return isolate->heap()->undefined_value(); @@ -1440,7 +1463,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { NoHandleAllocation nha; // args[0] == name - // args[1] == strict_mode + // args[1] == language_mode // args[2] == value (optional) // Determine if we need to assign to the variable if it already @@ -1451,8 +1474,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { CONVERT_ARG_CHECKED(String, name, 0); GlobalObject* global = isolate->context()->global(); RUNTIME_ASSERT(args[1]->IsSmi()); - StrictModeFlag strict_mode = static_cast<StrictModeFlag>(args.smi_at(1)); - ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); + CONVERT_LANGUAGE_MODE_ARG(language_mode, 1); + StrictModeFlag strict_mode_flag = (language_mode == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; // According to ECMA-262, section 12.2, page 62, the property must // not be deletable. @@ -1465,67 +1489,35 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { // to assign to the property. // Note that objects can have hidden prototypes, so we need to traverse // the whole chain of hidden prototypes to do a 'local' lookup. - JSObject* real_holder = global; - LookupResult lookup; - while (true) { - real_holder->LocalLookup(*name, &lookup); - if (lookup.IsProperty()) { - // Determine if this is a redeclaration of something read-only. - if (lookup.IsReadOnly()) { - // If we found readonly property on one of hidden prototypes, - // just shadow it. - if (real_holder != isolate->context()->global()) break; - return ThrowRedeclarationError(isolate, "const", name); - } - - // Determine if this is a redeclaration of an intercepted read-only - // property and figure out if the property exists at all. - bool found = true; - PropertyType type = lookup.type(); - if (type == INTERCEPTOR) { - HandleScope handle_scope(isolate); - Handle<JSObject> holder(real_holder); - PropertyAttributes intercepted = holder->GetPropertyAttribute(*name); - real_holder = *holder; - if (intercepted == ABSENT) { - // The interceptor claims the property isn't there. We need to - // make sure to introduce it. - found = false; - } else if ((intercepted & READ_ONLY) != 0) { - // The property is present, but read-only. Since we're trying to - // overwrite it with a variable declaration we must throw a - // re-declaration error. However if we found readonly property - // on one of hidden prototypes, just shadow it. - if (real_holder != isolate->context()->global()) break; - return ThrowRedeclarationError(isolate, "const", name); + Object* object = global; + LookupResult lookup(isolate); + while (object->IsJSObject() && + JSObject::cast(object)->map()->is_hidden_prototype()) { + JSObject* raw_holder = JSObject::cast(object); + raw_holder->LocalLookup(*name, &lookup); + if (lookup.IsFound() && lookup.type() == INTERCEPTOR) { + HandleScope handle_scope(isolate); + Handle<JSObject> holder(raw_holder); + PropertyAttributes intercepted = holder->GetPropertyAttribute(*name); + // Update the raw pointer in case it's changed due to GC. + raw_holder = *holder; + if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) { + // Found an interceptor that's not read only. + if (assign) { + return raw_holder->SetProperty( + &lookup, *name, args[2], attributes, strict_mode_flag); + } else { + return isolate->heap()->undefined_value(); } } - - if (found && !assign) { - // The global property is there and we're not assigning any value - // to it. Just return. - return isolate->heap()->undefined_value(); - } - - // Assign the value (or undefined) to the property. - Object* value = (assign) ? args[2] : isolate->heap()->undefined_value(); - return real_holder->SetProperty( - &lookup, *name, value, attributes, strict_mode); } - - Object* proto = real_holder->GetPrototype(); - if (!proto->IsJSObject()) - break; - - if (!JSObject::cast(proto)->map()->is_hidden_prototype()) - break; - - real_holder = JSObject::cast(proto); + object = raw_holder->GetPrototype(); } + // Reload global in case the loop above performed a GC. global = isolate->context()->global(); if (assign) { - return global->SetProperty(*name, args[2], attributes, strict_mode); + return global->SetProperty(*name, args[2], attributes, strict_mode_flag); } return isolate->heap()->undefined_value(); } @@ -1552,7 +1544,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { // add it as a local property even in case of callbacks in the // prototype chain (this rules out using SetProperty). // We use SetLocalPropertyIgnoreAttributes instead - LookupResult lookup; + LookupResult lookup(isolate); global->LocalLookup(*name, &lookup); if (!lookup.IsProperty()) { return global->SetLocalPropertyIgnoreAttributes(*name, @@ -1560,25 +1552,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { attributes); } - // Determine if this is a redeclaration of something not - // read-only. In case the result is hidden behind an interceptor we - // need to ask it for the property attributes. if (!lookup.IsReadOnly()) { - if (lookup.type() != INTERCEPTOR) { - return ThrowRedeclarationError(isolate, "var", name); - } - - PropertyAttributes intercepted = global->GetPropertyAttribute(*name); - - // Throw re-declaration error if the intercepted property is present - // but not read-only. - if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) { - return ThrowRedeclarationError(isolate, "var", name); - } - // Restore global object from context (in case of GC) and continue - // with setting the value because the property is either absent or - // read-only. We also have to do redo the lookup. + // with setting the value. HandleScope handle_scope(isolate); Handle<GlobalObject> global(isolate->context()->global()); @@ -1586,28 +1562,27 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { // property through an interceptor and only do it if it's // uninitialized, e.g. the hole. Nirk... // Passing non-strict mode because the property is writable. - RETURN_IF_EMPTY_HANDLE(isolate, - SetProperty(global, - name, - value, - attributes, - kNonStrictMode)); + RETURN_IF_EMPTY_HANDLE( + isolate, + JSReceiver::SetProperty(global, name, value, attributes, + kNonStrictMode)); return *value; } - // Set the value, but only we're assigning the initial value to a + // Set the value, but only if we're assigning the initial value to a // constant. For now, we determine this by checking if the // current value is the hole. - // Strict mode handling not needed (const disallowed in strict mode). + // Strict mode handling not needed (const is disallowed in strict mode). PropertyType type = lookup.type(); if (type == FIELD) { FixedArray* properties = global->properties(); int index = lookup.GetFieldIndex(); - if (properties->get(index)->IsTheHole()) { + if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) { properties->set(index, *value); } } else if (type == NORMAL) { - if (global->GetNormalizedProperty(&lookup)->IsTheHole()) { + if (global->GetNormalizedProperty(&lookup)->IsTheHole() || + !lookup.IsReadOnly()) { global->SetNormalizedProperty(&lookup, *value); } } else { @@ -1627,11 +1602,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) { Handle<Object> value(args[0], isolate); ASSERT(!value->IsTheHole()); - CONVERT_ARG_CHECKED(Context, context, 1); - Handle<String> name(String::cast(args[2])); // Initializations are always done in a function or global context. - context = Handle<Context>(context->declaration_context()); + RUNTIME_ASSERT(args[1]->IsContext()); + Handle<Context> context(Context::cast(args[1])->declaration_context()); + + Handle<String> name(String::cast(args[2])); int index; PropertyAttributes attributes; @@ -1640,72 +1616,64 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) { Handle<Object> holder = context->Lookup(name, flags, &index, &attributes, &binding_flags); - // In most situations, the property introduced by the const - // declaration should be present in the context extension object. - // However, because declaration and initialization are separate, the - // property might have been deleted (if it was introduced by eval) - // before we reach the initialization point. - // - // Example: - // - // function f() { eval("delete x; const x;"); } - // - // In that case, the initialization behaves like a normal assignment - // to property 'x'. if (index >= 0) { - if (holder->IsContext()) { - // Property was found in a context. Perform the assignment if we - // found some non-constant or an uninitialized constant. - Handle<Context> context = Handle<Context>::cast(holder); - if ((attributes & READ_ONLY) == 0 || context->get(index)->IsTheHole()) { - context->set(index, *value); - } - } else { - // The holder is an arguments object. - ASSERT((attributes & READ_ONLY) == 0); - Handle<JSObject> arguments(Handle<JSObject>::cast(holder)); - RETURN_IF_EMPTY_HANDLE( - isolate, - SetElement(arguments, index, value, kNonStrictMode)); + ASSERT(holder->IsContext()); + // Property was found in a context. Perform the assignment if we + // found some non-constant or an uninitialized constant. + Handle<Context> context = Handle<Context>::cast(holder); + if ((attributes & READ_ONLY) == 0 || context->get(index)->IsTheHole()) { + context->set(index, *value); } return *value; } - // The property could not be found, we introduce it in the global - // context. + // The property could not be found, we introduce it as a property of the + // global object. if (attributes == ABSENT) { Handle<JSObject> global = Handle<JSObject>( isolate->context()->global()); // Strict mode not needed (const disallowed in strict mode). RETURN_IF_EMPTY_HANDLE( isolate, - SetProperty(global, name, value, NONE, kNonStrictMode)); + JSReceiver::SetProperty(global, name, value, NONE, kNonStrictMode)); return *value; } - // The property was present in a context extension object. - Handle<JSObject> context_ext = Handle<JSObject>::cast(holder); + // The property was present in some function's context extension object, + // as a property on the subject of a with, or as a property of the global + // object. + // + // In most situations, eval-introduced consts should still be present in + // the context extension object. However, because declaration and + // initialization are separate, the property might have been deleted + // before we reach the initialization point. + // + // Example: + // + // function f() { eval("delete x; const x;"); } + // + // In that case, the initialization behaves like a normal assignment. + Handle<JSObject> object = Handle<JSObject>::cast(holder); - if (*context_ext == context->extension()) { - // This is the property that was introduced by the const - // declaration. Set it if it hasn't been set before. NOTE: We - // cannot use GetProperty() to get the current value as it - // 'unholes' the value. - LookupResult lookup; - context_ext->LocalLookupRealNamedProperty(*name, &lookup); - ASSERT(lookup.IsProperty()); // the property was declared + if (*object == context->extension()) { + // This is the property that was introduced by the const declaration. + // Set it if it hasn't been set before. NOTE: We cannot use + // GetProperty() to get the current value as it 'unholes' the value. + LookupResult lookup(isolate); + object->LocalLookupRealNamedProperty(*name, &lookup); + ASSERT(lookup.IsFound()); // the property was declared ASSERT(lookup.IsReadOnly()); // and it was declared as read-only PropertyType type = lookup.type(); if (type == FIELD) { - FixedArray* properties = context_ext->properties(); + FixedArray* properties = object->properties(); int index = lookup.GetFieldIndex(); if (properties->get(index)->IsTheHole()) { properties->set(index, *value); } } else if (type == NORMAL) { - if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) { - context_ext->SetNormalizedProperty(&lookup, *value); + if (object->GetNormalizedProperty(&lookup)->IsTheHole()) { + object->SetNormalizedProperty(&lookup, *value); } } else { // We should not reach here. Any real, named property should be @@ -1713,13 +1681,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) { UNREACHABLE(); } } else { - // The property was found in a different context extension object. - // Set it if it is not a read-only property. + // The property was found on some other object. Set it if it is not a + // read-only property. if ((attributes & READ_ONLY) == 0) { // Strict mode not needed (const disallowed in strict mode). RETURN_IF_EMPTY_HANDLE( isolate, - SetProperty(context_ext, name, value, attributes, kNonStrictMode)); + JSReceiver::SetProperty(object, name, value, attributes, + kNonStrictMode)); } } @@ -1734,7 +1703,7 @@ RUNTIME_FUNCTION(MaybeObject*, CONVERT_ARG_CHECKED(JSObject, object, 0); CONVERT_SMI_ARG_CHECKED(properties, 1); if (object->HasFastProperties()) { - NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); + JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); } return *object; } @@ -1818,14 +1787,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpInitializeObject) { JSFunction::cast(constructor)->initial_map() == map) { // If we still have the original map, set in-object properties directly. regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source); - // TODO(lrn): Consider skipping write barrier on booleans as well. - // Both true and false should be in oldspace at all times. - regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global); - regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase); - regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline); + // Both true and false are immovable immortal objects so no need for write + // barrier. + regexp->InObjectPropertyAtPut( + JSRegExp::kGlobalFieldIndex, global, SKIP_WRITE_BARRIER); + regexp->InObjectPropertyAtPut( + JSRegExp::kIgnoreCaseFieldIndex, ignoreCase, SKIP_WRITE_BARRIER); + regexp->InObjectPropertyAtPut( + JSRegExp::kMultilineFieldIndex, multiline, SKIP_WRITE_BARRIER); regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, Smi::FromInt(0), - SKIP_WRITE_BARRIER); + SKIP_WRITE_BARRIER); // It's a Smi. return regexp; } @@ -1887,7 +1859,7 @@ static Handle<JSFunction> InstallBuiltin(Isolate* isolate, code, false); optimized->shared()->DontAdaptArguments(); - SetProperty(holder, key, optimized, NONE, kStrictMode); + JSReceiver::SetProperty(holder, key, optimized, NONE, kStrictMode); return optimized; } @@ -1910,11 +1882,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) { RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultReceiver) { - NoHandleAllocation handle_free; ASSERT(args.length() == 1); - CONVERT_CHECKED(JSFunction, function, args[0]); + CONVERT_CHECKED(JSReceiver, callable, args[0]); + + if (!callable->IsJSFunction()) { + HandleScope scope(isolate); + bool threw = false; + Handle<Object> delegate = + Execution::TryGetFunctionDelegate(Handle<JSReceiver>(callable), &threw); + if (threw) return Failure::Exception(); + callable = JSFunction::cast(*delegate); + } + JSFunction* function = JSFunction::cast(callable); + SharedFunctionInfo* shared = function->shared(); - if (shared->native() || shared->strict_mode()) { + if (shared->native() || !shared->is_classic_mode()) { return isolate->heap()->undefined_value(); } // Returns undefined for strict or native functions, or @@ -1994,15 +1976,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionMarkNameShouldPrintAsAnonymous) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetBound) { - HandleScope scope(isolate); - ASSERT(args.length() == 1); - - CONVERT_CHECKED(JSFunction, fun, args[0]); - fun->shared()->set_bound(true); - return isolate->heap()->undefined_value(); -} - RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) { NoHandleAllocation ha; ASSERT(args.length() == 1); @@ -2028,11 +2001,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScript) { RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetSourceCode) { - NoHandleAllocation ha; + HandleScope scope(isolate); ASSERT(args.length() == 1); - CONVERT_CHECKED(JSFunction, f, args[0]); - return f->shared()->GetSourceCode(); + CONVERT_ARG_CHECKED(JSFunction, f, 0); + Handle<SharedFunctionInfo> shared(f->shared()); + return *shared->GetSourceCode(); } @@ -2081,24 +2055,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetLength) { } -// Creates a local, readonly, property called length with the correct -// length (when read by the user). This effectively overwrites the -// interceptor used to normally provide the length. -RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionSetLength) { - NoHandleAllocation ha; - ASSERT(args.length() == 2); - CONVERT_CHECKED(JSFunction, fun, args[0]); - CONVERT_CHECKED(Smi, length, args[1]); - MaybeObject* maybe_name = - isolate->heap()->AllocateStringFromAscii(CStrVector("length")); - String* name; - if (!maybe_name->To(&name)) return maybe_name; - PropertyAttributes attr = - static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); - return fun->AddProperty(name, length, attr, kNonStrictMode); -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) { NoHandleAllocation ha; ASSERT(args.length() == 2); @@ -2201,13 +2157,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) { Handle<JSFunction> fun = Handle<JSFunction>::cast(code); Handle<SharedFunctionInfo> shared(fun->shared()); - if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { + if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { return Failure::Exception(); } // Since we don't store the source for this we should never // optimize this. shared->code()->set_optimizable(false); - // Set the code, scope info, formal parameter count, // and the length of the target function. target->shared()->set_code(shared->code()); @@ -2239,9 +2194,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) { literals->set(JSFunction::kLiteralGlobalContextIndex, context->global_context()); } - // It's okay to skip the write barrier here because the literals - // are guaranteed to be in old space. - target->set_literals(*literals, SKIP_WRITE_BARRIER); + target->set_literals(*literals); target->set_next_function_link(isolate->heap()->undefined_value()); if (isolate->logger()->is_logging() || CpuProfiler::is_profiling(isolate)) { @@ -2325,7 +2278,8 @@ class FixedArrayBuilder { public: explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity) : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)), - length_(0) { + length_(0), + has_non_smi_elements_(false) { // Require a non-zero initial size. Ensures that doubling the size to // extend the array will work. ASSERT(initial_capacity > 0); @@ -2333,7 +2287,8 @@ class FixedArrayBuilder { explicit FixedArrayBuilder(Handle<FixedArray> backing_store) : array_(backing_store), - length_(0) { + length_(0), + has_non_smi_elements_(false) { // Require a non-zero initial size. Ensures that doubling the size to // extend the array will work. ASSERT(backing_store->length() > 0); @@ -2361,12 +2316,15 @@ class FixedArrayBuilder { } void Add(Object* value) { + ASSERT(!value->IsSmi()); ASSERT(length_ < capacity()); array_->set(length_, value); length_++; + has_non_smi_elements_ = true; } void Add(Smi* value) { + ASSERT(value->IsSmi()); ASSERT(length_ < capacity()); array_->set(length_, value); length_++; @@ -2391,7 +2349,7 @@ class FixedArrayBuilder { } Handle<JSArray> ToJSArray(Handle<JSArray> target_array) { - target_array->set_elements(*array_); + FACTORY->SetContent(target_array, array_); target_array->set_length(Smi::FromInt(length_)); return target_array; } @@ -2399,6 +2357,7 @@ class FixedArrayBuilder { private: Handle<FixedArray> array_; int length_; + bool has_non_smi_elements_; }; @@ -2893,7 +2852,7 @@ void FindStringIndicesDispatch(Isolate* isolate, } } else { Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); - if (pattern->IsAsciiRepresentation()) { + if (pattern_content.IsAscii()) { FindStringIndices(isolate, subject_vector, pattern_content.ToAsciiVector(), @@ -3019,7 +2978,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( // Shortcut for simple non-regexp global replacements if (is_global && - regexp->TypeTag() == JSRegExp::ATOM && + regexp_handle->TypeTag() == JSRegExp::ATOM && compiled_replacement.simple_hint()) { if (subject_handle->HasOnlyAsciiChars() && replacement_handle->HasOnlyAsciiChars()) { @@ -3242,6 +3201,9 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( Address end_of_string = answer->address() + string_size; isolate->heap()->CreateFillerObjectAt(end_of_string, delta); + if (Marking::IsBlack(Marking::MarkBitFrom(*answer))) { + MemoryChunk::IncrementLiveBytesFromMutator(answer->address(), -delta); + } return *answer; } @@ -3295,6 +3257,79 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) { } +Handle<String> Runtime::StringReplaceOneCharWithString(Isolate* isolate, + Handle<String> subject, + Handle<String> search, + Handle<String> replace, + bool* found, + int recursion_limit) { + if (recursion_limit == 0) return Handle<String>::null(); + if (subject->IsConsString()) { + ConsString* cons = ConsString::cast(*subject); + Handle<String> first = Handle<String>(cons->first()); + Handle<String> second = Handle<String>(cons->second()); + Handle<String> new_first = + StringReplaceOneCharWithString(isolate, + first, + search, + replace, + found, + recursion_limit - 1); + if (*found) return isolate->factory()->NewConsString(new_first, second); + if (new_first.is_null()) return new_first; + + Handle<String> new_second = + StringReplaceOneCharWithString(isolate, + second, + search, + replace, + found, + recursion_limit - 1); + if (*found) return isolate->factory()->NewConsString(first, new_second); + if (new_second.is_null()) return new_second; + + return subject; + } else { + int index = StringMatch(isolate, subject, search, 0); + if (index == -1) return subject; + *found = true; + Handle<String> first = isolate->factory()->NewSubString(subject, 0, index); + Handle<String> cons1 = isolate->factory()->NewConsString(first, replace); + Handle<String> second = + isolate->factory()->NewSubString(subject, index + 1, subject->length()); + return isolate->factory()->NewConsString(cons1, second); + } +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceOneCharWithString) { + ASSERT(args.length() == 3); + HandleScope scope(isolate); + CONVERT_ARG_CHECKED(String, subject, 0); + CONVERT_ARG_CHECKED(String, search, 1); + CONVERT_ARG_CHECKED(String, replace, 2); + + // If the cons string tree is too deep, we simply abort the recursion and + // retry with a flattened subject string. + const int kRecursionLimit = 0x1000; + bool found = false; + Handle<String> result = + Runtime::StringReplaceOneCharWithString(isolate, + subject, + search, + replace, + &found, + kRecursionLimit); + if (!result.is_null()) return *result; + return *Runtime::StringReplaceOneCharWithString(isolate, + FlattenGetString(subject), + search, + replace, + &found, + kRecursionLimit); +} + + // Perform string match of pattern on subject, starting at start index. // Caller must ensure that 0 <= start_index <= sub->length(), // and should check that pat->length() + start_index <= sub->length(). @@ -4001,13 +4036,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToRadixString) { // Slow case. CONVERT_DOUBLE_ARG_CHECKED(value, 0); if (isnan(value)) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("NaN")); + return *isolate->factory()->nan_symbol(); } if (isinf(value)) { if (value < 0) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("-Infinity")); + return *isolate->factory()->minus_infinity_symbol(); } - return isolate->heap()->AllocateStringFromAscii(CStrVector("Infinity")); + return *isolate->factory()->infinity_symbol(); } char* str = DoubleToRadixCString(value, radix); MaybeObject* result = @@ -4023,13 +4058,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToFixed) { CONVERT_DOUBLE_ARG_CHECKED(value, 0); if (isnan(value)) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("NaN")); + return *isolate->factory()->nan_symbol(); } if (isinf(value)) { if (value < 0) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("-Infinity")); + return *isolate->factory()->minus_infinity_symbol(); } - return isolate->heap()->AllocateStringFromAscii(CStrVector("Infinity")); + return *isolate->factory()->infinity_symbol(); } CONVERT_DOUBLE_ARG_CHECKED(f_number, 1); int f = FastD2I(f_number); @@ -4048,13 +4083,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToExponential) { CONVERT_DOUBLE_ARG_CHECKED(value, 0); if (isnan(value)) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("NaN")); + return *isolate->factory()->nan_symbol(); } if (isinf(value)) { if (value < 0) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("-Infinity")); + return *isolate->factory()->minus_infinity_symbol(); } - return isolate->heap()->AllocateStringFromAscii(CStrVector("Infinity")); + return *isolate->factory()->infinity_symbol(); } CONVERT_DOUBLE_ARG_CHECKED(f_number, 1); int f = FastD2I(f_number); @@ -4073,13 +4108,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToPrecision) { CONVERT_DOUBLE_ARG_CHECKED(value, 0); if (isnan(value)) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("NaN")); + return *isolate->factory()->nan_symbol(); } if (isinf(value)) { if (value < 0) { - return isolate->heap()->AllocateStringFromAscii(CStrVector("-Infinity")); + return *isolate->factory()->minus_infinity_symbol(); } - return isolate->heap()->AllocateStringFromAscii(CStrVector("Infinity")); + return *isolate->factory()->infinity_symbol(); } CONVERT_DOUBLE_ARG_CHECKED(f_number, 1); int f = FastD2I(f_number); @@ -4122,15 +4157,9 @@ MaybeObject* Runtime::GetElementOrCharAt(Isolate* isolate, } if (object->IsString() || object->IsNumber() || object->IsBoolean()) { - Handle<Object> prototype = GetPrototype(object); - return prototype->GetElement(index); + return object->GetPrototype()->GetElement(index); } - return GetElement(object, index); -} - - -MaybeObject* Runtime::GetElement(Handle<Object> object, uint32_t index) { return object->GetElement(index); } @@ -4203,40 +4232,63 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) { // // Additionally, we need to make sure that we do not cache results // for objects that require access checks. - if (args[0]->IsJSObject() && - !args[0]->IsJSGlobalProxy() && - !args[0]->IsAccessCheckNeeded() && - args[1]->IsString()) { - JSObject* receiver = JSObject::cast(args[0]); - String* key = String::cast(args[1]); - if (receiver->HasFastProperties()) { - // Attempt to use lookup cache. - Map* receiver_map = receiver->map(); - KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache(); - int offset = keyed_lookup_cache->Lookup(receiver_map, key); - if (offset != -1) { - Object* value = receiver->FastPropertyAt(offset); - return value->IsTheHole() ? isolate->heap()->undefined_value() : value; - } - // Lookup cache miss. Perform lookup and update the cache if appropriate. - LookupResult result; - receiver->LocalLookup(key, &result); - if (result.IsProperty() && result.type() == FIELD) { - int offset = result.GetFieldIndex(); - keyed_lookup_cache->Update(receiver_map, key, offset); - return receiver->FastPropertyAt(offset); + if (args[0]->IsJSObject()) { + if (!args[0]->IsJSGlobalProxy() && + !args[0]->IsAccessCheckNeeded() && + args[1]->IsString()) { + JSObject* receiver = JSObject::cast(args[0]); + String* key = String::cast(args[1]); + if (receiver->HasFastProperties()) { + // Attempt to use lookup cache. + Map* receiver_map = receiver->map(); + KeyedLookupCache* keyed_lookup_cache = isolate->keyed_lookup_cache(); + int offset = keyed_lookup_cache->Lookup(receiver_map, key); + if (offset != -1) { + Object* value = receiver->FastPropertyAt(offset); + return value->IsTheHole() + ? isolate->heap()->undefined_value() + : value; + } + // Lookup cache miss. Perform lookup and update the cache if + // appropriate. + LookupResult result(isolate); + receiver->LocalLookup(key, &result); + if (result.IsFound() && result.type() == FIELD) { + int offset = result.GetFieldIndex(); + keyed_lookup_cache->Update(receiver_map, key, offset); + return receiver->FastPropertyAt(offset); + } + } else { + // Attempt dictionary lookup. + StringDictionary* dictionary = receiver->property_dictionary(); + int entry = dictionary->FindEntry(key); + if ((entry != StringDictionary::kNotFound) && + (dictionary->DetailsAt(entry).type() == NORMAL)) { + Object* value = dictionary->ValueAt(entry); + if (!receiver->IsGlobalObject()) return value; + value = JSGlobalPropertyCell::cast(value)->value(); + if (!value->IsTheHole()) return value; + // If value is the hole do the general lookup. + } } - } else { - // Attempt dictionary lookup. - StringDictionary* dictionary = receiver->property_dictionary(); - int entry = dictionary->FindEntry(key); - if ((entry != StringDictionary::kNotFound) && - (dictionary->DetailsAt(entry).type() == NORMAL)) { - Object* value = dictionary->ValueAt(entry); - if (!receiver->IsGlobalObject()) return value; - value = JSGlobalPropertyCell::cast(value)->value(); - if (!value->IsTheHole()) return value; - // If value is the hole do the general lookup. + } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) { + // JSObject without a string key. If the key is a Smi, check for a + // definite out-of-bounds access to elements, which is a strong indicator + // that subsequent accesses will also call the runtime. Proactively + // transition elements to FAST_ELEMENTS to avoid excessive boxing of + // doubles for those future calls in the case that the elements would + // become FAST_DOUBLE_ELEMENTS. + Handle<JSObject> js_object(args.at<JSObject>(0)); + ElementsKind elements_kind = js_object->GetElementsKind(); + if (elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_DOUBLE_ELEMENTS) { + FixedArrayBase* elements = js_object->elements(); + if (args.at<Smi>(1)->value() >= elements->length()) { + MaybeObject* maybe_object = TransitionElements(js_object, + FAST_ELEMENTS, + isolate); + if (maybe_object->IsFailure()) return maybe_object; + } } } } else if (args[0]->IsString() && args[1]->IsSmi()) { @@ -4269,27 +4321,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) { CONVERT_CHECKED(String, name, args[1]); CONVERT_CHECKED(Smi, flag_setter, args[2]); Object* fun = args[3]; - RUNTIME_ASSERT(fun->IsJSFunction() || fun->IsUndefined()); CONVERT_CHECKED(Smi, flag_attr, args[4]); + int unchecked = flag_attr->value(); RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); - RUNTIME_ASSERT(!obj->IsNull()); - LookupResult result; - obj->LocalLookupRealNamedProperty(name, &result); - PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked); - // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION - // delete it to avoid running into trouble in DefineAccessor, which - // handles this incorrectly if the property is readonly (does nothing) - if (result.IsProperty() && - (result.type() == FIELD || result.type() == NORMAL - || result.type() == CONSTANT_FUNCTION)) { - Object* ok; - { MaybeObject* maybe_ok = - obj->DeleteProperty(name, JSReceiver::NORMAL_DELETION); - if (!maybe_ok->ToObject(&ok)) return maybe_ok; - } - } + + RUNTIME_ASSERT(!obj->IsNull()); + RUNTIME_ASSERT(fun->IsSpecFunction() || fun->IsUndefined()); return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr); } @@ -4305,22 +4344,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { CONVERT_ARG_CHECKED(JSObject, js_object, 0); CONVERT_ARG_CHECKED(String, name, 1); Handle<Object> obj_value = args.at<Object>(2); - CONVERT_CHECKED(Smi, flag, args[3]); + int unchecked = flag->value(); RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); - PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked); // Check if this is an element. uint32_t index; bool is_element = name->AsArrayIndex(&index); - // Special case for elements if any of the flags are true. + // Special case for elements if any of the flags might be involved. // If elements are in fast case we always implicitly assume that: // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false. - if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) && - is_element) { + if (is_element && (attr != NONE || + js_object->HasLocalElement(index) == JSObject::DICTIONARY_ELEMENT)) { // Normalize the elements to enable attributes on the property. if (js_object->IsJSGlobalProxy()) { // We do not need to do access checks here since these has already @@ -4342,12 +4380,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { return isolate->Throw(*error); } - Handle<SeededNumberDictionary> dictionary = NormalizeElements(js_object); + Handle<SeededNumberDictionary> dictionary = + JSObject::NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); Handle<SeededNumberDictionary> extended_dictionary = - SeededNumberDictionarySet(dictionary, index, obj_value, details); + SeededNumberDictionary::Set(dictionary, index, obj_value, details); if (*extended_dictionary != *dictionary) { if (js_object->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) { FixedArray::cast(js_object->elements())->set(1, *extended_dictionary); @@ -4358,15 +4397,29 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { return *obj_value; } - LookupResult result; + LookupResult result(isolate); js_object->LocalLookupRealNamedProperty(*name, &result); - // To be compatible with safari we do not change the value on API objects - // in defineProperty. Firefox disagrees here, and actually changes the value. - if (result.IsProperty() && - (result.type() == CALLBACKS) && - result.GetCallbackObject()->IsAccessorInfo()) { - return isolate->heap()->undefined_value(); + // Special case for callback properties. + if (result.IsFound() && result.type() == CALLBACKS) { + Object* callback = result.GetCallbackObject(); + // To be compatible with Safari we do not change the value on API objects + // in Object.defineProperty(). Firefox disagrees here, and actually changes + // the value. + if (callback->IsAccessorInfo()) { + return isolate->heap()->undefined_value(); + } + // Avoid redefining foreign callback as data property, just use the stored + // setter to update the value instead. + // TODO(mstarzinger): So far this only works if property attributes don't + // change, this should be fixed once we cleanup the underlying code. + if (callback->IsForeign() && result.GetAttributes() == attr) { + return js_object->SetPropertyWithCallback(callback, + *name, + *obj_value, + result.holder(), + kStrictMode); + } } // Take special care when attributes are different and there is already @@ -4383,7 +4436,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { // we don't have to check for null. js_object = Handle<JSObject>(JSObject::cast(js_object->GetPrototype())); } - NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0); + JSObject::NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0); // Use IgnoreAttributes version since a readonly property may be // overridden and SetProperty does not allow this. return js_object->SetLocalPropertyIgnoreAttributes(*name, @@ -4408,12 +4461,13 @@ static MaybeObject* NormalizeObjectSetElement(Isolate* isolate, Handle<Object> value, PropertyAttributes attr) { // Normalize the elements to enable attributes on the property. - Handle<SeededNumberDictionary> dictionary = NormalizeElements(js_object); + Handle<SeededNumberDictionary> dictionary = + JSObject::NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); Handle<SeededNumberDictionary> extended_dictionary = - SeededNumberDictionarySet(dictionary, index, value, details); + SeededNumberDictionary::Set(dictionary, index, value, details); if (*extended_dictionary != *dictionary) { js_object->set_elements(*extended_dictionary); } @@ -4437,6 +4491,14 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate, return isolate->Throw(*error); } + if (object->IsJSProxy()) { + bool has_pending_exception = false; + Handle<Object> name = Execution::ToString(key, &has_pending_exception); + if (has_pending_exception) return Failure::Exception(); + return JSProxy::cast(*object)->SetProperty( + String::cast(*name), *value, attr, strict_mode); + } + // If the object isn't a JavaScript object, we ignore the store. if (!object->IsJSObject()) return *value; @@ -4460,7 +4522,8 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate, return NormalizeObjectSetElement(isolate, js_object, index, value, attr); } - Handle<Object> result = SetElement(js_object, index, value, strict_mode); + Handle<Object> result = + JSObject::SetElement(js_object, index, value, strict_mode); if (result.is_null()) return Failure::Exception(); return *value; } @@ -4475,11 +4538,13 @@ MaybeObject* Runtime::SetObjectProperty(Isolate* isolate, value, attr); } - result = SetElement(js_object, index, value, strict_mode); + result = + JSObject::SetElement(js_object, index, value, strict_mode); } else { Handle<String> key_string = Handle<String>::cast(key); key_string->TryFlatten(); - result = SetProperty(js_object, key_string, value, attr, strict_mode); + result = JSReceiver::SetProperty( + js_object, key_string, value, attr, strict_mode); } if (result.is_null()) return Failure::Exception(); return *value; @@ -4556,7 +4621,7 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate, // Check if the given key is an array index. uint32_t index; - if (receiver->IsJSObject() && key->ToArrayIndex(&index)) { + if (key->ToArrayIndex(&index)) { // In Firefox/SpiderMonkey, Safari and Opera you can access the // characters of a string using [] notation. In the case of a // String object we just need to redirect the deletion to the @@ -4567,8 +4632,7 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Isolate* isolate, return isolate->heap()->true_value(); } - return JSObject::cast(*receiver)->DeleteElement( - index, JSReceiver::FORCE_DELETION); + return receiver->DeleteElement(index, JSReceiver::FORCE_DELETION); } Handle<String> key_string; @@ -4603,10 +4667,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) { StrictModeFlag strict_mode = kNonStrictMode; if (args.length() == 5) { - CONVERT_SMI_ARG_CHECKED(strict_unchecked, 4); - RUNTIME_ASSERT(strict_unchecked == kStrictMode || - strict_unchecked == kNonStrictMode); - strict_mode = static_cast<StrictModeFlag>(strict_unchecked); + CONVERT_STRICT_MODE_ARG(strict_mode_flag, 4); + strict_mode = strict_mode_flag; } return Runtime::SetObjectProperty(isolate, @@ -4618,6 +4680,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) { + NoHandleAllocation ha; + RUNTIME_ASSERT(args.length() == 1); + Handle<Object> object = args.at<Object>(0); + return TransitionElements(object, FAST_DOUBLE_ELEMENTS, isolate); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) { + NoHandleAllocation ha; + RUNTIME_ASSERT(args.length() == 1); + Handle<Object> object = args.at<Object>(0); + return TransitionElements(object, FAST_ELEMENTS, isolate); +} + + // Set the native flag on the function. // This is used to decide if we should transform null and undefined // into the global object when doing call and apply. @@ -4635,6 +4713,46 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNativeFlag) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) { + RUNTIME_ASSERT(args.length() == 5); + CONVERT_ARG_CHECKED(JSObject, object, 0); + CONVERT_SMI_ARG_CHECKED(store_index, 1); + Handle<Object> value = args.at<Object>(2); + CONVERT_ARG_CHECKED(FixedArray, literals, 3); + CONVERT_SMI_ARG_CHECKED(literal_index, 4); + HandleScope scope; + + Object* raw_boilerplate_object = literals->get(literal_index); + Handle<JSArray> boilerplate_object(JSArray::cast(raw_boilerplate_object)); +#if DEBUG + ElementsKind elements_kind = object->GetElementsKind(); +#endif + ASSERT(elements_kind <= FAST_DOUBLE_ELEMENTS); + // Smis should never trigger transitions. + ASSERT(!value->IsSmi()); + + if (value->IsNumber()) { + ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); + JSObject::TransitionElementsKind(object, FAST_DOUBLE_ELEMENTS); + JSObject::TransitionElementsKind(boilerplate_object, FAST_DOUBLE_ELEMENTS); + ASSERT(object->GetElementsKind() == FAST_DOUBLE_ELEMENTS); + FixedDoubleArray* double_array = + FixedDoubleArray::cast(object->elements()); + HeapNumber* number = HeapNumber::cast(*value); + double_array->set(store_index, number->Number()); + } else { + ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_DOUBLE_ELEMENTS); + JSObject::TransitionElementsKind(object, FAST_ELEMENTS); + JSObject::TransitionElementsKind(boilerplate_object, FAST_ELEMENTS); + FixedArray* object_array = + FixedArray::cast(object->elements()); + object_array->set(store_index, *value); + } + return *object; +} + + // Set a local property, even if it is READ_ONLY. If the property does not // exist, it will be added with attributes NONE. RUNTIME_FUNCTION(MaybeObject*, Runtime_IgnoreAttributesAndSetProperty) { @@ -4664,8 +4782,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteProperty) { CONVERT_CHECKED(JSReceiver, object, args[0]); CONVERT_CHECKED(String, key, args[1]); - CONVERT_SMI_ARG_CHECKED(strict, 2); - return object->DeleteProperty(key, (strict == kStrictMode) + CONVERT_STRICT_MODE_ARG(strict_mode, 2); + return object->DeleteProperty(key, (strict_mode == kStrictMode) ? JSReceiver::STRICT_DELETION : JSReceiver::NORMAL_DELETION); } @@ -4730,29 +4848,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLocalProperty) { RUNTIME_FUNCTION(MaybeObject*, Runtime_HasProperty) { NoHandleAllocation na; ASSERT(args.length() == 2); + CONVERT_CHECKED(JSReceiver, receiver, args[0]); + CONVERT_CHECKED(String, key, args[1]); - // Only JS receivers can have properties. - if (args[0]->IsJSReceiver()) { - JSReceiver* receiver = JSReceiver::cast(args[0]); - CONVERT_CHECKED(String, key, args[1]); - if (receiver->HasProperty(key)) return isolate->heap()->true_value(); - } - return isolate->heap()->false_value(); + bool result = receiver->HasProperty(key); + if (isolate->has_pending_exception()) return Failure::Exception(); + return isolate->heap()->ToBoolean(result); } RUNTIME_FUNCTION(MaybeObject*, Runtime_HasElement) { NoHandleAllocation na; ASSERT(args.length() == 2); + CONVERT_CHECKED(JSReceiver, receiver, args[0]); + CONVERT_CHECKED(Smi, index, args[1]); - // Only JS objects can have elements. - if (args[0]->IsJSObject()) { - JSObject* object = JSObject::cast(args[0]); - CONVERT_CHECKED(Smi, index_obj, args[1]); - uint32_t index = index_obj->value(); - if (object->HasElement(index)) return isolate->heap()->true_value(); - } - return isolate->heap()->false_value(); + bool result = receiver->HasElement(index->value()); + if (isolate->has_pending_exception()) return Failure::Exception(); + return isolate->heap()->ToBoolean(result); } @@ -4765,7 +4878,37 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) { uint32_t index; if (key->AsArrayIndex(&index)) { - return isolate->heap()->ToBoolean(object->HasElement(index)); + JSObject::LocalElementType type = object->HasLocalElement(index); + switch (type) { + case JSObject::UNDEFINED_ELEMENT: + case JSObject::STRING_CHARACTER_ELEMENT: + return isolate->heap()->false_value(); + case JSObject::INTERCEPTED_ELEMENT: + case JSObject::FAST_ELEMENT: + return isolate->heap()->true_value(); + case JSObject::DICTIONARY_ELEMENT: { + if (object->IsJSGlobalProxy()) { + Object* proto = object->GetPrototype(); + if (proto->IsNull()) { + return isolate->heap()->false_value(); + } + ASSERT(proto->IsJSGlobalObject()); + object = JSObject::cast(proto); + } + FixedArray* elements = FixedArray::cast(object->elements()); + SeededNumberDictionary* dictionary = NULL; + if (elements->map() == + isolate->heap()->non_strict_arguments_elements_map()) { + dictionary = SeededNumberDictionary::cast(elements->get(1)); + } else { + dictionary = SeededNumberDictionary::cast(elements); + } + int entry = dictionary->FindEntry(index); + ASSERT(entry != SeededNumberDictionary::kNotFound); + PropertyDetails details = dictionary->DetailsAt(entry); + return isolate->heap()->ToBoolean(!details.IsDontEnum()); + } + } } PropertyAttributes att = object->GetLocalPropertyAttribute(key); @@ -4776,8 +4919,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) { RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) { HandleScope scope(isolate); ASSERT(args.length() == 1); - CONVERT_ARG_CHECKED(JSObject, object, 0); - return *GetKeysFor(object); + CONVERT_ARG_CHECKED(JSReceiver, object, 0); + bool threw = false; + Handle<JSArray> result = GetKeysFor(object, &threw); + if (threw) return Failure::Exception(); + return *result; } @@ -4789,14 +4935,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) { RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNamesFast) { ASSERT(args.length() == 1); - CONVERT_CHECKED(JSObject, raw_object, args[0]); + CONVERT_CHECKED(JSReceiver, raw_object, args[0]); if (raw_object->IsSimpleEnum()) return raw_object->map(); HandleScope scope(isolate); - Handle<JSObject> object(raw_object); - Handle<FixedArray> content = GetKeysInFixedArrayFor(object, - INCLUDE_PROTOS); + Handle<JSReceiver> object(raw_object); + bool threw = false; + Handle<FixedArray> content = + GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, &threw); + if (threw) return Failure::Exception(); // Test again, since cache may have been built by preceding call. if (object->IsSimpleEnum()) return object->map(); @@ -4861,7 +5009,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) { return *isolate->factory()->NewJSArray(0); } int n; - n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)); + n = jsproto->NumberOfLocalProperties(); local_property_count[i] = n; total_property_count += n; if (i < length - 1) { @@ -4993,8 +5141,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LocalKeys) { object = Handle<JSObject>::cast(proto); } - Handle<FixedArray> contents = GetKeysInFixedArrayFor(object, - LOCAL_ONLY); + bool threw = false; + Handle<FixedArray> contents = + GetKeysInFixedArrayFor(object, LOCAL_ONLY, &threw); + if (threw) return Failure::Exception(); + // Some fast paths through GetKeysInFixedArrayFor reuse a cached // property array and since the result is mutable we have to create // a fresh clone on each invocation. @@ -5058,7 +5209,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArgumentsProperty) { if (key->Equals(isolate->heap()->callee_symbol())) { Object* function = frame->function(); if (function->IsJSFunction() && - JSFunction::cast(function)->shared()->strict_mode()) { + !JSFunction::cast(function)->shared()->is_classic_mode()) { return isolate->Throw(*isolate->factory()->NewTypeError( "strict_arguments_callee", HandleVector<Object>(NULL, 0))); } @@ -5071,31 +5222,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArgumentsProperty) { RUNTIME_FUNCTION(MaybeObject*, Runtime_ToFastProperties) { - HandleScope scope(isolate); - ASSERT(args.length() == 1); - Handle<Object> object = args.at<Object>(0); - if (object->IsJSObject()) { - Handle<JSObject> js_object = Handle<JSObject>::cast(object); - if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) { - MaybeObject* ok = js_object->TransformToFastProperties(0); - if (ok->IsRetryAfterGC()) return ok; - } - } - return *object; + Object* object = args[0]; + return (object->IsJSObject() && !object->IsGlobalObject()) + ? JSObject::cast(object)->TransformToFastProperties(0) + : object; } RUNTIME_FUNCTION(MaybeObject*, Runtime_ToSlowProperties) { - HandleScope scope(isolate); - ASSERT(args.length() == 1); - Handle<Object> object = args.at<Object>(0); - if (object->IsJSObject() && !object->IsJSGlobalProxy()) { - Handle<JSObject> js_object = Handle<JSObject>::cast(object); - NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0); - } - return *object; + Object* obj = args[0]; + return (obj->IsJSObject() && !obj->IsJSGlobalProxy()) + ? JSObject::cast(obj)->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0) + : obj; } @@ -5579,7 +5719,7 @@ static MaybeObject* SlowQuoteJsonString(Isolate* isolate, StringType* new_string = StringType::cast(new_object); Char* write_cursor = reinterpret_cast<Char*>( - new_string->address() + SeqAsciiString::kHeaderSize); + new_string->address() + SeqString::kHeaderSize); if (comma) *(write_cursor++) = ','; *(write_cursor++) = '"'; @@ -5667,16 +5807,15 @@ static MaybeObject* QuoteJsonString(Isolate* isolate, StringType* new_string = StringType::cast(new_object); ASSERT(isolate->heap()->new_space()->Contains(new_string)); - STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); Char* write_cursor = reinterpret_cast<Char*>( - new_string->address() + SeqAsciiString::kHeaderSize); + new_string->address() + SeqString::kHeaderSize); if (comma) *(write_cursor++) = ','; write_cursor = WriteQuoteJsonString<Char, Char>(isolate, write_cursor, characters); int final_length = static_cast<int>( write_cursor - reinterpret_cast<Char*>( - new_string->address() + SeqAsciiString::kHeaderSize)); + new_string->address() + SeqString::kHeaderSize)); isolate->heap()->new_space()-> template ShrinkStringAtAllocationBoundary<StringType>( new_string, final_length); @@ -5754,9 +5893,8 @@ static MaybeObject* QuoteJsonStringArray(Isolate* isolate, StringType* new_string = StringType::cast(new_object); ASSERT(isolate->heap()->new_space()->Contains(new_string)); - STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); Char* write_cursor = reinterpret_cast<Char*>( - new_string->address() + SeqAsciiString::kHeaderSize); + new_string->address() + SeqString::kHeaderSize); *(write_cursor++) = '['; for (int i = 0; i < length; i++) { if (i != 0) *(write_cursor++) = ','; @@ -5777,7 +5915,7 @@ static MaybeObject* QuoteJsonStringArray(Isolate* isolate, int final_length = static_cast<int>( write_cursor - reinterpret_cast<Char*>( - new_string->address() + SeqAsciiString::kHeaderSize)); + new_string->address() + SeqString::kHeaderSize)); isolate->heap()->new_space()-> template ShrinkStringAtAllocationBoundary<StringType>( new_string, final_length); @@ -5868,8 +6006,8 @@ MUST_USE_RESULT static MaybeObject* ConvertCaseHelper( // // Allocate the resulting string. // - // NOTE: This assumes that the upper/lower case of an ascii - // character is also ascii. This is currently the case, but it + // NOTE: This assumes that the upper/lower case of an ASCII + // character is also ASCII. This is currently the case, but it // might break in the future if we implement more context and locale // dependent upper/lower conversions. Object* o; @@ -5969,9 +6107,9 @@ static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF; // This function is only useful when it can be inlined and the // boundaries are statically known. // Requires: all bytes in the input word and the boundaries must be -// ascii (less than 0x7F). +// ASCII (less than 0x7F). static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) { - // Every byte in an ascii string is less than or equal to 0x7F. + // Every byte in an ASCII string is less than or equal to 0x7F. ASSERT((w & (kOneInEveryByte * 0x7F)) == w); // Use strict inequalities since in edge cases the function could be // further simplified. @@ -6099,10 +6237,10 @@ MUST_USE_RESULT static MaybeObject* ConvertCase( // Assume that the string is not empty; we need this assumption later if (length == 0) return s; - // Simpler handling of ascii strings. + // Simpler handling of ASCII strings. // - // NOTE: This assumes that the upper/lower case of an ascii - // character is also ascii. This is currently the case, but it + // NOTE: This assumes that the upper/lower case of an ASCII + // character is also ASCII. This is currently the case, but it // might break in the future if we implement more context and locale // dependent upper/lower conversions. if (s->IsSeqAsciiString()) { @@ -6146,7 +6284,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToUpperCase) { static inline bool IsTrimWhiteSpace(unibrow::uchar c) { - return unibrow::WhiteSpace::Is(c) || c == 0x200b; + return unibrow::WhiteSpace::Is(c) || c == 0x200b || c == 0xfeff; } @@ -6229,6 +6367,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { int part_count = indices.length(); Handle<JSArray> result = isolate->factory()->NewJSArray(part_count); + MaybeObject* maybe_result = result->EnsureCanContainHeapObjectElements(); + if (maybe_result->IsFailure()) return maybe_result; result->set_length(Smi::FromInt(part_count)); ASSERT(result->HasFastElements()); @@ -6263,7 +6403,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { } -// Copies ascii characters to the given fixed array looking up +// Copies ASCII characters to the given fixed array looking up // one-char strings in the cache. Gives up on the first char that is // not in the cache and fills the remainder with smi zeros. Returns // the length of the successfully copied prefix. @@ -6275,11 +6415,11 @@ static int CopyCachedAsciiCharsToArray(Heap* heap, FixedArray* ascii_cache = heap->single_character_string_cache(); Object* undefined = heap->undefined_value(); int i; + WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); for (i = 0; i < length; ++i) { Object* value = ascii_cache->get(chars[i]); if (value == undefined) break; - ASSERT(!heap->InNewSpace(value)); - elements->set(i, value, SKIP_WRITE_BARRIER); + elements->set(i, value, mode); } if (i < length) { ASSERT(Smi::FromInt(0) == 0); @@ -6603,6 +6743,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) { // This assumption is used by the slice encoding in one or two smis. ASSERT(Smi::kMaxValue >= String::kMaxLength); + MaybeObject* maybe_result = array->EnsureCanContainHeapObjectElements(); + if (maybe_result->IsFailure()) return maybe_result; + int special_length = special->length(); if (!array->HasFastElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); @@ -6830,7 +6973,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { NoHandleAllocation ha; ASSERT(args.length() == 3); CONVERT_CHECKED(JSArray, elements_array, args[0]); - RUNTIME_ASSERT(elements_array->HasFastElements()); + RUNTIME_ASSERT(elements_array->HasFastElements() || + elements_array->HasFastSmiOnlyElements()); CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); CONVERT_CHECKED(String, separator, args[2]); // elements_array is fast-mode JSarray of alternating positions @@ -7325,7 +7469,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_log) { return isolate->transcendental_cache()->Get(TranscendentalCache::LOG, x); } - +// Slow version of Math.pow. We check for fast paths for special cases. +// Used if SSE2/VFP3 is not available. RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow) { NoHandleAllocation ha; ASSERT(args.length() == 2); @@ -7341,22 +7486,36 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow) { } CONVERT_DOUBLE_ARG_CHECKED(y, 1); - return isolate->heap()->AllocateHeapNumber(power_double_double(x, y)); + int y_int = static_cast<int>(y); + double result; + if (y == y_int) { + result = power_double_int(x, y_int); // Returns 1 if exponent is 0. + } else if (y == 0.5) { + result = (isinf(x)) ? V8_INFINITY : sqrt(x + 0.0); // Convert -0 to +0. + } else if (y == -0.5) { + result = (isinf(x)) ? 0 : 1.0 / sqrt(x + 0.0); // Convert -0 to +0. + } else { + result = power_double_double(x, y); + } + if (isnan(result)) return isolate->heap()->nan_value(); + return isolate->heap()->AllocateHeapNumber(result); } -// Fast version of Math.pow if we know that y is not an integer and -// y is not -0.5 or 0.5. Used as slowcase from codegen. +// Fast version of Math.pow if we know that y is not an integer and y is not +// -0.5 or 0.5. Used as slow case from full codegen. RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow_cfunction) { NoHandleAllocation ha; ASSERT(args.length() == 2); + isolate->counters()->math_pow()->Increment(); + CONVERT_DOUBLE_ARG_CHECKED(x, 0); CONVERT_DOUBLE_ARG_CHECKED(y, 1); if (y == 0) { return Smi::FromInt(1); - } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) { - return isolate->heap()->nan_value(); } else { - return isolate->heap()->AllocateHeapNumber(pow(x, y)); + double result = power_double_double(x, y); + if (isnan(result)) return isolate->heap()->nan_value(); + return isolate->heap()->AllocateHeapNumber(result); } } @@ -7386,7 +7545,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RoundNumber) { // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and // should be rounded to 2^30, which is not smi (for 31-bit smis, similar - // agument holds for 32-bit smis). + // argument holds for 32-bit smis). if (!sign && exponent < kSmiValueSize - 2) { return Smi::FromInt(static_cast<int>(value + 0.5)); } @@ -7434,7 +7593,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) { } -static int MakeDay(int year, int month, int day) { +static int MakeDay(int year, int month) { static const int day_from_month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152, @@ -7471,23 +7630,22 @@ static int MakeDay(int year, int month, int day) { year1 / 400 - base_day; - if (year % 4 || (year % 100 == 0 && year % 400 != 0)) { - return day_from_year + day_from_month[month] + day - 1; + if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) { + return day_from_year + day_from_month[month]; } - return day_from_year + day_from_month_leap[month] + day - 1; + return day_from_year + day_from_month_leap[month]; } RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) { NoHandleAllocation ha; - ASSERT(args.length() == 3); + ASSERT(args.length() == 2); CONVERT_SMI_ARG_CHECKED(year, 0); CONVERT_SMI_ARG_CHECKED(month, 1); - CONVERT_SMI_ARG_CHECKED(date, 2); - return Smi::FromInt(MakeDay(year, month, date)); + return Smi::FromInt(MakeDay(year, month)); } @@ -7716,7 +7874,7 @@ static inline void DateYMDFromTimeAfter1970(int date, month = kMonthInYear[date]; day = kDayInYear[date]; - ASSERT(MakeDay(year, month, day) == save_date); + ASSERT(MakeDay(year, month) + day - 1 == save_date); } @@ -7730,7 +7888,7 @@ static inline void DateYMDFromTimeSlow(int date, year = 400 * (date / kDaysIn400Years) - kYearsOffset; date %= kDaysIn400Years; - ASSERT(MakeDay(year, 0, 1) + date == save_date); + ASSERT(MakeDay(year, 0) + date == save_date); date--; int yd1 = date / kDaysIn100Years; @@ -7753,8 +7911,8 @@ static inline void DateYMDFromTimeSlow(int date, ASSERT(is_leap || (date >= 0)); ASSERT((date < 365) || (is_leap && (date < 366))); ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0)))); - ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date)); - ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date)); + ASSERT(is_leap || ((MakeDay(year, 0) + date) == save_date)); + ASSERT(!is_leap || ((MakeDay(year, 0) + date + 1) == save_date)); if (is_leap) { day = kDayInYear[2*365 + 1 + date]; @@ -7764,7 +7922,7 @@ static inline void DateYMDFromTimeSlow(int date, month = kMonthInYear[date]; } - ASSERT(MakeDay(year, month, day) == save_date); + ASSERT(MakeDay(year, month) + day - 1 == save_date); } @@ -7788,11 +7946,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateYMDFromTime) { int year, month, day; DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day); - RUNTIME_ASSERT(res_array->elements()->map() == - isolate->heap()->fixed_array_map()); - FixedArray* elms = FixedArray::cast(res_array->elements()); - RUNTIME_ASSERT(elms->length() == 3); + FixedArrayBase* elms_base = FixedArrayBase::cast(res_array->elements()); + RUNTIME_ASSERT(elms_base->length() == 3); + RUNTIME_ASSERT(res_array->HasFastTypeElements()); + MaybeObject* maybe = res_array->EnsureWritableFastElements(); + if (maybe->IsFailure()) return maybe; + FixedArray* elms = FixedArray::cast(res_array->elements()); elms->set(0, Smi::FromInt(year)); elms->set(1, Smi::FromInt(month)); elms->set(2, Smi::FromInt(day)); @@ -7846,14 +8006,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewArgumentsFast) { --index; } - ScopeInfo<> scope_info(callee->shared()->scope_info()); + Handle<ScopeInfo> scope_info(callee->shared()->scope_info()); while (index >= 0) { // Detect duplicate names to the right in the parameter list. - Handle<String> name = scope_info.parameter_name(index); - int context_slot_count = scope_info.number_of_context_slots(); + Handle<String> name(scope_info->ParameterName(index)); + int context_local_count = scope_info->ContextLocalCount(); bool duplicate = false; for (int j = index + 1; j < parameter_count; ++j) { - if (scope_info.parameter_name(j).is_identical_to(name)) { + if (scope_info->ParameterName(j) == *name) { duplicate = true; break; } @@ -7868,17 +8028,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewArgumentsFast) { // The context index goes in the parameter map with a hole in the // arguments array. int context_index = -1; - for (int j = Context::MIN_CONTEXT_SLOTS; - j < context_slot_count; - ++j) { - if (scope_info.context_slot_name(j).is_identical_to(name)) { + for (int j = 0; j < context_local_count; ++j) { + if (scope_info->ContextLocalName(j) == *name) { context_index = j; break; } } ASSERT(context_index >= 0); arguments->set_the_hole(index); - parameter_map->set(index + 2, Smi::FromInt(context_index)); + parameter_map->set(index + 2, Smi::FromInt( + Context::MIN_CONTEXT_SLOTS + context_index)); } --index; @@ -7921,7 +8080,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewStrictArgumentsFast) { AssertNoAllocation no_gc; FixedArray* array = reinterpret_cast<FixedArray*>(obj); - array->set_map(isolate->heap()->fixed_array_map()); + array->set_map_no_write_barrier(isolate->heap()->fixed_array_map()); array->set_length(length); WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc); @@ -7952,76 +8111,170 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) { } -static SmartArrayPointer<Object**> GetNonBoundArguments(int bound_argc, - int* total_argc) { +// Find the arguments of the JavaScript function invocation that called +// into C++ code. Collect these in a newly allocated array of handles (possibly +// prefixed by a number of empty handles). +static SmartArrayPointer<Handle<Object> > GetCallerArguments( + int prefix_argc, + int* total_argc) { // Find frame containing arguments passed to the caller. JavaScriptFrameIterator it; JavaScriptFrame* frame = it.frame(); List<JSFunction*> functions(2); frame->GetFunctions(&functions); if (functions.length() > 1) { - int inlined_frame_index = functions.length() - 1; - JSFunction* inlined_function = functions[inlined_frame_index]; - int args_count = inlined_function->shared()->formal_parameter_count(); - ScopedVector<SlotRef> args_slots(args_count); - SlotRef::ComputeSlotMappingForArguments(frame, - inlined_frame_index, - &args_slots); - - *total_argc = bound_argc + args_count; - SmartArrayPointer<Object**> param_data(NewArray<Object**>(*total_argc)); + int inlined_jsframe_index = functions.length() - 1; + JSFunction* inlined_function = functions[inlined_jsframe_index]; + Vector<SlotRef> args_slots = + SlotRef::ComputeSlotMappingForArguments( + frame, + inlined_jsframe_index, + inlined_function->shared()->formal_parameter_count()); + + int args_count = args_slots.length(); + + *total_argc = prefix_argc + args_count; + SmartArrayPointer<Handle<Object> > param_data( + NewArray<Handle<Object> >(*total_argc)); for (int i = 0; i < args_count; i++) { Handle<Object> val = args_slots[i].GetValue(); - param_data[bound_argc + i] = val.location(); + param_data[prefix_argc + i] = val; } + + args_slots.Dispose(); + return param_data; } else { it.AdvanceToArgumentsFrame(); frame = it.frame(); int args_count = frame->ComputeParametersCount(); - *total_argc = bound_argc + args_count; - SmartArrayPointer<Object**> param_data(NewArray<Object**>(*total_argc)); + *total_argc = prefix_argc + args_count; + SmartArrayPointer<Handle<Object> > param_data( + NewArray<Handle<Object> >(*total_argc)); for (int i = 0; i < args_count; i++) { Handle<Object> val = Handle<Object>(frame->GetParameter(i)); - param_data[bound_argc + i] = val.location(); + param_data[prefix_argc + i] = val; } return param_data; } } +RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionBindArguments) { + HandleScope scope(isolate); + ASSERT(args.length() == 4); + CONVERT_ARG_CHECKED(JSFunction, bound_function, 0); + RUNTIME_ASSERT(args[3]->IsNumber()); + Handle<Object> bindee = args.at<Object>(1); + + // TODO(lrn): Create bound function in C++ code from premade shared info. + bound_function->shared()->set_bound(true); + // Get all arguments of calling function (Function.prototype.bind). + int argc = 0; + SmartArrayPointer<Handle<Object> > arguments = GetCallerArguments(0, &argc); + // Don't count the this-arg. + if (argc > 0) { + ASSERT(*arguments[0] == args[2]); + argc--; + } else { + ASSERT(args[2]->IsUndefined()); + } + // Initialize array of bindings (function, this, and any existing arguments + // if the function was already bound). + Handle<FixedArray> new_bindings; + int i; + if (bindee->IsJSFunction() && JSFunction::cast(*bindee)->shared()->bound()) { + Handle<FixedArray> old_bindings( + JSFunction::cast(*bindee)->function_bindings()); + new_bindings = + isolate->factory()->NewFixedArray(old_bindings->length() + argc); + bindee = Handle<Object>(old_bindings->get(JSFunction::kBoundFunctionIndex)); + i = 0; + for (int n = old_bindings->length(); i < n; i++) { + new_bindings->set(i, old_bindings->get(i)); + } + } else { + int array_size = JSFunction::kBoundArgumentsStartIndex + argc; + new_bindings = isolate->factory()->NewFixedArray(array_size); + new_bindings->set(JSFunction::kBoundFunctionIndex, *bindee); + new_bindings->set(JSFunction::kBoundThisIndex, args[2]); + i = 2; + } + // Copy arguments, skipping the first which is "this_arg". + for (int j = 0; j < argc; j++, i++) { + new_bindings->set(i, *arguments[j + 1]); + } + new_bindings->set_map_no_write_barrier( + isolate->heap()->fixed_cow_array_map()); + bound_function->set_function_bindings(*new_bindings); + + // Update length. + Handle<String> length_symbol = isolate->factory()->length_symbol(); + Handle<Object> new_length(args.at<Object>(3)); + PropertyAttributes attr = + static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); + ForceSetProperty(bound_function, length_symbol, new_length, attr); + return *bound_function; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionGetBindings) { + HandleScope handles(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSReceiver, callable, 0); + if (callable->IsJSFunction()) { + Handle<JSFunction> function = Handle<JSFunction>::cast(callable); + if (function->shared()->bound()) { + Handle<FixedArray> bindings(function->function_bindings()); + ASSERT(bindings->map() == isolate->heap()->fixed_cow_array_map()); + return *isolate->factory()->NewJSArrayWithElements(bindings); + } + } + return isolate->heap()->undefined_value(); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) { HandleScope scope(isolate); - ASSERT(args.length() == 2); + ASSERT(args.length() == 1); // First argument is a function to use as a constructor. CONVERT_ARG_CHECKED(JSFunction, function, 0); - - // Second argument is either null or an array of bound arguments. - Handle<FixedArray> bound_args; - int bound_argc = 0; - if (!args[1]->IsNull()) { - CONVERT_ARG_CHECKED(JSArray, params, 1); - RUNTIME_ASSERT(params->HasFastElements()); - bound_args = Handle<FixedArray>(FixedArray::cast(params->elements())); - bound_argc = Smi::cast(params->length())->value(); - } + RUNTIME_ASSERT(function->shared()->bound()); + + // The argument is a bound function. Extract its bound arguments + // and callable. + Handle<FixedArray> bound_args = + Handle<FixedArray>(FixedArray::cast(function->function_bindings())); + int bound_argc = bound_args->length() - JSFunction::kBoundArgumentsStartIndex; + Handle<Object> bound_function( + JSReceiver::cast(bound_args->get(JSFunction::kBoundFunctionIndex))); + ASSERT(!bound_function->IsJSFunction() || + !Handle<JSFunction>::cast(bound_function)->shared()->bound()); int total_argc = 0; - SmartArrayPointer<Object**> param_data = - GetNonBoundArguments(bound_argc, &total_argc); + SmartArrayPointer<Handle<Object> > param_data = + GetCallerArguments(bound_argc, &total_argc); for (int i = 0; i < bound_argc; i++) { - Handle<Object> val = Handle<Object>(bound_args->get(i)); - param_data[i] = val.location(); + param_data[i] = Handle<Object>(bound_args->get( + JSFunction::kBoundArgumentsStartIndex + i)); } + if (!bound_function->IsJSFunction()) { + bool exception_thrown; + bound_function = Execution::TryGetConstructorDelegate(bound_function, + &exception_thrown); + if (exception_thrown) return Failure::Exception(); + } + ASSERT(bound_function->IsJSFunction()); + bool exception = false; Handle<Object> result = - Execution::New(function, total_argc, *param_data, &exception); + Execution::New(Handle<JSFunction>::cast(bound_function), + total_argc, *param_data, &exception); if (exception) { - return Failure::Exception(); + return Failure::Exception(); } - ASSERT(!result.is_null()); return *result; } @@ -8034,12 +8287,9 @@ static void TrySettingInlineConstructStub(Isolate* isolate, prototype = Handle<Object>(function->instance_prototype(), isolate); } if (function->shared()->CanGenerateInlineConstructor(*prototype)) { - ConstructStubCompiler compiler; - MaybeObject* code = compiler.CompileConstructStub(*function); - if (!code->IsFailure()) { - function->shared()->set_construct_stub( - Code::cast(code->ToObjectUnchecked())); - } + ConstructStubCompiler compiler(isolate); + Handle<Code> code = compiler.CompileConstructStub(function); + function->shared()->set_construct_stub(*code); } } @@ -8098,9 +8348,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObject) { // available. We cannot use EnsureCompiled because that forces a // compilation through the shared function info which makes it // impossible for us to optimize. - Handle<SharedFunctionInfo> shared(function->shared(), isolate); - if (!function->is_compiled()) CompileLazy(function, CLEAR_EXCEPTION); + if (!function->is_compiled()) { + JSFunction::CompileLazy(function, CLEAR_EXCEPTION); + } + Handle<SharedFunctionInfo> shared(function->shared(), isolate); if (!function->has_initial_map() && shared->IsInobjectSlackTrackingInProgress()) { // The tracking is already in progress for another function. We can only @@ -8151,7 +8403,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyCompile) { // Compile the target function. ASSERT(!function->is_compiled()); - if (!CompileLazy(function, KEEP_EXCEPTION)) { + if (!JSFunction::CompileLazy(function, KEEP_EXCEPTION)) { return Failure::Exception(); } @@ -8166,6 +8418,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { ASSERT(args.length() == 1); Handle<JSFunction> function = args.at<JSFunction>(0); + function->shared()->set_profiler_ticks(0); + // If the function is not compiled ignore the lazy // recompilation. This can happen if the debugger is activated and // the function is returned to the not compiled state. @@ -8188,7 +8442,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { function->ReplaceCode(function->shared()->code()); return function->code(); } - if (CompileOptimized(function, AstNode::kNoNumber, CLEAR_EXCEPTION)) { + if (JSFunction::CompileOptimized(function, + AstNode::kNoNumber, + CLEAR_EXCEPTION)) { return function->code(); } if (FLAG_trace_opt) { @@ -8201,6 +8457,31 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { } +class ActivationsFinder : public ThreadVisitor { + public: + explicit ActivationsFinder(JSFunction* function) + : function_(function), has_activations_(false) {} + + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + if (has_activations_) return; + + for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { + JavaScriptFrame* frame = it.frame(); + if (frame->is_optimized() && frame->function() == function_) { + has_activations_ = true; + return; + } + } + } + + bool has_activations() { return has_activations_; } + + private: + JSFunction* function_; + bool has_activations_; +}; + + RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { HandleScope scope(isolate); ASSERT(args.length() == 1); @@ -8209,14 +8490,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { static_cast<Deoptimizer::BailoutType>(args.smi_at(0)); Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); ASSERT(isolate->heap()->IsAllocationAllowed()); - int frames = deoptimizer->output_count(); + int jsframes = deoptimizer->jsframe_count(); deoptimizer->MaterializeHeapNumbers(); delete deoptimizer; JavaScriptFrameIterator it(isolate); JavaScriptFrame* frame = NULL; - for (int i = 0; i < frames - 1; i++) it.Advance(); + for (int i = 0; i < jsframes - 1; i++) it.Advance(); frame = it.frame(); RUNTIME_ASSERT(frame->function()->IsJSFunction()); @@ -8247,17 +8528,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { return isolate->heap()->undefined_value(); } - // Count the number of optimized activations of the function. - int activations = 0; + // Find other optimized activations of the function. + bool has_other_activations = false; while (!it.done()) { JavaScriptFrame* frame = it.frame(); if (frame->is_optimized() && frame->function() == *function) { - activations++; + has_other_activations = true; + break; } it.Advance(); } - if (activations == 0) { + if (!has_other_activations) { + ActivationsFinder activations_finder(*function); + isolate->thread_manager()->IterateArchivedThreads(&activations_finder); + has_other_activations = activations_finder.has_activations(); + } + + if (!has_other_activations) { if (FLAG_trace_deopt) { PrintF("[removing optimized code for: "); function->PrintName(); @@ -8312,6 +8600,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) { RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) { HandleScope scope(isolate); ASSERT(args.length() == 1); + // The least significant bit (after untagging) indicates whether the + // function is currently optimized, regardless of reason. if (!V8::UseCrankshaft()) { return Smi::FromInt(4); // 4 == "never". } @@ -8395,7 +8685,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) { // Try to compile the optimized code. A true return value from // CompileOptimized means that compilation succeeded, not necessarily // that optimization succeeded. - if (CompileOptimized(function, ast_id, CLEAR_EXCEPTION) && + if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) && function->IsOptimized()) { DeoptimizationInputData* data = DeoptimizationInputData::cast( function->code()->deoptimization_data()); @@ -8452,19 +8742,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckIsBootstrapping) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) { +RUNTIME_FUNCTION(MaybeObject*, Runtime_Call) { HandleScope scope(isolate); - ASSERT(args.length() == 5); - CONVERT_CHECKED(JSReceiver, fun, args[0]); - Object* receiver = args[1]; - CONVERT_CHECKED(JSObject, arguments, args[2]); - CONVERT_CHECKED(Smi, shift, args[3]); - CONVERT_CHECKED(Smi, arity, args[4]); - - int offset = shift->value(); - int argc = arity->value(); - ASSERT(offset >= 0); - ASSERT(argc >= 0); + ASSERT(args.length() >= 2); + CONVERT_CHECKED(JSReceiver, fun, args[args.length() - 1]); + Object* receiver = args[0]; + int argc = args.length() - 2; // If there are too many arguments, allocate argv via malloc. const int argv_small_size = 10; @@ -8478,17 +8761,52 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) { } for (int i = 0; i < argc; ++i) { - MaybeObject* maybe = arguments->GetElement(offset + i); + MaybeObject* maybe = args[1 + i]; Object* object; if (!maybe->To<Object>(&object)) return maybe; argv[i] = Handle<Object>(object); } - bool threw = false; + bool threw; Handle<JSReceiver> hfun(fun); Handle<Object> hreceiver(receiver); - Handle<Object> result = Execution::Call( - hfun, hreceiver, argc, reinterpret_cast<Object***>(argv), &threw, true); + Handle<Object> result = + Execution::Call(hfun, hreceiver, argc, argv, &threw, true); + + if (threw) return Failure::Exception(); + return *result; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) { + HandleScope scope(isolate); + ASSERT(args.length() == 5); + CONVERT_ARG_CHECKED(JSReceiver, fun, 0); + Handle<Object> receiver = args.at<Object>(1); + CONVERT_ARG_CHECKED(JSObject, arguments, 2); + CONVERT_SMI_ARG_CHECKED(offset, 3); + CONVERT_SMI_ARG_CHECKED(argc, 4); + ASSERT(offset >= 0); + ASSERT(argc >= 0); + + // If there are too many arguments, allocate argv via malloc. + const int argv_small_size = 10; + Handle<Object> argv_small_buffer[argv_small_size]; + SmartArrayPointer<Handle<Object> > argv_large_buffer; + Handle<Object>* argv = argv_small_buffer; + if (argc > argv_small_size) { + argv = new Handle<Object>[argc]; + if (argv == NULL) return isolate->StackOverflow(); + argv_large_buffer = SmartArrayPointer<Handle<Object> >(argv); + } + + for (int i = 0; i < argc; ++i) { + argv[i] = Object::GetElement(arguments, offset + i); + } + + bool threw; + Handle<Object> result = + Execution::Call(fun, receiver, argc, argv, &threw, true); if (threw) return Failure::Exception(); return *result; @@ -8516,7 +8834,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewFunctionContext) { ASSERT(args.length() == 1); CONVERT_CHECKED(JSFunction, function, args[0]); - int length = function->shared()->scope_info()->NumberOfContextSlots(); + int length = function->shared()->scope_info()->ContextLength(); Object* result; { MaybeObject* maybe_result = isolate->heap()->AllocateFunctionContext(length, function); @@ -8602,7 +8920,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) { RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) { NoHandleAllocation ha; ASSERT(args.length() == 2); - SerializedScopeInfo* scope_info = SerializedScopeInfo::cast(args[0]); + ScopeInfo* scope_info = ScopeInfo::cast(args[0]); JSFunction* function; if (args[1]->IsSmi()) { // A smi sentinel indicates a context nested inside global code rather @@ -8651,18 +8969,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) { } // The slot was found in a JSObject, either a context extension object, - // the global object, or an arguments object. Try to delete it - // (respecting DONT_DELETE). For consistency with V8's usual behavior, - // which allows deleting all parameters in functions that mention - // 'arguments', we do this even for the case of slots found on an - // arguments object. The slot was found on an arguments object if the - // index is non-negative. + // the global object, or the subject of a with. Try to delete it + // (respecting DONT_DELETE). Handle<JSObject> object = Handle<JSObject>::cast(holder); - if (index >= 0) { - return object->DeleteElement(index, JSReceiver::NORMAL_DELETION); - } else { - return object->DeleteProperty(*name, JSReceiver::NORMAL_DELETION); - } + return object->DeleteProperty(*name, JSReceiver::NORMAL_DELETION); } @@ -8747,52 +9057,53 @@ static ObjectPair LoadContextSlotHelper(Arguments args, &attributes, &binding_flags); - // If the index is non-negative, the slot has been found in a local - // variable or a parameter. Read it from the context object or the - // arguments object. + // If the index is non-negative, the slot has been found in a context. if (index >= 0) { - // If the "property" we were looking for is a local variable or an - // argument in a context, the receiver is the global object; see - // ECMA-262, 3rd., 10.1.6 and 10.2.3. + ASSERT(holder->IsContext()); + // If the "property" we were looking for is a local variable, the + // receiver is the global object; see ECMA-262, 3rd., 10.1.6 and 10.2.3. // - // Use the hole as the receiver to signal that the receiver is - // implicit and that the global receiver should be used. + // Use the hole as the receiver to signal that the receiver is implicit + // and that the global receiver should be used (as distinguished from an + // explicit receiver that happens to be a global object). Handle<Object> receiver = isolate->factory()->the_hole_value(); - MaybeObject* value = (holder->IsContext()) - ? Context::cast(*holder)->get(index) - : JSObject::cast(*holder)->GetElement(index); + Object* value = Context::cast(*holder)->get(index); // Check for uninitialized bindings. - if (holder->IsContext() && - binding_flags == MUTABLE_CHECK_INITIALIZED && - value->IsTheHole()) { - Handle<Object> reference_error = - isolate->factory()->NewReferenceError("not_defined", - HandleVector(&name, 1)); - return MakePair(isolate->Throw(*reference_error), NULL); - } else { - return MakePair(Unhole(isolate->heap(), value, attributes), *receiver); + switch (binding_flags) { + case MUTABLE_CHECK_INITIALIZED: + case IMMUTABLE_CHECK_INITIALIZED_HARMONY: + if (value->IsTheHole()) { + Handle<Object> reference_error = + isolate->factory()->NewReferenceError("not_defined", + HandleVector(&name, 1)); + return MakePair(isolate->Throw(*reference_error), NULL); + } + // FALLTHROUGH + case MUTABLE_IS_INITIALIZED: + case IMMUTABLE_IS_INITIALIZED: + case IMMUTABLE_IS_INITIALIZED_HARMONY: + ASSERT(!value->IsTheHole()); + return MakePair(value, *receiver); + case IMMUTABLE_CHECK_INITIALIZED: + return MakePair(Unhole(isolate->heap(), value, attributes), *receiver); + case MISSING_BINDING: + UNREACHABLE(); + return MakePair(NULL, NULL); } } - // If the holder is found, we read the property from it. - if (!holder.is_null() && holder->IsJSObject()) { - ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name)); - JSObject* object = JSObject::cast(*holder); - Object* receiver; - if (object->IsGlobalObject()) { - receiver = GlobalObject::cast(object)->global_receiver(); - } else if (context->is_exception_holder(*holder)) { - // Use the hole as the receiver to signal that the receiver is - // implicit and that the global receiver should be used. - receiver = isolate->heap()->the_hole_value(); - } else { - receiver = ComputeReceiverForNonGlobal(isolate, object); - } - + // Otherwise, if the slot was found the holder is a context extension + // object, subject of a with, or a global object. We read the named + // property from it. + if (!holder.is_null()) { + Handle<JSObject> object = Handle<JSObject>::cast(holder); + ASSERT(object->HasProperty(*name)); // GetProperty below can cause GC. - Handle<Object> receiver_handle(receiver); + Handle<Object> receiver_handle(object->IsGlobalObject() + ? GlobalObject::cast(*object)->global_receiver() + : ComputeReceiverForNonGlobal(isolate, *object)); - // No need to unhole the value here. This is taken care of by the + // No need to unhole the value here. This is taken care of by the // GetProperty function. MaybeObject* value = object->GetProperty(*name); return MakePair(value, *receiver_handle); @@ -8829,10 +9140,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) { Handle<Object> value(args[0], isolate); CONVERT_ARG_CHECKED(Context, context, 1); CONVERT_ARG_CHECKED(String, name, 2); - CONVERT_SMI_ARG_CHECKED(strict_unchecked, 3); - RUNTIME_ASSERT(strict_unchecked == kStrictMode || - strict_unchecked == kNonStrictMode); - StrictModeFlag strict_mode = static_cast<StrictModeFlag>(strict_unchecked); + CONVERT_LANGUAGE_MODE_ARG(language_mode, 3); + StrictModeFlag strict_mode = (language_mode == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; int index; PropertyAttributes attributes; @@ -8845,45 +9155,37 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) { &binding_flags); if (index >= 0) { - if (holder->IsContext()) { - Handle<Context> context = Handle<Context>::cast(holder); - if (binding_flags == MUTABLE_CHECK_INITIALIZED && - context->get(index)->IsTheHole()) { - Handle<Object> error = - isolate->factory()->NewReferenceError("not_defined", - HandleVector(&name, 1)); - return isolate->Throw(*error); - } - // Ignore if read_only variable. - if ((attributes & READ_ONLY) == 0) { - // Context is a fixed array and set cannot fail. - context->set(index, *value); - } else if (strict_mode == kStrictMode) { - // Setting read only property in strict mode. - Handle<Object> error = - isolate->factory()->NewTypeError("strict_cannot_assign", - HandleVector(&name, 1)); - return isolate->Throw(*error); - } - } else { - ASSERT((attributes & READ_ONLY) == 0); - Handle<Object> result = - SetElement(Handle<JSObject>::cast(holder), index, value, strict_mode); - if (result.is_null()) { - ASSERT(isolate->has_pending_exception()); - return Failure::Exception(); - } + // The property was found in a context slot. + Handle<Context> context = Handle<Context>::cast(holder); + if (binding_flags == MUTABLE_CHECK_INITIALIZED && + context->get(index)->IsTheHole()) { + Handle<Object> error = + isolate->factory()->NewReferenceError("not_defined", + HandleVector(&name, 1)); + return isolate->Throw(*error); + } + // Ignore if read_only variable. + if ((attributes & READ_ONLY) == 0) { + // Context is a fixed array and set cannot fail. + context->set(index, *value); + } else if (strict_mode == kStrictMode) { + // Setting read only property in strict mode. + Handle<Object> error = + isolate->factory()->NewTypeError("strict_cannot_assign", + HandleVector(&name, 1)); + return isolate->Throw(*error); } return *value; } - // Slow case: The property is not in a FixedArray context. - // It is either in an JSObject extension context or it was not found. - Handle<JSObject> context_ext; + // Slow case: The property is not in a context slot. It is either in a + // context extension object, a property of the subject of a with, or a + // property of the global object. + Handle<JSObject> object; if (!holder.is_null()) { - // The property exists in the extension context. - context_ext = Handle<JSObject>::cast(holder); + // The property exists on the holder. + object = Handle<JSObject>::cast(holder); } else { // The property was not found. ASSERT(attributes == ABSENT); @@ -8891,22 +9193,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) { if (strict_mode == kStrictMode) { // Throw in strict mode (assignment to undefined variable). Handle<Object> error = - isolate->factory()->NewReferenceError( - "not_defined", HandleVector(&name, 1)); + isolate->factory()->NewReferenceError( + "not_defined", HandleVector(&name, 1)); return isolate->Throw(*error); } - // In non-strict mode, the property is stored in the global context. + // In non-strict mode, the property is added to the global object. attributes = NONE; - context_ext = Handle<JSObject>(isolate->context()->global()); + object = Handle<JSObject>(isolate->context()->global()); } - // Set the property, but ignore if read_only variable on the context - // extension object itself. + // Set the property if it's not read only or doesn't yet exist. if ((attributes & READ_ONLY) == 0 || - (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) { + (object->GetLocalPropertyAttribute(*name) == ABSENT)) { RETURN_IF_EMPTY_HANDLE( isolate, - SetProperty(context_ext, name, value, NONE, strict_mode)); + JSReceiver::SetProperty(object, name, value, NONE, strict_mode)); } else if (strict_mode == kStrictMode && (attributes & READ_ONLY) != 0) { // Setting read only property in strict mode. Handle<Object> error = @@ -8965,42 +9266,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StackGuard) { } -// NOTE: These PrintXXX functions are defined for all builds (not just -// DEBUG builds) because we may want to be able to trace function -// calls in all modes. -static void PrintString(String* str) { - // not uncommon to have empty strings - if (str->length() > 0) { - SmartArrayPointer<char> s = - str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - PrintF("%s", *s); - } -} - - -static void PrintObject(Object* obj) { - if (obj->IsSmi()) { - PrintF("%d", Smi::cast(obj)->value()); - } else if (obj->IsString() || obj->IsSymbol()) { - PrintString(String::cast(obj)); - } else if (obj->IsNumber()) { - PrintF("%g", obj->Number()); - } else if (obj->IsFailure()) { - PrintF("<failure>"); - } else if (obj->IsUndefined()) { - PrintF("<undefined>"); - } else if (obj->IsNull()) { - PrintF("<null>"); - } else if (obj->IsTrue()) { - PrintF("<true>"); - } else if (obj->IsFalse()) { - PrintF("<false>"); - } else { - PrintF("%p", reinterpret_cast<void*>(obj)); - } -} - - static int StackSize() { int n = 0; for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++; @@ -9019,33 +9284,12 @@ static void PrintTransition(Object* result) { } if (result == NULL) { - // constructor calls - JavaScriptFrameIterator it; - JavaScriptFrame* frame = it.frame(); - if (frame->IsConstructor()) PrintF("new "); - // function name - Object* fun = frame->function(); - if (fun->IsJSFunction()) { - PrintObject(JSFunction::cast(fun)->shared()->name()); - } else { - PrintObject(fun); - } - // function arguments - // (we are intentionally only printing the actually - // supplied parameters, not all parameters required) - PrintF("(this="); - PrintObject(frame->receiver()); - const int length = frame->ComputeParametersCount(); - for (int i = 0; i < length; i++) { - PrintF(", "); - PrintObject(frame->GetParameter(i)); - } - PrintF(") {\n"); - + JavaScriptFrame::PrintTop(stdout, true, false); + PrintF(" {\n"); } else { // function result PrintF("} -> "); - PrintObject(result); + result->ShortPrint(); PrintF("\n"); } } @@ -9126,6 +9370,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) { FlattenString(str); CONVERT_ARG_CHECKED(JSArray, output, 1); + + MaybeObject* maybe_result_array = + output->EnsureCanContainHeapObjectElements(); + if (maybe_result_array->IsFailure()) return maybe_result_array; RUNTIME_ASSERT(output->HasFastElements()); AssertNoAllocation no_allocation; @@ -9194,7 +9442,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ParseJson) { CONVERT_ARG_CHECKED(String, source, 0); source = Handle<String>(source->TryFlattenGetString()); - // Optimized fast case where we only have ascii characters. + // Optimized fast case where we only have ASCII characters. Handle<Object> result; if (source->IsSeqAsciiString()) { result = JsonParser<true>::Parse(source); @@ -9212,20 +9460,18 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ParseJson) { bool CodeGenerationFromStringsAllowed(Isolate* isolate, Handle<Context> context) { - if (context->allow_code_gen_from_strings()->IsFalse()) { - // Check with callback if set. - AllowCodeGenerationFromStringsCallback callback = - isolate->allow_code_gen_callback(); - if (callback == NULL) { - // No callback set and code generation disallowed. - return false; - } else { - // Callback set. Let it decide if code generation is allowed. - VMState state(isolate, EXTERNAL); - return callback(v8::Utils::ToLocal(context)); - } + ASSERT(context->allow_code_gen_from_strings()->IsFalse()); + // Check with callback if set. + AllowCodeGenerationFromStringsCallback callback = + isolate->allow_code_gen_callback(); + if (callback == NULL) { + // No callback set and code generation disallowed. + return false; + } else { + // Callback set. Let it decide if code generation is allowed. + VMState state(isolate, EXTERNAL); + return callback(v8::Utils::ToLocal(context)); } - return true; } @@ -9239,16 +9485,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) { // Check if global context allows code generation from // strings. Throw an exception if it doesn't. - if (!CodeGenerationFromStringsAllowed(isolate, context)) { + if (context->allow_code_gen_from_strings()->IsFalse() && + !CodeGenerationFromStringsAllowed(isolate, context)) { return isolate->Throw(*isolate->factory()->NewError( "code_gen_from_strings", HandleVector<Object>(NULL, 0))); } // Compile source string in the global context. - Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source, - context, - true, - kNonStrictMode); + Handle<SharedFunctionInfo> shared = Compiler::CompileEval( + source, context, true, CLASSIC_MODE, RelocInfo::kNoPosition); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> fun = isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, @@ -9261,13 +9506,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) { static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source, Handle<Object> receiver, - StrictModeFlag strict_mode) { + LanguageMode language_mode, + int scope_position) { Handle<Context> context = Handle<Context>(isolate->context()); Handle<Context> global_context = Handle<Context>(context->global_context()); // Check if global context allows code generation from // strings. Throw an exception if it doesn't. - if (!CodeGenerationFromStringsAllowed(isolate, global_context)) { + if (global_context->allow_code_gen_from_strings()->IsFalse() && + !CodeGenerationFromStringsAllowed(isolate, global_context)) { isolate->Throw(*isolate->factory()->NewError( "code_gen_from_strings", HandleVector<Object>(NULL, 0))); return MakePair(Failure::Exception(), NULL); @@ -9279,7 +9526,8 @@ static ObjectPair CompileGlobalEval(Isolate* isolate, source, Handle<Context>(isolate->context()), context->IsGlobalContext(), - strict_mode); + language_mode, + scope_position); if (shared.is_null()) return MakePair(Failure::Exception(), NULL); Handle<JSFunction> compiled = isolate->factory()->NewFunctionFromSharedFunctionInfo( @@ -9289,91 +9537,28 @@ static ObjectPair CompileGlobalEval(Isolate* isolate, RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) { - ASSERT(args.length() == 4); - - HandleScope scope(isolate); - Handle<Object> callee = args.at<Object>(0); - Handle<Object> receiver; // Will be overwritten. - - // Compute the calling context. - Handle<Context> context = Handle<Context>(isolate->context(), isolate); -#ifdef DEBUG - // Make sure Isolate::context() agrees with the old code that traversed - // the stack frames to compute the context. - StackFrameLocator locator; - JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); - ASSERT(Context::cast(frame->context()) == *context); -#endif - - // Find where the 'eval' symbol is bound. It is unaliased only if - // it is bound in the global context. - int index = -1; - PropertyAttributes attributes = ABSENT; - BindingFlags binding_flags; - while (true) { - receiver = context->Lookup(isolate->factory()->eval_symbol(), - FOLLOW_PROTOTYPE_CHAIN, - &index, - &attributes, - &binding_flags); - // Stop search when eval is found or when the global context is - // reached. - if (attributes != ABSENT || context->IsGlobalContext()) break; - context = Handle<Context>(context->previous(), isolate); - } - - // If eval could not be resolved, it has been deleted and we need to - // throw a reference error. - if (attributes == ABSENT) { - Handle<Object> name = isolate->factory()->eval_symbol(); - Handle<Object> reference_error = - isolate->factory()->NewReferenceError("not_defined", - HandleVector(&name, 1)); - return MakePair(isolate->Throw(*reference_error), NULL); - } - - if (!context->IsGlobalContext()) { - // 'eval' is not bound in the global context. Just call the function - // with the given arguments. This is not necessarily the global eval. - if (receiver->IsContext() || receiver->IsJSContextExtensionObject()) { - receiver = isolate->factory()->the_hole_value(); - } - return MakePair(*callee, *receiver); - } - - // 'eval' is bound in the global context, but it may have been overwritten. - // Compare it to the builtin 'GlobalEval' function to make sure. - if (*callee != isolate->global_context()->global_eval_fun() || - !args[1]->IsString()) { - return MakePair(*callee, isolate->heap()->the_hole_value()); - } - - ASSERT(args[3]->IsSmi()); - return CompileGlobalEval(isolate, - args.at<String>(1), - args.at<Object>(2), - static_cast<StrictModeFlag>(args.smi_at(3))); -} - - -RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) { - ASSERT(args.length() == 4); + ASSERT(args.length() == 5); HandleScope scope(isolate); Handle<Object> callee = args.at<Object>(0); - // 'eval' is bound in the global context, but it may have been overwritten. - // Compare it to the builtin 'GlobalEval' function to make sure. + // If "eval" didn't refer to the original GlobalEval, it's not a + // direct call to eval. + // (And even if it is, but the first argument isn't a string, just let + // execution default to an indirect call to eval, which will also return + // the first argument without doing anything). if (*callee != isolate->global_context()->global_eval_fun() || !args[1]->IsString()) { return MakePair(*callee, isolate->heap()->the_hole_value()); } - ASSERT(args[3]->IsSmi()); + CONVERT_LANGUAGE_MODE_ARG(language_mode, 3); + ASSERT(args[4]->IsSmi()); return CompileGlobalEval(isolate, args.at<String>(1), args.at<Object>(2), - static_cast<StrictModeFlag>(args.smi_at(3))); + language_mode, + args.smi_at(4)); } @@ -9386,9 +9571,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNewFunctionAttributes) { ASSERT(args.length() == 1); CONVERT_ARG_CHECKED(JSFunction, func, 0); - Handle<Map> map = func->shared()->strict_mode() - ? isolate->strict_mode_function_instance_map() - : isolate->function_instance_map(); + Handle<Map> map = func->shared()->is_classic_mode() + ? isolate->function_instance_map() + : isolate->strict_mode_function_instance_map(); ASSERT(func->map()->instance_type() == map->instance_type()); ASSERT(func->map()->instance_size() == map->instance_size()); @@ -9426,7 +9611,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) { ASSERT(args.length() == 2); CONVERT_CHECKED(JSArray, array, args[0]); CONVERT_CHECKED(JSObject, element, args[1]); - RUNTIME_ASSERT(array->HasFastElements()); + RUNTIME_ASSERT(array->HasFastElements() || array->HasFastSmiOnlyElements()); int length = Smi::cast(array->length())->value(); FixedArray* elements = FixedArray::cast(array->elements()); for (int i = 0; i < length; i++) { @@ -9510,9 +9695,11 @@ class ArrayConcatVisitor { isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); Handle<Map> map; if (fast_elements_) { - map = isolate_->factory()->GetFastElementsMap(Handle<Map>(array->map())); + map = isolate_->factory()->GetElementsTransitionMap(array, + FAST_ELEMENTS); } else { - map = isolate_->factory()->GetSlowElementsMap(Handle<Map>(array->map())); + map = isolate_->factory()->GetElementsTransitionMap(array, + DICTIONARY_ELEMENTS); } array->set_map(*map); array->set_length(*length); @@ -9568,6 +9755,7 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { uint32_t length = static_cast<uint32_t>(array->length()->Number()); int element_count = 0; switch (array->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { // Fast elements can't have lengths that are not representable by // a 32-bit signed integer. @@ -9579,6 +9767,10 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { } break; } + case FAST_DOUBLE_ELEMENTS: + // TODO(1810): Decide if it's worthwhile to implement this. + UNREACHABLE(); + break; case DICTIONARY_ELEMENTS: { Handle<SeededNumberDictionary> dictionary( SeededNumberDictionary::cast(array->elements())); @@ -9591,7 +9783,16 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { } break; } - default: + case NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: // External arrays are always dense. return length; } @@ -9657,6 +9858,7 @@ static void CollectElementIndices(Handle<JSObject> object, List<uint32_t>* indices) { ElementsKind kind = object->GetElementsKind(); switch (kind) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { Handle<FixedArray> elements(FixedArray::cast(object->elements())); uint32_t length = static_cast<uint32_t>(elements->length()); @@ -9668,6 +9870,11 @@ static void CollectElementIndices(Handle<JSObject> object, } break; } + case FAST_DOUBLE_ELEMENTS: { + // TODO(1810): Decide if it's worthwhile to implement this. + UNREACHABLE(); + break; + } case DICTIONARY_ELEMENTS: { Handle<SeededNumberDictionary> dict( SeededNumberDictionary::cast(object->elements())); @@ -9777,6 +9984,7 @@ static bool IterateElements(Isolate* isolate, ArrayConcatVisitor* visitor) { uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); switch (receiver->GetElementsKind()) { + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: { // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. @@ -9791,13 +9999,18 @@ static bool IterateElements(Isolate* isolate, } else if (receiver->HasElement(j)) { // Call GetElement on receiver, not its prototype, or getters won't // have the correct receiver. - element_value = GetElement(receiver, j); - if (element_value.is_null()) return false; + element_value = Object::GetElement(receiver, j); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false); visitor->visit(j, element_value); } } break; } + case FAST_DOUBLE_ELEMENTS: { + // TODO(1810): Decide if it's worthwhile to implement this. + UNREACHABLE(); + break; + } case DICTIONARY_ELEMENTS: { Handle<SeededNumberDictionary> dict(receiver->element_dictionary()); List<uint32_t> indices(dict->Capacity() / 2); @@ -9810,8 +10023,8 @@ static bool IterateElements(Isolate* isolate, while (j < n) { HandleScope loop_scope; uint32_t index = indices[j]; - Handle<Object> element = GetElement(receiver, index); - if (element.is_null()) return false; + Handle<Object> element = Object::GetElement(receiver, index); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element, false); visitor->visit(index, element); // Skip to next different index (i.e., omit duplicates). do { @@ -9908,6 +10121,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) { uint32_t element_estimate; if (obj->IsJSArray()) { Handle<JSArray> array(Handle<JSArray>::cast(obj)); + // TODO(1810): Find out if it's worthwhile to properly support + // arbitrary ElementsKinds. For now, pessimistically transition to + // FAST_ELEMENTS. + if (array->HasFastDoubleElements()) { + array = Handle<JSArray>::cast( + JSObject::TransitionElementsKind(array, FAST_ELEMENTS)); + } length_estimate = static_cast<uint32_t>(array->length()->Number()); element_estimate = @@ -10005,15 +10225,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) { CONVERT_CHECKED(JSArray, to, args[1]); FixedArrayBase* new_elements = from->elements(); MaybeObject* maybe_new_map; + ElementsKind elements_kind; if (new_elements->map() == isolate->heap()->fixed_array_map() || new_elements->map() == isolate->heap()->fixed_cow_array_map()) { - maybe_new_map = to->map()->GetFastElementsMap(); + elements_kind = FAST_ELEMENTS; } else if (new_elements->map() == isolate->heap()->fixed_double_array_map()) { - maybe_new_map = to->map()->GetFastDoubleElementsMap(); + elements_kind = FAST_DOUBLE_ELEMENTS; } else { - maybe_new_map = to->map()->GetSlowElementsMap(); + elements_kind = DICTIONARY_ELEMENTS; } + maybe_new_map = to->GetElementsTransitionMap(isolate, elements_kind); Object* new_map; if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; to->set_map(Map::cast(new_map)); @@ -10060,15 +10282,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SwapElements) { } Handle<JSObject> jsobject = Handle<JSObject>::cast(object); - Handle<Object> tmp1 = GetElement(jsobject, index1); + Handle<Object> tmp1 = Object::GetElement(jsobject, index1); RETURN_IF_EMPTY_HANDLE(isolate, tmp1); - Handle<Object> tmp2 = GetElement(jsobject, index2); + Handle<Object> tmp2 = Object::GetElement(jsobject, index2); RETURN_IF_EMPTY_HANDLE(isolate, tmp2); - RETURN_IF_EMPTY_HANDLE(isolate, - SetElement(jsobject, index1, tmp2, kStrictMode)); - RETURN_IF_EMPTY_HANDLE(isolate, - SetElement(jsobject, index2, tmp1, kStrictMode)); + RETURN_IF_EMPTY_HANDLE( + isolate, JSObject::SetElement(jsobject, index1, tmp2, kStrictMode)); + RETURN_IF_EMPTY_HANDLE( + isolate, JSObject::SetElement(jsobject, index2, tmp1, kStrictMode)); return isolate->heap()->undefined_value(); } @@ -10087,7 +10309,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) { if (array->elements()->IsDictionary()) { // Create an array and get all the keys into it, then remove all the // keys that are not integers in the range 0 to length-1. - Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS); + bool threw = false; + Handle<FixedArray> keys = + GetKeysInFixedArrayFor(array, INCLUDE_PROTOS, &threw); + if (threw) return Failure::Exception(); + int keys_length = keys->length(); for (int i = 0; i < keys_length; i++) { Object* key = keys->get(i); @@ -10099,7 +10325,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) { } return *isolate->factory()->NewJSArrayWithElements(keys); } else { - ASSERT(array->HasFastElements() || array->HasFastDoubleElements()); + ASSERT(array->HasFastElements() || + array->HasFastSmiOnlyElements() || + array->HasFastDoubleElements()); Handle<FixedArray> single_interval = isolate->factory()->NewFixedArray(2); // -1 means start of array. single_interval->set(0, Smi::FromInt(-1)); @@ -10116,7 +10344,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) { // DefineAccessor takes an optional final argument which is the -// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due +// property attributes (e.g. DONT_ENUM, DONT_DELETE). IMPORTANT: due // to the way accessors are implemented, it is set for both the getter // and setter on the first call to DefineAccessor and ignored on // subsequent calls. @@ -10218,8 +10446,8 @@ static MaybeObject* DebugLookupResultValue(Heap* heap, case CALLBACKS: { Object* structure = result->GetCallbackObject(); if (structure->IsForeign() || structure->IsAccessorInfo()) { - MaybeObject* maybe_value = receiver->GetPropertyWithCallback( - receiver, structure, name, result->holder()); + MaybeObject* maybe_value = result->holder()->GetPropertyWithCallback( + receiver, structure, name); if (!maybe_value->ToObject(&value)) { if (maybe_value->IsRetryAfterGC()) return maybe_value; ASSERT(maybe_value->IsException()); @@ -10241,10 +10469,11 @@ static MaybeObject* DebugLookupResultValue(Heap* heap, case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: return heap->undefined_value(); - default: + case HANDLER: UNREACHABLE(); + return heap->undefined_value(); } - UNREACHABLE(); + UNREACHABLE(); // keep the compiler happy return heap->undefined_value(); } @@ -10310,7 +10539,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) { // Try local lookup on each of the objects. Handle<JSObject> jsproto = obj; for (int i = 0; i < length; i++) { - LookupResult result; + LookupResult result(isolate); jsproto->LocalLookup(*name, &result); if (result.IsProperty()) { // LookupResult is not GC safe as it holds raw object pointers. @@ -10337,15 +10566,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) { // If the callback object is a fixed array then it contains JavaScript // getter and/or setter. bool hasJavaScriptAccessors = result_type == CALLBACKS && - result_callback_obj->IsFixedArray(); + result_callback_obj->IsAccessorPair(); Handle<FixedArray> details = isolate->factory()->NewFixedArray(hasJavaScriptAccessors ? 5 : 2); details->set(0, *value); details->set(1, property_details); if (hasJavaScriptAccessors) { details->set(2, isolate->heap()->ToBoolean(caught_exception)); - details->set(3, FixedArray::cast(*result_callback_obj)->get(0)); - details->set(4, FixedArray::cast(*result_callback_obj)->get(1)); + details->set(3, AccessorPair::cast(*result_callback_obj)->getter()); + details->set(4, AccessorPair::cast(*result_callback_obj)->setter()); } return *isolate->factory()->NewJSArrayWithElements(details); @@ -10367,7 +10596,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetProperty) { CONVERT_ARG_CHECKED(JSObject, obj, 0); CONVERT_ARG_CHECKED(String, name, 1); - LookupResult result; + LookupResult result(isolate); obj->Lookup(*name, &result); if (result.IsProperty()) { return DebugLookupResultValue(isolate->heap(), *obj, *name, &result, NULL); @@ -10478,13 +10707,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) { class FrameInspector { public: FrameInspector(JavaScriptFrame* frame, - int inlined_frame_index, + int inlined_jsframe_index, Isolate* isolate) : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) { // Calculate the deoptimized frame. if (frame->is_optimized()) { deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame( - frame, inlined_frame_index, isolate); + frame, inlined_jsframe_index, isolate); } has_adapted_arguments_ = frame_->has_adapted_arguments(); is_optimized_ = frame_->is_optimized(); @@ -10519,6 +10748,11 @@ class FrameInspector { ? deoptimized_frame_->GetExpression(index) : frame_->GetExpression(index); } + int GetSourcePosition() { + return is_optimized_ + ? deoptimized_frame_->GetSourcePosition() + : frame_->LookupCode()->SourcePosition(frame_->pc()); + } // To inspect all the provided arguments the frame might need to be // replaced with the arguments frame. @@ -10551,6 +10785,18 @@ static const int kFrameDetailsAtReturnIndex = 7; static const int kFrameDetailsFlagsIndex = 8; static const int kFrameDetailsFirstDynamicIndex = 9; + +static SaveContext* FindSavedContextForFrame(Isolate* isolate, + JavaScriptFrame* frame) { + SaveContext* save = isolate->save_context(); + while (save != NULL && !save->IsBelowFrame(frame)) { + save = save->prev(); + } + ASSERT(save != NULL); + return save; +} + + // Return an array with frame details // args[0]: number: break id // args[1]: number: frame index @@ -10588,8 +10834,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { return heap->undefined_value(); } - int inlined_frame_index = 0; // Inlined frame index in optimized frame. - int count = 0; JavaScriptFrameIterator it(isolate, id); for (; !it.done(); it.Advance()) { @@ -10598,38 +10842,34 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { } if (it.done()) return heap->undefined_value(); - if (it.frame()->is_optimized()) { - inlined_frame_index = + bool is_optimized = it.frame()->is_optimized(); + + int inlined_jsframe_index = 0; // Inlined frame index in optimized frame. + if (is_optimized) { + inlined_jsframe_index = it.frame()->GetInlineCount() - (index - count) - 1; } - FrameInspector frame_inspector(it.frame(), inlined_frame_index, isolate); + FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate); // Traverse the saved contexts chain to find the active context for the // selected frame. - SaveContext* save = isolate->save_context(); - while (save != NULL && !save->below(it.frame())) { - save = save->prev(); - } - ASSERT(save != NULL); + SaveContext* save = FindSavedContextForFrame(isolate, it.frame()); // Get the frame id. Handle<Object> frame_id(WrapFrameId(it.frame()->id()), isolate); - // Find source position. - int position = - it.frame()->LookupCode()->SourcePosition(it.frame()->pc()); + // Find source position in unoptimized code. + int position = frame_inspector.GetSourcePosition(); // Check for constructor frame. Inlined frames cannot be construct calls. - bool inlined_frame = - it.frame()->is_optimized() && inlined_frame_index != 0; + bool inlined_frame = is_optimized && inlined_jsframe_index != 0; bool constructor = !inlined_frame && it.frame()->IsConstructor(); // Get scope info and read from it for local variable information. - Handle<JSFunction> function(JSFunction::cast(it.frame()->function())); + Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction())); Handle<SharedFunctionInfo> shared(function->shared()); - Handle<SerializedScopeInfo> scope_info(shared->scope_info()); - ASSERT(*scope_info != SerializedScopeInfo::Empty()); - ScopeInfo<> info(*scope_info); + Handle<ScopeInfo> scope_info(shared->scope_info()); + ASSERT(*scope_info != ScopeInfo::Empty()); // Get the locals names and values into a temporary array. // @@ -10637,31 +10877,33 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // (e.g. .result)? For users of the debugger, they will probably be // confusing. Handle<FixedArray> locals = - isolate->factory()->NewFixedArray(info.NumberOfLocals() * 2); + isolate->factory()->NewFixedArray(scope_info->LocalCount() * 2); // Fill in the values of the locals. int i = 0; - for (; i < info.number_of_stack_slots(); ++i) { + for (; i < scope_info->StackLocalCount(); ++i) { // Use the value from the stack. - locals->set(i * 2, *info.LocalName(i)); + locals->set(i * 2, scope_info->LocalName(i)); locals->set(i * 2 + 1, frame_inspector.GetExpression(i)); } - if (i < info.NumberOfLocals()) { + if (i < scope_info->LocalCount()) { // Get the context containing declarations. Handle<Context> context( Context::cast(it.frame()->context())->declaration_context()); - for (; i < info.NumberOfLocals(); ++i) { - Handle<String> name = info.LocalName(i); + for (; i < scope_info->LocalCount(); ++i) { + Handle<String> name(scope_info->LocalName(i)); + VariableMode mode; + InitializationFlag init_flag; locals->set(i * 2, *name); - locals->set(i * 2 + 1, - context->get(scope_info->ContextSlotIndex(*name, NULL))); + locals->set(i * 2 + 1, context->get( + scope_info->ContextSlotIndex(*name, &mode, &init_flag))); } } // Check whether this frame is positioned at return. If not top // frame or if the frame is optimized it cannot be at a return. bool at_return = false; - if (!it.frame()->is_optimized() && index == 0) { + if (!is_optimized && index == 0) { at_return = isolate->debug()->IsBreakAtReturn(it.frame()); } @@ -10701,26 +10943,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // the provided parameters whereas the function frame always have the number // of arguments matching the functions parameters. The rest of the // information (except for what is collected above) is the same. - if (it.frame()->has_adapted_arguments()) { + if ((inlined_jsframe_index == 0) && it.frame()->has_adapted_arguments()) { it.AdvanceToArgumentsFrame(); frame_inspector.SetArgumentsFrame(it.frame()); } // Find the number of arguments to fill. At least fill the number of // parameters for the function and fill more if more parameters are provided. - int argument_count = info.number_of_parameters(); + int argument_count = scope_info->ParameterCount(); if (argument_count < frame_inspector.GetParametersCount()) { argument_count = frame_inspector.GetParametersCount(); } -#ifdef DEBUG - if (it.frame()->is_optimized()) { - ASSERT_EQ(argument_count, frame_inspector.GetParametersCount()); - } -#endif // Calculate the size of the result. int details_size = kFrameDetailsFirstDynamicIndex + - 2 * (argument_count + info.NumberOfLocals()) + + 2 * (argument_count + scope_info->LocalCount()) + (at_return ? 1 : 0); Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size); @@ -10735,7 +10972,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // Add the locals count details->set(kFrameDetailsLocalCountIndex, - Smi::FromInt(info.NumberOfLocals())); + Smi::FromInt(scope_info->LocalCount())); // Add the source position. if (position != RelocInfo::kNoPosition) { @@ -10758,9 +10995,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { if (*save->context() == *isolate->debug()->debug_context()) { flags |= 1 << 0; } - if (it.frame()->is_optimized()) { + if (is_optimized) { flags |= 1 << 1; - flags |= inlined_frame_index << 2; + flags |= inlined_jsframe_index << 2; } details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags)); @@ -10770,14 +11007,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // Add arguments name and value. for (int i = 0; i < argument_count; i++) { // Name of the argument. - if (i < info.number_of_parameters()) { - details->set(details_index++, *info.parameter_name(i)); + if (i < scope_info->ParameterCount()) { + details->set(details_index++, scope_info->ParameterName(i)); } else { details->set(details_index++, heap->undefined_value()); } // Parameter value. - if (i < it.frame()->ComputeParametersCount()) { + if (i < frame_inspector.GetParametersCount()) { // Get the value from the stack. details->set(details_index++, frame_inspector.GetParameter(i)); } else { @@ -10786,7 +11023,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { } // Add locals name and value from the temporary copy from the function frame. - for (int i = 0; i < info.NumberOfLocals() * 2; i++) { + for (int i = 0; i < scope_info->LocalCount() * 2; i++) { details->set(details_index++, locals->get(i)); } @@ -10799,7 +11036,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE // THE FRAME ITERATOR TO WRAP THE RECEIVER. Handle<Object> receiver(it.frame()->receiver(), isolate); - if (!receiver->IsJSObject() && !shared->strict_mode() && !shared->native()) { + if (!receiver->IsJSObject() && + shared->is_classic_mode() && + !shared->native()) { // If the receiver is not a JSObject and the function is not a // builtin or strict-mode we have hit an optimization where a // value object is not converted into a wrapped JS objects. To @@ -10822,21 +11061,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // Copy all the context locals into an object used to materialize a scope. static bool CopyContextLocalsToScopeObject( Isolate* isolate, - Handle<SerializedScopeInfo> serialized_scope_info, - ScopeInfo<>& scope_info, + Handle<ScopeInfo> scope_info, Handle<Context> context, Handle<JSObject> scope_object) { // Fill all context locals to the context extension. - for (int i = Context::MIN_CONTEXT_SLOTS; - i < scope_info.number_of_context_slots(); - i++) { - int context_index = serialized_scope_info->ContextSlotIndex( - *scope_info.context_slot_name(i), NULL); + for (int i = 0; i < scope_info->ContextLocalCount(); i++) { + VariableMode mode; + InitializationFlag init_flag; + int context_index = scope_info->ContextSlotIndex( + scope_info->ContextLocalName(i), &mode, &init_flag); RETURN_IF_EMPTY_HANDLE_VALUE( isolate, SetProperty(scope_object, - scope_info.context_slot_name(i), + Handle<String>(scope_info->ContextLocalName(i)), Handle<Object>(context->get(context_index), isolate), NONE, kNonStrictMode), @@ -10849,15 +11087,13 @@ static bool CopyContextLocalsToScopeObject( // Create a plain JSObject which materializes the local scope for the specified // frame. -static Handle<JSObject> MaterializeLocalScope( +static Handle<JSObject> MaterializeLocalScopeWithFrameInspector( Isolate* isolate, JavaScriptFrame* frame, - int inlined_frame_index) { - Handle<JSFunction> function(JSFunction::cast(frame->function())); + FrameInspector* frame_inspector) { + Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction())); Handle<SharedFunctionInfo> shared(function->shared()); - Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info()); - ScopeInfo<> scope_info(*serialized_scope_info); - FrameInspector frame_inspector(frame, inlined_frame_index, isolate); + Handle<ScopeInfo> scope_info(shared->scope_info()); // Allocate and initialize a JSObject with all the arguments, stack locals // heap locals and extension properties of the debugged function. @@ -10865,36 +11101,39 @@ static Handle<JSObject> MaterializeLocalScope( isolate->factory()->NewJSObject(isolate->object_function()); // First fill all parameters. - for (int i = 0; i < scope_info.number_of_parameters(); ++i) { + for (int i = 0; i < scope_info->ParameterCount(); ++i) { + Handle<Object> value( + i < frame_inspector->GetParametersCount() ? + frame_inspector->GetParameter(i) : isolate->heap()->undefined_value()); + RETURN_IF_EMPTY_HANDLE_VALUE( isolate, SetProperty(local_scope, - scope_info.parameter_name(i), - Handle<Object>(frame_inspector.GetParameter(i)), + Handle<String>(scope_info->ParameterName(i)), + value, NONE, kNonStrictMode), Handle<JSObject>()); } // Second fill all stack locals. - for (int i = 0; i < scope_info.number_of_stack_slots(); ++i) { + for (int i = 0; i < scope_info->StackLocalCount(); ++i) { RETURN_IF_EMPTY_HANDLE_VALUE( isolate, SetProperty(local_scope, - scope_info.stack_slot_name(i), - Handle<Object>(frame_inspector.GetExpression(i)), + Handle<String>(scope_info->StackLocalName(i)), + Handle<Object>(frame_inspector->GetExpression(i)), NONE, kNonStrictMode), Handle<JSObject>()); } - if (scope_info.number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) { + if (scope_info->HasContext()) { // Third fill all context locals. Handle<Context> frame_context(Context::cast(frame->context())); Handle<Context> function_context(frame_context->declaration_context()); - if (!CopyContextLocalsToScopeObject(isolate, - serialized_scope_info, scope_info, - function_context, local_scope)) { + if (!CopyContextLocalsToScopeObject( + isolate, scope_info, function_context, local_scope)) { return Handle<JSObject>(); } @@ -10904,7 +11143,11 @@ static Handle<JSObject> MaterializeLocalScope( if (function_context->has_extension() && !function_context->IsGlobalContext()) { Handle<JSObject> ext(JSObject::cast(function_context->extension())); - Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS); + bool threw = false; + Handle<FixedArray> keys = + GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw); + if (threw) return Handle<JSObject>(); + for (int i = 0; i < keys->length(); i++) { // Names of variables introduced by eval are strings. ASSERT(keys->get(i)->IsString()); @@ -10926,6 +11169,17 @@ static Handle<JSObject> MaterializeLocalScope( } +static Handle<JSObject> MaterializeLocalScope( + Isolate* isolate, + JavaScriptFrame* frame, + int inlined_jsframe_index) { + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); + return MaterializeLocalScopeWithFrameInspector(isolate, + frame, + &frame_inspector); +} + + // Create a plain JSObject which materializes the closure content for the // context. static Handle<JSObject> MaterializeClosure(Isolate* isolate, @@ -10933,18 +11187,16 @@ static Handle<JSObject> MaterializeClosure(Isolate* isolate, ASSERT(context->IsFunctionContext()); Handle<SharedFunctionInfo> shared(context->closure()->shared()); - Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info()); - ScopeInfo<> scope_info(*serialized_scope_info); + Handle<ScopeInfo> scope_info(shared->scope_info()); - // Allocate and initialize a JSObject with all the content of theis function + // Allocate and initialize a JSObject with all the content of this function // closure. Handle<JSObject> closure_scope = isolate->factory()->NewJSObject(isolate->object_function()); // Fill all context locals to the context extension. - if (!CopyContextLocalsToScopeObject(isolate, - serialized_scope_info, scope_info, - context, closure_scope)) { + if (!CopyContextLocalsToScopeObject( + isolate, scope_info, context, closure_scope)) { return Handle<JSObject>(); } @@ -10952,7 +11204,11 @@ static Handle<JSObject> MaterializeClosure(Isolate* isolate, // be variables introduced by eval. if (context->has_extension()) { Handle<JSObject> ext(JSObject::cast(context->extension())); - Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS); + bool threw = false; + Handle<FixedArray> keys = + GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw); + if (threw) return Handle<JSObject>(); + for (int i = 0; i < keys->length(); i++) { // Names of variables introduced by eval are strings. ASSERT(keys->get(i)->IsString()); @@ -10995,9 +11251,7 @@ static Handle<JSObject> MaterializeBlockScope( Isolate* isolate, Handle<Context> context) { ASSERT(context->IsBlockContext()); - Handle<SerializedScopeInfo> serialized_scope_info( - SerializedScopeInfo::cast(context->extension())); - ScopeInfo<> scope_info(*serialized_scope_info); + Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension())); // Allocate and initialize a JSObject with all the arguments, stack locals // heap locals and extension properties of the debugged function. @@ -11005,21 +11259,19 @@ static Handle<JSObject> MaterializeBlockScope( isolate->factory()->NewJSObject(isolate->object_function()); // Fill all context locals. - if (scope_info.number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) { - if (!CopyContextLocalsToScopeObject(isolate, - serialized_scope_info, scope_info, - context, block_scope)) { - return Handle<JSObject>(); - } + if (!CopyContextLocalsToScopeObject( + isolate, scope_info, context, block_scope)) { + return Handle<JSObject>(); } return block_scope; } -// Iterate over the actual scopes visible from a stack frame. All scopes are +// Iterate over the actual scopes visible from a stack frame. The iteration +// proceeds from the innermost visible nested scope outwards. All scopes are // backed by an actual context except the local scope, which is inserted -// "artifically" in the context chain. +// "artificially" in the context chain. class ScopeIterator { public: enum ScopeType { @@ -11033,33 +11285,89 @@ class ScopeIterator { ScopeIterator(Isolate* isolate, JavaScriptFrame* frame, - int inlined_frame_index) + int inlined_jsframe_index) : isolate_(isolate), frame_(frame), - inlined_frame_index_(inlined_frame_index), + inlined_jsframe_index_(inlined_jsframe_index), function_(JSFunction::cast(frame->function())), context_(Context::cast(frame->context())), - local_done_(false), - at_local_(false) { - - // Check whether the first scope is actually a local scope. - // If there is a stack slot for .result then this local scope has been - // created for evaluating top level code and it is not a real local scope. - // Checking for the existence of .result seems fragile, but the scope info - // saved with the code object does not otherwise have that information. - int index = function_->shared()->scope_info()-> - StackSlotIndex(isolate_->heap()->result_symbol()); - if (index >= 0) { - local_done_ = true; - } else if (context_->IsGlobalContext() || - context_->IsFunctionContext()) { - at_local_ = true; - } else if (context_->closure() != *function_) { - // The context_ is a block or with or catch block from the outer function. - ASSERT(context_->IsWithContext() || - context_->IsCatchContext() || - context_->IsBlockContext()); - at_local_ = true; + nested_scope_chain_(4) { + + // Catch the case when the debugger stops in an internal function. + Handle<SharedFunctionInfo> shared_info(function_->shared()); + Handle<ScopeInfo> scope_info(shared_info->scope_info()); + if (shared_info->script() == isolate->heap()->undefined_value()) { + while (context_->closure() == *function_) { + context_ = Handle<Context>(context_->previous(), isolate_); + } + return; + } + + // Get the debug info (create it if it does not exist). + if (!isolate->debug()->EnsureDebugInfo(shared_info)) { + // Return if ensuring debug info failed. + return; + } + Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info); + + // Find the break point where execution has stopped. + BreakLocationIterator break_location_iterator(debug_info, + ALL_BREAK_LOCATIONS); + break_location_iterator.FindBreakLocationFromAddress(frame->pc()); + if (break_location_iterator.IsExit()) { + // We are within the return sequence. At the momemt it is not possible to + // get a source position which is consistent with the current scope chain. + // Thus all nested with, catch and block contexts are skipped and we only + // provide the function scope. + if (scope_info->HasContext()) { + context_ = Handle<Context>(context_->declaration_context(), isolate_); + } else { + while (context_->closure() == *function_) { + context_ = Handle<Context>(context_->previous(), isolate_); + } + } + if (scope_info->Type() != EVAL_SCOPE) nested_scope_chain_.Add(scope_info); + } else { + // Reparse the code and analyze the scopes. + ZoneScope zone_scope(isolate, DELETE_ON_EXIT); + Handle<Script> script(Script::cast(shared_info->script())); + Scope* scope = NULL; + + // Check whether we are in global, eval or function code. + Handle<ScopeInfo> scope_info(shared_info->scope_info()); + if (scope_info->Type() != FUNCTION_SCOPE) { + // Global or eval code. + CompilationInfo info(script); + if (scope_info->Type() == GLOBAL_SCOPE) { + info.MarkAsGlobal(); + } else { + ASSERT(scope_info->Type() == EVAL_SCOPE); + info.MarkAsEval(); + info.SetCallingContext(Handle<Context>(function_->context())); + } + if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) { + scope = info.function()->scope(); + } + } else { + // Function code + CompilationInfo info(shared_info); + if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) { + scope = info.function()->scope(); + } + } + + // Retrieve the scope chain for the current position. + if (scope != NULL) { + int source_position = shared_info->code()->SourcePosition(frame_->pc()); + scope->GetNestedScopeChain(&nested_scope_chain_, source_position); + } else { + // A failed reparse indicates that the preparser has diverged from the + // parser or that the preparse data given to the initial parse has been + // faulty. We fail in debug mode but in release mode we only provide the + // information we get from the context chain but nothing about + // completely stack allocated scopes or stack allocated locals. + UNREACHABLE(); + } } } @@ -11068,40 +11376,49 @@ class ScopeIterator { // Move to the next scope. void Next() { - // If at a local scope mark the local scope as passed. - if (at_local_) { - at_local_ = false; - local_done_ = true; - - // If the current context is not associated with the local scope the - // current context is the next real scope, so don't move to the next - // context in this case. - if (context_->closure() != *function_) { - return; - } - } - - // The global scope is always the last in the chain. - if (context_->IsGlobalContext()) { + ScopeType scope_type = Type(); + if (scope_type == ScopeTypeGlobal) { + // The global scope is always the last in the chain. + ASSERT(context_->IsGlobalContext()); context_ = Handle<Context>(); return; } - - // Move to the next context. - context_ = Handle<Context>(context_->previous(), isolate_); - - // If passing the local scope indicate that the current scope is now the - // local scope. - if (!local_done_ && - (context_->IsGlobalContext() || context_->IsFunctionContext())) { - at_local_ = true; + if (nested_scope_chain_.is_empty()) { + context_ = Handle<Context>(context_->previous(), isolate_); + } else { + if (nested_scope_chain_.last()->HasContext()) { + ASSERT(context_->previous() != NULL); + context_ = Handle<Context>(context_->previous(), isolate_); + } + nested_scope_chain_.RemoveLast(); } } // Return the type of the current scope. ScopeType Type() { - if (at_local_) { - return ScopeTypeLocal; + if (!nested_scope_chain_.is_empty()) { + Handle<ScopeInfo> scope_info = nested_scope_chain_.last(); + switch (scope_info->Type()) { + case FUNCTION_SCOPE: + ASSERT(context_->IsFunctionContext() || + !scope_info->HasContext()); + return ScopeTypeLocal; + case GLOBAL_SCOPE: + ASSERT(context_->IsGlobalContext()); + return ScopeTypeGlobal; + case WITH_SCOPE: + ASSERT(context_->IsWithContext()); + return ScopeTypeWith; + case CATCH_SCOPE: + ASSERT(context_->IsCatchContext()); + return ScopeTypeCatch; + case BLOCK_SCOPE: + ASSERT(!scope_info->HasContext() || + context_->IsBlockContext()); + return ScopeTypeBlock; + case EVAL_SCOPE: + UNREACHABLE(); + } } if (context_->IsGlobalContext()) { ASSERT(context_->global()->IsGlobalObject()); @@ -11127,7 +11444,8 @@ class ScopeIterator { return Handle<JSObject>(CurrentContext()->global()); case ScopeIterator::ScopeTypeLocal: // Materialize the content of the local scope into a JSObject. - return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_); + ASSERT(nested_scope_chain_.length() == 1); + return MaterializeLocalScope(isolate_, frame_, inlined_jsframe_index_); case ScopeIterator::ScopeTypeWith: // Return the with object. return Handle<JSObject>(JSObject::cast(CurrentContext()->extension())); @@ -11143,13 +11461,28 @@ class ScopeIterator { return Handle<JSObject>(); } + Handle<ScopeInfo> CurrentScopeInfo() { + if (!nested_scope_chain_.is_empty()) { + return nested_scope_chain_.last(); + } else if (context_->IsBlockContext()) { + return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension())); + } else if (context_->IsFunctionContext()) { + return Handle<ScopeInfo>(context_->closure()->shared()->scope_info()); + } + return Handle<ScopeInfo>::null(); + } + // Return the context for this scope. For the local context there might not // be an actual context. Handle<Context> CurrentContext() { - if (at_local_ && context_->closure() != *function_) { + if (Type() == ScopeTypeGlobal || + nested_scope_chain_.is_empty()) { + return context_; + } else if (nested_scope_chain_.last()->HasContext()) { + return context_; + } else { return Handle<Context>(); } - return context_; } #ifdef DEBUG @@ -11163,8 +11496,7 @@ class ScopeIterator { case ScopeIterator::ScopeTypeLocal: { PrintF("Local:\n"); - ScopeInfo<> scope_info(function_->shared()->scope_info()); - scope_info.Print(); + function_->shared()->scope_info()->Print(); if (!CurrentContext().is_null()) { CurrentContext()->Print(); if (CurrentContext()->has_extension()) { @@ -11209,11 +11541,10 @@ class ScopeIterator { private: Isolate* isolate_; JavaScriptFrame* frame_; - int inlined_frame_index_; + int inlined_jsframe_index_; Handle<JSFunction> function_; Handle<Context> context_; - bool local_done_; - bool at_local_; + List<Handle<ScopeInfo> > nested_scope_chain_; DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); }; @@ -11272,7 +11603,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) { if (!maybe_check->ToObject(&check)) return maybe_check; } CONVERT_CHECKED(Smi, wrapped_id, args[1]); - CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]); + CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]); CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]); // Get the frame where the debugging is performed. @@ -11282,7 +11613,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) { // Find the requested scope. int n = 0; - ScopeIterator it(isolate, frame, inlined_frame_index); + ScopeIterator it(isolate, frame, inlined_jsframe_index); for (; !it.Done() && n < index; it.Next()) { n++; } @@ -11469,48 +11800,53 @@ Object* Runtime::FindSharedFunctionInfoInScript(Isolate* isolate, int target_start_position = RelocInfo::kNoPosition; Handle<SharedFunctionInfo> target; while (!done) { - HeapIterator iterator; - for (HeapObject* obj = iterator.next(); - obj != NULL; obj = iterator.next()) { - if (obj->IsSharedFunctionInfo()) { - Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj)); - if (shared->script() == *script) { - // If the SharedFunctionInfo found has the requested script data and - // contains the source position it is a candidate. - int start_position = shared->function_token_position(); - if (start_position == RelocInfo::kNoPosition) { - start_position = shared->start_position(); - } - if (start_position <= position && - position <= shared->end_position()) { - // If there is no candidate or this function is within the current - // candidate this is the new candidate. - if (target.is_null()) { - target_start_position = start_position; - target = shared; - } else { - if (target_start_position == start_position && - shared->end_position() == target->end_position()) { - // If a top-level function contain only one function - // declartion the source for the top-level and the function is - // the same. In that case prefer the non top-level function. - if (!shared->is_toplevel()) { + { // Extra scope for iterator and no-allocation. + isolate->heap()->EnsureHeapIsIterable(); + AssertNoAllocation no_alloc_during_heap_iteration; + HeapIterator iterator; + for (HeapObject* obj = iterator.next(); + obj != NULL; obj = iterator.next()) { + if (obj->IsSharedFunctionInfo()) { + Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj)); + if (shared->script() == *script) { + // If the SharedFunctionInfo found has the requested script data and + // contains the source position it is a candidate. + int start_position = shared->function_token_position(); + if (start_position == RelocInfo::kNoPosition) { + start_position = shared->start_position(); + } + if (start_position <= position && + position <= shared->end_position()) { + // If there is no candidate or this function is within the current + // candidate this is the new candidate. + if (target.is_null()) { + target_start_position = start_position; + target = shared; + } else { + if (target_start_position == start_position && + shared->end_position() == target->end_position()) { + // If a top-level function contain only one function + // declartion the source for the top-level and the + // function is the same. In that case prefer the non + // top-level function. + if (!shared->is_toplevel()) { + target_start_position = start_position; + target = shared; + } + } else if (target_start_position <= start_position && + shared->end_position() <= target->end_position()) { + // This containment check includes equality as a function + // inside a top-level function can share either start or end + // position with the top-level function. target_start_position = start_position; target = shared; } - } else if (target_start_position <= start_position && - shared->end_position() <= target->end_position()) { - // This containment check includes equality as a function inside - // a top-level function can share either start or end position - // with the top-level function. - target_start_position = start_position; - target = shared; } } } } - } - } + } // End for loop. + } // End No allocation scope. if (target.is_null()) { return isolate->heap()->undefined_value(); @@ -11523,9 +11859,9 @@ Object* Runtime::FindSharedFunctionInfoInScript(Isolate* isolate, if (!done) { // If the candidate is not compiled compile it to reveal any inner // functions which might contain the requested source position. - CompileLazyShared(target, KEEP_EXCEPTION); + SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION); } - } + } // End while loop. return *target; } @@ -11671,46 +12007,65 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) { // Creates a copy of the with context chain. The copy of the context chain is // is linked to the function context supplied. -static Handle<Context> CopyWithContextChain(Isolate* isolate, - Handle<JSFunction> function, - Handle<Context> current, - Handle<Context> base) { - // At the end of the chain. Return the base context to link to. - if (current->IsFunctionContext() || current->IsGlobalContext()) { - return base; +static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate, + Handle<JSFunction> function, + Handle<Context> base, + JavaScriptFrame* frame, + int inlined_jsframe_index) { + HandleScope scope(isolate); + List<Handle<ScopeInfo> > scope_chain; + List<Handle<Context> > context_chain; + + ScopeIterator it(isolate, frame, inlined_jsframe_index); + for (; it.Type() != ScopeIterator::ScopeTypeGlobal && + it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) { + ASSERT(!it.Done()); + scope_chain.Add(it.CurrentScopeInfo()); + context_chain.Add(it.CurrentContext()); } - // Recursively copy the with and catch contexts. - HandleScope scope(isolate); - Handle<Context> previous(current->previous()); - Handle<Context> new_previous = - CopyWithContextChain(isolate, function, previous, base); - Handle<Context> new_current; - if (current->IsCatchContext()) { - Handle<String> name(String::cast(current->extension())); - Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); - new_current = - isolate->factory()->NewCatchContext(function, - new_previous, - name, - thrown_object); - } else if (current->IsBlockContext()) { - Handle<SerializedScopeInfo> scope_info( - SerializedScopeInfo::cast(current->extension())); - new_current = - isolate->factory()->NewBlockContext(function, new_previous, scope_info); - // Copy context slots. - int num_context_slots = scope_info->NumberOfContextSlots(); - for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) { - new_current->set(i, current->get(i)); + // At the end of the chain. Return the base context to link to. + Handle<Context> context = base; + + // Iteratively copy and or materialize the nested contexts. + while (!scope_chain.is_empty()) { + Handle<ScopeInfo> scope_info = scope_chain.RemoveLast(); + Handle<Context> current = context_chain.RemoveLast(); + ASSERT(!(scope_info->HasContext() & current.is_null())); + + if (scope_info->Type() == CATCH_SCOPE) { + Handle<String> name(String::cast(current->extension())); + Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); + context = + isolate->factory()->NewCatchContext(function, + context, + name, + thrown_object); + } else if (scope_info->Type() == BLOCK_SCOPE) { + // Materialize the contents of the block scope into a JSObject. + Handle<JSObject> block_scope_object = + MaterializeBlockScope(isolate, current); + if (block_scope_object.is_null()) { + return Handle<Context>::null(); + } + // Allocate a new function context for the debug evaluation and set the + // extension object. + Handle<Context> new_context = + isolate->factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, + function); + new_context->set_extension(*block_scope_object); + new_context->set_previous(*context); + context = new_context; + } else { + ASSERT(scope_info->Type() == WITH_SCOPE); + ASSERT(current->IsWithContext()); + Handle<JSObject> extension(JSObject::cast(current->extension())); + context = + isolate->factory()->NewWithContext(function, context, extension); } - } else { - ASSERT(current->IsWithContext()); - Handle<JSObject> extension(JSObject::cast(current->extension())); - new_current = - isolate->factory()->NewWithContext(function, new_previous, extension); } - return scope.CloseAndEscape(new_current); + + return scope.CloseAndEscape(context); } @@ -11718,33 +12073,32 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate, // Runtime_DebugEvaluate. static Handle<Object> GetArgumentsObject(Isolate* isolate, JavaScriptFrame* frame, - int inlined_frame_index, - Handle<JSFunction> function, - Handle<SerializedScopeInfo> scope_info, - const ScopeInfo<>* sinfo, + FrameInspector* frame_inspector, + Handle<ScopeInfo> scope_info, Handle<Context> function_context) { // Try to find the value of 'arguments' to pass as parameter. If it is not // found (that is the debugged function does not reference 'arguments' and // does not support eval) then create an 'arguments' object. int index; - if (sinfo->number_of_stack_slots() > 0) { + if (scope_info->StackLocalCount() > 0) { index = scope_info->StackSlotIndex(isolate->heap()->arguments_symbol()); if (index != -1) { return Handle<Object>(frame->GetExpression(index), isolate); } } - if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) { - index = scope_info->ContextSlotIndex(isolate->heap()->arguments_symbol(), - NULL); + if (scope_info->HasHeapAllocatedLocals()) { + VariableMode mode; + InitializationFlag init_flag; + index = scope_info->ContextSlotIndex( + isolate->heap()->arguments_symbol(), &mode, &init_flag); if (index != -1) { return Handle<Object>(function_context->get(index), isolate); } } - FrameInspector frame_inspector(frame, inlined_frame_index, isolate); - - int length = frame_inspector.GetParametersCount(); + Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction())); + int length = frame_inspector->GetParametersCount(); Handle<JSObject> arguments = isolate->factory()->NewArgumentsObject(function, length); Handle<FixedArray> array = isolate->factory()->NewFixedArray(length); @@ -11752,7 +12106,7 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate, AssertNoAllocation no_gc; WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc); for (int i = 0; i < length; i++) { - array->set(i, frame_inspector.GetParameter(i), mode); + array->set(i, frame_inspector->GetParameter(i), mode); } arguments->set_elements(*array); return arguments; @@ -11788,7 +12142,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { } } CONVERT_CHECKED(Smi, wrapped_id, args[1]); - CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]); + CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]); CONVERT_ARG_CHECKED(String, source, 3); CONVERT_BOOLEAN_CHECKED(disable_break, args[4]); Handle<Object> additional_context(args[5]); @@ -11800,17 +12154,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { StackFrame::Id id = UnwrapFrameId(wrapped_id); JavaScriptFrameIterator it(isolate, id); JavaScriptFrame* frame = it.frame(); - Handle<JSFunction> function(JSFunction::cast(frame->function())); - Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info()); - ScopeInfo<> sinfo(*scope_info); + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); + Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction())); + Handle<ScopeInfo> scope_info(function->shared()->scope_info()); // Traverse the saved contexts chain to find the active context for the // selected frame. - SaveContext* save = isolate->save_context(); - while (save != NULL && !save->below(frame)) { - save = save->prev(); - } - ASSERT(save != NULL); + SaveContext* save = FindSavedContextForFrame(isolate, frame); + SaveContext savex(isolate); isolate->set_context(*(save->context())); @@ -11825,14 +12176,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { isolate->factory()->undefined_value()); go_between->set_context(function->context()); #ifdef DEBUG - ScopeInfo<> go_between_sinfo(go_between->shared()->scope_info()); - ASSERT(go_between_sinfo.number_of_parameters() == 0); - ASSERT(go_between_sinfo.number_of_context_slots() == 0); + Handle<ScopeInfo> go_between_scope_info(go_between->shared()->scope_info()); + ASSERT(go_between_scope_info->ParameterCount() == 0); + ASSERT(go_between_scope_info->ContextLocalCount() == 0); #endif // Materialize the content of the local scope into a JSObject. - Handle<JSObject> local_scope = MaterializeLocalScope( - isolate, frame, inlined_frame_index); + Handle<JSObject> local_scope = MaterializeLocalScopeWithFrameInspector( + isolate, frame, &frame_inspector); RETURN_IF_EMPTY_HANDLE(isolate, local_scope); // Allocate a new context for the debug evaluation and set the extension @@ -11845,10 +12196,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { Handle<Context> frame_context(Context::cast(frame->context())); Handle<Context> function_context; // Get the function's context if it has one. - if (scope_info->HasHeapAllocatedLocals()) { + if (scope_info->HasContext()) { function_context = Handle<Context>(frame_context->declaration_context()); } - context = CopyWithContextChain(isolate, go_between, frame_context, context); + context = CopyNestedScopeContextChain(isolate, + go_between, + context, + frame, + inlined_jsframe_index); if (additional_context->IsJSObject()) { Handle<JSObject> extension = Handle<JSObject>::cast(additional_context); @@ -11872,7 +12227,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { Compiler::CompileEval(function_source, context, context->IsGlobalContext(), - kNonStrictMode); + CLASSIC_MODE, + RelocInfo::kNoPosition); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> compiled_function = isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context); @@ -11886,17 +12242,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { if (has_pending_exception) return Failure::Exception(); Handle<Object> arguments = GetArgumentsObject(isolate, - frame, inlined_frame_index, - function, scope_info, - &sinfo, function_context); + frame, + &frame_inspector, + scope_info, + function_context); // Invoke the evaluation function and return the result. - const int argc = 2; - Object** argv[argc] = { arguments.location(), - Handle<Object>::cast(source).location() }; + Handle<Object> argv[] = { arguments, source }; Handle<Object> result = - Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver, - argc, argv, &has_pending_exception); + Execution::Call(Handle<JSFunction>::cast(evaluation_function), + receiver, + ARRAY_SIZE(argv), + argv, + &has_pending_exception); if (has_pending_exception) return Failure::Exception(); // Skip the global proxy as it has no properties and always delegates to the @@ -11946,15 +12304,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) { bool is_global = true; if (additional_context->IsJSObject()) { - // Create a function context first, than put 'with' context on top of it. - Handle<JSFunction> go_between = isolate->factory()->NewFunction( - isolate->factory()->empty_string(), - isolate->factory()->undefined_value()); - go_between->set_context(*context); - context = - isolate->factory()->NewFunctionContext( - Context::MIN_CONTEXT_SLOTS, go_between); - context->set_extension(JSObject::cast(*additional_context)); + // Create a new with context with the additional context information between + // the context of the debugged function and the eval code to be executed. + context = isolate->factory()->NewWithContext( + Handle<JSFunction>(context->closure()), + context, + Handle<JSObject>::cast(additional_context)); is_global = false; } @@ -11962,7 +12317,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) { // Currently, the eval code will be executed in non-strict mode, // even in the strict code context. Handle<SharedFunctionInfo> shared = - Compiler::CompileEval(source, context, is_global, kNonStrictMode); + Compiler::CompileEval(source, + context, + is_global, + CLASSIC_MODE, + RelocInfo::kNoPosition); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> compiled_function = Handle<JSFunction>( @@ -11975,6 +12334,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) { Handle<Object> result = Execution::Call(compiled_function, receiver, 0, NULL, &has_pending_exception); + // Clear the oneshot breakpoints so that the debugger does not step further. + isolate->debug()->ClearStepping(); if (has_pending_exception) return Failure::Exception(); return *result; } @@ -11994,7 +12355,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) { // because using // instances->set(i, *GetScriptWrapper(script)) // is unsafe as GetScriptWrapper might call GC and the C++ compiler might - // already have deferenced the instances handle. + // already have dereferenced the instances handle. Handle<JSValue> wrapper = GetScriptWrapper(script); instances->set(i, *wrapper); } @@ -12002,13 +12363,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) { // Return result as a JS array. Handle<JSObject> result = isolate->factory()->NewJSObject(isolate->array_function()); - Handle<JSArray>::cast(result)->SetContent(*instances); + isolate->factory()->SetContent(Handle<JSArray>::cast(result), instances); return *result; } // Helper function used by Runtime_DebugReferencedBy below. -static int DebugReferencedBy(JSObject* target, +static int DebugReferencedBy(HeapIterator* iterator, + JSObject* target, Object* instance_filter, int max_references, FixedArray* instances, int instances_size, JSFunction* arguments_function) { @@ -12018,9 +12380,8 @@ static int DebugReferencedBy(JSObject* target, // Iterate the heap. int count = 0; JSObject* last = NULL; - HeapIterator iterator; HeapObject* heap_obj = NULL; - while (((heap_obj = iterator.next()) != NULL) && + while (((heap_obj = iterator->next()) != NULL) && (max_references == 0 || count < max_references)) { // Only look at all JSObjects. if (heap_obj->IsJSObject()) { @@ -12085,7 +12446,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) { ASSERT(args.length() == 3); // First perform a full GC in order to avoid references from dead objects. - isolate->heap()->CollectAllGarbage(false); + isolate->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "%DebugReferencedBy"); + // The heap iterator reserves the right to do a GC to make the heap iterable. + // Due to the GC above we know it won't need to do that, but it seems cleaner + // to get the heap iterator constructed before we start having unprotected + // Object* locals that are not protected by handles. // Check parameters. CONVERT_CHECKED(JSObject, target, args[0]); @@ -12095,6 +12461,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) { CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]); RUNTIME_ASSERT(max_references >= 0); + // Get the constructor function for context extension and arguments array. JSObject* arguments_boilerplate = isolate->context()->global_context()->arguments_boilerplate(); @@ -12103,7 +12470,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) { // Get the number of referencing objects. int count; - count = DebugReferencedBy(target, instance_filter, max_references, + HeapIterator heap_iterator; + count = DebugReferencedBy(&heap_iterator, + target, instance_filter, max_references, NULL, 0, arguments_function); // Allocate an array to hold the result. @@ -12114,30 +12483,34 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) { FixedArray* instances = FixedArray::cast(object); // Fill the referencing objects. - count = DebugReferencedBy(target, instance_filter, max_references, + // AllocateFixedArray above does not make the heap non-iterable. + ASSERT(HEAP->IsHeapIterable()); + HeapIterator heap_iterator2; + count = DebugReferencedBy(&heap_iterator2, + target, instance_filter, max_references, instances, count, arguments_function); // Return result as JS array. Object* result; - { MaybeObject* maybe_result = isolate->heap()->AllocateJSObject( + MaybeObject* maybe_result = isolate->heap()->AllocateJSObject( isolate->context()->global_context()->array_function()); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - JSArray::cast(result)->SetContent(instances); - return result; + if (!maybe_result->ToObject(&result)) return maybe_result; + return JSArray::cast(result)->SetContent(instances); } // Helper function used by Runtime_DebugConstructedBy below. -static int DebugConstructedBy(JSFunction* constructor, int max_references, - FixedArray* instances, int instances_size) { +static int DebugConstructedBy(HeapIterator* iterator, + JSFunction* constructor, + int max_references, + FixedArray* instances, + int instances_size) { AssertNoAllocation no_alloc; // Iterate the heap. int count = 0; - HeapIterator iterator; HeapObject* heap_obj = NULL; - while (((heap_obj = iterator.next()) != NULL) && + while (((heap_obj = iterator->next()) != NULL) && (max_references == 0 || count < max_references)) { // Only look at all JSObjects. if (heap_obj->IsJSObject()) { @@ -12165,7 +12538,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) { ASSERT(args.length() == 2); // First perform a full GC in order to avoid dead objects. - isolate->heap()->CollectAllGarbage(false); + isolate->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "%DebugConstructedBy"); // Check parameters. CONVERT_CHECKED(JSFunction, constructor, args[0]); @@ -12174,7 +12548,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) { // Get the number of referencing objects. int count; - count = DebugConstructedBy(constructor, max_references, NULL, 0); + HeapIterator heap_iterator; + count = DebugConstructedBy(&heap_iterator, + constructor, + max_references, + NULL, + 0); // Allocate an array to hold the result. Object* object; @@ -12183,8 +12562,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) { } FixedArray* instances = FixedArray::cast(object); + ASSERT(HEAP->IsHeapIterable()); // Fill the referencing objects. - count = DebugConstructedBy(constructor, max_references, instances, count); + HeapIterator heap_iterator2; + count = DebugConstructedBy(&heap_iterator2, + constructor, + max_references, + instances, + count); // Return result as JS array. Object* result; @@ -12192,8 +12577,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) { isolate->context()->global_context()->array_function()); if (!maybe_result->ToObject(&result)) return maybe_result; } - JSArray::cast(result)->SetContent(instances); - return result; + return JSArray::cast(result)->SetContent(instances); } @@ -12223,7 +12607,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleFunction) { // Get the function and make sure it is compiled. CONVERT_ARG_CHECKED(JSFunction, func, 0); Handle<SharedFunctionInfo> shared(func->shared()); - if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { + if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { return Failure::Exception(); } func->code()->PrintLn(); @@ -12239,7 +12623,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleConstructor) { // Get the function and make sure it is compiled. CONVERT_ARG_CHECKED(JSFunction, func, 0); Handle<SharedFunctionInfo> shared(func->shared()); - if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { + if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { return Failure::Exception(); } shared->construct_stub()->PrintLn(); @@ -12257,14 +12641,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetInferredName) { } -static int FindSharedFunctionInfosForScript(Script* script, +static int FindSharedFunctionInfosForScript(HeapIterator* iterator, + Script* script, FixedArray* buffer) { AssertNoAllocation no_allocations; - int counter = 0; int buffer_size = buffer->length(); - HeapIterator iterator; - for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { + for (HeapObject* obj = iterator->next(); + obj != NULL; + obj = iterator->next()) { ASSERT(obj != NULL); if (!obj->IsSharedFunctionInfo()) { continue; @@ -12290,16 +12675,30 @@ RUNTIME_FUNCTION(MaybeObject*, HandleScope scope(isolate); CONVERT_CHECKED(JSValue, script_value, args[0]); + Handle<Script> script = Handle<Script>(Script::cast(script_value->value())); const int kBufferSize = 32; Handle<FixedArray> array; array = isolate->factory()->NewFixedArray(kBufferSize); - int number = FindSharedFunctionInfosForScript(*script, *array); + int number; + { + isolate->heap()->EnsureHeapIsIterable(); + AssertNoAllocation no_allocations; + HeapIterator heap_iterator; + Script* scr = *script; + FixedArray* arr = *array; + number = FindSharedFunctionInfosForScript(&heap_iterator, scr, arr); + } if (number > kBufferSize) { array = isolate->factory()->NewFixedArray(number); - FindSharedFunctionInfosForScript(*script, *array); + isolate->heap()->EnsureHeapIsIterable(); + AssertNoAllocation no_allocations; + HeapIterator heap_iterator; + Script* scr = *script; + FixedArray* arr = *array; + FindSharedFunctionInfosForScript(&heap_iterator, scr, arr); } Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(array); @@ -12538,7 +12937,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFlags) { // Performs a GC. // Presently, it only does a full GC. RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectGarbage) { - isolate->heap()->CollectAllGarbage(true); + isolate->heap()->CollectAllGarbage(true, "%CollectGarbage"); return isolate->heap()->undefined_value(); } @@ -12780,6 +13179,8 @@ static Handle<Object> Runtime_GetScriptFromScriptName( // Scan the heap for Script objects to find the script with the requested // script data. Handle<Script> script; + script_name->GetHeap()->EnsureHeapIsIterable(); + AssertNoAllocation no_allocation_during_heap_iteration; HeapIterator iterator; HeapObject* obj = NULL; while (script.is_null() && ((obj = iterator.next()) != NULL)) { @@ -12828,34 +13229,32 @@ static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller, bool* seen_caller) { // Only display JS frames. - if (!raw_frame->is_java_script()) + if (!raw_frame->is_java_script()) { return false; + } JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame); Object* raw_fun = frame->function(); // Not sure when this can happen but skip it just in case. - if (!raw_fun->IsJSFunction()) + if (!raw_fun->IsJSFunction()) { return false; + } if ((raw_fun == caller) && !(*seen_caller)) { *seen_caller = true; return false; } // Skip all frames until we've seen the caller. if (!(*seen_caller)) return false; - // Also, skip the most obvious builtin calls. We recognize builtins - // as (1) functions called with the builtins object as the receiver and - // as (2) functions from native scripts called with undefined as the - // receiver (direct calls to helper functions in the builtins - // code). Some builtin calls (such as Number.ADD which is invoked - // using 'call') are very difficult to recognize so we're leaving - // them in for now. - if (frame->receiver()->IsJSBuiltinsObject()) { - return false; - } - JSFunction* fun = JSFunction::cast(raw_fun); - Object* raw_script = fun->shared()->script(); - if (frame->receiver()->IsUndefined() && raw_script->IsScript()) { - int script_type = Script::cast(raw_script)->type()->value(); - return script_type != Script::TYPE_NATIVE; + // Also, skip non-visible built-in functions and any call with the builtins + // object as receiver, so as to not reveal either the builtins object or + // an internal function. + // The --builtins-in-stack-traces command line flag allows including + // internal call sites in the stack trace for debugging purposes. + if (!FLAG_builtins_in_stack_traces) { + JSFunction* fun = JSFunction::cast(raw_fun); + if (frame->receiver()->IsJSBuiltinsObject() || + (fun->IsBuiltin() && !fun->shared()->native())) { + return false; + } } return true; } @@ -12865,9 +13264,10 @@ static bool ShowFrameInStackTrace(StackFrame* raw_frame, // element segments each containing a receiver, function, code and // native code offset. RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectStackTrace) { - ASSERT_EQ(args.length(), 2); - Handle<Object> caller = args.at<Object>(0); - CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]); + ASSERT_EQ(args.length(), 3); + CONVERT_ARG_CHECKED(JSObject, error_object, 0); + Handle<Object> caller = args.at<Object>(1); + CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[2]); HandleScope scope(isolate); Factory* factory = isolate->factory(); @@ -12917,6 +13317,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectStackTrace) { iter.Advance(); } Handle<JSArray> result = factory->NewJSArrayWithElements(elements); + // Capture and attach a more detailed stack trace if necessary. + isolate->CaptureAndSetCurrentStackTraceFor(error_object); result->set_length(Smi::FromInt(cursor)); return *result; } @@ -12991,18 +13393,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFromCache) { // TODO(antonm): consider passing a receiver when constructing a cache. Handle<Object> receiver(isolate->global_context()->global()); // This handle is nor shared, nor used later, so it's safe. - Object** argv[] = { key_handle.location() }; - bool pending_exception = false; + Handle<Object> argv[] = { key_handle }; + bool pending_exception; value = Execution::Call(factory, receiver, - 1, + ARRAY_SIZE(argv), argv, &pending_exception); if (pending_exception) return Failure::Exception(); } #ifdef DEBUG - cache_handle->JSFunctionResultCacheVerify(); + if (FLAG_verify_heap) { + cache_handle->JSFunctionResultCacheVerify(); + } #endif // Function invocation may have cleared the cache. Reread all the data. @@ -13031,7 +13435,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFromCache) { cache_handle->set_finger_index(index); #ifdef DEBUG - cache_handle->JSFunctionResultCacheVerify(); + if (FLAG_verify_heap) { + cache_handle->JSFunctionResultCacheVerify(); + } #endif return *value; @@ -13148,6 +13554,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IS_VAR) { return isolate->heap()->ToBoolean(obj->Has##Name()); \ } +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOnlyElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements) @@ -13164,6 +13571,14 @@ ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalDoubleElements) #undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION + +RUNTIME_FUNCTION(MaybeObject*, Runtime_HaveSameMap) { + ASSERT(args.length() == 2); + CONVERT_CHECKED(JSObject, obj1, args[0]); + CONVERT_CHECKED(JSObject, obj2, args[1]); + return isolate->heap()->ToBoolean(obj1->map() == obj2->map()); +} + // ---------------------------------------------------------------------------- // Implementation of Runtime @@ -13231,14 +13646,19 @@ void Runtime::PerformGC(Object* result) { Isolate* isolate = Isolate::Current(); Failure* failure = Failure::cast(result); if (failure->IsRetryAfterGC()) { + if (isolate->heap()->new_space()->AddFreshPage()) { + return; + } // Try to do a garbage collection; ignore it if it fails. The C // entry stub will throw an out-of-memory exception in that case. - isolate->heap()->CollectGarbage(failure->allocation_space()); + isolate->heap()->CollectGarbage(failure->allocation_space(), + "Runtime::PerformGC"); } else { // Handle last resort GC and make sure to allow future allocations // to grow the heap without causing GCs (if possible). isolate->counters()->gc_last_resort_from_js()->Increment(); - isolate->heap()->CollectAllGarbage(false); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, + "Runtime::PerformGC"); } } diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 1538b7d846..fd818de6df 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -69,7 +69,6 @@ namespace internal { \ F(GetPrototype, 1, 1) \ F(IsInPrototypeChain, 2, 1) \ - F(SetHiddenPrototype, 2, 1) \ \ F(IsConstructCall, 0, 1) \ \ @@ -80,6 +79,7 @@ namespace internal { \ /* Utilities */ \ F(CheckIsBootstrapping, 0, 1) \ + F(Call, -1 /* >= 2 */, 1) \ F(Apply, 5, 1) \ F(GetFunctionDelegate, 1, 1) \ F(GetConstructorDelegate, 1, 1) \ @@ -98,6 +98,7 @@ namespace internal { F(SetNewFunctionAttributes, 1, 1) \ F(AllocateInNewSpace, 1, 1) \ F(SetNativeFlag, 1, 1) \ + F(StoreArrayLiteralElement, 5, 1) \ \ /* Array join support */ \ F(PushIfAbsent, 2, 1) \ @@ -142,7 +143,7 @@ namespace internal { F(StringAdd, 2, 1) \ F(StringBuilderConcat, 3, 1) \ F(StringBuilderJoin, 3, 1) \ - F(SparseJoinWithSeparator, 3, 1) \ + F(SparseJoinWithSeparator, 3, 1) \ \ /* Bit operations */ \ F(NumberOr, 2, 1) \ @@ -196,6 +197,7 @@ namespace internal { F(StringLocaleCompare, 2, 1) \ F(SubString, 3, 1) \ F(StringReplaceRegExpWithString, 4, 1) \ + F(StringReplaceOneCharWithString, 3, 1) \ F(StringMatch, 3, 1) \ F(StringTrim, 3, 1) \ F(StringToArray, 2, 1) \ @@ -211,14 +213,14 @@ namespace internal { /* Reflection */ \ F(FunctionSetInstanceClassName, 2, 1) \ F(FunctionSetLength, 2, 1) \ - F(BoundFunctionSetLength, 2, 1) \ F(FunctionSetPrototype, 2, 1) \ F(FunctionSetReadOnlyPrototype, 1, 1) \ F(FunctionGetName, 1, 1) \ F(FunctionSetName, 2, 1) \ F(FunctionNameShouldPrintAsAnonymous, 1, 1) \ F(FunctionMarkNameShouldPrintAsAnonymous, 1, 1) \ - F(FunctionSetBound, 1, 1) \ + F(FunctionBindArguments, 4, 1) \ + F(BoundFunctionGetBindings, 1, 1) \ F(FunctionRemovePrototype, 1, 1) \ F(FunctionGetSourceCode, 1, 1) \ F(FunctionGetScript, 1, 1) \ @@ -227,7 +229,7 @@ namespace internal { F(FunctionIsAPIFunction, 1, 1) \ F(FunctionIsBuiltin, 1, 1) \ F(GetScript, 1, 1) \ - F(CollectStackTrace, 2, 1) \ + F(CollectStackTrace, 3, 1) \ F(GetV8Version, 0, 1) \ \ F(ClassOf, 1, 1) \ @@ -246,7 +248,7 @@ namespace internal { F(DateLocalTimezone, 1, 1) \ F(DateLocalTimeOffset, 0, 1) \ F(DateDaylightSavingsOffset, 1, 1) \ - F(DateMakeDay, 3, 1) \ + F(DateMakeDay, 2, 1) \ F(DateYMDFromTime, 2, 1) \ \ /* Numbers */ \ @@ -257,8 +259,7 @@ namespace internal { \ /* Eval */ \ F(GlobalReceiver, 1, 1) \ - F(ResolvePossiblyDirectEval, 4, 2) \ - F(ResolvePossiblyDirectEvalNoLookup, 4, 2) \ + F(ResolvePossiblyDirectEval, 5, 2) \ \ F(SetProperty, -1 /* 4 or 5 */, 1) \ F(DefineOrRedefineDataProperty, 4, 1) \ @@ -278,9 +279,6 @@ namespace internal { \ /* Literals */ \ F(MaterializeRegExpLiteral, 4, 1)\ - F(CreateArrayLiteralBoilerplate, 3, 1) \ - F(CloneLiteralBoilerplate, 1, 1) \ - F(CloneShallowLiteralBoilerplate, 1, 1) \ F(CreateObjectLiteral, 4, 1) \ F(CreateObjectLiteralShallow, 4, 1) \ F(CreateArrayLiteral, 3, 1) \ @@ -296,6 +294,17 @@ namespace internal { F(GetConstructTrap, 1, 1) \ F(Fix, 1, 1) \ \ + /* Harmony sets */ \ + F(SetInitialize, 1, 1) \ + F(SetAdd, 2, 1) \ + F(SetHas, 2, 1) \ + F(SetDelete, 2, 1) \ + \ + /* Harmony maps */ \ + F(MapInitialize, 1, 1) \ + F(MapGet, 2, 1) \ + F(MapSet, 3, 1) \ + \ /* Harmony weakmaps */ \ F(WeakMapInitialize, 1, 1) \ F(WeakMapGet, 2, 1) \ @@ -304,7 +313,7 @@ namespace internal { /* Statements */ \ F(NewClosure, 3, 1) \ F(NewObject, 1, 1) \ - F(NewObjectFromBound, 2, 1) \ + F(NewObjectFromBound, 1, 1) \ F(FinalizeInstanceSize, 1, 1) \ F(Throw, 1, 1) \ F(ReThrow, 1, 1) \ @@ -354,6 +363,7 @@ namespace internal { F(IS_VAR, 1, 1) \ \ /* expose boolean functions from objects-inl.h */ \ + F(HasFastSmiOnlyElements, 1, 1) \ F(HasFastElements, 1, 1) \ F(HasFastDoubleElements, 1, 1) \ F(HasDictionaryElements, 1, 1) \ @@ -367,6 +377,9 @@ namespace internal { F(HasExternalUnsignedIntElements, 1, 1) \ F(HasExternalFloatElements, 1, 1) \ F(HasExternalDoubleElements, 1, 1) \ + F(TransitionElementsSmiToDouble, 1, 1) \ + F(TransitionElementsDoubleToObject, 1, 1) \ + F(HaveSameMap, 2, 1) \ /* profiler */ \ F(ProfilerResume, 0, 1) \ F(ProfilerPause, 0, 1) @@ -492,6 +505,7 @@ namespace internal { F(MathPow, 2, 1) \ F(MathSin, 1, 1) \ F(MathCos, 1, 1) \ + F(MathTan, 1, 1) \ F(MathSqrt, 1, 1) \ F(MathLog, 1, 1) \ F(IsRegExpEquivalent, 2, 1) \ @@ -616,6 +630,13 @@ class Runtime : public AllStatic { // Get the intrinsic function with the given FunctionId. static const Function* FunctionForId(FunctionId id); + static Handle<String> StringReplaceOneCharWithString(Isolate* isolate, + Handle<String> subject, + Handle<String> search, + Handle<String> replace, + bool* found, + int recursion_limit); + // General-purpose helper functions for runtime system. static int StringMatch(Isolate* isolate, Handle<String> sub, @@ -624,16 +645,14 @@ class Runtime : public AllStatic { static bool IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch); - // TODO(1240886): The following three methods are *not* handle safe, - // but accept handle arguments. This seems fragile. + // TODO(1240886): Some of the following methods are *not* handle safe, but + // accept handle arguments. This seems fragile. // Support getting the characters in a string using [] notation as // in Firefox/SpiderMonkey, Safari and Opera. MUST_USE_RESULT static MaybeObject* GetElementOrCharAt(Isolate* isolate, Handle<Object> object, uint32_t index); - MUST_USE_RESULT static MaybeObject* GetElement(Handle<Object> object, - uint32_t index); MUST_USE_RESULT static MaybeObject* SetObjectProperty( Isolate* isolate, @@ -667,17 +686,21 @@ class Runtime : public AllStatic { // Helper functions used stubs. static void PerformGC(Object* result); + + // Used in runtime.cc and hydrogen's VisitArrayLiteral. + static Handle<Object> CreateArrayLiteralBoilerplate( + Isolate* isolate, + Handle<FixedArray> literals, + Handle<FixedArray> elements); }; //--------------------------------------------------------------------------- // Constants used by interface to runtime functions. -enum kDeclareGlobalsFlags { - kDeclareGlobalsEvalFlag = 1 << 0, - kDeclareGlobalsStrictModeFlag = 1 << 1, - kDeclareGlobalsNativeFlag = 1 << 2 -}; +class DeclareGlobalsEvalFlag: public BitField<bool, 0, 1> {}; +class DeclareGlobalsNativeFlag: public BitField<bool, 1, 1> {}; +class DeclareGlobalsLanguageMode: public BitField<LanguageMode, 2, 2> {}; } } // namespace v8::internal diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js index 14ff1b69cf..d0cdb3ef60 100644 --- a/deps/v8/src/runtime.js +++ b/deps/v8/src/runtime.js @@ -355,7 +355,7 @@ function IN(x) { if (!IS_SPEC_OBJECT(x)) { throw %MakeTypeError('invalid_in_operator_use', [this, x]); } - return %_IsNonNegativeSmi(this) && !%IsJSProxy(x) ? + return %_IsNonNegativeSmi(this) ? %HasElement(x, this) : %HasProperty(x, %ToString(this)); } @@ -375,6 +375,12 @@ function INSTANCE_OF(F) { return 1; } + // Check if function is bound, if so, get [[BoundFunction]] from it + // and use that instead of F. + var bindings = %BoundFunctionGetBindings(F); + if (bindings) { + F = bindings[kBoundFunctionIndex]; // Always a non-bound function. + } // Get the prototype of F; if it is not an object, throw an error. var O = F.prototype; if (!IS_SPEC_OBJECT(O)) { @@ -386,13 +392,6 @@ function INSTANCE_OF(F) { } -// Get an array of property keys for the given object. Used in -// for-in statements. -function GET_KEYS() { - return %GetPropertyNames(this); -} - - // Filter a given key against an object by checking if the object // has a property with the given key; return the key as a string if // it has. Otherwise returns 0 (smi). Used in for-in statements. @@ -429,20 +428,10 @@ function CALL_FUNCTION_PROXY() { } -function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR(proxy) { - var arity = %_ArgumentsLength() - 1; +function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR() { + var proxy = this; var trap = %GetConstructTrap(proxy); - var receiver = void 0; - if (!IS_UNDEFINED(trap)) { - trap = %GetCallTrap(proxy); - var proto = proxy.prototype; - if (!IS_SPEC_OBJECT(proto) && proto !== null) { - throw MakeTypeError("proto_object_or_null", [proto]); - } - receiver = new global.Object(); - receiver.__proto__ = proto; - } - return %Apply(trap, this, arguments, 1, arity); + return %Apply(trap, this, arguments, 0, %_ArgumentsLength()); } @@ -469,11 +458,12 @@ function APPLY_PREPARE(args) { } if (!IS_SPEC_FUNCTION(this)) { - throw %MakeTypeError('apply_non_function', [ %ToString(this), typeof this ]); + throw %MakeTypeError('apply_non_function', + [ %ToString(this), typeof this ]); } // Make sure the arguments list has the right type. - if (args != null && !IS_ARRAY(args) && !IS_ARGUMENTS(args)) { + if (args != null && !IS_SPEC_OBJECT(args)) { throw %MakeTypeError('apply_wrong_args', []); } diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc index 69ea8ae6e7..42a1c2bc31 100644..100755 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -36,30 +36,27 @@ namespace v8 { namespace internal { // ---------------------------------------------------------------------------- -// Scanner::LiteralScope - -Scanner::LiteralScope::LiteralScope(Scanner* self) - : scanner_(self), complete_(false) { - self->StartLiteral(); -} - +// Scanner -Scanner::LiteralScope::~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); -} +Scanner::Scanner(UnicodeCache* unicode_cache) + : unicode_cache_(unicode_cache), + octal_pos_(Location::invalid()), + harmony_scoping_(false), + harmony_modules_(false) { } -void Scanner::LiteralScope::Complete() { - scanner_->TerminateLiteral(); - complete_ = true; +void Scanner::Initialize(UC16CharacterStream* source) { + source_ = source; + // Need to capture identifiers in order to recognize "get" and "set" + // in object literals. + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); } -// ---------------------------------------------------------------------------- -// Scanner - -Scanner::Scanner(UnicodeCache* unicode_cache) - : unicode_cache_(unicode_cache) { } - uc32 Scanner::ScanHexNumber(int expected_length) { ASSERT(expected_length <= 4); // prevent overflow @@ -88,29 +85,6 @@ uc32 Scanner::ScanHexNumber(int expected_length) { } - -// ---------------------------------------------------------------------------- -// JavaScriptScanner - -JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants) - : Scanner(scanner_contants), - octal_pos_(Location::invalid()), - harmony_block_scoping_(false) { } - - -void JavaScriptScanner::Initialize(UC16CharacterStream* source) { - source_ = source; - // Need to capture identifiers in order to recognize "get" and "set" - // in object literals. - Init(); - // Skip initial whitespace allowing HTML comment ends just like - // after a newline and scan first token. - has_line_terminator_before_next_ = true; - SkipWhiteSpace(); - Scan(); -} - - // Ensure that tokens can be stored in a byte. STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); @@ -247,7 +221,7 @@ static const byte one_char_tokens[] = { }; -Token::Value JavaScriptScanner::Next() { +Token::Value Scanner::Next() { current_ = next_; has_line_terminator_before_next_ = false; has_multiline_comment_before_next_ = false; @@ -279,7 +253,7 @@ static inline bool IsByteOrderMark(uc32 c) { } -bool JavaScriptScanner::SkipWhiteSpace() { +bool Scanner::SkipWhiteSpace() { int start_position = source_pos(); while (true) { @@ -319,7 +293,7 @@ bool JavaScriptScanner::SkipWhiteSpace() { } -Token::Value JavaScriptScanner::SkipSingleLineComment() { +Token::Value Scanner::SkipSingleLineComment() { Advance(); // The line terminator at the end of the line is not considered @@ -335,7 +309,7 @@ Token::Value JavaScriptScanner::SkipSingleLineComment() { } -Token::Value JavaScriptScanner::SkipMultiLineComment() { +Token::Value Scanner::SkipMultiLineComment() { ASSERT(c0_ == '*'); Advance(); @@ -361,7 +335,7 @@ Token::Value JavaScriptScanner::SkipMultiLineComment() { } -Token::Value JavaScriptScanner::ScanHtmlComment() { +Token::Value Scanner::ScanHtmlComment() { // Check for <!-- comments. ASSERT(c0_ == '!'); Advance(); @@ -376,7 +350,7 @@ Token::Value JavaScriptScanner::ScanHtmlComment() { } -void JavaScriptScanner::Scan() { +void Scanner::Scan() { next_.literal_chars = NULL; Token::Value token; do { @@ -616,7 +590,7 @@ void JavaScriptScanner::Scan() { } -void JavaScriptScanner::SeekForward(int pos) { +void Scanner::SeekForward(int pos) { // After this call, we will have the token at the given position as // the "next" token. The "current" token will be invalid. if (pos == next_.location.beg_pos) return; @@ -637,7 +611,7 @@ void JavaScriptScanner::SeekForward(int pos) { } -void JavaScriptScanner::ScanEscape() { +void Scanner::ScanEscape() { uc32 c = c0_; Advance(); @@ -689,7 +663,7 @@ void JavaScriptScanner::ScanEscape() { // Octal escapes of the forms '\0xx' and '\xxx' are not a part of // ECMA-262. Other JS VMs support them. -uc32 JavaScriptScanner::ScanOctalEscape(uc32 c, int length) { +uc32 Scanner::ScanOctalEscape(uc32 c, int length) { uc32 x = c - '0'; int i = 0; for (; i < length; i++) { @@ -712,7 +686,7 @@ uc32 JavaScriptScanner::ScanOctalEscape(uc32 c, int length) { } -Token::Value JavaScriptScanner::ScanString() { +Token::Value Scanner::ScanString() { uc32 quote = c0_; Advance(); // consume quote @@ -736,13 +710,13 @@ Token::Value JavaScriptScanner::ScanString() { } -void JavaScriptScanner::ScanDecimalDigits() { +void Scanner::ScanDecimalDigits() { while (IsDecimalDigit(c0_)) AddLiteralCharAdvance(); } -Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { +Token::Value Scanner::ScanNumber(bool seen_period) { ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; @@ -827,7 +801,7 @@ Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { } -uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { +uc32 Scanner::ScanIdentifierUnicodeEscape() { Advance(); if (c0_ != 'u') return -1; Advance(); @@ -857,7 +831,8 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { KEYWORD_GROUP('e') \ KEYWORD("else", Token::ELSE) \ KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("export", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("export", harmony_modules \ + ? Token::EXPORT : Token::FUTURE_RESERVED_WORD) \ KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \ KEYWORD_GROUP('f') \ KEYWORD("false", Token::FALSE_LITERAL) \ @@ -867,13 +842,17 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { KEYWORD_GROUP('i') \ KEYWORD("if", Token::IF) \ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("import", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("import", harmony_modules \ + ? Token::IMPORT : Token::FUTURE_RESERVED_WORD) \ KEYWORD("in", Token::IN) \ KEYWORD("instanceof", Token::INSTANCEOF) \ KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ - KEYWORD("let", harmony_block_scoping \ + KEYWORD("let", harmony_scoping \ ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('m') \ + KEYWORD("module", harmony_modules \ + ? Token::MODULE : Token::IDENTIFIER) \ KEYWORD_GROUP('n') \ KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ @@ -906,7 +885,8 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { static Token::Value KeywordOrIdentifierToken(const char* input, int input_length, - bool harmony_block_scoping) { + bool harmony_scoping, + bool harmony_modules) { ASSERT(input_length >= 1); const int kMinLength = 2; const int kMaxLength = 10; @@ -944,7 +924,7 @@ static Token::Value KeywordOrIdentifierToken(const char* input, } -Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { +Token::Value Scanner::ScanIdentifierOrKeyword() { ASSERT(unicode_cache_->IsIdentifierStart(c0_)); LiteralScope literal(this); // Scan identifier start character. @@ -982,14 +962,15 @@ Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { Vector<const char> chars = next_.literal_chars->ascii_literal(); return KeywordOrIdentifierToken(chars.start(), chars.length(), - harmony_block_scoping_); + harmony_scoping_, + harmony_modules_); } return Token::IDENTIFIER; } -Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { +Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal) { // Scan the rest of the identifier characters. while (unicode_cache_->IsIdentifierPart(c0_)) { if (c0_ == '\\') { @@ -1012,7 +993,7 @@ Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { } -bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { +bool Scanner::ScanRegExpPattern(bool seen_equal) { // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags bool in_character_class = false; @@ -1059,7 +1040,7 @@ bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { } -bool JavaScriptScanner::ScanLiteralUnicodeEscape() { +bool Scanner::ScanLiteralUnicodeEscape() { ASSERT(c0_ == '\\'); uc32 chars_read[6] = {'\\', 'u', 0, 0, 0, 0}; Advance(); @@ -1089,7 +1070,7 @@ bool JavaScriptScanner::ScanLiteralUnicodeEscape() { } -bool JavaScriptScanner::ScanRegExpFlags() { +bool Scanner::ScanRegExpFlags() { // Scan regular expression flags. LiteralScope literal(this); while (unicode_cache_->IsIdentifierPart(c0_)) { diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index 16c3a427c9..e892fe0c1f 100644 --- a/deps/v8/src/scanner.h +++ b/deps/v8/src/scanner.h @@ -41,6 +41,26 @@ namespace v8 { namespace internal { + +// General collection of (multi-)bit-flags that can be passed to scanners and +// parsers to signify their (initial) mode of operation. +enum ParsingFlags { + kNoParsingFlags = 0, + // Embed LanguageMode values in parsing flags, i.e., equivalent to: + // CLASSIC_MODE = 0, + // STRICT_MODE, + // EXTENDED_MODE, + kLanguageModeMask = 0x03, + kAllowLazy = 0x04, + kAllowNativesSyntax = 0x08, + kAllowModules = 0x10 +}; + +STATIC_ASSERT((kLanguageModeMask & CLASSIC_MODE) == CLASSIC_MODE); +STATIC_ASSERT((kLanguageModeMask & STRICT_MODE) == STRICT_MODE); +STATIC_ASSERT((kLanguageModeMask & EXTENDED_MODE) == EXTENDED_MODE); + + // Returns the value (0 .. 15) of a hexadecimal character c. // If c is not a legal hexadecimal character, returns a value < 0. inline int HexValue(uc32 c) { @@ -158,7 +178,7 @@ class LiteralBuffer { } } - inline void AddChar(uc16 character) { + INLINE(void AddChar(uc16 character)) { if (position_ >= backing_store_.length()) ExpandBuffer(); if (is_ascii_) { if (character < kMaxAsciiCharCodeU) { @@ -249,35 +269,32 @@ class LiteralBuffer { // ---------------------------------------------------------------------------- -// Scanner base-class. +// JavaScript Scanner. -// Generic functionality used by both JSON and JavaScript scanners. class Scanner { public: - // -1 is outside of the range of any real source code. - static const int kNoOctalLocation = -1; - - typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; - + // Scoped helper for literal recording. Automatically drops the literal + // if aborting the scanning before it's complete. class LiteralScope { public: - explicit LiteralScope(Scanner* self); - ~LiteralScope(); - void Complete(); + explicit LiteralScope(Scanner* self) + : scanner_(self), complete_(false) { + scanner_->StartLiteral(); + } + ~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); + } + void Complete() { + scanner_->TerminateLiteral(); + complete_ = true; + } private: Scanner* scanner_; bool complete_; }; - explicit Scanner(UnicodeCache* scanner_contants); - - // Returns the current token again. - Token::Value current_token() { return current_.token; } - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return next_.token; } - + // Representation of an interval of source positions. struct Location { Location(int b, int e) : beg_pos(b), end_pos(e) { } Location() : beg_pos(0), end_pos(0) { } @@ -292,21 +309,28 @@ class Scanner { int end_pos; }; + // -1 is outside of the range of any real source code. + static const int kNoOctalLocation = -1; + + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + explicit Scanner(UnicodeCache* scanner_contants); + + void Initialize(UC16CharacterStream* source); + + // Returns the next token and advances input. + Token::Value Next(); + // Returns the current token again. + Token::Value current_token() { return current_.token; } // Returns the location information for the current token - // (the token returned by Next()). + // (the token last returned by Next()). Location location() const { return current_.location; } - Location peek_location() const { return next_.location; } - // Returns the literal string, if any, for the current token (the - // token returned by Next()). The string is 0-terminated and in - // UTF-8 format; they may contain 0-characters. Literal strings are - // collected for identifiers, strings, and numbers. + // token last returned by Next()). The string is 0-terminated. + // Literal strings are collected for identifiers, strings, and + // numbers. // These functions only give the correct result if the literal // was scanned between calls to StartLiteral() and TerminateLiteral(). - bool is_literal_ascii() { - ASSERT_NOT_NULL(current_.literal_chars); - return current_.literal_chars->is_ascii(); - } Vector<const char> literal_ascii_string() { ASSERT_NOT_NULL(current_.literal_chars); return current_.literal_chars->ascii_literal(); @@ -315,6 +339,10 @@ class Scanner { ASSERT_NOT_NULL(current_.literal_chars); return current_.literal_chars->uc16_literal(); } + bool is_literal_ascii() { + ASSERT_NOT_NULL(current_.literal_chars); + return current_.literal_chars->is_ascii(); + } int literal_length() const { ASSERT_NOT_NULL(current_.literal_chars); return current_.literal_chars->length(); @@ -330,12 +358,15 @@ class Scanner { return current_.literal_chars->length() != source_length; } + // Similar functions for the upcoming token. + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return next_.token; } + + Location peek_location() const { return next_.location; } + // Returns the literal string for the next token (the token that // would be returned if Next() were called). - bool is_next_literal_ascii() { - ASSERT_NOT_NULL(next_.literal_chars); - return next_.literal_chars->is_ascii(); - } Vector<const char> next_literal_ascii_string() { ASSERT_NOT_NULL(next_.literal_chars); return next_.literal_chars->ascii_literal(); @@ -344,6 +375,10 @@ class Scanner { ASSERT_NOT_NULL(next_.literal_chars); return next_.literal_chars->uc16_literal(); } + bool is_next_literal_ascii() { + ASSERT_NOT_NULL(next_.literal_chars); + return next_.literal_chars->is_ascii(); + } int next_literal_length() const { ASSERT_NOT_NULL(next_.literal_chars); return next_.literal_chars->length(); @@ -353,7 +388,52 @@ class Scanner { static const int kCharacterLookaheadBufferSize = 1; - protected: + // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. + uc32 ScanOctalEscape(uc32 c, int length); + + // Returns the location of the last seen octal literal. + Location octal_position() const { return octal_pos_; } + void clear_octal_position() { octal_pos_ = Location::invalid(); } + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + bool HarmonyScoping() const { + return harmony_scoping_; + } + void SetHarmonyScoping(bool scoping) { + harmony_scoping_ = scoping; + } + bool HarmonyModules() const { + return harmony_modules_; + } + void SetHarmonyModules(bool modules) { + harmony_modules_ = modules; + } + + + // Returns true if there was a line terminator before the peek'ed token, + // possibly inside a multi-line comment. + bool HasAnyLineTerminatorBeforeNext() const { + return has_line_terminator_before_next_ || + has_multiline_comment_before_next_; + } + + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Returns true if regexp flags are scanned (always since flags can + // be empty). + bool ScanRegExpFlags(); + + // Tells whether the buffer contains an identifier (no escapes). + // Used for checking if a property name is an identifier. + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + private: // The current and look-ahead token. struct TokenDesc { Token::Value token; @@ -378,7 +458,7 @@ class Scanner { next_.literal_chars = free_buffer; } - inline void AddLiteralChar(uc32 c) { + INLINE(void AddLiteralChar(uc32 c)) { ASSERT_NOT_NULL(next_.literal_chars); next_.literal_chars->AddChar(c); } @@ -423,107 +503,14 @@ class Scanner { uc32 ScanHexNumber(int expected_length); - // Return the current source position. - int source_pos() { - return source_->pos() - kCharacterLookaheadBufferSize; - } - - UnicodeCache* unicode_cache_; - - // Buffers collecting literal strings, numbers, etc. - LiteralBuffer literal_buffer1_; - LiteralBuffer literal_buffer2_; - - TokenDesc current_; // desc for current token (as returned by Next()) - TokenDesc next_; // desc for next token (one token look-ahead) - - // Input stream. Must be initialized to an UC16CharacterStream. - UC16CharacterStream* source_; - - // One Unicode character look-ahead; c0_ < 0 at the end of the input. - uc32 c0_; -}; - -// ---------------------------------------------------------------------------- -// JavaScriptScanner - base logic for JavaScript scanning. - -class JavaScriptScanner : public Scanner { - public: - // A LiteralScope that disables recording of some types of JavaScript - // literals. If the scanner is configured to not record the specific - // type of literal, the scope will not call StartLiteral. - class LiteralScope { - public: - explicit LiteralScope(JavaScriptScanner* self) - : scanner_(self), complete_(false) { - scanner_->StartLiteral(); - } - ~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); - } - void Complete() { - scanner_->TerminateLiteral(); - complete_ = true; - } - - private: - JavaScriptScanner* scanner_; - bool complete_; - }; - - explicit JavaScriptScanner(UnicodeCache* scanner_contants); - - void Initialize(UC16CharacterStream* source); - - // Returns the next token. - Token::Value Next(); - - // Returns true if there was a line terminator before the peek'ed token, - // possibly inside a multi-line comment. - bool HasAnyLineTerminatorBeforeNext() const { - return has_line_terminator_before_next_ || - has_multiline_comment_before_next_; - } - - // Scans the input as a regular expression pattern, previous - // character(s) must be /(=). Returns true if a pattern is scanned. - bool ScanRegExpPattern(bool seen_equal); - // Returns true if regexp flags are scanned (always since flags can - // be empty). - bool ScanRegExpFlags(); - - // Tells whether the buffer contains an identifier (no escapes). - // Used for checking if a property name is an identifier. - static bool IsIdentifier(unibrow::CharacterStream* buffer); - - // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. - uc32 ScanOctalEscape(uc32 c, int length); - - // Returns the location of the last seen octal literal - Location octal_position() const { return octal_pos_; } - void clear_octal_position() { octal_pos_ = Location::invalid(); } - - // Seek forward to the given position. This operation does not - // work in general, for instance when there are pushed back - // characters, but works for seeking forward until simple delimiter - // tokens, which is what it is used for. - void SeekForward(int pos); - - bool HarmonyBlockScoping() const { - return harmony_block_scoping_; - } - void SetHarmonyBlockScoping(bool block_scoping) { - harmony_block_scoping_ = block_scoping; - } - + // Scans a single JavaScript token. + void Scan(); - protected: bool SkipWhiteSpace(); Token::Value SkipSingleLineComment(); Token::Value SkipMultiLineComment(); - - // Scans a single JavaScript token. - void Scan(); + // Scans a possible HTML comment -- begins with '<!'. + Token::Value ScanHtmlComment(); void ScanDecimalDigits(); Token::Value ScanNumber(bool seen_period); @@ -533,9 +520,6 @@ class JavaScriptScanner : public Scanner { void ScanEscape(); Token::Value ScanString(); - // Scans a possible HTML comment -- begins with '<!'. - Token::Value ScanHtmlComment(); - // Decodes a unicode escape-sequence which is part of an identifier. // If the escape sequence cannot be decoded the result is kBadChar. uc32 ScanIdentifierUnicodeEscape(); @@ -544,9 +528,30 @@ class JavaScriptScanner : public Scanner { // flags. bool ScanLiteralUnicodeEscape(); + // Return the current source position. + int source_pos() { + return source_->pos() - kCharacterLookaheadBufferSize; + } + + UnicodeCache* unicode_cache_; + + // Buffers collecting literal strings, numbers, etc. + LiteralBuffer literal_buffer1_; + LiteralBuffer literal_buffer2_; + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + + // Input stream. Must be initialized to an UC16CharacterStream. + UC16CharacterStream* source_; + + // Start position of the octal literal last scanned. Location octal_pos_; + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; + // Whether there is a line terminator whitespace character after // the current token, and before the next. Does not count newlines // inside multiline comments. @@ -554,9 +559,10 @@ class JavaScriptScanner : public Scanner { // Whether there is a multi-line comment that contains a // line-terminator after the current token, and before the next. bool has_multiline_comment_before_next_; - // Whether we scan 'let' as a keyword for harmony block scoped - // let bindings. - bool harmony_block_scoping_; + // Whether we scan 'let' as a keyword for harmony block-scoped let bindings. + bool harmony_scoping_; + // Whether we scan 'module', 'import', 'export' as keywords. + bool harmony_modules_; }; } } // namespace v8::internal diff --git a/deps/v8/src/scopeinfo.cc b/deps/v8/src/scopeinfo.cc index ad31ca47c6..0f36234701 100644 --- a/deps/v8/src/scopeinfo.cc +++ b/deps/v8/src/scopeinfo.cc @@ -38,456 +38,297 @@ namespace v8 { namespace internal { -static int CompareLocal(Variable* const* v, Variable* const* w) { - int x = (*v)->index(); - int y = (*w)->index(); - // Consider sorting them according to type as well? - return x - y; -} - - -template<class Allocator> -ScopeInfo<Allocator>::ScopeInfo(Scope* scope) - : function_name_(FACTORY->empty_symbol()), - calls_eval_(scope->calls_eval()), - is_strict_mode_(scope->is_strict_mode()), - parameters_(scope->num_parameters()), - stack_slots_(scope->num_stack_slots()), - context_slots_(scope->num_heap_slots()), - context_modes_(scope->num_heap_slots()) { - // Add parameters. - for (int i = 0; i < scope->num_parameters(); i++) { - ASSERT(parameters_.length() == i); - parameters_.Add(scope->parameter(i)->name()); - } - - // Add stack locals and collect heap locals. - // We are assuming that the locals' slots are allocated in - // increasing order, so we can simply add them to the - // ScopeInfo lists. However, due to usage analysis, this is - // not true for context-allocated locals: Some of them - // may be parameters which are allocated before the - // non-parameter locals. When the non-parameter locals are - // sorted according to usage, the allocated slot indices may - // not be in increasing order with the variable list anymore. - // Thus, we first collect the context-allocated locals, and then - // sort them by context slot index before adding them to the - // ScopeInfo list. - List<Variable*, Allocator> locals(32); // 32 is a wild guess - ASSERT(locals.is_empty()); - scope->CollectUsedVariables(&locals); - locals.Sort(&CompareLocal); - - List<Variable*, Allocator> heap_locals(locals.length()); - for (int i = 0; i < locals.length(); i++) { - Variable* var = locals[i]; - if (var->is_used()) { - switch (var->location()) { - case Variable::UNALLOCATED: - case Variable::PARAMETER: - break; - - case Variable::LOCAL: - ASSERT(stack_slots_.length() == var->index()); - stack_slots_.Add(var->name()); - break; - - case Variable::CONTEXT: - heap_locals.Add(var); - break; - - case Variable::LOOKUP: - // We don't expect lookup variables in the locals list. - UNREACHABLE(); - break; - } - } - } - - // Add heap locals. - if (scope->num_heap_slots() > 0) { - // Add user-defined slots. - for (int i = 0; i < heap_locals.length(); i++) { - ASSERT(heap_locals[i]->index() - Context::MIN_CONTEXT_SLOTS == - context_slots_.length()); - ASSERT(heap_locals[i]->index() - Context::MIN_CONTEXT_SLOTS == - context_modes_.length()); - context_slots_.Add(heap_locals[i]->name()); - context_modes_.Add(heap_locals[i]->mode()); +Handle<ScopeInfo> ScopeInfo::Create(Scope* scope) { + // Collect stack and context locals. + ZoneList<Variable*> stack_locals(scope->StackLocalCount()); + ZoneList<Variable*> context_locals(scope->ContextLocalCount()); + scope->CollectStackAndContextLocals(&stack_locals, &context_locals); + const int stack_local_count = stack_locals.length(); + const int context_local_count = context_locals.length(); + // Make sure we allocate the correct amount. + ASSERT(scope->StackLocalCount() == stack_local_count); + ASSERT(scope->ContextLocalCount() == context_local_count); + + // Determine use and location of the function variable if it is present. + FunctionVariableInfo function_name_info; + VariableMode function_variable_mode; + if (scope->is_function_scope() && scope->function() != NULL) { + Variable* var = scope->function()->var(); + if (!var->is_used()) { + function_name_info = UNUSED; + } else if (var->IsContextSlot()) { + function_name_info = CONTEXT; + } else { + ASSERT(var->IsStackLocal()); + function_name_info = STACK; } - + function_variable_mode = var->mode(); } else { - ASSERT(heap_locals.length() == 0); + function_name_info = NONE; + function_variable_mode = VAR; } - // Add the function context slot, if present. - // For now, this must happen at the very end because of the - // ordering of the scope info slots and the respective slot indices. - if (scope->is_function_scope()) { - VariableProxy* proxy = scope->function(); - if (proxy != NULL && - proxy->var()->is_used() && - proxy->var()->IsContextSlot()) { - function_name_ = proxy->name(); - // Note that we must not find the function name in the context slot - // list - instead it must be handled separately in the - // Contexts::Lookup() function. Thus record an empty symbol here so we - // get the correct number of context slots. - ASSERT(proxy->var()->index() - Context::MIN_CONTEXT_SLOTS == - context_slots_.length()); - ASSERT(proxy->var()->index() - Context::MIN_CONTEXT_SLOTS == - context_modes_.length()); - context_slots_.Add(FACTORY->empty_symbol()); - context_modes_.Add(Variable::INTERNAL); - } + const bool has_function_name = function_name_info != NONE; + const int parameter_count = scope->num_parameters(); + const int length = kVariablePartIndex + + parameter_count + stack_local_count + 2 * context_local_count + + (has_function_name ? 2 : 0); + + Handle<ScopeInfo> scope_info = FACTORY->NewScopeInfo(length); + + // Encode the flags. + int flags = TypeField::encode(scope->type()) | + CallsEvalField::encode(scope->calls_eval()) | + LanguageModeField::encode(scope->language_mode()) | + FunctionVariableField::encode(function_name_info) | + FunctionVariableMode::encode(function_variable_mode); + scope_info->SetFlags(flags); + scope_info->SetParameterCount(parameter_count); + scope_info->SetStackLocalCount(stack_local_count); + scope_info->SetContextLocalCount(context_local_count); + + int index = kVariablePartIndex; + // Add parameters. + ASSERT(index == scope_info->ParameterEntriesIndex()); + for (int i = 0; i < parameter_count; ++i) { + scope_info->set(index++, *scope->parameter(i)->name()); } -} - -// Encoding format in a FixedArray object: -// -// - function name -// -// - calls eval boolean flag -// -// - number of variables in the context object (smi) (= function context -// slot index + 1) -// - list of pairs (name, Var mode) of context-allocated variables (starting -// with context slot 0) -// -// - number of parameters (smi) -// - list of parameter names (starting with parameter 0 first) -// -// - number of variables on the stack (smi) -// - list of names of stack-allocated variables (starting with stack slot 0) - -// The ScopeInfo representation could be simplified and the ScopeInfo -// re-implemented (with almost the same interface). Here is a -// suggestion for the new format: -// -// - have a single list with all variable names (parameters, stack locals, -// context locals), followed by a list of non-Object* values containing -// the variables information (what kind, index, attributes) -// - searching the linear list of names is fast and yields an index into the -// list if the variable name is found -// - that list index is then used to find the variable information in the -// subsequent list -// - the list entries don't have to be in any particular order, so all the -// current sorting business can go away -// - the ScopeInfo lookup routines can be reduced to perhaps a single lookup -// which returns all information at once -// - when gathering the information from a Scope, we only need to iterate -// through the local variables (parameters and context info is already -// present) + // Add stack locals' names. We are assuming that the stack locals' + // slots are allocated in increasing order, so we can simply add + // them to the ScopeInfo object. + ASSERT(index == scope_info->StackLocalEntriesIndex()); + for (int i = 0; i < stack_local_count; ++i) { + ASSERT(stack_locals[i]->index() == i); + scope_info->set(index++, *stack_locals[i]->name()); + } + // Due to usage analysis, context-allocated locals are not necessarily in + // increasing order: Some of them may be parameters which are allocated before + // the non-parameter locals. When the non-parameter locals are sorted + // according to usage, the allocated slot indices may not be in increasing + // order with the variable list anymore. Thus, we first need to sort them by + // context slot index before adding them to the ScopeInfo object. + context_locals.Sort(&Variable::CompareIndex); + + // Add context locals' names. + ASSERT(index == scope_info->ContextLocalNameEntriesIndex()); + for (int i = 0; i < context_local_count; ++i) { + scope_info->set(index++, *context_locals[i]->name()); + } -static inline Object** ReadInt(Object** p, int* x) { - *x = (reinterpret_cast<Smi*>(*p++))->value(); - return p; -} + // Add context locals' info. + ASSERT(index == scope_info->ContextLocalInfoEntriesIndex()); + for (int i = 0; i < context_local_count; ++i) { + Variable* var = context_locals[i]; + uint32_t value = ContextLocalMode::encode(var->mode()) | + ContextLocalInitFlag::encode(var->initialization_flag()); + scope_info->set(index++, Smi::FromInt(value)); + } + // If present, add the function variable name and its index. + ASSERT(index == scope_info->FunctionNameEntryIndex()); + if (has_function_name) { + int var_index = scope->function()->var()->index(); + scope_info->set(index++, *scope->function()->name()); + scope_info->set(index++, Smi::FromInt(var_index)); + ASSERT(function_name_info != STACK || + (var_index == scope_info->StackLocalCount() && + var_index == scope_info->StackSlotCount() - 1)); + ASSERT(function_name_info != CONTEXT || + var_index == scope_info->ContextLength() - 1); + } -static inline Object** ReadBool(Object** p, bool* x) { - *x = (reinterpret_cast<Smi*>(*p++))->value() != 0; - return p; + ASSERT(index == scope_info->length()); + ASSERT(scope->num_parameters() == scope_info->ParameterCount()); + ASSERT(scope->num_stack_slots() == scope_info->StackSlotCount()); + ASSERT(scope->num_heap_slots() == scope_info->ContextLength()); + return scope_info; } -static inline Object** ReadSymbol(Object** p, Handle<String>* s) { - *s = Handle<String>(reinterpret_cast<String*>(*p++)); - return p; +ScopeInfo* ScopeInfo::Empty() { + return reinterpret_cast<ScopeInfo*>(HEAP->empty_fixed_array()); } -template <class Allocator> -static Object** ReadList(Object** p, List<Handle<String>, Allocator >* list) { - ASSERT(list->is_empty()); - int n; - p = ReadInt(p, &n); - while (n-- > 0) { - Handle<String> s; - p = ReadSymbol(p, &s); - list->Add(s); - } - return p; -} - - -template <class Allocator> -static Object** ReadList(Object** p, - List<Handle<String>, Allocator>* list, - List<Variable::Mode, Allocator>* modes) { - ASSERT(list->is_empty()); - int n; - p = ReadInt(p, &n); - while (n-- > 0) { - Handle<String> s; - int m; - p = ReadSymbol(p, &s); - p = ReadInt(p, &m); - list->Add(s); - modes->Add(static_cast<Variable::Mode>(m)); - } - return p; -} - - -template<class Allocator> -ScopeInfo<Allocator>::ScopeInfo(SerializedScopeInfo* data) - : function_name_(FACTORY->empty_symbol()), - parameters_(4), - stack_slots_(8), - context_slots_(8), - context_modes_(8) { - if (data->length() > 0) { - Object** p0 = data->data_start(); - Object** p = p0; - p = ReadSymbol(p, &function_name_); - p = ReadBool(p, &calls_eval_); - p = ReadBool(p, &is_strict_mode_); - p = ReadList<Allocator>(p, &context_slots_, &context_modes_); - p = ReadList<Allocator>(p, ¶meters_); - p = ReadList<Allocator>(p, &stack_slots_); - ASSERT((p - p0) == FixedArray::cast(data)->length()); - } +ScopeType ScopeInfo::Type() { + ASSERT(length() > 0); + return TypeField::decode(Flags()); } -static inline Object** WriteInt(Object** p, int x) { - *p++ = Smi::FromInt(x); - return p; +bool ScopeInfo::CallsEval() { + return length() > 0 && CallsEvalField::decode(Flags()); } -static inline Object** WriteBool(Object** p, bool b) { - *p++ = Smi::FromInt(b ? 1 : 0); - return p; +LanguageMode ScopeInfo::language_mode() { + return length() > 0 ? LanguageModeField::decode(Flags()) : CLASSIC_MODE; } -static inline Object** WriteSymbol(Object** p, Handle<String> s) { - *p++ = *s; - return p; +int ScopeInfo::LocalCount() { + return StackLocalCount() + ContextLocalCount(); } -template <class Allocator> -static Object** WriteList(Object** p, List<Handle<String>, Allocator >* list) { - const int n = list->length(); - p = WriteInt(p, n); - for (int i = 0; i < n; i++) { - p = WriteSymbol(p, list->at(i)); +int ScopeInfo::StackSlotCount() { + if (length() > 0) { + bool function_name_stack_slot = + FunctionVariableField::decode(Flags()) == STACK; + return StackLocalCount() + (function_name_stack_slot ? 1 : 0); } - return p; + return 0; } -template <class Allocator> -static Object** WriteList(Object** p, - List<Handle<String>, Allocator>* list, - List<Variable::Mode, Allocator>* modes) { - const int n = list->length(); - p = WriteInt(p, n); - for (int i = 0; i < n; i++) { - p = WriteSymbol(p, list->at(i)); - p = WriteInt(p, modes->at(i)); +int ScopeInfo::ContextLength() { + if (length() > 0) { + int context_locals = ContextLocalCount(); + bool function_name_context_slot = + FunctionVariableField::decode(Flags()) == CONTEXT; + bool has_context = context_locals > 0 || + function_name_context_slot || + Type() == WITH_SCOPE || + (Type() == FUNCTION_SCOPE && CallsEval()); + if (has_context) { + return Context::MIN_CONTEXT_SLOTS + context_locals + + (function_name_context_slot ? 1 : 0); + } } - return p; -} - - -template<class Allocator> -Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() { - // function name, calls eval, is_strict_mode, length for 3 tables: - const int extra_slots = 1 + 1 + 1 + 3; - int length = extra_slots + - context_slots_.length() * 2 + - parameters_.length() + - stack_slots_.length(); - - Handle<SerializedScopeInfo> data( - SerializedScopeInfo::cast(*FACTORY->NewSerializedScopeInfo(length))); - AssertNoAllocation nogc; - - Object** p0 = data->data_start(); - Object** p = p0; - p = WriteSymbol(p, function_name_); - p = WriteBool(p, calls_eval_); - p = WriteBool(p, is_strict_mode_); - p = WriteList(p, &context_slots_, &context_modes_); - p = WriteList(p, ¶meters_); - p = WriteList(p, &stack_slots_); - ASSERT((p - p0) == length); - - return data; + return 0; } -template<class Allocator> -Handle<String> ScopeInfo<Allocator>::LocalName(int i) const { - // A local variable can be allocated either on the stack or in the context. - // For variables allocated in the context they are always preceded by - // Context::MIN_CONTEXT_SLOTS of fixed allocated slots in the context. - if (i < number_of_stack_slots()) { - return stack_slot_name(i); +bool ScopeInfo::HasFunctionName() { + if (length() > 0) { + return NONE != FunctionVariableField::decode(Flags()); } else { - return context_slot_name(i - number_of_stack_slots() + - Context::MIN_CONTEXT_SLOTS); + return false; } } -template<class Allocator> -int ScopeInfo<Allocator>::NumberOfLocals() const { - int number_of_locals = number_of_stack_slots(); - if (number_of_context_slots() > 0) { - ASSERT(number_of_context_slots() >= Context::MIN_CONTEXT_SLOTS); - number_of_locals += number_of_context_slots() - Context::MIN_CONTEXT_SLOTS; +bool ScopeInfo::HasHeapAllocatedLocals() { + if (length() > 0) { + return ContextLocalCount() > 0; + } else { + return false; } - return number_of_locals; } -Handle<SerializedScopeInfo> SerializedScopeInfo::Create(Scope* scope) { - ScopeInfo<ZoneListAllocationPolicy> sinfo(scope); - return sinfo.Serialize(); -} - - -SerializedScopeInfo* SerializedScopeInfo::Empty() { - return reinterpret_cast<SerializedScopeInfo*>(HEAP->empty_fixed_array()); -} - - -Object** SerializedScopeInfo::ContextEntriesAddr() { - ASSERT(length() > 0); - // +3 for function name, calls eval, strict mode. - return data_start() + 3; +bool ScopeInfo::HasContext() { + if (length() > 0) { + return ContextLength() > 0; + } else { + return false; + } } -Object** SerializedScopeInfo::ParameterEntriesAddr() { - ASSERT(length() > 0); - Object** p = ContextEntriesAddr(); - int number_of_context_slots; - p = ReadInt(p, &number_of_context_slots); - return p + number_of_context_slots*2; // *2 for pairs +String* ScopeInfo::FunctionName() { + ASSERT(HasFunctionName()); + return String::cast(get(FunctionNameEntryIndex())); } -Object** SerializedScopeInfo::StackSlotEntriesAddr() { - ASSERT(length() > 0); - Object** p = ParameterEntriesAddr(); - int number_of_parameter_slots; - p = ReadInt(p, &number_of_parameter_slots); - return p + number_of_parameter_slots; +String* ScopeInfo::ParameterName(int var) { + ASSERT(0 <= var && var < ParameterCount()); + int info_index = ParameterEntriesIndex() + var; + return String::cast(get(info_index)); } -bool SerializedScopeInfo::CallsEval() { - if (length() > 0) { - Object** p = data_start() + 1; // +1 for function name. - bool calls_eval; - p = ReadBool(p, &calls_eval); - return calls_eval; - } - return false; +String* ScopeInfo::LocalName(int var) { + ASSERT(0 <= var && var < LocalCount()); + ASSERT(StackLocalEntriesIndex() + StackLocalCount() == + ContextLocalNameEntriesIndex()); + int info_index = StackLocalEntriesIndex() + var; + return String::cast(get(info_index)); } -bool SerializedScopeInfo::IsStrictMode() { - if (length() > 0) { - Object** p = data_start() + 2; // +2 for function name, calls eval. - bool strict_mode; - p = ReadBool(p, &strict_mode); - return strict_mode; - } - return false; +String* ScopeInfo::StackLocalName(int var) { + ASSERT(0 <= var && var < StackLocalCount()); + int info_index = StackLocalEntriesIndex() + var; + return String::cast(get(info_index)); } -int SerializedScopeInfo::NumberOfStackSlots() { - if (length() > 0) { - Object** p = StackSlotEntriesAddr(); - int number_of_stack_slots; - ReadInt(p, &number_of_stack_slots); - return number_of_stack_slots; - } - return 0; +String* ScopeInfo::ContextLocalName(int var) { + ASSERT(0 <= var && var < ContextLocalCount()); + int info_index = ContextLocalNameEntriesIndex() + var; + return String::cast(get(info_index)); } -int SerializedScopeInfo::NumberOfContextSlots() { - if (length() > 0) { - Object** p = ContextEntriesAddr(); - int number_of_context_slots; - ReadInt(p, &number_of_context_slots); - return number_of_context_slots + Context::MIN_CONTEXT_SLOTS; - } - return 0; +VariableMode ScopeInfo::ContextLocalMode(int var) { + ASSERT(0 <= var && var < ContextLocalCount()); + int info_index = ContextLocalInfoEntriesIndex() + var; + int value = Smi::cast(get(info_index))->value(); + return ContextLocalMode::decode(value); } -bool SerializedScopeInfo::HasHeapAllocatedLocals() { - if (length() > 0) { - Object** p = ContextEntriesAddr(); - int number_of_context_slots; - ReadInt(p, &number_of_context_slots); - return number_of_context_slots > 0; - } - return false; +InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) { + ASSERT(0 <= var && var < ContextLocalCount()); + int info_index = ContextLocalInfoEntriesIndex() + var; + int value = Smi::cast(get(info_index))->value(); + return ContextLocalInitFlag::decode(value); } -int SerializedScopeInfo::StackSlotIndex(String* name) { +int ScopeInfo::StackSlotIndex(String* name) { ASSERT(name->IsSymbol()); if (length() > 0) { - // Slots start after length entry. - Object** p0 = StackSlotEntriesAddr(); - int number_of_stack_slots; - p0 = ReadInt(p0, &number_of_stack_slots); - Object** p = p0; - Object** end = p0 + number_of_stack_slots; - while (p != end) { - if (*p == name) return static_cast<int>(p - p0); - p++; + int start = StackLocalEntriesIndex(); + int end = StackLocalEntriesIndex() + StackLocalCount(); + for (int i = start; i < end; ++i) { + if (name == get(i)) { + return i - start; + } } } return -1; } -int SerializedScopeInfo::ContextSlotIndex(String* name, Variable::Mode* mode) { + +int ScopeInfo::ContextSlotIndex(String* name, + VariableMode* mode, + InitializationFlag* init_flag) { ASSERT(name->IsSymbol()); - Isolate* isolate = GetIsolate(); - int result = isolate->context_slot_cache()->Lookup(this, name, mode); - if (result != ContextSlotCache::kNotFound) return result; + ASSERT(mode != NULL); + ASSERT(init_flag != NULL); if (length() > 0) { - // Slots start after length entry. - Object** p0 = ContextEntriesAddr(); - int number_of_context_slots; - p0 = ReadInt(p0, &number_of_context_slots); - Object** p = p0; - Object** end = p0 + number_of_context_slots * 2; - while (p != end) { - if (*p == name) { - ASSERT(((p - p0) & 1) == 0); - int v; - ReadInt(p + 1, &v); - Variable::Mode mode_value = static_cast<Variable::Mode>(v); - if (mode != NULL) *mode = mode_value; - result = static_cast<int>((p - p0) >> 1) + Context::MIN_CONTEXT_SLOTS; - isolate->context_slot_cache()->Update(this, name, mode_value, result); + ContextSlotCache* context_slot_cache = GetIsolate()->context_slot_cache(); + int result = context_slot_cache->Lookup(this, name, mode, init_flag); + if (result != ContextSlotCache::kNotFound) { + ASSERT(result < ContextLength()); + return result; + } + + int start = ContextLocalNameEntriesIndex(); + int end = ContextLocalNameEntriesIndex() + ContextLocalCount(); + for (int i = start; i < end; ++i) { + if (name == get(i)) { + int var = i - start; + *mode = ContextLocalMode(var); + *init_flag = ContextLocalInitFlag(var); + result = Context::MIN_CONTEXT_SLOTS + var; + context_slot_cache->Update(this, name, *mode, *init_flag, result); + ASSERT(result < ContextLength()); return result; } - p += 2; } + context_slot_cache->Update(this, name, INTERNAL, kNeedsInitialization, -1); } - isolate->context_slot_cache()->Update(this, name, Variable::INTERNAL, -1); return -1; } -int SerializedScopeInfo::ParameterIndex(String* name) { +int ScopeInfo::ParameterIndex(String* name) { ASSERT(name->IsSymbol()); if (length() > 0) { // We must read parameters from the end since for @@ -495,41 +336,58 @@ int SerializedScopeInfo::ParameterIndex(String* name) { // last declaration of that parameter is used // inside a function (and thus we need to look // at the last index). Was bug# 1110337. - // - // Eventually, we should only register such parameters - // once, with corresponding index. This requires a new - // implementation of the ScopeInfo code. See also other - // comments in this file regarding this. - Object** p = ParameterEntriesAddr(); - int number_of_parameter_slots; - Object** p0 = ReadInt(p, &number_of_parameter_slots); - p = p0 + number_of_parameter_slots; - while (p > p0) { - p--; - if (*p == name) return static_cast<int>(p - p0); + int start = ParameterEntriesIndex(); + int end = ParameterEntriesIndex() + ParameterCount(); + for (int i = end - 1; i >= start; --i) { + if (name == get(i)) { + return i - start; + } } } return -1; } -int SerializedScopeInfo::FunctionContextSlotIndex(String* name) { +int ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) { ASSERT(name->IsSymbol()); + ASSERT(mode != NULL); if (length() > 0) { - Object** p = data_start(); - if (*p == name) { - p = ContextEntriesAddr(); - int number_of_context_slots; - ReadInt(p, &number_of_context_slots); - ASSERT(number_of_context_slots != 0); - // The function context slot is the last entry. - return number_of_context_slots + Context::MIN_CONTEXT_SLOTS - 1; + if (FunctionVariableField::decode(Flags()) == CONTEXT && + FunctionName() == name) { + *mode = FunctionVariableMode::decode(Flags()); + return Smi::cast(get(FunctionNameEntryIndex() + 1))->value(); } } return -1; } +int ScopeInfo::ParameterEntriesIndex() { + ASSERT(length() > 0); + return kVariablePartIndex; +} + + +int ScopeInfo::StackLocalEntriesIndex() { + return ParameterEntriesIndex() + ParameterCount(); +} + + +int ScopeInfo::ContextLocalNameEntriesIndex() { + return StackLocalEntriesIndex() + StackLocalCount(); +} + + +int ScopeInfo::ContextLocalInfoEntriesIndex() { + return ContextLocalNameEntriesIndex() + ContextLocalCount(); +} + + +int ScopeInfo::FunctionNameEntryIndex() { + return ContextLocalInfoEntriesIndex() + ContextLocalCount(); +} + + int ContextSlotCache::Hash(Object* data, String* name) { // Uses only lower 32 bits if pointers are larger. uintptr_t addr_hash = @@ -540,12 +398,14 @@ int ContextSlotCache::Hash(Object* data, String* name) { int ContextSlotCache::Lookup(Object* data, String* name, - Variable::Mode* mode) { + VariableMode* mode, + InitializationFlag* init_flag) { int index = Hash(data, name); Key& key = keys_[index]; if ((key.data == data) && key.name->Equals(name)) { Value result(values_[index]); if (mode != NULL) *mode = result.mode(); + if (init_flag != NULL) *init_flag = result.initialization_flag(); return result.index() + kNotFound; } return kNotFound; @@ -554,7 +414,8 @@ int ContextSlotCache::Lookup(Object* data, void ContextSlotCache::Update(Object* data, String* name, - Variable::Mode mode, + VariableMode mode, + InitializationFlag init_flag, int slot_index) { String* symbol; ASSERT(slot_index > kNotFound); @@ -564,9 +425,9 @@ void ContextSlotCache::Update(Object* data, key.data = data; key.name = symbol; // Please note value only takes a uint as index. - values_[index] = Value(mode, slot_index - kNotFound).raw(); + values_[index] = Value(mode, init_flag, slot_index - kNotFound).raw(); #ifdef DEBUG - ValidateEntry(data, name, mode, slot_index); + ValidateEntry(data, name, mode, init_flag, slot_index); #endif } } @@ -581,7 +442,8 @@ void ContextSlotCache::Clear() { void ContextSlotCache::ValidateEntry(Object* data, String* name, - Variable::Mode mode, + VariableMode mode, + InitializationFlag init_flag, int slot_index) { String* symbol; if (HEAP->LookupSymbolIfExists(name, &symbol)) { @@ -591,51 +453,56 @@ void ContextSlotCache::ValidateEntry(Object* data, ASSERT(key.name->Equals(name)); Value result(values_[index]); ASSERT(result.mode() == mode); + ASSERT(result.initialization_flag() == init_flag); ASSERT(result.index() + kNotFound == slot_index); } } -template <class Allocator> static void PrintList(const char* list_name, int nof_internal_slots, - List<Handle<String>, Allocator>& list) { - if (list.length() > 0) { + int start, + int end, + ScopeInfo* scope_info) { + if (start < end) { PrintF("\n // %s\n", list_name); if (nof_internal_slots > 0) { PrintF(" %2d - %2d [internal slots]\n", 0 , nof_internal_slots - 1); } - for (int i = 0; i < list.length(); i++) { - PrintF(" %2d ", i + nof_internal_slots); - list[i]->ShortPrint(); + for (int i = nof_internal_slots; start < end; ++i, ++start) { + PrintF(" %2d ", i); + String::cast(scope_info->get(start))->ShortPrint(); PrintF("\n"); } } } -template<class Allocator> -void ScopeInfo<Allocator>::Print() { +void ScopeInfo::Print() { PrintF("ScopeInfo "); - if (function_name_->length() > 0) - function_name_->ShortPrint(); - else + if (HasFunctionName()) { + FunctionName()->ShortPrint(); + } else { PrintF("/* no function name */"); + } PrintF("{"); - PrintList<Allocator>("parameters", 0, parameters_); - PrintList<Allocator>("stack slots", 0, stack_slots_); - PrintList<Allocator>("context slots", Context::MIN_CONTEXT_SLOTS, - context_slots_); + PrintList("parameters", 0, + ParameterEntriesIndex(), + ParameterEntriesIndex() + ParameterCount(), + this); + PrintList("stack slots", 0, + StackLocalEntriesIndex(), + StackLocalEntriesIndex() + StackLocalCount(), + this); + PrintList("context slots", + Context::MIN_CONTEXT_SLOTS, + ContextLocalNameEntriesIndex(), + ContextLocalNameEntriesIndex() + ContextLocalCount(), + this); PrintF("}\n"); } #endif // DEBUG - -// Make sure the classes get instantiated by the template system. -template class ScopeInfo<FreeStoreAllocationPolicy>; -template class ScopeInfo<PreallocatedStorage>; -template class ScopeInfo<ZoneListAllocationPolicy>; - } } // namespace v8::internal diff --git a/deps/v8/src/scopeinfo.h b/deps/v8/src/scopeinfo.h index 40c5c8a687..93734f5a16 100644 --- a/deps/v8/src/scopeinfo.h +++ b/deps/v8/src/scopeinfo.h @@ -35,135 +35,6 @@ namespace v8 { namespace internal { -// Scope information represents information about a functions's -// scopes (currently only one, because we don't do any inlining) -// and the allocation of the scope's variables. Scope information -// is stored in a compressed form in FixedArray objects and is used -// at runtime (stack dumps, deoptimization, etc.). -// -// Historical note: In other VMs built by this team, ScopeInfo was -// usually called DebugInfo since the information was used (among -// other things) for on-demand debugging (Self, Smalltalk). However, -// DebugInfo seems misleading, since this information is primarily used -// in debugging-unrelated contexts. - -// Forward defined as -// template <class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; -template<class Allocator> -class ScopeInfo BASE_EMBEDDED { - public: - // Create a ScopeInfo instance from a scope. - explicit ScopeInfo(Scope* scope); - - // Create a ScopeInfo instance from SerializedScopeInfo. - explicit ScopeInfo(SerializedScopeInfo* data); - - // Creates a SerializedScopeInfo holding the serialized scope info. - Handle<SerializedScopeInfo> Serialize(); - - // -------------------------------------------------------------------------- - // Lookup - - Handle<String> function_name() const { return function_name_; } - - Handle<String> parameter_name(int i) const { return parameters_[i]; } - int number_of_parameters() const { return parameters_.length(); } - - Handle<String> stack_slot_name(int i) const { return stack_slots_[i]; } - int number_of_stack_slots() const { return stack_slots_.length(); } - - Handle<String> context_slot_name(int i) const { - return context_slots_[i - Context::MIN_CONTEXT_SLOTS]; - } - int number_of_context_slots() const { - int l = context_slots_.length(); - return l == 0 ? 0 : l + Context::MIN_CONTEXT_SLOTS; - } - - Handle<String> LocalName(int i) const; - int NumberOfLocals() const; - - // -------------------------------------------------------------------------- - // Debugging support - -#ifdef DEBUG - void Print(); -#endif - - private: - Handle<String> function_name_; - bool calls_eval_; - bool is_strict_mode_; - List<Handle<String>, Allocator > parameters_; - List<Handle<String>, Allocator > stack_slots_; - List<Handle<String>, Allocator > context_slots_; - List<Variable::Mode, Allocator > context_modes_; -}; - - -// This object provides quick access to scope info details for runtime -// routines w/o the need to explicitly create a ScopeInfo object. -class SerializedScopeInfo : public FixedArray { - public : - - static SerializedScopeInfo* cast(Object* object) { - ASSERT(object->IsSerializedScopeInfo()); - return reinterpret_cast<SerializedScopeInfo*>(object); - } - - // Does this scope call eval? - bool CallsEval(); - - // Is this scope a strict mode scope? - bool IsStrictMode(); - - // Return the number of stack slots for code. - int NumberOfStackSlots(); - - // Return the number of context slots for code. - int NumberOfContextSlots(); - - // Return if this has context slots besides MIN_CONTEXT_SLOTS; - bool HasHeapAllocatedLocals(); - - // Lookup support for serialized scope info. Returns the - // the stack slot index for a given slot name if the slot is - // present; otherwise returns a value < 0. The name must be a symbol - // (canonicalized). - int StackSlotIndex(String* name); - - // Lookup support for serialized scope info. Returns the - // context slot index for a given slot name if the slot is present; otherwise - // returns a value < 0. The name must be a symbol (canonicalized). - // If the slot is present and mode != NULL, sets *mode to the corresponding - // mode for that variable. - int ContextSlotIndex(String* name, Variable::Mode* mode); - - // Lookup support for serialized scope info. Returns the - // parameter index for a given parameter name if the parameter is present; - // otherwise returns a value < 0. The name must be a symbol (canonicalized). - int ParameterIndex(String* name); - - // Lookup support for serialized scope info. Returns the - // function context slot index if the function name is present (named - // function expressions, only), otherwise returns a value < 0. The name - // must be a symbol (canonicalized). - int FunctionContextSlotIndex(String* name); - - static Handle<SerializedScopeInfo> Create(Scope* scope); - - // Serializes empty scope info. - static SerializedScopeInfo* Empty(); - - private: - inline Object** ContextEntriesAddr(); - - inline Object** ParameterEntriesAddr(); - - inline Object** StackSlotEntriesAddr(); -}; - - // Cache for mapping (data, property name) into context slot index. // The cache contains both positive and negative results. // Slot index equals -1 means the property is absent. @@ -174,12 +45,14 @@ class ContextSlotCache { // If absent, kNotFound is returned. int Lookup(Object* data, String* name, - Variable::Mode* mode); + VariableMode* mode, + InitializationFlag* init_flag); // Update an element in the cache. void Update(Object* data, String* name, - Variable::Mode mode, + VariableMode mode, + InitializationFlag init_flag, int slot_index); // Clear the cache. @@ -201,7 +74,8 @@ class ContextSlotCache { #ifdef DEBUG void ValidateEntry(Object* data, String* name, - Variable::Mode mode, + VariableMode mode, + InitializationFlag init_flag, int slot_index); #endif @@ -212,11 +86,17 @@ class ContextSlotCache { }; struct Value { - Value(Variable::Mode mode, int index) { + Value(VariableMode mode, + InitializationFlag init_flag, + int index) { ASSERT(ModeField::is_valid(mode)); + ASSERT(InitField::is_valid(init_flag)); ASSERT(IndexField::is_valid(index)); - value_ = ModeField::encode(mode) | IndexField::encode(index); + value_ = ModeField::encode(mode) | + IndexField::encode(index) | + InitField::encode(init_flag); ASSERT(mode == this->mode()); + ASSERT(init_flag == this->initialization_flag()); ASSERT(index == this->index()); } @@ -224,14 +104,20 @@ class ContextSlotCache { uint32_t raw() { return value_; } - Variable::Mode mode() { return ModeField::decode(value_); } + VariableMode mode() { return ModeField::decode(value_); } + + InitializationFlag initialization_flag() { + return InitField::decode(value_); + } int index() { return IndexField::decode(value_); } // Bit fields in value_ (type, shift, size). Must be public so the // constants can be embedded in generated code. - class ModeField: public BitField<Variable::Mode, 0, 3> {}; - class IndexField: public BitField<int, 3, 32-3> {}; + class ModeField: public BitField<VariableMode, 0, 3> {}; + class InitField: public BitField<InitializationFlag, 3, 1> {}; + class IndexField: public BitField<int, 4, 32-4> {}; + private: uint32_t value_; }; diff --git a/deps/v8/src/scopes.cc b/deps/v8/src/scopes.cc index d5a7a9f9ca..35d804dff6 100644 --- a/deps/v8/src/scopes.cc +++ b/deps/v8/src/scopes.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -31,6 +31,7 @@ #include "bootstrapper.h" #include "compiler.h" +#include "messages.h" #include "scopeinfo.h" #include "allocation-inl.h" @@ -55,7 +56,7 @@ class ZoneAllocator: public Allocator { }; -static ZoneAllocator LocalsMapAllocator; +static ZoneAllocator* LocalsMapAllocator = ::new ZoneAllocator(); // ---------------------------------------------------------------------------- @@ -76,23 +77,27 @@ static bool Match(void* key1, void* key2) { } -// Dummy constructor -VariableMap::VariableMap(bool gotta_love_static_overloading) : HashMap() {} - -VariableMap::VariableMap() : HashMap(Match, &LocalsMapAllocator, 8) {} +VariableMap::VariableMap() : HashMap(Match, LocalsMapAllocator, 8) {} VariableMap::~VariableMap() {} -Variable* VariableMap::Declare(Scope* scope, - Handle<String> name, - Variable::Mode mode, - bool is_valid_lhs, - Variable::Kind kind) { +Variable* VariableMap::Declare( + Scope* scope, + Handle<String> name, + VariableMode mode, + bool is_valid_lhs, + Variable::Kind kind, + InitializationFlag initialization_flag) { HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true); if (p->value == NULL) { // The variable has not been declared yet -> insert it. ASSERT(p->key == name.location()); - p->value = new Variable(scope, name, mode, is_valid_lhs, kind); + p->value = new Variable(scope, + name, + mode, + is_valid_lhs, + kind, + initialization_flag); } return reinterpret_cast<Variable*>(p->value); } @@ -112,22 +117,7 @@ Variable* VariableMap::Lookup(Handle<String> name) { // ---------------------------------------------------------------------------- // Implementation of Scope - -// Dummy constructor -Scope::Scope(Type type) - : isolate_(Isolate::Current()), - inner_scopes_(0), - variables_(false), - temps_(0), - params_(0), - unresolved_(0), - decls_(0), - already_resolved_(false) { - SetDefaults(type, NULL, Handle<SerializedScopeInfo>::null()); -} - - -Scope::Scope(Scope* outer_scope, Type type) +Scope::Scope(Scope* outer_scope, ScopeType type) : isolate_(Isolate::Current()), inner_scopes_(4), variables_(), @@ -136,18 +126,18 @@ Scope::Scope(Scope* outer_scope, Type type) unresolved_(16), decls_(4), already_resolved_(false) { - SetDefaults(type, outer_scope, Handle<SerializedScopeInfo>::null()); + SetDefaults(type, outer_scope, Handle<ScopeInfo>::null()); // At some point we might want to provide outer scopes to // eval scopes (by walking the stack and reading the scope info). // In that case, the ASSERT below needs to be adjusted. - ASSERT((type == GLOBAL_SCOPE || type == EVAL_SCOPE) == (outer_scope == NULL)); + ASSERT_EQ(type == GLOBAL_SCOPE, outer_scope == NULL); ASSERT(!HasIllegalRedeclaration()); } Scope::Scope(Scope* inner_scope, - Type type, - Handle<SerializedScopeInfo> scope_info) + ScopeType type, + Handle<ScopeInfo> scope_info) : isolate_(Isolate::Current()), inner_scopes_(4), variables_(), @@ -156,11 +146,13 @@ Scope::Scope(Scope* inner_scope, unresolved_(16), decls_(4), already_resolved_(true) { - ASSERT(!scope_info.is_null()); SetDefaults(type, NULL, scope_info); - if (scope_info->HasHeapAllocatedLocals()) { - num_heap_slots_ = scope_info_->NumberOfContextSlots(); + if (!scope_info.is_null()) { + num_heap_slots_ = scope_info_->ContextLength(); } + // Ensure at least MIN_CONTEXT_SLOTS to indicate a materialized context. + num_heap_slots_ = Max(num_heap_slots_, + static_cast<int>(Context::MIN_CONTEXT_SLOTS)); AddInnerScope(inner_scope); } @@ -174,21 +166,23 @@ Scope::Scope(Scope* inner_scope, Handle<String> catch_variable_name) unresolved_(0), decls_(0), already_resolved_(true) { - SetDefaults(CATCH_SCOPE, NULL, Handle<SerializedScopeInfo>::null()); + SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null()); AddInnerScope(inner_scope); ++num_var_or_const_; + num_heap_slots_ = Context::MIN_CONTEXT_SLOTS; Variable* variable = variables_.Declare(this, catch_variable_name, - Variable::VAR, + VAR, true, // Valid left-hand side. - Variable::NORMAL); + Variable::NORMAL, + kCreatedInitialized); AllocateHeapSlot(variable); } -void Scope::SetDefaults(Type type, +void Scope::SetDefaults(ScopeType type, Scope* outer_scope, - Handle<SerializedScopeInfo> scope_info) { + Handle<ScopeInfo> scope_info) { outer_scope_ = outer_scope; type_ = type; scope_name_ = isolate_->factory()->empty_symbol(); @@ -201,53 +195,57 @@ void Scope::SetDefaults(Type type, scope_contains_with_ = false; scope_calls_eval_ = false; // Inherit the strict mode from the parent scope. - strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_; - outer_scope_calls_eval_ = false; + language_mode_ = (outer_scope != NULL) + ? outer_scope->language_mode_ : CLASSIC_MODE; outer_scope_calls_non_strict_eval_ = false; inner_scope_calls_eval_ = false; - outer_scope_is_eval_scope_ = false; force_eager_compilation_ = false; num_var_or_const_ = 0; num_stack_slots_ = 0; num_heap_slots_ = 0; scope_info_ = scope_info; + start_position_ = RelocInfo::kNoPosition; + end_position_ = RelocInfo::kNoPosition; + if (!scope_info.is_null()) { + scope_calls_eval_ = scope_info->CallsEval(); + language_mode_ = scope_info->language_mode(); + } } -Scope* Scope::DeserializeScopeChain(CompilationInfo* info, - Scope* global_scope) { +Scope* Scope::DeserializeScopeChain(Context* context, Scope* global_scope) { // Reconstruct the outer scope chain from a closure's context chain. - ASSERT(!info->closure().is_null()); - Context* context = info->closure()->context(); Scope* current_scope = NULL; Scope* innermost_scope = NULL; bool contains_with = false; while (!context->IsGlobalContext()) { if (context->IsWithContext()) { + Scope* with_scope = new Scope(current_scope, + WITH_SCOPE, + Handle<ScopeInfo>::null()); + current_scope = with_scope; // All the inner scopes are inside a with. contains_with = true; for (Scope* s = innermost_scope; s != NULL; s = s->outer_scope()) { s->scope_inside_with_ = true; } + } else if (context->IsFunctionContext()) { + ScopeInfo* scope_info = context->closure()->shared()->scope_info(); + current_scope = new Scope(current_scope, + FUNCTION_SCOPE, + Handle<ScopeInfo>(scope_info)); + } else if (context->IsBlockContext()) { + ScopeInfo* scope_info = ScopeInfo::cast(context->extension()); + current_scope = new Scope(current_scope, + BLOCK_SCOPE, + Handle<ScopeInfo>(scope_info)); } else { - if (context->IsFunctionContext()) { - SerializedScopeInfo* scope_info = - context->closure()->shared()->scope_info(); - current_scope = new Scope(current_scope, FUNCTION_SCOPE, - Handle<SerializedScopeInfo>(scope_info)); - } else if (context->IsBlockContext()) { - SerializedScopeInfo* scope_info = - SerializedScopeInfo::cast(context->extension()); - current_scope = new Scope(current_scope, BLOCK_SCOPE, - Handle<SerializedScopeInfo>(scope_info)); - } else { - ASSERT(context->IsCatchContext()); - String* name = String::cast(context->extension()); - current_scope = new Scope(current_scope, Handle<String>(name)); - } - if (contains_with) current_scope->RecordWithStatement(); - if (innermost_scope == NULL) innermost_scope = current_scope; + ASSERT(context->IsCatchContext()); + String* name = String::cast(context->extension()); + current_scope = new Scope(current_scope, Handle<String>(name)); } + if (contains_with) current_scope->RecordWithStatement(); + if (innermost_scope == NULL) innermost_scope = current_scope; // Forget about a with when we move to a context for a different function. if (context->previous()->closure() != context->closure()) { @@ -257,39 +255,68 @@ Scope* Scope::DeserializeScopeChain(CompilationInfo* info, } global_scope->AddInnerScope(current_scope); + global_scope->PropagateScopeInfo(false); return (innermost_scope == NULL) ? global_scope : innermost_scope; } bool Scope::Analyze(CompilationInfo* info) { ASSERT(info->function() != NULL); - Scope* top = info->function()->scope(); + Scope* scope = info->function()->scope(); + Scope* top = scope; + + // Traverse the scope tree up to the first unresolved scope or the global + // scope and start scope resolution and variable allocation from that scope. + while (!top->is_global_scope() && + !top->outer_scope()->already_resolved()) { + top = top->outer_scope(); + } - while (top->outer_scope() != NULL) top = top->outer_scope(); - top->AllocateVariables(info->calling_context()); + // Allocate the variables. + { + AstNodeFactory<AstNullVisitor> ast_node_factory(info->isolate()); + top->AllocateVariables(info->global_scope(), &ast_node_factory); + } #ifdef DEBUG if (info->isolate()->bootstrapper()->IsActive() ? FLAG_print_builtin_scopes : FLAG_print_scopes) { - info->function()->scope()->Print(); + scope->Print(); } #endif - info->SetScope(info->function()->scope()); - return true; // Can not fail. + if (FLAG_harmony_scoping) { + VariableProxy* proxy = scope->CheckAssignmentToConst(); + if (proxy != NULL) { + // Found an assignment to const. Throw a syntax error. + MessageLocation location(info->script(), + proxy->position(), + proxy->position()); + Isolate* isolate = info->isolate(); + Factory* factory = isolate->factory(); + Handle<JSArray> array = factory->NewJSArray(0); + Handle<Object> result = + factory->NewSyntaxError("harmony_const_assign", array); + isolate->Throw(*result, &location); + return false; + } + } + + info->SetScope(scope); + return true; } -void Scope::Initialize(bool inside_with) { +void Scope::Initialize() { ASSERT(!already_resolved()); // Add this scope as a new inner scope of the outer scope. if (outer_scope_ != NULL) { outer_scope_->inner_scopes_.Add(this); - scope_inside_with_ = outer_scope_->scope_inside_with_ || inside_with; + scope_inside_with_ = outer_scope_->scope_inside_with_ || is_with_scope(); } else { - scope_inside_with_ = inside_with; + scope_inside_with_ = is_with_scope(); } // Declare convenience variables. @@ -300,21 +327,19 @@ void Scope::Initialize(bool inside_with) { // instead load them directly from the stack. Currently, the only // such parameter is 'this' which is passed on the stack when // invoking scripts - if (is_catch_scope() || is_block_scope()) { - ASSERT(outer_scope() != NULL); - receiver_ = outer_scope()->receiver(); - } else { - ASSERT(is_function_scope() || - is_global_scope() || - is_eval_scope()); + if (is_declaration_scope()) { Variable* var = variables_.Declare(this, isolate_->factory()->this_symbol(), - Variable::VAR, + VAR, false, - Variable::THIS); + Variable::THIS, + kCreatedInitialized); var->AllocateTo(Variable::PARAMETER, -1); receiver_ = var; + } else { + ASSERT(outer_scope() != NULL); + receiver_ = outer_scope()->receiver(); } if (is_function_scope()) { @@ -323,9 +348,10 @@ void Scope::Initialize(bool inside_with) { // allocated during variable allocation. variables_.Declare(this, isolate_->factory()->arguments_symbol(), - Variable::VAR, + VAR, true, - Variable::ARGUMENTS); + Variable::ARGUMENTS, + kCreatedInitialized); } } @@ -365,34 +391,51 @@ Variable* Scope::LocalLookup(Handle<String> name) { return result; } // If we have a serialized scope info, we might find the variable there. - // - // We should never lookup 'arguments' in this scope as it is implicitly - // present in every scope. - ASSERT(*name != *isolate_->factory()->arguments_symbol()); // There should be no local slot with the given name. ASSERT(scope_info_->StackSlotIndex(*name) < 0); // Check context slot lookup. - Variable::Mode mode; - int index = scope_info_->ContextSlotIndex(*name, &mode); + VariableMode mode; + InitializationFlag init_flag; + int index = scope_info_->ContextSlotIndex(*name, &mode, &init_flag); if (index < 0) { // Check parameters. - mode = Variable::VAR; + mode = VAR; + init_flag = kCreatedInitialized; index = scope_info_->ParameterIndex(*name); - if (index < 0) { - // Check the function name. - index = scope_info_->FunctionContextSlotIndex(*name); - if (index < 0) return NULL; - } + if (index < 0) return NULL; } Variable* var = - variables_.Declare(this, name, mode, true, Variable::NORMAL); + variables_.Declare(this, + name, + mode, + true, + Variable::NORMAL, + init_flag); var->AllocateTo(Variable::CONTEXT, index); return var; } +Variable* Scope::LookupFunctionVar(Handle<String> name, + AstNodeFactory<AstNullVisitor>* factory) { + if (function_ != NULL && function_->name().is_identical_to(name)) { + return function_->var(); + } else if (!scope_info_.is_null()) { + // If we are backed by a scope info, try to lookup the variable there. + VariableMode mode; + int index = scope_info_->FunctionContextSlotIndex(*name, &mode); + if (index < 0) return NULL; + Variable* var = DeclareFunctionVar(name, mode, factory); + var->AllocateTo(Variable::CONTEXT, index); + return var; + } else { + return NULL; + } +} + + Variable* Scope::Lookup(Handle<String> name) { for (Scope* scope = this; scope != NULL; @@ -404,56 +447,40 @@ Variable* Scope::Lookup(Handle<String> name) { } -Variable* Scope::DeclareFunctionVar(Handle<String> name) { - ASSERT(is_function_scope() && function_ == NULL); - Variable* function_var = - new Variable(this, name, Variable::CONST, true, Variable::NORMAL); - function_ = new(isolate_->zone()) VariableProxy(isolate_, function_var); - return function_var; -} - - -void Scope::DeclareParameter(Handle<String> name, Variable::Mode mode) { +void Scope::DeclareParameter(Handle<String> name, VariableMode mode) { ASSERT(!already_resolved()); ASSERT(is_function_scope()); - Variable* var = - variables_.Declare(this, name, mode, true, Variable::NORMAL); + Variable* var = variables_.Declare( + this, name, mode, true, Variable::NORMAL, kCreatedInitialized); params_.Add(var); } -Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) { +Variable* Scope::DeclareLocal(Handle<String> name, + VariableMode mode, + InitializationFlag init_flag) { ASSERT(!already_resolved()); // This function handles VAR and CONST modes. DYNAMIC variables are // introduces during variable allocation, INTERNAL variables are allocated // explicitly, and TEMPORARY variables are allocated via NewTemporary(). - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); ++num_var_or_const_; - return variables_.Declare(this, name, mode, true, Variable::NORMAL); + return + variables_.Declare(this, name, mode, true, Variable::NORMAL, init_flag); } Variable* Scope::DeclareGlobal(Handle<String> name) { ASSERT(is_global_scope()); - return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, + return variables_.Declare(this, + name, + DYNAMIC_GLOBAL, true, - Variable::NORMAL); -} - - -VariableProxy* Scope::NewUnresolved(Handle<String> name, - bool inside_with, - int position) { - // Note that we must not share the unresolved variables with - // the same name because they may be removed selectively via - // RemoveUnresolved(). - ASSERT(!already_resolved()); - VariableProxy* proxy = new(isolate_->zone()) VariableProxy( - isolate_, name, false, inside_with, position); - unresolved_.Add(proxy); - return proxy; + Variable::NORMAL, + kCreatedInitialized); } @@ -473,9 +500,10 @@ Variable* Scope::NewTemporary(Handle<String> name) { ASSERT(!already_resolved()); Variable* var = new Variable(this, name, - Variable::TEMPORARY, + TEMPORARY, true, - Variable::NORMAL); + Variable::NORMAL, + kCreatedInitialized); temps_.Add(var); return var; } @@ -505,81 +533,92 @@ Declaration* Scope::CheckConflictingVarDeclarations() { int length = decls_.length(); for (int i = 0; i < length; i++) { Declaration* decl = decls_[i]; - if (decl->mode() != Variable::VAR) continue; + if (decl->mode() != VAR) continue; Handle<String> name = decl->proxy()->name(); - bool cond = true; - for (Scope* scope = decl->scope(); cond ; scope = scope->outer_scope_) { + + // Iterate through all scopes until and including the declaration scope. + Scope* previous = NULL; + Scope* current = decl->scope(); + do { // There is a conflict if there exists a non-VAR binding. - Variable* other_var = scope->variables_.Lookup(name); - if (other_var != NULL && other_var->mode() != Variable::VAR) { + Variable* other_var = current->variables_.Lookup(name); + if (other_var != NULL && other_var->mode() != VAR) { return decl; } + previous = current; + current = current->outer_scope_; + } while (!previous->is_declaration_scope()); + } + return NULL; +} - // Include declaration scope in the iteration but stop after. - if (!scope->is_block_scope() && !scope->is_catch_scope()) cond = false; + +VariableProxy* Scope::CheckAssignmentToConst() { + // Check this scope. + if (is_extended_mode()) { + for (int i = 0; i < unresolved_.length(); i++) { + ASSERT(unresolved_[i]->var() != NULL); + if (unresolved_[i]->var()->is_const_mode() && + unresolved_[i]->IsLValue()) { + return unresolved_[i]; + } } } + + // Check inner scopes. + for (int i = 0; i < inner_scopes_.length(); i++) { + VariableProxy* proxy = inner_scopes_[i]->CheckAssignmentToConst(); + if (proxy != NULL) return proxy; + } + + // No assignments to const found. return NULL; } -template<class Allocator> -void Scope::CollectUsedVariables(List<Variable*, Allocator>* locals) { - // Collect variables in this scope. - // Note that the function_ variable - if present - is not - // collected here but handled separately in ScopeInfo - // which is the current user of this function). +void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals, + ZoneList<Variable*>* context_locals) { + ASSERT(stack_locals != NULL); + ASSERT(context_locals != NULL); + + // Collect temporaries which are always allocated on the stack. for (int i = 0; i < temps_.length(); i++) { Variable* var = temps_[i]; if (var->is_used()) { - locals->Add(var); + ASSERT(var->IsStackLocal()); + stack_locals->Add(var); } } + + // Collect declared local variables. for (VariableMap::Entry* p = variables_.Start(); p != NULL; p = variables_.Next(p)) { Variable* var = reinterpret_cast<Variable*>(p->value); if (var->is_used()) { - locals->Add(var); + if (var->IsStackLocal()) { + stack_locals->Add(var); + } else if (var->IsContextSlot()) { + context_locals->Add(var); + } } } } -// Make sure the method gets instantiated by the template system. -template void Scope::CollectUsedVariables( - List<Variable*, FreeStoreAllocationPolicy>* locals); -template void Scope::CollectUsedVariables( - List<Variable*, PreallocatedStorage>* locals); -template void Scope::CollectUsedVariables( - List<Variable*, ZoneListAllocationPolicy>* locals); - - -void Scope::AllocateVariables(Handle<Context> context) { - ASSERT(outer_scope_ == NULL); // eval or global scopes only - +void Scope::AllocateVariables(Scope* global_scope, + AstNodeFactory<AstNullVisitor>* factory) { // 1) Propagate scope information. - // If we are in an eval scope, we may have other outer scopes about - // which we don't know anything at this point. Thus we must be conservative - // and assume they may invoke eval themselves. Eventually we could capture - // this information in the ScopeInfo and then use it here (by traversing - // the call chain stack, at compile time). - - bool eval_scope = is_eval_scope(); - bool outer_scope_calls_eval = false; bool outer_scope_calls_non_strict_eval = false; - if (!is_global_scope()) { - context->ComputeEvalScopeInfo(&outer_scope_calls_eval, - &outer_scope_calls_non_strict_eval); + if (outer_scope_ != NULL) { + outer_scope_calls_non_strict_eval = + outer_scope_->outer_scope_calls_non_strict_eval() | + outer_scope_->calls_non_strict_eval(); } - PropagateScopeInfo(outer_scope_calls_eval, - outer_scope_calls_non_strict_eval, - eval_scope); + PropagateScopeInfo(outer_scope_calls_non_strict_eval); // 2) Resolve variables. - Scope* global_scope = NULL; - if (is_global_scope()) global_scope = this; - ResolveVariablesRecursively(global_scope, context); + ResolveVariablesRecursively(global_scope, factory); // 3) Allocate variables. AllocateVariablesRecursively(); @@ -627,30 +666,48 @@ int Scope::ContextChainLength(Scope* scope) { Scope* Scope::DeclarationScope() { Scope* scope = this; - while (scope->is_catch_scope() || - scope->is_block_scope()) { + while (!scope->is_declaration_scope()) { scope = scope->outer_scope(); } return scope; } -Handle<SerializedScopeInfo> Scope::GetSerializedScopeInfo() { +Handle<ScopeInfo> Scope::GetScopeInfo() { if (scope_info_.is_null()) { - scope_info_ = SerializedScopeInfo::Create(this); + scope_info_ = ScopeInfo::Create(this); } return scope_info_; } +void Scope::GetNestedScopeChain( + List<Handle<ScopeInfo> >* chain, + int position) { + if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo())); + + for (int i = 0; i < inner_scopes_.length(); i++) { + Scope* scope = inner_scopes_[i]; + int beg_pos = scope->start_position(); + int end_pos = scope->end_position(); + ASSERT(beg_pos >= 0 && end_pos >= 0); + if (beg_pos <= position && position < end_pos) { + scope->GetNestedScopeChain(chain, position); + return; + } + } +} + + #ifdef DEBUG -static const char* Header(Scope::Type type) { +static const char* Header(ScopeType type) { switch (type) { - case Scope::EVAL_SCOPE: return "eval"; - case Scope::FUNCTION_SCOPE: return "function"; - case Scope::GLOBAL_SCOPE: return "global"; - case Scope::CATCH_SCOPE: return "catch"; - case Scope::BLOCK_SCOPE: return "block"; + case EVAL_SCOPE: return "eval"; + case FUNCTION_SCOPE: return "function"; + case GLOBAL_SCOPE: return "global"; + case CATCH_SCOPE: return "catch"; + case BLOCK_SCOPE: return "block"; + case WITH_SCOPE: return "with"; } UNREACHABLE(); return NULL; @@ -695,9 +752,9 @@ static void PrintVar(int indent, Variable* var) { PrintName(var->name()); PrintF("; // "); PrintLocation(var); - if (var->is_accessed_from_inner_scope()) { + if (var->has_forced_context_allocation()) { if (!var->IsUnallocated()) PrintF(", "); - PrintF("inner scope access"); + PrintF("forced context allocation"); } PrintF("\n"); } @@ -733,7 +790,7 @@ void Scope::Print(int n) { PrintF(")"); } - PrintF(" {\n"); + PrintF(" { // (%d, %d)\n", start_position(), end_position()); // Function name, if any (named function literals, only). if (function_ != NULL) { @@ -746,18 +803,23 @@ void Scope::Print(int n) { if (HasTrivialOuterContext()) { Indent(n1, "// scope has trivial outer context\n"); } - if (is_strict_mode()) Indent(n1, "// strict mode scope\n"); + switch (language_mode()) { + case CLASSIC_MODE: + break; + case STRICT_MODE: + Indent(n1, "// strict mode scope\n"); + break; + case EXTENDED_MODE: + Indent(n1, "// extended mode scope\n"); + break; + } if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n"); if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n"); if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n"); - if (outer_scope_calls_eval_) Indent(n1, "// outer scope calls 'eval'\n"); if (outer_scope_calls_non_strict_eval_) { Indent(n1, "// outer scope calls 'eval' in non-strict context\n"); } if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n"); - if (outer_scope_is_eval_scope_) { - Indent(n1, "// outer scope is 'eval' scope\n"); - } if (num_stack_slots_ > 0) { Indent(n1, "// "); PrintF("%d stack slots\n", num_stack_slots_); } if (num_heap_slots_ > 0) { Indent(n1, "// "); @@ -779,9 +841,9 @@ void Scope::Print(int n) { Indent(n1, "// dynamic vars\n"); if (dynamics_ != NULL) { - PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC)); - PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC_LOCAL)); - PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC_GLOBAL)); + PrintMap(n1, dynamics_->GetMap(DYNAMIC)); + PrintMap(n1, dynamics_->GetMap(DYNAMIC_LOCAL)); + PrintMap(n1, dynamics_->GetMap(DYNAMIC_GLOBAL)); } // Print inner scopes (disable by providing negative n). @@ -797,13 +859,20 @@ void Scope::Print(int n) { #endif // DEBUG -Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) { +Variable* Scope::NonLocal(Handle<String> name, VariableMode mode) { if (dynamics_ == NULL) dynamics_ = new DynamicScopePart(); VariableMap* map = dynamics_->GetMap(mode); Variable* var = map->Lookup(name); if (var == NULL) { // Declare a new non-local. - var = map->Declare(NULL, name, mode, true, Variable::NORMAL); + InitializationFlag init_flag = (mode == VAR) + ? kCreatedInitialized : kNeedsInitialization; + var = map->Declare(NULL, + name, + mode, + true, + Variable::NORMAL, + init_flag); // Allocate it by giving it a dynamic lookup. var->AllocateTo(Variable::LOOKUP, -1); } @@ -811,81 +880,64 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) { } -// Lookup a variable starting with this scope. The result is either -// the statically resolved variable belonging to an outer scope, or -// NULL. It may be NULL because a) we couldn't find a variable, or b) -// because the variable is just a guess (and may be shadowed by -// another variable that is introduced dynamically via an 'eval' call -// or a 'with' statement). Variable* Scope::LookupRecursive(Handle<String> name, - bool from_inner_scope, - Variable** invalidated_local) { - // If we find a variable, but the current scope calls 'eval', the found - // variable may not be the correct one (the 'eval' may introduce a - // property with the same name). In that case, remember that the variable - // found is just a guess. - bool guess = scope_calls_eval_; - + BindingKind* binding_kind, + AstNodeFactory<AstNullVisitor>* factory) { + ASSERT(binding_kind != NULL); // Try to find the variable in this scope. Variable* var = LocalLookup(name); + // We found a variable and we are done. (Even if there is an 'eval' in + // this scope which introduces the same variable again, the resulting + // variable remains the same.) if (var != NULL) { - // We found a variable. If this is not an inner lookup, we are done. - // (Even if there is an 'eval' in this scope which introduces the - // same variable again, the resulting variable remains the same. - // Note that enclosing 'with' statements are handled at the call site.) - if (!from_inner_scope) - return var; - - } else { - // We did not find a variable locally. Check against the function variable, - // if any. We can do this for all scopes, since the function variable is - // only present - if at all - for function scopes. - // - // This lookup corresponds to a lookup in the "intermediate" scope sitting - // between this scope and the outer scope. (ECMA-262, 3rd., requires that - // the name of named function literal is kept in an intermediate scope - // in between this scope and the next outer scope.) - if (function_ != NULL && function_->name().is_identical_to(name)) { - var = function_->var(); - - } else if (outer_scope_ != NULL) { - var = outer_scope_->LookupRecursive(name, true, invalidated_local); - // We may have found a variable in an outer scope. However, if - // the current scope is inside a 'with', the actual variable may - // be a property introduced via the 'with' statement. Then, the - // variable we may have found is just a guess. - if (scope_inside_with_) - guess = true; - } - - // If we did not find a variable, we are done. - if (var == NULL) - return NULL; + *binding_kind = BOUND; + return var; } - ASSERT(var != NULL); - - // If this is a lookup from an inner scope, mark the variable. - if (from_inner_scope) { - var->MarkAsAccessedFromInnerScope(); + // We did not find a variable locally. Check against the function variable, + // if any. We can do this for all scopes, since the function variable is + // only present - if at all - for function scopes. + *binding_kind = UNBOUND; + var = LookupFunctionVar(name, factory); + if (var != NULL) { + *binding_kind = BOUND; + } else if (outer_scope_ != NULL) { + var = outer_scope_->LookupRecursive(name, binding_kind, factory); + if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) { + var->ForceContextAllocation(); + } + } else { + ASSERT(is_global_scope()); } - // If the variable we have found is just a guess, invalidate the - // result. If the found variable is local, record that fact so we - // can generate fast code to get it if it is not shadowed by eval. - if (guess) { - if (!var->is_global()) *invalidated_local = var; - var = NULL; + if (is_with_scope()) { + // The current scope is a with scope, so the variable binding can not be + // statically resolved. However, note that it was necessary to do a lookup + // in the outer scope anyway, because if a binding exists in an outer scope, + // the associated variable has to be marked as potentially being accessed + // from inside of an inner with scope (the property may not be in the 'with' + // object). + *binding_kind = DYNAMIC_LOOKUP; + return NULL; + } else if (calls_non_strict_eval()) { + // A variable binding may have been found in an outer scope, but the current + // scope makes a non-strict 'eval' call, so the found variable may not be + // the correct one (the 'eval' may introduce a binding with the same name). + // In that case, change the lookup result to reflect this situation. + if (*binding_kind == BOUND) { + *binding_kind = BOUND_EVAL_SHADOWED; + } else if (*binding_kind == UNBOUND) { + *binding_kind = UNBOUND_EVAL_SHADOWED; + } } - return var; } void Scope::ResolveVariable(Scope* global_scope, - Handle<Context> context, - VariableProxy* proxy) { + VariableProxy* proxy, + AstNodeFactory<AstNullVisitor>* factory) { ASSERT(global_scope == NULL || global_scope->is_global_scope()); // If the proxy is already resolved there's nothing to do @@ -893,116 +945,75 @@ void Scope::ResolveVariable(Scope* global_scope, if (proxy->var() != NULL) return; // Otherwise, try to resolve the variable. - Variable* invalidated_local = NULL; - Variable* var = LookupRecursive(proxy->name(), false, &invalidated_local); - - if (proxy->inside_with()) { - // If we are inside a local 'with' statement, all bets are off - // and we cannot resolve the proxy to a local variable even if - // we found an outer matching variable. - // Note that we must do a lookup anyway, because if we find one, - // we must mark that variable as potentially accessed from this - // inner scope (the property may not be in the 'with' object). - var = NonLocal(proxy->name(), Variable::DYNAMIC); - - } else { - // We are not inside a local 'with' statement. - - if (var == NULL) { - // We did not find the variable. We have a global variable - // if we are in the global scope (we know already that we - // are outside a 'with' statement) or if there is no way - // that the variable might be introduced dynamically (through - // a local or outer eval() call, or an outer 'with' statement), - // or we don't know about the outer scope (because we are - // in an eval scope). - if (is_global_scope() || - !(scope_inside_with_ || outer_scope_is_eval_scope_ || - scope_calls_eval_ || outer_scope_calls_eval_)) { - // We must have a global variable. - ASSERT(global_scope != NULL); - var = global_scope->DeclareGlobal(proxy->name()); - - } else if (scope_inside_with_) { - // If we are inside a with statement we give up and look up - // the variable at runtime. - var = NonLocal(proxy->name(), Variable::DYNAMIC); - - } else if (invalidated_local != NULL) { - // No with statements are involved and we found a local - // variable that might be shadowed by eval introduced - // variables. - var = NonLocal(proxy->name(), Variable::DYNAMIC_LOCAL); - var->set_local_if_not_shadowed(invalidated_local); - - } else if (outer_scope_is_eval_scope_) { - // No with statements and we did not find a local and the code - // is executed with a call to eval. The context contains - // scope information that we can use to determine if the - // variable is global if it is not shadowed by eval-introduced - // variables. - if (context->GlobalIfNotShadowedByEval(proxy->name())) { - var = NonLocal(proxy->name(), Variable::DYNAMIC_GLOBAL); - - } else { - var = NonLocal(proxy->name(), Variable::DYNAMIC); - } + BindingKind binding_kind; + Variable* var = LookupRecursive(proxy->name(), &binding_kind, factory); + switch (binding_kind) { + case BOUND: + // We found a variable binding. + break; + case BOUND_EVAL_SHADOWED: + // We found a variable variable binding that might be shadowed + // by 'eval' introduced variable bindings. + if (var->is_global()) { + var = NonLocal(proxy->name(), DYNAMIC_GLOBAL); } else { - // No with statements and we did not find a local and the code - // is not executed with a call to eval. We know that this - // variable is global unless it is shadowed by eval-introduced - // variables. - var = NonLocal(proxy->name(), Variable::DYNAMIC_GLOBAL); + Variable* invalidated = var; + var = NonLocal(proxy->name(), DYNAMIC_LOCAL); + var->set_local_if_not_shadowed(invalidated); } - } + break; + + case UNBOUND: + // No binding has been found. Declare a variable in global scope. + ASSERT(global_scope != NULL); + var = global_scope->DeclareGlobal(proxy->name()); + break; + + case UNBOUND_EVAL_SHADOWED: + // No binding has been found. But some scope makes a + // non-strict 'eval' call. + var = NonLocal(proxy->name(), DYNAMIC_GLOBAL); + break; + + case DYNAMIC_LOOKUP: + // The variable could not be resolved statically. + var = NonLocal(proxy->name(), DYNAMIC); + break; } + ASSERT(var != NULL); proxy->BindTo(var); } -void Scope::ResolveVariablesRecursively(Scope* global_scope, - Handle<Context> context) { +void Scope::ResolveVariablesRecursively( + Scope* global_scope, + AstNodeFactory<AstNullVisitor>* factory) { ASSERT(global_scope == NULL || global_scope->is_global_scope()); // Resolve unresolved variables for this scope. for (int i = 0; i < unresolved_.length(); i++) { - ResolveVariable(global_scope, context, unresolved_[i]); + ResolveVariable(global_scope, unresolved_[i], factory); } // Resolve unresolved variables for inner scopes. for (int i = 0; i < inner_scopes_.length(); i++) { - inner_scopes_[i]->ResolveVariablesRecursively(global_scope, context); + inner_scopes_[i]->ResolveVariablesRecursively(global_scope, factory); } } -bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval, - bool outer_scope_calls_non_strict_eval, - bool outer_scope_is_eval_scope) { - if (outer_scope_calls_eval) { - outer_scope_calls_eval_ = true; - } - +bool Scope::PropagateScopeInfo(bool outer_scope_calls_non_strict_eval ) { if (outer_scope_calls_non_strict_eval) { outer_scope_calls_non_strict_eval_ = true; } - if (outer_scope_is_eval_scope) { - outer_scope_is_eval_scope_ = true; - } - - bool calls_eval = scope_calls_eval_ || outer_scope_calls_eval_; - bool is_eval = is_eval_scope() || outer_scope_is_eval_scope_; bool calls_non_strict_eval = - (scope_calls_eval_ && !is_strict_mode()) || - outer_scope_calls_non_strict_eval_; + this->calls_non_strict_eval() || outer_scope_calls_non_strict_eval_; for (int i = 0; i < inner_scopes_.length(); i++) { Scope* inner_scope = inner_scopes_[i]; - if (inner_scope->PropagateScopeInfo(calls_eval, - calls_non_strict_eval, - is_eval)) { + if (inner_scope->PropagateScopeInfo(calls_non_strict_eval)) { inner_scope_calls_eval_ = true; } if (inner_scope->force_eager_compilation_) { @@ -1019,7 +1030,7 @@ bool Scope::MustAllocate(Variable* var) { // via an eval() call. This is only possible if the variable has a // visible name. if ((var->is_this() || var->name()->length() > 0) && - (var->is_accessed_from_inner_scope() || + (var->has_forced_context_allocation() || scope_calls_eval_ || inner_scope_calls_eval_ || scope_contains_with_ || @@ -1040,9 +1051,9 @@ bool Scope::MustAllocateInContext(Variable* var) { // // Exceptions: temporary variables are never allocated in a context; // catch-bound variables are always allocated in a context. - if (var->mode() == Variable::TEMPORARY) return false; + if (var->mode() == TEMPORARY) return false; if (is_catch_scope() || is_block_scope()) return true; - return var->is_accessed_from_inner_scope() || + return var->has_forced_context_allocation() || scope_calls_eval_ || inner_scope_calls_eval_ || scope_contains_with_ || @@ -1095,7 +1106,7 @@ void Scope::AllocateParameterLocals() { // In strict mode 'arguments' does not alias formal parameters. // Therefore in strict mode we allocate parameters as if 'arguments' // were not used. - uses_nonstrict_arguments = !is_strict_mode(); + uses_nonstrict_arguments = is_classic_mode(); } // The same parameter may occur multiple times in the parameters_ list. @@ -1106,9 +1117,8 @@ void Scope::AllocateParameterLocals() { Variable* var = params_[i]; ASSERT(var->scope() == this); if (uses_nonstrict_arguments) { - // Give the parameter a use from an inner scope, to force allocation - // to the context. - var->MarkAsAccessedFromInnerScope(); + // Force context allocation of the parameter. + var->ForceContextAllocation(); } if (MustAllocate(var)) { @@ -1183,21 +1193,15 @@ void Scope::AllocateVariablesRecursively() { if (is_function_scope()) AllocateParameterLocals(); AllocateNonParameterLocals(); - // Allocate context if necessary. - bool must_have_local_context = false; - if (scope_calls_eval_ || scope_contains_with_) { - // The context for the eval() call or 'with' statement in this scope. - // Unless we are in the global or an eval scope, we need a local - // context even if we didn't statically allocate any locals in it, - // and the compiler will access the context variable. If we are - // not in an inner scope, the scope is provided from the outside. - must_have_local_context = is_function_scope(); - } + // Force allocation of a context for this scope if necessary. For a 'with' + // scope and for a function scope that makes an 'eval' call we need a context, + // even if no local variables were statically allocated in the scope. + bool must_have_context = is_with_scope() || + (is_function_scope() && calls_eval()); // If we didn't allocate any locals in the local context, then we only - // need the minimal number of slots if we must have a local context. - if (num_heap_slots_ == Context::MIN_CONTEXT_SLOTS && - !must_have_local_context) { + // need the minimal number of slots if we must have a context. + if (num_heap_slots_ == Context::MIN_CONTEXT_SLOTS && !must_have_context) { num_heap_slots_ = 0; } @@ -1205,4 +1209,17 @@ void Scope::AllocateVariablesRecursively() { ASSERT(num_heap_slots_ == 0 || num_heap_slots_ >= Context::MIN_CONTEXT_SLOTS); } + +int Scope::StackLocalCount() const { + return num_stack_slots() - + (function_ != NULL && function_->var()->IsStackLocal() ? 1 : 0); +} + + +int Scope::ContextLocalCount() const { + if (num_heap_slots() == 0) return 0; + return num_heap_slots() - Context::MIN_CONTEXT_SLOTS - + (function_ != NULL && function_->var()->IsContextSlot() ? 1 : 0); +} + } } // namespace v8::internal diff --git a/deps/v8/src/scopes.h b/deps/v8/src/scopes.h index 2917a63bba..06202c493b 100644 --- a/deps/v8/src/scopes.h +++ b/deps/v8/src/scopes.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -42,17 +42,14 @@ class VariableMap: public HashMap { public: VariableMap(); - // Dummy constructor. This constructor doesn't set up the map - // properly so don't use it unless you have a good reason. - explicit VariableMap(bool gotta_love_static_overloading); - virtual ~VariableMap(); Variable* Declare(Scope* scope, Handle<String> name, - Variable::Mode mode, + VariableMode mode, bool is_valid_lhs, - Variable::Kind kind); + Variable::Kind kind, + InitializationFlag initialization_flag); Variable* Lookup(Handle<String> name); }; @@ -64,8 +61,8 @@ class VariableMap: public HashMap { // and setup time for scopes that don't need them. class DynamicScopePart : public ZoneObject { public: - VariableMap* GetMap(Variable::Mode mode) { - int index = mode - Variable::DYNAMIC; + VariableMap* GetMap(VariableMode mode) { + int index = mode - DYNAMIC; ASSERT(index >= 0 && index < 3); return &maps_[index]; } @@ -89,28 +86,19 @@ class Scope: public ZoneObject { // --------------------------------------------------------------------------- // Construction - enum Type { - EVAL_SCOPE, // The top-level scope for an eval source. - FUNCTION_SCOPE, // The top-level scope for a function. - GLOBAL_SCOPE, // The top-level scope for a program or a top-level eval. - CATCH_SCOPE, // The scope introduced by catch. - BLOCK_SCOPE // The scope introduced by a new block. - }; - - Scope(Scope* outer_scope, Type type); + Scope(Scope* outer_scope, ScopeType type); // Compute top scope and allocate variables. For lazy compilation the top // scope only contains the single lazily compiled function, so this // doesn't re-allocate variables repeatedly. static bool Analyze(CompilationInfo* info); - static Scope* DeserializeScopeChain(CompilationInfo* info, - Scope* innermost_scope); + static Scope* DeserializeScopeChain(Context* context, Scope* global_scope); // The scope name is only used for printing/debugging. void SetScopeName(Handle<String> scope_name) { scope_name_ = scope_name; } - void Initialize(bool inside_with); + void Initialize(); // Checks if the block scope is redundant, i.e. it does not contain any // block scoped declarations. In that case it is removed from the scope @@ -123,6 +111,13 @@ class Scope: public ZoneObject { // Lookup a variable in this scope. Returns the variable or NULL if not found. Variable* LocalLookup(Handle<String> name); + // This lookup corresponds to a lookup in the "intermediate" scope sitting + // between this scope and the outer scope. (ECMA-262, 3rd., requires that + // the name of named function literal is kept in an intermediate scope + // in between this scope and the next outer scope.) + Variable* LookupFunctionVar(Handle<String> name, + AstNodeFactory<AstNullVisitor>* factory); + // Lookup a variable in this scope or outer scopes. // Returns the variable or NULL if not found. Variable* Lookup(Handle<String> name); @@ -130,16 +125,27 @@ class Scope: public ZoneObject { // Declare the function variable for a function literal. This variable // is in an intermediate scope between this function scope and the the // outer scope. Only possible for function scopes; at most one variable. - Variable* DeclareFunctionVar(Handle<String> name); + template<class Visitor> + Variable* DeclareFunctionVar(Handle<String> name, + VariableMode mode, + AstNodeFactory<Visitor>* factory) { + ASSERT(is_function_scope() && function_ == NULL); + Variable* function_var = new Variable( + this, name, mode, true, Variable::NORMAL, kCreatedInitialized); + function_ = factory->NewVariableProxy(function_var); + return function_var; + } // Declare a parameter in this scope. When there are duplicated // parameters the rightmost one 'wins'. However, the implementation // expects all parameters to be declared and from left to right. - void DeclareParameter(Handle<String> name, Variable::Mode mode); + void DeclareParameter(Handle<String> name, VariableMode mode); // Declare a local variable in this scope. If the variable has been // declared before, the previously declared variable is returned. - Variable* DeclareLocal(Handle<String> name, Variable::Mode mode); + Variable* DeclareLocal(Handle<String> name, + VariableMode mode, + InitializationFlag init_flag); // Declare an implicit global variable in this scope which must be a // global scope. The variable was introduced (possibly from an inner @@ -148,9 +154,18 @@ class Scope: public ZoneObject { Variable* DeclareGlobal(Handle<String> name); // Create a new unresolved variable. - VariableProxy* NewUnresolved(Handle<String> name, - bool inside_with, - int position = RelocInfo::kNoPosition); + template<class Visitor> + VariableProxy* NewUnresolved(AstNodeFactory<Visitor>* factory, + Handle<String> name, + int position = RelocInfo::kNoPosition) { + // Note that we must not share the unresolved variables with + // the same name because they may be removed selectively via + // RemoveUnresolved(). + ASSERT(!already_resolved()); + VariableProxy* proxy = factory->NewVariableProxy(name, false, position); + unresolved_.Add(proxy); + return proxy; + } // Remove a unresolved variable. During parsing, an unresolved variable // may have been added optimistically, but then only the variable name @@ -192,6 +207,11 @@ class Scope: public ZoneObject { // scope over a let binding of the same name. Declaration* CheckConflictingVarDeclarations(); + // For harmony block scoping mode: Check if the scope has variable proxies + // that are used as lvalues and point to const variables. Assumes that scopes + // have been analyzed and variables been resolved. + VariableProxy* CheckAssignmentToConst(); + // --------------------------------------------------------------------------- // Scope-specific info. @@ -199,11 +219,42 @@ class Scope: public ZoneObject { void RecordWithStatement() { scope_contains_with_ = true; } // Inform the scope that the corresponding code contains an eval call. - void RecordEvalCall() { scope_calls_eval_ = true; } + void RecordEvalCall() { if (!is_global_scope()) scope_calls_eval_ = true; } + + // Set the strict mode flag (unless disabled by a global flag). + void SetLanguageMode(LanguageMode language_mode) { + language_mode_ = language_mode; + } - // Enable strict mode for the scope (unless disabled by a global flag). - void EnableStrictMode() { - strict_mode_ = FLAG_strict_mode; + // Position in the source where this scope begins and ends. + // + // * For the scope of a with statement + // with (obj) stmt + // start position: start position of first token of 'stmt' + // end position: end position of last token of 'stmt' + // * For the scope of a block + // { stmts } + // start position: start position of '{' + // end position: end position of '}' + // * For the scope of a function literal or decalaration + // function fun(a,b) { stmts } + // start position: start position of '(' + // end position: end position of '}' + // * For the scope of a catch block + // try { stms } catch(e) { stmts } + // start position: start position of '(' + // end position: end position of ')' + // * For the scope of a for-statement + // for (let x ...) stmt + // start position: start position of '(' + // end position: end position of last token of 'stmt' + int start_position() const { return start_position_; } + void set_start_position(int statement_pos) { + start_position_ = statement_pos; + } + int end_position() const { return end_position_; } + void set_end_position(int statement_pos) { + end_position_ = statement_pos; } // --------------------------------------------------------------------------- @@ -215,14 +266,25 @@ class Scope: public ZoneObject { bool is_global_scope() const { return type_ == GLOBAL_SCOPE; } bool is_catch_scope() const { return type_ == CATCH_SCOPE; } bool is_block_scope() const { return type_ == BLOCK_SCOPE; } - bool is_strict_mode() const { return strict_mode_; } - bool is_strict_mode_eval_scope() const { - return is_eval_scope() && is_strict_mode(); + bool is_with_scope() const { return type_ == WITH_SCOPE; } + bool is_declaration_scope() const { + return is_eval_scope() || is_function_scope() || is_global_scope(); + } + bool is_classic_mode() const { + return language_mode() == CLASSIC_MODE; + } + bool is_extended_mode() const { + return language_mode() == EXTENDED_MODE; + } + bool is_strict_or_extended_eval_scope() const { + return is_eval_scope() && !is_classic_mode(); } // Information about which scopes calls eval. bool calls_eval() const { return scope_calls_eval_; } - bool outer_scope_calls_eval() const { return outer_scope_calls_eval_; } + bool calls_non_strict_eval() { + return scope_calls_eval_ && is_classic_mode(); + } bool outer_scope_calls_non_strict_eval() const { return outer_scope_calls_non_strict_eval_; } @@ -238,6 +300,12 @@ class Scope: public ZoneObject { // --------------------------------------------------------------------------- // Accessors. + // The type of this scope. + ScopeType type() const { return type_; } + + // The language mode of this scope. + LanguageMode language_mode() const { return language_mode_; } + // The variable corresponding the 'this' value. Variable* receiver() { return receiver_; } @@ -264,13 +332,17 @@ class Scope: public ZoneObject { // Declarations list. ZoneList<Declaration*>* declarations() { return &decls_; } + // Inner scope list. + ZoneList<Scope*>* inner_scopes() { return &inner_scopes_; } // --------------------------------------------------------------------------- // Variable allocation. - // Collect all used locals in this scope. - template<class Allocator> - void CollectUsedVariables(List<Variable*, Allocator>* locals); + // Collect stack and context allocated local variables in this scope. Note + // that the function variable - if present - is not collected and should be + // handled separately. + void CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals, + ZoneList<Variable*>* context_locals); // Resolve and fill in the allocation information for all variables // in this scopes. Must be called *after* all scopes have been @@ -280,7 +352,8 @@ class Scope: public ZoneObject { // In the case of code compiled and run using 'eval', the context // parameter is the context in which eval was called. In all other // cases the context parameter is an empty handle. - void AllocateVariables(Handle<Context> context); + void AllocateVariables(Scope* global_scope, + AstNodeFactory<AstNullVisitor>* factory); // Current number of var or const locals. int num_var_or_const() { return num_var_or_const_; } @@ -289,6 +362,9 @@ class Scope: public ZoneObject { int num_stack_slots() const { return num_stack_slots_; } int num_heap_slots() const { return num_heap_slots_; } + int StackLocalCount() const; + int ContextLocalCount() const; + // Make sure this scope and all outer scopes are eagerly compiled. void ForceEagerCompilation() { force_eager_compilation_ = true; } @@ -305,7 +381,14 @@ class Scope: public ZoneObject { // where var declarations will be hoisted to in the implementation. Scope* DeclarationScope(); - Handle<SerializedScopeInfo> GetSerializedScopeInfo(); + Handle<ScopeInfo> GetScopeInfo(); + + // Get the chain of nested scopes within this scope for the source statement + // position. The scopes will be added to the list from the outermost scope to + // the innermost scope. Only nested block, catch or with scopes are tracked + // and will be returned, but no inner function scopes. + void GetNestedScopeChain(List<Handle<ScopeInfo> >* chain, + int statement_position); // --------------------------------------------------------------------------- // Strict mode support. @@ -330,8 +413,6 @@ class Scope: public ZoneObject { protected: friend class ParserFactory; - explicit Scope(Type type); - Isolate* const isolate_; // Scope tree. @@ -339,7 +420,7 @@ class Scope: public ZoneObject { ZoneList<Scope*> inner_scopes_; // the immediately enclosed inner scopes // The scope type. - Type type_; + ScopeType type_; // Debugging support. Handle<String> scope_name_; @@ -379,14 +460,15 @@ class Scope: public ZoneObject { // This scope or a nested catch scope or with scope contain an 'eval' call. At // the 'eval' call site this scope is the declaration scope. bool scope_calls_eval_; - // This scope is a strict mode scope. - bool strict_mode_; + // The language mode of this scope. + LanguageMode language_mode_; + // Source positions. + int start_position_; + int end_position_; // Computed via PropagateScopeInfo. - bool outer_scope_calls_eval_; bool outer_scope_calls_non_strict_eval_; bool inner_scope_calls_eval_; - bool outer_scope_is_eval_scope_; bool force_eager_compilation_; // True if it doesn't need scope resolution (e.g., if the scope was @@ -396,32 +478,78 @@ class Scope: public ZoneObject { // Computed as variables are declared. int num_var_or_const_; - // Computed via AllocateVariables; function scopes only. + // Computed via AllocateVariables; function, block and catch scopes only. int num_stack_slots_; int num_heap_slots_; - // Serialized scopes support. - Handle<SerializedScopeInfo> scope_info_; + // Serialized scope info support. + Handle<ScopeInfo> scope_info_; bool already_resolved() { return already_resolved_; } // Create a non-local variable with a given name. // These variables are looked up dynamically at runtime. - Variable* NonLocal(Handle<String> name, Variable::Mode mode); + Variable* NonLocal(Handle<String> name, VariableMode mode); // Variable resolution. + // Possible results of a recursive variable lookup telling if and how a + // variable is bound. These are returned in the output parameter *binding_kind + // of the LookupRecursive function. + enum BindingKind { + // The variable reference could be statically resolved to a variable binding + // which is returned. There is no 'with' statement between the reference and + // the binding and no scope between the reference scope (inclusive) and + // binding scope (exclusive) makes a non-strict 'eval' call. + BOUND, + + // The variable reference could be statically resolved to a variable binding + // which is returned. There is no 'with' statement between the reference and + // the binding, but some scope between the reference scope (inclusive) and + // binding scope (exclusive) makes a non-strict 'eval' call, that might + // possibly introduce variable bindings shadowing the found one. Thus the + // found variable binding is just a guess. + BOUND_EVAL_SHADOWED, + + // The variable reference could not be statically resolved to any binding + // and thus should be considered referencing a global variable. NULL is + // returned. The variable reference is not inside any 'with' statement and + // no scope between the reference scope (inclusive) and global scope + // (exclusive) makes a non-strict 'eval' call. + UNBOUND, + + // The variable reference could not be statically resolved to any binding + // NULL is returned. The variable reference is not inside any 'with' + // statement, but some scope between the reference scope (inclusive) and + // global scope (exclusive) makes a non-strict 'eval' call, that might + // possibly introduce a variable binding. Thus the reference should be + // considered referencing a global variable unless it is shadowed by an + // 'eval' introduced binding. + UNBOUND_EVAL_SHADOWED, + + // The variable could not be statically resolved and needs to be looked up + // dynamically. NULL is returned. There are two possible reasons: + // * A 'with' statement has been encountered and there is no variable + // binding for the name between the variable reference and the 'with'. + // The variable potentially references a property of the 'with' object. + // * The code is being executed as part of a call to 'eval' and the calling + // context chain contains either a variable binding for the name or it + // contains a 'with' context. + DYNAMIC_LOOKUP + }; + + // Lookup a variable reference given by name recursively starting with this + // scope. If the code is executed because of a call to 'eval', the context + // parameter should be set to the calling context of 'eval'. Variable* LookupRecursive(Handle<String> name, - bool from_inner_function, - Variable** invalidated_local); + BindingKind* binding_kind, + AstNodeFactory<AstNullVisitor>* factory); void ResolveVariable(Scope* global_scope, - Handle<Context> context, - VariableProxy* proxy); + VariableProxy* proxy, + AstNodeFactory<AstNullVisitor>* factory); void ResolveVariablesRecursively(Scope* global_scope, - Handle<Context> context); + AstNodeFactory<AstNullVisitor>* factory); // Scope analysis. - bool PropagateScopeInfo(bool outer_scope_calls_eval, - bool outer_scope_calls_non_strict_eval, - bool outer_scope_is_eval_scope); + bool PropagateScopeInfo(bool outer_scope_calls_non_strict_eval); bool HasTrivialContext() const; // Predicates. @@ -438,8 +566,8 @@ class Scope: public ZoneObject { void AllocateVariablesRecursively(); private: - // Construct a function or block scope based on the scope info. - Scope(Scope* inner_scope, Type type, Handle<SerializedScopeInfo> scope_info); + // Construct a scope based on the scope info. + Scope(Scope* inner_scope, ScopeType type, Handle<ScopeInfo> scope_info); // Construct a catch scope with a binding for the name. Scope(Scope* inner_scope, Handle<String> catch_variable_name); @@ -451,9 +579,9 @@ class Scope: public ZoneObject { } } - void SetDefaults(Type type, + void SetDefaults(ScopeType type, Scope* outer_scope, - Handle<SerializedScopeInfo> scope_info); + Handle<ScopeInfo> scope_info); }; } } // namespace v8::internal diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index ecb480a8f8..d9fc2b7b7c 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -300,16 +300,28 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { RUNTIME_ENTRY, 4, "HandleScope::DeleteExtensions"); + Add(ExternalReference:: + incremental_marking_record_write_function(isolate).address(), + RUNTIME_ENTRY, + 5, + "IncrementalMarking::RecordWrite"); + Add(ExternalReference::store_buffer_overflow_function(isolate).address(), + RUNTIME_ENTRY, + 6, + "StoreBuffer::StoreBufferOverflow"); + Add(ExternalReference:: + incremental_evacuation_record_write_function(isolate).address(), + RUNTIME_ENTRY, + 7, + "IncrementalMarking::RecordWrite"); + + // Miscellaneous - Add(ExternalReference::the_hole_value_location(isolate).address(), - UNCLASSIFIED, - 2, - "Factory::the_hole_value().location()"); - Add(ExternalReference::roots_address(isolate).address(), + Add(ExternalReference::roots_array_start(isolate).address(), UNCLASSIFIED, 3, - "Heap::roots_address()"); + "Heap::roots_array_start()"); Add(ExternalReference::address_of_stack_limit(isolate).address(), UNCLASSIFIED, 4, @@ -351,129 +363,137 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { "Heap::always_allocate_scope_depth()"); Add(ExternalReference::new_space_allocation_limit_address(isolate).address(), UNCLASSIFIED, - 13, + 14, "Heap::NewSpaceAllocationLimitAddress()"); Add(ExternalReference::new_space_allocation_top_address(isolate).address(), UNCLASSIFIED, - 14, + 15, "Heap::NewSpaceAllocationTopAddress()"); #ifdef ENABLE_DEBUGGER_SUPPORT Add(ExternalReference::debug_break(isolate).address(), UNCLASSIFIED, - 15, + 16, "Debug::Break()"); Add(ExternalReference::debug_step_in_fp_address(isolate).address(), UNCLASSIFIED, - 16, + 17, "Debug::step_in_fp_addr()"); #endif Add(ExternalReference::double_fp_operation(Token::ADD, isolate).address(), UNCLASSIFIED, - 17, + 18, "add_two_doubles"); Add(ExternalReference::double_fp_operation(Token::SUB, isolate).address(), UNCLASSIFIED, - 18, + 19, "sub_two_doubles"); Add(ExternalReference::double_fp_operation(Token::MUL, isolate).address(), UNCLASSIFIED, - 19, + 20, "mul_two_doubles"); Add(ExternalReference::double_fp_operation(Token::DIV, isolate).address(), UNCLASSIFIED, - 20, + 21, "div_two_doubles"); Add(ExternalReference::double_fp_operation(Token::MOD, isolate).address(), UNCLASSIFIED, - 21, + 22, "mod_two_doubles"); Add(ExternalReference::compare_doubles(isolate).address(), UNCLASSIFIED, - 22, + 23, "compare_doubles"); #ifndef V8_INTERPRETED_REGEXP Add(ExternalReference::re_case_insensitive_compare_uc16(isolate).address(), UNCLASSIFIED, - 23, + 24, "NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()"); Add(ExternalReference::re_check_stack_guard_state(isolate).address(), UNCLASSIFIED, - 24, + 25, "RegExpMacroAssembler*::CheckStackGuardState()"); Add(ExternalReference::re_grow_stack(isolate).address(), UNCLASSIFIED, - 25, + 26, "NativeRegExpMacroAssembler::GrowStack()"); Add(ExternalReference::re_word_character_map().address(), UNCLASSIFIED, - 26, + 27, "NativeRegExpMacroAssembler::word_character_map"); #endif // V8_INTERPRETED_REGEXP // Keyed lookup cache. Add(ExternalReference::keyed_lookup_cache_keys(isolate).address(), UNCLASSIFIED, - 27, + 28, "KeyedLookupCache::keys()"); Add(ExternalReference::keyed_lookup_cache_field_offsets(isolate).address(), UNCLASSIFIED, - 28, + 29, "KeyedLookupCache::field_offsets()"); Add(ExternalReference::transcendental_cache_array_address(isolate).address(), UNCLASSIFIED, - 29, + 30, "TranscendentalCache::caches()"); Add(ExternalReference::handle_scope_next_address().address(), UNCLASSIFIED, - 30, + 31, "HandleScope::next"); Add(ExternalReference::handle_scope_limit_address().address(), UNCLASSIFIED, - 31, + 32, "HandleScope::limit"); Add(ExternalReference::handle_scope_level_address().address(), UNCLASSIFIED, - 32, + 33, "HandleScope::level"); Add(ExternalReference::new_deoptimizer_function(isolate).address(), UNCLASSIFIED, - 33, + 34, "Deoptimizer::New()"); Add(ExternalReference::compute_output_frames_function(isolate).address(), UNCLASSIFIED, - 34, + 35, "Deoptimizer::ComputeOutputFrames()"); Add(ExternalReference::address_of_min_int().address(), UNCLASSIFIED, - 35, + 36, "LDoubleConstant::min_int"); Add(ExternalReference::address_of_one_half().address(), UNCLASSIFIED, - 36, + 37, "LDoubleConstant::one_half"); Add(ExternalReference::isolate_address().address(), UNCLASSIFIED, - 37, + 38, "isolate"); Add(ExternalReference::address_of_minus_zero().address(), UNCLASSIFIED, - 38, + 39, "LDoubleConstant::minus_zero"); Add(ExternalReference::address_of_negative_infinity().address(), UNCLASSIFIED, - 39, + 40, "LDoubleConstant::negative_infinity"); Add(ExternalReference::power_double_double_function(isolate).address(), UNCLASSIFIED, - 40, + 41, "power_double_double_function"); Add(ExternalReference::power_double_int_function(isolate).address(), UNCLASSIFIED, - 41, + 42, "power_double_int_function"); - Add(ExternalReference::arguments_marker_location(isolate).address(), + Add(ExternalReference::store_buffer_top(isolate).address(), UNCLASSIFIED, - 42, - "Factory::arguments_marker().location()"); + 43, + "store_buffer_top"); + Add(ExternalReference::address_of_canonical_non_hole_nan().address(), + UNCLASSIFIED, + 44, + "canonical_nan"); + Add(ExternalReference::address_of_the_hole_nan().address(), + UNCLASSIFIED, + 45, + "the_hole_nan"); } @@ -569,6 +589,7 @@ Address Deserializer::Allocate(int space_index, Space* space, int size) { maybe_new_allocation = reinterpret_cast<PagedSpace*>(space)->AllocateRaw(size); } + ASSERT(!maybe_new_allocation->IsFailure()); Object* new_allocation = maybe_new_allocation->ToObjectUnchecked(); HeapObject* new_object = HeapObject::cast(new_allocation); address = new_object->address(); @@ -577,14 +598,13 @@ Address Deserializer::Allocate(int space_index, Space* space, int size) { ASSERT(SpaceIsLarge(space_index)); LargeObjectSpace* lo_space = reinterpret_cast<LargeObjectSpace*>(space); Object* new_allocation; - if (space_index == kLargeData) { - new_allocation = lo_space->AllocateRaw(size)->ToObjectUnchecked(); - } else if (space_index == kLargeFixedArray) { + if (space_index == kLargeData || space_index == kLargeFixedArray) { new_allocation = - lo_space->AllocateRawFixedArray(size)->ToObjectUnchecked(); + lo_space->AllocateRaw(size, NOT_EXECUTABLE)->ToObjectUnchecked(); } else { ASSERT_EQ(kLargeCode, space_index); - new_allocation = lo_space->AllocateRawCode(size)->ToObjectUnchecked(); + new_allocation = + lo_space->AllocateRaw(size, EXECUTABLE)->ToObjectUnchecked(); } HeapObject* new_object = HeapObject::cast(new_allocation); // Record all large objects in the same space. @@ -629,6 +649,7 @@ HeapObject* Deserializer::GetAddressFromStart(int space) { void Deserializer::Deserialize() { isolate_ = Isolate::Current(); + ASSERT(isolate_ != NULL); // Don't GC while deserializing - just expand the heap. AlwaysAllocateScope always_allocate; // Don't use the free lists while deserializing. @@ -648,6 +669,14 @@ void Deserializer::Deserialize() { isolate_->heap()->set_global_contexts_list( isolate_->heap()->undefined_value()); + + // Update data pointers to the external strings containing natives sources. + for (int i = 0; i < Natives::GetBuiltinsCount(); i++) { + Object* source = isolate_->heap()->natives_source_cache()->get(i); + if (!source->IsUndefined()) { + ExternalAsciiString::cast(source)->update_data_cache(); + } + } } @@ -685,9 +714,8 @@ void Deserializer::VisitPointers(Object** start, Object** end) { // This routine writes the new object into the pointer provided and then // returns true if the new object was in young space and false otherwise. // The reason for this strange interface is that otherwise the object is -// written very late, which means the ByteArray map is not set up by the -// time we need to use it to mark the space at the end of a page free (by -// making it into a byte array). +// written very late, which means the FreeSpace map is not set up by the +// time we need to use it to mark the space at the end of a page free. void Deserializer::ReadObject(int space_number, Space* space, Object** write_back) { @@ -737,8 +765,13 @@ static const int kUnknownOffsetFromStart = -1; void Deserializer::ReadChunk(Object** current, Object** limit, int source_space, - Address address) { + Address current_object_address) { Isolate* const isolate = isolate_; + bool write_barrier_needed = (current_object_address != NULL && + source_space != NEW_SPACE && + source_space != CELL_SPACE && + source_space != CODE_SPACE && + source_space != OLD_DATA_SPACE); while (current < limit) { int data = source_->Get(); switch (data) { @@ -758,8 +791,7 @@ void Deserializer::ReadChunk(Object** current, if (where == kNewObject && how == kPlain && within == kStartOfObject) {\ ASSIGN_DEST_SPACE(space_number) \ ReadObject(space_number, dest_space, current); \ - emit_write_barrier = \ - (space_number == NEW_SPACE && source_space != NEW_SPACE); \ + emit_write_barrier = (space_number == NEW_SPACE); \ } else { \ Object* new_object = NULL; /* May not be a real Object pointer. */ \ if (where == kNewObject) { \ @@ -767,25 +799,25 @@ void Deserializer::ReadChunk(Object** current, ReadObject(space_number, dest_space, &new_object); \ } else if (where == kRootArray) { \ int root_id = source_->GetInt(); \ - new_object = isolate->heap()->roots_address()[root_id]; \ + new_object = isolate->heap()->roots_array_start()[root_id]; \ + emit_write_barrier = isolate->heap()->InNewSpace(new_object); \ } else if (where == kPartialSnapshotCache) { \ int cache_index = source_->GetInt(); \ new_object = isolate->serialize_partial_snapshot_cache() \ [cache_index]; \ + emit_write_barrier = isolate->heap()->InNewSpace(new_object); \ } else if (where == kExternalReference) { \ int reference_id = source_->GetInt(); \ Address address = external_reference_decoder_-> \ Decode(reference_id); \ new_object = reinterpret_cast<Object*>(address); \ } else if (where == kBackref) { \ - emit_write_barrier = \ - (space_number == NEW_SPACE && source_space != NEW_SPACE); \ + emit_write_barrier = (space_number == NEW_SPACE); \ new_object = GetAddressFromEnd(data & kSpaceMask); \ } else { \ ASSERT(where == kFromStart); \ if (offset_from_start == kUnknownOffsetFromStart) { \ - emit_write_barrier = \ - (space_number == NEW_SPACE && source_space != NEW_SPACE); \ + emit_write_barrier = (space_number == NEW_SPACE); \ new_object = GetAddressFromStart(data & kSpaceMask); \ } else { \ Address object_address = pages_[space_number][0] + \ @@ -812,12 +844,14 @@ void Deserializer::ReadChunk(Object** current, *current = new_object; \ } \ } \ - if (emit_write_barrier) { \ - isolate->heap()->RecordWrite(address, static_cast<int>( \ - reinterpret_cast<Address>(current) - address)); \ + if (emit_write_barrier && write_barrier_needed) { \ + Address current_address = reinterpret_cast<Address>(current); \ + isolate->heap()->RecordWrite( \ + current_object_address, \ + static_cast<int>(current_address - current_object_address)); \ } \ if (!current_was_incremented) { \ - current++; /* Increment current if it wasn't done above. */ \ + current++; \ } \ break; \ } \ @@ -864,11 +898,17 @@ void Deserializer::ReadChunk(Object** current, CASE_STATEMENT(where, how, within, kLargeCode) \ CASE_BODY(where, how, within, kLargeCode, kUnknownOffsetFromStart) -#define EMIT_COMMON_REFERENCE_PATTERNS(pseudo_space_number, \ - space_number, \ - offset_from_start) \ - CASE_STATEMENT(kFromStart, kPlain, kStartOfObject, pseudo_space_number) \ - CASE_BODY(kFromStart, kPlain, kStartOfObject, space_number, offset_from_start) +#define FOUR_CASES(byte_code) \ + case byte_code: \ + case byte_code + 1: \ + case byte_code + 2: \ + case byte_code + 3: + +#define SIXTEEN_CASES(byte_code) \ + FOUR_CASES(byte_code) \ + FOUR_CASES(byte_code + 4) \ + FOUR_CASES(byte_code + 8) \ + FOUR_CASES(byte_code + 12) // We generate 15 cases and bodies that process special tags that combine // the raw data tag and the length into one byte. @@ -892,6 +932,38 @@ void Deserializer::ReadChunk(Object** current, break; } + SIXTEEN_CASES(kRootArrayLowConstants) + SIXTEEN_CASES(kRootArrayHighConstants) { + int root_id = RootArrayConstantFromByteCode(data); + Object* object = isolate->heap()->roots_array_start()[root_id]; + ASSERT(!isolate->heap()->InNewSpace(object)); + *current++ = object; + break; + } + + case kRepeat: { + int repeats = source_->GetInt(); + Object* object = current[-1]; + ASSERT(!isolate->heap()->InNewSpace(object)); + for (int i = 0; i < repeats; i++) current[i] = object; + current += repeats; + break; + } + + STATIC_ASSERT(kRootArrayNumberOfConstantEncodings == + Heap::kOldSpaceRoots); + STATIC_ASSERT(kMaxRepeats == 12); + FOUR_CASES(kConstantRepeat) + FOUR_CASES(kConstantRepeat + 4) + FOUR_CASES(kConstantRepeat + 8) { + int repeats = RepeatsForCode(data); + Object* object = current[-1]; + ASSERT(!isolate->heap()->InNewSpace(object)); + for (int i = 0; i < repeats; i++) current[i] = object; + current += repeats; + break; + } + // Deserialize a new object and write a pointer to it to the current // object. ONE_PER_SPACE(kNewObject, kPlain, kStartOfObject) @@ -917,9 +989,6 @@ void Deserializer::ReadChunk(Object** current, // start and write a pointer to its first instruction to the current code // object. ALL_SPACES(kFromStart, kFromCode, kFirstInstruction) - // Find an already deserialized object at one of the predetermined popular - // offsets from the start and write a pointer to it in the current object. - COMMON_REFERENCE_PATTERNS(EMIT_COMMON_REFERENCE_PATTERNS) // Find an object in the roots array and write a pointer to it to the // current object. CASE_STATEMENT(kRootArray, kPlain, kStartOfObject, 0) @@ -961,7 +1030,6 @@ void Deserializer::ReadChunk(Object** current, #undef CASE_BODY #undef ONE_PER_SPACE #undef ALL_SPACES -#undef EMIT_COMMON_REFERENCE_PATTERNS #undef ASSIGN_DEST_SPACE case kNewPage: { @@ -973,6 +1041,11 @@ void Deserializer::ReadChunk(Object** current, break; } + case kSkip: { + current++; + break; + } + case kNativesStringResource: { int index = source_->Get(); Vector<const char> source_vector = Natives::GetRawScriptSource(index); @@ -1008,42 +1081,13 @@ void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) { PutSection(static_cast<int>(integer & 0x7f), "IntLastPart"); } -#ifdef DEBUG - -void Deserializer::Synchronize(const char* tag) { - int data = source_->Get(); - // If this assert fails then that indicates that you have a mismatch between - // the number of GC roots when serializing and deserializing. - ASSERT_EQ(kSynchronize, data); - do { - int character = source_->Get(); - if (character == 0) break; - if (FLAG_debug_serialization) { - PrintF("%c", character); - } - } while (true); - if (FLAG_debug_serialization) { - PrintF("\n"); - } -} - - -void Serializer::Synchronize(const char* tag) { - sink_->Put(kSynchronize, tag); - int character; - do { - character = *tag++; - sink_->PutSection(character, "TagCharacter"); - } while (character != 0); -} - -#endif Serializer::Serializer(SnapshotByteSink* sink) : sink_(sink), current_root_index_(0), external_reference_encoder_(new ExternalReferenceEncoder), - large_object_total_(0) { + large_object_total_(0), + root_index_wave_front_(0) { // The serializer is meant to be used only to generate initial heap images // from a context in which there is only one isolate. ASSERT(Isolate::Current()->IsDefaultIsolate()); @@ -1066,11 +1110,8 @@ void StartupSerializer::SerializeStrongReferences() { CHECK(isolate->handle_scope_implementer()->blocks()->is_empty()); CHECK_EQ(0, isolate->global_handles()->NumberOfWeakHandles()); // We don't support serializing installed extensions. - for (RegisteredExtension* ext = v8::RegisteredExtension::first_extension(); - ext != NULL; - ext = ext->next()) { - CHECK_NE(v8::INSTALLED, ext->state()); - } + CHECK(!isolate->has_installed_extensions()); + HEAP->IterateStrongRoots(this, VISIT_ONLY_STRONG); } @@ -1097,8 +1138,17 @@ void PartialSerializer::Serialize(Object** object) { void Serializer::VisitPointers(Object** start, Object** end) { + Isolate* isolate = Isolate::Current(); + for (Object** current = start; current < end; current++) { - if ((*current)->IsSmi()) { + if (start == isolate->heap()->roots_array_start()) { + root_index_wave_front_ = + Max(root_index_wave_front_, static_cast<intptr_t>(current - start)); + } + if (reinterpret_cast<Address>(current) == + isolate->heap()->store_buffer()->TopAddress()) { + sink_->Put(kSkip, "Skip"); + } else if ((*current)->IsSmi()) { sink_->Put(kRawData, "RawData"); sink_->PutInt(kPointerSize, "length"); for (int i = 0; i < kPointerSize; i++) { @@ -1162,10 +1212,12 @@ int PartialSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) { } -int PartialSerializer::RootIndex(HeapObject* heap_object) { - for (int i = 0; i < Heap::kRootListLength; i++) { - Object* root = HEAP->roots_address()[i]; - if (root == heap_object) return i; +int Serializer::RootIndex(HeapObject* heap_object) { + Heap* heap = HEAP; + if (heap->InNewSpace(heap_object)) return kInvalidRootIndex; + for (int i = 0; i < root_index_wave_front_; i++) { + Object* root = heap->roots_array_start()[i]; + if (!root->IsSmi() && root == heap_object) return i; } return kInvalidRootIndex; } @@ -1201,18 +1253,8 @@ void Serializer::SerializeReferenceToPreviousObject( // all objects) then we should shift out the bits that are always 0. if (!SpaceIsLarge(space)) address >>= kObjectAlignmentBits; if (from_start) { -#define COMMON_REFS_CASE(pseudo_space, actual_space, offset) \ - if (space == actual_space && address == offset && \ - how_to_code == kPlain && where_to_point == kStartOfObject) { \ - sink_->Put(kFromStart + how_to_code + where_to_point + \ - pseudo_space, "RefSer"); \ - } else /* NOLINT */ - COMMON_REFERENCE_PATTERNS(COMMON_REFS_CASE) -#undef COMMON_REFS_CASE - { /* NOLINT */ - sink_->Put(kFromStart + how_to_code + where_to_point + space, "RefSer"); - sink_->PutInt(address, "address"); - } + sink_->Put(kFromStart + how_to_code + where_to_point + space, "RefSer"); + sink_->PutInt(address, "address"); } else { sink_->Put(kBackref + how_to_code + where_to_point + space, "BackRefSer"); sink_->PutInt(address, "address"); @@ -1227,6 +1269,12 @@ void StartupSerializer::SerializeObject( CHECK(o->IsHeapObject()); HeapObject* heap_object = HeapObject::cast(o); + int root_index; + if ((root_index = RootIndex(heap_object)) != kInvalidRootIndex) { + PutRoot(root_index, heap_object, how_to_code, where_to_point); + return; + } + if (address_mapper_.IsMapped(heap_object)) { int space = SpaceOfAlreadySerializedObject(heap_object); int address = address_mapper_.MappedTo(heap_object); @@ -1257,6 +1305,28 @@ void StartupSerializer::SerializeWeakReferences() { } +void Serializer::PutRoot(int root_index, + HeapObject* object, + SerializerDeserializer::HowToCode how_to_code, + SerializerDeserializer::WhereToPoint where_to_point) { + if (how_to_code == kPlain && + where_to_point == kStartOfObject && + root_index < kRootArrayNumberOfConstantEncodings && + !HEAP->InNewSpace(object)) { + if (root_index < kRootArrayNumberOfLowConstantEncodings) { + sink_->Put(kRootArrayLowConstants + root_index, "RootLoConstant"); + } else { + sink_->Put(kRootArrayHighConstants + root_index - + kRootArrayNumberOfLowConstantEncodings, + "RootHiConstant"); + } + } else { + sink_->Put(kRootArray + how_to_code + where_to_point, "RootSerialization"); + sink_->PutInt(root_index, "root_index"); + } +} + + void PartialSerializer::SerializeObject( Object* o, HowToCode how_to_code, @@ -1264,10 +1334,16 @@ void PartialSerializer::SerializeObject( CHECK(o->IsHeapObject()); HeapObject* heap_object = HeapObject::cast(o); + if (heap_object->IsMap()) { + // The code-caches link to context-specific code objects, which + // the startup and context serializes cannot currently handle. + ASSERT(Map::cast(heap_object)->code_cache() == + heap_object->GetHeap()->raw_unchecked_empty_fixed_array()); + } + int root_index; if ((root_index = RootIndex(heap_object)) != kInvalidRootIndex) { - sink_->Put(kRootArray + how_to_code + where_to_point, "RootSerialization"); - sink_->PutInt(root_index, "root_index"); + PutRoot(root_index, heap_object, how_to_code, where_to_point); return; } @@ -1345,14 +1421,48 @@ void Serializer::ObjectSerializer::VisitPointers(Object** start, if (current < end) OutputRawData(reinterpret_cast<Address>(current)); while (current < end && !(*current)->IsSmi()) { - serializer_->SerializeObject(*current, kPlain, kStartOfObject); - bytes_processed_so_far_ += kPointerSize; - current++; + HeapObject* current_contents = HeapObject::cast(*current); + int root_index = serializer_->RootIndex(current_contents); + // Repeats are not subject to the write barrier so there are only some + // objects that can be used in a repeat encoding. These are the early + // ones in the root array that are never in new space. + if (current != start && + root_index != kInvalidRootIndex && + root_index < kRootArrayNumberOfConstantEncodings && + current_contents == current[-1]) { + ASSERT(!HEAP->InNewSpace(current_contents)); + int repeat_count = 1; + while (current < end - 1 && current[repeat_count] == current_contents) { + repeat_count++; + } + current += repeat_count; + bytes_processed_so_far_ += repeat_count * kPointerSize; + if (repeat_count > kMaxRepeats) { + sink_->Put(kRepeat, "SerializeRepeats"); + sink_->PutInt(repeat_count, "SerializeRepeats"); + } else { + sink_->Put(CodeForRepeats(repeat_count), "SerializeRepeats"); + } + } else { + serializer_->SerializeObject(current_contents, kPlain, kStartOfObject); + bytes_processed_so_far_ += kPointerSize; + current++; + } } } } +void Serializer::ObjectSerializer::VisitEmbeddedPointer(RelocInfo* rinfo) { + Object** current = rinfo->target_object_address(); + + OutputRawData(rinfo->target_address_address()); + HowToCode representation = rinfo->IsCodedSpecially() ? kFromCode : kPlain; + serializer_->SerializeObject(*current, representation, kStartOfObject); + bytes_processed_so_far_ += rinfo->target_address_size(); +} + + void Serializer::ObjectSerializer::VisitExternalReferences(Address* start, Address* end) { Address references_start = reinterpret_cast<Address>(start); @@ -1367,6 +1477,20 @@ void Serializer::ObjectSerializer::VisitExternalReferences(Address* start, } +void Serializer::ObjectSerializer::VisitExternalReference(RelocInfo* rinfo) { + Address references_start = rinfo->target_address_address(); + OutputRawData(references_start); + + Address* current = rinfo->target_reference_address(); + int representation = rinfo->IsCodedSpecially() ? + kFromCode + kStartOfObject : kPlain + kStartOfObject; + sink_->Put(kExternalReference + representation, "ExternalRef"); + int reference_id = serializer_->EncodeExternalReference(*current); + sink_->PutInt(reference_id, "reference id"); + bytes_processed_so_far_ += rinfo->target_address_size(); +} + + void Serializer::ObjectSerializer::VisitRuntimeEntry(RelocInfo* rinfo) { Address target_start = rinfo->target_address_address(); OutputRawData(target_start); @@ -1420,7 +1544,7 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString( if (!source->IsUndefined()) { ExternalAsciiString* string = ExternalAsciiString::cast(source); typedef v8::String::ExternalAsciiStringResource Resource; - Resource* resource = string->resource(); + const Resource* resource = string->resource(); if (resource == *resource_pointer) { sink_->Put(kNativesStringResource, "NativesStringResource"); sink_->PutSection(i, "NativesStringResourceEnd"); diff --git a/deps/v8/src/serialize.h b/deps/v8/src/serialize.h index 66d6fb5111..72eed5ad2f 100644 --- a/deps/v8/src/serialize.h +++ b/deps/v8/src/serialize.h @@ -1,4 +1,4 @@ -// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -187,24 +187,6 @@ class SnapshotByteSource { }; -// It is very common to have a reference to objects at certain offsets in the -// heap. These offsets have been determined experimentally. We code -// references to such objects in a single byte that encodes the way the pointer -// is written (only plain pointers allowed), the space number and the offset. -// This only works for objects in the first page of a space. Don't use this for -// things in newspace since it bypasses the write barrier. - -static const int k64 = (sizeof(uintptr_t) - 4) / 4; - -#define COMMON_REFERENCE_PATTERNS(f) \ - f(kNumberOfSpaces, 2, (11 - k64)) \ - f((kNumberOfSpaces + 1), 2, 0) \ - f((kNumberOfSpaces + 2), 2, (142 - 16 * k64)) \ - f((kNumberOfSpaces + 3), 2, (74 - 15 * k64)) \ - f((kNumberOfSpaces + 4), 2, 5) \ - f((kNumberOfSpaces + 5), 1, 135) \ - f((kNumberOfSpaces + 6), 2, (228 - 39 * k64)) - #define COMMON_RAW_LENGTHS(f) \ f(1, 1) \ f(2, 2) \ @@ -238,14 +220,15 @@ class SerializerDeserializer: public ObjectVisitor { kRootArray = 0x9, // Object is found in root array. kPartialSnapshotCache = 0xa, // Object is in the cache. kExternalReference = 0xb, // Pointer to an external reference. - // 0xc-0xf Free. + kSkip = 0xc, // Skip a pointer sized cell. + // 0xd-0xf Free. kBackref = 0x10, // Object is described relative to end. // 0x11-0x18 One per space. - // 0x19-0x1f Common backref offsets. + // 0x19-0x1f Free. kFromStart = 0x20, // Object is described relative to start. // 0x21-0x28 One per space. // 0x29-0x2f Free. - // 0x30-0x3f Used by misc tags below. + // 0x30-0x3f Used by misc. tags below. kPointedToMask = 0x3f }; @@ -278,9 +261,29 @@ class SerializerDeserializer: public ObjectVisitor { // is referred to from external strings in the snapshot. static const int kNativesStringResource = 0x71; static const int kNewPage = 0x72; - // 0x73-0x7f Free. - // 0xb0-0xbf Free. - // 0xf0-0xff Free. + static const int kRepeat = 0x73; + static const int kConstantRepeat = 0x74; + // 0x74-0x7f Repeat last word (subtract 0x73 to get the count). + static const int kMaxRepeats = 0x7f - 0x73; + static int CodeForRepeats(int repeats) { + ASSERT(repeats >= 1 && repeats <= kMaxRepeats); + return 0x73 + repeats; + } + static int RepeatsForCode(int byte_code) { + ASSERT(byte_code >= kConstantRepeat && byte_code <= 0x7f); + return byte_code - 0x73; + } + static const int kRootArrayLowConstants = 0xb0; + // 0xb0-0xbf Things from the first 16 elements of the root array. + static const int kRootArrayHighConstants = 0xf0; + // 0xf0-0xff Things from the next 16 elements of the root array. + static const int kRootArrayNumberOfConstantEncodings = 0x20; + static const int kRootArrayNumberOfLowConstantEncodings = 0x10; + static int RootArrayConstantFromByteCode(int byte_code) { + int constant = (byte_code & 0xf) | ((byte_code & 0x40) >> 2); + ASSERT(constant >= 0 && constant < kRootArrayNumberOfConstantEncodings); + return constant; + } static const int kLargeData = LAST_SPACE; @@ -338,10 +341,6 @@ class Deserializer: public SerializerDeserializer { // Deserialize a single object and the objects reachable from it. void DeserializePartial(Object** root); -#ifdef DEBUG - virtual void Synchronize(const char* tag); -#endif - private: virtual void VisitPointers(Object** start, Object** end); @@ -353,7 +352,13 @@ class Deserializer: public SerializerDeserializer { UNREACHABLE(); } - void ReadChunk(Object** start, Object** end, int space, Address address); + // Fills in some heap data in an area from start to end (non-inclusive). The + // space id is used for the write barrier. The object_address is the address + // of the object we are writing into, or NULL if we are not writing into an + // object, i.e. if we are writing a series of tagged values that are not on + // the heap. + void ReadChunk( + Object** start, Object** end, int space, Address object_address); HeapObject* GetAddressFromStart(int space); inline HeapObject* GetAddressFromEnd(int space); Address Allocate(int space_number, Space* space, int size); @@ -474,14 +479,19 @@ class Serializer : public SerializerDeserializer { static void TooLateToEnableNow() { too_late_to_enable_now_ = true; } static bool enabled() { return serialization_enabled_; } SerializationAddressMapper* address_mapper() { return &address_mapper_; } -#ifdef DEBUG - virtual void Synchronize(const char* tag); -#endif + void PutRoot( + int index, HeapObject* object, HowToCode how, WhereToPoint where); protected: static const int kInvalidRootIndex = -1; - virtual int RootIndex(HeapObject* heap_object) = 0; + + int RootIndex(HeapObject* heap_object); virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) = 0; + intptr_t root_index_wave_front() { return root_index_wave_front_; } + void set_root_index_wave_front(intptr_t value) { + ASSERT(value >= root_index_wave_front_); + root_index_wave_front_ = value; + } class ObjectSerializer : public ObjectVisitor { public: @@ -497,7 +507,9 @@ class Serializer : public SerializerDeserializer { bytes_processed_so_far_(0) { } void Serialize(); void VisitPointers(Object** start, Object** end); + void VisitEmbeddedPointer(RelocInfo* target); void VisitExternalReferences(Address* start, Address* end); + void VisitExternalReference(RelocInfo* rinfo); void VisitCodeTarget(RelocInfo* target); void VisitCodeEntry(Address entry_address); void VisitGlobalPropertyCell(RelocInfo* rinfo); @@ -557,10 +569,12 @@ class Serializer : public SerializerDeserializer { static bool too_late_to_enable_now_; int large_object_total_; SerializationAddressMapper address_mapper_; + intptr_t root_index_wave_front_; friend class ObjectSerializer; friend class Deserializer; + private: DISALLOW_COPY_AND_ASSIGN(Serializer); }; @@ -571,6 +585,7 @@ class PartialSerializer : public Serializer { SnapshotByteSink* sink) : Serializer(sink), startup_serializer_(startup_snapshot_serializer) { + set_root_index_wave_front(Heap::kStrongRootListLength); } // Serialize the objects reachable from a single object pointer. @@ -580,7 +595,6 @@ class PartialSerializer : public Serializer { WhereToPoint where_to_point); protected: - virtual int RootIndex(HeapObject* o); virtual int PartialSnapshotCacheIndex(HeapObject* o); virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) { // Scripts should be referred only through shared function infos. We can't @@ -590,7 +604,7 @@ class PartialSerializer : public Serializer { ASSERT(!o->IsScript()); return o->IsString() || o->IsSharedFunctionInfo() || o->IsHeapNumber() || o->IsCode() || - o->IsSerializedScopeInfo() || + o->IsScopeInfo() || o->map() == HEAP->fixed_cow_array_map(); } @@ -605,14 +619,14 @@ class StartupSerializer : public Serializer { explicit StartupSerializer(SnapshotByteSink* sink) : Serializer(sink) { // Clear the cache of objects used by the partial snapshot. After the // strong roots have been serialized we can create a partial snapshot - // which will repopulate the cache with objects neede by that partial + // which will repopulate the cache with objects needed by that partial // snapshot. Isolate::Current()->set_serialize_partial_snapshot_cache_length(0); } // Serialize the current state of the heap. The order is: // 1) Strong references. // 2) Partial snapshot cache. - // 3) Weak references (eg the symbol table). + // 3) Weak references (e.g. the symbol table). virtual void SerializeStrongReferences(); virtual void SerializeObject(Object* o, HowToCode how_to_code, @@ -624,7 +638,6 @@ class StartupSerializer : public Serializer { } private: - virtual int RootIndex(HeapObject* o) { return kInvalidRootIndex; } virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) { return false; } diff --git a/deps/v8/src/spaces-inl.h b/deps/v8/src/spaces-inl.h index 35d7224099..d0cddebf78 100644 --- a/deps/v8/src/spaces-inl.h +++ b/deps/v8/src/spaces-inl.h @@ -1,4 +1,4 @@ -// Copyright 2006-2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -37,427 +37,286 @@ namespace internal { // ----------------------------------------------------------------------------- -// PageIterator +// Bitmap -bool PageIterator::has_next() { - return prev_page_ != stop_page_; -} - - -Page* PageIterator::next() { - ASSERT(has_next()); - prev_page_ = (prev_page_ == NULL) - ? space_->first_page_ - : prev_page_->next_page(); - return prev_page_; +void Bitmap::Clear(MemoryChunk* chunk) { + Bitmap* bitmap = chunk->markbits(); + for (int i = 0; i < bitmap->CellsCount(); i++) bitmap->cells()[i] = 0; + chunk->ResetLiveBytes(); } // ----------------------------------------------------------------------------- -// Page - -Page* Page::next_page() { - return heap_->isolate()->memory_allocator()->GetNextPage(this); -} - - -Address Page::AllocationTop() { - PagedSpace* owner = heap_->isolate()->memory_allocator()->PageOwner(this); - return owner->PageAllocationTop(this); -} - - -Address Page::AllocationWatermark() { - PagedSpace* owner = heap_->isolate()->memory_allocator()->PageOwner(this); - if (this == owner->AllocationTopPage()) { - return owner->top(); - } - return address() + AllocationWatermarkOffset(); -} - +// PageIterator -uint32_t Page::AllocationWatermarkOffset() { - return static_cast<uint32_t>((flags_ & kAllocationWatermarkOffsetMask) >> - kAllocationWatermarkOffsetShift); -} +PageIterator::PageIterator(PagedSpace* space) + : space_(space), + prev_page_(&space->anchor_), + next_page_(prev_page_->next_page()) { } -void Page::SetAllocationWatermark(Address allocation_watermark) { - if ((heap_->gc_state() == Heap::SCAVENGE) && IsWatermarkValid()) { - // When iterating intergenerational references during scavenge - // we might decide to promote an encountered young object. - // We will allocate a space for such an object and put it - // into the promotion queue to process it later. - // If space for object was allocated somewhere beyond allocation - // watermark this might cause garbage pointers to appear under allocation - // watermark. To avoid visiting them during dirty regions iteration - // which might be still in progress we store a valid allocation watermark - // value and mark this page as having an invalid watermark. - SetCachedAllocationWatermark(AllocationWatermark()); - InvalidateWatermark(true); - } - flags_ = (flags_ & kFlagsMask) | - Offset(allocation_watermark) << kAllocationWatermarkOffsetShift; - ASSERT(AllocationWatermarkOffset() - == static_cast<uint32_t>(Offset(allocation_watermark))); +bool PageIterator::has_next() { + return next_page_ != &space_->anchor_; } -void Page::SetCachedAllocationWatermark(Address allocation_watermark) { - mc_first_forwarded = allocation_watermark; +Page* PageIterator::next() { + ASSERT(has_next()); + prev_page_ = next_page_; + next_page_ = next_page_->next_page(); + return prev_page_; } -Address Page::CachedAllocationWatermark() { - return mc_first_forwarded; -} +// ----------------------------------------------------------------------------- +// NewSpacePageIterator -uint32_t Page::GetRegionMarks() { - return dirty_regions_; -} +NewSpacePageIterator::NewSpacePageIterator(NewSpace* space) + : prev_page_(NewSpacePage::FromAddress(space->ToSpaceStart())->prev_page()), + next_page_(NewSpacePage::FromAddress(space->ToSpaceStart())), + last_page_(NewSpacePage::FromLimit(space->ToSpaceEnd())) { } +NewSpacePageIterator::NewSpacePageIterator(SemiSpace* space) + : prev_page_(space->anchor()), + next_page_(prev_page_->next_page()), + last_page_(prev_page_->prev_page()) { } -void Page::SetRegionMarks(uint32_t marks) { - dirty_regions_ = marks; +NewSpacePageIterator::NewSpacePageIterator(Address start, Address limit) + : prev_page_(NewSpacePage::FromAddress(start)->prev_page()), + next_page_(NewSpacePage::FromAddress(start)), + last_page_(NewSpacePage::FromLimit(limit)) { + SemiSpace::AssertValidRange(start, limit); } -int Page::GetRegionNumberForAddress(Address addr) { - // Each page is divided into 256 byte regions. Each region has a corresponding - // dirty mark bit in the page header. Region can contain intergenerational - // references iff its dirty mark is set. - // A normal 8K page contains exactly 32 regions so all region marks fit - // into 32-bit integer field. To calculate a region number we just divide - // offset inside page by region size. - // A large page can contain more then 32 regions. But we want to avoid - // additional write barrier code for distinguishing between large and normal - // pages so we just ignore the fact that addr points into a large page and - // calculate region number as if addr pointed into a normal 8K page. This way - // we get a region number modulo 32 so for large pages several regions might - // be mapped to a single dirty mark. - ASSERT_PAGE_ALIGNED(this->address()); - STATIC_ASSERT((kPageAlignmentMask >> kRegionSizeLog2) < kBitsPerInt); - - // We are using masking with kPageAlignmentMask instead of Page::Offset() - // to get an offset to the beginning of 8K page containing addr not to the - // beginning of actual page which can be bigger then 8K. - intptr_t offset_inside_normal_page = OffsetFrom(addr) & kPageAlignmentMask; - return static_cast<int>(offset_inside_normal_page >> kRegionSizeLog2); +bool NewSpacePageIterator::has_next() { + return prev_page_ != last_page_; } -uint32_t Page::GetRegionMaskForAddress(Address addr) { - return 1 << GetRegionNumberForAddress(addr); +NewSpacePage* NewSpacePageIterator::next() { + ASSERT(has_next()); + prev_page_ = next_page_; + next_page_ = next_page_->next_page(); + return prev_page_; } -uint32_t Page::GetRegionMaskForSpan(Address start, int length_in_bytes) { - uint32_t result = 0; - static const intptr_t kRegionMask = (1 << kRegionSizeLog2) - 1; - if (length_in_bytes + (OffsetFrom(start) & kRegionMask) >= kPageSize) { - result = kAllRegionsDirtyMarks; - } else if (length_in_bytes > 0) { - int start_region = GetRegionNumberForAddress(start); - int end_region = - GetRegionNumberForAddress(start + length_in_bytes - kPointerSize); - uint32_t start_mask = (~0) << start_region; - uint32_t end_mask = ~((~1) << end_region); - result = start_mask & end_mask; - // if end_region < start_region, the mask is ored. - if (result == 0) result = start_mask | end_mask; - } -#ifdef DEBUG - if (FLAG_enable_slow_asserts) { - uint32_t expected = 0; - for (Address a = start; a < start + length_in_bytes; a += kPointerSize) { - expected |= GetRegionMaskForAddress(a); +// ----------------------------------------------------------------------------- +// HeapObjectIterator +HeapObject* HeapObjectIterator::FromCurrentPage() { + while (cur_addr_ != cur_end_) { + if (cur_addr_ == space_->top() && cur_addr_ != space_->limit()) { + cur_addr_ = space_->limit(); + continue; + } + HeapObject* obj = HeapObject::FromAddress(cur_addr_); + int obj_size = (size_func_ == NULL) ? obj->Size() : size_func_(obj); + cur_addr_ += obj_size; + ASSERT(cur_addr_ <= cur_end_); + if (!obj->IsFiller()) { + ASSERT_OBJECT_SIZE(obj_size); + return obj; } - ASSERT(expected == result); } -#endif - return result; + return NULL; } -void Page::MarkRegionDirty(Address address) { - SetRegionMarks(GetRegionMarks() | GetRegionMaskForAddress(address)); -} +// ----------------------------------------------------------------------------- +// MemoryAllocator +#ifdef ENABLE_HEAP_PROTECTION -bool Page::IsRegionDirty(Address address) { - return GetRegionMarks() & GetRegionMaskForAddress(address); +void MemoryAllocator::Protect(Address start, size_t size) { + OS::Protect(start, size); } -void Page::ClearRegionMarks(Address start, Address end, bool reaches_limit) { - int rstart = GetRegionNumberForAddress(start); - int rend = GetRegionNumberForAddress(end); - - if (reaches_limit) { - end += 1; - } - - if ((rend - rstart) == 0) { - return; - } - - uint32_t bitmask = 0; - - if ((OffsetFrom(start) & kRegionAlignmentMask) == 0 - || (start == ObjectAreaStart())) { - // First region is fully covered - bitmask = 1 << rstart; - } +void MemoryAllocator::Unprotect(Address start, + size_t size, + Executability executable) { + OS::Unprotect(start, size, executable); +} - while (++rstart < rend) { - bitmask |= 1 << rstart; - } - if (bitmask) { - SetRegionMarks(GetRegionMarks() & ~bitmask); - } +void MemoryAllocator::ProtectChunkFromPage(Page* page) { + int id = GetChunkId(page); + OS::Protect(chunks_[id].address(), chunks_[id].size()); } -void Page::FlipMeaningOfInvalidatedWatermarkFlag(Heap* heap) { - heap->page_watermark_invalidated_mark_ ^= 1 << WATERMARK_INVALIDATED; +void MemoryAllocator::UnprotectChunkFromPage(Page* page) { + int id = GetChunkId(page); + OS::Unprotect(chunks_[id].address(), chunks_[id].size(), + chunks_[id].owner()->executable() == EXECUTABLE); } +#endif -bool Page::IsWatermarkValid() { - return (flags_ & (1 << WATERMARK_INVALIDATED)) != - heap_->page_watermark_invalidated_mark_; -} +// -------------------------------------------------------------------------- +// PagedSpace +Page* Page::Initialize(Heap* heap, + MemoryChunk* chunk, + Executability executable, + PagedSpace* owner) { + Page* page = reinterpret_cast<Page*>(chunk); + ASSERT(chunk->size() == static_cast<size_t>(kPageSize)); + ASSERT(chunk->owner() == owner); + owner->IncreaseCapacity(Page::kObjectAreaSize); + owner->Free(page->ObjectAreaStart(), + static_cast<int>(page->ObjectAreaEnd() - + page->ObjectAreaStart())); -void Page::InvalidateWatermark(bool value) { - if (value) { - flags_ = (flags_ & ~(1 << WATERMARK_INVALIDATED)) | - heap_->page_watermark_invalidated_mark_; - } else { - flags_ = - (flags_ & ~(1 << WATERMARK_INVALIDATED)) | - (heap_->page_watermark_invalidated_mark_ ^ - (1 << WATERMARK_INVALIDATED)); - } + heap->incremental_marking()->SetOldSpacePageFlags(chunk); - ASSERT(IsWatermarkValid() == !value); + return page; } -bool Page::GetPageFlag(PageFlag flag) { - return (flags_ & static_cast<intptr_t>(1 << flag)) != 0; +bool PagedSpace::Contains(Address addr) { + Page* p = Page::FromAddress(addr); + if (!p->is_valid()) return false; + return p->owner() == this; } -void Page::SetPageFlag(PageFlag flag, bool value) { - if (value) { - flags_ |= static_cast<intptr_t>(1 << flag); +void MemoryChunk::set_scan_on_scavenge(bool scan) { + if (scan) { + if (!scan_on_scavenge()) heap_->increment_scan_on_scavenge_pages(); + SetFlag(SCAN_ON_SCAVENGE); } else { - flags_ &= ~static_cast<intptr_t>(1 << flag); + if (scan_on_scavenge()) heap_->decrement_scan_on_scavenge_pages(); + ClearFlag(SCAN_ON_SCAVENGE); } -} - - -void Page::ClearPageFlags() { - flags_ = 0; -} - - -void Page::ClearGCFields() { - InvalidateWatermark(true); - SetAllocationWatermark(ObjectAreaStart()); - if (heap_->gc_state() == Heap::SCAVENGE) { - SetCachedAllocationWatermark(ObjectAreaStart()); + heap_->incremental_marking()->SetOldSpacePageFlags(this); +} + + +MemoryChunk* MemoryChunk::FromAnyPointerAddress(Address addr) { + MemoryChunk* maybe = reinterpret_cast<MemoryChunk*>( + OffsetFrom(addr) & ~Page::kPageAlignmentMask); + if (maybe->owner() != NULL) return maybe; + LargeObjectIterator iterator(HEAP->lo_space()); + for (HeapObject* o = iterator.Next(); o != NULL; o = iterator.Next()) { + // Fixed arrays are the only pointer-containing objects in large object + // space. + if (o->IsFixedArray()) { + MemoryChunk* chunk = MemoryChunk::FromAddress(o->address()); + if (chunk->Contains(addr)) { + return chunk; + } + } } - SetRegionMarks(kAllRegionsCleanMarks); + UNREACHABLE(); + return NULL; } -bool Page::WasInUseBeforeMC() { - return GetPageFlag(WAS_IN_USE_BEFORE_MC); -} +PointerChunkIterator::PointerChunkIterator(Heap* heap) + : state_(kOldPointerState), + old_pointer_iterator_(heap->old_pointer_space()), + map_iterator_(heap->map_space()), + lo_iterator_(heap->lo_space()) { } -void Page::SetWasInUseBeforeMC(bool was_in_use) { - SetPageFlag(WAS_IN_USE_BEFORE_MC, was_in_use); -} - - -bool Page::IsLargeObjectPage() { - return !GetPageFlag(IS_NORMAL_PAGE); -} - - -void Page::SetIsLargeObjectPage(bool is_large_object_page) { - SetPageFlag(IS_NORMAL_PAGE, !is_large_object_page); -} - -Executability Page::PageExecutability() { - return GetPageFlag(IS_EXECUTABLE) ? EXECUTABLE : NOT_EXECUTABLE; -} - - -void Page::SetPageExecutability(Executability executable) { - SetPageFlag(IS_EXECUTABLE, executable == EXECUTABLE); -} - - -// ----------------------------------------------------------------------------- -// MemoryAllocator - -void MemoryAllocator::ChunkInfo::init(Address a, size_t s, PagedSpace* o) { - address_ = a; - size_ = s; - owner_ = o; - executable_ = (o == NULL) ? NOT_EXECUTABLE : o->executable(); - owner_identity_ = (o == NULL) ? FIRST_SPACE : o->identity(); -} - - -bool MemoryAllocator::IsValidChunk(int chunk_id) { - if (!IsValidChunkId(chunk_id)) return false; - - ChunkInfo& c = chunks_[chunk_id]; - return (c.address() != NULL) && (c.size() != 0) && (c.owner() != NULL); -} - - -bool MemoryAllocator::IsValidChunkId(int chunk_id) { - return (0 <= chunk_id) && (chunk_id < max_nof_chunks_); -} - - -bool MemoryAllocator::IsPageInSpace(Page* p, PagedSpace* space) { - ASSERT(p->is_valid()); - - int chunk_id = GetChunkId(p); - if (!IsValidChunkId(chunk_id)) return false; - - ChunkInfo& c = chunks_[chunk_id]; - return (c.address() <= p->address()) && - (p->address() < c.address() + c.size()) && - (space == c.owner()); -} - - -Page* MemoryAllocator::GetNextPage(Page* p) { - ASSERT(p->is_valid()); - intptr_t raw_addr = p->opaque_header & ~Page::kPageAlignmentMask; - return Page::FromAddress(AddressFrom<Address>(raw_addr)); -} - - -int MemoryAllocator::GetChunkId(Page* p) { - ASSERT(p->is_valid()); - return static_cast<int>(p->opaque_header & Page::kPageAlignmentMask); -} - - -void MemoryAllocator::SetNextPage(Page* prev, Page* next) { - ASSERT(prev->is_valid()); - int chunk_id = GetChunkId(prev); - ASSERT_PAGE_ALIGNED(next->address()); - prev->opaque_header = OffsetFrom(next->address()) | chunk_id; +Page* Page::next_page() { + ASSERT(next_chunk()->owner() == owner()); + return static_cast<Page*>(next_chunk()); } -PagedSpace* MemoryAllocator::PageOwner(Page* page) { - int chunk_id = GetChunkId(page); - ASSERT(IsValidChunk(chunk_id)); - return chunks_[chunk_id].owner(); +Page* Page::prev_page() { + ASSERT(prev_chunk()->owner() == owner()); + return static_cast<Page*>(prev_chunk()); } -bool MemoryAllocator::InInitialChunk(Address address) { - if (initial_chunk_ == NULL) return false; - - Address start = static_cast<Address>(initial_chunk_->address()); - return (start <= address) && (address < start + initial_chunk_->size()); +void Page::set_next_page(Page* page) { + ASSERT(page->owner() == owner()); + set_next_chunk(page); } -// -------------------------------------------------------------------------- -// PagedSpace - -bool PagedSpace::Contains(Address addr) { - Page* p = Page::FromAddress(addr); - if (!p->is_valid()) return false; - return heap()->isolate()->memory_allocator()->IsPageInSpace(p, this); +void Page::set_prev_page(Page* page) { + ASSERT(page->owner() == owner()); + set_prev_chunk(page); } // Try linear allocation in the page of alloc_info's allocation top. Does -// not contain slow case logic (eg, move to the next page or try free list +// not contain slow case logic (e.g. move to the next page or try free list // allocation) so it can be used by all the allocation functions and for all // the paged spaces. -HeapObject* PagedSpace::AllocateLinearly(AllocationInfo* alloc_info, - int size_in_bytes) { - Address current_top = alloc_info->top; +HeapObject* PagedSpace::AllocateLinearly(int size_in_bytes) { + Address current_top = allocation_info_.top; Address new_top = current_top + size_in_bytes; - if (new_top > alloc_info->limit) return NULL; + if (new_top > allocation_info_.limit) return NULL; - alloc_info->top = new_top; - ASSERT(alloc_info->VerifyPagedAllocation()); - accounting_stats_.AllocateBytes(size_in_bytes); + allocation_info_.top = new_top; return HeapObject::FromAddress(current_top); } // Raw allocation. MaybeObject* PagedSpace::AllocateRaw(int size_in_bytes) { - ASSERT(HasBeenSetup()); - ASSERT_OBJECT_SIZE(size_in_bytes); - HeapObject* object = AllocateLinearly(&allocation_info_, size_in_bytes); - if (object != NULL) return object; + HeapObject* object = AllocateLinearly(size_in_bytes); + if (object != NULL) { + if (identity() == CODE_SPACE) { + SkipList::Update(object->address(), size_in_bytes); + } + return object; + } + + object = free_list_.Allocate(size_in_bytes); + if (object != NULL) { + if (identity() == CODE_SPACE) { + SkipList::Update(object->address(), size_in_bytes); + } + return object; + } object = SlowAllocateRaw(size_in_bytes); - if (object != NULL) return object; + if (object != NULL) { + if (identity() == CODE_SPACE) { + SkipList::Update(object->address(), size_in_bytes); + } + return object; + } return Failure::RetryAfterGC(identity()); } -// Reallocating (and promoting) objects during a compacting collection. -MaybeObject* PagedSpace::MCAllocateRaw(int size_in_bytes) { - ASSERT(HasBeenSetup()); - ASSERT_OBJECT_SIZE(size_in_bytes); - HeapObject* object = AllocateLinearly(&mc_forwarding_info_, size_in_bytes); - if (object != NULL) return object; - - object = SlowMCAllocateRaw(size_in_bytes); - if (object != NULL) return object; +// ----------------------------------------------------------------------------- +// NewSpace - return Failure::RetryAfterGC(identity()); -} +MaybeObject* NewSpace::AllocateRaw(int size_in_bytes) { + Address old_top = allocation_info_.top; + if (allocation_info_.limit - old_top < size_in_bytes) { + return SlowAllocateRaw(size_in_bytes); + } -// ----------------------------------------------------------------------------- -// NewSpace + Object* obj = HeapObject::FromAddress(allocation_info_.top); + allocation_info_.top += size_in_bytes; + ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); -MaybeObject* NewSpace::AllocateRawInternal(int size_in_bytes, - AllocationInfo* alloc_info) { - Address new_top = alloc_info->top + size_in_bytes; - if (new_top > alloc_info->limit) return Failure::RetryAfterGC(); - - Object* obj = HeapObject::FromAddress(alloc_info->top); - alloc_info->top = new_top; -#ifdef DEBUG - SemiSpace* space = - (alloc_info == &allocation_info_) ? &to_space_ : &from_space_; - ASSERT(space->low() <= alloc_info->top - && alloc_info->top <= space->high() - && alloc_info->limit == space->high()); -#endif return obj; } +LargePage* LargePage::Initialize(Heap* heap, MemoryChunk* chunk) { + heap->incremental_marking()->SetOldSpacePageFlags(chunk); + return static_cast<LargePage*>(chunk); +} + + intptr_t LargeObjectSpace::Available() { - return LargeObjectChunk::ObjectSizeFor( - heap()->isolate()->memory_allocator()->Available()); + return ObjectSizeFor(heap()->isolate()->memory_allocator()->Available()); } @@ -467,16 +326,23 @@ void NewSpace::ShrinkStringAtAllocationBoundary(String* string, int length) { ASSERT(string->IsSeqString()); ASSERT(string->address() + StringType::SizeFor(string->length()) == allocation_info_.top); + Address old_top = allocation_info_.top; allocation_info_.top = string->address() + StringType::SizeFor(length); string->set_length(length); + if (Marking::IsBlack(Marking::MarkBitFrom(string))) { + int delta = static_cast<int>(old_top - allocation_info_.top); + MemoryChunk::IncrementLiveBytesFromMutator(string->address(), -delta); + } } bool FreeListNode::IsFreeListNode(HeapObject* object) { - return object->map() == HEAP->raw_unchecked_byte_array_map() - || object->map() == HEAP->raw_unchecked_one_pointer_filler_map() - || object->map() == HEAP->raw_unchecked_two_pointer_filler_map(); + Map* map = object->map(); + Heap* heap = object->GetHeap(); + return map == heap->raw_unchecked_free_space_map() + || map == heap->raw_unchecked_one_pointer_filler_map() + || map == heap->raw_unchecked_two_pointer_filler_map(); } } } // namespace v8::internal diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc index 97c6d2ac19..05c5876fdf 100644 --- a/deps/v8/src/spaces.cc +++ b/deps/v8/src/spaces.cc @@ -35,112 +35,87 @@ namespace v8 { namespace internal { -// For contiguous spaces, top should be in the space (or at the end) and limit -// should be the end of the space. -#define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \ - ASSERT((space).low() <= (info).top \ - && (info).top <= (space).high() \ - && (info).limit == (space).high()) // ---------------------------------------------------------------------------- // HeapObjectIterator HeapObjectIterator::HeapObjectIterator(PagedSpace* space) { - Initialize(space->bottom(), space->top(), NULL); + // You can't actually iterate over the anchor page. It is not a real page, + // just an anchor for the double linked page list. Initialize as if we have + // reached the end of the anchor page, then the first iteration will move on + // to the first page. + Initialize(space, + NULL, + NULL, + kAllPagesInSpace, + NULL); } HeapObjectIterator::HeapObjectIterator(PagedSpace* space, HeapObjectCallback size_func) { - Initialize(space->bottom(), space->top(), size_func); -} - - -HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start) { - Initialize(start, space->top(), NULL); -} - - -HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start, - HeapObjectCallback size_func) { - Initialize(start, space->top(), size_func); + // You can't actually iterate over the anchor page. It is not a real page, + // just an anchor for the double linked page list. Initialize the current + // address and end as NULL, then the first iteration will move on + // to the first page. + Initialize(space, + NULL, + NULL, + kAllPagesInSpace, + size_func); } HeapObjectIterator::HeapObjectIterator(Page* page, HeapObjectCallback size_func) { - Initialize(page->ObjectAreaStart(), page->AllocationTop(), size_func); -} - - -void HeapObjectIterator::Initialize(Address cur, Address end, + Space* owner = page->owner(); + ASSERT(owner == HEAP->old_pointer_space() || + owner == HEAP->old_data_space() || + owner == HEAP->map_space() || + owner == HEAP->cell_space() || + owner == HEAP->code_space()); + Initialize(reinterpret_cast<PagedSpace*>(owner), + page->ObjectAreaStart(), + page->ObjectAreaEnd(), + kOnePageOnly, + size_func); + ASSERT(page->WasSweptPrecisely()); +} + + +void HeapObjectIterator::Initialize(PagedSpace* space, + Address cur, Address end, + HeapObjectIterator::PageMode mode, HeapObjectCallback size_f) { + // Check that we actually can iterate this space. + ASSERT(!space->was_swept_conservatively()); + + space_ = space; cur_addr_ = cur; - end_addr_ = end; - end_page_ = Page::FromAllocationTop(end); + cur_end_ = end; + page_mode_ = mode; size_func_ = size_f; - Page* p = Page::FromAllocationTop(cur_addr_); - cur_limit_ = (p == end_page_) ? end_addr_ : p->AllocationTop(); - -#ifdef DEBUG - Verify(); -#endif } -HeapObject* HeapObjectIterator::FromNextPage() { - if (cur_addr_ == end_addr_) return NULL; - - Page* cur_page = Page::FromAllocationTop(cur_addr_); +// We have hit the end of the page and should advance to the next block of +// objects. This happens at the end of the page. +bool HeapObjectIterator::AdvanceToNextPage() { + ASSERT(cur_addr_ == cur_end_); + if (page_mode_ == kOnePageOnly) return false; + Page* cur_page; + if (cur_addr_ == NULL) { + cur_page = space_->anchor(); + } else { + cur_page = Page::FromAddress(cur_addr_ - 1); + ASSERT(cur_addr_ == cur_page->ObjectAreaEnd()); + } cur_page = cur_page->next_page(); - ASSERT(cur_page->is_valid()); - + if (cur_page == space_->anchor()) return false; cur_addr_ = cur_page->ObjectAreaStart(); - cur_limit_ = (cur_page == end_page_) ? end_addr_ : cur_page->AllocationTop(); - - if (cur_addr_ == end_addr_) return NULL; - ASSERT(cur_addr_ < cur_limit_); -#ifdef DEBUG - Verify(); -#endif - return FromCurrentPage(); -} - - -#ifdef DEBUG -void HeapObjectIterator::Verify() { - Page* p = Page::FromAllocationTop(cur_addr_); - ASSERT(p == Page::FromAllocationTop(cur_limit_)); - ASSERT(p->Offset(cur_addr_) <= p->Offset(cur_limit_)); -} -#endif - - -// ----------------------------------------------------------------------------- -// PageIterator - -PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { - prev_page_ = NULL; - switch (mode) { - case PAGES_IN_USE: - stop_page_ = space->AllocationTopPage(); - break; - case PAGES_USED_BY_MC: - stop_page_ = space->MCRelocationTopPage(); - break; - case ALL_PAGES: -#ifdef DEBUG - // Verify that the cached last page in the space is actually the - // last page. - for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { - if (!p->next_page()->is_valid()) { - ASSERT(space->last_page_ == p); - } - } -#endif - stop_page_ = space->last_page_; - break; - } + cur_end_ = cur_page->ObjectAreaEnd(); + ASSERT(cur_page->WasSweptPrecisely()); + return true; } @@ -157,7 +132,7 @@ CodeRange::CodeRange(Isolate* isolate) } -bool CodeRange::Setup(const size_t requested) { +bool CodeRange::SetUp(const size_t requested) { ASSERT(code_range_ == NULL); code_range_ = new VirtualMemory(requested); @@ -171,7 +146,12 @@ bool CodeRange::Setup(const size_t requested) { // We are sure that we have mapped a block of requested addresses. ASSERT(code_range_->size() == requested); LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested)); - allocation_list_.Add(FreeBlock(code_range_->address(), code_range_->size())); + Address base = reinterpret_cast<Address>(code_range_->address()); + Address aligned_base = + RoundUp(reinterpret_cast<Address>(code_range_->address()), + MemoryChunk::kAlignment); + size_t size = code_range_->size() - (aligned_base - base); + allocation_list_.Add(FreeBlock(aligned_base, size)); current_allocation_block_index_ = 0; return true; } @@ -228,7 +208,8 @@ void CodeRange::GetNextAllocationBlock(size_t requested) { -void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { +Address CodeRange::AllocateRawMemory(const size_t requested, + size_t* allocated) { ASSERT(current_allocation_block_index_ < allocation_list_.length()); if (requested > allocation_list_[current_allocation_block_index_].size) { // Find an allocation block large enough. This function call may @@ -236,13 +217,16 @@ void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { GetNextAllocationBlock(requested); } // Commit the requested memory at the start of the current allocation block. - *allocated = RoundUp(requested, Page::kPageSize); + size_t aligned_requested = RoundUp(requested, MemoryChunk::kAlignment); FreeBlock current = allocation_list_[current_allocation_block_index_]; - if (*allocated >= current.size - Page::kPageSize) { + if (aligned_requested >= (current.size - Page::kPageSize)) { // Don't leave a small free block, useless for a large object or chunk. *allocated = current.size; + } else { + *allocated = aligned_requested; } ASSERT(*allocated <= current.size); + ASSERT(IsAddressAligned(current.start, MemoryChunk::kAlignment)); if (!code_range_->Commit(current.start, *allocated, true)) { *allocated = 0; return NULL; @@ -256,7 +240,8 @@ void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { } -void CodeRange::FreeRawMemory(void* address, size_t length) { +void CodeRange::FreeRawMemory(Address address, size_t length) { + ASSERT(IsAddressAligned(address, MemoryChunk::kAlignment)); free_list_.Add(FreeBlock(address, length)); code_range_->Uncommit(address, length); } @@ -274,306 +259,317 @@ void CodeRange::TearDown() { // MemoryAllocator // -// 270 is an estimate based on the static default heap size of a pair of 256K -// semispaces and a 64M old generation. -const int kEstimatedNumberOfChunks = 270; - - MemoryAllocator::MemoryAllocator(Isolate* isolate) : isolate_(isolate), capacity_(0), capacity_executable_(0), size_(0), - size_executable_(0), - initial_chunk_(NULL), - chunks_(kEstimatedNumberOfChunks), - free_chunk_ids_(kEstimatedNumberOfChunks), - max_nof_chunks_(0), - top_(0) { -} - - -void MemoryAllocator::Push(int free_chunk_id) { - ASSERT(max_nof_chunks_ > 0); - ASSERT(top_ < max_nof_chunks_); - free_chunk_ids_[top_++] = free_chunk_id; -} - - -int MemoryAllocator::Pop() { - ASSERT(top_ > 0); - return free_chunk_ids_[--top_]; + size_executable_(0) { } -bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { +bool MemoryAllocator::SetUp(intptr_t capacity, intptr_t capacity_executable) { capacity_ = RoundUp(capacity, Page::kPageSize); capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); ASSERT_GE(capacity_, capacity_executable_); - // Over-estimate the size of chunks_ array. It assumes the expansion of old - // space is always in the unit of a chunk (kChunkSize) except the last - // expansion. - // - // Due to alignment, allocated space might be one page less than required - // number (kPagesPerChunk) of pages for old spaces. - // - // Reserve two chunk ids for semispaces, one for map space, one for old - // space, and one for code space. - max_nof_chunks_ = - static_cast<int>((capacity_ / (kChunkSize - Page::kPageSize))) + 5; - if (max_nof_chunks_ > kMaxNofChunks) return false; - size_ = 0; size_executable_ = 0; - ChunkInfo info; // uninitialized element. - for (int i = max_nof_chunks_ - 1; i >= 0; i--) { - chunks_.Add(info); - free_chunk_ids_.Add(i); - } - top_ = max_nof_chunks_; + return true; } void MemoryAllocator::TearDown() { - for (int i = 0; i < max_nof_chunks_; i++) { - if (chunks_[i].address() != NULL) DeleteChunk(i); - } - chunks_.Clear(); - free_chunk_ids_.Clear(); - - if (initial_chunk_ != NULL) { - LOG(isolate_, DeleteEvent("InitialChunk", initial_chunk_->address())); - delete initial_chunk_; - initial_chunk_ = NULL; - } - - ASSERT(top_ == max_nof_chunks_); // all chunks are free - top_ = 0; + // Check that spaces were torn down before MemoryAllocator. + ASSERT(size_ == 0); + // TODO(gc) this will be true again when we fix FreeMemory. + // ASSERT(size_executable_ == 0); capacity_ = 0; capacity_executable_ = 0; - size_ = 0; - max_nof_chunks_ = 0; } -void* MemoryAllocator::AllocateRawMemory(const size_t requested, - size_t* allocated, - Executability executable) { - if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { - return NULL; - } +void MemoryAllocator::FreeMemory(VirtualMemory* reservation, + Executability executable) { + // TODO(gc) make code_range part of memory allocator? + ASSERT(reservation->IsReserved()); + size_t size = reservation->size(); + ASSERT(size_ >= size); + size_ -= size; + + isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); - void* mem; if (executable == EXECUTABLE) { - // Check executable memory limit. - if (size_executable_ + requested > - static_cast<size_t>(capacity_executable_)) { - LOG(isolate_, - StringEvent("MemoryAllocator::AllocateRawMemory", - "V8 Executable Allocation capacity exceeded")); - return NULL; - } - // Allocate executable memory either from code range or from the - // OS. - if (isolate_->code_range()->exists()) { - mem = isolate_->code_range()->AllocateRawMemory(requested, allocated); - } else { - mem = OS::Allocate(requested, allocated, true); - } - // Update executable memory size. - size_executable_ += static_cast<int>(*allocated); - } else { - mem = OS::Allocate(requested, allocated, false); + ASSERT(size_executable_ >= size); + size_executable_ -= size; } - int alloced = static_cast<int>(*allocated); - size_ += alloced; - -#ifdef DEBUG - ZapBlock(reinterpret_cast<Address>(mem), alloced); -#endif - isolate_->counters()->memory_allocated()->Increment(alloced); - return mem; + // Code which is part of the code-range does not have its own VirtualMemory. + ASSERT(!isolate_->code_range()->contains( + static_cast<Address>(reservation->address()))); + ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists()); + reservation->Release(); } -void MemoryAllocator::FreeRawMemory(void* mem, - size_t length, - Executability executable) { -#ifdef DEBUG - // Do not try to zap the guard page. - size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; - ZapBlock(reinterpret_cast<Address>(mem) + guard_size, length - guard_size); -#endif - if (isolate_->code_range()->contains(static_cast<Address>(mem))) { - isolate_->code_range()->FreeRawMemory(mem, length); +void MemoryAllocator::FreeMemory(Address base, + size_t size, + Executability executable) { + // TODO(gc) make code_range part of memory allocator? + ASSERT(size_ >= size); + size_ -= size; + + isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); + + if (executable == EXECUTABLE) { + ASSERT(size_executable_ >= size); + size_executable_ -= size; + } + if (isolate_->code_range()->contains(static_cast<Address>(base))) { + ASSERT(executable == EXECUTABLE); + isolate_->code_range()->FreeRawMemory(base, size); } else { - OS::Free(mem, length); + ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists()); + bool result = VirtualMemory::ReleaseRegion(base, size); + USE(result); + ASSERT(result); } - isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(length)); - size_ -= static_cast<int>(length); - if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); +} + - ASSERT(size_ >= 0); - ASSERT(size_executable_ >= 0); +Address MemoryAllocator::ReserveAlignedMemory(size_t size, + size_t alignment, + VirtualMemory* controller) { + VirtualMemory reservation(size, alignment); + + if (!reservation.IsReserved()) return NULL; + size_ += reservation.size(); + Address base = RoundUp(static_cast<Address>(reservation.address()), + alignment); + controller->TakeControl(&reservation); + return base; } -void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, - AllocationAction action, - size_t size) { - for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { - MemoryAllocationCallbackRegistration registration = - memory_allocation_callbacks_[i]; - if ((registration.space & space) == space && - (registration.action & action) == action) - registration.callback(space, action, static_cast<int>(size)); +Address MemoryAllocator::AllocateAlignedMemory(size_t size, + size_t alignment, + Executability executable, + VirtualMemory* controller) { + VirtualMemory reservation; + Address base = ReserveAlignedMemory(size, alignment, &reservation); + if (base == NULL) return NULL; + if (!reservation.Commit(base, + size, + executable == EXECUTABLE)) { + return NULL; } + controller->TakeControl(&reservation); + return base; } -bool MemoryAllocator::MemoryAllocationCallbackRegistered( - MemoryAllocationCallback callback) { - for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { - if (memory_allocation_callbacks_[i].callback == callback) return true; - } - return false; +void Page::InitializeAsAnchor(PagedSpace* owner) { + set_owner(owner); + set_prev_page(this); + set_next_page(this); } -void MemoryAllocator::AddMemoryAllocationCallback( - MemoryAllocationCallback callback, - ObjectSpace space, - AllocationAction action) { - ASSERT(callback != NULL); - MemoryAllocationCallbackRegistration registration(callback, space, action); - ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); - return memory_allocation_callbacks_.Add(registration); +NewSpacePage* NewSpacePage::Initialize(Heap* heap, + Address start, + SemiSpace* semi_space) { + MemoryChunk* chunk = MemoryChunk::Initialize(heap, + start, + Page::kPageSize, + NOT_EXECUTABLE, + semi_space); + chunk->set_next_chunk(NULL); + chunk->set_prev_chunk(NULL); + chunk->initialize_scan_on_scavenge(true); + bool in_to_space = (semi_space->id() != kFromSpace); + chunk->SetFlag(in_to_space ? MemoryChunk::IN_TO_SPACE + : MemoryChunk::IN_FROM_SPACE); + ASSERT(!chunk->IsFlagSet(in_to_space ? MemoryChunk::IN_FROM_SPACE + : MemoryChunk::IN_TO_SPACE)); + NewSpacePage* page = static_cast<NewSpacePage*>(chunk); + heap->incremental_marking()->SetNewSpacePageFlags(page); + return page; } -void MemoryAllocator::RemoveMemoryAllocationCallback( - MemoryAllocationCallback callback) { - ASSERT(callback != NULL); - for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { - if (memory_allocation_callbacks_[i].callback == callback) { - memory_allocation_callbacks_.Remove(i); - return; - } - } - UNREACHABLE(); +void NewSpacePage::InitializeAsAnchor(SemiSpace* semi_space) { + set_owner(semi_space); + set_next_chunk(this); + set_prev_chunk(this); + // Flags marks this invalid page as not being in new-space. + // All real new-space pages will be in new-space. + SetFlags(0, ~0); } -void* MemoryAllocator::ReserveInitialChunk(const size_t requested) { - ASSERT(initial_chunk_ == NULL); - initial_chunk_ = new VirtualMemory(requested); - CHECK(initial_chunk_ != NULL); - if (!initial_chunk_->IsReserved()) { - delete initial_chunk_; - initial_chunk_ = NULL; - return NULL; - } +MemoryChunk* MemoryChunk::Initialize(Heap* heap, + Address base, + size_t size, + Executability executable, + Space* owner) { + MemoryChunk* chunk = FromAddress(base); - // We are sure that we have mapped a block of requested addresses. - ASSERT(initial_chunk_->size() == requested); - LOG(isolate_, - NewEvent("InitialChunk", initial_chunk_->address(), requested)); - size_ += static_cast<int>(requested); - return initial_chunk_->address(); -} + ASSERT(base == chunk->address()); + + chunk->heap_ = heap; + chunk->size_ = size; + chunk->flags_ = 0; + chunk->set_owner(owner); + chunk->InitializeReservedMemory(); + chunk->slots_buffer_ = NULL; + chunk->skip_list_ = NULL; + chunk->ResetLiveBytes(); + Bitmap::Clear(chunk); + chunk->initialize_scan_on_scavenge(false); + chunk->SetFlag(WAS_SWEPT_PRECISELY); + ASSERT(OFFSET_OF(MemoryChunk, flags_) == kFlagsOffset); + ASSERT(OFFSET_OF(MemoryChunk, live_byte_count_) == kLiveBytesOffset); -static int PagesInChunk(Address start, size_t size) { - // The first page starts on the first page-aligned address from start onward - // and the last page ends on the last page-aligned address before - // start+size. Page::kPageSize is a power of two so we can divide by - // shifting. - return static_cast<int>((RoundDown(start + size, Page::kPageSize) - - RoundUp(start, Page::kPageSize)) >> kPageSizeBits); + if (executable == EXECUTABLE) chunk->SetFlag(IS_EXECUTABLE); + + if (owner == heap->old_data_space()) chunk->SetFlag(CONTAINS_ONLY_DATA); + + return chunk; } -Page* MemoryAllocator::AllocatePages(int requested_pages, - int* allocated_pages, - PagedSpace* owner) { - if (requested_pages <= 0) return Page::FromAddress(NULL); - size_t chunk_size = requested_pages * Page::kPageSize; +void MemoryChunk::InsertAfter(MemoryChunk* other) { + next_chunk_ = other->next_chunk_; + prev_chunk_ = other; + other->next_chunk_->prev_chunk_ = this; + other->next_chunk_ = this; +} - void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable()); - if (chunk == NULL) return Page::FromAddress(NULL); - LOG(isolate_, NewEvent("PagedChunk", chunk, chunk_size)); - *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size); +void MemoryChunk::Unlink() { + if (!InNewSpace() && IsFlagSet(SCAN_ON_SCAVENGE)) { + heap_->decrement_scan_on_scavenge_pages(); + ClearFlag(SCAN_ON_SCAVENGE); + } + next_chunk_->prev_chunk_ = prev_chunk_; + prev_chunk_->next_chunk_ = next_chunk_; + prev_chunk_ = NULL; + next_chunk_ = NULL; +} - // We may 'lose' a page due to alignment. - ASSERT(*allocated_pages >= kPagesPerChunk - 1); - size_t guard_size = (owner->executable() == EXECUTABLE) ? Page::kPageSize : 0; +MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size, + Executability executable, + Space* owner) { + size_t chunk_size = MemoryChunk::kObjectStartOffset + body_size; + Heap* heap = isolate_->heap(); + Address base = NULL; + VirtualMemory reservation; + if (executable == EXECUTABLE) { + // Check executable memory limit. + if (size_executable_ + chunk_size > capacity_executable_) { + LOG(isolate_, + StringEvent("MemoryAllocator::AllocateRawMemory", + "V8 Executable Allocation capacity exceeded")); + return NULL; + } + + // Allocate executable memory either from code range or from the + // OS. + if (isolate_->code_range()->exists()) { + base = isolate_->code_range()->AllocateRawMemory(chunk_size, &chunk_size); + ASSERT(IsAligned(reinterpret_cast<intptr_t>(base), + MemoryChunk::kAlignment)); + if (base == NULL) return NULL; + size_ += chunk_size; + // Update executable memory size. + size_executable_ += chunk_size; + } else { + base = AllocateAlignedMemory(chunk_size, + MemoryChunk::kAlignment, + executable, + &reservation); + if (base == NULL) return NULL; + // Update executable memory size. + size_executable_ += reservation.size(); + } + } else { + base = AllocateAlignedMemory(chunk_size, + MemoryChunk::kAlignment, + executable, + &reservation); - // Check that we got at least one page that we can use. - if (*allocated_pages <= ((guard_size != 0) ? 1 : 0)) { - FreeRawMemory(chunk, - chunk_size, - owner->executable()); - LOG(isolate_, DeleteEvent("PagedChunk", chunk)); - return Page::FromAddress(NULL); + if (base == NULL) return NULL; } - if (guard_size != 0) { - OS::Guard(chunk, guard_size); - chunk_size -= guard_size; - chunk = static_cast<Address>(chunk) + guard_size; - --*allocated_pages; +#ifdef DEBUG + ZapBlock(base, chunk_size); +#endif + isolate_->counters()->memory_allocated()-> + Increment(static_cast<int>(chunk_size)); + + LOG(isolate_, NewEvent("MemoryChunk", base, chunk_size)); + if (owner != NULL) { + ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); + PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); } - int chunk_id = Pop(); - chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner); + MemoryChunk* result = MemoryChunk::Initialize(heap, + base, + chunk_size, + executable, + owner); + result->set_reserved_memory(&reservation); + return result; +} + + +Page* MemoryAllocator::AllocatePage(PagedSpace* owner, + Executability executable) { + MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner); - ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); - PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); - Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner); + if (chunk == NULL) return NULL; - return new_pages; + return Page::Initialize(isolate_->heap(), chunk, executable, owner); } -Page* MemoryAllocator::CommitPages(Address start, size_t size, - PagedSpace* owner, int* num_pages) { - ASSERT(start != NULL); - *num_pages = PagesInChunk(start, size); - ASSERT(*num_pages > 0); - ASSERT(initial_chunk_ != NULL); - ASSERT(InInitialChunk(start)); - ASSERT(InInitialChunk(start + size - 1)); - if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) { - return Page::FromAddress(NULL); +LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size, + Executability executable, + Space* owner) { + MemoryChunk* chunk = AllocateChunk(object_size, executable, owner); + if (chunk == NULL) return NULL; + return LargePage::Initialize(isolate_->heap(), chunk); +} + + +void MemoryAllocator::Free(MemoryChunk* chunk) { + LOG(isolate_, DeleteEvent("MemoryChunk", chunk)); + if (chunk->owner() != NULL) { + ObjectSpace space = + static_cast<ObjectSpace>(1 << chunk->owner()->identity()); + PerformAllocationCallback(space, kAllocationActionFree, chunk->size()); } -#ifdef DEBUG - ZapBlock(start, size); -#endif - isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size)); - // So long as we correctly overestimated the number of chunks we should not - // run out of chunk ids. - CHECK(!OutOfChunkIds()); - int chunk_id = Pop(); - chunks_[chunk_id].init(start, size, owner); - return InitializePagesInChunk(chunk_id, *num_pages, owner); + delete chunk->slots_buffer(); + delete chunk->skip_list(); + + VirtualMemory* reservation = chunk->reserved_memory(); + if (reservation->IsReserved()) { + FreeMemory(reservation, chunk->executable()); + } else { + FreeMemory(chunk->address(), + chunk->size(), + chunk->executable()); + } } bool MemoryAllocator::CommitBlock(Address start, size_t size, Executability executable) { - ASSERT(start != NULL); - ASSERT(size > 0); - ASSERT(initial_chunk_ != NULL); - ASSERT(InInitialChunk(start)); - ASSERT(InInitialChunk(start + size - 1)); - - if (!initial_chunk_->Commit(start, size, executable)) return false; + if (!VirtualMemory::CommitRegion(start, size, executable)) return false; #ifdef DEBUG ZapBlock(start, size); #endif @@ -583,13 +579,7 @@ bool MemoryAllocator::CommitBlock(Address start, bool MemoryAllocator::UncommitBlock(Address start, size_t size) { - ASSERT(start != NULL); - ASSERT(size > 0); - ASSERT(initial_chunk_ != NULL); - ASSERT(InInitialChunk(start)); - ASSERT(InInitialChunk(start + size - 1)); - - if (!initial_chunk_->Uncommit(start, size)) return false; + if (!VirtualMemory::UncommitRegion(start, size)) return false; isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); return true; } @@ -602,130 +592,49 @@ void MemoryAllocator::ZapBlock(Address start, size_t size) { } -Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, - PagedSpace* owner) { - ASSERT(IsValidChunk(chunk_id)); - ASSERT(pages_in_chunk > 0); - - Address chunk_start = chunks_[chunk_id].address(); - - Address low = RoundUp(chunk_start, Page::kPageSize); - -#ifdef DEBUG - size_t chunk_size = chunks_[chunk_id].size(); - Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); - ASSERT(pages_in_chunk <= - ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize)); -#endif - - Address page_addr = low; - for (int i = 0; i < pages_in_chunk; i++) { - Page* p = Page::FromAddress(page_addr); - p->heap_ = owner->heap(); - p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; - p->InvalidateWatermark(true); - p->SetIsLargeObjectPage(false); - p->SetAllocationWatermark(p->ObjectAreaStart()); - p->SetCachedAllocationWatermark(p->ObjectAreaStart()); - page_addr += Page::kPageSize; +void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, + AllocationAction action, + size_t size) { + for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { + MemoryAllocationCallbackRegistration registration = + memory_allocation_callbacks_[i]; + if ((registration.space & space) == space && + (registration.action & action) == action) + registration.callback(space, action, static_cast<int>(size)); } - - // Set the next page of the last page to 0. - Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); - last_page->opaque_header = OffsetFrom(0) | chunk_id; - - return Page::FromAddress(low); } -Page* MemoryAllocator::FreePages(Page* p) { - if (!p->is_valid()) return p; - - // Find the first page in the same chunk as 'p' - Page* first_page = FindFirstPageInSameChunk(p); - Page* page_to_return = Page::FromAddress(NULL); - - if (p != first_page) { - // Find the last page in the same chunk as 'prev'. - Page* last_page = FindLastPageInSameChunk(p); - first_page = GetNextPage(last_page); // first page in next chunk - - // set the next_page of last_page to NULL - SetNextPage(last_page, Page::FromAddress(NULL)); - page_to_return = p; // return 'p' when exiting - } - - while (first_page->is_valid()) { - int chunk_id = GetChunkId(first_page); - ASSERT(IsValidChunk(chunk_id)); - - // Find the first page of the next chunk before deleting this chunk. - first_page = GetNextPage(FindLastPageInSameChunk(first_page)); - - // Free the current chunk. - DeleteChunk(chunk_id); +bool MemoryAllocator::MemoryAllocationCallbackRegistered( + MemoryAllocationCallback callback) { + for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { + if (memory_allocation_callbacks_[i].callback == callback) return true; } - - return page_to_return; + return false; } -void MemoryAllocator::FreeAllPages(PagedSpace* space) { - for (int i = 0, length = chunks_.length(); i < length; i++) { - if (chunks_[i].owner() == space) { - DeleteChunk(i); - } - } +void MemoryAllocator::AddMemoryAllocationCallback( + MemoryAllocationCallback callback, + ObjectSpace space, + AllocationAction action) { + ASSERT(callback != NULL); + MemoryAllocationCallbackRegistration registration(callback, space, action); + ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); + return memory_allocation_callbacks_.Add(registration); } -void MemoryAllocator::DeleteChunk(int chunk_id) { - ASSERT(IsValidChunk(chunk_id)); - - ChunkInfo& c = chunks_[chunk_id]; - - // We cannot free a chunk contained in the initial chunk because it was not - // allocated with AllocateRawMemory. Instead we uncommit the virtual - // memory. - if (InInitialChunk(c.address())) { - // TODO(1240712): VirtualMemory::Uncommit has a return value which - // is ignored here. - initial_chunk_->Uncommit(c.address(), c.size()); - Counters* counters = isolate_->counters(); - counters->memory_allocated()->Decrement(static_cast<int>(c.size())); - } else { - LOG(isolate_, DeleteEvent("PagedChunk", c.address())); - ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner_identity()); - size_t size = c.size(); - size_t guard_size = (c.executable() == EXECUTABLE) ? Page::kPageSize : 0; - FreeRawMemory(c.address() - guard_size, size + guard_size, c.executable()); - PerformAllocationCallback(space, kAllocationActionFree, size); +void MemoryAllocator::RemoveMemoryAllocationCallback( + MemoryAllocationCallback callback) { + ASSERT(callback != NULL); + for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { + if (memory_allocation_callbacks_[i].callback == callback) { + memory_allocation_callbacks_.Remove(i); + return; + } } - c.init(NULL, 0, NULL); - Push(chunk_id); -} - - -Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { - int chunk_id = GetChunkId(p); - ASSERT(IsValidChunk(chunk_id)); - - Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); - return Page::FromAddress(low); -} - - -Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { - int chunk_id = GetChunkId(p); - ASSERT(IsValidChunk(chunk_id)); - - Address chunk_start = chunks_[chunk_id].address(); - size_t chunk_size = chunks_[chunk_id].size(); - - Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); - ASSERT(chunk_start <= p->address() && p->address() < high); - - return Page::FromAddress(high - Page::kPageSize); + UNREACHABLE(); } @@ -739,75 +648,17 @@ void MemoryAllocator::ReportStatistics() { } #endif +// ----------------------------------------------------------------------------- +// MemoryChunk implementation -void MemoryAllocator::RelinkPageListInChunkOrder(PagedSpace* space, - Page** first_page, - Page** last_page, - Page** last_page_in_use) { - Page* first = NULL; - Page* last = NULL; - - for (int i = 0, length = chunks_.length(); i < length; i++) { - ChunkInfo& chunk = chunks_[i]; - - if (chunk.owner() == space) { - if (first == NULL) { - Address low = RoundUp(chunk.address(), Page::kPageSize); - first = Page::FromAddress(low); - } - last = RelinkPagesInChunk(i, - chunk.address(), - chunk.size(), - last, - last_page_in_use); - } - } - - if (first_page != NULL) { - *first_page = first; - } - - if (last_page != NULL) { - *last_page = last; - } -} - - -Page* MemoryAllocator::RelinkPagesInChunk(int chunk_id, - Address chunk_start, - size_t chunk_size, - Page* prev, - Page** last_page_in_use) { - Address page_addr = RoundUp(chunk_start, Page::kPageSize); - int pages_in_chunk = PagesInChunk(chunk_start, chunk_size); - - if (prev->is_valid()) { - SetNextPage(prev, Page::FromAddress(page_addr)); - } - - for (int i = 0; i < pages_in_chunk; i++) { - Page* p = Page::FromAddress(page_addr); - p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; - page_addr += Page::kPageSize; - - p->InvalidateWatermark(true); - if (p->WasInUseBeforeMC()) { - *last_page_in_use = p; - } - } - - // Set the next page of the last page to 0. - Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); - last_page->opaque_header = OffsetFrom(0) | chunk_id; - - if (last_page->WasInUseBeforeMC()) { - *last_page_in_use = last_page; +void MemoryChunk::IncrementLiveBytesFromMutator(Address address, int by) { + MemoryChunk* chunk = MemoryChunk::FromAddress(address); + if (!chunk->InNewSpace() && !static_cast<Page*>(chunk)->WasSwept()) { + static_cast<PagedSpace*>(chunk->owner())->IncrementUnsweptFreeBytes(-by); } - - return last_page; + chunk->IncrementLiveBytes(by); } - // ----------------------------------------------------------------------------- // PagedSpace implementation @@ -815,7 +666,11 @@ PagedSpace::PagedSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id, Executability executable) - : Space(heap, id, executable) { + : Space(heap, id, executable), + free_list_(this), + was_swept_conservatively_(false), + first_unswept_page_(Page::FromAddress(NULL)), + unswept_free_bytes_(0) { max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) * Page::kObjectAreaSize; accounting_stats_.Clear(); @@ -823,288 +678,152 @@ PagedSpace::PagedSpace(Heap* heap, allocation_info_.top = NULL; allocation_info_.limit = NULL; - mc_forwarding_info_.top = NULL; - mc_forwarding_info_.limit = NULL; + anchor_.InitializeAsAnchor(this); } -bool PagedSpace::Setup(Address start, size_t size) { - if (HasBeenSetup()) return false; - - int num_pages = 0; - // Try to use the virtual memory range passed to us. If it is too small to - // contain at least one page, ignore it and allocate instead. - int pages_in_chunk = PagesInChunk(start, size); - if (pages_in_chunk > 0) { - first_page_ = Isolate::Current()->memory_allocator()->CommitPages( - RoundUp(start, Page::kPageSize), - Page::kPageSize * pages_in_chunk, - this, &num_pages); - } else { - int requested_pages = - Min(MemoryAllocator::kPagesPerChunk, - static_cast<int>(max_capacity_ / Page::kObjectAreaSize)); - first_page_ = - Isolate::Current()->memory_allocator()->AllocatePages( - requested_pages, &num_pages, this); - if (!first_page_->is_valid()) return false; - } - - // We are sure that the first page is valid and that we have at least one - // page. - ASSERT(first_page_->is_valid()); - ASSERT(num_pages > 0); - accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize); - ASSERT(Capacity() <= max_capacity_); - - // Sequentially clear region marks in the newly allocated - // pages and cache the current last page in the space. - for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { - p->SetRegionMarks(Page::kAllRegionsCleanMarks); - last_page_ = p; - } - - // Use first_page_ for allocation. - SetAllocationInfo(&allocation_info_, first_page_); - - page_list_is_chunk_ordered_ = true; - +bool PagedSpace::SetUp() { return true; } -bool PagedSpace::HasBeenSetup() { - return (Capacity() > 0); +bool PagedSpace::HasBeenSetUp() { + return true; } void PagedSpace::TearDown() { - Isolate::Current()->memory_allocator()->FreeAllPages(this); - first_page_ = NULL; - accounting_stats_.Clear(); -} - - -void PagedSpace::MarkAllPagesClean() { - PageIterator it(this, PageIterator::ALL_PAGES); - while (it.has_next()) { - it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks); + PageIterator iterator(this); + while (iterator.has_next()) { + heap()->isolate()->memory_allocator()->Free(iterator.next()); } + anchor_.set_next_page(&anchor_); + anchor_.set_prev_page(&anchor_); + accounting_stats_.Clear(); } MaybeObject* PagedSpace::FindObject(Address addr) { - // Note: this function can only be called before or after mark-compact GC - // because it accesses map pointers. + // Note: this function can only be called on precisely swept spaces. ASSERT(!heap()->mark_compact_collector()->in_use()); if (!Contains(addr)) return Failure::Exception(); Page* p = Page::FromAddress(addr); - ASSERT(IsUsed(p)); - Address cur = p->ObjectAreaStart(); - Address end = p->AllocationTop(); - while (cur < end) { - HeapObject* obj = HeapObject::FromAddress(cur); + HeapObjectIterator it(p, NULL); + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { + Address cur = obj->address(); Address next = cur + obj->Size(); if ((cur <= addr) && (addr < next)) return obj; - cur = next; } UNREACHABLE(); return Failure::Exception(); } +bool PagedSpace::CanExpand() { + ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); + ASSERT(Capacity() % Page::kObjectAreaSize == 0); -bool PagedSpace::IsUsed(Page* page) { - PageIterator it(this, PageIterator::PAGES_IN_USE); - while (it.has_next()) { - if (page == it.next()) return true; - } - return false; -} - - -void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { - alloc_info->top = p->ObjectAreaStart(); - alloc_info->limit = p->ObjectAreaEnd(); - ASSERT(alloc_info->VerifyPagedAllocation()); -} - - -void PagedSpace::MCResetRelocationInfo() { - // Set page indexes. - int i = 0; - PageIterator it(this, PageIterator::ALL_PAGES); - while (it.has_next()) { - Page* p = it.next(); - p->mc_page_index = i++; - } - - // Set mc_forwarding_info_ to the first page in the space. - SetAllocationInfo(&mc_forwarding_info_, first_page_); - // All the bytes in the space are 'available'. We will rediscover - // allocated and wasted bytes during GC. - accounting_stats_.Reset(); -} - - -int PagedSpace::MCSpaceOffsetForAddress(Address addr) { -#ifdef DEBUG - // The Contains function considers the address at the beginning of a - // page in the page, MCSpaceOffsetForAddress considers it is in the - // previous page. - if (Page::IsAlignedToPageSize(addr)) { - ASSERT(Contains(addr - kPointerSize)); - } else { - ASSERT(Contains(addr)); - } -#endif - - // If addr is at the end of a page, it belongs to previous page - Page* p = Page::IsAlignedToPageSize(addr) - ? Page::FromAllocationTop(addr) - : Page::FromAddress(addr); - int index = p->mc_page_index; - return (index * Page::kPageSize) + p->Offset(addr); -} + if (Capacity() == max_capacity_) return false; + ASSERT(Capacity() < max_capacity_); -// Slow case for reallocating and promoting objects during a compacting -// collection. This function is not space-specific. -HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) { - Page* current_page = TopPageOf(mc_forwarding_info_); - if (!current_page->next_page()->is_valid()) { - if (!Expand(current_page)) { - return NULL; - } - } + // Are we going to exceed capacity for this space? + if ((Capacity() + Page::kPageSize) > max_capacity_) return false; - // There are surely more pages in the space now. - ASSERT(current_page->next_page()->is_valid()); - // We do not add the top of page block for current page to the space's - // free list---the block may contain live objects so we cannot write - // bookkeeping information to it. Instead, we will recover top of page - // blocks when we move objects to their new locations. - // - // We do however write the allocation pointer to the page. The encoding - // of forwarding addresses is as an offset in terms of live bytes, so we - // need quick access to the allocation top of each page to decode - // forwarding addresses. - current_page->SetAllocationWatermark(mc_forwarding_info_.top); - current_page->next_page()->InvalidateWatermark(true); - SetAllocationInfo(&mc_forwarding_info_, current_page->next_page()); - return AllocateLinearly(&mc_forwarding_info_, size_in_bytes); + return true; } +bool PagedSpace::Expand() { + if (!CanExpand()) return false; -bool PagedSpace::Expand(Page* last_page) { - ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); - ASSERT(Capacity() % Page::kObjectAreaSize == 0); - - if (Capacity() == max_capacity_) return false; + Page* p = heap()->isolate()->memory_allocator()-> + AllocatePage(this, executable()); + if (p == NULL) return false; - ASSERT(Capacity() < max_capacity_); - // Last page must be valid and its next page is invalid. - ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); - - int available_pages = - static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); - // We don't want to have to handle small chunks near the end so if there are - // not kPagesPerChunk pages available without exceeding the max capacity then - // act as if memory has run out. - if (available_pages < MemoryAllocator::kPagesPerChunk) return false; - - int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); - Page* p = heap()->isolate()->memory_allocator()->AllocatePages( - desired_pages, &desired_pages, this); - if (!p->is_valid()) return false; - - accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); ASSERT(Capacity() <= max_capacity_); - heap()->isolate()->memory_allocator()->SetNextPage(last_page, p); - - // Sequentially clear region marks of new pages and and cache the - // new last page in the space. - while (p->is_valid()) { - p->SetRegionMarks(Page::kAllRegionsCleanMarks); - last_page_ = p; - p = p->next_page(); - } + p->InsertAfter(anchor_.prev_page()); return true; } -#ifdef DEBUG int PagedSpace::CountTotalPages() { + PageIterator it(this); int count = 0; - for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { + while (it.has_next()) { + it.next(); count++; } return count; } -#endif -void PagedSpace::Shrink() { - if (!page_list_is_chunk_ordered_) { - // We can't shrink space if pages is not chunk-ordered - // (see comment for class MemoryAllocator for definition). - return; - } +void PagedSpace::ReleasePage(Page* page) { + ASSERT(page->LiveBytes() == 0); - // Release half of free pages. - Page* top_page = AllocationTopPage(); - ASSERT(top_page->is_valid()); + // Adjust list of unswept pages if the page is the head of the list. + if (first_unswept_page_ == page) { + first_unswept_page_ = page->next_page(); + if (first_unswept_page_ == anchor()) { + first_unswept_page_ = Page::FromAddress(NULL); + } + } - // Count the number of pages we would like to free. - int pages_to_free = 0; - for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { - pages_to_free++; + if (page->WasSwept()) { + intptr_t size = free_list_.EvictFreeListItems(page); + accounting_stats_.AllocateBytes(size); + ASSERT_EQ(Page::kObjectAreaSize, static_cast<int>(size)); + } else { + DecreaseUnsweptFreeBytes(page); } - // Free pages after top_page. - Page* p = heap()->isolate()->memory_allocator()-> - FreePages(top_page->next_page()); - heap()->isolate()->memory_allocator()->SetNextPage(top_page, p); + if (Page::FromAllocationTop(allocation_info_.top) == page) { + allocation_info_.top = allocation_info_.limit = NULL; + } - // Find out how many pages we failed to free and update last_page_. - // Please note pages can only be freed in whole chunks. - last_page_ = top_page; - for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { - pages_to_free--; - last_page_ = p; + page->Unlink(); + if (page->IsFlagSet(MemoryChunk::CONTAINS_ONLY_DATA)) { + heap()->isolate()->memory_allocator()->Free(page); + } else { + heap()->QueueMemoryChunkForFree(page); } - accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize); - ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); + ASSERT(Capacity() > 0); + ASSERT(Capacity() % Page::kObjectAreaSize == 0); + accounting_stats_.ShrinkSpace(Page::kObjectAreaSize); } -bool PagedSpace::EnsureCapacity(int capacity) { - if (Capacity() >= capacity) return true; - - // Start from the allocation top and loop to the last page in the space. - Page* last_page = AllocationTopPage(); - Page* next_page = last_page->next_page(); - while (next_page->is_valid()) { - last_page = heap()->isolate()->memory_allocator()-> - FindLastPageInSameChunk(next_page); - next_page = last_page->next_page(); +void PagedSpace::ReleaseAllUnusedPages() { + PageIterator it(this); + while (it.has_next()) { + Page* page = it.next(); + if (!page->WasSwept()) { + if (page->LiveBytes() == 0) ReleasePage(page); + } else { + HeapObject* obj = HeapObject::FromAddress(page->body()); + if (obj->IsFreeSpace() && + FreeSpace::cast(obj)->size() == Page::kObjectAreaSize) { + // Sometimes we allocate memory from free list but don't + // immediately initialize it (e.g. see PagedSpace::ReserveSpace + // called from Heap::ReserveSpace that can cause GC before + // reserved space is actually initialized). + // Thus we can't simply assume that obj represents a valid + // node still owned by a free list + // Instead we should verify that the page is fully covered + // by free list items. + FreeList::SizeStats sizes; + free_list_.CountFreeListItems(page, &sizes); + if (sizes.Total() == Page::kObjectAreaSize) { + ReleasePage(page); + } + } + } } - - // Expand the space until it has the required capacity or expansion fails. - do { - if (!Expand(last_page)) return false; - ASSERT(last_page->next_page()->is_valid()); - last_page = - heap()->isolate()->memory_allocator()->FindLastPageInSameChunk( - last_page->next_page()); - } while (Capacity() < capacity); - - return true; + heap()->FreeQueuedChunks(); } @@ -1114,61 +833,52 @@ void PagedSpace::Print() { } #ifdef DEBUG -// We do not assume that the PageIterator works, because it depends on the -// invariants we are checking during verification. void PagedSpace::Verify(ObjectVisitor* visitor) { - // The allocation pointer should be valid, and it should be in a page in the - // space. - ASSERT(allocation_info_.VerifyPagedAllocation()); - Page* top_page = Page::FromAllocationTop(allocation_info_.top); - ASSERT(heap()->isolate()->memory_allocator()->IsPageInSpace(top_page, this)); - - // Loop over all the pages. - bool above_allocation_top = false; - Page* current_page = first_page_; - while (current_page->is_valid()) { - if (above_allocation_top) { - // We don't care what's above the allocation top. - } else { - Address top = current_page->AllocationTop(); - if (current_page == top_page) { - ASSERT(top == allocation_info_.top); - // The next page will be above the allocation top. - above_allocation_top = true; - } - - // It should be packed with objects from the bottom to the top. - Address current = current_page->ObjectAreaStart(); - while (current < top) { - HeapObject* object = HeapObject::FromAddress(current); - - // The first word should be a map, and we expect all map pointers to - // be in map space. - Map* map = object->map(); - ASSERT(map->IsMap()); - ASSERT(heap()->map_space()->Contains(map)); - - // Perform space-specific object verification. - VerifyObject(object); - - // The object itself should look OK. - object->Verify(); - - // All the interior pointers should be contained in the heap and - // have page regions covering intergenerational references should be - // marked dirty. - int size = object->Size(); - object->IterateBody(map->instance_type(), size, visitor); - - current += size; + // We can only iterate over the pages if they were swept precisely. + if (was_swept_conservatively_) return; + + bool allocation_pointer_found_in_space = + (allocation_info_.top == allocation_info_.limit); + PageIterator page_iterator(this); + while (page_iterator.has_next()) { + Page* page = page_iterator.next(); + ASSERT(page->owner() == this); + if (page == Page::FromAllocationTop(allocation_info_.top)) { + allocation_pointer_found_in_space = true; + } + ASSERT(page->WasSweptPrecisely()); + HeapObjectIterator it(page, NULL); + Address end_of_previous_object = page->ObjectAreaStart(); + Address top = page->ObjectAreaEnd(); + int black_size = 0; + for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { + ASSERT(end_of_previous_object <= object->address()); + + // The first word should be a map, and we expect all map pointers to + // be in map space. + Map* map = object->map(); + ASSERT(map->IsMap()); + ASSERT(heap()->map_space()->Contains(map)); + + // Perform space-specific object verification. + VerifyObject(object); + + // The object itself should look OK. + object->Verify(); + + // All the interior pointers should be contained in the heap. + int size = object->Size(); + object->IterateBody(map->instance_type(), size, visitor); + if (Marking::IsBlack(Marking::MarkBitFrom(object))) { + black_size += size; } - // The allocation pointer should not be in the middle of an object. - ASSERT(current == top); + ASSERT(object->address() + size <= top); + end_of_previous_object = object->address() + size; } - - current_page = current_page->next_page(); + ASSERT_LE(black_size, page->LiveBytes()); } + ASSERT(allocation_pointer_found_in_space); } #endif @@ -1177,18 +887,28 @@ void PagedSpace::Verify(ObjectVisitor* visitor) { // NewSpace implementation -bool NewSpace::Setup(Address start, int size) { - // Setup new space based on the preallocated memory block defined by +bool NewSpace::SetUp(int reserved_semispace_capacity, + int maximum_semispace_capacity) { + // Set up new space based on the preallocated memory block defined by // start and size. The provided space is divided into two semi-spaces. // To support fast containment testing in the new space, the size of // this chunk must be a power of two and it must be aligned to its size. int initial_semispace_capacity = heap()->InitialSemiSpaceSize(); - int maximum_semispace_capacity = heap()->MaxSemiSpaceSize(); + + size_t size = 2 * reserved_semispace_capacity; + Address base = + heap()->isolate()->memory_allocator()->ReserveAlignedMemory( + size, size, &reservation_); + if (base == NULL) return false; + + chunk_base_ = base; + chunk_size_ = static_cast<uintptr_t>(size); + LOG(heap()->isolate(), NewEvent("InitialChunk", chunk_base_, chunk_size_)); ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); ASSERT(IsPowerOf2(maximum_semispace_capacity)); - // Allocate and setup the histogram arrays if necessary. + // Allocate and set up the histogram arrays if necessary. allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); @@ -1197,31 +917,28 @@ bool NewSpace::Setup(Address start, int size) { INSTANCE_TYPE_LIST(SET_NAME) #undef SET_NAME - ASSERT(size == 2 * heap()->ReservedSemiSpaceSize()); - ASSERT(IsAddressAligned(start, size, 0)); - - if (!to_space_.Setup(start, - initial_semispace_capacity, - maximum_semispace_capacity)) { - return false; - } - if (!from_space_.Setup(start + maximum_semispace_capacity, - initial_semispace_capacity, - maximum_semispace_capacity)) { + ASSERT(reserved_semispace_capacity == heap()->ReservedSemiSpaceSize()); + ASSERT(static_cast<intptr_t>(chunk_size_) >= + 2 * heap()->ReservedSemiSpaceSize()); + ASSERT(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0)); + + to_space_.SetUp(chunk_base_, + initial_semispace_capacity, + maximum_semispace_capacity); + from_space_.SetUp(chunk_base_ + reserved_semispace_capacity, + initial_semispace_capacity, + maximum_semispace_capacity); + if (!to_space_.Commit()) { return false; } - start_ = start; - address_mask_ = ~(size - 1); + start_ = chunk_base_; + address_mask_ = ~(2 * reserved_semispace_capacity - 1); object_mask_ = address_mask_ | kHeapObjectTagMask; - object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; + object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag; - allocation_info_.top = to_space_.low(); - allocation_info_.limit = to_space_.high(); - mc_forwarding_info_.top = NULL; - mc_forwarding_info_.limit = NULL; + ResetAllocationInfo(); - ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); return true; } @@ -1239,28 +956,34 @@ void NewSpace::TearDown() { start_ = NULL; allocation_info_.top = NULL; allocation_info_.limit = NULL; - mc_forwarding_info_.top = NULL; - mc_forwarding_info_.limit = NULL; to_space_.TearDown(); from_space_.TearDown(); + + LOG(heap()->isolate(), DeleteEvent("InitialChunk", chunk_base_)); + + ASSERT(reservation_.IsReserved()); + heap()->isolate()->memory_allocator()->FreeMemory(&reservation_, + NOT_EXECUTABLE); + chunk_base_ = NULL; + chunk_size_ = 0; } void NewSpace::Flip() { - SemiSpace tmp = from_space_; - from_space_ = to_space_; - to_space_ = tmp; + SemiSpace::Swap(&from_space_, &to_space_); } void NewSpace::Grow() { + // Double the semispace size but only up to maximum capacity. ASSERT(Capacity() < MaximumCapacity()); - if (to_space_.Grow()) { - // Only grow from space if we managed to grow to space. - if (!from_space_.Grow()) { - // If we managed to grow to space but couldn't grow from space, - // attempt to shrink to space. + int new_capacity = Min(MaximumCapacity(), 2 * static_cast<int>(Capacity())); + if (to_space_.GrowTo(new_capacity)) { + // Only grow from space if we managed to grow to-space. + if (!from_space_.GrowTo(new_capacity)) { + // If we managed to grow to-space but couldn't grow from-space, + // attempt to shrink to-space. if (!to_space_.ShrinkTo(from_space_.Capacity())) { // We are in an inconsistent state because we could not // commit/uncommit memory from new space. @@ -1268,21 +991,20 @@ void NewSpace::Grow() { } } } - allocation_info_.limit = to_space_.high(); ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); } void NewSpace::Shrink() { int new_capacity = Max(InitialCapacity(), 2 * SizeAsInt()); - int rounded_new_capacity = - RoundUp(new_capacity, static_cast<int>(OS::AllocateAlignment())); + int rounded_new_capacity = RoundUp(new_capacity, Page::kPageSize); if (rounded_new_capacity < Capacity() && to_space_.ShrinkTo(rounded_new_capacity)) { - // Only shrink from space if we managed to shrink to space. + // Only shrink from-space if we managed to shrink to-space. + from_space_.Reset(); if (!from_space_.ShrinkTo(rounded_new_capacity)) { - // If we managed to shrink to space but couldn't shrink from - // space, attempt to grow to space again. + // If we managed to shrink to-space but couldn't shrink from + // space, attempt to grow to-space again. if (!to_space_.GrowTo(from_space_.Capacity())) { // We are in an inconsistent state because we could not // commit/uncommit memory from new space. @@ -1290,36 +1012,98 @@ void NewSpace::Shrink() { } } } - allocation_info_.limit = to_space_.high(); + allocation_info_.limit = to_space_.page_high(); ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); } -void NewSpace::ResetAllocationInfo() { - allocation_info_.top = to_space_.low(); - allocation_info_.limit = to_space_.high(); +void NewSpace::UpdateAllocationInfo() { + allocation_info_.top = to_space_.page_low(); + allocation_info_.limit = to_space_.page_high(); + + // Lower limit during incremental marking. + if (heap()->incremental_marking()->IsMarking() && + inline_allocation_limit_step() != 0) { + Address new_limit = + allocation_info_.top + inline_allocation_limit_step(); + allocation_info_.limit = Min(new_limit, allocation_info_.limit); + } ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); } -void NewSpace::MCResetRelocationInfo() { - mc_forwarding_info_.top = from_space_.low(); - mc_forwarding_info_.limit = from_space_.high(); - ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_); +void NewSpace::ResetAllocationInfo() { + to_space_.Reset(); + UpdateAllocationInfo(); + pages_used_ = 0; + // Clear all mark-bits in the to-space. + NewSpacePageIterator it(&to_space_); + while (it.has_next()) { + Bitmap::Clear(it.next()); + } } -void NewSpace::MCCommitRelocationInfo() { - // Assumes that the spaces have been flipped so that mc_forwarding_info_ is - // valid allocation info for the to space. - allocation_info_.top = mc_forwarding_info_.top; - allocation_info_.limit = to_space_.high(); - ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); +bool NewSpace::AddFreshPage() { + Address top = allocation_info_.top; + if (NewSpacePage::IsAtStart(top)) { + // The current page is already empty. Don't try to make another. + + // We should only get here if someone asks to allocate more + // than what can be stored in a single page. + // TODO(gc): Change the limit on new-space allocation to prevent this + // from happening (all such allocations should go directly to LOSpace). + return false; + } + if (!to_space_.AdvancePage()) { + // Failed to get a new page in to-space. + return false; + } + + // Clear remainder of current page. + Address limit = NewSpacePage::FromLimit(top)->body_limit(); + if (heap()->gc_state() == Heap::SCAVENGE) { + heap()->promotion_queue()->SetNewLimit(limit); + heap()->promotion_queue()->ActivateGuardIfOnTheSamePage(); + } + + int remaining_in_page = static_cast<int>(limit - top); + heap()->CreateFillerObjectAt(top, remaining_in_page); + pages_used_++; + UpdateAllocationInfo(); + + return true; +} + + +MaybeObject* NewSpace::SlowAllocateRaw(int size_in_bytes) { + Address old_top = allocation_info_.top; + Address new_top = old_top + size_in_bytes; + Address high = to_space_.page_high(); + if (allocation_info_.limit < high) { + // Incremental marking has lowered the limit to get a + // chance to do a step. + allocation_info_.limit = Min( + allocation_info_.limit + inline_allocation_limit_step_, + high); + int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_); + heap()->incremental_marking()->Step(bytes_allocated); + top_on_previous_step_ = new_top; + return AllocateRaw(size_in_bytes); + } else if (AddFreshPage()) { + // Switched to new page. Try allocating again. + int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_); + heap()->incremental_marking()->Step(bytes_allocated); + top_on_previous_step_ = to_space_.page_low(); + return AllocateRaw(size_in_bytes); + } else { + return Failure::RetryAfterGC(); + } } #ifdef DEBUG -// We do not use the SemispaceIterator because verification doesn't assume +// We do not use the SemiSpaceIterator because verification doesn't assume // that it works (it depends on the invariants we are checking). void NewSpace::Verify() { // The allocation pointer should be in the space or at the very end. @@ -1327,63 +1111,57 @@ void NewSpace::Verify() { // There should be objects packed in from the low address up to the // allocation pointer. - Address current = to_space_.low(); - while (current < top()) { - HeapObject* object = HeapObject::FromAddress(current); - - // The first word should be a map, and we expect all map pointers to - // be in map space. - Map* map = object->map(); - ASSERT(map->IsMap()); - ASSERT(heap()->map_space()->Contains(map)); + Address current = to_space_.first_page()->body(); + CHECK_EQ(current, to_space_.space_start()); - // The object should not be code or a map. - ASSERT(!object->IsMap()); - ASSERT(!object->IsCode()); + while (current != top()) { + if (!NewSpacePage::IsAtEnd(current)) { + // The allocation pointer should not be in the middle of an object. + CHECK(!NewSpacePage::FromLimit(current)->ContainsLimit(top()) || + current < top()); - // The object itself should look OK. - object->Verify(); + HeapObject* object = HeapObject::FromAddress(current); - // All the interior pointers should be contained in the heap. - VerifyPointersVisitor visitor; - int size = object->Size(); - object->IterateBody(map->instance_type(), size, &visitor); + // The first word should be a map, and we expect all map pointers to + // be in map space. + Map* map = object->map(); + CHECK(map->IsMap()); + CHECK(heap()->map_space()->Contains(map)); - current += size; - } + // The object should not be code or a map. + CHECK(!object->IsMap()); + CHECK(!object->IsCode()); - // The allocation pointer should not be in the middle of an object. - ASSERT(current == top()); -} -#endif + // The object itself should look OK. + object->Verify(); + // All the interior pointers should be contained in the heap. + VerifyPointersVisitor visitor; + int size = object->Size(); + object->IterateBody(map->instance_type(), size, &visitor); -bool SemiSpace::Commit() { - ASSERT(!is_committed()); - if (!heap()->isolate()->memory_allocator()->CommitBlock( - start_, capacity_, executable())) { - return false; + current += size; + } else { + // At end of page, switch to next page. + NewSpacePage* page = NewSpacePage::FromLimit(current)->next_page(); + // Next page should be valid. + CHECK(!page->is_anchor()); + current = page->body(); + } } - committed_ = true; - return true; -} - -bool SemiSpace::Uncommit() { - ASSERT(is_committed()); - if (!heap()->isolate()->memory_allocator()->UncommitBlock( - start_, capacity_)) { - return false; - } - committed_ = false; - return true; + // Check semi-spaces. + ASSERT_EQ(from_space_.id(), kFromSpace); + ASSERT_EQ(to_space_.id(), kToSpace); + from_space_.Verify(); + to_space_.Verify(); } - +#endif // ----------------------------------------------------------------------------- // SemiSpace implementation -bool SemiSpace::Setup(Address start, +void SemiSpace::SetUp(Address start, int initial_capacity, int maximum_capacity) { // Creates a space in the young generation. The constructor does not @@ -1392,18 +1170,16 @@ bool SemiSpace::Setup(Address start, // otherwise. In the mark-compact collector, the memory region of the from // space is used as the marking stack. It requires contiguous memory // addresses. - initial_capacity_ = initial_capacity; + ASSERT(maximum_capacity >= Page::kPageSize); + initial_capacity_ = RoundDown(initial_capacity, Page::kPageSize); capacity_ = initial_capacity; - maximum_capacity_ = maximum_capacity; + maximum_capacity_ = RoundDown(maximum_capacity, Page::kPageSize); committed_ = false; - start_ = start; address_mask_ = ~(maximum_capacity - 1); object_mask_ = address_mask_ | kHeapObjectTagMask; object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; age_mark_ = start_; - - return Commit(); } @@ -1413,81 +1189,266 @@ void SemiSpace::TearDown() { } -bool SemiSpace::Grow() { - // Double the semispace size but only up to maximum capacity. - int maximum_extra = maximum_capacity_ - capacity_; - int extra = Min(RoundUp(capacity_, static_cast<int>(OS::AllocateAlignment())), - maximum_extra); - if (!heap()->isolate()->memory_allocator()->CommitBlock( - high(), extra, executable())) { +bool SemiSpace::Commit() { + ASSERT(!is_committed()); + int pages = capacity_ / Page::kPageSize; + Address end = start_ + maximum_capacity_; + Address start = end - pages * Page::kPageSize; + if (!heap()->isolate()->memory_allocator()->CommitBlock(start, + capacity_, + executable())) { return false; } - capacity_ += extra; + + NewSpacePage* page = anchor(); + for (int i = 1; i <= pages; i++) { + NewSpacePage* new_page = + NewSpacePage::Initialize(heap(), end - i * Page::kPageSize, this); + new_page->InsertAfter(page); + page = new_page; + } + + committed_ = true; + Reset(); + return true; +} + + +bool SemiSpace::Uncommit() { + ASSERT(is_committed()); + Address start = start_ + maximum_capacity_ - capacity_; + if (!heap()->isolate()->memory_allocator()->UncommitBlock(start, capacity_)) { + return false; + } + anchor()->set_next_page(anchor()); + anchor()->set_prev_page(anchor()); + + committed_ = false; return true; } bool SemiSpace::GrowTo(int new_capacity) { + if (!is_committed()) { + if (!Commit()) return false; + } + ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); ASSERT(new_capacity <= maximum_capacity_); ASSERT(new_capacity > capacity_); + int pages_before = capacity_ / Page::kPageSize; + int pages_after = new_capacity / Page::kPageSize; + + Address end = start_ + maximum_capacity_; + Address start = end - new_capacity; size_t delta = new_capacity - capacity_; + ASSERT(IsAligned(delta, OS::AllocateAlignment())); if (!heap()->isolate()->memory_allocator()->CommitBlock( - high(), delta, executable())) { + start, delta, executable())) { return false; } capacity_ = new_capacity; + NewSpacePage* last_page = anchor()->prev_page(); + ASSERT(last_page != anchor()); + for (int i = pages_before + 1; i <= pages_after; i++) { + Address page_address = end - i * Page::kPageSize; + NewSpacePage* new_page = NewSpacePage::Initialize(heap(), + page_address, + this); + new_page->InsertAfter(last_page); + Bitmap::Clear(new_page); + // Duplicate the flags that was set on the old page. + new_page->SetFlags(last_page->GetFlags(), + NewSpacePage::kCopyOnFlipFlagsMask); + last_page = new_page; + } return true; } bool SemiSpace::ShrinkTo(int new_capacity) { + ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); ASSERT(new_capacity >= initial_capacity_); ASSERT(new_capacity < capacity_); - size_t delta = capacity_ - new_capacity; - ASSERT(IsAligned(delta, OS::AllocateAlignment())); - if (!heap()->isolate()->memory_allocator()->UncommitBlock( - high() - delta, delta)) { - return false; + if (is_committed()) { + // Semispaces grow backwards from the end of their allocated capacity, + // so we find the before and after start addresses relative to the + // end of the space. + Address space_end = start_ + maximum_capacity_; + Address old_start = space_end - capacity_; + size_t delta = capacity_ - new_capacity; + ASSERT(IsAligned(delta, OS::AllocateAlignment())); + + MemoryAllocator* allocator = heap()->isolate()->memory_allocator(); + if (!allocator->UncommitBlock(old_start, delta)) { + return false; + } + + int pages_after = new_capacity / Page::kPageSize; + NewSpacePage* new_last_page = + NewSpacePage::FromAddress(space_end - pages_after * Page::kPageSize); + new_last_page->set_next_page(anchor()); + anchor()->set_prev_page(new_last_page); + ASSERT((current_page_ <= first_page()) && (current_page_ >= new_last_page)); } + capacity_ = new_capacity; + return true; } +void SemiSpace::FlipPages(intptr_t flags, intptr_t mask) { + anchor_.set_owner(this); + // Fixup back-pointers to anchor. Address of anchor changes + // when we swap. + anchor_.prev_page()->set_next_page(&anchor_); + anchor_.next_page()->set_prev_page(&anchor_); + + bool becomes_to_space = (id_ == kFromSpace); + id_ = becomes_to_space ? kToSpace : kFromSpace; + NewSpacePage* page = anchor_.next_page(); + while (page != &anchor_) { + page->set_owner(this); + page->SetFlags(flags, mask); + if (becomes_to_space) { + page->ClearFlag(MemoryChunk::IN_FROM_SPACE); + page->SetFlag(MemoryChunk::IN_TO_SPACE); + page->ClearFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); + page->ResetLiveBytes(); + } else { + page->SetFlag(MemoryChunk::IN_FROM_SPACE); + page->ClearFlag(MemoryChunk::IN_TO_SPACE); + } + ASSERT(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); + ASSERT(page->IsFlagSet(MemoryChunk::IN_TO_SPACE) || + page->IsFlagSet(MemoryChunk::IN_FROM_SPACE)); + page = page->next_page(); + } +} + + +void SemiSpace::Reset() { + ASSERT(anchor_.next_page() != &anchor_); + current_page_ = anchor_.next_page(); +} + + +void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) { + // We won't be swapping semispaces without data in them. + ASSERT(from->anchor_.next_page() != &from->anchor_); + ASSERT(to->anchor_.next_page() != &to->anchor_); + + // Swap bits. + SemiSpace tmp = *from; + *from = *to; + *to = tmp; + + // Fixup back-pointers to the page list anchor now that its address + // has changed. + // Swap to/from-space bits on pages. + // Copy GC flags from old active space (from-space) to new (to-space). + intptr_t flags = from->current_page()->GetFlags(); + to->FlipPages(flags, NewSpacePage::kCopyOnFlipFlagsMask); + + from->FlipPages(0, 0); +} + + +void SemiSpace::set_age_mark(Address mark) { + ASSERT(NewSpacePage::FromLimit(mark)->semi_space() == this); + age_mark_ = mark; + // Mark all pages up to the one containing mark. + NewSpacePageIterator it(space_start(), mark); + while (it.has_next()) { + it.next()->SetFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); + } +} + + #ifdef DEBUG void SemiSpace::Print() { } -void SemiSpace::Verify() { } +void SemiSpace::Verify() { + bool is_from_space = (id_ == kFromSpace); + NewSpacePage* page = anchor_.next_page(); + CHECK(anchor_.semi_space() == this); + while (page != &anchor_) { + CHECK(page->semi_space() == this); + CHECK(page->InNewSpace()); + CHECK(page->IsFlagSet(is_from_space ? MemoryChunk::IN_FROM_SPACE + : MemoryChunk::IN_TO_SPACE)); + CHECK(!page->IsFlagSet(is_from_space ? MemoryChunk::IN_TO_SPACE + : MemoryChunk::IN_FROM_SPACE)); + CHECK(page->IsFlagSet(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING)); + if (!is_from_space) { + // The pointers-from-here-are-interesting flag isn't updated dynamically + // on from-space pages, so it might be out of sync with the marking state. + if (page->heap()->incremental_marking()->IsMarking()) { + CHECK(page->IsFlagSet(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); + } else { + CHECK(!page->IsFlagSet( + MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); + } + // TODO(gc): Check that the live_bytes_count_ field matches the + // black marking on the page (if we make it match in new-space). + } + CHECK(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); + CHECK(page->prev_page()->next_page() == page); + page = page->next_page(); + } +} + + +void SemiSpace::AssertValidRange(Address start, Address end) { + // Addresses belong to same semi-space + NewSpacePage* page = NewSpacePage::FromLimit(start); + NewSpacePage* end_page = NewSpacePage::FromLimit(end); + SemiSpace* space = page->semi_space(); + CHECK_EQ(space, end_page->semi_space()); + // Start address is before end address, either on same page, + // or end address is on a later page in the linked list of + // semi-space pages. + if (page == end_page) { + CHECK(start <= end); + } else { + while (page != end_page) { + page = page->next_page(); + CHECK_NE(page, space->anchor()); + } + } +} #endif // ----------------------------------------------------------------------------- // SemiSpaceIterator implementation. SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) { - Initialize(space, space->bottom(), space->top(), NULL); + Initialize(space->bottom(), space->top(), NULL); } SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, HeapObjectCallback size_func) { - Initialize(space, space->bottom(), space->top(), size_func); + Initialize(space->bottom(), space->top(), size_func); } SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) { - Initialize(space, start, space->top(), NULL); + Initialize(start, space->top(), NULL); +} + + +SemiSpaceIterator::SemiSpaceIterator(Address from, Address to) { + Initialize(from, to, NULL); } -void SemiSpaceIterator::Initialize(NewSpace* space, Address start, +void SemiSpaceIterator::Initialize(Address start, Address end, HeapObjectCallback size_func) { - ASSERT(space->ToSpaceContains(start)); - ASSERT(space->ToSpaceLow() <= end - && end <= space->ToSpaceHigh()); - space_ = &space->to_space_; + SemiSpace::AssertValidRange(start, end); current_ = start; limit_ = end; size_func_ = size_func; @@ -1623,7 +1584,7 @@ void NewSpace::ClearHistograms() { void NewSpace::CollectStatistics() { ClearHistograms(); SemiSpaceIterator it(this); - for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) RecordAllocation(obj); } @@ -1699,7 +1660,6 @@ void NewSpace::RecordPromotion(HeapObject* obj) { promoted_histogram_[type].increment_bytes(obj->Size()); } - // ----------------------------------------------------------------------------- // Free lists for old object spaces implementation @@ -1708,541 +1668,509 @@ void FreeListNode::set_size(Heap* heap, int size_in_bytes) { ASSERT(IsAligned(size_in_bytes, kPointerSize)); // We write a map and possibly size information to the block. If the block - // is big enough to be a ByteArray with at least one extra word (the next - // pointer), we set its map to be the byte array map and its size to an + // is big enough to be a FreeSpace with at least one extra word (the next + // pointer), we set its map to be the free space map and its size to an // appropriate array length for the desired size from HeapObject::Size(). // If the block is too small (eg, one or two words), to hold both a size // field and a next pointer, we give it a filler map that gives it the // correct size. - if (size_in_bytes > ByteArray::kHeaderSize) { - set_map(heap->raw_unchecked_byte_array_map()); - // Can't use ByteArray::cast because it fails during deserialization. - ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this); - this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes)); + if (size_in_bytes > FreeSpace::kHeaderSize) { + set_map_no_write_barrier(heap->raw_unchecked_free_space_map()); + // Can't use FreeSpace::cast because it fails during deserialization. + FreeSpace* this_as_free_space = reinterpret_cast<FreeSpace*>(this); + this_as_free_space->set_size(size_in_bytes); } else if (size_in_bytes == kPointerSize) { - set_map(heap->raw_unchecked_one_pointer_filler_map()); + set_map_no_write_barrier(heap->raw_unchecked_one_pointer_filler_map()); } else if (size_in_bytes == 2 * kPointerSize) { - set_map(heap->raw_unchecked_two_pointer_filler_map()); + set_map_no_write_barrier(heap->raw_unchecked_two_pointer_filler_map()); } else { UNREACHABLE(); } // We would like to ASSERT(Size() == size_in_bytes) but this would fail during - // deserialization because the byte array map is not done yet. + // deserialization because the free space map is not done yet. } -Address FreeListNode::next(Heap* heap) { +FreeListNode* FreeListNode::next() { ASSERT(IsFreeListNode(this)); - if (map() == heap->raw_unchecked_byte_array_map()) { - ASSERT(Size() >= kNextOffset + kPointerSize); - return Memory::Address_at(address() + kNextOffset); + if (map() == HEAP->raw_unchecked_free_space_map()) { + ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); + return reinterpret_cast<FreeListNode*>( + Memory::Address_at(address() + kNextOffset)); } else { - return Memory::Address_at(address() + kPointerSize); + return reinterpret_cast<FreeListNode*>( + Memory::Address_at(address() + kPointerSize)); } } -void FreeListNode::set_next(Heap* heap, Address next) { +FreeListNode** FreeListNode::next_address() { ASSERT(IsFreeListNode(this)); - if (map() == heap->raw_unchecked_byte_array_map()) { + if (map() == HEAP->raw_unchecked_free_space_map()) { ASSERT(Size() >= kNextOffset + kPointerSize); - Memory::Address_at(address() + kNextOffset) = next; + return reinterpret_cast<FreeListNode**>(address() + kNextOffset); } else { - Memory::Address_at(address() + kPointerSize) = next; + return reinterpret_cast<FreeListNode**>(address() + kPointerSize); } } -OldSpaceFreeList::OldSpaceFreeList(Heap* heap, AllocationSpace owner) - : heap_(heap), - owner_(owner) { - Reset(); +void FreeListNode::set_next(FreeListNode* next) { + ASSERT(IsFreeListNode(this)); + // While we are booting the VM the free space map will actually be null. So + // we have to make sure that we don't try to use it for anything at that + // stage. + if (map() == HEAP->raw_unchecked_free_space_map()) { + ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); + Memory::Address_at(address() + kNextOffset) = + reinterpret_cast<Address>(next); + } else { + Memory::Address_at(address() + kPointerSize) = + reinterpret_cast<Address>(next); + } } -void OldSpaceFreeList::Reset() { - available_ = 0; - for (int i = 0; i < kFreeListsLength; i++) { - free_[i].head_node_ = NULL; - } - needs_rebuild_ = false; - finger_ = kHead; - free_[kHead].next_size_ = kEnd; +FreeList::FreeList(PagedSpace* owner) + : owner_(owner), heap_(owner->heap()) { + Reset(); } -void OldSpaceFreeList::RebuildSizeList() { - ASSERT(needs_rebuild_); - int cur = kHead; - for (int i = cur + 1; i < kFreeListsLength; i++) { - if (free_[i].head_node_ != NULL) { - free_[cur].next_size_ = i; - cur = i; - } - } - free_[cur].next_size_ = kEnd; - needs_rebuild_ = false; +void FreeList::Reset() { + available_ = 0; + small_list_ = NULL; + medium_list_ = NULL; + large_list_ = NULL; + huge_list_ = NULL; } -int OldSpaceFreeList::Free(Address start, int size_in_bytes) { -#ifdef DEBUG - Isolate::Current()->memory_allocator()->ZapBlock(start, size_in_bytes); -#endif +int FreeList::Free(Address start, int size_in_bytes) { + if (size_in_bytes == 0) return 0; FreeListNode* node = FreeListNode::FromAddress(start); node->set_size(heap_, size_in_bytes); - // We don't use the freelists in compacting mode. This makes it more like a - // GC that only has mark-sweep-compact and doesn't have a mark-sweep - // collector. - if (FLAG_always_compact) { - return size_in_bytes; - } - - // Early return to drop too-small blocks on the floor (one or two word - // blocks cannot hold a map pointer, a size field, and a pointer to the - // next block in the free list). - if (size_in_bytes < kMinBlockSize) { - return size_in_bytes; + // Early return to drop too-small blocks on the floor. + if (size_in_bytes < kSmallListMin) return size_in_bytes; + + // Insert other blocks at the head of a free list of the appropriate + // magnitude. + if (size_in_bytes <= kSmallListMax) { + node->set_next(small_list_); + small_list_ = node; + } else if (size_in_bytes <= kMediumListMax) { + node->set_next(medium_list_); + medium_list_ = node; + } else if (size_in_bytes <= kLargeListMax) { + node->set_next(large_list_); + large_list_ = node; + } else { + node->set_next(huge_list_); + huge_list_ = node; } - - // Insert other blocks at the head of an exact free list. - int index = size_in_bytes >> kPointerSizeLog2; - node->set_next(heap_, free_[index].head_node_); - free_[index].head_node_ = node->address(); available_ += size_in_bytes; - needs_rebuild_ = true; + ASSERT(IsVeryLong() || available_ == SumFreeLists()); return 0; } -MaybeObject* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { - ASSERT(0 < size_in_bytes); - ASSERT(size_in_bytes <= kMaxBlockSize); - ASSERT(IsAligned(size_in_bytes, kPointerSize)); +FreeListNode* FreeList::PickNodeFromList(FreeListNode** list, int* node_size) { + FreeListNode* node = *list; - if (needs_rebuild_) RebuildSizeList(); - int index = size_in_bytes >> kPointerSizeLog2; - // Check for a perfect fit. - if (free_[index].head_node_ != NULL) { - FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_); - // If this was the last block of its size, remove the size. - if ((free_[index].head_node_ = node->next(heap_)) == NULL) - RemoveSize(index); - available_ -= size_in_bytes; - *wasted_bytes = 0; - ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. - return node; - } - // Search the size list for the best fit. - int prev = finger_ < index ? finger_ : kHead; - int cur = FindSize(index, &prev); - ASSERT(index < cur); - if (cur == kEnd) { - // No large enough size in list. - *wasted_bytes = 0; - return Failure::RetryAfterGC(owner_); - } - ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. - int rem = cur - index; - int rem_bytes = rem << kPointerSizeLog2; - FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_); - ASSERT(cur_node->Size() == (cur << kPointerSizeLog2)); - FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ + - size_in_bytes); - // Distinguish the cases prev < rem < cur and rem <= prev < cur - // to avoid many redundant tests and calls to Insert/RemoveSize. - if (prev < rem) { - // Simple case: insert rem between prev and cur. - finger_ = prev; - free_[prev].next_size_ = rem; - // If this was the last block of size cur, remove the size. - if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { - free_[rem].next_size_ = free_[cur].next_size_; - } else { - free_[rem].next_size_ = cur; - } - // Add the remainder block. - rem_node->set_size(heap_, rem_bytes); - rem_node->set_next(heap_, free_[rem].head_node_); - free_[rem].head_node_ = rem_node->address(); + if (node == NULL) return NULL; + + while (node != NULL && + Page::FromAddress(node->address())->IsEvacuationCandidate()) { + available_ -= node->Size(); + node = node->next(); + } + + if (node != NULL) { + *node_size = node->Size(); + *list = node->next(); } else { - // If this was the last block of size cur, remove the size. - if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { - finger_ = prev; - free_[prev].next_size_ = free_[cur].next_size_; - } - if (rem_bytes < kMinBlockSize) { - // Too-small remainder is wasted. - rem_node->set_size(heap_, rem_bytes); - available_ -= size_in_bytes + rem_bytes; - *wasted_bytes = rem_bytes; - return cur_node; - } - // Add the remainder block and, if needed, insert its size. - rem_node->set_size(heap_, rem_bytes); - rem_node->set_next(heap_, free_[rem].head_node_); - free_[rem].head_node_ = rem_node->address(); - if (rem_node->next(heap_) == NULL) InsertSize(rem); + *list = NULL; } - available_ -= size_in_bytes; - *wasted_bytes = 0; - return cur_node; + + return node; } -void OldSpaceFreeList::MarkNodes() { - for (int i = 0; i < kFreeListsLength; i++) { - Address cur_addr = free_[i].head_node_; - while (cur_addr != NULL) { - FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); - cur_addr = cur_node->next(heap_); - cur_node->SetMark(); - } +FreeListNode* FreeList::FindNodeFor(int size_in_bytes, int* node_size) { + FreeListNode* node = NULL; + + if (size_in_bytes <= kSmallAllocationMax) { + node = PickNodeFromList(&small_list_, node_size); + if (node != NULL) return node; + } + + if (size_in_bytes <= kMediumAllocationMax) { + node = PickNodeFromList(&medium_list_, node_size); + if (node != NULL) return node; } -} + if (size_in_bytes <= kLargeAllocationMax) { + node = PickNodeFromList(&large_list_, node_size); + if (node != NULL) return node; + } -#ifdef DEBUG -bool OldSpaceFreeList::Contains(FreeListNode* node) { - for (int i = 0; i < kFreeListsLength; i++) { - Address cur_addr = free_[i].head_node_; - while (cur_addr != NULL) { - FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); - if (cur_node == node) return true; - cur_addr = cur_node->next(heap_); + for (FreeListNode** cur = &huge_list_; + *cur != NULL; + cur = (*cur)->next_address()) { + FreeListNode* cur_node = *cur; + while (cur_node != NULL && + Page::FromAddress(cur_node->address())->IsEvacuationCandidate()) { + available_ -= reinterpret_cast<FreeSpace*>(cur_node)->Size(); + cur_node = cur_node->next(); + } + + *cur = cur_node; + if (cur_node == NULL) break; + + ASSERT((*cur)->map() == HEAP->raw_unchecked_free_space_map()); + FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(*cur); + int size = cur_as_free_space->Size(); + if (size >= size_in_bytes) { + // Large enough node found. Unlink it from the list. + node = *cur; + *node_size = size; + *cur = node->next(); + break; } } - return false; + + return node; } -#endif -FixedSizeFreeList::FixedSizeFreeList(Heap* heap, - AllocationSpace owner, - int object_size) - : heap_(heap), owner_(owner), object_size_(object_size) { - Reset(); -} +// Allocation on the old space free list. If it succeeds then a new linear +// allocation space has been set up with the top and limit of the space. If +// the allocation fails then NULL is returned, and the caller can perform a GC +// or allocate a new page before retrying. +HeapObject* FreeList::Allocate(int size_in_bytes) { + ASSERT(0 < size_in_bytes); + ASSERT(size_in_bytes <= kMaxBlockSize); + ASSERT(IsAligned(size_in_bytes, kPointerSize)); + // Don't free list allocate if there is linear space available. + ASSERT(owner_->limit() - owner_->top() < size_in_bytes); + int new_node_size = 0; + FreeListNode* new_node = FindNodeFor(size_in_bytes, &new_node_size); + if (new_node == NULL) return NULL; -void FixedSizeFreeList::Reset() { - available_ = 0; - head_ = tail_ = NULL; -} + available_ -= new_node_size; + ASSERT(IsVeryLong() || available_ == SumFreeLists()); + int bytes_left = new_node_size - size_in_bytes; + ASSERT(bytes_left >= 0); + + int old_linear_size = static_cast<int>(owner_->limit() - owner_->top()); + // Mark the old linear allocation area with a free space map so it can be + // skipped when scanning the heap. This also puts it back in the free list + // if it is big enough. + owner_->Free(owner_->top(), old_linear_size); -void FixedSizeFreeList::Free(Address start) { #ifdef DEBUG - Isolate::Current()->memory_allocator()->ZapBlock(start, object_size_); + for (int i = 0; i < size_in_bytes / kPointerSize; i++) { + reinterpret_cast<Object**>(new_node->address())[i] = Smi::FromInt(0); + } #endif - // We only use the freelists with mark-sweep. - ASSERT(!HEAP->mark_compact_collector()->IsCompacting()); - FreeListNode* node = FreeListNode::FromAddress(start); - node->set_size(heap_, object_size_); - node->set_next(heap_, NULL); - if (head_ == NULL) { - tail_ = head_ = node->address(); + + owner_->heap()->incremental_marking()->OldSpaceStep( + size_in_bytes - old_linear_size); + + // The old-space-step might have finished sweeping and restarted marking. + // Verify that it did not turn the page of the new node into an evacuation + // candidate. + ASSERT(!MarkCompactCollector::IsOnEvacuationCandidate(new_node)); + + const int kThreshold = IncrementalMarking::kAllocatedThreshold; + + // Memory in the linear allocation area is counted as allocated. We may free + // a little of this again immediately - see below. + owner_->Allocate(new_node_size); + + if (bytes_left > kThreshold && + owner_->heap()->incremental_marking()->IsMarkingIncomplete() && + FLAG_incremental_marking_steps) { + int linear_size = owner_->RoundSizeDownToObjectAlignment(kThreshold); + // We don't want to give too large linear areas to the allocator while + // incremental marking is going on, because we won't check again whether + // we want to do another increment until the linear area is used up. + owner_->Free(new_node->address() + size_in_bytes + linear_size, + new_node_size - size_in_bytes - linear_size); + owner_->SetTop(new_node->address() + size_in_bytes, + new_node->address() + size_in_bytes + linear_size); + } else if (bytes_left > 0) { + // Normally we give the rest of the node to the allocator as its new + // linear allocation area. + owner_->SetTop(new_node->address() + size_in_bytes, + new_node->address() + new_node_size); } else { - FreeListNode::FromAddress(tail_)->set_next(heap_, node->address()); - tail_ = node->address(); + // TODO(gc) Try not freeing linear allocation region when bytes_left + // are zero. + owner_->SetTop(NULL, NULL); } - available_ += object_size_; + + return new_node; } -MaybeObject* FixedSizeFreeList::Allocate() { - if (head_ == NULL) { - return Failure::RetryAfterGC(owner_); +static intptr_t CountFreeListItemsInList(FreeListNode* n, Page* p) { + intptr_t sum = 0; + while (n != NULL) { + if (Page::FromAddress(n->address()) == p) { + FreeSpace* free_space = reinterpret_cast<FreeSpace*>(n); + sum += free_space->Size(); + } + n = n->next(); } + return sum; +} - ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. - FreeListNode* node = FreeListNode::FromAddress(head_); - head_ = node->next(heap_); - available_ -= object_size_; - return node; + +void FreeList::CountFreeListItems(Page* p, SizeStats* sizes) { + sizes->huge_size_ = CountFreeListItemsInList(huge_list_, p); + if (sizes->huge_size_ < Page::kObjectAreaSize) { + sizes->small_size_ = CountFreeListItemsInList(small_list_, p); + sizes->medium_size_ = CountFreeListItemsInList(medium_list_, p); + sizes->large_size_ = CountFreeListItemsInList(large_list_, p); + } else { + sizes->small_size_ = 0; + sizes->medium_size_ = 0; + sizes->large_size_ = 0; + } } -void FixedSizeFreeList::MarkNodes() { - Address cur_addr = head_; - while (cur_addr != NULL && cur_addr != tail_) { - FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); - cur_addr = cur_node->next(heap_); - cur_node->SetMark(); +static intptr_t EvictFreeListItemsInList(FreeListNode** n, Page* p) { + intptr_t sum = 0; + while (*n != NULL) { + if (Page::FromAddress((*n)->address()) == p) { + FreeSpace* free_space = reinterpret_cast<FreeSpace*>(*n); + sum += free_space->Size(); + *n = (*n)->next(); + } else { + n = (*n)->next_address(); + } } + return sum; } -// ----------------------------------------------------------------------------- -// OldSpace implementation +intptr_t FreeList::EvictFreeListItems(Page* p) { + intptr_t sum = EvictFreeListItemsInList(&huge_list_, p); -void OldSpace::PrepareForMarkCompact(bool will_compact) { - // Call prepare of the super class. - PagedSpace::PrepareForMarkCompact(will_compact); - - if (will_compact) { - // Reset relocation info. During a compacting collection, everything in - // the space is considered 'available' and we will rediscover live data - // and waste during the collection. - MCResetRelocationInfo(); - ASSERT(Available() == Capacity()); - } else { - // During a non-compacting collection, everything below the linear - // allocation pointer is considered allocated (everything above is - // available) and we will rediscover available and wasted bytes during - // the collection. - accounting_stats_.AllocateBytes(free_list_.available()); - accounting_stats_.FillWastedBytes(Waste()); + if (sum < Page::kObjectAreaSize) { + sum += EvictFreeListItemsInList(&small_list_, p) + + EvictFreeListItemsInList(&medium_list_, p) + + EvictFreeListItemsInList(&large_list_, p); } - // Clear the free list before a full GC---it will be rebuilt afterward. - free_list_.Reset(); + available_ -= static_cast<int>(sum); + + return sum; } -void OldSpace::MCCommitRelocationInfo() { - // Update fast allocation info. - allocation_info_.top = mc_forwarding_info_.top; - allocation_info_.limit = mc_forwarding_info_.limit; - ASSERT(allocation_info_.VerifyPagedAllocation()); +#ifdef DEBUG +intptr_t FreeList::SumFreeList(FreeListNode* cur) { + intptr_t sum = 0; + while (cur != NULL) { + ASSERT(cur->map() == HEAP->raw_unchecked_free_space_map()); + FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(cur); + sum += cur_as_free_space->Size(); + cur = cur->next(); + } + return sum; +} - // The space is compacted and we haven't yet built free lists or - // wasted any space. - ASSERT(Waste() == 0); - ASSERT(AvailableFree() == 0); - // Build the free list for the space. - int computed_size = 0; - PageIterator it(this, PageIterator::PAGES_USED_BY_MC); - while (it.has_next()) { - Page* p = it.next(); - // Space below the relocation pointer is allocated. - computed_size += - static_cast<int>(p->AllocationWatermark() - p->ObjectAreaStart()); - if (it.has_next()) { - // Free the space at the top of the page. - int extra_size = - static_cast<int>(p->ObjectAreaEnd() - p->AllocationWatermark()); - if (extra_size > 0) { - int wasted_bytes = free_list_.Free(p->AllocationWatermark(), - extra_size); - // The bytes we have just "freed" to add to the free list were - // already accounted as available. - accounting_stats_.WasteBytes(wasted_bytes); - } - } +static const int kVeryLongFreeList = 500; + + +int FreeList::FreeListLength(FreeListNode* cur) { + int length = 0; + while (cur != NULL) { + length++; + cur = cur->next(); + if (length == kVeryLongFreeList) return length; } + return length; +} - // Make sure the computed size - based on the used portion of the pages in - // use - matches the size obtained while computing forwarding addresses. - ASSERT(computed_size == Size()); + +bool FreeList::IsVeryLong() { + if (FreeListLength(small_list_) == kVeryLongFreeList) return true; + if (FreeListLength(medium_list_) == kVeryLongFreeList) return true; + if (FreeListLength(large_list_) == kVeryLongFreeList) return true; + if (FreeListLength(huge_list_) == kVeryLongFreeList) return true; + return false; } +// This can take a very long time because it is linear in the number of entries +// on the free list, so it should not be called if FreeListLength returns +// kVeryLongFreeList. +intptr_t FreeList::SumFreeLists() { + intptr_t sum = SumFreeList(small_list_); + sum += SumFreeList(medium_list_); + sum += SumFreeList(large_list_); + sum += SumFreeList(huge_list_); + return sum; +} +#endif + + +// ----------------------------------------------------------------------------- +// OldSpace implementation + bool NewSpace::ReserveSpace(int bytes) { // We can't reliably unpack a partial snapshot that needs more new space - // space than the minimum NewSpace size. + // space than the minimum NewSpace size. The limit can be set lower than + // the end of new space either because there is more space on the next page + // or because we have lowered the limit in order to get periodic incremental + // marking. The most reliable way to ensure that there is linear space is + // to do the allocation, then rewind the limit. ASSERT(bytes <= InitialCapacity()); - Address limit = allocation_info_.limit; + MaybeObject* maybe = AllocateRaw(bytes); + Object* object = NULL; + if (!maybe->ToObject(&object)) return false; + HeapObject* allocation = HeapObject::cast(object); Address top = allocation_info_.top; - return limit - top >= bytes; + if ((top - bytes) == allocation->address()) { + allocation_info_.top = allocation->address(); + return true; + } + // There may be a borderline case here where the allocation succeeded, but + // the limit and top have moved on to a new page. In that case we try again. + return ReserveSpace(bytes); +} + + +void PagedSpace::PrepareForMarkCompact() { + // We don't have a linear allocation area while sweeping. It will be restored + // on the first allocation after the sweep. + // Mark the old linear allocation area with a free space map so it can be + // skipped when scanning the heap. + int old_linear_size = static_cast<int>(limit() - top()); + Free(top(), old_linear_size); + SetTop(NULL, NULL); + + // Stop lazy sweeping and clear marking bits for unswept pages. + if (first_unswept_page_ != NULL) { + Page* p = first_unswept_page_; + do { + // Do not use ShouldBeSweptLazily predicate here. + // New evacuation candidates were selected but they still have + // to be swept before collection starts. + if (!p->WasSwept()) { + Bitmap::Clear(p); + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " lazily abandoned.\n", + reinterpret_cast<intptr_t>(p)); + } + } + p = p->next_page(); + } while (p != anchor()); + } + first_unswept_page_ = Page::FromAddress(NULL); + unswept_free_bytes_ = 0; + + // Clear the free list before a full GC---it will be rebuilt afterward. + free_list_.Reset(); } -void PagedSpace::FreePages(Page* prev, Page* last) { - if (last == AllocationTopPage()) { - // Pages are already at the end of used pages. - return; - } +bool PagedSpace::ReserveSpace(int size_in_bytes) { + ASSERT(size_in_bytes <= Page::kMaxHeapObjectSize); + ASSERT(size_in_bytes == RoundSizeDownToObjectAlignment(size_in_bytes)); + Address current_top = allocation_info_.top; + Address new_top = current_top + size_in_bytes; + if (new_top <= allocation_info_.limit) return true; - Page* first = NULL; + HeapObject* new_area = free_list_.Allocate(size_in_bytes); + if (new_area == NULL) new_area = SlowAllocateRaw(size_in_bytes); + if (new_area == NULL) return false; - // Remove pages from the list. - if (prev == NULL) { - first = first_page_; - first_page_ = last->next_page(); - } else { - first = prev->next_page(); - heap()->isolate()->memory_allocator()->SetNextPage( - prev, last->next_page()); - } + int old_linear_size = static_cast<int>(limit() - top()); + // Mark the old linear allocation area with a free space so it can be + // skipped when scanning the heap. This also puts it back in the free list + // if it is big enough. + Free(top(), old_linear_size); - // Attach it after the last page. - heap()->isolate()->memory_allocator()->SetNextPage(last_page_, first); - last_page_ = last; - heap()->isolate()->memory_allocator()->SetNextPage(last, NULL); + SetTop(new_area->address(), new_area->address() + size_in_bytes); + Allocate(size_in_bytes); + return true; +} - // Clean them up. - do { - first->InvalidateWatermark(true); - first->SetAllocationWatermark(first->ObjectAreaStart()); - first->SetCachedAllocationWatermark(first->ObjectAreaStart()); - first->SetRegionMarks(Page::kAllRegionsCleanMarks); - first = first->next_page(); - } while (first != NULL); - - // Order of pages in this space might no longer be consistent with - // order of pages in chunks. - page_list_is_chunk_ordered_ = false; -} - - -void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) { - const bool add_to_freelist = true; - - // Mark used and unused pages to properly fill unused pages - // after reordering. - PageIterator all_pages_iterator(this, PageIterator::ALL_PAGES); - Page* last_in_use = AllocationTopPage(); - bool in_use = true; - - while (all_pages_iterator.has_next()) { - Page* p = all_pages_iterator.next(); - p->SetWasInUseBeforeMC(in_use); - if (p == last_in_use) { - // We passed a page containing allocation top. All consequent - // pages are not used. - in_use = false; - } - } - if (page_list_is_chunk_ordered_) return; +// You have to call this last, since the implementation from PagedSpace +// doesn't know that memory was 'promised' to large object space. +bool LargeObjectSpace::ReserveSpace(int bytes) { + return heap()->OldGenerationSpaceAvailable() >= bytes; +} - Page* new_last_in_use = Page::FromAddress(NULL); - heap()->isolate()->memory_allocator()->RelinkPageListInChunkOrder( - this, &first_page_, &last_page_, &new_last_in_use); - ASSERT(new_last_in_use->is_valid()); - if (new_last_in_use != last_in_use) { - // Current allocation top points to a page which is now in the middle - // of page list. We should move allocation top forward to the new last - // used page so various object iterators will continue to work properly. - int size_in_bytes = static_cast<int>(PageAllocationLimit(last_in_use) - - last_in_use->AllocationTop()); +bool PagedSpace::AdvanceSweeper(intptr_t bytes_to_sweep) { + if (IsSweepingComplete()) return true; - last_in_use->SetAllocationWatermark(last_in_use->AllocationTop()); - if (size_in_bytes > 0) { - Address start = last_in_use->AllocationTop(); - if (deallocate_blocks) { - accounting_stats_.AllocateBytes(size_in_bytes); - DeallocateBlock(start, size_in_bytes, add_to_freelist); - } else { - heap()->CreateFillerObjectAt(start, size_in_bytes); + intptr_t freed_bytes = 0; + Page* p = first_unswept_page_; + do { + Page* next_page = p->next_page(); + if (ShouldBeSweptLazily(p)) { + if (FLAG_gc_verbose) { + PrintF("Sweeping 0x%" V8PRIxPTR " lazily advanced.\n", + reinterpret_cast<intptr_t>(p)); } + DecreaseUnsweptFreeBytes(p); + freed_bytes += MarkCompactCollector::SweepConservatively(this, p); } + p = next_page; + } while (p != anchor() && freed_bytes < bytes_to_sweep); - // New last in use page was in the middle of the list before - // sorting so it full. - SetTop(new_last_in_use->AllocationTop()); - - ASSERT(AllocationTopPage() == new_last_in_use); - ASSERT(AllocationTopPage()->WasInUseBeforeMC()); + if (p == anchor()) { + first_unswept_page_ = Page::FromAddress(NULL); + } else { + first_unswept_page_ = p; } - PageIterator pages_in_use_iterator(this, PageIterator::PAGES_IN_USE); - while (pages_in_use_iterator.has_next()) { - Page* p = pages_in_use_iterator.next(); - if (!p->WasInUseBeforeMC()) { - // Empty page is in the middle of a sequence of used pages. - // Allocate it as a whole and deallocate immediately. - int size_in_bytes = static_cast<int>(PageAllocationLimit(p) - - p->ObjectAreaStart()); + heap()->LowerOldGenLimits(freed_bytes); - p->SetAllocationWatermark(p->ObjectAreaStart()); - Address start = p->ObjectAreaStart(); - if (deallocate_blocks) { - accounting_stats_.AllocateBytes(size_in_bytes); - DeallocateBlock(start, size_in_bytes, add_to_freelist); - } else { - heap()->CreateFillerObjectAt(start, size_in_bytes); - } - } - } + heap()->FreeQueuedChunks(); - page_list_is_chunk_ordered_ = true; + return IsSweepingComplete(); } -void PagedSpace::PrepareForMarkCompact(bool will_compact) { - if (will_compact) { - RelinkPageListInChunkOrder(false); - } -} +void PagedSpace::EvictEvacuationCandidatesFromFreeLists() { + if (allocation_info_.top >= allocation_info_.limit) return; + if (Page::FromAllocationTop(allocation_info_.top)->IsEvacuationCandidate()) { + // Create filler object to keep page iterable if it was iterable. + int remaining = + static_cast<int>(allocation_info_.limit - allocation_info_.top); + heap()->CreateFillerObjectAt(allocation_info_.top, remaining); -bool PagedSpace::ReserveSpace(int bytes) { - Address limit = allocation_info_.limit; - Address top = allocation_info_.top; - if (limit - top >= bytes) return true; - - // There wasn't enough space in the current page. Lets put the rest - // of the page on the free list and start a fresh page. - PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); - - Page* reserved_page = TopPageOf(allocation_info_); - int bytes_left_to_reserve = bytes; - while (bytes_left_to_reserve > 0) { - if (!reserved_page->next_page()->is_valid()) { - if (heap()->OldGenerationAllocationLimitReached()) return false; - Expand(reserved_page); - } - bytes_left_to_reserve -= Page::kPageSize; - reserved_page = reserved_page->next_page(); - if (!reserved_page->is_valid()) return false; - } - ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); - TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); - SetAllocationInfo(&allocation_info_, - TopPageOf(allocation_info_)->next_page()); - return true; + allocation_info_.top = NULL; + allocation_info_.limit = NULL; + } } -// You have to call this last, since the implementation from PagedSpace -// doesn't know that memory was 'promised' to large object space. -bool LargeObjectSpace::ReserveSpace(int bytes) { - return heap()->OldGenerationSpaceAvailable() >= bytes; -} - +HeapObject* PagedSpace::SlowAllocateRaw(int size_in_bytes) { + // Allocation in this space has failed. -// Slow case for normal allocation. Try in order: (1) allocate in the next -// page in the space, (2) allocate off the space's free list, (3) expand the -// space, (4) fail. -HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) { - // Linear allocation in this space has failed. If there is another page - // in the space, move to that page and allocate there. This allocation - // should succeed (size_in_bytes should not be greater than a page's - // object area size). - Page* current_page = TopPageOf(allocation_info_); - if (current_page->next_page()->is_valid()) { - return AllocateInNextPage(current_page, size_in_bytes); - } - - // There is no next page in this space. Try free list allocation unless that - // is currently forbidden. - if (!heap()->linear_allocation()) { - int wasted_bytes; - Object* result; - MaybeObject* maybe = free_list_.Allocate(size_in_bytes, &wasted_bytes); - accounting_stats_.WasteBytes(wasted_bytes); - if (maybe->ToObject(&result)) { - accounting_stats_.AllocateBytes(size_in_bytes); - - HeapObject* obj = HeapObject::cast(result); - Page* p = Page::FromAddress(obj->address()); - - if (obj->address() >= p->AllocationWatermark()) { - // There should be no hole between the allocation watermark - // and allocated object address. - // Memory above the allocation watermark was not swept and - // might contain garbage pointers to new space. - ASSERT(obj->address() == p->AllocationWatermark()); - p->SetAllocationWatermark(obj->address() + size_in_bytes); - } + // If there are unswept pages advance lazy sweeper then sweep one page before + // allocating a new page. + if (first_unswept_page_->is_valid()) { + AdvanceSweeper(size_in_bytes); - return obj; - } + // Retry the free list allocation. + HeapObject* object = free_list_.Allocate(size_in_bytes); + if (object != NULL) return object; } // Free list allocation failed and there is no next page. Fail if we have @@ -2254,60 +2182,22 @@ HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) { } // Try to expand the space and allocate in the new next page. - ASSERT(!current_page->next_page()->is_valid()); - if (Expand(current_page)) { - return AllocateInNextPage(current_page, size_in_bytes); + if (Expand()) { + return free_list_.Allocate(size_in_bytes); } - // Finally, fail. - return NULL; -} - + // Last ditch, sweep all the remaining pages to try to find space. This may + // cause a pause. + if (!IsSweepingComplete()) { + AdvanceSweeper(kMaxInt); -void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { - current_page->SetAllocationWatermark(allocation_info_.top); - int free_size = - static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); - if (free_size > 0) { - int wasted_bytes = free_list_.Free(allocation_info_.top, free_size); - accounting_stats_.WasteBytes(wasted_bytes); + // Retry the free list allocation. + HeapObject* object = free_list_.Allocate(size_in_bytes); + if (object != NULL) return object; } -} - - -void FixedSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { - current_page->SetAllocationWatermark(allocation_info_.top); - int free_size = - static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); - // In the fixed space free list all the free list items have the right size. - // We use up the rest of the page while preserving this invariant. - while (free_size >= object_size_in_bytes_) { - free_list_.Free(allocation_info_.top); - allocation_info_.top += object_size_in_bytes_; - free_size -= object_size_in_bytes_; - accounting_stats_.WasteBytes(object_size_in_bytes_); - } -} - -// Add the block at the top of the page to the space's free list, set the -// allocation info to the next page (assumed to be one), and allocate -// linearly there. -HeapObject* OldSpace::AllocateInNextPage(Page* current_page, - int size_in_bytes) { - ASSERT(current_page->next_page()->is_valid()); - Page* next_page = current_page->next_page(); - next_page->ClearGCFields(); - PutRestOfCurrentPageOnFreeList(current_page); - SetAllocationInfo(&allocation_info_, next_page); - return AllocateLinearly(&allocation_info_, size_in_bytes); -} - - -void OldSpace::DeallocateBlock(Address start, - int size_in_bytes, - bool add_to_freelist) { - Free(start, size_in_bytes, add_to_freelist); + // Finally, fail. + return NULL; } @@ -2413,7 +2303,7 @@ static void CollectCommentStatistics(Isolate* isolate, RelocIterator* it) { void PagedSpace::CollectCodeStatistics() { Isolate* isolate = heap()->isolate(); HeapObjectIterator obj_it(this); - for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { + for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { if (obj->IsCode()) { Code* code = Code::cast(obj); isolate->code_kind_statistics()[code->kind()] += code->Size(); @@ -2438,16 +2328,17 @@ void PagedSpace::CollectCodeStatistics() { } -void OldSpace::ReportStatistics() { +void PagedSpace::ReportStatistics() { int pct = static_cast<int>(Available() * 100 / Capacity()); PrintF(" capacity: %" V8_PTR_PREFIX "d" ", waste: %" V8_PTR_PREFIX "d" ", available: %" V8_PTR_PREFIX "d, %%%d\n", Capacity(), Waste(), Available(), pct); + if (was_swept_conservatively_) return; ClearHistograms(); HeapObjectIterator obj_it(this); - for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) + for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) CollectHistogramInfo(obj); ReportHistogram(true); } @@ -2456,192 +2347,28 @@ void OldSpace::ReportStatistics() { // ----------------------------------------------------------------------------- // FixedSpace implementation -void FixedSpace::PrepareForMarkCompact(bool will_compact) { +void FixedSpace::PrepareForMarkCompact() { // Call prepare of the super class. - PagedSpace::PrepareForMarkCompact(will_compact); - - if (will_compact) { - // Reset relocation info. - MCResetRelocationInfo(); + PagedSpace::PrepareForMarkCompact(); - // During a compacting collection, everything in the space is considered - // 'available' (set by the call to MCResetRelocationInfo) and we will - // rediscover live and wasted bytes during the collection. - ASSERT(Available() == Capacity()); - } else { - // During a non-compacting collection, everything below the linear - // allocation pointer except wasted top-of-page blocks is considered - // allocated and we will rediscover available bytes during the - // collection. - accounting_stats_.AllocateBytes(free_list_.available()); - } + // During a non-compacting collection, everything below the linear + // allocation pointer except wasted top-of-page blocks is considered + // allocated and we will rediscover available bytes during the + // collection. + accounting_stats_.AllocateBytes(free_list_.available()); // Clear the free list before a full GC---it will be rebuilt afterward. free_list_.Reset(); } -void FixedSpace::MCCommitRelocationInfo() { - // Update fast allocation info. - allocation_info_.top = mc_forwarding_info_.top; - allocation_info_.limit = mc_forwarding_info_.limit; - ASSERT(allocation_info_.VerifyPagedAllocation()); - - // The space is compacted and we haven't yet wasted any space. - ASSERT(Waste() == 0); - - // Update allocation_top of each page in use and compute waste. - int computed_size = 0; - PageIterator it(this, PageIterator::PAGES_USED_BY_MC); - while (it.has_next()) { - Page* page = it.next(); - Address page_top = page->AllocationTop(); - computed_size += static_cast<int>(page_top - page->ObjectAreaStart()); - if (it.has_next()) { - accounting_stats_.WasteBytes( - static_cast<int>(page->ObjectAreaEnd() - page_top)); - page->SetAllocationWatermark(page_top); - } - } - - // Make sure the computed size - based on the used portion of the - // pages in use - matches the size we adjust during allocation. - ASSERT(computed_size == Size()); -} - - -// Slow case for normal allocation. Try in order: (1) allocate in the next -// page in the space, (2) allocate off the space's free list, (3) expand the -// space, (4) fail. -HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { - ASSERT_EQ(object_size_in_bytes_, size_in_bytes); - // Linear allocation in this space has failed. If there is another page - // in the space, move to that page and allocate there. This allocation - // should succeed. - Page* current_page = TopPageOf(allocation_info_); - if (current_page->next_page()->is_valid()) { - return AllocateInNextPage(current_page, size_in_bytes); - } - - // There is no next page in this space. Try free list allocation unless - // that is currently forbidden. The fixed space free list implicitly assumes - // that all free blocks are of the fixed size. - if (!heap()->linear_allocation()) { - Object* result; - MaybeObject* maybe = free_list_.Allocate(); - if (maybe->ToObject(&result)) { - accounting_stats_.AllocateBytes(size_in_bytes); - HeapObject* obj = HeapObject::cast(result); - Page* p = Page::FromAddress(obj->address()); - - if (obj->address() >= p->AllocationWatermark()) { - // There should be no hole between the allocation watermark - // and allocated object address. - // Memory above the allocation watermark was not swept and - // might contain garbage pointers to new space. - ASSERT(obj->address() == p->AllocationWatermark()); - p->SetAllocationWatermark(obj->address() + size_in_bytes); - } - - return obj; - } - } - - // Free list allocation failed and there is no next page. Fail if we have - // hit the old generation size limit that should cause a garbage - // collection. - if (!heap()->always_allocate() && - heap()->OldGenerationAllocationLimitReached()) { - return NULL; - } - - // Try to expand the space and allocate in the new next page. - ASSERT(!current_page->next_page()->is_valid()); - if (Expand(current_page)) { - return AllocateInNextPage(current_page, size_in_bytes); - } - - // Finally, fail. - return NULL; -} - - -// Move to the next page (there is assumed to be one) and allocate there. -// The top of page block is always wasted, because it is too small to hold a -// map. -HeapObject* FixedSpace::AllocateInNextPage(Page* current_page, - int size_in_bytes) { - ASSERT(current_page->next_page()->is_valid()); - ASSERT(allocation_info_.top == PageAllocationLimit(current_page)); - ASSERT_EQ(object_size_in_bytes_, size_in_bytes); - Page* next_page = current_page->next_page(); - next_page->ClearGCFields(); - current_page->SetAllocationWatermark(allocation_info_.top); - accounting_stats_.WasteBytes(page_extra_); - SetAllocationInfo(&allocation_info_, next_page); - return AllocateLinearly(&allocation_info_, size_in_bytes); -} - - -void FixedSpace::DeallocateBlock(Address start, - int size_in_bytes, - bool add_to_freelist) { - // Free-list elements in fixed space are assumed to have a fixed size. - // We break the free block into chunks and add them to the free list - // individually. - int size = object_size_in_bytes(); - ASSERT(size_in_bytes % size == 0); - Address end = start + size_in_bytes; - for (Address a = start; a < end; a += size) { - Free(a, add_to_freelist); - } -} - - -#ifdef DEBUG -void FixedSpace::ReportStatistics() { - int pct = static_cast<int>(Available() * 100 / Capacity()); - PrintF(" capacity: %" V8_PTR_PREFIX "d" - ", waste: %" V8_PTR_PREFIX "d" - ", available: %" V8_PTR_PREFIX "d, %%%d\n", - Capacity(), Waste(), Available(), pct); - - ClearHistograms(); - HeapObjectIterator obj_it(this); - for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) - CollectHistogramInfo(obj); - ReportHistogram(false); -} -#endif - - // ----------------------------------------------------------------------------- // MapSpace implementation -void MapSpace::PrepareForMarkCompact(bool will_compact) { - // Call prepare of the super class. - FixedSpace::PrepareForMarkCompact(will_compact); - - if (will_compact) { - // Initialize map index entry. - int page_count = 0; - PageIterator it(this, PageIterator::ALL_PAGES); - while (it.has_next()) { - ASSERT_MAP_PAGE_INDEX(page_count); - - Page* p = it.next(); - ASSERT(p->mc_page_index == page_count); - - page_addresses_[page_count++] = p->address(); - } - } -} - - #ifdef DEBUG void MapSpace::VerifyObject(HeapObject* object) { // The object should be a map or a free-list node. - ASSERT(object->IsMap() || object->IsByteArray()); + ASSERT(object->IsMap() || object->IsFreeSpace()); } #endif @@ -2662,107 +2389,43 @@ void CellSpace::VerifyObject(HeapObject* object) { // LargeObjectIterator LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { - current_ = space->first_chunk_; + current_ = space->first_page_; size_func_ = NULL; } LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, HeapObjectCallback size_func) { - current_ = space->first_chunk_; + current_ = space->first_page_; size_func_ = size_func; } -HeapObject* LargeObjectIterator::next() { +HeapObject* LargeObjectIterator::Next() { if (current_ == NULL) return NULL; HeapObject* object = current_->GetObject(); - current_ = current_->next(); + current_ = current_->next_page(); return object; } // ----------------------------------------------------------------------------- -// LargeObjectChunk - -LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes, - Executability executable) { - size_t requested = ChunkSizeFor(size_in_bytes); - size_t size; - size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; - Isolate* isolate = Isolate::Current(); - void* mem = isolate->memory_allocator()->AllocateRawMemory( - requested + guard_size, &size, executable); - if (mem == NULL) return NULL; - - // The start of the chunk may be overlayed with a page so we have to - // make sure that the page flags fit in the size field. - ASSERT((size & Page::kPageFlagMask) == 0); - - LOG(isolate, NewEvent("LargeObjectChunk", mem, size)); - if (size < requested + guard_size) { - isolate->memory_allocator()->FreeRawMemory( - mem, size, executable); - LOG(isolate, DeleteEvent("LargeObjectChunk", mem)); - return NULL; - } - - if (guard_size != 0) { - OS::Guard(mem, guard_size); - size -= guard_size; - mem = static_cast<Address>(mem) + guard_size; - } - - ObjectSpace space = (executable == EXECUTABLE) - ? kObjectSpaceCodeSpace - : kObjectSpaceLoSpace; - isolate->memory_allocator()->PerformAllocationCallback( - space, kAllocationActionAllocate, size); - - LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem); - chunk->size_ = size; - chunk->GetPage()->heap_ = isolate->heap(); - return chunk; -} - - -void LargeObjectChunk::Free(Executability executable) { - size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; - ObjectSpace space = - (executable == EXECUTABLE) ? kObjectSpaceCodeSpace : kObjectSpaceLoSpace; - // Do not access instance fields after FreeRawMemory! - Address my_address = address(); - size_t my_size = size(); - Isolate* isolate = GetPage()->heap_->isolate(); - MemoryAllocator* a = isolate->memory_allocator(); - a->FreeRawMemory(my_address - guard_size, my_size + guard_size, executable); - a->PerformAllocationCallback(space, kAllocationActionFree, my_size); - LOG(isolate, DeleteEvent("LargeObjectChunk", my_address)); -} - - -int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) { - int os_alignment = static_cast<int>(OS::AllocateAlignment()); - if (os_alignment < Page::kPageSize) { - size_in_bytes += (Page::kPageSize - os_alignment); - } - return size_in_bytes + Page::kObjectStartOffset; -} - -// ----------------------------------------------------------------------------- // LargeObjectSpace -LargeObjectSpace::LargeObjectSpace(Heap* heap, AllocationSpace id) +LargeObjectSpace::LargeObjectSpace(Heap* heap, + intptr_t max_capacity, + AllocationSpace id) : Space(heap, id, NOT_EXECUTABLE), // Managed on a per-allocation basis - first_chunk_(NULL), + max_capacity_(max_capacity), + first_page_(NULL), size_(0), page_count_(0), objects_size_(0) {} -bool LargeObjectSpace::Setup() { - first_chunk_ = NULL; +bool LargeObjectSpace::SetUp() { + first_page_ = NULL; size_ = 0; page_count_ = 0; objects_size_ = 0; @@ -2771,20 +2434,22 @@ bool LargeObjectSpace::Setup() { void LargeObjectSpace::TearDown() { - while (first_chunk_ != NULL) { - LargeObjectChunk* chunk = first_chunk_; - first_chunk_ = first_chunk_->next(); - chunk->Free(chunk->GetPage()->PageExecutability()); + while (first_page_ != NULL) { + LargePage* page = first_page_; + first_page_ = first_page_->next_page(); + LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", page->address())); + + ObjectSpace space = static_cast<ObjectSpace>(1 << identity()); + heap()->isolate()->memory_allocator()->PerformAllocationCallback( + space, kAllocationActionFree, page->size()); + heap()->isolate()->memory_allocator()->Free(page); } - Setup(); + SetUp(); } -MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, - int object_size, - Executability executable) { - ASSERT(0 < object_size && object_size <= requested_size); - +MaybeObject* LargeObjectSpace::AllocateRaw(int object_size, + Executability executable) { // Check if we want to force a GC before growing the old space further. // If so, fail the allocation. if (!heap()->always_allocate() && @@ -2792,75 +2457,55 @@ MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, return Failure::RetryAfterGC(identity()); } - LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); - if (chunk == NULL) { + if (Size() + object_size > max_capacity_) { return Failure::RetryAfterGC(identity()); } - size_ += static_cast<int>(chunk->size()); - objects_size_ += requested_size; - page_count_++; - chunk->set_next(first_chunk_); - first_chunk_ = chunk; - - // Initialize page header. - Page* page = chunk->GetPage(); - Address object_address = page->ObjectAreaStart(); - - // Clear the low order bit of the second word in the page to flag it as a - // large object page. If the chunk_size happened to be written there, its - // low order bit should already be clear. - page->SetIsLargeObjectPage(true); - page->SetPageExecutability(executable); - page->SetRegionMarks(Page::kAllRegionsCleanMarks); - return HeapObject::FromAddress(object_address); -} - + LargePage* page = heap()->isolate()->memory_allocator()-> + AllocateLargePage(object_size, executable, this); + if (page == NULL) return Failure::RetryAfterGC(identity()); + ASSERT(page->body_size() >= object_size); -MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { - ASSERT(0 < size_in_bytes); - return AllocateRawInternal(size_in_bytes, - size_in_bytes, - EXECUTABLE); -} + size_ += static_cast<int>(page->size()); + objects_size_ += object_size; + page_count_++; + page->set_next_page(first_page_); + first_page_ = page; + HeapObject* object = page->GetObject(); -MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { - ASSERT(0 < size_in_bytes); - return AllocateRawInternal(size_in_bytes, - size_in_bytes, - NOT_EXECUTABLE); -} - +#ifdef DEBUG + // Make the object consistent so the heap can be vefified in OldSpaceStep. + reinterpret_cast<Object**>(object->address())[0] = + heap()->fixed_array_map(); + reinterpret_cast<Object**>(object->address())[1] = Smi::FromInt(0); +#endif -MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { - ASSERT(0 < size_in_bytes); - return AllocateRawInternal(size_in_bytes, - size_in_bytes, - NOT_EXECUTABLE); + heap()->incremental_marking()->OldSpaceStep(object_size); + return object; } // GC support MaybeObject* LargeObjectSpace::FindObject(Address a) { - for (LargeObjectChunk* chunk = first_chunk_; - chunk != NULL; - chunk = chunk->next()) { - Address chunk_address = chunk->address(); - if (chunk_address <= a && a < chunk_address + chunk->size()) { - return chunk->GetObject(); + for (LargePage* page = first_page_; + page != NULL; + page = page->next_page()) { + Address page_address = page->address(); + if (page_address <= a && a < page_address + page->size()) { + return page->GetObject(); } } return Failure::Exception(); } -LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { +LargePage* LargeObjectSpace::FindPageContainingPc(Address pc) { // TODO(853): Change this implementation to only find executable // chunks and use some kind of hash-based approach to speed it up. - for (LargeObjectChunk* chunk = first_chunk_; + for (LargePage* chunk = first_page_; chunk != NULL; - chunk = chunk->next()) { + chunk = chunk->next_page()) { Address chunk_address = chunk->address(); if (chunk_address <= pc && pc < chunk_address + chunk->size()) { return chunk; @@ -2870,112 +2515,57 @@ LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { } -void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { - LargeObjectIterator it(this); - for (HeapObject* object = it.next(); object != NULL; object = it.next()) { - // We only have code, sequential strings, or fixed arrays in large - // object space, and only fixed arrays can possibly contain pointers to - // the young generation. - if (object->IsFixedArray()) { - Page* page = Page::FromAddress(object->address()); - uint32_t marks = page->GetRegionMarks(); - uint32_t newmarks = Page::kAllRegionsCleanMarks; - - if (marks != Page::kAllRegionsCleanMarks) { - // For a large page a single dirty mark corresponds to several - // regions (modulo 32). So we treat a large page as a sequence of - // normal pages of size Page::kPageSize having same dirty marks - // and subsequently iterate dirty regions on each of these pages. - Address start = object->address(); - Address end = page->ObjectAreaEnd(); - Address object_end = start + object->Size(); - - // Iterate regions of the first normal page covering object. - uint32_t first_region_number = page->GetRegionNumberForAddress(start); - newmarks |= - heap()->IterateDirtyRegions(marks >> first_region_number, - start, - end, - &Heap::IteratePointersInDirtyRegion, - copy_object) << first_region_number; - - start = end; - end = start + Page::kPageSize; - while (end <= object_end) { - // Iterate next 32 regions. - newmarks |= - heap()->IterateDirtyRegions(marks, - start, - end, - &Heap::IteratePointersInDirtyRegion, - copy_object); - start = end; - end = start + Page::kPageSize; - } - - if (start != object_end) { - // Iterate the last piece of an object which is less than - // Page::kPageSize. - newmarks |= - heap()->IterateDirtyRegions(marks, - start, - object_end, - &Heap::IteratePointersInDirtyRegion, - copy_object); - } - - page->SetRegionMarks(newmarks); - } - } - } -} - - void LargeObjectSpace::FreeUnmarkedObjects() { - LargeObjectChunk* previous = NULL; - LargeObjectChunk* current = first_chunk_; + LargePage* previous = NULL; + LargePage* current = first_page_; while (current != NULL) { HeapObject* object = current->GetObject(); - if (object->IsMarked()) { - object->ClearMark(); - heap()->mark_compact_collector()->tracer()->decrement_marked_count(); + // Can this large page contain pointers to non-trivial objects. No other + // pointer object is this big. + bool is_pointer_object = object->IsFixedArray(); + MarkBit mark_bit = Marking::MarkBitFrom(object); + if (mark_bit.Get()) { + mark_bit.Clear(); + MemoryChunk::IncrementLiveBytesFromGC(object->address(), -object->Size()); previous = current; - current = current->next(); + current = current->next_page(); } else { + LargePage* page = current; // Cut the chunk out from the chunk list. - LargeObjectChunk* current_chunk = current; - current = current->next(); + current = current->next_page(); if (previous == NULL) { - first_chunk_ = current; + first_page_ = current; } else { - previous->set_next(current); + previous->set_next_page(current); } // Free the chunk. heap()->mark_compact_collector()->ReportDeleteIfNeeded( object, heap()->isolate()); - LiveObjectList::ProcessNonLive(object); - - size_ -= static_cast<int>(current_chunk->size()); + size_ -= static_cast<int>(page->size()); objects_size_ -= object->Size(); page_count_--; - current_chunk->Free(current_chunk->GetPage()->PageExecutability()); + + if (is_pointer_object) { + heap()->QueueMemoryChunkForFree(page); + } else { + heap()->isolate()->memory_allocator()->Free(page); + } } } + heap()->FreeQueuedChunks(); } bool LargeObjectSpace::Contains(HeapObject* object) { Address address = object->address(); - if (heap()->new_space()->Contains(address)) { - return false; - } - Page* page = Page::FromAddress(address); + MemoryChunk* chunk = MemoryChunk::FromAddress(address); + + bool owned = (chunk->owner() == this); - SLOW_ASSERT(!page->IsLargeObjectPage() - || !FindObject(address)->IsFailure()); + SLOW_ASSERT(!owned || !FindObject(address)->IsFailure()); - return page->IsLargeObjectPage(); + return owned; } @@ -2983,9 +2573,9 @@ bool LargeObjectSpace::Contains(HeapObject* object) { // We do not assume that the large object iterator works, because it depends // on the invariants we are checking during verification. void LargeObjectSpace::Verify() { - for (LargeObjectChunk* chunk = first_chunk_; + for (LargePage* chunk = first_page_; chunk != NULL; - chunk = chunk->next()) { + chunk = chunk->next_page()) { // Each chunk contains an object that starts at the large object page's // object area start. HeapObject* object = chunk->GetObject(); @@ -3015,9 +2605,6 @@ void LargeObjectSpace::Verify() { object->Size(), &code_visitor); } else if (object->IsFixedArray()) { - // We loop over fixed arrays ourselves, rather then using the visitor, - // because the visitor doesn't support the start/offset iteration - // needed for IsRegionDirty. FixedArray* array = FixedArray::cast(object); for (int j = 0; j < array->length(); j++) { Object* element = array->get(j); @@ -3025,13 +2612,6 @@ void LargeObjectSpace::Verify() { HeapObject* element_object = HeapObject::cast(element); ASSERT(heap()->Contains(element_object)); ASSERT(element_object->map()->IsMap()); - if (heap()->InNewSpace(element_object)) { - Address array_addr = object->address(); - Address element_addr = array_addr + FixedArray::kHeaderSize + - j * kPointerSize; - - ASSERT(Page::FromAddress(array_addr)->IsRegionDirty(element_addr)); - } } } } @@ -3041,7 +2621,7 @@ void LargeObjectSpace::Verify() { void LargeObjectSpace::Print() { LargeObjectIterator it(this); - for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { obj->Print(); } } @@ -3052,7 +2632,7 @@ void LargeObjectSpace::ReportStatistics() { int num_objects = 0; ClearHistograms(); LargeObjectIterator it(this); - for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { + for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { num_objects++; CollectHistogramInfo(obj); } @@ -3066,13 +2646,38 @@ void LargeObjectSpace::ReportStatistics() { void LargeObjectSpace::CollectCodeStatistics() { Isolate* isolate = heap()->isolate(); LargeObjectIterator obj_it(this); - for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { + for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { if (obj->IsCode()) { Code* code = Code::cast(obj); isolate->code_kind_statistics()[code->kind()] += code->Size(); } } } + + +void Page::Print() { + // Make a best-effort to print the objects in the page. + PrintF("Page@%p in %s\n", + this->address(), + AllocationSpaceName(this->owner()->identity())); + printf(" --------------------------------------\n"); + HeapObjectIterator objects(this, heap()->GcSafeSizeOfOldObjectFunction()); + unsigned mark_size = 0; + for (HeapObject* object = objects.Next(); + object != NULL; + object = objects.Next()) { + bool is_marked = Marking::MarkBitFrom(object).Get(); + PrintF(" %c ", (is_marked ? '!' : ' ')); // Indent a little. + if (is_marked) { + mark_size += heap()->GcSafeSizeOfOldObjectFunction()(object); + } + object->ShortPrint(); + PrintF("\n"); + } + printf(" --------------------------------------\n"); + printf(" Marked: %x, LiveCount: %x\n", mark_size, LiveBytes()); +} + #endif // DEBUG } } // namespace v8::internal diff --git a/deps/v8/src/spaces.h b/deps/v8/src/spaces.h index f1564967e1..0ff62b58e4 100644 --- a/deps/v8/src/spaces.h +++ b/deps/v8/src/spaces.h @@ -49,45 +49,47 @@ class Isolate; // // The semispaces of the young generation are contiguous. The old and map // spaces consists of a list of pages. A page has a page header and an object -// area. A page size is deliberately chosen as 8K bytes. -// The first word of a page is an opaque page header that has the -// address of the next page and its ownership information. The second word may -// have the allocation top address of this page. Heap objects are aligned to the -// pointer size. +// area. // // There is a separate large object space for objects larger than // Page::kMaxHeapObjectSize, so that they do not have to move during // collection. The large object space is paged. Pages in large object space -// may be larger than 8K. +// may be larger than the page size. // -// A card marking write barrier is used to keep track of intergenerational -// references. Old space pages are divided into regions of Page::kRegionSize -// size. Each region has a corresponding dirty bit in the page header which is -// set if the region might contain pointers to new space. For details about -// dirty bits encoding see comments in the Page::GetRegionNumberForAddress() -// method body. +// A store-buffer based write barrier is used to keep track of intergenerational +// references. See store-buffer.h. // -// During scavenges and mark-sweep collections we iterate intergenerational -// pointers without decoding heap object maps so if the page belongs to old -// pointer space or large object space it is essential to guarantee that -// the page does not contain any garbage pointers to new space: every pointer -// aligned word which satisfies the Heap::InNewSpace() predicate must be a -// pointer to a live heap object in new space. Thus objects in old pointer -// and large object spaces should have a special layout (e.g. no bare integer -// fields). This requirement does not apply to map space which is iterated in -// a special fashion. However we still require pointer fields of dead maps to -// be cleaned. +// During scavenges and mark-sweep collections we sometimes (after a store +// buffer overflow) iterate intergenerational pointers without decoding heap +// object maps so if the page belongs to old pointer space or large object +// space it is essential to guarantee that the page does not contain any +// garbage pointers to new space: every pointer aligned word which satisfies +// the Heap::InNewSpace() predicate must be a pointer to a live heap object in +// new space. Thus objects in old pointer and large object spaces should have a +// special layout (e.g. no bare integer fields). This requirement does not +// apply to map space which is iterated in a special fashion. However we still +// require pointer fields of dead maps to be cleaned. // -// To enable lazy cleaning of old space pages we use a notion of allocation -// watermark. Every pointer under watermark is considered to be well formed. -// Page allocation watermark is not necessarily equal to page allocation top but -// all alive objects on page should reside under allocation watermark. -// During scavenge allocation watermark might be bumped and invalid pointers -// might appear below it. To avoid following them we store a valid watermark -// into special field in the page header and set a page WATERMARK_INVALIDATED -// flag. For details see comments in the Page::SetAllocationWatermark() method -// body. +// To enable lazy cleaning of old space pages we can mark chunks of the page +// as being garbage. Garbage sections are marked with a special map. These +// sections are skipped when scanning the page, even if we are otherwise +// scanning without regard for object boundaries. Garbage sections are chained +// together to form a free list after a GC. Garbage sections created outside +// of GCs by object trunctation etc. may not be in the free list chain. Very +// small free spaces are ignored, they need only be cleaned of bogus pointers +// into new space. // +// Each page may have up to one special garbage section. The start of this +// section is denoted by the top field in the space. The end of the section +// is denoted by the limit field in the space. This special garbage section +// is not marked with a free space map in the data. The point of this section +// is to enable linear allocation without having to constantly update the byte +// array every time the top field is updated and a new object is created. The +// special garbage section is not in the chain of garbage sections. +// +// Since the top and limit fields are in the space, not the page, only one page +// has a special garbage section, and if the top and limit are equal then there +// is no special garbage section. // Some assertion macros used in the debugging mode. @@ -114,30 +116,528 @@ class Isolate; class PagedSpace; class MemoryAllocator; class AllocationInfo; +class Space; +class FreeList; +class MemoryChunk; + +class MarkBit { + public: + typedef uint32_t CellType; + + inline MarkBit(CellType* cell, CellType mask, bool data_only) + : cell_(cell), mask_(mask), data_only_(data_only) { } + + inline CellType* cell() { return cell_; } + inline CellType mask() { return mask_; } + +#ifdef DEBUG + bool operator==(const MarkBit& other) { + return cell_ == other.cell_ && mask_ == other.mask_; + } +#endif + + inline void Set() { *cell_ |= mask_; } + inline bool Get() { return (*cell_ & mask_) != 0; } + inline void Clear() { *cell_ &= ~mask_; } + + inline bool data_only() { return data_only_; } + + inline MarkBit Next() { + CellType new_mask = mask_ << 1; + if (new_mask == 0) { + return MarkBit(cell_ + 1, 1, data_only_); + } else { + return MarkBit(cell_, new_mask, data_only_); + } + } + + private: + CellType* cell_; + CellType mask_; + // This boolean indicates that the object is in a data-only space with no + // pointers. This enables some optimizations when marking. + // It is expected that this field is inlined and turned into control flow + // at the place where the MarkBit object is created. + bool data_only_; +}; + + +// Bitmap is a sequence of cells each containing fixed number of bits. +class Bitmap { + public: + static const uint32_t kBitsPerCell = 32; + static const uint32_t kBitsPerCellLog2 = 5; + static const uint32_t kBitIndexMask = kBitsPerCell - 1; + static const uint32_t kBytesPerCell = kBitsPerCell / kBitsPerByte; + static const uint32_t kBytesPerCellLog2 = kBitsPerCellLog2 - kBitsPerByteLog2; + + static const size_t kLength = + (1 << kPageSizeBits) >> (kPointerSizeLog2); + + static const size_t kSize = + (1 << kPageSizeBits) >> (kPointerSizeLog2 + kBitsPerByteLog2); + + + static int CellsForLength(int length) { + return (length + kBitsPerCell - 1) >> kBitsPerCellLog2; + } + + int CellsCount() { + return CellsForLength(kLength); + } + + static int SizeFor(int cells_count) { + return sizeof(MarkBit::CellType) * cells_count; + } + + INLINE(static uint32_t IndexToCell(uint32_t index)) { + return index >> kBitsPerCellLog2; + } + + INLINE(static uint32_t CellToIndex(uint32_t index)) { + return index << kBitsPerCellLog2; + } + + INLINE(static uint32_t CellAlignIndex(uint32_t index)) { + return (index + kBitIndexMask) & ~kBitIndexMask; + } + + INLINE(MarkBit::CellType* cells()) { + return reinterpret_cast<MarkBit::CellType*>(this); + } + + INLINE(Address address()) { + return reinterpret_cast<Address>(this); + } + + INLINE(static Bitmap* FromAddress(Address addr)) { + return reinterpret_cast<Bitmap*>(addr); + } + + inline MarkBit MarkBitFromIndex(uint32_t index, bool data_only = false) { + MarkBit::CellType mask = 1 << (index & kBitIndexMask); + MarkBit::CellType* cell = this->cells() + (index >> kBitsPerCellLog2); + return MarkBit(cell, mask, data_only); + } + + static inline void Clear(MemoryChunk* chunk); + + static void PrintWord(uint32_t word, uint32_t himask = 0) { + for (uint32_t mask = 1; mask != 0; mask <<= 1) { + if ((mask & himask) != 0) PrintF("["); + PrintF((mask & word) ? "1" : "0"); + if ((mask & himask) != 0) PrintF("]"); + } + } + + class CellPrinter { + public: + CellPrinter() : seq_start(0), seq_type(0), seq_length(0) { } + + void Print(uint32_t pos, uint32_t cell) { + if (cell == seq_type) { + seq_length++; + return; + } + + Flush(); + + if (IsSeq(cell)) { + seq_start = pos; + seq_length = 0; + seq_type = cell; + return; + } + + PrintF("%d: ", pos); + PrintWord(cell); + PrintF("\n"); + } + + void Flush() { + if (seq_length > 0) { + PrintF("%d: %dx%d\n", + seq_start, + seq_type == 0 ? 0 : 1, + seq_length * kBitsPerCell); + seq_length = 0; + } + } + + static bool IsSeq(uint32_t cell) { return cell == 0 || cell == 0xFFFFFFFF; } + + private: + uint32_t seq_start; + uint32_t seq_type; + uint32_t seq_length; + }; + + void Print() { + CellPrinter printer; + for (int i = 0; i < CellsCount(); i++) { + printer.Print(i, cells()[i]); + } + printer.Flush(); + PrintF("\n"); + } + + bool IsClean() { + for (int i = 0; i < CellsCount(); i++) { + if (cells()[i] != 0) return false; + } + return true; + } +}; + + +class SkipList; +class SlotsBuffer; + +// MemoryChunk represents a memory region owned by a specific space. +// It is divided into the header and the body. Chunk start is always +// 1MB aligned. Start of the body is aligned so it can accommodate +// any heap object. +class MemoryChunk { + public: + // Only works if the pointer is in the first kPageSize of the MemoryChunk. + static MemoryChunk* FromAddress(Address a) { + return reinterpret_cast<MemoryChunk*>(OffsetFrom(a) & ~kAlignmentMask); + } + + // Only works for addresses in pointer spaces, not data or code spaces. + static inline MemoryChunk* FromAnyPointerAddress(Address addr); + + Address address() { return reinterpret_cast<Address>(this); } + + bool is_valid() { return address() != NULL; } + + MemoryChunk* next_chunk() const { return next_chunk_; } + MemoryChunk* prev_chunk() const { return prev_chunk_; } + + void set_next_chunk(MemoryChunk* next) { next_chunk_ = next; } + void set_prev_chunk(MemoryChunk* prev) { prev_chunk_ = prev; } + + Space* owner() const { + if ((reinterpret_cast<intptr_t>(owner_) & kFailureTagMask) == + kFailureTag) { + return reinterpret_cast<Space*>(owner_ - kFailureTag); + } else { + return NULL; + } + } + + void set_owner(Space* space) { + ASSERT((reinterpret_cast<intptr_t>(space) & kFailureTagMask) == 0); + owner_ = reinterpret_cast<Address>(space) + kFailureTag; + ASSERT((reinterpret_cast<intptr_t>(owner_) & kFailureTagMask) == + kFailureTag); + } + + VirtualMemory* reserved_memory() { + return &reservation_; + } + + void InitializeReservedMemory() { + reservation_.Reset(); + } + + void set_reserved_memory(VirtualMemory* reservation) { + ASSERT_NOT_NULL(reservation); + reservation_.TakeControl(reservation); + } + + bool scan_on_scavenge() { return IsFlagSet(SCAN_ON_SCAVENGE); } + void initialize_scan_on_scavenge(bool scan) { + if (scan) { + SetFlag(SCAN_ON_SCAVENGE); + } else { + ClearFlag(SCAN_ON_SCAVENGE); + } + } + inline void set_scan_on_scavenge(bool scan); + + int store_buffer_counter() { return store_buffer_counter_; } + void set_store_buffer_counter(int counter) { + store_buffer_counter_ = counter; + } + + Address body() { return address() + kObjectStartOffset; } + + Address body_limit() { return address() + size(); } + + int body_size() { return static_cast<int>(size() - kObjectStartOffset); } + + bool Contains(Address addr) { + return addr >= body() && addr < address() + size(); + } + + // Checks whether addr can be a limit of addresses in this page. + // It's a limit if it's in the page, or if it's just after the + // last byte of the page. + bool ContainsLimit(Address addr) { + return addr >= body() && addr <= address() + size(); + } + + enum MemoryChunkFlags { + IS_EXECUTABLE, + ABOUT_TO_BE_FREED, + POINTERS_TO_HERE_ARE_INTERESTING, + POINTERS_FROM_HERE_ARE_INTERESTING, + SCAN_ON_SCAVENGE, + IN_FROM_SPACE, // Mutually exclusive with IN_TO_SPACE. + IN_TO_SPACE, // All pages in new space has one of these two set. + NEW_SPACE_BELOW_AGE_MARK, + CONTAINS_ONLY_DATA, + EVACUATION_CANDIDATE, + RESCAN_ON_EVACUATION, + + // Pages swept precisely can be iterated, hitting only the live objects. + // Whereas those swept conservatively cannot be iterated over. Both flags + // indicate that marking bits have been cleared by the sweeper, otherwise + // marking bits are still intact. + WAS_SWEPT_PRECISELY, + WAS_SWEPT_CONSERVATIVELY, + + // Last flag, keep at bottom. + NUM_MEMORY_CHUNK_FLAGS + }; + + + static const int kPointersToHereAreInterestingMask = + 1 << POINTERS_TO_HERE_ARE_INTERESTING; + + static const int kPointersFromHereAreInterestingMask = + 1 << POINTERS_FROM_HERE_ARE_INTERESTING; + + static const int kEvacuationCandidateMask = + 1 << EVACUATION_CANDIDATE; + + static const int kSkipEvacuationSlotsRecordingMask = + (1 << EVACUATION_CANDIDATE) | + (1 << RESCAN_ON_EVACUATION) | + (1 << IN_FROM_SPACE) | + (1 << IN_TO_SPACE); + + + void SetFlag(int flag) { + flags_ |= static_cast<uintptr_t>(1) << flag; + } + + void ClearFlag(int flag) { + flags_ &= ~(static_cast<uintptr_t>(1) << flag); + } + + void SetFlagTo(int flag, bool value) { + if (value) { + SetFlag(flag); + } else { + ClearFlag(flag); + } + } + + bool IsFlagSet(int flag) { + return (flags_ & (static_cast<uintptr_t>(1) << flag)) != 0; + } + + // Set or clear multiple flags at a time. The flags in the mask + // are set to the value in "flags", the rest retain the current value + // in flags_. + void SetFlags(intptr_t flags, intptr_t mask) { + flags_ = (flags_ & ~mask) | (flags & mask); + } + + // Return all current flags. + intptr_t GetFlags() { return flags_; } + + // Manage live byte count (count of bytes known to be live, + // because they are marked black). + void ResetLiveBytes() { + if (FLAG_gc_verbose) { + PrintF("ResetLiveBytes:%p:%x->0\n", + static_cast<void*>(this), live_byte_count_); + } + live_byte_count_ = 0; + } + void IncrementLiveBytes(int by) { + if (FLAG_gc_verbose) { + printf("UpdateLiveBytes:%p:%x%c=%x->%x\n", + static_cast<void*>(this), live_byte_count_, + ((by < 0) ? '-' : '+'), ((by < 0) ? -by : by), + live_byte_count_ + by); + } + live_byte_count_ += by; + ASSERT_LE(static_cast<unsigned>(live_byte_count_), size_); + } + int LiveBytes() { + ASSERT(static_cast<unsigned>(live_byte_count_) <= size_); + return live_byte_count_; + } + + static void IncrementLiveBytesFromGC(Address address, int by) { + MemoryChunk::FromAddress(address)->IncrementLiveBytes(by); + } + + static void IncrementLiveBytesFromMutator(Address address, int by); + + static const intptr_t kAlignment = + (static_cast<uintptr_t>(1) << kPageSizeBits); + + static const intptr_t kAlignmentMask = kAlignment - 1; + + static const intptr_t kSizeOffset = kPointerSize + kPointerSize; + + static const intptr_t kLiveBytesOffset = + kSizeOffset + kPointerSize + kPointerSize + kPointerSize + + kPointerSize + kPointerSize + kPointerSize + kIntSize; + + static const size_t kSlotsBufferOffset = kLiveBytesOffset + kIntSize; + + static const size_t kHeaderSize = + kSlotsBufferOffset + kPointerSize + kPointerSize; + + static const int kBodyOffset = + CODE_POINTER_ALIGN(MAP_POINTER_ALIGN(kHeaderSize + Bitmap::kSize)); + + // The start offset of the object area in a page. Aligned to both maps and + // code alignment to be suitable for both. Also aligned to 32 words because + // the marking bitmap is arranged in 32 bit chunks. + static const int kObjectStartAlignment = 32 * kPointerSize; + static const int kObjectStartOffset = kBodyOffset - 1 + + (kObjectStartAlignment - (kBodyOffset - 1) % kObjectStartAlignment); + + size_t size() const { return size_; } + + void set_size(size_t size) { + size_ = size; + } + + Executability executable() { + return IsFlagSet(IS_EXECUTABLE) ? EXECUTABLE : NOT_EXECUTABLE; + } + + bool ContainsOnlyData() { + return IsFlagSet(CONTAINS_ONLY_DATA); + } + + bool InNewSpace() { + return (flags_ & ((1 << IN_FROM_SPACE) | (1 << IN_TO_SPACE))) != 0; + } + + bool InToSpace() { + return IsFlagSet(IN_TO_SPACE); + } + + bool InFromSpace() { + return IsFlagSet(IN_FROM_SPACE); + } + + // --------------------------------------------------------------------- + // Markbits support + + inline Bitmap* markbits() { + return Bitmap::FromAddress(address() + kHeaderSize); + } + + void PrintMarkbits() { markbits()->Print(); } + + inline uint32_t AddressToMarkbitIndex(Address addr) { + return static_cast<uint32_t>(addr - this->address()) >> kPointerSizeLog2; + } + + inline static uint32_t FastAddressToMarkbitIndex(Address addr) { + const intptr_t offset = + reinterpret_cast<intptr_t>(addr) & kAlignmentMask; + + return static_cast<uint32_t>(offset) >> kPointerSizeLog2; + } + + inline Address MarkbitIndexToAddress(uint32_t index) { + return this->address() + (index << kPointerSizeLog2); + } + + void InsertAfter(MemoryChunk* other); + void Unlink(); + + inline Heap* heap() { return heap_; } + + static const int kFlagsOffset = kPointerSize * 3; + + bool IsEvacuationCandidate() { return IsFlagSet(EVACUATION_CANDIDATE); } + + bool ShouldSkipEvacuationSlotRecording() { + return (flags_ & kSkipEvacuationSlotsRecordingMask) != 0; + } + + inline SkipList* skip_list() { + return skip_list_; + } + + inline void set_skip_list(SkipList* skip_list) { + skip_list_ = skip_list; + } + + inline SlotsBuffer* slots_buffer() { + return slots_buffer_; + } + + inline SlotsBuffer** slots_buffer_address() { + return &slots_buffer_; + } + + void MarkEvacuationCandidate() { + ASSERT(slots_buffer_ == NULL); + SetFlag(EVACUATION_CANDIDATE); + } + + void ClearEvacuationCandidate() { + ASSERT(slots_buffer_ == NULL); + ClearFlag(EVACUATION_CANDIDATE); + } + + + protected: + MemoryChunk* next_chunk_; + MemoryChunk* prev_chunk_; + size_t size_; + intptr_t flags_; + // If the chunk needs to remember its memory reservation, it is stored here. + VirtualMemory reservation_; + // The identity of the owning space. This is tagged as a failure pointer, but + // no failure can be in an object, so this can be distinguished from any entry + // in a fixed array. + Address owner_; + Heap* heap_; + // Used by the store buffer to keep track of which pages to mark scan-on- + // scavenge. + int store_buffer_counter_; + // Count of bytes marked black on page. + int live_byte_count_; + SlotsBuffer* slots_buffer_; + SkipList* skip_list_; + + static MemoryChunk* Initialize(Heap* heap, + Address base, + size_t size, + Executability executable, + Space* owner); + + friend class MemoryAllocator; +}; + +STATIC_CHECK(sizeof(MemoryChunk) <= MemoryChunk::kHeaderSize); // ----------------------------------------------------------------------------- -// A page normally has 8K bytes. Large object pages may be larger. A page -// address is always aligned to the 8K page size. -// -// Each page starts with a header of Page::kPageHeaderSize size which contains -// bookkeeping data. -// -// The mark-compact collector transforms a map pointer into a page index and a -// page offset. The exact encoding is described in the comments for -// class MapWord in objects.h. +// A page is a memory chunk of a size 1MB. Large object pages may be larger. // // The only way to get a page pointer is by calling factory methods: // Page* p = Page::FromAddress(addr); or // Page* p = Page::FromAllocationTop(top); -class Page { +class Page : public MemoryChunk { public: // Returns the page containing a given address. The address ranges // from [page_addr .. page_addr + kPageSize[ - // - // Note that this function only works for addresses in normal paged - // spaces and addresses in the first 8K of large object pages (i.e., - // the start of large objects but not necessarily derived pointers - // within them). + // This only works if the object is in fact in a page. See also MemoryChunk:: + // FromAddress() and FromAnyAddress(). INLINE(static Page* FromAddress(Address a)) { return reinterpret_cast<Page*>(OffsetFrom(a) & ~kPageAlignmentMask); } @@ -148,34 +648,14 @@ class Page { // [page_addr + kObjectStartOffset .. page_addr + kPageSize]. INLINE(static Page* FromAllocationTop(Address top)) { Page* p = FromAddress(top - kPointerSize); - ASSERT_PAGE_OFFSET(p->Offset(top)); return p; } - // Returns the start address of this page. - Address address() { return reinterpret_cast<Address>(this); } - - // Checks whether this is a valid page address. - bool is_valid() { return address() != NULL; } - - // Returns the next page of this page. + // Returns the next page in the chain of pages owned by a space. inline Page* next_page(); - - // Return the end of allocation in this page. Undefined for unused pages. - inline Address AllocationTop(); - - // Return the allocation watermark for the page. - // For old space pages it is guaranteed that the area under the watermark - // does not contain any garbage pointers to new space. - inline Address AllocationWatermark(); - - // Return the allocation watermark offset from the beginning of the page. - inline uint32_t AllocationWatermarkOffset(); - - inline void SetAllocationWatermark(Address allocation_watermark); - - inline void SetCachedAllocationWatermark(Address allocation_watermark); - inline Address CachedAllocationWatermark(); + inline Page* prev_page(); + inline void set_next_page(Page* page); + inline void set_prev_page(Page* page); // Returns the start address of the object area in this page. Address ObjectAreaStart() { return address() + kObjectStartOffset; } @@ -188,26 +668,9 @@ class Page { return 0 == (OffsetFrom(a) & kPageAlignmentMask); } - // True if this page was in use before current compaction started. - // Result is valid only for pages owned by paged spaces and - // only after PagedSpace::PrepareForMarkCompact was called. - inline bool WasInUseBeforeMC(); - - inline void SetWasInUseBeforeMC(bool was_in_use); - - // True if this page is a large object page. - inline bool IsLargeObjectPage(); - - inline void SetIsLargeObjectPage(bool is_large_object_page); - - inline Executability PageExecutability(); - - inline void SetPageExecutability(Executability executable); - // Returns the offset of a given address to this page. INLINE(int Offset(Address a)) { int offset = static_cast<int>(a - address()); - ASSERT_PAGE_OFFSET(offset); return offset; } @@ -218,24 +681,6 @@ class Page { } // --------------------------------------------------------------------- - // Card marking support - - static const uint32_t kAllRegionsCleanMarks = 0x0; - static const uint32_t kAllRegionsDirtyMarks = 0xFFFFFFFF; - - inline uint32_t GetRegionMarks(); - inline void SetRegionMarks(uint32_t dirty); - - inline uint32_t GetRegionMaskForAddress(Address addr); - inline uint32_t GetRegionMaskForSpan(Address start, int length_in_bytes); - inline int GetRegionNumberForAddress(Address addr); - - inline void MarkRegionDirty(Address addr); - inline bool IsRegionDirty(Address addr); - - inline void ClearRegionMarks(Address start, - Address end, - bool reaches_limit); // Page size in bytes. This must be a multiple of the OS page size. static const int kPageSize = 1 << kPageSizeBits; @@ -243,118 +688,69 @@ class Page { // Page size mask. static const intptr_t kPageAlignmentMask = (1 << kPageSizeBits) - 1; - static const int kPageHeaderSize = kPointerSize + kPointerSize + kIntSize + - kIntSize + kPointerSize + kPointerSize; - - // The start offset of the object area in a page. Aligned to both maps and - // code alignment to be suitable for both. - static const int kObjectStartOffset = - CODE_POINTER_ALIGN(MAP_POINTER_ALIGN(kPageHeaderSize)); - // Object area size in bytes. static const int kObjectAreaSize = kPageSize - kObjectStartOffset; // Maximum object size that fits in a page. static const int kMaxHeapObjectSize = kObjectAreaSize; - static const int kDirtyFlagOffset = 2 * kPointerSize; - static const int kRegionSizeLog2 = 8; - static const int kRegionSize = 1 << kRegionSizeLog2; - static const intptr_t kRegionAlignmentMask = (kRegionSize - 1); + static const int kFirstUsedCell = + (kObjectStartOffset/kPointerSize) >> Bitmap::kBitsPerCellLog2; - STATIC_CHECK(kRegionSize == kPageSize / kBitsPerInt); + static const int kLastUsedCell = + ((kPageSize - kPointerSize)/kPointerSize) >> + Bitmap::kBitsPerCellLog2; - enum PageFlag { - IS_NORMAL_PAGE = 0, - WAS_IN_USE_BEFORE_MC, + inline void ClearGCFields(); - // Page allocation watermark was bumped by preallocation during scavenge. - // Correct watermark can be retrieved by CachedAllocationWatermark() method - WATERMARK_INVALIDATED, - IS_EXECUTABLE, - NUM_PAGE_FLAGS // Must be last - }; - static const int kPageFlagMask = (1 << NUM_PAGE_FLAGS) - 1; - - // To avoid an additional WATERMARK_INVALIDATED flag clearing pass during - // scavenge we just invalidate the watermark on each old space page after - // processing it. And then we flip the meaning of the WATERMARK_INVALIDATED - // flag at the beginning of the next scavenge and each page becomes marked as - // having a valid watermark. - // - // The following invariant must hold for pages in old pointer and map spaces: - // If page is in use then page is marked as having invalid watermark at - // the beginning and at the end of any GC. - // - // This invariant guarantees that after flipping flag meaning at the - // beginning of scavenge all pages in use will be marked as having valid - // watermark. - static inline void FlipMeaningOfInvalidatedWatermarkFlag(Heap* heap); - - // Returns true if the page allocation watermark was not altered during - // scavenge. - inline bool IsWatermarkValid(); + static inline Page* Initialize(Heap* heap, + MemoryChunk* chunk, + Executability executable, + PagedSpace* owner); - inline void InvalidateWatermark(bool value); + void InitializeAsAnchor(PagedSpace* owner); - inline bool GetPageFlag(PageFlag flag); - inline void SetPageFlag(PageFlag flag, bool value); - inline void ClearPageFlags(); + bool WasSweptPrecisely() { return IsFlagSet(WAS_SWEPT_PRECISELY); } + bool WasSweptConservatively() { return IsFlagSet(WAS_SWEPT_CONSERVATIVELY); } + bool WasSwept() { return WasSweptPrecisely() || WasSweptConservatively(); } - inline void ClearGCFields(); + void MarkSweptPrecisely() { SetFlag(WAS_SWEPT_PRECISELY); } + void MarkSweptConservatively() { SetFlag(WAS_SWEPT_CONSERVATIVELY); } - static const int kAllocationWatermarkOffsetShift = WATERMARK_INVALIDATED + 1; - static const int kAllocationWatermarkOffsetBits = kPageSizeBits + 1; - static const uint32_t kAllocationWatermarkOffsetMask = - ((1 << kAllocationWatermarkOffsetBits) - 1) << - kAllocationWatermarkOffsetShift; - - static const uint32_t kFlagsMask = - ((1 << kAllocationWatermarkOffsetShift) - 1); - - STATIC_CHECK(kBitsPerInt - kAllocationWatermarkOffsetShift >= - kAllocationWatermarkOffsetBits); - - //--------------------------------------------------------------------------- - // Page header description. - // - // If a page is not in the large object space, the first word, - // opaque_header, encodes the next page address (aligned to kPageSize 8K) - // and the chunk number (0 ~ 8K-1). Only MemoryAllocator should use - // opaque_header. The value range of the opaque_header is [0..kPageSize[, - // or [next_page_start, next_page_end[. It cannot point to a valid address - // in the current page. If a page is in the large object space, the first - // word *may* (if the page start and large object chunk start are the - // same) contain the address of the next large object chunk. - intptr_t opaque_header; - - // If the page is not in the large object space, the low-order bit of the - // second word is set. If the page is in the large object space, the - // second word *may* (if the page start and large object chunk start are - // the same) contain the large object chunk size. In either case, the - // low-order bit for large object pages will be cleared. - // For normal pages this word is used to store page flags and - // offset of allocation top. - intptr_t flags_; + void ClearSweptPrecisely() { ClearFlag(WAS_SWEPT_PRECISELY); } + void ClearSweptConservatively() { ClearFlag(WAS_SWEPT_CONSERVATIVELY); } - // This field contains dirty marks for regions covering the page. Only dirty - // regions might contain intergenerational references. - // Only 32 dirty marks are supported so for large object pages several regions - // might be mapped to a single dirty mark. - uint32_t dirty_regions_; +#ifdef DEBUG + void Print(); +#endif // DEBUG + + friend class MemoryAllocator; +}; - // The index of the page in its owner space. - int mc_page_index; - // During mark-compact collections this field contains the forwarding address - // of the first live object in this page. - // During scavenge collection this field is used to store allocation watermark - // if it is altered during scavenge. - Address mc_first_forwarded; +STATIC_CHECK(sizeof(Page) <= MemoryChunk::kHeaderSize); - Heap* heap_; + +class LargePage : public MemoryChunk { + public: + HeapObject* GetObject() { + return HeapObject::FromAddress(body()); + } + + inline LargePage* next_page() const { + return static_cast<LargePage*>(next_chunk()); + } + + inline void set_next_page(LargePage* page) { + set_next_chunk(page); + } + private: + static inline LargePage* Initialize(Heap* heap, MemoryChunk* chunk); + + friend class MemoryAllocator; }; +STATIC_CHECK(sizeof(LargePage) <= MemoryChunk::kHeaderSize); // ---------------------------------------------------------------------------- // Space is the abstract superclass for all allocation spaces. @@ -380,6 +776,14 @@ class Space : public Malloced { // (e.g. see LargeObjectSpace). virtual intptr_t SizeOfObjects() { return Size(); } + virtual int RoundSizeDownToObjectAlignment(int size) { + if (id_ == CODE_SPACE) { + return RoundDown(size, kCodeAlignment); + } else { + return RoundDown(size, kPointerSize); + } + } + #ifdef DEBUG virtual void Print() = 0; #endif @@ -414,7 +818,7 @@ class CodeRange { // Reserves a range of virtual memory, but does not commit any of it. // Can only be called once, at heap initialization time. // Returns false on failure. - bool Setup(const size_t requested_size); + bool SetUp(const size_t requested_size); // Frees the range of virtual memory, and frees the data structures used to // manage it. @@ -430,9 +834,9 @@ class CodeRange { // Allocates a chunk of memory from the large-object portion of // the code range. On platforms with no separate code range, should // not be called. - MUST_USE_RESULT void* AllocateRawMemory(const size_t requested, - size_t* allocated); - void FreeRawMemory(void* buf, size_t length); + MUST_USE_RESULT Address AllocateRawMemory(const size_t requested, + size_t* allocated); + void FreeRawMemory(Address buf, size_t length); private: Isolate* isolate_; @@ -443,9 +847,15 @@ class CodeRange { class FreeBlock { public: FreeBlock(Address start_arg, size_t size_arg) - : start(start_arg), size(size_arg) {} + : start(start_arg), size(size_arg) { + ASSERT(IsAddressAligned(start, MemoryChunk::kAlignment)); + ASSERT(size >= static_cast<size_t>(Page::kPageSize)); + } FreeBlock(void* start_arg, size_t size_arg) - : start(static_cast<Address>(start_arg)), size(size_arg) {} + : start(static_cast<Address>(start_arg)), size(size_arg) { + ASSERT(IsAddressAligned(start, MemoryChunk::kAlignment)); + ASSERT(size >= static_cast<size_t>(Page::kPageSize)); + } Address start; size_t size; @@ -473,123 +883,80 @@ class CodeRange { }; +class SkipList { + public: + SkipList() { + Clear(); + } + + void Clear() { + for (int idx = 0; idx < kSize; idx++) { + starts_[idx] = reinterpret_cast<Address>(-1); + } + } + + Address StartFor(Address addr) { + return starts_[RegionNumber(addr)]; + } + + void AddObject(Address addr, int size) { + int start_region = RegionNumber(addr); + int end_region = RegionNumber(addr + size - kPointerSize); + for (int idx = start_region; idx <= end_region; idx++) { + if (starts_[idx] > addr) starts_[idx] = addr; + } + } + + static inline int RegionNumber(Address addr) { + return (OffsetFrom(addr) & Page::kPageAlignmentMask) >> kRegionSizeLog2; + } + + static void Update(Address addr, int size) { + Page* page = Page::FromAddress(addr); + SkipList* list = page->skip_list(); + if (list == NULL) { + list = new SkipList(); + page->set_skip_list(list); + } + + list->AddObject(addr, size); + } + + private: + static const int kRegionSizeLog2 = 13; + static const int kRegionSize = 1 << kRegionSizeLog2; + static const int kSize = Page::kPageSize / kRegionSize; + + STATIC_ASSERT(Page::kPageSize % kRegionSize == 0); + + Address starts_[kSize]; +}; + + // ---------------------------------------------------------------------------- // A space acquires chunks of memory from the operating system. The memory -// allocator manages chunks for the paged heap spaces (old space and map -// space). A paged chunk consists of pages. Pages in a chunk have contiguous -// addresses and are linked as a list. +// allocator allocated and deallocates pages for the paged heap spaces and large +// pages for large object space. // -// The allocator keeps an initial chunk which is used for the new space. The -// leftover regions of the initial chunk are used for the initial chunks of -// old space and map space if they are big enough to hold at least one page. -// The allocator assumes that there is one old space and one map space, each -// expands the space by allocating kPagesPerChunk pages except the last -// expansion (before running out of space). The first chunk may contain fewer -// than kPagesPerChunk pages as well. +// Each space has to manage it's own pages. // -// The memory allocator also allocates chunks for the large object space, but -// they are managed by the space itself. The new space does not expand. -// -// The fact that pages for paged spaces are allocated and deallocated in chunks -// induces a constraint on the order of pages in a linked lists. We say that -// pages are linked in the chunk-order if and only if every two consecutive -// pages from the same chunk are consecutive in the linked list. -// - - class MemoryAllocator { public: explicit MemoryAllocator(Isolate* isolate); // Initializes its internal bookkeeping structures. // Max capacity of the total space and executable memory limit. - bool Setup(intptr_t max_capacity, intptr_t capacity_executable); + bool SetUp(intptr_t max_capacity, intptr_t capacity_executable); - // Deletes valid chunks. void TearDown(); - // Reserves an initial address range of virtual memory to be split between - // the two new space semispaces, the old space, and the map space. The - // memory is not yet committed or assigned to spaces and split into pages. - // The initial chunk is unmapped when the memory allocator is torn down. - // This function should only be called when there is not already a reserved - // initial chunk (initial_chunk_ should be NULL). It returns the start - // address of the initial chunk if successful, with the side effect of - // setting the initial chunk, or else NULL if unsuccessful and leaves the - // initial chunk NULL. - void* ReserveInitialChunk(const size_t requested); - - // Commits pages from an as-yet-unmanaged block of virtual memory into a - // paged space. The block should be part of the initial chunk reserved via - // a call to ReserveInitialChunk. The number of pages is always returned in - // the output parameter num_pages. This function assumes that the start - // address is non-null and that it is big enough to hold at least one - // page-aligned page. The call always succeeds, and num_pages is always - // greater than zero. - Page* CommitPages(Address start, size_t size, PagedSpace* owner, - int* num_pages); - - // Commit a contiguous block of memory from the initial chunk. Assumes that - // the address is not NULL, the size is greater than zero, and that the - // block is contained in the initial chunk. Returns true if it succeeded - // and false otherwise. - bool CommitBlock(Address start, size_t size, Executability executable); - - // Uncommit a contiguous block of memory [start..(start+size)[. - // start is not NULL, the size is greater than zero, and the - // block is contained in the initial chunk. Returns true if it succeeded - // and false otherwise. - bool UncommitBlock(Address start, size_t size); + Page* AllocatePage(PagedSpace* owner, Executability executable); - // Zaps a contiguous block of memory [start..(start+size)[ thus - // filling it up with a recognizable non-NULL bit pattern. - void ZapBlock(Address start, size_t size); - - // Attempts to allocate the requested (non-zero) number of pages from the - // OS. Fewer pages might be allocated than requested. If it fails to - // allocate memory for the OS or cannot allocate a single page, this - // function returns an invalid page pointer (NULL). The caller must check - // whether the returned page is valid (by calling Page::is_valid()). It is - // guaranteed that allocated pages have contiguous addresses. The actual - // number of allocated pages is returned in the output parameter - // allocated_pages. If the PagedSpace owner is executable and there is - // a code range, the pages are allocated from the code range. - Page* AllocatePages(int requested_pages, int* allocated_pages, - PagedSpace* owner); - - // Frees pages from a given page and after. Requires pages to be - // linked in chunk-order (see comment for class). - // If 'p' is the first page of a chunk, pages from 'p' are freed - // and this function returns an invalid page pointer. - // Otherwise, the function searches a page after 'p' that is - // the first page of a chunk. Pages after the found page - // are freed and the function returns 'p'. - Page* FreePages(Page* p); - - // Frees all pages owned by given space. - void FreeAllPages(PagedSpace* space); - - // Allocates and frees raw memory of certain size. - // These are just thin wrappers around OS::Allocate and OS::Free, - // but keep track of allocated bytes as part of heap. - // If the flag is EXECUTABLE and a code range exists, the requested - // memory is allocated from the code range. If a code range exists - // and the freed memory is in it, the code range manages the freed memory. - MUST_USE_RESULT void* AllocateRawMemory(const size_t requested, - size_t* allocated, - Executability executable); - void FreeRawMemory(void* buf, - size_t length, - Executability executable); - void PerformAllocationCallback(ObjectSpace space, - AllocationAction action, - size_t size); + LargePage* AllocateLargePage(intptr_t object_size, + Executability executable, + Space* owner); - void AddMemoryAllocationCallback(MemoryAllocationCallback callback, - ObjectSpace space, - AllocationAction action); - void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback); - bool MemoryAllocationCallbackRegistered(MemoryAllocationCallback callback); + void Free(MemoryChunk* chunk); // Returns the maximum available bytes of heaps. intptr_t Available() { return capacity_ < size_ ? 0 : capacity_ - size_; } @@ -611,67 +978,68 @@ class MemoryAllocator { return (Available() / Page::kPageSize) * Page::kObjectAreaSize; } - // Links two pages. - inline void SetNextPage(Page* prev, Page* next); +#ifdef DEBUG + // Reports statistic info of the space. + void ReportStatistics(); +#endif - // Returns the next page of a given page. - inline Page* GetNextPage(Page* p); + MemoryChunk* AllocateChunk(intptr_t body_size, + Executability executable, + Space* space); - // Checks whether a page belongs to a space. - inline bool IsPageInSpace(Page* p, PagedSpace* space); + Address ReserveAlignedMemory(size_t requested, + size_t alignment, + VirtualMemory* controller); + Address AllocateAlignedMemory(size_t requested, + size_t alignment, + Executability executable, + VirtualMemory* controller); - // Returns the space that owns the given page. - inline PagedSpace* PageOwner(Page* page); + void FreeMemory(VirtualMemory* reservation, Executability executable); + void FreeMemory(Address addr, size_t size, Executability executable); - // Finds the first/last page in the same chunk as a given page. - Page* FindFirstPageInSameChunk(Page* p); - Page* FindLastPageInSameChunk(Page* p); + // Commit a contiguous block of memory from the initial chunk. Assumes that + // the address is not NULL, the size is greater than zero, and that the + // block is contained in the initial chunk. Returns true if it succeeded + // and false otherwise. + bool CommitBlock(Address start, size_t size, Executability executable); - // Relinks list of pages owned by space to make it chunk-ordered. - // Returns new first and last pages of space. - // Also returns last page in relinked list which has WasInUsedBeforeMC - // flag set. - void RelinkPageListInChunkOrder(PagedSpace* space, - Page** first_page, - Page** last_page, - Page** last_page_in_use); + // Uncommit a contiguous block of memory [start..(start+size)[. + // start is not NULL, the size is greater than zero, and the + // block is contained in the initial chunk. Returns true if it succeeded + // and false otherwise. + bool UncommitBlock(Address start, size_t size); -#ifdef DEBUG - // Reports statistic info of the space. - void ReportStatistics(); -#endif + // Zaps a contiguous block of memory [start..(start+size)[ thus + // filling it up with a recognizable non-NULL bit pattern. + void ZapBlock(Address start, size_t size); - // Due to encoding limitation, we can only have 8K chunks. - static const int kMaxNofChunks = 1 << kPageSizeBits; - // If a chunk has at least 16 pages, the maximum heap size is about - // 8K * 8K * 16 = 1G bytes. -#ifdef V8_TARGET_ARCH_X64 - static const int kPagesPerChunk = 32; - // On 64 bit the chunk table consists of 4 levels of 4096-entry tables. - static const int kChunkTableLevels = 4; - static const int kChunkTableBitsPerLevel = 12; -#else - static const int kPagesPerChunk = 16; - // On 32 bit the chunk table consists of 2 levels of 256-entry tables. - static const int kChunkTableLevels = 2; - static const int kChunkTableBitsPerLevel = 8; -#endif + void PerformAllocationCallback(ObjectSpace space, + AllocationAction action, + size_t size); - private: - static const int kChunkSize = kPagesPerChunk * Page::kPageSize; + void AddMemoryAllocationCallback(MemoryAllocationCallback callback, + ObjectSpace space, + AllocationAction action); + void RemoveMemoryAllocationCallback( + MemoryAllocationCallback callback); + + bool MemoryAllocationCallbackRegistered( + MemoryAllocationCallback callback); + + private: Isolate* isolate_; // Maximum space size in bytes. - intptr_t capacity_; + size_t capacity_; // Maximum subset of capacity_ that can be executable - intptr_t capacity_executable_; + size_t capacity_executable_; // Allocated space size in bytes. - intptr_t size_; - + size_t size_; // Allocated executable space size in bytes. - intptr_t size_executable_; + size_t size_executable_; struct MemoryAllocationCallbackRegistration { MemoryAllocationCallbackRegistration(MemoryAllocationCallback callback, @@ -683,64 +1051,11 @@ class MemoryAllocator { ObjectSpace space; AllocationAction action; }; + // A List of callback that are triggered when memory is allocated or free'd List<MemoryAllocationCallbackRegistration> memory_allocation_callbacks_; - // The initial chunk of virtual memory. - VirtualMemory* initial_chunk_; - - // Allocated chunk info: chunk start address, chunk size, and owning space. - class ChunkInfo BASE_EMBEDDED { - public: - ChunkInfo() : address_(NULL), - size_(0), - owner_(NULL), - executable_(NOT_EXECUTABLE), - owner_identity_(FIRST_SPACE) {} - inline void init(Address a, size_t s, PagedSpace* o); - Address address() { return address_; } - size_t size() { return size_; } - PagedSpace* owner() { return owner_; } - // We save executability of the owner to allow using it - // when collecting stats after the owner has been destroyed. - Executability executable() const { return executable_; } - AllocationSpace owner_identity() const { return owner_identity_; } - - private: - Address address_; - size_t size_; - PagedSpace* owner_; - Executability executable_; - AllocationSpace owner_identity_; - }; - - // Chunks_, free_chunk_ids_ and top_ act as a stack of free chunk ids. - List<ChunkInfo> chunks_; - List<int> free_chunk_ids_; - int max_nof_chunks_; - int top_; - - // Push/pop a free chunk id onto/from the stack. - void Push(int free_chunk_id); - int Pop(); - bool OutOfChunkIds() { return top_ == 0; } - - // Frees a chunk. - void DeleteChunk(int chunk_id); - - // Basic check whether a chunk id is in the valid range. - inline bool IsValidChunkId(int chunk_id); - - // Checks whether a chunk id identifies an allocated chunk. - inline bool IsValidChunk(int chunk_id); - - // Returns the chunk id that a page belongs to. - inline int GetChunkId(Page* p); - - // True if the address lies in the initial chunk. - inline bool InInitialChunk(Address address); - // Initializes pages in a chunk. Returns the first page address. // This function and GetChunkId() are provided for the mark-compact // collector to rebuild page headers in the from space, which is @@ -748,13 +1063,7 @@ class MemoryAllocator { Page* InitializePagesInChunk(int chunk_id, int pages_in_chunk, PagedSpace* owner); - Page* RelinkPagesInChunk(int chunk_id, - Address chunk_start, - size_t chunk_size, - Page* prev, - Page** last_page_in_use); - - DISALLOW_COPY_AND_ASSIGN(MemoryAllocator); + DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryAllocator); }; @@ -777,111 +1086,67 @@ class ObjectIterator : public Malloced { // ----------------------------------------------------------------------------- // Heap object iterator in new/old/map spaces. // -// A HeapObjectIterator iterates objects from a given address to the -// top of a space. The given address must be below the current -// allocation pointer (space top). There are some caveats. +// A HeapObjectIterator iterates objects from the bottom of the given space +// to its top or from the bottom of the given page to its top. // -// (1) If the space top changes upward during iteration (because of -// allocating new objects), the iterator does not iterate objects -// above the original space top. The caller must create a new -// iterator starting from the old top in order to visit these new -// objects. -// -// (2) If new objects are allocated below the original allocation top -// (e.g., free-list allocation in paged spaces), the new objects -// may or may not be iterated depending on their position with -// respect to the current point of iteration. -// -// (3) The space top should not change downward during iteration, -// otherwise the iterator will return not-necessarily-valid -// objects. - +// If objects are allocated in the page during iteration the iterator may +// or may not iterate over those objects. The caller must create a new +// iterator in order to be sure to visit these new objects. class HeapObjectIterator: public ObjectIterator { public: - // Creates a new object iterator in a given space. If a start - // address is not given, the iterator starts from the space bottom. + // Creates a new object iterator in a given space. // If the size function is not given, the iterator calls the default // Object::Size(). explicit HeapObjectIterator(PagedSpace* space); HeapObjectIterator(PagedSpace* space, HeapObjectCallback size_func); - HeapObjectIterator(PagedSpace* space, Address start); - HeapObjectIterator(PagedSpace* space, - Address start, - HeapObjectCallback size_func); HeapObjectIterator(Page* page, HeapObjectCallback size_func); - inline HeapObject* next() { - return (cur_addr_ < cur_limit_) ? FromCurrentPage() : FromNextPage(); + // Advance to the next object, skipping free spaces and other fillers and + // skipping the special garbage section of which there is one per space. + // Returns NULL when the iteration has ended. + inline HeapObject* Next() { + do { + HeapObject* next_obj = FromCurrentPage(); + if (next_obj != NULL) return next_obj; + } while (AdvanceToNextPage()); + return NULL; } - // implementation of ObjectIterator. - virtual HeapObject* next_object() { return next(); } + virtual HeapObject* next_object() { + return Next(); + } private: - Address cur_addr_; // current iteration point - Address end_addr_; // end iteration point - Address cur_limit_; // current page limit - HeapObjectCallback size_func_; // size function - Page* end_page_; // caches the page of the end address - - HeapObject* FromCurrentPage() { - ASSERT(cur_addr_ < cur_limit_); - - HeapObject* obj = HeapObject::FromAddress(cur_addr_); - int obj_size = (size_func_ == NULL) ? obj->Size() : size_func_(obj); - ASSERT_OBJECT_SIZE(obj_size); + enum PageMode { kOnePageOnly, kAllPagesInSpace }; - cur_addr_ += obj_size; - ASSERT(cur_addr_ <= cur_limit_); + Address cur_addr_; // Current iteration point. + Address cur_end_; // End iteration point. + HeapObjectCallback size_func_; // Size function or NULL. + PagedSpace* space_; + PageMode page_mode_; - return obj; - } + // Fast (inlined) path of next(). + inline HeapObject* FromCurrentPage(); - // Slow path of next, goes into the next page. - HeapObject* FromNextPage(); + // Slow path of next(), goes into the next page. Returns false if the + // iteration has ended. + bool AdvanceToNextPage(); // Initializes fields. - void Initialize(Address start, Address end, HeapObjectCallback size_func); - -#ifdef DEBUG - // Verifies whether fields have valid values. - void Verify(); -#endif + inline void Initialize(PagedSpace* owner, + Address start, + Address end, + PageMode mode, + HeapObjectCallback size_func); }; // ----------------------------------------------------------------------------- // A PageIterator iterates the pages in a paged space. -// -// The PageIterator class provides three modes for iterating pages in a space: -// PAGES_IN_USE iterates pages containing allocated objects. -// PAGES_USED_BY_MC iterates pages that hold relocated objects during a -// mark-compact collection. -// ALL_PAGES iterates all pages in the space. -// -// There are some caveats. -// -// (1) If the space expands during iteration, new pages will not be -// returned by the iterator in any mode. -// -// (2) If new objects are allocated during iteration, they will appear -// in pages returned by the iterator. Allocation may cause the -// allocation pointer or MC allocation pointer in the last page to -// change between constructing the iterator and iterating the last -// page. -// -// (3) The space should not shrink during iteration, otherwise the -// iterator will return deallocated pages. class PageIterator BASE_EMBEDDED { public: - enum Mode { - PAGES_IN_USE, - PAGES_USED_BY_MC, - ALL_PAGES - }; - - PageIterator(PagedSpace* space, Mode mode); + explicit inline PageIterator(PagedSpace* space); inline bool has_next(); inline Page* next(); @@ -889,21 +1154,25 @@ class PageIterator BASE_EMBEDDED { private: PagedSpace* space_; Page* prev_page_; // Previous page returned. - Page* stop_page_; // Page to stop at (last page returned by the iterator). + // Next page that will be returned. Cached here so that we can use this + // iterator for operations that deallocate pages. + Page* next_page_; }; // ----------------------------------------------------------------------------- -// A space has a list of pages. The next page can be accessed via -// Page::next_page() call. The next page of the last page is an -// invalid page pointer. A space can expand and shrink dynamically. +// A space has a circular list of pages. The next page can be accessed via +// Page::next_page() call. // An abstraction of allocation and relocation pointers in a page-structured // space. class AllocationInfo { public: - Address top; // current allocation top - Address limit; // current allocation limit + AllocationInfo() : top(NULL), limit(NULL) { + } + + Address top; // Current allocation top. + Address limit; // Current allocation limit. #ifdef DEBUG bool VerifyPagedAllocation() { @@ -915,11 +1184,11 @@ class AllocationInfo { // An abstraction of the accounting statistics of a page-structured space. -// The 'capacity' of a space is the number of object-area bytes (ie, not +// The 'capacity' of a space is the number of object-area bytes (i.e., not // including page bookkeeping structures) currently in the space. The 'size' // of a space is the number of allocated bytes, the 'waste' in the space is // the number of bytes that are not allocated and not available to -// allocation without reorganizing the space via a GC (eg, small blocks due +// allocation without reorganizing the space via a GC (e.g. small blocks due // to internal fragmentation, top of page areas in map space), and the bytes // 'available' is the number of unallocated bytes that are not waste. The // capacity is the sum of size, waste, and available. @@ -932,73 +1201,213 @@ class AllocationStats BASE_EMBEDDED { public: AllocationStats() { Clear(); } - // Zero out all the allocation statistics (ie, no capacity). + // Zero out all the allocation statistics (i.e., no capacity). void Clear() { capacity_ = 0; - available_ = 0; size_ = 0; waste_ = 0; } - // Reset the allocation statistics (ie, available = capacity with no + void ClearSizeWaste() { + size_ = capacity_; + waste_ = 0; + } + + // Reset the allocation statistics (i.e., available = capacity with no // wasted or allocated bytes). void Reset() { - available_ = capacity_; size_ = 0; waste_ = 0; } // Accessors for the allocation statistics. intptr_t Capacity() { return capacity_; } - intptr_t Available() { return available_; } intptr_t Size() { return size_; } intptr_t Waste() { return waste_; } - // Grow the space by adding available bytes. + // Grow the space by adding available bytes. They are initially marked as + // being in use (part of the size), but will normally be immediately freed, + // putting them on the free list and removing them from size_. void ExpandSpace(int size_in_bytes) { capacity_ += size_in_bytes; - available_ += size_in_bytes; + size_ += size_in_bytes; + ASSERT(size_ >= 0); } - // Shrink the space by removing available bytes. + // Shrink the space by removing available bytes. Since shrinking is done + // during sweeping, bytes have been marked as being in use (part of the size) + // and are hereby freed. void ShrinkSpace(int size_in_bytes) { capacity_ -= size_in_bytes; - available_ -= size_in_bytes; + size_ -= size_in_bytes; + ASSERT(size_ >= 0); } // Allocate from available bytes (available -> size). void AllocateBytes(intptr_t size_in_bytes) { - available_ -= size_in_bytes; size_ += size_in_bytes; + ASSERT(size_ >= 0); } // Free allocated bytes, making them available (size -> available). void DeallocateBytes(intptr_t size_in_bytes) { size_ -= size_in_bytes; - available_ += size_in_bytes; + ASSERT(size_ >= 0); } // Waste free bytes (available -> waste). void WasteBytes(int size_in_bytes) { - available_ -= size_in_bytes; + size_ -= size_in_bytes; waste_ += size_in_bytes; - } - - // Consider the wasted bytes to be allocated, as they contain filler - // objects (waste -> size). - void FillWastedBytes(intptr_t size_in_bytes) { - waste_ -= size_in_bytes; - size_ += size_in_bytes; + ASSERT(size_ >= 0); } private: intptr_t capacity_; - intptr_t available_; intptr_t size_; intptr_t waste_; }; +// ----------------------------------------------------------------------------- +// Free lists for old object spaces +// +// Free-list nodes are free blocks in the heap. They look like heap objects +// (free-list node pointers have the heap object tag, and they have a map like +// a heap object). They have a size and a next pointer. The next pointer is +// the raw address of the next free list node (or NULL). +class FreeListNode: public HeapObject { + public: + // Obtain a free-list node from a raw address. This is not a cast because + // it does not check nor require that the first word at the address is a map + // pointer. + static FreeListNode* FromAddress(Address address) { + return reinterpret_cast<FreeListNode*>(HeapObject::FromAddress(address)); + } + + static inline bool IsFreeListNode(HeapObject* object); + + // Set the size in bytes, which can be read with HeapObject::Size(). This + // function also writes a map to the first word of the block so that it + // looks like a heap object to the garbage collector and heap iteration + // functions. + void set_size(Heap* heap, int size_in_bytes); + + // Accessors for the next field. + inline FreeListNode* next(); + inline FreeListNode** next_address(); + inline void set_next(FreeListNode* next); + + inline void Zap(); + + private: + static const int kNextOffset = POINTER_SIZE_ALIGN(FreeSpace::kHeaderSize); + + DISALLOW_IMPLICIT_CONSTRUCTORS(FreeListNode); +}; + + +// The free list for the old space. The free list is organized in such a way +// as to encourage objects allocated around the same time to be near each +// other. The normal way to allocate is intended to be by bumping a 'top' +// pointer until it hits a 'limit' pointer. When the limit is hit we need to +// find a new space to allocate from. This is done with the free list, which +// is divided up into rough categories to cut down on waste. Having finer +// categories would scatter allocation more. + +// The old space free list is organized in categories. +// 1-31 words: Such small free areas are discarded for efficiency reasons. +// They can be reclaimed by the compactor. However the distance between top +// and limit may be this small. +// 32-255 words: There is a list of spaces this large. It is used for top and +// limit when the object we need to allocate is 1-31 words in size. These +// spaces are called small. +// 256-2047 words: There is a list of spaces this large. It is used for top and +// limit when the object we need to allocate is 32-255 words in size. These +// spaces are called medium. +// 1048-16383 words: There is a list of spaces this large. It is used for top +// and limit when the object we need to allocate is 256-2047 words in size. +// These spaces are call large. +// At least 16384 words. This list is for objects of 2048 words or larger. +// Empty pages are added to this list. These spaces are called huge. +class FreeList BASE_EMBEDDED { + public: + explicit FreeList(PagedSpace* owner); + + // Clear the free list. + void Reset(); + + // Return the number of bytes available on the free list. + intptr_t available() { return available_; } + + // Place a node on the free list. The block of size 'size_in_bytes' + // starting at 'start' is placed on the free list. The return value is the + // number of bytes that have been lost due to internal fragmentation by + // freeing the block. Bookkeeping information will be written to the block, + // i.e., its contents will be destroyed. The start address should be word + // aligned, and the size should be a non-zero multiple of the word size. + int Free(Address start, int size_in_bytes); + + // Allocate a block of size 'size_in_bytes' from the free list. The block + // is unitialized. A failure is returned if no block is available. The + // number of bytes lost to fragmentation is returned in the output parameter + // 'wasted_bytes'. The size should be a non-zero multiple of the word size. + MUST_USE_RESULT HeapObject* Allocate(int size_in_bytes); + +#ifdef DEBUG + void Zap(); + static intptr_t SumFreeList(FreeListNode* node); + static int FreeListLength(FreeListNode* cur); + intptr_t SumFreeLists(); + bool IsVeryLong(); +#endif + + struct SizeStats { + intptr_t Total() { + return small_size_ + medium_size_ + large_size_ + huge_size_; + } + + intptr_t small_size_; + intptr_t medium_size_; + intptr_t large_size_; + intptr_t huge_size_; + }; + + void CountFreeListItems(Page* p, SizeStats* sizes); + + intptr_t EvictFreeListItems(Page* p); + + private: + // The size range of blocks, in bytes. + static const int kMinBlockSize = 3 * kPointerSize; + static const int kMaxBlockSize = Page::kMaxHeapObjectSize; + + FreeListNode* PickNodeFromList(FreeListNode** list, int* node_size); + + FreeListNode* FindNodeFor(int size_in_bytes, int* node_size); + + PagedSpace* owner_; + Heap* heap_; + + // Total available bytes in all blocks on this free list. + int available_; + + static const int kSmallListMin = 0x20 * kPointerSize; + static const int kSmallListMax = 0xff * kPointerSize; + static const int kMediumListMax = 0x7ff * kPointerSize; + static const int kLargeListMax = 0x3fff * kPointerSize; + static const int kSmallAllocationMax = kSmallListMin - kPointerSize; + static const int kMediumAllocationMax = kSmallListMax; + static const int kLargeAllocationMax = kMediumListMax; + FreeListNode* small_list_; + FreeListNode* medium_list_; + FreeListNode* large_list_; + FreeListNode* huge_list_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FreeList); +}; + + class PagedSpace : public Space { public: // Creates a space with a maximum capacity, and an id. @@ -1013,11 +1422,11 @@ class PagedSpace : public Space { // the memory allocator's initial chunk) if possible. If the block of // addresses is not big enough to contain a single page-aligned page, a // fresh chunk will be allocated. - bool Setup(Address start, size_t size); + bool SetUp(); // Returns true if the space has been successfully set up and not // subsequently torn down. - bool HasBeenSetup(); + bool HasBeenSetUp(); // Cleans up the space, frees all pages in this space except those belonging // to the initial chunk, uncommits addresses in the initial chunk. @@ -1026,8 +1435,6 @@ class PagedSpace : public Space { // Checks whether an object/address is in this space. inline bool Contains(Address a); bool Contains(HeapObject* o) { return Contains(o->address()); } - // Never crashes even if a is not a valid pointer. - inline bool SafeContains(Address a); // Given an address occupied by a live object, return that object if it is // in this space, or Failure::Exception() if it is not. The implementation @@ -1035,104 +1442,92 @@ class PagedSpace : public Space { // linear in the number of objects in the page. It may be slow. MUST_USE_RESULT MaybeObject* FindObject(Address addr); - // Checks whether page is currently in use by this space. - bool IsUsed(Page* page); - - void MarkAllPagesClean(); - // Prepares for a mark-compact GC. - virtual void PrepareForMarkCompact(bool will_compact); + virtual void PrepareForMarkCompact(); - // The top of allocation in a page in this space. Undefined if page is unused. - Address PageAllocationTop(Page* page) { - return page == TopPageOf(allocation_info_) ? top() - : PageAllocationLimit(page); - } - - // The limit of allocation for a page in this space. - virtual Address PageAllocationLimit(Page* page) = 0; - - void FlushTopPageWatermark() { - AllocationTopPage()->SetCachedAllocationWatermark(top()); - AllocationTopPage()->InvalidateWatermark(true); - } - - // Current capacity without growing (Size() + Available() + Waste()). + // Current capacity without growing (Size() + Available()). intptr_t Capacity() { return accounting_stats_.Capacity(); } // Total amount of memory committed for this space. For paged // spaces this equals the capacity. intptr_t CommittedMemory() { return Capacity(); } - // Available bytes without growing. - intptr_t Available() { return accounting_stats_.Available(); } + // Sets the capacity, the available space and the wasted space to zero. + // The stats are rebuilt during sweeping by adding each page to the + // capacity and the size when it is encountered. As free spaces are + // discovered during the sweeping they are subtracted from the size and added + // to the available and wasted totals. + void ClearStats() { + accounting_stats_.ClearSizeWaste(); + } + + // Available bytes without growing. These are the bytes on the free list. + // The bytes in the linear allocation area are not included in this total + // because updating the stats would slow down allocation. New pages are + // immediately added to the free list so they show up here. + intptr_t Available() { return free_list_.available(); } - // Allocated bytes in this space. + // Allocated bytes in this space. Garbage bytes that were not found due to + // lazy sweeping are counted as being allocated! The bytes in the current + // linear allocation area (between top and limit) are also counted here. virtual intptr_t Size() { return accounting_stats_.Size(); } - // Wasted bytes due to fragmentation and not recoverable until the - // next GC of this space. - intptr_t Waste() { return accounting_stats_.Waste(); } + // As size, but the bytes in lazily swept pages are estimated and the bytes + // in the current linear allocation area are not included. + virtual intptr_t SizeOfObjects() { + ASSERT(!IsSweepingComplete() || (unswept_free_bytes_ == 0)); + return Size() - unswept_free_bytes_ - (limit() - top()); + } - // Returns the address of the first object in this space. - Address bottom() { return first_page_->ObjectAreaStart(); } + // Wasted bytes in this space. These are just the bytes that were thrown away + // due to being too small to use for allocation. They do not include the + // free bytes that were not found at all due to lazy sweeping. + virtual intptr_t Waste() { return accounting_stats_.Waste(); } // Returns the allocation pointer in this space. Address top() { return allocation_info_.top; } + Address limit() { return allocation_info_.limit; } // Allocate the requested number of bytes in the space if possible, return a // failure object if not. MUST_USE_RESULT inline MaybeObject* AllocateRaw(int size_in_bytes); - // Allocate the requested number of bytes for relocation during mark-compact - // collection. - MUST_USE_RESULT inline MaybeObject* MCAllocateRaw(int size_in_bytes); - virtual bool ReserveSpace(int bytes); - // Used by ReserveSpace. - virtual void PutRestOfCurrentPageOnFreeList(Page* current_page) = 0; - - // Free all pages in range from prev (exclusive) to last (inclusive). - // Freed pages are moved to the end of page list. - void FreePages(Page* prev, Page* last); - - // Deallocates a block. - virtual void DeallocateBlock(Address start, - int size_in_bytes, - bool add_to_freelist) = 0; + // Give a block of memory to the space's free list. It might be added to + // the free list or accounted as waste. + // If add_to_freelist is false then just accounting stats are updated and + // no attempt to add area to free list is made. + int Free(Address start, int size_in_bytes) { + int wasted = free_list_.Free(start, size_in_bytes); + accounting_stats_.DeallocateBytes(size_in_bytes - wasted); + return size_in_bytes - wasted; + } // Set space allocation info. - void SetTop(Address top) { + void SetTop(Address top, Address limit) { + ASSERT(top == limit || + Page::FromAddress(top) == Page::FromAddress(limit - 1)); allocation_info_.top = top; - allocation_info_.limit = PageAllocationLimit(Page::FromAllocationTop(top)); + allocation_info_.limit = limit; } - // --------------------------------------------------------------------------- - // Mark-compact collection support functions - - // Set the relocation point to the beginning of the space. - void MCResetRelocationInfo(); - - // Writes relocation info to the top page. - void MCWriteRelocationInfoToPage() { - TopPageOf(mc_forwarding_info_)-> - SetAllocationWatermark(mc_forwarding_info_.top); + void Allocate(int bytes) { + accounting_stats_.AllocateBytes(bytes); } - // Computes the offset of a given address in this space to the beginning - // of the space. - int MCSpaceOffsetForAddress(Address addr); + void IncreaseCapacity(int size) { + accounting_stats_.ExpandSpace(size); + } - // Updates the allocation pointer to the relocation top after a mark-compact - // collection. - virtual void MCCommitRelocationInfo() = 0; + // Releases an unused page and shrinks the space. + void ReleasePage(Page* page); - // Releases half of unused pages. - void Shrink(); + // Releases all of the unused pages. + void ReleaseAllUnusedPages(); - // Ensures that the capacity is at least 'capacity'. Returns false on failure. - bool EnsureCapacity(int capacity); + // The dummy page that anchors the linked list of pages. + Page* anchor() { return &anchor_; } #ifdef DEBUG // Print meta info and objects in this space. @@ -1141,6 +1536,9 @@ class PagedSpace : public Space { // Verify integrity of this space. virtual void Verify(ObjectVisitor* visitor); + // Reports statistics for the space + void ReportStatistics(); + // Overridden by subclasses to verify space-specific object // properties (e.g., only maps or free-list nodes are in map space). virtual void VerifyObject(HeapObject* obj) {} @@ -1151,10 +1549,56 @@ class PagedSpace : public Space { static void ResetCodeStatistics(); #endif - // Returns the page of the allocation pointer. - Page* AllocationTopPage() { return TopPageOf(allocation_info_); } + bool was_swept_conservatively() { return was_swept_conservatively_; } + void set_was_swept_conservatively(bool b) { was_swept_conservatively_ = b; } + + // Evacuation candidates are swept by evacuator. Needs to return a valid + // result before _and_ after evacuation has finished. + static bool ShouldBeSweptLazily(Page* p) { + return !p->IsEvacuationCandidate() && + !p->IsFlagSet(Page::RESCAN_ON_EVACUATION) && + !p->WasSweptPrecisely(); + } + + void SetPagesToSweep(Page* first) { + ASSERT(unswept_free_bytes_ == 0); + if (first == &anchor_) first = NULL; + first_unswept_page_ = first; + } + + void IncrementUnsweptFreeBytes(int by) { + unswept_free_bytes_ += by; + } + + void IncreaseUnsweptFreeBytes(Page* p) { + ASSERT(ShouldBeSweptLazily(p)); + unswept_free_bytes_ += (Page::kObjectAreaSize - p->LiveBytes()); + } + + void DecreaseUnsweptFreeBytes(Page* p) { + ASSERT(ShouldBeSweptLazily(p)); + unswept_free_bytes_ -= (Page::kObjectAreaSize - p->LiveBytes()); + } + + bool AdvanceSweeper(intptr_t bytes_to_sweep); + + bool IsSweepingComplete() { + return !first_unswept_page_->is_valid(); + } + + Page* FirstPage() { return anchor_.next_page(); } + Page* LastPage() { return anchor_.prev_page(); } + + void CountFreeListItems(Page* p, FreeList::SizeStats* sizes) { + free_list_.CountFreeListItems(p, sizes); + } + + void EvictEvacuationCandidatesFromFreeLists(); - void RelinkPageListInChunkOrder(bool deallocate_blocks); + bool CanExpand(); + + // Returns the number of total pages in this space. + int CountTotalPages(); protected: // Maximum capacity of this space. @@ -1163,79 +1607,43 @@ class PagedSpace : public Space { // Accounting information for this space. AllocationStats accounting_stats_; - // The first page in this space. - Page* first_page_; + // The dummy page that anchors the double linked list of pages. + Page anchor_; - // The last page in this space. Initially set in Setup, updated in - // Expand and Shrink. - Page* last_page_; - - // True if pages owned by this space are linked in chunk-order. - // See comment for class MemoryAllocator for definition of chunk-order. - bool page_list_is_chunk_ordered_; + // The space's free list. + FreeList free_list_; // Normal allocation information. AllocationInfo allocation_info_; - // Relocation information during mark-compact collections. - AllocationInfo mc_forwarding_info_; - // Bytes of each page that cannot be allocated. Possibly non-zero // for pages in spaces with only fixed-size objects. Always zero // for pages in spaces with variable sized objects (those pages are // padded with free-list nodes). int page_extra_; - // Sets allocation pointer to a page bottom. - static void SetAllocationInfo(AllocationInfo* alloc_info, Page* p); + bool was_swept_conservatively_; - // Returns the top page specified by an allocation info structure. - static Page* TopPageOf(AllocationInfo alloc_info) { - return Page::FromAllocationTop(alloc_info.limit); - } + // The first page to be swept when the lazy sweeper advances. Is set + // to NULL when all pages have been swept. + Page* first_unswept_page_; - int CountPagesToTop() { - Page* p = Page::FromAllocationTop(allocation_info_.top); - PageIterator it(this, PageIterator::ALL_PAGES); - int counter = 1; - while (it.has_next()) { - if (it.next() == p) return counter; - counter++; - } - UNREACHABLE(); - return -1; - } + // The number of free bytes which could be reclaimed by advancing the + // lazy sweeper. This is only an estimation because lazy sweeping is + // done conservatively. + intptr_t unswept_free_bytes_; // Expands the space by allocating a fixed number of pages. Returns false if - // it cannot allocate requested number of pages from OS. Newly allocated - // pages are append to the last_page; - bool Expand(Page* last_page); + // it cannot allocate requested number of pages from OS, or if the hard heap + // size limit has been hit. + bool Expand(); - // Generic fast case allocation function that tries linear allocation in - // the top page of 'alloc_info'. Returns NULL on failure. - inline HeapObject* AllocateLinearly(AllocationInfo* alloc_info, - int size_in_bytes); - - // During normal allocation or deserialization, roll to the next page in - // the space (there is assumed to be one) and allocate there. This - // function is space-dependent. - virtual HeapObject* AllocateInNextPage(Page* current_page, - int size_in_bytes) = 0; + // Generic fast case allocation function that tries linear allocation at the + // address denoted by top in allocation_info_. + inline HeapObject* AllocateLinearly(int size_in_bytes); // Slow path of AllocateRaw. This function is space-dependent. - MUST_USE_RESULT virtual HeapObject* SlowAllocateRaw(int size_in_bytes) = 0; - - // Slow path of MCAllocateRaw. - MUST_USE_RESULT HeapObject* SlowMCAllocateRaw(int size_in_bytes); - -#ifdef DEBUG - // Returns the number of total pages in this space. - int CountTotalPages(); -#endif - - private: - // Returns a pointer to the page of the relocation pointer. - Page* MCRelocationTopPage() { return TopPageOf(mc_forwarding_info_); } + MUST_USE_RESULT virtual HeapObject* SlowAllocateRaw(int size_in_bytes); friend class PageIterator; }; @@ -1276,39 +1684,126 @@ class HistogramInfo: public NumberAndSizeInfo { }; +enum SemiSpaceId { + kFromSpace = 0, + kToSpace = 1 +}; + + +class SemiSpace; + + +class NewSpacePage : public MemoryChunk { + public: + // GC related flags copied from from-space to to-space when + // flipping semispaces. + static const intptr_t kCopyOnFlipFlagsMask = + (1 << MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING) | + (1 << MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING) | + (1 << MemoryChunk::SCAN_ON_SCAVENGE); + + inline NewSpacePage* next_page() const { + return static_cast<NewSpacePage*>(next_chunk()); + } + + inline void set_next_page(NewSpacePage* page) { + set_next_chunk(page); + } + + inline NewSpacePage* prev_page() const { + return static_cast<NewSpacePage*>(prev_chunk()); + } + + inline void set_prev_page(NewSpacePage* page) { + set_prev_chunk(page); + } + + SemiSpace* semi_space() { + return reinterpret_cast<SemiSpace*>(owner()); + } + + bool is_anchor() { return !this->InNewSpace(); } + + static bool IsAtStart(Address addr) { + return (reinterpret_cast<intptr_t>(addr) & Page::kPageAlignmentMask) + == kObjectStartOffset; + } + + static bool IsAtEnd(Address addr) { + return (reinterpret_cast<intptr_t>(addr) & Page::kPageAlignmentMask) == 0; + } + + Address address() { + return reinterpret_cast<Address>(this); + } + + // Finds the NewSpacePage containg the given address. + static inline NewSpacePage* FromAddress(Address address_in_page) { + Address page_start = + reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address_in_page) & + ~Page::kPageAlignmentMask); + NewSpacePage* page = reinterpret_cast<NewSpacePage*>(page_start); + return page; + } + + // Find the page for a limit address. A limit address is either an address + // inside a page, or the address right after the last byte of a page. + static inline NewSpacePage* FromLimit(Address address_limit) { + return NewSpacePage::FromAddress(address_limit - 1); + } + + private: + // Create a NewSpacePage object that is only used as anchor + // for the doubly-linked list of real pages. + explicit NewSpacePage(SemiSpace* owner) { + InitializeAsAnchor(owner); + } + + static NewSpacePage* Initialize(Heap* heap, + Address start, + SemiSpace* semi_space); + + // Intialize a fake NewSpacePage used as sentinel at the ends + // of a doubly-linked list of real NewSpacePages. + // Only uses the prev/next links, and sets flags to not be in new-space. + void InitializeAsAnchor(SemiSpace* owner); + + friend class SemiSpace; + friend class SemiSpaceIterator; +}; + + // ----------------------------------------------------------------------------- // SemiSpace in young generation // -// A semispace is a contiguous chunk of memory. The mark-compact collector -// uses the memory in the from space as a marking stack when tracing live -// objects. +// A semispace is a contiguous chunk of memory holding page-like memory +// chunks. The mark-compact collector uses the memory of the first page in +// the from space as a marking stack when tracing live objects. class SemiSpace : public Space { public: // Constructor. - explicit SemiSpace(Heap* heap) : Space(heap, NEW_SPACE, NOT_EXECUTABLE) { - start_ = NULL; - age_mark_ = NULL; - } + SemiSpace(Heap* heap, SemiSpaceId semispace) + : Space(heap, NEW_SPACE, NOT_EXECUTABLE), + start_(NULL), + age_mark_(NULL), + id_(semispace), + anchor_(this), + current_page_(NULL) { } // Sets up the semispace using the given chunk. - bool Setup(Address start, int initial_capacity, int maximum_capacity); + void SetUp(Address start, int initial_capacity, int maximum_capacity); // Tear down the space. Heap memory was not allocated by the space, so it // is not deallocated here. void TearDown(); // True if the space has been set up but not torn down. - bool HasBeenSetup() { return start_ != NULL; } - - // Grow the size of the semispace by committing extra virtual memory. - // Assumes that the caller has checked that the semispace has not reached - // its maximum capacity (and thus there is space available in the reserved - // address range to grow). - bool Grow(); + bool HasBeenSetUp() { return start_ != NULL; } // Grow the semispace to the new capacity. The new capacity - // requested must be larger than the current capacity. + // requested must be larger than the current capacity and less than + // the maximum capacity. bool GrowTo(int new_capacity); // Shrinks the semispace to the new capacity. The new capacity @@ -1316,14 +1811,40 @@ class SemiSpace : public Space { // semispace and less than the current capacity. bool ShrinkTo(int new_capacity); - // Returns the start address of the space. - Address low() { return start_; } + // Returns the start address of the first page of the space. + Address space_start() { + ASSERT(anchor_.next_page() != &anchor_); + return anchor_.next_page()->body(); + } + + // Returns the start address of the current page of the space. + Address page_low() { + return current_page_->body(); + } + // Returns one past the end address of the space. - Address high() { return low() + capacity_; } + Address space_end() { + return anchor_.prev_page()->body_limit(); + } + + // Returns one past the end address of the current page of the space. + Address page_high() { + return current_page_->body_limit(); + } + + bool AdvancePage() { + NewSpacePage* next_page = current_page_->next_page(); + if (next_page == anchor()) return false; + current_page_ = next_page; + return true; + } + + // Resets the space to using the first page. + void Reset(); // Age mark accessors. Address age_mark() { return age_mark_; } - void set_age_mark(Address mark) { age_mark_ = mark; } + void set_age_mark(Address mark); // True if the address is in the address range of this semispace (not // necessarily below the allocation pointer). @@ -1338,11 +1859,6 @@ class SemiSpace : public Space { return (reinterpret_cast<uintptr_t>(o) & object_mask_) == object_expected_; } - // The offset of an address from the beginning of the space. - int SpaceOffsetForAddress(Address addr) { - return static_cast<int>(addr - low()); - } - // If we don't have these here then SemiSpace will be abstract. However // they should never be called. virtual intptr_t Size() { @@ -1359,9 +1875,19 @@ class SemiSpace : public Space { bool Commit(); bool Uncommit(); + NewSpacePage* first_page() { return anchor_.next_page(); } + NewSpacePage* current_page() { return current_page_; } + #ifdef DEBUG virtual void Print(); virtual void Verify(); + // Validate a range of of addresses in a SemiSpace. + // The "from" address must be on a page prior to the "to" address, + // in the linked page order, or it must be earlier on the same page. + static void AssertValidRange(Address from, Address to); +#else + // Do nothing. + inline static void AssertValidRange(Address from, Address to) {} #endif // Returns the current capacity of the semi space. @@ -1373,7 +1899,17 @@ class SemiSpace : public Space { // Returns the initial capacity of the semi space. int InitialCapacity() { return initial_capacity_; } + SemiSpaceId id() { return id_; } + + static void Swap(SemiSpace* from, SemiSpace* to); + private: + // Flips the semispace between being from-space and to-space. + // Copies the flags into the masked positions on all pages in the space. + void FlipPages(intptr_t flags, intptr_t flag_mask); + + NewSpacePage* anchor() { return &anchor_; } + // The current and maximum capacity of the space. int capacity_; int maximum_capacity_; @@ -1390,7 +1926,13 @@ class SemiSpace : public Space { uintptr_t object_expected_; bool committed_; + SemiSpaceId id_; + NewSpacePage anchor_; + NewSpacePage* current_page_; + + friend class SemiSpaceIterator; + friend class NewSpacePageIterator; public: TRACK_MEMORY("SemiSpace") }; @@ -1406,12 +1948,26 @@ class SemiSpaceIterator : public ObjectIterator { // Create an iterator over the objects in the given space. If no start // address is given, the iterator starts from the bottom of the space. If // no size function is given, the iterator calls Object::Size(). + + // Iterate over all of allocated to-space. explicit SemiSpaceIterator(NewSpace* space); + // Iterate over all of allocated to-space, with a custome size function. SemiSpaceIterator(NewSpace* space, HeapObjectCallback size_func); + // Iterate over part of allocated to-space, from start to the end + // of allocation. SemiSpaceIterator(NewSpace* space, Address start); + // Iterate from one address to another in the same semi-space. + SemiSpaceIterator(Address from, Address to); - HeapObject* next() { + HeapObject* Next() { if (current_ == limit_) return NULL; + if (NewSpacePage::IsAtEnd(current_)) { + NewSpacePage* page = NewSpacePage::FromLimit(current_); + page = page->next_page(); + ASSERT(!page->is_anchor()); + current_ = page->body(); + if (current_ == limit_) return NULL; + } HeapObject* object = HeapObject::FromAddress(current_); int size = (size_func_ == NULL) ? object->Size() : size_func_(object); @@ -1421,14 +1977,13 @@ class SemiSpaceIterator : public ObjectIterator { } // Implementation of the ObjectIterator functions. - virtual HeapObject* next_object() { return next(); } + virtual HeapObject* next_object() { return Next(); } private: - void Initialize(NewSpace* space, Address start, Address end, + void Initialize(Address start, + Address end, HeapObjectCallback size_func); - // The semispace. - SemiSpace* space_; // The current iteration point. Address current_; // The end of iteration. @@ -1439,6 +1994,34 @@ class SemiSpaceIterator : public ObjectIterator { // ----------------------------------------------------------------------------- +// A PageIterator iterates the pages in a semi-space. +class NewSpacePageIterator BASE_EMBEDDED { + public: + // Make an iterator that runs over all pages in to-space. + explicit inline NewSpacePageIterator(NewSpace* space); + + // Make an iterator that runs over all pages in the given semispace, + // even those not used in allocation. + explicit inline NewSpacePageIterator(SemiSpace* space); + + // Make iterator that iterates from the page containing start + // to the page that contains limit in the same semispace. + inline NewSpacePageIterator(Address start, Address limit); + + inline bool has_next(); + inline NewSpacePage* next(); + + private: + NewSpacePage* prev_page_; // Previous page returned. + // Next page that will be returned. Cached here so that we can use this + // iterator for operations that deallocate pages. + NewSpacePage* next_page_; + // Last page returned. + NewSpacePage* last_page_; +}; + + +// ----------------------------------------------------------------------------- // The young generation space. // // The new space consists of a contiguous pair of semispaces. It simply @@ -1449,19 +2032,21 @@ class NewSpace : public Space { // Constructor. explicit NewSpace(Heap* heap) : Space(heap, NEW_SPACE, NOT_EXECUTABLE), - to_space_(heap), - from_space_(heap) {} + to_space_(heap, kToSpace), + from_space_(heap, kFromSpace), + reservation_(), + inline_allocation_limit_step_(0) {} // Sets up the new space using the given chunk. - bool Setup(Address start, int size); + bool SetUp(int reserved_semispace_size_, int max_semispace_size); // Tears down the space. Heap memory was not allocated by the space, so it // is not deallocated here. void TearDown(); // True if the space has been set up but not torn down. - bool HasBeenSetup() { - return to_space_.HasBeenSetup() && from_space_.HasBeenSetup(); + bool HasBeenSetUp() { + return to_space_.HasBeenSetUp() && from_space_.HasBeenSetUp(); } // Flip the pair of spaces. @@ -1480,18 +2065,30 @@ class NewSpace : public Space { return (reinterpret_cast<uintptr_t>(a) & address_mask_) == reinterpret_cast<uintptr_t>(start_); } + bool Contains(Object* o) { - return (reinterpret_cast<uintptr_t>(o) & object_mask_) == object_expected_; + Address a = reinterpret_cast<Address>(o); + return (reinterpret_cast<uintptr_t>(a) & object_mask_) == object_expected_; } // Return the allocated bytes in the active semispace. - virtual intptr_t Size() { return static_cast<int>(top() - bottom()); } + virtual intptr_t Size() { + return pages_used_ * Page::kObjectAreaSize + + static_cast<int>(top() - to_space_.page_low()); + } + // The same, but returning an int. We have to have the one that returns // intptr_t because it is inherited, but if we know we are dealing with the // new space, which can't get as big as the other spaces then this is useful: int SizeAsInt() { return static_cast<int>(Size()); } // Return the current capacity of a semispace. + intptr_t EffectiveCapacity() { + SLOW_ASSERT(to_space_.Capacity() == from_space_.Capacity()); + return (to_space_.Capacity() / Page::kPageSize) * Page::kObjectAreaSize; + } + + // Return the current capacity of a semispace. intptr_t Capacity() { ASSERT(to_space_.Capacity() == from_space_.Capacity()); return to_space_.Capacity(); @@ -1503,8 +2100,10 @@ class NewSpace : public Space { return Capacity(); } - // Return the available bytes without growing in the active semispace. - intptr_t Available() { return Capacity() - Size(); } + // Return the available bytes without growing. + intptr_t Available() { + return Capacity() - Size(); + } // Return the maximum capacity of a semispace. int MaximumCapacity() { @@ -1519,9 +2118,12 @@ class NewSpace : public Space { } // Return the address of the allocation pointer in the active semispace. - Address top() { return allocation_info_.top; } + Address top() { + ASSERT(to_space_.current_page()->ContainsLimit(allocation_info_.top)); + return allocation_info_.top; + } // Return the address of the first object in the active semispace. - Address bottom() { return to_space_.low(); } + Address bottom() { return to_space_.space_start(); } // Get the age mark of the inactive semispace. Address age_mark() { return from_space_.age_mark(); } @@ -1533,54 +2135,68 @@ class NewSpace : public Space { Address start() { return start_; } uintptr_t mask() { return address_mask_; } + INLINE(uint32_t AddressToMarkbitIndex(Address addr)) { + ASSERT(Contains(addr)); + ASSERT(IsAligned(OffsetFrom(addr), kPointerSize) || + IsAligned(OffsetFrom(addr) - 1, kPointerSize)); + return static_cast<uint32_t>(addr - start_) >> kPointerSizeLog2; + } + + INLINE(Address MarkbitIndexToAddress(uint32_t index)) { + return reinterpret_cast<Address>(index << kPointerSizeLog2); + } + // The allocation top and limit addresses. Address* allocation_top_address() { return &allocation_info_.top; } Address* allocation_limit_address() { return &allocation_info_.limit; } - MUST_USE_RESULT MaybeObject* AllocateRaw(int size_in_bytes) { - return AllocateRawInternal(size_in_bytes, &allocation_info_); - } - - // Allocate the requested number of bytes for relocation during mark-compact - // collection. - MUST_USE_RESULT MaybeObject* MCAllocateRaw(int size_in_bytes) { - return AllocateRawInternal(size_in_bytes, &mc_forwarding_info_); - } + MUST_USE_RESULT INLINE(MaybeObject* AllocateRaw(int size_in_bytes)); // Reset the allocation pointer to the beginning of the active semispace. void ResetAllocationInfo(); - // Reset the reloction pointer to the bottom of the inactive semispace in - // preparation for mark-compact collection. - void MCResetRelocationInfo(); - // Update the allocation pointer in the active semispace after a - // mark-compact collection. - void MCCommitRelocationInfo(); - // Get the extent of the inactive semispace (for use as a marking stack). - Address FromSpaceLow() { return from_space_.low(); } - Address FromSpaceHigh() { return from_space_.high(); } + void LowerInlineAllocationLimit(intptr_t step) { + inline_allocation_limit_step_ = step; + if (step == 0) { + allocation_info_.limit = to_space_.page_high(); + } else { + allocation_info_.limit = Min( + allocation_info_.top + inline_allocation_limit_step_, + allocation_info_.limit); + } + top_on_previous_step_ = allocation_info_.top; + } - // Get the extent of the active semispace (to sweep newly copied objects - // during a scavenge collection). - Address ToSpaceLow() { return to_space_.low(); } - Address ToSpaceHigh() { return to_space_.high(); } + // Get the extent of the inactive semispace (for use as a marking stack, + // or to zap it). Notice: space-addresses are not necessarily on the + // same page, so FromSpaceStart() might be above FromSpaceEnd(). + Address FromSpacePageLow() { return from_space_.page_low(); } + Address FromSpacePageHigh() { return from_space_.page_high(); } + Address FromSpaceStart() { return from_space_.space_start(); } + Address FromSpaceEnd() { return from_space_.space_end(); } - // Offsets from the beginning of the semispaces. - int ToSpaceOffsetForAddress(Address a) { - return to_space_.SpaceOffsetForAddress(a); + // Get the extent of the active semispace's pages' memory. + Address ToSpaceStart() { return to_space_.space_start(); } + Address ToSpaceEnd() { return to_space_.space_end(); } + + inline bool ToSpaceContains(Address address) { + return to_space_.Contains(address); } - int FromSpaceOffsetForAddress(Address a) { - return from_space_.SpaceOffsetForAddress(a); + inline bool FromSpaceContains(Address address) { + return from_space_.Contains(address); } // True if the object is a heap object in the address range of the // respective semispace (not necessarily below the allocation pointer of the // semispace). - bool ToSpaceContains(Object* o) { return to_space_.Contains(o); } - bool FromSpaceContains(Object* o) { return from_space_.Contains(o); } + inline bool ToSpaceContains(Object* o) { return to_space_.Contains(o); } + inline bool FromSpaceContains(Object* o) { return from_space_.Contains(o); } - bool ToSpaceContains(Address a) { return to_space_.Contains(a); } - bool FromSpaceContains(Address a) { return from_space_.Contains(a); } + // Try to switch the active semispace to a new, empty, page. + // Returns false if this isn't possible or reasonable (i.e., there + // are no pages, or the current page is already empty), or true + // if successful. + bool AddFreshPage(); virtual bool ReserveSpace(int bytes); @@ -1620,10 +2236,24 @@ class NewSpace : public Space { return from_space_.Uncommit(); } + inline intptr_t inline_allocation_limit_step() { + return inline_allocation_limit_step_; + } + + SemiSpace* active_space() { return &to_space_; } + private: + // Update allocation info to match the current to-space page. + void UpdateAllocationInfo(); + + Address chunk_base_; + uintptr_t chunk_size_; + // The semispaces. SemiSpace to_space_; SemiSpace from_space_; + VirtualMemory reservation_; + int pages_used_; // Start address and bit mask for containment testing. Address start_; @@ -1634,15 +2264,19 @@ class NewSpace : public Space { // Allocation pointer and limit for normal allocation and allocation during // mark-compact collection. AllocationInfo allocation_info_; - AllocationInfo mc_forwarding_info_; + + // When incremental marking is active we will set allocation_info_.limit + // to be lower than actual limit and then will gradually increase it + // in steps to guarantee that we do incremental marking steps even + // when all allocation is performed from inlined generated code. + intptr_t inline_allocation_limit_step_; + + Address top_on_previous_step_; HistogramInfo* allocated_histogram_; HistogramInfo* promoted_histogram_; - // Implementation of AllocateRaw and MCAllocateRaw. - MUST_USE_RESULT inline MaybeObject* AllocateRawInternal( - int size_in_bytes, - AllocationInfo* alloc_info); + MUST_USE_RESULT MaybeObject* SlowAllocateRaw(int size_in_bytes); friend class SemiSpaceIterator; @@ -1652,193 +2286,6 @@ class NewSpace : public Space { // ----------------------------------------------------------------------------- -// Free lists for old object spaces -// -// Free-list nodes are free blocks in the heap. They look like heap objects -// (free-list node pointers have the heap object tag, and they have a map like -// a heap object). They have a size and a next pointer. The next pointer is -// the raw address of the next free list node (or NULL). -class FreeListNode: public HeapObject { - public: - // Obtain a free-list node from a raw address. This is not a cast because - // it does not check nor require that the first word at the address is a map - // pointer. - static FreeListNode* FromAddress(Address address) { - return reinterpret_cast<FreeListNode*>(HeapObject::FromAddress(address)); - } - - static inline bool IsFreeListNode(HeapObject* object); - - // Set the size in bytes, which can be read with HeapObject::Size(). This - // function also writes a map to the first word of the block so that it - // looks like a heap object to the garbage collector and heap iteration - // functions. - void set_size(Heap* heap, int size_in_bytes); - - // Accessors for the next field. - inline Address next(Heap* heap); - inline void set_next(Heap* heap, Address next); - - private: - static const int kNextOffset = POINTER_SIZE_ALIGN(ByteArray::kHeaderSize); - - DISALLOW_IMPLICIT_CONSTRUCTORS(FreeListNode); -}; - - -// The free list for the old space. -class OldSpaceFreeList BASE_EMBEDDED { - public: - OldSpaceFreeList(Heap* heap, AllocationSpace owner); - - // Clear the free list. - void Reset(); - - // Return the number of bytes available on the free list. - intptr_t available() { return available_; } - - // Place a node on the free list. The block of size 'size_in_bytes' - // starting at 'start' is placed on the free list. The return value is the - // number of bytes that have been lost due to internal fragmentation by - // freeing the block. Bookkeeping information will be written to the block, - // ie, its contents will be destroyed. The start address should be word - // aligned, and the size should be a non-zero multiple of the word size. - int Free(Address start, int size_in_bytes); - - // Allocate a block of size 'size_in_bytes' from the free list. The block - // is unitialized. A failure is returned if no block is available. The - // number of bytes lost to fragmentation is returned in the output parameter - // 'wasted_bytes'. The size should be a non-zero multiple of the word size. - MUST_USE_RESULT MaybeObject* Allocate(int size_in_bytes, int* wasted_bytes); - - void MarkNodes(); - - private: - // The size range of blocks, in bytes. (Smaller allocations are allowed, but - // will always result in waste.) - static const int kMinBlockSize = 2 * kPointerSize; - static const int kMaxBlockSize = Page::kMaxHeapObjectSize; - - Heap* heap_; - - // The identity of the owning space, for building allocation Failure - // objects. - AllocationSpace owner_; - - // Total available bytes in all blocks on this free list. - int available_; - - // Blocks are put on exact free lists in an array, indexed by size in words. - // The available sizes are kept in an increasingly ordered list. Entries - // corresponding to sizes < kMinBlockSize always have an empty free list - // (but index kHead is used for the head of the size list). - struct SizeNode { - // Address of the head FreeListNode of the implied block size or NULL. - Address head_node_; - // Size (words) of the next larger available size if head_node_ != NULL. - int next_size_; - }; - static const int kFreeListsLength = kMaxBlockSize / kPointerSize + 1; - SizeNode free_[kFreeListsLength]; - - // Sentinel elements for the size list. Real elements are in ]kHead..kEnd[. - static const int kHead = kMinBlockSize / kPointerSize - 1; - static const int kEnd = kMaxInt; - - // We keep a "finger" in the size list to speed up a common pattern: - // repeated requests for the same or increasing sizes. - int finger_; - - // Starting from *prev, find and return the smallest size >= index (words), - // or kEnd. Update *prev to be the largest size < index, or kHead. - int FindSize(int index, int* prev) { - int cur = free_[*prev].next_size_; - while (cur < index) { - *prev = cur; - cur = free_[cur].next_size_; - } - return cur; - } - - // Remove an existing element from the size list. - void RemoveSize(int index) { - int prev = kHead; - int cur = FindSize(index, &prev); - ASSERT(cur == index); - free_[prev].next_size_ = free_[cur].next_size_; - finger_ = prev; - } - - // Insert a new element into the size list. - void InsertSize(int index) { - int prev = kHead; - int cur = FindSize(index, &prev); - ASSERT(cur != index); - free_[prev].next_size_ = index; - free_[index].next_size_ = cur; - } - - // The size list is not updated during a sequence of calls to Free, but is - // rebuilt before the next allocation. - void RebuildSizeList(); - bool needs_rebuild_; - -#ifdef DEBUG - // Does this free list contain a free block located at the address of 'node'? - bool Contains(FreeListNode* node); -#endif - - DISALLOW_COPY_AND_ASSIGN(OldSpaceFreeList); -}; - - -// The free list for the map space. -class FixedSizeFreeList BASE_EMBEDDED { - public: - FixedSizeFreeList(Heap* heap, AllocationSpace owner, int object_size); - - // Clear the free list. - void Reset(); - - // Return the number of bytes available on the free list. - intptr_t available() { return available_; } - - // Place a node on the free list. The block starting at 'start' (assumed to - // have size object_size_) is placed on the free list. Bookkeeping - // information will be written to the block, ie, its contents will be - // destroyed. The start address should be word aligned. - void Free(Address start); - - // Allocate a fixed sized block from the free list. The block is unitialized. - // A failure is returned if no block is available. - MUST_USE_RESULT MaybeObject* Allocate(); - - void MarkNodes(); - - private: - Heap* heap_; - - // Available bytes on the free list. - intptr_t available_; - - // The head of the free list. - Address head_; - - // The tail of the free list. - Address tail_; - - // The identity of the owning space, for building allocation Failure - // objects. - AllocationSpace owner_; - - // The size of the objects in this space. - int object_size_; - - DISALLOW_COPY_AND_ASSIGN(FixedSizeFreeList); -}; - - -// ----------------------------------------------------------------------------- // Old object space (excluding map objects) class OldSpace : public PagedSpace { @@ -1849,71 +2296,28 @@ class OldSpace : public PagedSpace { intptr_t max_capacity, AllocationSpace id, Executability executable) - : PagedSpace(heap, max_capacity, id, executable), - free_list_(heap, id) { + : PagedSpace(heap, max_capacity, id, executable) { page_extra_ = 0; } - // The bytes available on the free list (ie, not above the linear allocation - // pointer). - intptr_t AvailableFree() { return free_list_.available(); } - // The limit of allocation for a page in this space. virtual Address PageAllocationLimit(Page* page) { return page->ObjectAreaEnd(); } - // Give a block of memory to the space's free list. It might be added to - // the free list or accounted as waste. - // If add_to_freelist is false then just accounting stats are updated and - // no attempt to add area to free list is made. - void Free(Address start, int size_in_bytes, bool add_to_freelist) { - accounting_stats_.DeallocateBytes(size_in_bytes); - - if (add_to_freelist) { - int wasted_bytes = free_list_.Free(start, size_in_bytes); - accounting_stats_.WasteBytes(wasted_bytes); - } - } - - virtual void DeallocateBlock(Address start, - int size_in_bytes, - bool add_to_freelist); - - // Prepare for full garbage collection. Resets the relocation pointer and - // clears the free list. - virtual void PrepareForMarkCompact(bool will_compact); - - // Updates the allocation pointer to the relocation top after a mark-compact - // collection. - virtual void MCCommitRelocationInfo(); - - virtual void PutRestOfCurrentPageOnFreeList(Page* current_page); - - void MarkFreeListNodes() { free_list_.MarkNodes(); } - -#ifdef DEBUG - // Reports statistics for the space - void ReportStatistics(); -#endif - - protected: - // Virtual function in the superclass. Slow path of AllocateRaw. - MUST_USE_RESULT HeapObject* SlowAllocateRaw(int size_in_bytes); - - // Virtual function in the superclass. Allocate linearly at the start of - // the page after current_page (there is assumed to be one). - HeapObject* AllocateInNextPage(Page* current_page, int size_in_bytes); - - private: - // The space's free list. - OldSpaceFreeList free_list_; - public: TRACK_MEMORY("OldSpace") }; +// For contiguous spaces, top should be in the space (or at the end) and limit +// should be the end of the space. +#define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \ + SLOW_ASSERT((space).page_low() <= (info).top \ + && (info).top <= (space).page_high() \ + && (info).limit <= (space).page_high()) + + // ----------------------------------------------------------------------------- // Old space for objects of a fixed size @@ -1926,8 +2330,7 @@ class FixedSpace : public PagedSpace { const char* name) : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE), object_size_in_bytes_(object_size_in_bytes), - name_(name), - free_list_(heap, id, object_size_in_bytes) { + name_(name) { page_extra_ = Page::kObjectAreaSize % object_size_in_bytes; } @@ -1938,44 +2341,10 @@ class FixedSpace : public PagedSpace { int object_size_in_bytes() { return object_size_in_bytes_; } - // Give a fixed sized block of memory to the space's free list. - // If add_to_freelist is false then just accounting stats are updated and - // no attempt to add area to free list is made. - void Free(Address start, bool add_to_freelist) { - if (add_to_freelist) { - free_list_.Free(start); - } - accounting_stats_.DeallocateBytes(object_size_in_bytes_); - } - // Prepares for a mark-compact GC. - virtual void PrepareForMarkCompact(bool will_compact); - - // Updates the allocation pointer to the relocation top after a mark-compact - // collection. - virtual void MCCommitRelocationInfo(); - - virtual void PutRestOfCurrentPageOnFreeList(Page* current_page); - - virtual void DeallocateBlock(Address start, - int size_in_bytes, - bool add_to_freelist); - - void MarkFreeListNodes() { free_list_.MarkNodes(); } - -#ifdef DEBUG - // Reports statistic info of the space - void ReportStatistics(); -#endif + virtual void PrepareForMarkCompact(); protected: - // Virtual function in the superclass. Slow path of AllocateRaw. - MUST_USE_RESULT HeapObject* SlowAllocateRaw(int size_in_bytes); - - // Virtual function in the superclass. Allocate linearly at the start of - // the page after current_page (there is assumed to be one). - HeapObject* AllocateInNextPage(Page* current_page, int size_in_bytes); - void ResetFreeList() { free_list_.Reset(); } @@ -1986,9 +2355,6 @@ class FixedSpace : public PagedSpace { // The name of this space. const char* name_; - - // The space's free list. - FixedSizeFreeList free_list_; }; @@ -1998,89 +2364,21 @@ class FixedSpace : public PagedSpace { class MapSpace : public FixedSpace { public: // Creates a map space object with a maximum capacity. - MapSpace(Heap* heap, - intptr_t max_capacity, - int max_map_space_pages, - AllocationSpace id) + MapSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id) : FixedSpace(heap, max_capacity, id, Map::kSize, "map"), - max_map_space_pages_(max_map_space_pages) { - ASSERT(max_map_space_pages < kMaxMapPageIndex); + max_map_space_pages_(kMaxMapPageIndex - 1) { } - // Prepares for a mark-compact GC. - virtual void PrepareForMarkCompact(bool will_compact); - // Given an index, returns the page address. - Address PageAddress(int page_index) { return page_addresses_[page_index]; } - - static const int kMaxMapPageIndex = 1 << MapWord::kMapPageIndexBits; - - // Are map pointers encodable into map word? - bool MapPointersEncodable() { - if (!FLAG_use_big_map_space) { - ASSERT(CountPagesToTop() <= kMaxMapPageIndex); - return true; - } - return CountPagesToTop() <= max_map_space_pages_; - } - - // Should be called after forced sweep to find out if map space needs - // compaction. - bool NeedsCompaction(int live_maps) { - return !MapPointersEncodable() && live_maps <= CompactionThreshold(); - } - - Address TopAfterCompaction(int live_maps) { - ASSERT(NeedsCompaction(live_maps)); - - int pages_left = live_maps / kMapsPerPage; - PageIterator it(this, PageIterator::ALL_PAGES); - while (pages_left-- > 0) { - ASSERT(it.has_next()); - it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks); - } - ASSERT(it.has_next()); - Page* top_page = it.next(); - top_page->SetRegionMarks(Page::kAllRegionsCleanMarks); - ASSERT(top_page->is_valid()); - - int offset = live_maps % kMapsPerPage * Map::kSize; - Address top = top_page->ObjectAreaStart() + offset; - ASSERT(top < top_page->ObjectAreaEnd()); - ASSERT(Contains(top)); - - return top; - } - - void FinishCompaction(Address new_top, int live_maps) { - Page* top_page = Page::FromAddress(new_top); - ASSERT(top_page->is_valid()); - - SetAllocationInfo(&allocation_info_, top_page); - allocation_info_.top = new_top; - - int new_size = live_maps * Map::kSize; - accounting_stats_.DeallocateBytes(accounting_stats_.Size()); - accounting_stats_.AllocateBytes(new_size); - - // Flush allocation watermarks. - for (Page* p = first_page_; p != top_page; p = p->next_page()) { - p->SetAllocationWatermark(p->AllocationTop()); + // TODO(1600): this limit is artifical just to keep code compilable + static const int kMaxMapPageIndex = 1 << 16; + + virtual int RoundSizeDownToObjectAlignment(int size) { + if (IsPowerOf2(Map::kSize)) { + return RoundDown(size, Map::kSize); + } else { + return (size / Map::kSize) * Map::kSize; } - top_page->SetAllocationWatermark(new_top); - -#ifdef DEBUG - if (FLAG_enable_slow_asserts) { - intptr_t actual_size = 0; - for (Page* p = first_page_; p != top_page; p = p->next_page()) - actual_size += kMapsPerPage * Map::kSize; - actual_size += (new_top - top_page->ObjectAreaStart()); - ASSERT(accounting_stats_.Size() == actual_size); - } -#endif - - Shrink(); - ResetFreeList(); } protected: @@ -2098,9 +2396,6 @@ class MapSpace : public FixedSpace { const int max_map_space_pages_; - // An array of page start address in a map space. - Address page_addresses_[kMaxMapPageIndex]; - public: TRACK_MEMORY("MapSpace") }; @@ -2116,6 +2411,14 @@ class CellSpace : public FixedSpace { : FixedSpace(heap, max_capacity, id, JSGlobalPropertyCell::kSize, "cell") {} + virtual int RoundSizeDownToObjectAlignment(int size) { + if (IsPowerOf2(JSGlobalPropertyCell::kSize)) { + return RoundDown(size, JSGlobalPropertyCell::kSize); + } else { + return (size / JSGlobalPropertyCell::kSize) * JSGlobalPropertyCell::kSize; + } + } + protected: #ifdef DEBUG virtual void VerifyObject(HeapObject* obj); @@ -2133,81 +2436,26 @@ class CellSpace : public FixedSpace { // A large object always starts at Page::kObjectStartOffset to a page. // Large objects do not move during garbage collections. -// A LargeObjectChunk holds exactly one large object page with exactly one -// large object. -class LargeObjectChunk { - public: - // Allocates a new LargeObjectChunk that contains a large object page - // (Page::kPageSize aligned) that has at least size_in_bytes (for a large - // object) bytes after the object area start of that page. - static LargeObjectChunk* New(int size_in_bytes, Executability executable); - - // Free the memory associated with the chunk. - void Free(Executability executable); - - // Interpret a raw address as a large object chunk. - static LargeObjectChunk* FromAddress(Address address) { - return reinterpret_cast<LargeObjectChunk*>(address); - } - - // Returns the address of this chunk. - Address address() { return reinterpret_cast<Address>(this); } - - Page* GetPage() { - return Page::FromAddress(RoundUp(address(), Page::kPageSize)); - } - - // Accessors for the fields of the chunk. - LargeObjectChunk* next() { return next_; } - void set_next(LargeObjectChunk* chunk) { next_ = chunk; } - size_t size() { return size_ & ~Page::kPageFlagMask; } - - // Compute the start address in the chunk. - Address GetStartAddress() { return GetPage()->ObjectAreaStart(); } - - // Returns the object in this chunk. - HeapObject* GetObject() { return HeapObject::FromAddress(GetStartAddress()); } - - // Given a requested size returns the physical size of a chunk to be - // allocated. - static int ChunkSizeFor(int size_in_bytes); - - // Given a chunk size, returns the object size it can accommodate. Used by - // LargeObjectSpace::Available. - static intptr_t ObjectSizeFor(intptr_t chunk_size) { - if (chunk_size <= (Page::kPageSize + Page::kObjectStartOffset)) return 0; - return chunk_size - Page::kPageSize - Page::kObjectStartOffset; - } - - private: - // A pointer to the next large object chunk in the space or NULL. - LargeObjectChunk* next_; - - // The total size of this chunk. - size_t size_; - - public: - TRACK_MEMORY("LargeObjectChunk") -}; - - class LargeObjectSpace : public Space { public: - LargeObjectSpace(Heap* heap, AllocationSpace id); + LargeObjectSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id); virtual ~LargeObjectSpace() {} // Initializes internal data structures. - bool Setup(); + bool SetUp(); // Releases internal resources, frees objects in this space. void TearDown(); - // Allocates a (non-FixedArray, non-Code) large object. - MUST_USE_RESULT MaybeObject* AllocateRaw(int size_in_bytes); - // Allocates a large Code object. - MUST_USE_RESULT MaybeObject* AllocateRawCode(int size_in_bytes); - // Allocates a large FixedArray. - MUST_USE_RESULT MaybeObject* AllocateRawFixedArray(int size_in_bytes); + static intptr_t ObjectSizeFor(intptr_t chunk_size) { + if (chunk_size <= (Page::kPageSize + Page::kObjectStartOffset)) return 0; + return chunk_size - Page::kPageSize - Page::kObjectStartOffset; + } + + // Shared implementation of AllocateRaw, AllocateRawCode and + // AllocateRawFixedArray. + MUST_USE_RESULT MaybeObject* AllocateRaw(int object_size, + Executability executable); // Available bytes for objects in this space. inline intptr_t Available(); @@ -2231,10 +2479,7 @@ class LargeObjectSpace : public Space { // Finds a large object page containing the given pc, returns NULL // if such a page doesn't exist. - LargeObjectChunk* FindChunkContainingPc(Address pc); - - // Iterates objects covered by dirty regions. - void IterateDirtyRegions(ObjectSlotCallback func); + LargePage* FindPageContainingPc(Address pc); // Frees unmarked objects. void FreeUnmarkedObjects(); @@ -2243,13 +2488,15 @@ class LargeObjectSpace : public Space { bool Contains(HeapObject* obj); // Checks whether the space is empty. - bool IsEmpty() { return first_chunk_ == NULL; } + bool IsEmpty() { return first_page_ == NULL; } // See the comments for ReserveSpace in the Space class. This has to be // called after ReserveSpace has been called on the paged spaces, since they // may use some memory, leaving less for large objects. virtual bool ReserveSpace(int bytes); + LargePage* first_page() { return first_page_; } + #ifdef DEBUG virtual void Verify(); virtual void Print(); @@ -2261,18 +2508,13 @@ class LargeObjectSpace : public Space { bool SlowContains(Address addr) { return !FindObject(addr)->IsFailure(); } private: + intptr_t max_capacity_; // The head of the linked list of large object chunks. - LargeObjectChunk* first_chunk_; + LargePage* first_page_; intptr_t size_; // allocated bytes int page_count_; // number of chunks intptr_t objects_size_; // size of objects - // Shared implementation of AllocateRaw, AllocateRawCode and - // AllocateRawFixedArray. - MUST_USE_RESULT MaybeObject* AllocateRawInternal(int requested_size, - int object_size, - Executability executable); - friend class LargeObjectIterator; public: @@ -2285,17 +2527,78 @@ class LargeObjectIterator: public ObjectIterator { explicit LargeObjectIterator(LargeObjectSpace* space); LargeObjectIterator(LargeObjectSpace* space, HeapObjectCallback size_func); - HeapObject* next(); + HeapObject* Next(); // implementation of ObjectIterator. - virtual HeapObject* next_object() { return next(); } + virtual HeapObject* next_object() { return Next(); } private: - LargeObjectChunk* current_; + LargePage* current_; HeapObjectCallback size_func_; }; +// Iterates over the chunks (pages and large object pages) that can contain +// pointers to new space. +class PointerChunkIterator BASE_EMBEDDED { + public: + inline explicit PointerChunkIterator(Heap* heap); + + // Return NULL when the iterator is done. + MemoryChunk* next() { + switch (state_) { + case kOldPointerState: { + if (old_pointer_iterator_.has_next()) { + return old_pointer_iterator_.next(); + } + state_ = kMapState; + // Fall through. + } + case kMapState: { + if (map_iterator_.has_next()) { + return map_iterator_.next(); + } + state_ = kLargeObjectState; + // Fall through. + } + case kLargeObjectState: { + HeapObject* heap_object; + do { + heap_object = lo_iterator_.Next(); + if (heap_object == NULL) { + state_ = kFinishedState; + return NULL; + } + // Fixed arrays are the only pointer-containing objects in large + // object space. + } while (!heap_object->IsFixedArray()); + MemoryChunk* answer = MemoryChunk::FromAddress(heap_object->address()); + return answer; + } + case kFinishedState: + return NULL; + default: + break; + } + UNREACHABLE(); + return NULL; + } + + + private: + enum State { + kOldPointerState, + kMapState, + kLargeObjectState, + kFinishedState + }; + State state_; + PageIterator old_pointer_iterator_; + PageIterator map_iterator_; + LargeObjectIterator lo_iterator_; +}; + + #ifdef DEBUG struct CommentStatistic { const char* comment; diff --git a/deps/v8/src/splay-tree-inl.h b/deps/v8/src/splay-tree-inl.h index 9c2287eab7..4640ed5b08 100644 --- a/deps/v8/src/splay-tree-inl.h +++ b/deps/v8/src/splay-tree-inl.h @@ -45,7 +45,7 @@ template<typename Config, class Allocator> bool SplayTree<Config, Allocator>::Insert(const Key& key, Locator* locator) { if (is_empty()) { // If the tree is empty, insert the new node. - root_ = new Node(key, Config::kNoValue); + root_ = new Node(key, Config::NoValue()); } else { // Splay on the key to move the last node on the search path // for the key to the root of the tree. @@ -57,7 +57,7 @@ bool SplayTree<Config, Allocator>::Insert(const Key& key, Locator* locator) { return false; } // Insert the new node. - Node* node = new Node(key, Config::kNoValue); + Node* node = new Node(key, Config::NoValue()); InsertInternal(cmp, node); } locator->bind(root_); @@ -226,7 +226,7 @@ template<typename Config, class Allocator> void SplayTree<Config, Allocator>::Splay(const Key& key) { if (is_empty()) return; - Node dummy_node(Config::kNoKey, Config::kNoValue); + Node dummy_node(Config::kNoKey, Config::NoValue()); // Create a dummy node. The use of the dummy node is a bit // counter-intuitive: The right child of the dummy node will hold // the L tree of the algorithm. The left child of the dummy node diff --git a/deps/v8/src/store-buffer-inl.h b/deps/v8/src/store-buffer-inl.h new file mode 100644 index 0000000000..dd65cbcc9c --- /dev/null +++ b/deps/v8/src/store-buffer-inl.h @@ -0,0 +1,79 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_STORE_BUFFER_INL_H_ +#define V8_STORE_BUFFER_INL_H_ + +#include "store-buffer.h" + +namespace v8 { +namespace internal { + +Address StoreBuffer::TopAddress() { + return reinterpret_cast<Address>(heap_->store_buffer_top_address()); +} + + +void StoreBuffer::Mark(Address addr) { + ASSERT(!heap_->cell_space()->Contains(addr)); + ASSERT(!heap_->code_space()->Contains(addr)); + Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top()); + *top++ = addr; + heap_->public_set_store_buffer_top(top); + if ((reinterpret_cast<uintptr_t>(top) & kStoreBufferOverflowBit) != 0) { + ASSERT(top == limit_); + Compact(); + } else { + ASSERT(top < limit_); + } +} + + +void StoreBuffer::EnterDirectlyIntoStoreBuffer(Address addr) { + if (store_buffer_rebuilding_enabled_) { + SLOW_ASSERT(!heap_->cell_space()->Contains(addr) && + !heap_->code_space()->Contains(addr) && + !heap_->old_data_space()->Contains(addr) && + !heap_->new_space()->Contains(addr)); + Address* top = old_top_; + *top++ = addr; + old_top_ = top; + old_buffer_is_sorted_ = false; + old_buffer_is_filtered_ = false; + if (top >= old_limit_) { + ASSERT(callback_ != NULL); + (*callback_)(heap_, + MemoryChunk::FromAnyPointerAddress(addr), + kStoreBufferFullEvent); + } + } +} + + +} } // namespace v8::internal + +#endif // V8_STORE_BUFFER_INL_H_ diff --git a/deps/v8/src/store-buffer.cc b/deps/v8/src/store-buffer.cc new file mode 100644 index 0000000000..9022b3be83 --- /dev/null +++ b/deps/v8/src/store-buffer.cc @@ -0,0 +1,719 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "store-buffer.h" +#include "store-buffer-inl.h" +#include "v8-counters.h" + +namespace v8 { +namespace internal { + +StoreBuffer::StoreBuffer(Heap* heap) + : heap_(heap), + start_(NULL), + limit_(NULL), + old_start_(NULL), + old_limit_(NULL), + old_top_(NULL), + old_reserved_limit_(NULL), + old_buffer_is_sorted_(false), + old_buffer_is_filtered_(false), + during_gc_(false), + store_buffer_rebuilding_enabled_(false), + callback_(NULL), + may_move_store_buffer_entries_(true), + virtual_memory_(NULL), + hash_set_1_(NULL), + hash_set_2_(NULL), + hash_sets_are_empty_(true) { +} + + +void StoreBuffer::SetUp() { + virtual_memory_ = new VirtualMemory(kStoreBufferSize * 3); + uintptr_t start_as_int = + reinterpret_cast<uintptr_t>(virtual_memory_->address()); + start_ = + reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize * 2)); + limit_ = start_ + (kStoreBufferSize / kPointerSize); + + old_virtual_memory_ = + new VirtualMemory(kOldStoreBufferLength * kPointerSize); + old_top_ = old_start_ = + reinterpret_cast<Address*>(old_virtual_memory_->address()); + // Don't know the alignment requirements of the OS, but it is certainly not + // less than 0xfff. + ASSERT((reinterpret_cast<uintptr_t>(old_start_) & 0xfff) == 0); + int initial_length = static_cast<int>(OS::CommitPageSize() / kPointerSize); + ASSERT(initial_length > 0); + ASSERT(initial_length <= kOldStoreBufferLength); + old_limit_ = old_start_ + initial_length; + old_reserved_limit_ = old_start_ + kOldStoreBufferLength; + + CHECK(old_virtual_memory_->Commit( + reinterpret_cast<void*>(old_start_), + (old_limit_ - old_start_) * kPointerSize, + false)); + + ASSERT(reinterpret_cast<Address>(start_) >= virtual_memory_->address()); + ASSERT(reinterpret_cast<Address>(limit_) >= virtual_memory_->address()); + Address* vm_limit = reinterpret_cast<Address*>( + reinterpret_cast<char*>(virtual_memory_->address()) + + virtual_memory_->size()); + ASSERT(start_ <= vm_limit); + ASSERT(limit_ <= vm_limit); + USE(vm_limit); + ASSERT((reinterpret_cast<uintptr_t>(limit_) & kStoreBufferOverflowBit) != 0); + ASSERT((reinterpret_cast<uintptr_t>(limit_ - 1) & kStoreBufferOverflowBit) == + 0); + + CHECK(virtual_memory_->Commit(reinterpret_cast<Address>(start_), + kStoreBufferSize, + false)); // Not executable. + heap_->public_set_store_buffer_top(start_); + + hash_set_1_ = new uintptr_t[kHashSetLength]; + hash_set_2_ = new uintptr_t[kHashSetLength]; + hash_sets_are_empty_ = false; + + ClearFilteringHashSets(); +} + + +void StoreBuffer::TearDown() { + delete virtual_memory_; + delete old_virtual_memory_; + delete[] hash_set_1_; + delete[] hash_set_2_; + old_start_ = old_top_ = old_limit_ = old_reserved_limit_ = NULL; + start_ = limit_ = NULL; + heap_->public_set_store_buffer_top(start_); +} + + +void StoreBuffer::StoreBufferOverflow(Isolate* isolate) { + isolate->heap()->store_buffer()->Compact(); +} + + +#if V8_TARGET_ARCH_X64 +static int CompareAddresses(const void* void_a, const void* void_b) { + intptr_t a = + reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_a)); + intptr_t b = + reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_b)); + // Unfortunately if int is smaller than intptr_t there is no branch-free + // way to return a number with the same sign as the difference between the + // pointers. + if (a == b) return 0; + if (a < b) return -1; + ASSERT(a > b); + return 1; +} +#else +static int CompareAddresses(const void* void_a, const void* void_b) { + intptr_t a = + reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_a)); + intptr_t b = + reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_b)); + ASSERT(sizeof(1) == sizeof(a)); + // Shift down to avoid wraparound. + return (a >> kPointerSizeLog2) - (b >> kPointerSizeLog2); +} +#endif + + +void StoreBuffer::Uniq() { + // Remove adjacent duplicates and cells that do not point at new space. + Address previous = NULL; + Address* write = old_start_; + ASSERT(may_move_store_buffer_entries_); + for (Address* read = old_start_; read < old_top_; read++) { + Address current = *read; + if (current != previous) { + if (heap_->InNewSpace(*reinterpret_cast<Object**>(current))) { + *write++ = current; + } + } + previous = current; + } + old_top_ = write; +} + + +void StoreBuffer::EnsureSpace(intptr_t space_needed) { + while (old_limit_ - old_top_ < space_needed && + old_limit_ < old_reserved_limit_) { + size_t grow = old_limit_ - old_start_; // Double size. + CHECK(old_virtual_memory_->Commit(reinterpret_cast<void*>(old_limit_), + grow * kPointerSize, + false)); + old_limit_ += grow; + } + + if (old_limit_ - old_top_ >= space_needed) return; + + if (old_buffer_is_filtered_) return; + ASSERT(may_move_store_buffer_entries_); + Compact(); + + old_buffer_is_filtered_ = true; + bool page_has_scan_on_scavenge_flag = false; + + PointerChunkIterator it(heap_); + MemoryChunk* chunk; + while ((chunk = it.next()) != NULL) { + if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true; + } + + if (page_has_scan_on_scavenge_flag) { + Filter(MemoryChunk::SCAN_ON_SCAVENGE); + } + + // If filtering out the entries from scan_on_scavenge pages got us down to + // less than half full, then we are satisfied with that. + if (old_limit_ - old_top_ > old_top_ - old_start_) return; + + // Sample 1 entry in 97 and filter out the pages where we estimate that more + // than 1 in 8 pointers are to new space. + static const int kSampleFinenesses = 5; + static const struct Samples { + int prime_sample_step; + int threshold; + } samples[kSampleFinenesses] = { + { 97, ((Page::kPageSize / kPointerSize) / 97) / 8 }, + { 23, ((Page::kPageSize / kPointerSize) / 23) / 16 }, + { 7, ((Page::kPageSize / kPointerSize) / 7) / 32 }, + { 3, ((Page::kPageSize / kPointerSize) / 3) / 256 }, + { 1, 0} + }; + for (int i = kSampleFinenesses - 1; i >= 0; i--) { + ExemptPopularPages(samples[i].prime_sample_step, samples[i].threshold); + // As a last resort we mark all pages as being exempt from the store buffer. + ASSERT(i != 0 || old_top_ == old_start_); + if (old_limit_ - old_top_ > old_top_ - old_start_) return; + } + UNREACHABLE(); +} + + +// Sample the store buffer to see if some pages are taking up a lot of space +// in the store buffer. +void StoreBuffer::ExemptPopularPages(int prime_sample_step, int threshold) { + PointerChunkIterator it(heap_); + MemoryChunk* chunk; + while ((chunk = it.next()) != NULL) { + chunk->set_store_buffer_counter(0); + } + bool created_new_scan_on_scavenge_pages = false; + MemoryChunk* previous_chunk = NULL; + for (Address* p = old_start_; p < old_top_; p += prime_sample_step) { + Address addr = *p; + MemoryChunk* containing_chunk = NULL; + if (previous_chunk != NULL && previous_chunk->Contains(addr)) { + containing_chunk = previous_chunk; + } else { + containing_chunk = MemoryChunk::FromAnyPointerAddress(addr); + } + int old_counter = containing_chunk->store_buffer_counter(); + if (old_counter == threshold) { + containing_chunk->set_scan_on_scavenge(true); + created_new_scan_on_scavenge_pages = true; + } + containing_chunk->set_store_buffer_counter(old_counter + 1); + previous_chunk = containing_chunk; + } + if (created_new_scan_on_scavenge_pages) { + Filter(MemoryChunk::SCAN_ON_SCAVENGE); + } + old_buffer_is_filtered_ = true; +} + + +void StoreBuffer::Filter(int flag) { + Address* new_top = old_start_; + MemoryChunk* previous_chunk = NULL; + for (Address* p = old_start_; p < old_top_; p++) { + Address addr = *p; + MemoryChunk* containing_chunk = NULL; + if (previous_chunk != NULL && previous_chunk->Contains(addr)) { + containing_chunk = previous_chunk; + } else { + containing_chunk = MemoryChunk::FromAnyPointerAddress(addr); + previous_chunk = containing_chunk; + } + if (!containing_chunk->IsFlagSet(flag)) { + *new_top++ = addr; + } + } + old_top_ = new_top; + + // Filtering hash sets are inconsistent with the store buffer after this + // operation. + ClearFilteringHashSets(); +} + + +void StoreBuffer::SortUniq() { + Compact(); + if (old_buffer_is_sorted_) return; + qsort(reinterpret_cast<void*>(old_start_), + old_top_ - old_start_, + sizeof(*old_top_), + &CompareAddresses); + Uniq(); + + old_buffer_is_sorted_ = true; + + // Filtering hash sets are inconsistent with the store buffer after this + // operation. + ClearFilteringHashSets(); +} + + +bool StoreBuffer::PrepareForIteration() { + Compact(); + PointerChunkIterator it(heap_); + MemoryChunk* chunk; + bool page_has_scan_on_scavenge_flag = false; + while ((chunk = it.next()) != NULL) { + if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true; + } + + if (page_has_scan_on_scavenge_flag) { + Filter(MemoryChunk::SCAN_ON_SCAVENGE); + } + + // Filtering hash sets are inconsistent with the store buffer after + // iteration. + ClearFilteringHashSets(); + + return page_has_scan_on_scavenge_flag; +} + + +#ifdef DEBUG +void StoreBuffer::Clean() { + ClearFilteringHashSets(); + Uniq(); // Also removes things that no longer point to new space. + CheckForFullBuffer(); +} + + +static Address* in_store_buffer_1_element_cache = NULL; + + +bool StoreBuffer::CellIsInStoreBuffer(Address cell_address) { + if (!FLAG_enable_slow_asserts) return true; + if (in_store_buffer_1_element_cache != NULL && + *in_store_buffer_1_element_cache == cell_address) { + return true; + } + Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top()); + for (Address* current = top - 1; current >= start_; current--) { + if (*current == cell_address) { + in_store_buffer_1_element_cache = current; + return true; + } + } + for (Address* current = old_top_ - 1; current >= old_start_; current--) { + if (*current == cell_address) { + in_store_buffer_1_element_cache = current; + return true; + } + } + return false; +} +#endif + + +void StoreBuffer::ClearFilteringHashSets() { + if (!hash_sets_are_empty_) { + memset(reinterpret_cast<void*>(hash_set_1_), + 0, + sizeof(uintptr_t) * kHashSetLength); + memset(reinterpret_cast<void*>(hash_set_2_), + 0, + sizeof(uintptr_t) * kHashSetLength); + hash_sets_are_empty_ = true; + } +} + + +void StoreBuffer::GCPrologue() { + ClearFilteringHashSets(); + during_gc_ = true; +} + + +#ifdef DEBUG +static void DummyScavengePointer(HeapObject** p, HeapObject* o) { + // Do nothing. +} + + +void StoreBuffer::VerifyPointers(PagedSpace* space, + RegionCallback region_callback) { + PageIterator it(space); + + while (it.has_next()) { + Page* page = it.next(); + FindPointersToNewSpaceOnPage( + reinterpret_cast<PagedSpace*>(page->owner()), + page, + region_callback, + &DummyScavengePointer); + } +} + + +void StoreBuffer::VerifyPointers(LargeObjectSpace* space) { + LargeObjectIterator it(space); + for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { + if (object->IsFixedArray()) { + Address slot_address = object->address(); + Address end = object->address() + object->Size(); + + while (slot_address < end) { + HeapObject** slot = reinterpret_cast<HeapObject**>(slot_address); + // When we are not in GC the Heap::InNewSpace() predicate + // checks that pointers which satisfy predicate point into + // the active semispace. + heap_->InNewSpace(*slot); + slot_address += kPointerSize; + } + } + } +} +#endif + + +void StoreBuffer::Verify() { +#ifdef DEBUG + VerifyPointers(heap_->old_pointer_space(), + &StoreBuffer::FindPointersToNewSpaceInRegion); + VerifyPointers(heap_->map_space(), + &StoreBuffer::FindPointersToNewSpaceInMapsRegion); + VerifyPointers(heap_->lo_space()); +#endif +} + + +void StoreBuffer::GCEpilogue() { + during_gc_ = false; + if (FLAG_verify_heap) { + Verify(); + } +} + + +void StoreBuffer::FindPointersToNewSpaceInRegion( + Address start, Address end, ObjectSlotCallback slot_callback) { + for (Address slot_address = start; + slot_address < end; + slot_address += kPointerSize) { + Object** slot = reinterpret_cast<Object**>(slot_address); + if (heap_->InNewSpace(*slot)) { + HeapObject* object = reinterpret_cast<HeapObject*>(*slot); + ASSERT(object->IsHeapObject()); + slot_callback(reinterpret_cast<HeapObject**>(slot), object); + if (heap_->InNewSpace(*slot)) { + EnterDirectlyIntoStoreBuffer(slot_address); + } + } + } +} + + +// Compute start address of the first map following given addr. +static inline Address MapStartAlign(Address addr) { + Address page = Page::FromAddress(addr)->ObjectAreaStart(); + return page + (((addr - page) + (Map::kSize - 1)) / Map::kSize * Map::kSize); +} + + +// Compute end address of the first map preceding given addr. +static inline Address MapEndAlign(Address addr) { + Address page = Page::FromAllocationTop(addr)->ObjectAreaStart(); + return page + ((addr - page) / Map::kSize * Map::kSize); +} + + +void StoreBuffer::FindPointersToNewSpaceInMaps( + Address start, + Address end, + ObjectSlotCallback slot_callback) { + ASSERT(MapStartAlign(start) == start); + ASSERT(MapEndAlign(end) == end); + + Address map_address = start; + while (map_address < end) { + ASSERT(!heap_->InNewSpace(Memory::Object_at(map_address))); + ASSERT(Memory::Object_at(map_address)->IsMap()); + + Address pointer_fields_start = map_address + Map::kPointerFieldsBeginOffset; + Address pointer_fields_end = map_address + Map::kPointerFieldsEndOffset; + + FindPointersToNewSpaceInRegion(pointer_fields_start, + pointer_fields_end, + slot_callback); + map_address += Map::kSize; + } +} + + +void StoreBuffer::FindPointersToNewSpaceInMapsRegion( + Address start, + Address end, + ObjectSlotCallback slot_callback) { + Address map_aligned_start = MapStartAlign(start); + Address map_aligned_end = MapEndAlign(end); + + ASSERT(map_aligned_start == start); + ASSERT(map_aligned_end == end); + + FindPointersToNewSpaceInMaps(map_aligned_start, + map_aligned_end, + slot_callback); +} + + +// This function iterates over all the pointers in a paged space in the heap, +// looking for pointers into new space. Within the pages there may be dead +// objects that have not been overwritten by free spaces or fillers because of +// lazy sweeping. These dead objects may not contain pointers to new space. +// The garbage areas that have been swept properly (these will normally be the +// large ones) will be marked with free space and filler map words. In +// addition any area that has never been used at all for object allocation must +// be marked with a free space or filler. Because the free space and filler +// maps do not move we can always recognize these even after a compaction. +// Normal objects like FixedArrays and JSObjects should not contain references +// to these maps. The special garbage section (see comment in spaces.h) is +// skipped since it can contain absolutely anything. Any objects that are +// allocated during iteration may or may not be visited by the iteration, but +// they will not be partially visited. +void StoreBuffer::FindPointersToNewSpaceOnPage( + PagedSpace* space, + Page* page, + RegionCallback region_callback, + ObjectSlotCallback slot_callback) { + Address visitable_start = page->ObjectAreaStart(); + Address end_of_page = page->ObjectAreaEnd(); + + Address visitable_end = visitable_start; + + Object* free_space_map = heap_->free_space_map(); + Object* two_pointer_filler_map = heap_->two_pointer_filler_map(); + + while (visitable_end < end_of_page) { + Object* o = *reinterpret_cast<Object**>(visitable_end); + // Skip fillers but not things that look like fillers in the special + // garbage section which can contain anything. + if (o == free_space_map || + o == two_pointer_filler_map || + (visitable_end == space->top() && visitable_end != space->limit())) { + if (visitable_start != visitable_end) { + // After calling this the special garbage section may have moved. + (this->*region_callback)(visitable_start, + visitable_end, + slot_callback); + if (visitable_end >= space->top() && visitable_end < space->limit()) { + visitable_end = space->limit(); + visitable_start = visitable_end; + continue; + } + } + if (visitable_end == space->top() && visitable_end != space->limit()) { + visitable_start = visitable_end = space->limit(); + } else { + // At this point we are either at the start of a filler or we are at + // the point where the space->top() used to be before the + // visit_pointer_region call above. Either way we can skip the + // object at the current spot: We don't promise to visit objects + // allocated during heap traversal, and if space->top() moved then it + // must be because an object was allocated at this point. + visitable_start = + visitable_end + HeapObject::FromAddress(visitable_end)->Size(); + visitable_end = visitable_start; + } + } else { + ASSERT(o != free_space_map); + ASSERT(o != two_pointer_filler_map); + ASSERT(visitable_end < space->top() || visitable_end >= space->limit()); + visitable_end += kPointerSize; + } + } + ASSERT(visitable_end == end_of_page); + if (visitable_start != visitable_end) { + (this->*region_callback)(visitable_start, + visitable_end, + slot_callback); + } +} + + +void StoreBuffer::IteratePointersInStoreBuffer( + ObjectSlotCallback slot_callback) { + Address* limit = old_top_; + old_top_ = old_start_; + { + DontMoveStoreBufferEntriesScope scope(this); + for (Address* current = old_start_; current < limit; current++) { +#ifdef DEBUG + Address* saved_top = old_top_; +#endif + Object** slot = reinterpret_cast<Object**>(*current); + Object* object = *slot; + if (heap_->InFromSpace(object)) { + HeapObject* heap_object = reinterpret_cast<HeapObject*>(object); + slot_callback(reinterpret_cast<HeapObject**>(slot), heap_object); + if (heap_->InNewSpace(*slot)) { + EnterDirectlyIntoStoreBuffer(reinterpret_cast<Address>(slot)); + } + } + ASSERT(old_top_ == saved_top + 1 || old_top_ == saved_top); + } + } +} + + +void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback slot_callback) { + // We do not sort or remove duplicated entries from the store buffer because + // we expect that callback will rebuild the store buffer thus removing + // all duplicates and pointers to old space. + bool some_pages_to_scan = PrepareForIteration(); + + // TODO(gc): we want to skip slots on evacuation candidates + // but we can't simply figure that out from slot address + // because slot can belong to a large object. + IteratePointersInStoreBuffer(slot_callback); + + // We are done scanning all the pointers that were in the store buffer, but + // there may be some pages marked scan_on_scavenge that have pointers to new + // space that are not in the store buffer. We must scan them now. As we + // scan, the surviving pointers to new space will be added to the store + // buffer. If there are still a lot of pointers to new space then we will + // keep the scan_on_scavenge flag on the page and discard the pointers that + // were added to the store buffer. If there are not many pointers to new + // space left on the page we will keep the pointers in the store buffer and + // remove the flag from the page. + if (some_pages_to_scan) { + if (callback_ != NULL) { + (*callback_)(heap_, NULL, kStoreBufferStartScanningPagesEvent); + } + PointerChunkIterator it(heap_); + MemoryChunk* chunk; + while ((chunk = it.next()) != NULL) { + if (chunk->scan_on_scavenge()) { + chunk->set_scan_on_scavenge(false); + if (callback_ != NULL) { + (*callback_)(heap_, chunk, kStoreBufferScanningPageEvent); + } + if (chunk->owner() == heap_->lo_space()) { + LargePage* large_page = reinterpret_cast<LargePage*>(chunk); + HeapObject* array = large_page->GetObject(); + ASSERT(array->IsFixedArray()); + Address start = array->address(); + Address end = start + array->Size(); + FindPointersToNewSpaceInRegion(start, end, slot_callback); + } else { + Page* page = reinterpret_cast<Page*>(chunk); + PagedSpace* owner = reinterpret_cast<PagedSpace*>(page->owner()); + FindPointersToNewSpaceOnPage( + owner, + page, + (owner == heap_->map_space() ? + &StoreBuffer::FindPointersToNewSpaceInMapsRegion : + &StoreBuffer::FindPointersToNewSpaceInRegion), + slot_callback); + } + } + } + if (callback_ != NULL) { + (*callback_)(heap_, NULL, kStoreBufferScanningPageEvent); + } + } +} + + +void StoreBuffer::Compact() { + Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top()); + + if (top == start_) return; + + // There's no check of the limit in the loop below so we check here for + // the worst case (compaction doesn't eliminate any pointers). + ASSERT(top <= limit_); + heap_->public_set_store_buffer_top(start_); + EnsureSpace(top - start_); + ASSERT(may_move_store_buffer_entries_); + // Goes through the addresses in the store buffer attempting to remove + // duplicates. In the interest of speed this is a lossy operation. Some + // duplicates will remain. We have two hash sets with different hash + // functions to reduce the number of unnecessary clashes. + hash_sets_are_empty_ = false; // Hash sets are in use. + for (Address* current = start_; current < top; current++) { + ASSERT(!heap_->cell_space()->Contains(*current)); + ASSERT(!heap_->code_space()->Contains(*current)); + ASSERT(!heap_->old_data_space()->Contains(*current)); + uintptr_t int_addr = reinterpret_cast<uintptr_t>(*current); + // Shift out the last bits including any tags. + int_addr >>= kPointerSizeLog2; + int hash1 = + ((int_addr ^ (int_addr >> kHashSetLengthLog2)) & (kHashSetLength - 1)); + if (hash_set_1_[hash1] == int_addr) continue; + uintptr_t hash2 = (int_addr - (int_addr >> kHashSetLengthLog2)); + hash2 ^= hash2 >> (kHashSetLengthLog2 * 2); + hash2 &= (kHashSetLength - 1); + if (hash_set_2_[hash2] == int_addr) continue; + if (hash_set_1_[hash1] == 0) { + hash_set_1_[hash1] = int_addr; + } else if (hash_set_2_[hash2] == 0) { + hash_set_2_[hash2] = int_addr; + } else { + // Rather than slowing down we just throw away some entries. This will + // cause some duplicates to remain undetected. + hash_set_1_[hash1] = int_addr; + hash_set_2_[hash2] = 0; + } + old_buffer_is_sorted_ = false; + old_buffer_is_filtered_ = false; + *old_top_++ = reinterpret_cast<Address>(int_addr << kPointerSizeLog2); + ASSERT(old_top_ <= old_limit_); + } + heap_->isolate()->counters()->store_buffer_compactions()->Increment(); + CheckForFullBuffer(); +} + + +void StoreBuffer::CheckForFullBuffer() { + EnsureSpace(kStoreBufferSize * 2); +} + +} } // namespace v8::internal diff --git a/deps/v8/src/store-buffer.h b/deps/v8/src/store-buffer.h new file mode 100644 index 0000000000..951a9ca2bc --- /dev/null +++ b/deps/v8/src/store-buffer.h @@ -0,0 +1,255 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_STORE_BUFFER_H_ +#define V8_STORE_BUFFER_H_ + +#include "allocation.h" +#include "checks.h" +#include "globals.h" +#include "platform.h" +#include "v8globals.h" + +namespace v8 { +namespace internal { + +class StoreBuffer; + +typedef void (*ObjectSlotCallback)(HeapObject** from, HeapObject* to); + +typedef void (StoreBuffer::*RegionCallback)( + Address start, Address end, ObjectSlotCallback slot_callback); + +// Used to implement the write barrier by collecting addresses of pointers +// between spaces. +class StoreBuffer { + public: + explicit StoreBuffer(Heap* heap); + + static void StoreBufferOverflow(Isolate* isolate); + + inline Address TopAddress(); + + void SetUp(); + void TearDown(); + + // This is used by the mutator to enter addresses into the store buffer. + inline void Mark(Address addr); + + // This is used by the heap traversal to enter the addresses into the store + // buffer that should still be in the store buffer after GC. It enters + // addresses directly into the old buffer because the GC starts by wiping the + // old buffer and thereafter only visits each cell once so there is no need + // to attempt to remove any dupes. During the first part of a GC we + // are using the store buffer to access the old spaces and at the same time + // we are rebuilding the store buffer using this function. There is, however + // no issue of overwriting the buffer we are iterating over, because this + // stage of the scavenge can only reduce the number of addresses in the store + // buffer (some objects are promoted so pointers to them do not need to be in + // the store buffer). The later parts of the GC scan the pages that are + // exempt from the store buffer and process the promotion queue. These steps + // can overflow this buffer. We check for this and on overflow we call the + // callback set up with the StoreBufferRebuildScope object. + inline void EnterDirectlyIntoStoreBuffer(Address addr); + + // Iterates over all pointers that go from old space to new space. It will + // delete the store buffer as it starts so the callback should reenter + // surviving old-to-new pointers into the store buffer to rebuild it. + void IteratePointersToNewSpace(ObjectSlotCallback callback); + + static const int kStoreBufferOverflowBit = 1 << (14 + kPointerSizeLog2); + static const int kStoreBufferSize = kStoreBufferOverflowBit; + static const int kStoreBufferLength = kStoreBufferSize / sizeof(Address); + static const int kOldStoreBufferLength = kStoreBufferLength * 16; + static const int kHashSetLengthLog2 = 12; + static const int kHashSetLength = 1 << kHashSetLengthLog2; + + void Compact(); + + void GCPrologue(); + void GCEpilogue(); + + Object*** Limit() { return reinterpret_cast<Object***>(old_limit_); } + Object*** Start() { return reinterpret_cast<Object***>(old_start_); } + Object*** Top() { return reinterpret_cast<Object***>(old_top_); } + void SetTop(Object*** top) { + ASSERT(top >= Start()); + ASSERT(top <= Limit()); + old_top_ = reinterpret_cast<Address*>(top); + } + + bool old_buffer_is_sorted() { return old_buffer_is_sorted_; } + bool old_buffer_is_filtered() { return old_buffer_is_filtered_; } + + // Goes through the store buffer removing pointers to things that have + // been promoted. Rebuilds the store buffer completely if it overflowed. + void SortUniq(); + + void EnsureSpace(intptr_t space_needed); + void Verify(); + + bool PrepareForIteration(); + +#ifdef DEBUG + void Clean(); + // Slow, for asserts only. + bool CellIsInStoreBuffer(Address cell); +#endif + + void Filter(int flag); + + private: + Heap* heap_; + + // The store buffer is divided up into a new buffer that is constantly being + // filled by mutator activity and an old buffer that is filled with the data + // from the new buffer after compression. + Address* start_; + Address* limit_; + + Address* old_start_; + Address* old_limit_; + Address* old_top_; + Address* old_reserved_limit_; + VirtualMemory* old_virtual_memory_; + + bool old_buffer_is_sorted_; + bool old_buffer_is_filtered_; + bool during_gc_; + // The garbage collector iterates over many pointers to new space that are not + // handled by the store buffer. This flag indicates whether the pointers + // found by the callbacks should be added to the store buffer or not. + bool store_buffer_rebuilding_enabled_; + StoreBufferCallback callback_; + bool may_move_store_buffer_entries_; + + VirtualMemory* virtual_memory_; + + // Two hash sets used for filtering. + // If address is in the hash set then it is guaranteed to be in the + // old part of the store buffer. + uintptr_t* hash_set_1_; + uintptr_t* hash_set_2_; + bool hash_sets_are_empty_; + + void ClearFilteringHashSets(); + + void CheckForFullBuffer(); + void Uniq(); + void ExemptPopularPages(int prime_sample_step, int threshold); + + void FindPointersToNewSpaceInRegion(Address start, + Address end, + ObjectSlotCallback slot_callback); + + // For each region of pointers on a page in use from an old space call + // visit_pointer_region callback. + // If either visit_pointer_region or callback can cause an allocation + // in old space and changes in allocation watermark then + // can_preallocate_during_iteration should be set to true. + void IteratePointersOnPage( + PagedSpace* space, + Page* page, + RegionCallback region_callback, + ObjectSlotCallback slot_callback); + + void FindPointersToNewSpaceInMaps( + Address start, + Address end, + ObjectSlotCallback slot_callback); + + void FindPointersToNewSpaceInMapsRegion( + Address start, + Address end, + ObjectSlotCallback slot_callback); + + void FindPointersToNewSpaceOnPage( + PagedSpace* space, + Page* page, + RegionCallback region_callback, + ObjectSlotCallback slot_callback); + + void IteratePointersInStoreBuffer(ObjectSlotCallback slot_callback); + +#ifdef DEBUG + void VerifyPointers(PagedSpace* space, RegionCallback region_callback); + void VerifyPointers(LargeObjectSpace* space); +#endif + + friend class StoreBufferRebuildScope; + friend class DontMoveStoreBufferEntriesScope; +}; + + +class StoreBufferRebuildScope { + public: + explicit StoreBufferRebuildScope(Heap* heap, + StoreBuffer* store_buffer, + StoreBufferCallback callback) + : heap_(heap), + store_buffer_(store_buffer), + stored_state_(store_buffer->store_buffer_rebuilding_enabled_), + stored_callback_(store_buffer->callback_) { + store_buffer_->store_buffer_rebuilding_enabled_ = true; + store_buffer_->callback_ = callback; + (*callback)(heap, NULL, kStoreBufferStartScanningPagesEvent); + } + + ~StoreBufferRebuildScope() { + store_buffer_->callback_ = stored_callback_; + store_buffer_->store_buffer_rebuilding_enabled_ = stored_state_; + store_buffer_->CheckForFullBuffer(); + } + + private: + Heap* heap_; + StoreBuffer* store_buffer_; + bool stored_state_; + StoreBufferCallback stored_callback_; +}; + + +class DontMoveStoreBufferEntriesScope { + public: + explicit DontMoveStoreBufferEntriesScope(StoreBuffer* store_buffer) + : store_buffer_(store_buffer), + stored_state_(store_buffer->may_move_store_buffer_entries_) { + store_buffer_->may_move_store_buffer_entries_ = false; + } + + ~DontMoveStoreBufferEntriesScope() { + store_buffer_->may_move_store_buffer_entries_ = stored_state_; + } + + private: + StoreBuffer* store_buffer_; + bool stored_state_; +}; + +} } // namespace v8::internal + +#endif // V8_STORE_BUFFER_H_ diff --git a/deps/v8/src/string-search.h b/deps/v8/src/string-search.h index 1223db0f98..8c3456aa0a 100644 --- a/deps/v8/src/string-search.h +++ b/deps/v8/src/string-search.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -242,9 +242,9 @@ int StringSearch<PatternChar, SubjectChar>::SingleCharSearch( template <typename PatternChar, typename SubjectChar> -static inline bool CharCompare(const PatternChar* pattern, - const SubjectChar* subject, - int length) { +inline bool CharCompare(const PatternChar* pattern, + const SubjectChar* subject, + int length) { ASSERT(length > 0); int pos = 0; do { @@ -369,6 +369,10 @@ void StringSearch<PatternChar, SubjectChar>::PopulateBoyerMooreTable() { shift_table[pattern_length] = 1; suffix_table[pattern_length] = pattern_length + 1; + if (pattern_length <= start) { + return; + } + // Find suffixes. PatternChar last_char = pattern[pattern_length - 1]; int suffix = pattern_length + 1; @@ -555,10 +559,10 @@ int StringSearch<PatternChar, SubjectChar>::InitialSearch( // object should be constructed once and the Search function then called // for each search. template <typename SubjectChar, typename PatternChar> -static int SearchString(Isolate* isolate, - Vector<const SubjectChar> subject, - Vector<const PatternChar> pattern, - int start_index) { +int SearchString(Isolate* isolate, + Vector<const SubjectChar> subject, + Vector<const PatternChar> pattern, + int start_index) { StringSearch<PatternChar, SubjectChar> search(isolate, pattern); return search.Search(subject, start_index); } diff --git a/deps/v8/src/string-stream.cc b/deps/v8/src/string-stream.cc index 8086cf9515..35f7be5416 100644 --- a/deps/v8/src/string-stream.cc +++ b/deps/v8/src/string-stream.cc @@ -350,29 +350,24 @@ void StringStream::PrintUsingMap(JSObject* js_object) { } DescriptorArray* descs = map->instance_descriptors(); for (int i = 0; i < descs->number_of_descriptors(); i++) { - switch (descs->GetType(i)) { - case FIELD: { - Object* key = descs->GetKey(i); - if (key->IsString() || key->IsNumber()) { - int len = 3; - if (key->IsString()) { - len = String::cast(key)->length(); - } - for (; len < 18; len++) - Put(' '); - if (key->IsString()) { - Put(String::cast(key)); - } else { - key->ShortPrint(); - } - Add(": "); - Object* value = js_object->FastPropertyAt(descs->GetFieldIndex(i)); - Add("%o\n", value); + if (descs->GetType(i) == FIELD) { + Object* key = descs->GetKey(i); + if (key->IsString() || key->IsNumber()) { + int len = 3; + if (key->IsString()) { + len = String::cast(key)->length(); } + for (; len < 18; len++) + Put(' '); + if (key->IsString()) { + Put(String::cast(key)); + } else { + key->ShortPrint(); + } + Add(": "); + Object* value = js_object->FastPropertyAt(descs->GetFieldIndex(i)); + Add("%o\n", value); } - break; - default: - break; } } } diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js index 297105d047..2d6896120e 100644 --- a/deps/v8/src/string.js +++ b/deps/v8/src/string.js @@ -46,16 +46,18 @@ // ECMA-262 section 15.5.4.2 function StringToString() { - if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) + if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { throw new $TypeError('String.prototype.toString is not generic'); + } return %_ValueOf(this); } // ECMA-262 section 15.5.4.3 function StringValueOf() { - if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) + if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { throw new $TypeError('String.prototype.valueOf is not generic'); + } return %_ValueOf(this); } @@ -91,7 +93,8 @@ function StringCharCodeAt(pos) { // ECMA-262, section 15.5.4.6 function StringConcat() { if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { - throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]); + throw MakeTypeError("called_on_null_or_undefined", + ["String.prototype.concat"]); } var len = %_ArgumentsLength(); var this_as_string = TO_STRING_INLINE(this); @@ -241,6 +244,15 @@ function StringReplace(search, replace) { // Convert the search argument to a string and search for it. search = TO_STRING_INLINE(search); + if (search.length == 1 && + subject.length > 0xFF && + IS_STRING(replace) && + %StringIndexOf(replace, '$', 0) < 0) { + // Searching by traversing a cons string tree and replace with cons of + // slices works only when the replaced string is a single character, being + // replaced by a simple string and only pays off for long strings. + return %StringReplaceOneCharWithString(subject, search, replace); + } var start = %StringIndexOf(subject, search, 0); if (start < 0) return subject; var end = start + search.length; @@ -358,7 +370,7 @@ function ExpandReplacement(string, subject, matchInfo, builder) { builder_elements.push(SubString(string, position, next)); } } -}; +} // Compute the string of a given regular expression capture. @@ -371,7 +383,7 @@ function CaptureString(string, lastCaptureInfo, index) { if (start < 0) return; var end = lastCaptureInfo[CAPTURE(scaled + 1)]; return SubString(string, start, end); -}; +} // Add the string of a given regular expression capture to the @@ -384,7 +396,7 @@ function addCaptureString(builder, matchInfo, index) { if (start < 0) return; var end = matchInfo[CAPTURE(scaled + 1)]; builder.addSpecialSlice(start, end); -}; +} // TODO(lrn): This array will survive indefinitely if replace is never // called again. However, it will be empty, since the contents are cleared @@ -531,30 +543,36 @@ function StringSlice(start, end) { var s_len = s.length; var start_i = TO_INTEGER(start); var end_i = s_len; - if (end !== void 0) + if (end !== void 0) { end_i = TO_INTEGER(end); + } if (start_i < 0) { start_i += s_len; - if (start_i < 0) + if (start_i < 0) { start_i = 0; + } } else { - if (start_i > s_len) + if (start_i > s_len) { start_i = s_len; + } } if (end_i < 0) { end_i += s_len; - if (end_i < 0) + if (end_i < 0) { end_i = 0; + } } else { - if (end_i > s_len) + if (end_i > s_len) { end_i = s_len; + } } var num_c = end_i - start_i; - if (num_c < 0) + if (num_c < 0) { num_c = 0; + } return SubString(s, start_i, start_i + num_c); } @@ -568,7 +586,6 @@ function StringSplit(separator, limit) { } var subject = TO_STRING_INLINE(this); limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit); - if (limit === 0) return []; // ECMA-262 says that if separator is undefined, the result should // be an array of size 1 containing the entire string. SpiderMonkey @@ -582,6 +599,9 @@ function StringSplit(separator, limit) { var length = subject.length; if (!IS_REGEXP(separator)) { separator = TO_STRING_INLINE(separator); + + if (limit === 0) return []; + var separator_length = separator.length; // If the separator string is empty then return the elements in the subject. @@ -592,6 +612,8 @@ function StringSplit(separator, limit) { return result; } + if (limit === 0) return []; + %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); if (length === 0) { @@ -688,7 +710,7 @@ function StringSubstring(start, end) { } } - return (start_i + 1 == end_i + return ((start_i + 1 == end_i) ? %_StringCharAt(s, start_i) : %_SubString(s, start_i, end_i)); } @@ -732,7 +754,7 @@ function StringSubstr(start, n) { var end = start + len; if (end > s.length) end = s.length; - return (start + 1 == end + return ((start + 1 == end) ? %_StringCharAt(s, start) : %_SubString(s, start, end)); } @@ -832,7 +854,7 @@ function HtmlEscape(str) { .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); -}; +} // Compatibility support for KJS. @@ -953,7 +975,7 @@ function SetUpString() { // Set up the non-enumerable functions on the String prototype object. - InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array( + InstallFunctions($String.prototype, DONT_ENUM, $Array( "valueOf", StringValueOf, "toString", StringToString, "charAt", StringCharAt, diff --git a/deps/v8/src/strtod.cc b/deps/v8/src/strtod.cc index c89c8f3339..be79c80085 100644 --- a/deps/v8/src/strtod.cc +++ b/deps/v8/src/strtod.cc @@ -27,7 +27,6 @@ #include <stdarg.h> #include <math.h> -#include <limits> #include "globals.h" #include "utils.h" diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index cdb4874fcb..c7f4f94386 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -55,7 +55,15 @@ void StubCache::Initialize(bool create_heap_objects) { ASSERT(IsPowerOf2(kSecondaryTableSize)); if (create_heap_objects) { HandleScope scope; - Clear(); + Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); + for (int i = 0; i < kPrimaryTableSize; i++) { + primary_[i].key = heap()->empty_string(); + primary_[i].value = empty; + } + for (int j = 0; j < kSecondaryTableSize; j++) { + secondary_[j].key = heap()->empty_string(); + secondary_[j].value = empty; + } } } @@ -101,8 +109,8 @@ Code* StubCache::Set(String* name, Map* map, Code* code) { } -MaybeObject* StubCache::ComputeLoadNonexistent(String* name, - JSObject* receiver) { +Handle<Code> StubCache::ComputeLoadNonexistent(Handle<String> name, + Handle<JSObject> receiver) { ASSERT(receiver->IsGlobalObject() || receiver->HasFastProperties()); // If no global objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map @@ -110,558 +118,431 @@ MaybeObject* StubCache::ComputeLoadNonexistent(String* name, // there are global objects involved, we need to check global // property cells in the stub and therefore the stub will be // specific to the name. - String* cache_name = heap()->empty_string(); + Handle<String> cache_name = factory()->empty_string(); if (receiver->IsGlobalObject()) cache_name = name; - JSObject* last = receiver; + Handle<JSObject> last = receiver; while (last->GetPrototype() != heap()->null_value()) { - last = JSObject::cast(last->GetPrototype()); + last = Handle<JSObject>(JSObject::cast(last->GetPrototype())); if (last->IsGlobalObject()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NONEXISTENT); - Object* code = receiver->map()->FindInCodeCache(cache_name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadNonexistent(cache_name, receiver, last); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), cache_name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, cache_name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(cache_name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*cache_name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadNonexistent(cache_name, receiver, last); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *cache_name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *cache_name, *code)); + JSObject::UpdateMapCodeCache(receiver, cache_name, code); return code; } -MaybeObject* StubCache::ComputeLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> StubCache::ComputeLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int field_index) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadField(receiver, holder, field_index, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadField(receiver, holder, field_index, name); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeLoadCallback(String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> StubCache::ComputeLoadCallback(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { ASSERT(v8::ToCData<Address>(callback->getter()) != 0); - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadCallback(name, receiver, holder, callback); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadCallback(name, receiver, holder, callback); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); +Handle<Code> StubCache::ComputeLoadConstant(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadConstant(receiver, holder, value, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadConstant(receiver, holder, value, name); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeLoadInterceptor(String* name, - JSObject* receiver, - JSObject* holder) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); +Handle<Code> StubCache::ComputeLoadInterceptor(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder) { + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadInterceptor(receiver, holder, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadInterceptor(receiver, holder, name); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeLoadNormal() { - return isolate_->builtins()->builtin(Builtins::kLoadIC_Normal); +Handle<Code> StubCache::ComputeLoadNormal() { + return isolate_->builtins()->LoadIC_Normal(); } -MaybeObject* StubCache::ComputeLoadGlobal(String* name, - JSObject* receiver, - GlobalObject* holder, - JSGlobalPropertyCell* cell, +Handle<Code> StubCache::ComputeLoadGlobal(Handle<String> name, + Handle<JSObject> receiver, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, bool is_dont_delete) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - LoadStubCompiler compiler; - { MaybeObject* maybe_code = compiler.CompileLoadGlobal(receiver, - holder, - cell, - name, - is_dont_delete); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + LoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); + PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> StubCache::ComputeKeyedLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int field_index) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadField(name, receiver, holder, field_index); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadField(name, receiver, holder, field_index); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); +Handle<Code> StubCache::ComputeKeyedLoadConstant(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadConstant(name, receiver, holder, value); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadConstant(name, receiver, holder, value); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadInterceptor(String* name, - JSObject* receiver, - JSObject* holder) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); +Handle<Code> StubCache::ComputeKeyedLoadInterceptor(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder) { + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadInterceptor(receiver, holder, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileLoadInterceptor(receiver, holder, name); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadCallback(String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { - ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); +Handle<Code> StubCache::ComputeKeyedLoadCallback( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { + ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = - compiler.CompileLoadCallback(name, receiver, holder, callback); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = + compiler.CompileLoadCallback(name, receiver, holder, callback); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } - -MaybeObject* StubCache::ComputeKeyedLoadArrayLength(String* name, - JSArray* receiver) { +Handle<Code> StubCache::ComputeKeyedLoadArrayLength(Handle<String> name, + Handle<JSArray> receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); - ASSERT(receiver->IsJSObject()); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = compiler.CompileLoadArrayLength(name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileLoadArrayLength(name); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadStringLength(String* name, - String* receiver) { +Handle<Code> StubCache::ComputeKeyedLoadStringLength(Handle<String> name, + Handle<String> receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); - Map* map = receiver->map(); - Object* code = map->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = compiler.CompileLoadStringLength(name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = map->UpdateCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Map> map(receiver->map()); + Handle<Object> probe(map->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileLoadStringLength(name); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + Map::UpdateCodeCache(map, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadFunctionPrototype( - String* name, - JSFunction* receiver) { +Handle<Code> StubCache::ComputeKeyedLoadFunctionPrototype( + Handle<String> name, + Handle<JSFunction> receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedLoadStubCompiler compiler; - { MaybeObject* maybe_code = compiler.CompileLoadFunctionPrototype(name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileLoadFunctionPrototype(name); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeStoreField(String* name, - JSObject* receiver, +Handle<Code> StubCache::ComputeStoreField(Handle<String> name, + Handle<JSObject> receiver, int field_index, - Map* transition, + Handle<Map> transition, StrictModeFlag strict_mode) { - PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; + PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - StoreStubCompiler compiler(strict_mode); - { MaybeObject* maybe_code = - compiler.CompileStoreField(receiver, field_index, transition, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + StoreStubCompiler compiler(isolate_, strict_mode); + Handle<Code> code = + compiler.CompileStoreField(receiver, field_index, transition, name); + PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeKeyedLoadOrStoreElement( - JSObject* receiver, - bool is_store, +Handle<Code> StubCache::ComputeKeyedLoadOrStoreElement( + Handle<JSObject> receiver, + KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( - is_store ? Code::KEYED_STORE_IC : - Code::KEYED_LOAD_IC, + stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC + : Code::KEYED_STORE_IC, NORMAL, strict_mode); - String* name = is_store - ? isolate()->heap()->KeyedStoreElementMonomorphic_symbol() - : isolate()->heap()->KeyedLoadElementMonomorphic_symbol(); - Object* maybe_code = receiver->map()->FindInCodeCache(name, flags); - if (!maybe_code->IsUndefined()) return Code::cast(maybe_code); - - MaybeObject* maybe_new_code = NULL; - Map* receiver_map = receiver->map(); - if (is_store) { - KeyedStoreStubCompiler compiler(strict_mode); - maybe_new_code = compiler.CompileStoreElement(receiver_map); - } else { - KeyedLoadStubCompiler compiler; - maybe_new_code = compiler.CompileLoadElement(receiver_map); + Handle<String> name; + switch (stub_kind) { + case KeyedIC::LOAD: + name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); + break; + case KeyedIC::STORE_NO_TRANSITION: + name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); + break; + default: + UNREACHABLE(); + break; + } + Handle<Map> receiver_map(receiver->map()); + Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + Handle<Code> code; + switch (stub_kind) { + case KeyedIC::LOAD: { + KeyedLoadStubCompiler compiler(isolate_); + code = compiler.CompileLoadElement(receiver_map); + break; + } + case KeyedIC::STORE_NO_TRANSITION: { + KeyedStoreStubCompiler compiler(isolate_, strict_mode); + code = compiler.CompileStoreElement(receiver_map); + break; + } + default: + UNREACHABLE(); + break; } - Code* code; - if (!maybe_new_code->To(&code)) return maybe_new_code; - if (is_store) { - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, - Code::cast(code), 0)); + + ASSERT(!code.is_null()); + + if (stub_kind == KeyedIC::LOAD) { + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { - PROFILE(isolate_, - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, - Code::cast(code), 0)); - } - ASSERT(code->IsCode()); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { - return isolate_->builtins()->builtin((strict_mode == kStrictMode) - ? Builtins::kStoreIC_Normal_Strict - : Builtins::kStoreIC_Normal); +Handle<Code> StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { + return (strict_mode == kStrictMode) + ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() + : isolate_->builtins()->Builtins::StoreIC_Normal(); } -MaybeObject* StubCache::ComputeStoreGlobal(String* name, - GlobalObject* receiver, - JSGlobalPropertyCell* cell, +Handle<Code> StubCache::ComputeStoreGlobal(Handle<String> name, + Handle<GlobalObject> receiver, + Handle<JSGlobalPropertyCell> cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - StoreStubCompiler compiler(strict_mode); - { MaybeObject* maybe_code = - compiler.CompileStoreGlobal(receiver, cell, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + StoreStubCompiler compiler(isolate_, strict_mode); + Handle<Code> code = compiler.CompileStoreGlobal(receiver, cell, name); + PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeStoreCallback( - String* name, - JSObject* receiver, - AccessorInfo* callback, - StrictModeFlag strict_mode) { +Handle<Code> StubCache::ComputeStoreCallback(Handle<String> name, + Handle<JSObject> receiver, + Handle<AccessorInfo> callback, + StrictModeFlag strict_mode) { ASSERT(v8::ToCData<Address>(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - StoreStubCompiler compiler(strict_mode); - { MaybeObject* maybe_code = - compiler.CompileStoreCallback(receiver, callback, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + StoreStubCompiler compiler(isolate_, strict_mode); + Handle<Code> code = compiler.CompileStoreCallback(receiver, callback, name); + PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } -MaybeObject* StubCache::ComputeStoreInterceptor( - String* name, - JSObject* receiver, - StrictModeFlag strict_mode) { +Handle<Code> StubCache::ComputeStoreInterceptor(Handle<String> name, + Handle<JSObject> receiver, + StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - StoreStubCompiler compiler(strict_mode); - { MaybeObject* maybe_code = - compiler.CompileStoreInterceptor(receiver, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate_, - CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + StoreStubCompiler compiler(isolate_, strict_mode); + Handle<Code> code = compiler.CompileStoreInterceptor(receiver, name); + PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } - -MaybeObject* StubCache::ComputeKeyedStoreField(String* name, - JSObject* receiver, +Handle<Code> StubCache::ComputeKeyedStoreField(Handle<String> name, + Handle<JSObject> receiver, int field_index, - Map* transition, + Handle<Map> transition, StrictModeFlag strict_mode) { - PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; + PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); - Object* code = receiver->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - KeyedStoreStubCompiler compiler(strict_mode); - { MaybeObject* maybe_code = - compiler.CompileStoreField(receiver, field_index, transition, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - PROFILE(isolate(), - CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, - Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - receiver->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + KeyedStoreStubCompiler compiler(isolate(), strict_mode); + Handle<Code> code = + compiler.CompileStoreField(receiver, field_index, transition, name); + PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); + JSObject::UpdateMapCodeCache(receiver, name, code); return code; } + #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) -MaybeObject* StubCache::ComputeCallConstant(int argc, +Handle<Code> StubCache::ComputeCallConstant(int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder, - JSFunction* function) { + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = - IC::GetCodeCacheForObject(object, holder); - JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); + IC::GetCodeCacheForObject(*object, *holder); + Handle<JSObject> map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; @@ -673,51 +554,36 @@ MaybeObject* StubCache::ComputeCallConstant(int argc, check = BOOLEAN_CHECK; } - Code::Flags flags = Code::ComputeMonomorphicFlags(kind, - CONSTANT_FUNCTION, - extra_ic_state, - cache_holder, - argc); - Object* code = map_holder->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - // If the function hasn't been compiled yet, we cannot do it now - // because it may cause GC. To avoid this issue, we return an - // internal error which will make sure we do not update any - // caches. - if (!function->is_compiled()) return Failure::InternalError(); - // Compile the stub - only create stubs for fully compiled functions. - CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder); - { MaybeObject* maybe_code = - compiler.CompileCallConstant(object, holder, function, name, check); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - Code::cast(code)->set_check_type(check); - ASSERT_EQ(flags, Code::cast(code)->flags()); - PROFILE(isolate_, - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), - Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - map_holder->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Code::Flags flags = + Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, + cache_holder, argc); + Handle<Object> probe(map_holder->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); + Handle<Code> code = + compiler.CompileCallConstant(object, holder, function, name, check); + code->set_check_type(check); + ASSERT_EQ(flags, code->flags()); + PROFILE(isolate_, + CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); + GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); + JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } -MaybeObject* StubCache::ComputeCallField(int argc, +Handle<Code> StubCache::ComputeCallField(int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = - IC::GetCodeCacheForObject(object, holder); - JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); + IC::GetCodeCacheForObject(*object, *holder); + Handle<JSObject> map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a @@ -726,47 +592,35 @@ MaybeObject* StubCache::ComputeCallField(int argc, object = holder; } - Code::Flags flags = Code::ComputeMonomorphicFlags(kind, - FIELD, - extra_ic_state, - cache_holder, - argc); - Object* code = map_holder->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder); - { MaybeObject* maybe_code = - compiler.CompileCallField(JSObject::cast(object), - holder, - index, - name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - ASSERT_EQ(flags, Code::cast(code)->flags()); - PROFILE(isolate_, - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), - Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - map_holder->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + Code::Flags flags = + Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, + cache_holder, argc); + Handle<Object> probe(map_holder->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); + Handle<Code> code = + compiler.CompileCallField(Handle<JSObject>::cast(object), + holder, index, name); + ASSERT_EQ(flags, code->flags()); + PROFILE(isolate_, + CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); + GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); + JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } -MaybeObject* StubCache::ComputeCallInterceptor( - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder) { +Handle<Code> StubCache::ComputeCallInterceptor(int argc, + Code::Kind kind, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = - IC::GetCodeCacheForObject(object, holder); - JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); + IC::GetCodeCacheForObject(*object, *holder); + Handle<JSObject> map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a @@ -775,135 +629,60 @@ MaybeObject* StubCache::ComputeCallInterceptor( object = holder; } - Code::Flags flags = Code::ComputeMonomorphicFlags(kind, - INTERCEPTOR, - extra_ic_state, - cache_holder, - argc); - Object* code = map_holder->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder); - { MaybeObject* maybe_code = - compiler.CompileCallInterceptor(JSObject::cast(object), holder, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - ASSERT_EQ(flags, Code::cast(code)->flags()); - PROFILE(isolate(), - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), - Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - map_holder->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } - return code; -} - - -MaybeObject* StubCache::ComputeCallNormal(int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state, - String* name, - JSObject* receiver) { - Object* code; - { MaybeObject* maybe_code = ComputeCallNormal(argc, kind, extra_ic_state); - if (!maybe_code->ToObject(&code)) return maybe_code; - } + Code::Flags flags = + Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, + cache_holder, argc); + Handle<Object> probe(map_holder->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); + Handle<Code> code = + compiler.CompileCallInterceptor(Handle<JSObject>::cast(object), + holder, name); + ASSERT_EQ(flags, code->flags()); + PROFILE(isolate(), + CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); + GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); + JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } -MaybeObject* StubCache::ComputeCallGlobal(int argc, +Handle<Code> StubCache::ComputeCallGlobal(int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state, - String* name, - JSObject* receiver, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function) { + Code::ExtraICState extra_state, + Handle<String> name, + Handle<JSObject> receiver, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function) { InlineCacheHolderFlag cache_holder = - IC::GetCodeCacheForObject(receiver, holder); - JSObject* map_holder = IC::GetCodeCacheHolder(receiver, cache_holder); - Code::Flags flags = Code::ComputeMonomorphicFlags(kind, - NORMAL, - extra_ic_state, - cache_holder, - argc); - Object* code = map_holder->map()->FindInCodeCache(name, flags); - if (code->IsUndefined()) { - // If the function hasn't been compiled yet, we cannot do it now - // because it may cause GC. To avoid this issue, we return an - // internal error which will make sure we do not update any - // caches. - if (!function->is_compiled()) return Failure::InternalError(); - CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder); - { MaybeObject* maybe_code = - compiler.CompileCallGlobal(receiver, holder, cell, function, name); - if (!maybe_code->ToObject(&code)) return maybe_code; - } - ASSERT_EQ(flags, Code::cast(code)->flags()); - PROFILE(isolate(), - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), - Code::cast(code), name)); - GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); - Object* result; - { MaybeObject* maybe_result = - map_holder->UpdateMapCodeCache(name, Code::cast(code)); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - } + IC::GetCodeCacheForObject(*receiver, *holder); + Handle<JSObject> map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); + Code::Flags flags = + Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, + cache_holder, argc); + Handle<Object> probe(map_holder->map()->FindInCodeCache(*name, flags)); + if (probe->IsCode()) return Handle<Code>::cast(probe); + + CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); + Handle<Code> code = + compiler.CompileCallGlobal(receiver, holder, cell, function, name); + ASSERT_EQ(flags, code->flags()); + PROFILE(isolate(), + CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); + GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); + JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } -static Object* GetProbeValue(Isolate* isolate, Code::Flags flags) { - // Use raw_unchecked... so we don't get assert failures during GC. - UnseededNumberDictionary* dictionary = - isolate->heap()->raw_unchecked_non_monomorphic_cache(); - int entry = dictionary->FindEntry(isolate, flags); - if (entry != -1) return dictionary->ValueAt(entry); - return isolate->heap()->raw_unchecked_undefined_value(); -} - - -MUST_USE_RESULT static MaybeObject* ProbeCache(Isolate* isolate, - Code::Flags flags) { - Heap* heap = isolate->heap(); - Object* probe = GetProbeValue(isolate, flags); - if (probe != heap->undefined_value()) return probe; - // Seed the cache with an undefined value to make sure that any - // generated code object can always be inserted into the cache - // without causing allocation failures. - Object* result; - { MaybeObject* maybe_result = - heap->non_monomorphic_cache()->AtNumberPut(flags, - heap->undefined_value()); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - heap->public_set_non_monomorphic_cache( - UnseededNumberDictionary::cast(result)); - return probe; -} - - -static MaybeObject* FillCache(Isolate* isolate, MaybeObject* maybe_code) { - Object* code; - if (maybe_code->ToObject(&code)) { - if (code->IsCode()) { - Heap* heap = isolate->heap(); - int entry = heap->non_monomorphic_cache()->FindEntry( - Code::cast(code)->flags()); - // The entry must be present see comment in ProbeCache. - ASSERT(entry != -1); - ASSERT(heap->non_monomorphic_cache()->ValueAt(entry) == - heap->undefined_value()); - heap->non_monomorphic_cache()->ValueAtPut(entry, code); - CHECK(GetProbeValue(isolate, Code::cast(code)->flags()) == code); - } - } - return maybe_code; +static void FillCache(Isolate* isolate, Handle<Code> code) { + Handle<UnseededNumberDictionary> dictionary = + UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), + code->flags(), + code); + isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } @@ -913,209 +692,200 @@ Code* StubCache::FindCallInitialize(int argc, Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); - Code::Flags flags = Code::ComputeFlags(kind, - UNINITIALIZED, - extra_state, - NORMAL, - argc); - Object* result = ProbeCache(isolate(), flags)->ToObjectUnchecked(); - ASSERT(result != heap()->undefined_value()); + Code::Flags flags = + Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); + + // Use raw_unchecked... so we don't get assert failures during GC. + UnseededNumberDictionary* dictionary = + isolate()->heap()->raw_unchecked_non_monomorphic_cache(); + int entry = dictionary->FindEntry(isolate(), flags); + ASSERT(entry != -1); + Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. - return reinterpret_cast<Code*>(result); + return reinterpret_cast<Code*>(code); } -MaybeObject* StubCache::ComputeCallInitialize(int argc, +Handle<Code> StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); - Code::Flags flags = Code::ComputeFlags(kind, - UNINITIALIZED, - extra_state, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallInitialize(flags)); + Code::Flags flags = + Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallInitialize(flags); + FillCache(isolate_, code); + return code; } -Handle<Code> StubCache::ComputeCallInitialize(int argc, - RelocInfo::Mode mode) { - CALL_HEAP_FUNCTION(isolate_, - ComputeCallInitialize(argc, mode, Code::CALL_IC), - Code); +Handle<Code> StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { + return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc) { - CALL_HEAP_FUNCTION( - isolate_, - ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC), - Code); + return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, + Code::KEYED_CALL_IC); } -MaybeObject* StubCache::ComputeCallPreMonomorphic( +Handle<Code> StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state) { - Code::Flags flags = Code::ComputeFlags(kind, - PREMONOMORPHIC, - extra_ic_state, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallPreMonomorphic(flags)); + Code::ExtraICState extra_state) { + Code::Flags flags = + Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallPreMonomorphic(flags); + FillCache(isolate_, code); + return code; } -MaybeObject* StubCache::ComputeCallNormal(int argc, +Handle<Code> StubCache::ComputeCallNormal(int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state) { - Code::Flags flags = Code::ComputeFlags(kind, - MONOMORPHIC, - extra_ic_state, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallNormal(flags)); + Code::ExtraICState extra_state) { + Code::Flags flags = + Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallNormal(flags); + FillCache(isolate_, code); + return code; } -MaybeObject* StubCache::ComputeCallArguments(int argc, Code::Kind kind) { +Handle<Code> StubCache::ComputeCallArguments(int argc, Code::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); - Code::Flags flags = Code::ComputeFlags(kind, - MEGAMORPHIC, - Code::kNoExtraICState, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallArguments(flags)); + Code::Flags flags = + Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, + NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallArguments(flags); + FillCache(isolate_, code); + return code; } -MaybeObject* StubCache::ComputeCallMegamorphic( +Handle<Code> StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state) { - Code::Flags flags = Code::ComputeFlags(kind, - MEGAMORPHIC, - extra_ic_state, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallMegamorphic(flags)); + Code::ExtraICState extra_state) { + Code::Flags flags = + Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, + NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallMegamorphic(flags); + FillCache(isolate_, code); + return code; } -MaybeObject* StubCache::ComputeCallMiss(int argc, +Handle<Code> StubCache::ComputeCallMiss(int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state) { + Code::ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. - Code::Flags flags = Code::ComputeFlags(kind, - MONOMORPHIC_PROTOTYPE_FAILURE, - extra_ic_state, - NORMAL, - argc, - OWN_MAP); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallMiss(flags)); + Code::Flags flags = + Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, + NORMAL, argc, OWN_MAP); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallMiss(flags); + FillCache(isolate_, code); + return code; } #ifdef ENABLE_DEBUGGER_SUPPORT -MaybeObject* StubCache::ComputeCallDebugBreak( - int argc, - Code::Kind kind) { +Handle<Code> StubCache::ComputeCallDebugBreak(int argc, + Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. - Code::Flags flags = Code::ComputeFlags(kind, - DEBUG_BREAK, - Code::kNoExtraICState, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallDebugBreak(flags)); + Code::Flags flags = + Code::ComputeFlags(kind, DEBUG_BREAK, Code::kNoExtraICState, + NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallDebugBreak(flags); + FillCache(isolate_, code); + return code; } -MaybeObject* StubCache::ComputeCallDebugPrepareStepIn( - int argc, - Code::Kind kind) { +Handle<Code> StubCache::ComputeCallDebugPrepareStepIn(int argc, + Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. - Code::Flags flags = Code::ComputeFlags(kind, - DEBUG_PREPARE_STEP_IN, - Code::kNoExtraICState, - NORMAL, - argc); - Object* probe; - { MaybeObject* maybe_probe = ProbeCache(isolate_, flags); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(isolate_, compiler.CompileCallDebugPrepareStepIn(flags)); + Code::Flags flags = + Code::ComputeFlags(kind, DEBUG_PREPARE_STEP_IN, Code::kNoExtraICState, + NORMAL, argc); + Handle<UnseededNumberDictionary> cache = + isolate_->factory()->non_monomorphic_cache(); + int entry = cache->FindEntry(isolate_, flags); + if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); + + StubCompiler compiler(isolate_); + Handle<Code> code = compiler.CompileCallDebugPrepareStepIn(flags); + FillCache(isolate_, code); + return code; } #endif void StubCache::Clear() { + Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); - primary_[i].value = isolate_->builtins()->builtin( - Builtins::kIllegal); + primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); - secondary_[j].value = isolate_->builtins()->builtin( - Builtins::kIllegal); + secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, - Code::Flags flags) { + Code::Flags flags, + Handle<Context> global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); @@ -1124,7 +894,8 @@ void StubCache::CollectMatchingMaps(SmallMapList* types, if (map == NULL) continue; int offset = PrimaryOffset(name, flags, map); - if (entry(primary_, offset) == &primary_[i]) { + if (entry(primary_, offset) == &primary_[i] && + !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle<Map>(map)); } } @@ -1147,7 +918,8 @@ void StubCache::CollectMatchingMaps(SmallMapList* types, // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); - if (entry(secondary_, offset) == &secondary_[i]) { + if (entry(secondary_, offset) == &secondary_[i] && + !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle<Map>(map)); } } @@ -1342,8 +1114,8 @@ RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; + ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast<StrictModeFlag>(args.smi_at(3)); - ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( @@ -1360,62 +1132,47 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { } -MaybeObject* StubCompiler::CompileCallInitialize(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); - Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags); + Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { - CallIC::GenerateInitialize(masm(), argc, extra_ic_state); + CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallInitialize"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); - Code* code = Code::cast(result); - USE(code); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, Code::cast(code))); - return result; + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); + return code; } -MaybeObject* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); - Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags); + Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { - CallIC::GenerateInitialize(masm(), argc, extra_ic_state); + CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); - Code* code = Code::cast(result); - USE(code); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, Code::cast(code))); - return result; + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); + return code; } -MaybeObject* StubCompiler::CompileCallNormal(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { @@ -1426,116 +1183,82 @@ MaybeObject* StubCompiler::CompileCallNormal(Code::Flags flags) { } else { KeyedCallIC::GenerateNormal(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = GetCodeWithFlags(flags, "CompileCallNormal"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); - Code* code = Code::cast(result); - USE(code); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, Code::cast(code))); - return result; + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); + return code; } -MaybeObject* StubCompiler::CompileCallMegamorphic(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); - Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags); + Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { - CallIC::GenerateMegamorphic(masm(), argc, extra_ic_state); + CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallMegamorphic"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); - Code* code = Code::cast(result); - USE(code); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, Code::cast(code))); - return result; + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); + return code; } -MaybeObject* StubCompiler::CompileCallArguments(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); - Code::Kind kind = Code::ExtractKindFromFlags(flags); - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallArguments"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Code* code = Code::cast(result); - USE(code); + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, Code::cast(code))); - return result; + CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), + CALL_MEGAMORPHIC_TAG), + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); + return code; } -MaybeObject* StubCompiler::CompileCallMiss(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); - Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags); + Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { - CallIC::GenerateMiss(masm(), argc, extra_ic_state); + CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = GetCodeWithFlags(flags, "CompileCallMiss"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); - Code* code = Code::cast(result); - USE(code); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), - code, code->arguments_count())); - GDBJIT(AddCode(GDBJITInterface::CALL_MISS, Code::cast(code))); - return result; + *code, code->arguments_count())); + GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); + return code; } #ifdef ENABLE_DEBUGGER_SUPPORT -MaybeObject* StubCompiler::CompileCallDebugBreak(Code::Flags flags) { - HandleScope scope(isolate()); +Handle<Code> StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallDebugBreak"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Code* code = Code::cast(result); - USE(code); - Code::Kind kind = Code::ExtractKindFromFlags(flags); - USE(kind); + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), - CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_DEBUG_BREAK_TAG), - code, code->arguments_count())); - return result; + CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), + CALL_DEBUG_BREAK_TAG), + *code, code->arguments_count())); + return code; } -MaybeObject* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { - HandleScope scope(isolate()); - // Use the same code for the the step in preparations as we do for - // the miss case. +Handle<Code> StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { + // Use the same code for the the step in preparations as we do for the + // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { @@ -1544,133 +1267,94 @@ MaybeObject* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { } else { KeyedCallIC::GenerateMiss(masm(), argc); } - Object* result; - { MaybeObject* maybe_result = - GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Code* code = Code::cast(result); - USE(code); + Handle<Code> code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), - code, + *code, code->arguments_count())); - return result; + return code; } -#endif +#endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG -MaybeObject* StubCompiler::GetCodeWithFlags(Code::Flags flags, - const char* name) { - // Check for allocation failures during stub compilation. - if (failure_->IsFailure()) return failure_; +Handle<Code> StubCompiler::GetCodeWithFlags(Code::Flags flags, + const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); - MaybeObject* result = heap()->CreateCode(desc, flags, masm_.CodeObject()); + Handle<Code> code = factory()->NewCode(desc, flags, masm_.CodeObject()); #ifdef ENABLE_DISASSEMBLER - if (FLAG_print_code_stubs && !result->IsFailure()) { - Code::cast(result->ToObjectUnchecked())->Disassemble(name); - } + if (FLAG_print_code_stubs) code->Disassemble(name); #endif - return result; + return code; } -MaybeObject* StubCompiler::GetCodeWithFlags(Code::Flags flags, String* name) { - if (FLAG_print_code_stubs && (name != NULL)) { - return GetCodeWithFlags(flags, *name->ToCString()); - } - return GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL)); +Handle<Code> StubCompiler::GetCodeWithFlags(Code::Flags flags, + Handle<String> name) { + return (FLAG_print_code_stubs && !name.is_null()) + ? GetCodeWithFlags(flags, *name->ToCString()) + : GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL)); } -void StubCompiler::LookupPostInterceptor(JSObject* holder, - String* name, +void StubCompiler::LookupPostInterceptor(Handle<JSObject> holder, + Handle<String> name, LookupResult* lookup) { - holder->LocalLookupRealNamedProperty(name, lookup); - if (!lookup->IsProperty()) { - lookup->NotFound(); - Object* proto = holder->GetPrototype(); - if (!proto->IsNull()) { - proto->Lookup(name, lookup); - } - } -} + holder->LocalLookupRealNamedProperty(*name, lookup); + if (lookup->IsProperty()) return; + lookup->NotFound(); + if (holder->GetPrototype()->IsNull()) return; + + holder->GetPrototype()->Lookup(*name, lookup); +} -MaybeObject* LoadStubCompiler::GetCode(PropertyType type, String* name) { +Handle<Code> LoadStubCompiler::GetCode(PropertyType type, Handle<String> name) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, type); - MaybeObject* result = GetCodeWithFlags(flags, name); - if (!result->IsFailure()) { - PROFILE(isolate(), - CodeCreateEvent(Logger::LOAD_IC_TAG, - Code::cast(result->ToObjectUnchecked()), - name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, - name, - Code::cast(result->ToObjectUnchecked()))); - } - return result; + Handle<Code> code = GetCodeWithFlags(flags, name); + PROFILE(isolate(), CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + return code; } -MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type, - String* name, +Handle<Code> KeyedLoadStubCompiler::GetCode(PropertyType type, + Handle<String> name, InlineCacheState state) { Code::Flags flags = Code::ComputeFlags( Code::KEYED_LOAD_IC, state, Code::kNoExtraICState, type); - MaybeObject* result = GetCodeWithFlags(flags, name); - if (!result->IsFailure()) { - PROFILE(isolate(), - CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, - Code::cast(result->ToObjectUnchecked()), - name)); - GDBJIT(AddCode(GDBJITInterface::LOAD_IC, - name, - Code::cast(result->ToObjectUnchecked()))); - } - return result; + Handle<Code> code = GetCodeWithFlags(flags, name); + PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); + return code; } -MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) { +Handle<Code> StoreStubCompiler::GetCode(PropertyType type, + Handle<String> name) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type, strict_mode_); - MaybeObject* result = GetCodeWithFlags(flags, name); - if (!result->IsFailure()) { - PROFILE(isolate(), - CodeCreateEvent(Logger::STORE_IC_TAG, - Code::cast(result->ToObjectUnchecked()), - name)); - GDBJIT(AddCode(GDBJITInterface::STORE_IC, - name, - Code::cast(result->ToObjectUnchecked()))); - } - return result; + Handle<Code> code = GetCodeWithFlags(flags, name); + PROFILE(isolate(), CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); + return code; } -MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, - String* name, +Handle<Code> KeyedStoreStubCompiler::GetCode(PropertyType type, + Handle<String> name, InlineCacheState state) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, state, strict_mode_, type); - MaybeObject* result = GetCodeWithFlags(flags, name); - if (!result->IsFailure()) { - PROFILE(isolate(), - CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, - Code::cast(result->ToObjectUnchecked()), - name)); - GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, - name, - Code::cast(result->ToObjectUnchecked()))); - } - return result; + Handle<Code> code = GetCodeWithFlags(flags, name); + PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); + GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); + return code; } @@ -1680,50 +1364,49 @@ void KeyedStoreStubCompiler::GenerateStoreDictionaryElement( } -CallStubCompiler::CallStubCompiler(int argc, +CallStubCompiler::CallStubCompiler(Isolate* isolate, + int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state, + Code::ExtraICState extra_state, InlineCacheHolderFlag cache_holder) - : arguments_(argc), + : StubCompiler(isolate), + arguments_(argc), kind_(kind), - extra_ic_state_(extra_ic_state), + extra_state_(extra_state), cache_holder_(cache_holder) { } -bool CallStubCompiler::HasCustomCallGenerator(JSFunction* function) { - SharedFunctionInfo* info = function->shared(); - if (info->HasBuiltinFunctionId()) { - BuiltinFunctionId id = info->builtin_function_id(); +bool CallStubCompiler::HasCustomCallGenerator(Handle<JSFunction> function) { + if (function->shared()->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function->shared()->builtin_function_id(); #define CALL_GENERATOR_CASE(name) if (id == k##name) return true; CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE) #undef CALL_GENERATOR_CASE } + CallOptimization optimization(function); - if (optimization.is_simple_api_call()) { - return true; - } - return false; + return optimization.is_simple_api_call(); } -MaybeObject* CallStubCompiler::CompileCustomCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* fname) { +Handle<Code> CallStubCompiler::CompileCustomCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> fname) { ASSERT(HasCustomCallGenerator(function)); - SharedFunctionInfo* info = function->shared(); - if (info->HasBuiltinFunctionId()) { - BuiltinFunctionId id = info->builtin_function_id(); -#define CALL_GENERATOR_CASE(name) \ - if (id == k##name) { \ - return CallStubCompiler::Compile##name##Call(object, \ - holder, \ - cell, \ - function, \ - fname); \ + if (function->shared()->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function->shared()->builtin_function_id(); +#define CALL_GENERATOR_CASE(name) \ + if (id == k##name) { \ + return CallStubCompiler::Compile##name##Call(object, \ + holder, \ + cell, \ + function, \ + fname); \ } CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE) #undef CALL_GENERATOR_CASE @@ -1739,100 +1422,99 @@ MaybeObject* CallStubCompiler::CompileCustomCall(Object* object, } -MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) { +Handle<Code> CallStubCompiler::GetCode(PropertyType type, Handle<String> name) { int argc = arguments_.immediate(); Code::Flags flags = Code::ComputeMonomorphicFlags(kind_, type, - extra_ic_state_, + extra_state_, cache_holder_, argc); return GetCodeWithFlags(flags, name); } -MaybeObject* CallStubCompiler::GetCode(JSFunction* function) { - String* function_name = NULL; +Handle<Code> CallStubCompiler::GetCode(Handle<JSFunction> function) { + Handle<String> function_name; if (function->shared()->name()->IsString()) { - function_name = String::cast(function->shared()->name()); + function_name = Handle<String>(String::cast(function->shared()->name())); } return GetCode(CONSTANT_FUNCTION, function_name); } -MaybeObject* ConstructStubCompiler::GetCode() { +Handle<Code> ConstructStubCompiler::GetCode() { Code::Flags flags = Code::ComputeFlags(Code::STUB); - Object* result; - { MaybeObject* maybe_result = GetCodeWithFlags(flags, "ConstructStub"); - if (!maybe_result->ToObject(&result)) return maybe_result; - } - Code* code = Code::cast(result); - USE(code); - PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, code, "ConstructStub")); - GDBJIT(AddCode(GDBJITInterface::STUB, "ConstructStub", Code::cast(code))); - return result; + Handle<Code> code = GetCodeWithFlags(flags, "ConstructStub"); + PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, "ConstructStub")); + GDBJIT(AddCode(GDBJITInterface::STUB, "ConstructStub", *code)); + return code; } CallOptimization::CallOptimization(LookupResult* lookup) { - if (!lookup->IsProperty() || !lookup->IsCacheable() || - lookup->type() != CONSTANT_FUNCTION) { - Initialize(NULL); - } else { + if (lookup->IsFound() && + lookup->IsCacheable() && + lookup->type() == CONSTANT_FUNCTION) { // We only optimize constant function calls. - Initialize(lookup->GetConstantFunction()); + Initialize(Handle<JSFunction>(lookup->GetConstantFunction())); + } else { + Initialize(Handle<JSFunction>::null()); } } -CallOptimization::CallOptimization(JSFunction* function) { +CallOptimization::CallOptimization(Handle<JSFunction> function) { Initialize(function); } -int CallOptimization::GetPrototypeDepthOfExpectedType(JSObject* object, - JSObject* holder) const { - ASSERT(is_simple_api_call_); - if (expected_receiver_type_ == NULL) return 0; +int CallOptimization::GetPrototypeDepthOfExpectedType( + Handle<JSObject> object, + Handle<JSObject> holder) const { + ASSERT(is_simple_api_call()); + if (expected_receiver_type_.is_null()) return 0; int depth = 0; - while (object != holder) { - if (object->IsInstanceOf(expected_receiver_type_)) return depth; - object = JSObject::cast(object->GetPrototype()); + while (!object.is_identical_to(holder)) { + if (object->IsInstanceOf(*expected_receiver_type_)) return depth; + object = Handle<JSObject>(JSObject::cast(object->GetPrototype())); ++depth; } - if (holder->IsInstanceOf(expected_receiver_type_)) return depth; + if (holder->IsInstanceOf(*expected_receiver_type_)) return depth; return kInvalidProtoDepth; } -void CallOptimization::Initialize(JSFunction* function) { - constant_function_ = NULL; +void CallOptimization::Initialize(Handle<JSFunction> function) { + constant_function_ = Handle<JSFunction>::null(); is_simple_api_call_ = false; - expected_receiver_type_ = NULL; - api_call_info_ = NULL; + expected_receiver_type_ = Handle<FunctionTemplateInfo>::null(); + api_call_info_ = Handle<CallHandlerInfo>::null(); - if (function == NULL || !function->is_compiled()) return; + if (function.is_null() || !function->is_compiled()) return; constant_function_ = function; AnalyzePossibleApiFunction(function); } -void CallOptimization::AnalyzePossibleApiFunction(JSFunction* function) { - SharedFunctionInfo* sfi = function->shared(); - if (!sfi->IsApiFunction()) return; - FunctionTemplateInfo* info = sfi->get_api_func_data(); +void CallOptimization::AnalyzePossibleApiFunction(Handle<JSFunction> function) { + if (!function->shared()->IsApiFunction()) return; + Handle<FunctionTemplateInfo> info(function->shared()->get_api_func_data()); // Require a C++ callback. if (info->call_code()->IsUndefined()) return; - api_call_info_ = CallHandlerInfo::cast(info->call_code()); + api_call_info_ = + Handle<CallHandlerInfo>(CallHandlerInfo::cast(info->call_code())); // Accept signatures that either have no restrictions at all or // only have restrictions on the receiver. if (!info->signature()->IsUndefined()) { - SignatureInfo* signature = SignatureInfo::cast(info->signature()); + Handle<SignatureInfo> signature = + Handle<SignatureInfo>(SignatureInfo::cast(info->signature())); if (!signature->args()->IsUndefined()) return; if (!signature->receiver()->IsUndefined()) { expected_receiver_type_ = - FunctionTemplateInfo::cast(signature->receiver()); + Handle<FunctionTemplateInfo>( + FunctionTemplateInfo::cast(signature->receiver())); } } diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index 18c157b165..398d9f415c 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -30,6 +30,7 @@ #include "allocation.h" #include "arguments.h" +#include "ic-inl.h" #include "macro-assembler.h" #include "objects.h" #include "zone-inl.h" @@ -75,207 +76,167 @@ class StubCache { // Computes the right stub matching. Inserts the result in the // cache before returning. This might compile a stub if needed. - MUST_USE_RESULT MaybeObject* ComputeLoadNonexistent( - String* name, - JSObject* receiver); + Handle<Code> ComputeLoadNonexistent(Handle<String> name, + Handle<JSObject> receiver); - MUST_USE_RESULT MaybeObject* ComputeLoadField(String* name, - JSObject* receiver, - JSObject* holder, - int field_index); + Handle<Code> ComputeLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + int field_index); - MUST_USE_RESULT MaybeObject* ComputeLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback); + Handle<Code> ComputeLoadCallback(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback); - MUST_USE_RESULT MaybeObject* ComputeLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value); + Handle<Code> ComputeLoadConstant(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value); - MUST_USE_RESULT MaybeObject* ComputeLoadInterceptor( - String* name, - JSObject* receiver, - JSObject* holder); + Handle<Code> ComputeLoadInterceptor(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder); - MUST_USE_RESULT MaybeObject* ComputeLoadNormal(); - - - MUST_USE_RESULT MaybeObject* ComputeLoadGlobal( - String* name, - JSObject* receiver, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - bool is_dont_delete); + Handle<Code> ComputeLoadNormal(); + Handle<Code> ComputeLoadGlobal(Handle<String> name, + Handle<JSObject> receiver, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + bool is_dont_delete); // --- - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadField(String* name, - JSObject* receiver, - JSObject* holder, - int field_index); + Handle<Code> ComputeKeyedLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + int field_index); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback); + Handle<Code> ComputeKeyedLoadCallback(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadConstant( - String* name, - JSObject* receiver, - JSObject* holder, - Object* value); + Handle<Code> ComputeKeyedLoadConstant(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadInterceptor( - String* name, - JSObject* receiver, - JSObject* holder); + Handle<Code> ComputeKeyedLoadInterceptor(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadArrayLength( - String* name, - JSArray* receiver); + Handle<Code> ComputeKeyedLoadArrayLength(Handle<String> name, + Handle<JSArray> receiver); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadStringLength( - String* name, - String* receiver); + Handle<Code> ComputeKeyedLoadStringLength(Handle<String> name, + Handle<String> receiver); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadFunctionPrototype( - String* name, - JSFunction* receiver); + Handle<Code> ComputeKeyedLoadFunctionPrototype(Handle<String> name, + Handle<JSFunction> receiver); // --- - MUST_USE_RESULT MaybeObject* ComputeStoreField( - String* name, - JSObject* receiver, - int field_index, - Map* transition, - StrictModeFlag strict_mode); - - MUST_USE_RESULT MaybeObject* ComputeStoreNormal( - StrictModeFlag strict_mode); - - MUST_USE_RESULT MaybeObject* ComputeStoreGlobal( - String* name, - GlobalObject* receiver, - JSGlobalPropertyCell* cell, - StrictModeFlag strict_mode); - - MUST_USE_RESULT MaybeObject* ComputeStoreCallback( - String* name, - JSObject* receiver, - AccessorInfo* callback, - StrictModeFlag strict_mode); - - MUST_USE_RESULT MaybeObject* ComputeStoreInterceptor( - String* name, - JSObject* receiver, - StrictModeFlag strict_mode); + Handle<Code> ComputeStoreField(Handle<String> name, + Handle<JSObject> receiver, + int field_index, + Handle<Map> transition, + StrictModeFlag strict_mode); - // --- + Handle<Code> ComputeStoreNormal(StrictModeFlag strict_mode); + + Handle<Code> ComputeStoreGlobal(Handle<String> name, + Handle<GlobalObject> receiver, + Handle<JSGlobalPropertyCell> cell, + StrictModeFlag strict_mode); - MUST_USE_RESULT MaybeObject* ComputeKeyedStoreField( - String* name, - JSObject* receiver, - int field_index, - Map* transition, - StrictModeFlag strict_mode); + Handle<Code> ComputeStoreCallback(Handle<String> name, + Handle<JSObject> receiver, + Handle<AccessorInfo> callback, + StrictModeFlag strict_mode); - MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreElement( - JSObject* receiver, - bool is_store, - StrictModeFlag strict_mode); + Handle<Code> ComputeStoreInterceptor(Handle<String> name, + Handle<JSObject> receiver, + StrictModeFlag strict_mode); // --- - MUST_USE_RESULT MaybeObject* ComputeCallField( - int argc, - Code::Kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder, - int index); - - MUST_USE_RESULT MaybeObject* ComputeCallConstant( - int argc, - Code::Kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder, - JSFunction* function); - - MUST_USE_RESULT MaybeObject* ComputeCallNormal( - int argc, - Code::Kind, - Code::ExtraICState extra_ic_state, - String* name, - JSObject* receiver); - - MUST_USE_RESULT MaybeObject* ComputeCallInterceptor( - int argc, - Code::Kind, - Code::ExtraICState extra_ic_state, - String* name, - Object* object, - JSObject* holder); - - MUST_USE_RESULT MaybeObject* ComputeCallGlobal( - int argc, - Code::Kind, - Code::ExtraICState extra_ic_state, - String* name, - JSObject* receiver, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function); + Handle<Code> ComputeKeyedStoreField(Handle<String> name, + Handle<JSObject> receiver, + int field_index, + Handle<Map> transition, + StrictModeFlag strict_mode); + + Handle<Code> ComputeKeyedLoadOrStoreElement(Handle<JSObject> receiver, + KeyedIC::StubKind stub_kind, + StrictModeFlag strict_mode); // --- - MUST_USE_RESULT MaybeObject* ComputeCallInitialize(int argc, - RelocInfo::Mode mode, - Code::Kind kind); + Handle<Code> ComputeCallField(int argc, + Code::Kind, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder, + int index); + + Handle<Code> ComputeCallConstant(int argc, + Code::Kind, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function); + + Handle<Code> ComputeCallInterceptor(int argc, + Code::Kind, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<Object> object, + Handle<JSObject> holder); + + Handle<Code> ComputeCallGlobal(int argc, + Code::Kind, + Code::ExtraICState extra_state, + Handle<String> name, + Handle<JSObject> receiver, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function); - Handle<Code> ComputeCallInitialize(int argc, - RelocInfo::Mode mode); + // --- + + Handle<Code> ComputeCallInitialize(int argc, RelocInfo::Mode mode); Handle<Code> ComputeKeyedCallInitialize(int argc); - MUST_USE_RESULT MaybeObject* ComputeCallPreMonomorphic( - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state); + Handle<Code> ComputeCallPreMonomorphic(int argc, + Code::Kind kind, + Code::ExtraICState extra_state); - MUST_USE_RESULT MaybeObject* ComputeCallNormal(int argc, - Code::Kind kind, - Code::ExtraICState state); + Handle<Code> ComputeCallNormal(int argc, + Code::Kind kind, + Code::ExtraICState state); - MUST_USE_RESULT MaybeObject* ComputeCallArguments(int argc, - Code::Kind kind); + Handle<Code> ComputeCallArguments(int argc, Code::Kind kind); - MUST_USE_RESULT MaybeObject* ComputeCallMegamorphic(int argc, - Code::Kind kind, - Code::ExtraICState state); + Handle<Code> ComputeCallMegamorphic(int argc, + Code::Kind kind, + Code::ExtraICState state); - MUST_USE_RESULT MaybeObject* ComputeCallMiss(int argc, - Code::Kind kind, - Code::ExtraICState state); + Handle<Code> ComputeCallMiss(int argc, + Code::Kind kind, + Code::ExtraICState state); // Finds the Code object stored in the Heap::non_monomorphic_cache(). - MUST_USE_RESULT Code* FindCallInitialize(int argc, - RelocInfo::Mode mode, - Code::Kind kind); + Code* FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind); #ifdef ENABLE_DEBUGGER_SUPPORT - MUST_USE_RESULT MaybeObject* ComputeCallDebugBreak(int argc, Code::Kind kind); + Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind); - MUST_USE_RESULT MaybeObject* ComputeCallDebugPrepareStepIn(int argc, - Code::Kind kind); + Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind); #endif // Update cache for entry hash(name, map). @@ -287,7 +248,8 @@ class StubCache { // Collect all maps that match the name and flags. void CollectMatchingMaps(SmallMapList* types, String* name, - Code::Flags flags); + Code::Flags flags, + Handle<Context> global_context); // Generate code for probing the stub cache table. // Arguments extra and extra2 may be used to pass additional scratch @@ -329,16 +291,14 @@ class StubCache { Isolate* isolate() { return isolate_; } Heap* heap() { return isolate()->heap(); } + Factory* factory() { return isolate()->factory(); } private: explicit StubCache(Isolate* isolate); - friend class Isolate; - friend class SCTableReference; - static const int kPrimaryTableSize = 2048; - static const int kSecondaryTableSize = 512; - Entry primary_[kPrimaryTableSize]; - Entry secondary_[kSecondaryTableSize]; + Handle<Code> ComputeCallInitialize(int argc, + RelocInfo::Mode mode, + Code::Kind kind); // Computes the hashed offsets for primary and secondary caches. static int PrimaryOffset(String* name, Code::Flags flags, Map* map) { @@ -383,8 +343,18 @@ class StubCache { reinterpret_cast<Address>(table) + (offset << shift_amount)); } + static const int kPrimaryTableBits = 11; + static const int kPrimaryTableSize = (1 << kPrimaryTableBits); + static const int kSecondaryTableBits = 9; + static const int kSecondaryTableSize = (1 << kSecondaryTableBits); + + Entry primary_[kPrimaryTableSize]; + Entry secondary_[kSecondaryTableSize]; Isolate* isolate_; + friend class Isolate; + friend class SCTableReference; + DISALLOW_COPY_AND_ASSIGN(StubCache); }; @@ -406,21 +376,24 @@ DECLARE_RUNTIME_FUNCTION(MaybeObject*, CallInterceptorProperty); DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor); -// The stub compiler compiles stubs for the stub cache. +// The stub compilers compile stubs for the stub cache. class StubCompiler BASE_EMBEDDED { public: - StubCompiler() - : scope_(), masm_(Isolate::Current(), NULL, 256), failure_(NULL) { } - - MUST_USE_RESULT MaybeObject* CompileCallInitialize(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallPreMonomorphic(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallNormal(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallMegamorphic(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallArguments(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallMiss(Code::Flags flags); + explicit StubCompiler(Isolate* isolate) + : isolate_(isolate), masm_(isolate, NULL, 256), failure_(NULL) { } + + // Functions to compile either CallIC or KeyedCallIC. The specific kind + // is extracted from the code flags. + Handle<Code> CompileCallInitialize(Code::Flags flags); + Handle<Code> CompileCallPreMonomorphic(Code::Flags flags); + Handle<Code> CompileCallNormal(Code::Flags flags); + Handle<Code> CompileCallMegamorphic(Code::Flags flags); + Handle<Code> CompileCallArguments(Code::Flags flags); + Handle<Code> CompileCallMiss(Code::Flags flags); + #ifdef ENABLE_DEBUGGER_SUPPORT - MUST_USE_RESULT MaybeObject* CompileCallDebugBreak(Code::Flags flags); - MUST_USE_RESULT MaybeObject* CompileCallDebugPrepareStepIn(Code::Flags flags); + Handle<Code> CompileCallDebugBreak(Code::Flags flags); + Handle<Code> CompileCallDebugPrepareStepIn(Code::Flags flags); #endif // Static functions for generating parts of stubs. @@ -440,8 +413,10 @@ class StubCompiler BASE_EMBEDDED { Label* miss); static void GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index); + Register dst, + Register src, + Handle<JSObject> holder, + int index); static void GenerateLoadArrayLength(MacroAssembler* masm, Register receiver, @@ -462,9 +437,9 @@ class StubCompiler BASE_EMBEDDED { Label* miss_label); static void GenerateStoreField(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, int index, - Map* transition, + Handle<Map> transition, Register receiver_reg, Register name_reg, Register scratch, @@ -490,88 +465,87 @@ class StubCompiler BASE_EMBEDDED { // The function can optionally (when save_at_depth != // kInvalidProtoDepth) save the object at the given depth by moving // it to [esp + kPointerSize]. - - Register CheckPrototypes(JSObject* object, + Register CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, Label* miss) { return CheckPrototypes(object, object_reg, holder, holder_reg, scratch1, scratch2, name, kInvalidProtoDepth, miss); } - Register CheckPrototypes(JSObject* object, + Register CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, int save_at_depth, Label* miss); protected: - MaybeObject* GetCodeWithFlags(Code::Flags flags, const char* name); - MaybeObject* GetCodeWithFlags(Code::Flags flags, String* name); + Handle<Code> GetCodeWithFlags(Code::Flags flags, const char* name); + Handle<Code> GetCodeWithFlags(Code::Flags flags, Handle<String> name); MacroAssembler* masm() { return &masm_; } void set_failure(Failure* failure) { failure_ = failure; } - void GenerateLoadField(JSObject* object, - JSObject* holder, + void GenerateLoadField(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, int index, - String* name, + Handle<String> name, Label* miss); - MaybeObject* GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss); + void GenerateLoadCallback(Handle<JSObject> object, + Handle<JSObject> holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss); - void GenerateLoadConstant(JSObject* object, - JSObject* holder, + void GenerateLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, - Object* value, - String* name, + Handle<JSFunction> value, + Handle<String> name, Label* miss); - void GenerateLoadInterceptor(JSObject* object, - JSObject* holder, + void GenerateLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, LookupResult* lookup, Register receiver, Register name_reg, Register scratch1, Register scratch2, Register scratch3, - String* name, + Handle<String> name, Label* miss); - static void LookupPostInterceptor(JSObject* holder, - String* name, + static void LookupPostInterceptor(Handle<JSObject> holder, + Handle<String> name, LookupResult* lookup); - Isolate* isolate() { return scope_.isolate(); } + Isolate* isolate() { return isolate_; } Heap* heap() { return isolate()->heap(); } Factory* factory() { return isolate()->factory(); } private: - HandleScope scope_; + Isolate* isolate_; MacroAssembler masm_; Failure* failure_; }; @@ -579,70 +553,75 @@ class StubCompiler BASE_EMBEDDED { class LoadStubCompiler: public StubCompiler { public: - MUST_USE_RESULT MaybeObject* CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last); - - MUST_USE_RESULT MaybeObject* CompileLoadField(JSObject* object, - JSObject* holder, - int index, - String* name); - - MUST_USE_RESULT MaybeObject* CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback); - - MUST_USE_RESULT MaybeObject* CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name); - - MUST_USE_RESULT MaybeObject* CompileLoadInterceptor(JSObject* object, - JSObject* holder, - String* name); - - MUST_USE_RESULT MaybeObject* CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete); + explicit LoadStubCompiler(Isolate* isolate) : StubCompiler(isolate) { } + + Handle<Code> CompileLoadNonexistent(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> last); + + Handle<Code> CompileLoadField(Handle<JSObject> object, + Handle<JSObject> holder, + int index, + Handle<String> name); + + Handle<Code> CompileLoadCallback(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback); + + Handle<Code> CompileLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value, + Handle<String> name); + + Handle<Code> CompileLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name); + + Handle<Code> CompileLoadGlobal(Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name, + bool is_dont_delete); private: - MUST_USE_RESULT MaybeObject* GetCode(PropertyType type, String* name); + Handle<Code> GetCode(PropertyType type, Handle<String> name); }; class KeyedLoadStubCompiler: public StubCompiler { public: - MUST_USE_RESULT MaybeObject* CompileLoadField(String* name, - JSObject* object, - JSObject* holder, - int index); + explicit KeyedLoadStubCompiler(Isolate* isolate) : StubCompiler(isolate) { } + + Handle<Code> CompileLoadField(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + int index); + + Handle<Code> CompileLoadCallback(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback); + + Handle<Code> CompileLoadConstant(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value); - MUST_USE_RESULT MaybeObject* CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback); + Handle<Code> CompileLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileLoadConstant(String* name, - JSObject* object, - JSObject* holder, - Object* value); + Handle<Code> CompileLoadArrayLength(Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileLoadInterceptor(JSObject* object, - JSObject* holder, - String* name); + Handle<Code> CompileLoadStringLength(Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileLoadArrayLength(String* name); - MUST_USE_RESULT MaybeObject* CompileLoadStringLength(String* name); - MUST_USE_RESULT MaybeObject* CompileLoadFunctionPrototype(String* name); + Handle<Code> CompileLoadFunctionPrototype(Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileLoadElement(Map* receiver_map); + Handle<Code> CompileLoadElement(Handle<Map> receiver_map); - MUST_USE_RESULT MaybeObject* CompileLoadMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics); + Handle<Code> CompileLoadPolymorphic(MapHandleList* receiver_maps, + CodeHandleList* handler_ics); static void GenerateLoadExternalArray(MacroAssembler* masm, ElementsKind elements_kind); @@ -654,34 +633,36 @@ class KeyedLoadStubCompiler: public StubCompiler { static void GenerateLoadDictionaryElement(MacroAssembler* masm); private: - MaybeObject* GetCode(PropertyType type, - String* name, + Handle<Code> GetCode(PropertyType type, + Handle<String> name, InlineCacheState state = MONOMORPHIC); }; class StoreStubCompiler: public StubCompiler { public: - explicit StoreStubCompiler(StrictModeFlag strict_mode) - : strict_mode_(strict_mode) { } + StoreStubCompiler(Isolate* isolate, StrictModeFlag strict_mode) + : StubCompiler(isolate), strict_mode_(strict_mode) { } + + + Handle<Code> CompileStoreField(Handle<JSObject> object, + int index, + Handle<Map> transition, + Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileStoreField(JSObject* object, - int index, - Map* transition, - String* name); + Handle<Code> CompileStoreCallback(Handle<JSObject> object, + Handle<AccessorInfo> callback, + Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileStoreCallback(JSObject* object, - AccessorInfo* callbacks, - String* name); - MUST_USE_RESULT MaybeObject* CompileStoreInterceptor(JSObject* object, - String* name); - MUST_USE_RESULT MaybeObject* CompileStoreGlobal(GlobalObject* object, - JSGlobalPropertyCell* holder, - String* name); + Handle<Code> CompileStoreInterceptor(Handle<JSObject> object, + Handle<String> name); + Handle<Code> CompileStoreGlobal(Handle<GlobalObject> object, + Handle<JSGlobalPropertyCell> holder, + Handle<String> name); private: - MaybeObject* GetCode(PropertyType type, String* name); + Handle<Code> GetCode(PropertyType type, Handle<String> name); StrictModeFlag strict_mode_; }; @@ -689,22 +670,23 @@ class StoreStubCompiler: public StubCompiler { class KeyedStoreStubCompiler: public StubCompiler { public: - explicit KeyedStoreStubCompiler(StrictModeFlag strict_mode) - : strict_mode_(strict_mode) { } + KeyedStoreStubCompiler(Isolate* isolate, StrictModeFlag strict_mode) + : StubCompiler(isolate), strict_mode_(strict_mode) { } - MUST_USE_RESULT MaybeObject* CompileStoreField(JSObject* object, - int index, - Map* transition, - String* name); + Handle<Code> CompileStoreField(Handle<JSObject> object, + int index, + Handle<Map> transition, + Handle<String> name); - MUST_USE_RESULT MaybeObject* CompileStoreElement(Map* receiver_map); + Handle<Code> CompileStoreElement(Handle<Map> receiver_map); - MUST_USE_RESULT MaybeObject* CompileStoreMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics); + Handle<Code> CompileStorePolymorphic(MapHandleList* receiver_maps, + CodeHandleList* handler_stubs, + MapHandleList* transitioned_maps); static void GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array); + bool is_js_array, + ElementsKind element_kind); static void GenerateStoreFastDoubleElement(MacroAssembler* masm, bool is_js_array); @@ -715,8 +697,8 @@ class KeyedStoreStubCompiler: public StubCompiler { static void GenerateStoreDictionaryElement(MacroAssembler* masm); private: - MaybeObject* GetCode(PropertyType type, - String* name, + Handle<Code> GetCode(PropertyType type, + Handle<String> name, InlineCacheState state = MONOMORPHIC); StrictModeFlag strict_mode_; @@ -739,105 +721,97 @@ class CallOptimization; class CallStubCompiler: public StubCompiler { public: - CallStubCompiler(int argc, + CallStubCompiler(Isolate* isolate, + int argc, Code::Kind kind, - Code::ExtraICState extra_ic_state, + Code::ExtraICState extra_state, InlineCacheHolderFlag cache_holder); - MUST_USE_RESULT MaybeObject* CompileCallField( - JSObject* object, - JSObject* holder, - int index, - String* name); - - MUST_USE_RESULT MaybeObject* CompileCallConstant( - Object* object, - JSObject* holder, - JSFunction* function, - String* name, - CheckType check); - - MUST_USE_RESULT MaybeObject* CompileCallInterceptor( - JSObject* object, - JSObject* holder, - String* name); - - MUST_USE_RESULT MaybeObject* CompileCallGlobal( - JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name); - - static bool HasCustomCallGenerator(JSFunction* function); + Handle<Code> CompileCallField(Handle<JSObject> object, + Handle<JSObject> holder, + int index, + Handle<String> name); + + Handle<Code> CompileCallConstant(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function, + Handle<String> name, + CheckType check); + + Handle<Code> CompileCallInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name); + + Handle<Code> CompileCallGlobal(Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name); + + static bool HasCustomCallGenerator(Handle<JSFunction> function); private: - // Compiles a custom call constant/global IC. For constant calls - // cell is NULL. Returns undefined if there is no custom call code - // for the given function or it can't be generated. - MUST_USE_RESULT MaybeObject* CompileCustomCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name); - -#define DECLARE_CALL_GENERATOR(name) \ - MUST_USE_RESULT MaybeObject* Compile##name##Call(Object* object, \ - JSObject* holder, \ - JSGlobalPropertyCell* cell, \ - JSFunction* function, \ - String* fname); + // Compiles a custom call constant/global IC. For constant calls cell is + // NULL. Returns an empty handle if there is no custom call code for the + // given function. + Handle<Code> CompileCustomCall(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name); + +#define DECLARE_CALL_GENERATOR(name) \ + Handle<Code> Compile##name##Call(Handle<Object> object, \ + Handle<JSObject> holder, \ + Handle<JSGlobalPropertyCell> cell, \ + Handle<JSFunction> function, \ + Handle<String> fname); CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR) #undef DECLARE_CALL_GENERATOR - MUST_USE_RESULT MaybeObject* CompileFastApiCall( - const CallOptimization& optimization, - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name); + Handle<Code> CompileFastApiCall(const CallOptimization& optimization, + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name); - const ParameterCount arguments_; - const Code::Kind kind_; - const Code::ExtraICState extra_ic_state_; - const InlineCacheHolderFlag cache_holder_; + Handle<Code> GetCode(PropertyType type, Handle<String> name); + Handle<Code> GetCode(Handle<JSFunction> function); const ParameterCount& arguments() { return arguments_; } - MUST_USE_RESULT MaybeObject* GetCode(PropertyType type, String* name); - - // Convenience function. Calls GetCode above passing - // CONSTANT_FUNCTION type and the name of the given function. - MUST_USE_RESULT MaybeObject* GetCode(JSFunction* function); + void GenerateNameCheck(Handle<String> name, Label* miss); - void GenerateNameCheck(String* name, Label* miss); - - void GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, + void GenerateGlobalReceiverCheck(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, Label* miss); // Generates code to load the function from the cell checking that // it still contains the same function. - void GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, + void GenerateLoadFunctionFromCell(Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, Label* miss); - // Generates a jump to CallIC miss stub. Returns Failure if the jump cannot - // be generated. - MUST_USE_RESULT MaybeObject* GenerateMissBranch(); + // Generates a jump to CallIC miss stub. + void GenerateMissBranch(); + + const ParameterCount arguments_; + const Code::Kind kind_; + const Code::ExtraICState extra_state_; + const InlineCacheHolderFlag cache_holder_; }; class ConstructStubCompiler: public StubCompiler { public: - explicit ConstructStubCompiler() {} + explicit ConstructStubCompiler(Isolate* isolate) : StubCompiler(isolate) { } - MUST_USE_RESULT MaybeObject* CompileConstructStub(JSFunction* function); + Handle<Code> CompileConstructStub(Handle<JSFunction> function); private: - MaybeObject* GetCode(); + Handle<Code> GetCode(); }; @@ -846,14 +820,14 @@ class CallOptimization BASE_EMBEDDED { public: explicit CallOptimization(LookupResult* lookup); - explicit CallOptimization(JSFunction* function); + explicit CallOptimization(Handle<JSFunction> function); bool is_constant_call() const { - return constant_function_ != NULL; + return !constant_function_.is_null(); } - JSFunction* constant_function() const { - ASSERT(constant_function_ != NULL); + Handle<JSFunction> constant_function() const { + ASSERT(is_constant_call()); return constant_function_; } @@ -861,32 +835,32 @@ class CallOptimization BASE_EMBEDDED { return is_simple_api_call_; } - FunctionTemplateInfo* expected_receiver_type() const { - ASSERT(is_simple_api_call_); + Handle<FunctionTemplateInfo> expected_receiver_type() const { + ASSERT(is_simple_api_call()); return expected_receiver_type_; } - CallHandlerInfo* api_call_info() const { - ASSERT(is_simple_api_call_); + Handle<CallHandlerInfo> api_call_info() const { + ASSERT(is_simple_api_call()); return api_call_info_; } // Returns the depth of the object having the expected type in the // prototype chain between the two arguments. - int GetPrototypeDepthOfExpectedType(JSObject* object, - JSObject* holder) const; + int GetPrototypeDepthOfExpectedType(Handle<JSObject> object, + Handle<JSObject> holder) const; private: - void Initialize(JSFunction* function); + void Initialize(Handle<JSFunction> function); // Determines whether the given function can be called using the // fast api call builtin. - void AnalyzePossibleApiFunction(JSFunction* function); + void AnalyzePossibleApiFunction(Handle<JSFunction> function); - JSFunction* constant_function_; + Handle<JSFunction> constant_function_; bool is_simple_api_call_; - FunctionTemplateInfo* expected_receiver_type_; - CallHandlerInfo* api_call_info_; + Handle<FunctionTemplateInfo> expected_receiver_type_; + Handle<CallHandlerInfo> api_call_info_; }; diff --git a/deps/v8/src/token.h b/deps/v8/src/token.h index eb825c1a74..b305c88a30 100644 --- a/deps/v8/src/token.h +++ b/deps/v8/src/token.h @@ -73,6 +73,7 @@ namespace internal { T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \ T(INIT_LET, "=init_let", 2) /* AST-use only. */ \ T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ + T(INIT_CONST_HARMONY, "=init_const_harmony", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ T(ASSIGN_BIT_OR, "|=", 2) \ T(ASSIGN_BIT_XOR, "^=", 2) \ @@ -169,7 +170,10 @@ namespace internal { T(FUTURE_RESERVED_WORD, NULL, 0) \ T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ K(CONST, "const", 0) \ + K(EXPORT, "export", 0) \ + K(IMPORT, "import", 0) \ K(LET, "let", 0) \ + K(MODULE, "module", 0) \ \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ @@ -216,6 +220,10 @@ class Token { return op == LT || op == LTE || op == GT || op == GTE; } + static bool IsEqualityOp(Value op) { + return op == EQ || op == EQ_STRICT; + } + static Value NegateCompareOp(Value op) { ASSERT(IsCompareOp(op)); switch (op) { diff --git a/deps/v8/src/type-info.cc b/deps/v8/src/type-info.cc index 4df7ece086..e663998ccb 100644 --- a/deps/v8/src/type-info.cc +++ b/deps/v8/src/type-info.cc @@ -60,8 +60,10 @@ TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) { TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code, - Handle<Context> global_context) { + Handle<Context> global_context, + Isolate* isolate) { global_context_ = global_context; + isolate_ = isolate; BuildDictionary(code); ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue); } @@ -71,29 +73,30 @@ Handle<Object> TypeFeedbackOracle::GetInfo(unsigned ast_id) { int entry = dictionary_->FindEntry(ast_id); return entry != UnseededNumberDictionary::kNotFound ? Handle<Object>(dictionary_->ValueAt(entry)) - : Isolate::Current()->factory()->undefined_value(); + : Handle<Object>::cast(isolate_->factory()->undefined_value()); } bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) { - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsMap()) return true; if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); return code->is_keyed_load_stub() && code->ic_state() == MONOMORPHIC && Code::ExtractTypeFromFlags(code->flags()) == NORMAL && - code->FindFirstMap() != NULL; + code->FindFirstMap() != NULL && + !CanRetainOtherContext(code->FindFirstMap(), *global_context_); } return false; } bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) { - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); - Builtins* builtins = Isolate::Current()->builtins(); + Builtins* builtins = isolate_->builtins(); return code->is_keyed_load_stub() && *code != builtins->builtin(Builtins::kKeyedLoadIC_Generic) && code->ic_state() == MEGAMORPHIC; @@ -103,23 +106,25 @@ bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) { bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) { - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsMap()) return true; if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); return code->is_keyed_store_stub() && code->ic_state() == MONOMORPHIC && - Code::ExtractTypeFromFlags(code->flags()) == NORMAL; + Code::ExtractTypeFromFlags(code->flags()) == NORMAL && + code->FindFirstMap() != NULL && + !CanRetainOtherContext(code->FindFirstMap(), *global_context_); } return false; } bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) { - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); - Builtins* builtins = Isolate::Current()->builtins(); + Builtins* builtins = isolate_->builtins(); return code->is_keyed_store_stub() && *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic) && *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic_Strict) && @@ -131,18 +136,26 @@ bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) { bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) { Handle<Object> value = GetInfo(expr->id()); - return value->IsMap() || value->IsSmi(); + return value->IsMap() || value->IsSmi() || value->IsJSFunction(); +} + + +bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) { + Handle<Object> value = GetInfo(expr->id()); + return value->IsJSFunction(); } Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) { ASSERT(LoadIsMonomorphicNormal(expr)); - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); Map* first_map = code->FindFirstMap(); ASSERT(first_map != NULL); - return Handle<Map>(first_map); + return CanRetainOtherContext(first_map, *global_context_) + ? Handle<Map>::null() + : Handle<Map>(first_map); } return Handle<Map>::cast(map_or_code); } @@ -150,10 +163,14 @@ Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) { Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) { ASSERT(StoreIsMonomorphicNormal(expr)); - Handle<Object> map_or_code(GetInfo(expr->id())); + Handle<Object> map_or_code = GetInfo(expr->id()); if (map_or_code->IsCode()) { Handle<Code> code = Handle<Code>::cast(map_or_code); - return Handle<Map>(code->FindFirstMap()); + Map* first_map = code->FindFirstMap(); + ASSERT(first_map != NULL); + return CanRetainOtherContext(first_map, *global_context_) + ? Handle<Map>::null() + : Handle<Map>(first_map); } return Handle<Map>::cast(map_or_code); } @@ -203,6 +220,7 @@ CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) { return check; } + Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck( CheckType check) { JSFunction* function = NULL; @@ -225,9 +243,14 @@ Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck( } +Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) { + return Handle<JSFunction>::cast(GetInfo(expr->id())); +} + + bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) { return *GetInfo(expr->id()) == - Isolate::Current()->builtins()->builtin(id); + isolate_->builtins()->builtin(id); } @@ -251,6 +274,7 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) { case CompareIC::STRINGS: return TypeInfo::String(); case CompareIC::OBJECTS: + case CompareIC::KNOWN_OBJECTS: // TODO(kasperl): We really need a type for JS objects here. return TypeInfo::NonPrimitive(); case CompareIC::GENERIC: @@ -270,6 +294,23 @@ bool TypeFeedbackOracle::IsSymbolCompare(CompareOperation* expr) { } +Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) { + Handle<Object> object = GetInfo(expr->id()); + if (!object->IsCode()) return Handle<Map>::null(); + Handle<Code> code = Handle<Code>::cast(object); + if (!code->is_compare_ic_stub()) return Handle<Map>::null(); + CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); + if (state != CompareIC::KNOWN_OBJECTS) { + return Handle<Map>::null(); + } + Map* first_map = code->FindFirstMap(); + ASSERT(first_map != NULL); + return CanRetainOtherContext(first_map, *global_context_) + ? Handle<Map>::null() + : Handle<Map>(first_map); +} + + TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) { Handle<Object> object = GetInfo(expr->id()); TypeInfo unknown = TypeInfo::Unknown(); @@ -352,9 +393,14 @@ TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) { return unknown; case CompareIC::SMIS: return TypeInfo::Smi(); + case CompareIC::STRINGS: + return TypeInfo::String(); + case CompareIC::SYMBOLS: + return TypeInfo::Symbol(); case CompareIC::HEAP_NUMBERS: return TypeInfo::Number(); case CompareIC::OBJECTS: + case CompareIC::KNOWN_OBJECTS: // TODO(kasperl): We really need a type for JS objects here. return TypeInfo::NonPrimitive(); case CompareIC::GENERIC: @@ -397,21 +443,67 @@ void TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id, Handle<String> name, Code::Flags flags, SmallMapList* types) { - Isolate* isolate = Isolate::Current(); Handle<Object> object = GetInfo(ast_id); if (object->IsUndefined() || object->IsSmi()) return; - if (*object == isolate->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) { + if (*object == + isolate_->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) { // TODO(fschneider): We could collect the maps and signal that // we need a generic store (or load) here. ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC); } else if (object->IsMap()) { types->Add(Handle<Map>::cast(object)); - } else if (Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) { + } else if (FLAG_collect_megamorphic_maps_from_stub_cache && + Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) { types->Reserve(4); ASSERT(object->IsCode()); - isolate->stub_cache()->CollectMatchingMaps(types, *name, flags); + isolate_->stub_cache()->CollectMatchingMaps(types, + *name, + flags, + global_context_); + } +} + + +// Check if a map originates from a given global context. We use this +// information to filter out maps from different context to avoid +// retaining objects from different tabs in Chrome via optimized code. +bool TypeFeedbackOracle::CanRetainOtherContext(Map* map, + Context* global_context) { + Object* constructor = NULL; + while (!map->prototype()->IsNull()) { + constructor = map->constructor(); + if (!constructor->IsNull()) { + // If the constructor is not null or a JSFunction, we have to + // conservatively assume that it may retain a global context. + if (!constructor->IsJSFunction()) return true; + // Check if the constructor directly references a foreign context. + if (CanRetainOtherContext(JSFunction::cast(constructor), + global_context)) { + return true; + } + } + map = HeapObject::cast(map->prototype())->map(); } + constructor = map->constructor(); + if (constructor->IsNull()) return false; + JSFunction* function = JSFunction::cast(constructor); + return CanRetainOtherContext(function, global_context); +} + + +bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function, + Context* global_context) { + return function->context()->global() != global_context->global() + && function->context()->global() != global_context->builtins(); +} + + +static void AddMapIfMissing(Handle<Map> map, SmallMapList* list) { + for (int i = 0; i < list->length(); ++i) { + if (list->at(i).is_identical_to(map)) return; + } + list->Add(map); } @@ -428,7 +520,10 @@ void TypeFeedbackOracle::CollectKeyedReceiverTypes(unsigned ast_id, RelocInfo* info = it.rinfo(); Object* object = info->target_object(); if (object->IsMap()) { - types->Add(Handle<Map>(Map::cast(object))); + Map* map = Map::cast(object); + if (!CanRetainOtherContext(map, *global_context_)) { + AddMapIfMissing(Handle<Map>(map), types); + } } } } @@ -452,6 +547,7 @@ void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) { GetRelocInfos(code, &infos); CreateDictionary(code, &infos); ProcessRelocInfos(&infos); + ProcessTypeFeedbackCells(code); // Allocate handle in the parent scope. dictionary_ = scope.CloseAndEscape(dictionary_); } @@ -469,8 +565,9 @@ void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code, void TypeFeedbackOracle::CreateDictionary(Handle<Code> code, ZoneList<RelocInfo>* infos) { DisableAssertNoAllocation allocation_allowed; + int length = infos->length() + code->type_feedback_cells()->CellCount(); byte* old_start = code->instruction_start(); - dictionary_ = FACTORY->NewUnseededNumberDictionary(infos->length()); + dictionary_ = FACTORY->NewUnseededNumberDictionary(length); byte* new_start = code->instruction_start(); RelocateRelocInfos(infos, old_start, new_start); } @@ -488,49 +585,65 @@ void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos, void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) { for (int i = 0; i < infos->length(); i++) { + RelocInfo reloc_entry = (*infos)[i]; + Address target_address = reloc_entry.target_address(); unsigned ast_id = static_cast<unsigned>((*infos)[i].data()); - Code* target = Code::GetCodeFromTargetAddress((*infos)[i].target_address()); - ProcessTarget(ast_id, target); - } -} - + Code* target = Code::GetCodeFromTargetAddress(target_address); + switch (target->kind()) { + case Code::LOAD_IC: + case Code::STORE_IC: + case Code::CALL_IC: + case Code::KEYED_CALL_IC: + if (target->ic_state() == MONOMORPHIC) { + if (target->kind() == Code::CALL_IC && + target->check_type() != RECEIVER_MAP_CHECK) { + SetInfo(ast_id, Smi::FromInt(target->check_type())); + } else { + Object* map = target->FindFirstMap(); + if (map == NULL) { + SetInfo(ast_id, static_cast<Object*>(target)); + } else if (!CanRetainOtherContext(Map::cast(map), + *global_context_)) { + SetInfo(ast_id, map); + } + } + } else if (target->ic_state() == MEGAMORPHIC) { + SetInfo(ast_id, target); + } + break; -void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) { - switch (target->kind()) { - case Code::LOAD_IC: - case Code::STORE_IC: - case Code::CALL_IC: - case Code::KEYED_CALL_IC: - if (target->ic_state() == MONOMORPHIC) { - if (target->kind() == Code::CALL_IC && - target->check_type() != RECEIVER_MAP_CHECK) { - SetInfo(ast_id, Smi::FromInt(target->check_type())); - } else { - Object* map = target->FindFirstMap(); - SetInfo(ast_id, map == NULL ? static_cast<Object*>(target) : map); + case Code::KEYED_LOAD_IC: + case Code::KEYED_STORE_IC: + if (target->ic_state() == MONOMORPHIC || + target->ic_state() == MEGAMORPHIC) { + SetInfo(ast_id, target); } - } else if (target->ic_state() == MEGAMORPHIC) { - SetInfo(ast_id, target); - } - break; + break; - case Code::KEYED_LOAD_IC: - case Code::KEYED_STORE_IC: - if (target->ic_state() == MONOMORPHIC || - target->ic_state() == MEGAMORPHIC) { + case Code::UNARY_OP_IC: + case Code::BINARY_OP_IC: + case Code::COMPARE_IC: + case Code::TO_BOOLEAN_IC: SetInfo(ast_id, target); - } - break; + break; - case Code::UNARY_OP_IC: - case Code::BINARY_OP_IC: - case Code::COMPARE_IC: - case Code::TO_BOOLEAN_IC: - SetInfo(ast_id, target); - break; + default: + break; + } + } +} - default: - break; + +void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) { + Handle<TypeFeedbackCells> cache(code->type_feedback_cells()); + for (int i = 0; i < cache->CellCount(); i++) { + unsigned ast_id = cache->AstId(i)->value(); + Object* value = cache->Cell(i)->value(); + if (value->IsJSFunction() && + !CanRetainOtherContext(JSFunction::cast(value), + *global_context_)) { + SetInfo(ast_id, value); + } } } diff --git a/deps/v8/src/type-info.h b/deps/v8/src/type-info.h index a0317402b0..9b8b431cde 100644 --- a/deps/v8/src/type-info.h +++ b/deps/v8/src/type-info.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -64,6 +64,8 @@ class TypeInfo { static TypeInfo Integer32() { return TypeInfo(kInteger32); } // We know it's a Smi. static TypeInfo Smi() { return TypeInfo(kSmi); } + // We know it's a Symbol. + static TypeInfo Symbol() { return TypeInfo(kSymbol); } // We know it's a heap number. static TypeInfo Double() { return TypeInfo(kDouble); } // We know it's a string. @@ -137,6 +139,16 @@ class TypeInfo { return ((type_ & kSmi) == kSmi); } + inline bool IsSymbol() { + ASSERT(type_ != kUninitialized); + return ((type_ & kSymbol) == kSymbol); + } + + inline bool IsNonSymbol() { + ASSERT(type_ != kUninitialized); + return ((type_ & kSymbol) == kString); + } + inline bool IsInteger32() { ASSERT(type_ != kUninitialized); return ((type_ & kInteger32) == kInteger32); @@ -168,6 +180,7 @@ class TypeInfo { case kNumber: return "Number"; case kInteger32: return "Integer32"; case kSmi: return "Smi"; + case kSymbol: return "Symbol"; case kDouble: return "Double"; case kString: return "String"; case kNonPrimitive: return "Object"; @@ -186,6 +199,7 @@ class TypeInfo { kSmi = 0x17, // 0010111 kDouble = 0x19, // 0011001 kString = 0x30, // 0110000 + kSymbol = 0x32, // 0110010 kNonPrimitive = 0x40, // 1000000 kUninitialized = 0x7f // 1111111 }; @@ -205,10 +219,12 @@ enum StringStubFeedback { class Assignment; class BinaryOperation; class Call; +class CallNew; class CaseClause; class CompareOperation; class CompilationInfo; class CountOperation; +class Expression; class Property; class SmallMapList; class UnaryOperation; @@ -216,13 +232,16 @@ class UnaryOperation; class TypeFeedbackOracle BASE_EMBEDDED { public: - TypeFeedbackOracle(Handle<Code> code, Handle<Context> global_context); + TypeFeedbackOracle(Handle<Code> code, + Handle<Context> global_context, + Isolate* isolate); bool LoadIsMonomorphicNormal(Property* expr); bool LoadIsMegamorphicWithTypeInfo(Property* expr); bool StoreIsMonomorphicNormal(Expression* expr); bool StoreIsMegamorphicWithTypeInfo(Expression* expr); bool CallIsMonomorphic(Call* expr); + bool CallNewIsMonomorphic(CallNew* expr); Handle<Map> LoadMonomorphicReceiverType(Property* expr); Handle<Map> StoreMonomorphicReceiverType(Expression* expr); @@ -240,9 +259,15 @@ class TypeFeedbackOracle BASE_EMBEDDED { void CollectKeyedReceiverTypes(unsigned ast_id, SmallMapList* types); + static bool CanRetainOtherContext(Map* map, Context* global_context); + static bool CanRetainOtherContext(JSFunction* function, + Context* global_context); + CheckType GetCallCheckType(Call* expr); Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check); + Handle<JSFunction> GetCallTarget(Call* expr); + bool LoadIsBuiltin(Property* expr, Builtins::Name id); // TODO(1571) We can't use ToBooleanStub::Types as the return value because @@ -255,6 +280,7 @@ class TypeFeedbackOracle BASE_EMBEDDED { TypeInfo BinaryType(BinaryOperation* expr); TypeInfo CompareType(CompareOperation* expr); bool IsSymbolCompare(CompareOperation* expr); + Handle<Map> GetCompareMap(CompareOperation* expr); TypeInfo SwitchType(CaseClause* clause); TypeInfo IncrementType(CountOperation* expr); @@ -273,13 +299,14 @@ class TypeFeedbackOracle BASE_EMBEDDED { byte* old_start, byte* new_start); void ProcessRelocInfos(ZoneList<RelocInfo>* infos); - void ProcessTarget(unsigned ast_id, Code* target); + void ProcessTypeFeedbackCells(Handle<Code> code); // Returns an element from the backing store. Returns undefined if // there is no information. Handle<Object> GetInfo(unsigned ast_id); Handle<Context> global_context_; + Isolate* isolate_; Handle<UnseededNumberDictionary> dictionary_; DISALLOW_COPY_AND_ASSIGN(TypeFeedbackOracle); diff --git a/deps/v8/src/unicode.cc b/deps/v8/src/unicode.cc index 6e0ac1a357..147f716c46 100644 --- a/deps/v8/src/unicode.cc +++ b/deps/v8/src/unicode.cc @@ -210,7 +210,7 @@ static int LookupMapping(const int32_t* table, uchar Utf8::CalculateValue(const byte* str, unsigned length, unsigned* cursor) { - // We only get called for non-ascii characters. + // We only get called for non-ASCII characters. if (length == 1) { *cursor += 1; return kBadChar; @@ -286,8 +286,8 @@ const byte* Utf8::ReadBlock(Buffer<const char*> str, byte* buffer, } const byte* data = reinterpret_cast<const byte*>(str.data()); if (data[offset] <= kMaxOneByteChar) { - // The next character is an ascii char so we scan forward over - // the following ascii characters and return the next pure ascii + // The next character is an ASCII char so we scan forward over + // the following ASCII characters and return the next pure ASCII // substring const byte* result = data + offset; offset++; @@ -297,13 +297,13 @@ const byte* Utf8::ReadBlock(Buffer<const char*> str, byte* buffer, *offset_ptr = offset; return result; } else { - // The next character is non-ascii so we just fill the buffer + // The next character is non-ASCII so we just fill the buffer unsigned cursor = 0; unsigned chars_read = 0; while (offset < str.length()) { uchar c = data[offset]; if (c <= kMaxOneByteChar) { - // Fast case for ascii characters + // Fast case for ASCII characters if (!CharacterStream::EncodeAsciiCharacter(c, buffer, capacity, diff --git a/deps/v8/src/unicode.h b/deps/v8/src/unicode.h index 39fc349687..fb9e6339e1 100644 --- a/deps/v8/src/unicode.h +++ b/deps/v8/src/unicode.h @@ -1,4 +1,4 @@ -// Copyright 2007-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -44,7 +44,7 @@ typedef unsigned char byte; * The max length of the result of converting the case of a single * character. */ -static const int kMaxMappingSize = 4; +const int kMaxMappingSize = 4; template <class T, int size = 256> class Predicate { diff --git a/deps/v8/src/uri.js b/deps/v8/src/uri.js index c910d756b4..e76104a707 100644 --- a/deps/v8/src/uri.js +++ b/deps/v8/src/uri.js @@ -111,47 +111,59 @@ function URIDecodeOctets(octets, result, index) { var o1 = octets[1]; if (o0 < 0xe0) { var a = o0 & 0x1f; - if ((o1 < 0x80) || (o1 > 0xbf)) + if ((o1 < 0x80) || (o1 > 0xbf)) { throw new $URIError("URI malformed"); + } var b = o1 & 0x3f; value = (a << 6) + b; - if (value < 0x80 || value > 0x7ff) + if (value < 0x80 || value > 0x7ff) { throw new $URIError("URI malformed"); + } } else { var o2 = octets[2]; if (o0 < 0xf0) { var a = o0 & 0x0f; - if ((o1 < 0x80) || (o1 > 0xbf)) + if ((o1 < 0x80) || (o1 > 0xbf)) { throw new $URIError("URI malformed"); + } var b = o1 & 0x3f; - if ((o2 < 0x80) || (o2 > 0xbf)) + if ((o2 < 0x80) || (o2 > 0xbf)) { throw new $URIError("URI malformed"); + } var c = o2 & 0x3f; value = (a << 12) + (b << 6) + c; - if ((value < 0x800) || (value > 0xffff)) + if ((value < 0x800) || (value > 0xffff)) { throw new $URIError("URI malformed"); + } } else { var o3 = octets[3]; if (o0 < 0xf8) { var a = (o0 & 0x07); - if ((o1 < 0x80) || (o1 > 0xbf)) + if ((o1 < 0x80) || (o1 > 0xbf)) { throw new $URIError("URI malformed"); + } var b = (o1 & 0x3f); - if ((o2 < 0x80) || (o2 > 0xbf)) + if ((o2 < 0x80) || (o2 > 0xbf)) { throw new $URIError("URI malformed"); + } var c = (o2 & 0x3f); - if ((o3 < 0x80) || (o3 > 0xbf)) + if ((o3 < 0x80) || (o3 > 0xbf)) { throw new $URIError("URI malformed"); + } var d = (o3 & 0x3f); value = (a << 18) + (b << 12) + (c << 6) + d; - if ((value < 0x10000) || (value > 0x10ffff)) + if ((value < 0x10000) || (value > 0x10ffff)) { throw new $URIError("URI malformed"); + } } else { throw new $URIError("URI malformed"); } } } } + if (0xD800 <= value && value <= 0xDFFF) { + throw new $URIError("URI malformed"); + } if (value < 0x10000) { result[index++] = value; return index; @@ -207,14 +219,15 @@ function Decode(uri, reserved) { var cc = URIHexCharsToCharCode(uri.charCodeAt(++k), uri.charCodeAt(++k)); if (cc >> 7) { var n = 0; - while (((cc << ++n) & 0x80) != 0) ; + while (((cc << ++n) & 0x80) != 0) { } if (n == 1 || n > 4) throw new $URIError("URI malformed"); var octets = new $Array(n); octets[0] = cc; if (k + 3 * (n - 1) >= uriLength) throw new $URIError("URI malformed"); for (var i = 1; i < n; i++) { if (uri.charAt(++k) != '%') throw new $URIError("URI malformed"); - octets[i] = URIHexCharsToCharCode(uri.charCodeAt(++k), uri.charCodeAt(++k)); + octets[i] = URIHexCharsToCharCode(uri.charCodeAt(++k), + uri.charCodeAt(++k)); } index = URIDecodeOctets(octets, result, index); } else { @@ -254,7 +267,7 @@ function URIDecode(uri) { if (63 <= cc && cc <= 64) return true; return false; - }; + } var string = ToString(uri); return Decode(string, reservedPredicate); } @@ -262,7 +275,7 @@ function URIDecode(uri) { // ECMA-262 - 15.1.3.2. function URIDecodeComponent(component) { - function reservedPredicate(cc) { return false; }; + function reservedPredicate(cc) { return false; } var string = ToString(component); return Decode(string, reservedPredicate); } @@ -303,7 +316,7 @@ function URIEncode(uri) { if (cc == 126) return true; return false; - }; + } var string = ToString(uri); return Encode(string, unescapePredicate); @@ -326,7 +339,7 @@ function URIEncodeComponent(component) { if (cc == 126) return true; return false; - }; + } var string = ToString(component); return Encode(string, unescapePredicate); @@ -366,7 +379,9 @@ function CharCodeToHex4Str(cc) { function IsValidHex(s) { for (var i = 0; i < s.length; ++i) { var cc = s.charCodeAt(i); - if ((48 <= cc && cc <= 57) || (65 <= cc && cc <= 70) || (97 <= cc && cc <= 102)) { + if ((48 <= cc && cc <= 57) || + (65 <= cc && cc <= 70) || + (97 <= cc && cc <= 102)) { // '0'..'9', 'A'..'F' and 'a' .. 'f'. } else { return false; diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index cf7819e4a2..1d40c98b9e 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -47,13 +47,13 @@ namespace internal { // Returns true iff x is a power of 2 (or zero). Cannot be used with the // maximally negative value of the type T (the -1 overflows). template <typename T> -static inline bool IsPowerOf2(T x) { +inline bool IsPowerOf2(T x) { return IS_POWER_OF_TWO(x); } // X must be a power of 2. Returns the number of trailing zeros. -static inline int WhichPowerOf2(uint32_t x) { +inline int WhichPowerOf2(uint32_t x) { ASSERT(IsPowerOf2(x)); ASSERT(x != 0); int bits = 0; @@ -88,7 +88,7 @@ static inline int WhichPowerOf2(uint32_t x) { // The C++ standard leaves the semantics of '>>' undefined for // negative signed operands. Most implementations do the right thing, // though. -static inline int ArithmeticShiftRight(int x, int s) { +inline int ArithmeticShiftRight(int x, int s) { return x >> s; } @@ -97,7 +97,7 @@ static inline int ArithmeticShiftRight(int x, int s) { // This allows conversion of Addresses and integral types into // 0-relative int offsets. template <typename T> -static inline intptr_t OffsetFrom(T x) { +inline intptr_t OffsetFrom(T x) { return x - static_cast<T>(0); } @@ -106,14 +106,14 @@ static inline intptr_t OffsetFrom(T x) { // This allows conversion of 0-relative int offsets into Addresses and // integral types. template <typename T> -static inline T AddressFrom(intptr_t x) { +inline T AddressFrom(intptr_t x) { return static_cast<T>(static_cast<T>(0) + x); } // Return the largest multiple of m which is <= x. template <typename T> -static inline T RoundDown(T x, int m) { +inline T RoundDown(T x, intptr_t m) { ASSERT(IsPowerOf2(m)); return AddressFrom<T>(OffsetFrom(x) & -m); } @@ -121,13 +121,13 @@ static inline T RoundDown(T x, int m) { // Return the smallest multiple of m which is >= x. template <typename T> -static inline T RoundUp(T x, int m) { - return RoundDown(x + m - 1, m); +inline T RoundUp(T x, intptr_t m) { + return RoundDown<T>(static_cast<T>(x + m - 1), m); } template <typename T> -static int Compare(const T& a, const T& b) { +int Compare(const T& a, const T& b) { if (a == b) return 0; else if (a < b) @@ -138,16 +138,26 @@ static int Compare(const T& a, const T& b) { template <typename T> -static int PointerValueCompare(const T* a, const T* b) { +int PointerValueCompare(const T* a, const T* b) { return Compare<T>(*a, *b); } +// Compare function to compare the object pointer value of two +// handlified objects. The handles are passed as pointers to the +// handles. +template<typename T> class Handle; // Forward declaration. +template <typename T> +int HandleObjectPointerCompare(const Handle<T>* a, const Handle<T>* b) { + return Compare<T*>(*(*a), *(*b)); +} + + // Returns the smallest power of two which is >= x. If you pass in a // number that is already a power of two, it is returned as is. // Implementation is from "Hacker's Delight" by Henry S. Warren, Jr., // figure 3-3, page 48, where the function is called clp2. -static inline uint32_t RoundUpToPowerOf2(uint32_t x) { +inline uint32_t RoundUpToPowerOf2(uint32_t x) { ASSERT(x <= 0x80000000u); x = x - 1; x = x | (x >> 1); @@ -159,18 +169,23 @@ static inline uint32_t RoundUpToPowerOf2(uint32_t x) { } +inline uint32_t RoundDownToPowerOf2(uint32_t x) { + uint32_t rounded_up = RoundUpToPowerOf2(x); + if (rounded_up > x) return rounded_up >> 1; + return rounded_up; +} -template <typename T> -static inline bool IsAligned(T value, T alignment) { - ASSERT(IsPowerOf2(alignment)); + +template <typename T, typename U> +inline bool IsAligned(T value, U alignment) { return (value & (alignment - 1)) == 0; } // Returns true if (addr + offset) is aligned. -static inline bool IsAddressAligned(Address addr, - intptr_t alignment, - int offset) { +inline bool IsAddressAligned(Address addr, + intptr_t alignment, + int offset = 0) { intptr_t offs = OffsetFrom(addr + offset); return IsAligned(offs, alignment); } @@ -178,14 +193,14 @@ static inline bool IsAddressAligned(Address addr, // Returns the maximum of the two parameters. template <typename T> -static T Max(T a, T b) { +T Max(T a, T b) { return a < b ? b : a; } // Returns the minimum of the two parameters. template <typename T> -static T Min(T a, T b) { +T Min(T a, T b) { return a < b ? a : b; } @@ -241,7 +256,7 @@ static const uint32_t kZeroHashSeed = 0; // Thomas Wang, Integer Hash Functions. // http://www.concentric.net/~Ttwang/tech/inthash.htm -static inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { +inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { uint32_t hash = key; hash = hash ^ seed; hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; @@ -254,7 +269,19 @@ static inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { } -static inline uint32_t ComputePointerHash(void* ptr) { +inline uint32_t ComputeLongHash(uint64_t key) { + uint64_t hash = key; + hash = ~hash + (hash << 18); // hash = (hash << 18) - hash - 1; + hash = hash ^ (hash >> 31); + hash = hash * 21; // hash = (hash + (hash << 2)) + (hash << 4); + hash = hash ^ (hash >> 11); + hash = hash + (hash << 6); + hash = hash ^ (hash >> 22); + return (uint32_t) hash; +} + + +inline uint32_t ComputePointerHash(void* ptr) { return ComputeIntegerHash( static_cast<uint32_t>(reinterpret_cast<intptr_t>(ptr)), v8::internal::kZeroHashSeed); @@ -711,7 +738,7 @@ class SequenceCollector : public Collector<T, growth_factor, max_growth> { // Compare ASCII/16bit chars to ASCII/16bit chars. template <typename lchar, typename rchar> -static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) { +inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) { const lchar* limit = lhs + chars; #ifdef V8_HOST_CAN_READ_UNALIGNED if (sizeof(*lhs) == sizeof(*rhs)) { @@ -738,7 +765,7 @@ static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) { // Calculate 10^exponent. -static inline int TenToThe(int exponent) { +inline int TenToThe(int exponent) { ASSERT(exponent <= 9); ASSERT(exponent >= 1); int answer = 10; @@ -904,9 +931,17 @@ class EnumSet { explicit EnumSet(T bits = 0) : bits_(bits) {} bool IsEmpty() const { return bits_ == 0; } bool Contains(E element) const { return (bits_ & Mask(element)) != 0; } + bool ContainsAnyOf(const EnumSet& set) const { + return (bits_ & set.bits_) != 0; + } void Add(E element) { bits_ |= Mask(element); } + void Add(const EnumSet& set) { bits_ |= set.bits_; } void Remove(E element) { bits_ &= ~Mask(element); } + void Remove(const EnumSet& set) { bits_ &= ~set.bits_; } + void RemoveAll() { bits_ = 0; } + void Intersect(const EnumSet& set) { bits_ &= set.bits_; } T ToIntegral() const { return bits_; } + bool operator==(const EnumSet& set) { return bits_ == set.bits_; } private: T Mask(E element) const { diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h index 2de830300d..47341e72c5 100644 --- a/deps/v8/src/v8-counters.h +++ b/deps/v8/src/v8-counters.h @@ -107,7 +107,10 @@ namespace internal { SC(contexts_created_by_snapshot, V8.ContextsCreatedBySnapshot) \ /* Number of code objects found from pc. */ \ SC(pc_to_code, V8.PcToCode) \ - SC(pc_to_code_cached, V8.PcToCodeCached) + SC(pc_to_code_cached, V8.PcToCodeCached) \ + /* The store-buffer implementation of the write barrier. */ \ + SC(store_buffer_compactions, V8.StoreBufferCompactions) \ + SC(store_buffer_overflows, V8.StoreBufferOverflows) #define STATS_COUNTER_LIST_2(SC) \ @@ -126,10 +129,6 @@ namespace internal { V8.GCCompactorCausedByWeakHandles) \ SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \ SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \ - SC(map_to_fast_elements, V8.MapToFastElements) \ - SC(map_to_fast_double_elements, V8.MapToFastDoubleElements) \ - SC(map_to_slow_elements, V8.MapToSlowElements) \ - SC(map_to_external_array_elements, V8.MapToExternalArrayElements) \ /* How is the generic keyed-load stub used? */ \ SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \ diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index 1e9b5dc142..e4b37b180e 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -38,6 +38,7 @@ #include "log.h" #include "runtime-profiler.h" #include "serialize.h" +#include "store-buffer.h" namespace v8 { namespace internal { @@ -46,16 +47,19 @@ static Mutex* init_once_mutex = OS::CreateMutex(); static bool init_once_called = false; bool V8::is_running_ = false; -bool V8::has_been_setup_ = false; +bool V8::has_been_set_up_ = false; bool V8::has_been_disposed_ = false; bool V8::has_fatal_error_ = false; bool V8::use_crankshaft_ = true; +List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL; static Mutex* entropy_mutex = OS::CreateMutex(); static EntropySource entropy_source; bool V8::Initialize(Deserializer* des) { + FlagList::EnforceFlagImplications(); + InitializeOncePerProcess(); // The current thread may not yet had entered an isolate to run. @@ -78,7 +82,7 @@ bool V8::Initialize(Deserializer* des) { if (isolate->IsInitialized()) return true; is_running_ = true; - has_been_setup_ = true; + has_been_set_up_ = true; has_fatal_error_ = false; has_been_disposed_ = false; @@ -96,11 +100,14 @@ void V8::TearDown() { Isolate* isolate = Isolate::Current(); ASSERT(isolate->IsDefaultIsolate()); - if (!has_been_setup_ || has_been_disposed_) return; + if (!has_been_set_up_ || has_been_disposed_) return; isolate->TearDown(); is_running_ = false; has_been_disposed_ = true; + + delete call_completed_callbacks_; + call_completed_callbacks_ = NULL; } @@ -140,9 +147,10 @@ void V8::SetEntropySource(EntropySource source) { // Used by JavaScript APIs -uint32_t V8::Random(Isolate* isolate) { - ASSERT(isolate == Isolate::Current()); - return random_base(isolate->random_seed()); +uint32_t V8::Random(Context* context) { + ASSERT(context->IsGlobalContext()); + ByteArray* seed = context->random_seed(); + return random_base(reinterpret_cast<uint32_t*>(seed->GetDataStartAddress())); } @@ -155,13 +163,48 @@ uint32_t V8::RandomPrivate(Isolate* isolate) { } -bool V8::IdleNotification() { +bool V8::IdleNotification(int hint) { // Returning true tells the caller that there is no need to call // IdleNotification again. if (!FLAG_use_idle_notification) return true; // Tell the heap that it may want to adjust. - return HEAP->IdleNotification(); + return HEAP->IdleNotification(hint); +} + + +void V8::AddCallCompletedCallback(CallCompletedCallback callback) { + if (call_completed_callbacks_ == NULL) { // Lazy init. + call_completed_callbacks_ = new List<CallCompletedCallback>(); + } + for (int i = 0; i < call_completed_callbacks_->length(); i++) { + if (callback == call_completed_callbacks_->at(i)) return; + } + call_completed_callbacks_->Add(callback); +} + + +void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) { + if (call_completed_callbacks_ == NULL) return; + for (int i = 0; i < call_completed_callbacks_->length(); i++) { + if (callback == call_completed_callbacks_->at(i)) { + call_completed_callbacks_->Remove(i); + } + } +} + + +void V8::FireCallCompletedCallback(Isolate* isolate) { + if (call_completed_callbacks_ == NULL) return; + HandleScopeImplementer* handle_scope_implementer = + isolate->handle_scope_implementer(); + if (!handle_scope_implementer->CallDepthIsZero()) return; + // Fire callbacks. Increase call depth to prevent recursive callbacks. + handle_scope_implementer->IncrementCallDepth(); + for (int i = 0; i < call_completed_callbacks_->length(); i++) { + call_completed_callbacks_->at(i)(); + } + handle_scope_implementer->DecrementCallDepth(); } @@ -172,8 +215,9 @@ typedef union { } double_int_union; -Object* V8::FillHeapNumberWithRandom(Object* heap_number, Isolate* isolate) { - uint64_t random_bits = Random(isolate); +Object* V8::FillHeapNumberWithRandom(Object* heap_number, + Context* context) { + uint64_t random_bits = Random(context); // Make a double* from address (heap_number + sizeof(double)). double_int_union* r = reinterpret_cast<double_int_union*>( reinterpret_cast<char*>(heap_number) + @@ -195,8 +239,8 @@ void V8::InitializeOncePerProcess() { if (init_once_called) return; init_once_called = true; - // Setup the platform OS support. - OS::Setup(); + // Set up the platform OS support. + OS::SetUp(); use_crankshaft_ = FLAG_crankshaft; @@ -204,17 +248,20 @@ void V8::InitializeOncePerProcess() { use_crankshaft_ = false; } - CPU::Setup(); + CPU::SetUp(); if (!CPU::SupportsCrankshaft()) { use_crankshaft_ = false; } RuntimeProfiler::GlobalSetup(); - // Peephole optimization might interfere with deoptimization. - FLAG_peephole_optimization = !use_crankshaft_; - ElementsAccessor::InitializeOncePerProcess(); + + if (FLAG_stress_compaction) { + FLAG_force_marking_deque_overflows = true; + FLAG_gc_global = true; + FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2; + } } } } // namespace v8::internal diff --git a/deps/v8/src/v8.h b/deps/v8/src/v8.h index e565ca5ae9..adfdb3ea88 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -60,10 +60,11 @@ #include "objects-inl.h" #include "spaces-inl.h" #include "heap-inl.h" +#include "incremental-marking-inl.h" +#include "mark-compact-inl.h" #include "log-inl.h" #include "cpu-profiler-inl.h" #include "handles-inl.h" -#include "isolate-inl.h" namespace v8 { namespace internal { @@ -95,17 +96,21 @@ class V8 : public AllStatic { // generation. static void SetEntropySource(EntropySource source); // Random number generation support. Not cryptographically safe. - static uint32_t Random(Isolate* isolate); + static uint32_t Random(Context* context); // We use random numbers internally in memory allocation and in the // compilers for security. In order to prevent information leaks we // use a separate random state for internal random number // generation. static uint32_t RandomPrivate(Isolate* isolate); static Object* FillHeapNumberWithRandom(Object* heap_number, - Isolate* isolate); + Context* context); // Idle notification directly from the API. - static bool IdleNotification(); + static bool IdleNotification(int hint); + + static void AddCallCompletedCallback(CallCompletedCallback callback); + static void RemoveCallCompletedCallback(CallCompletedCallback callback); + static void FireCallCompletedCallback(Isolate* isolate); private: static void InitializeOncePerProcess(); @@ -113,7 +118,7 @@ class V8 : public AllStatic { // True if engine is currently running static bool is_running_; // True if V8 has ever been run - static bool has_been_setup_; + static bool has_been_set_up_; // True if error has been signaled for current engine // (reset to false if engine is restarted) static bool has_fatal_error_; @@ -122,8 +127,19 @@ class V8 : public AllStatic { static bool has_been_disposed_; // True if we are using the crankshaft optimizing compiler. static bool use_crankshaft_; + // List of callbacks when a Call completes. + static List<CallCompletedCallback>* call_completed_callbacks_; }; + +// JavaScript defines two kinds of 'nil'. +enum NilValue { kNullValue, kUndefinedValue }; + + +// JavaScript defines two kinds of equality. +enum EqualityKind { kStrictEquality, kNonStrictEquality }; + + } } // namespace v8::internal namespace i = v8::internal; diff --git a/deps/v8/src/v8conversions.h b/deps/v8/src/v8conversions.h index 1840e3a34b..0147d8c371 100644 --- a/deps/v8/src/v8conversions.h +++ b/deps/v8/src/v8conversions.h @@ -34,13 +34,13 @@ namespace v8 { namespace internal { // Convert from Number object to C integer. -static inline int32_t NumberToInt32(Object* number) { +inline int32_t NumberToInt32(Object* number) { if (number->IsSmi()) return Smi::cast(number)->value(); return DoubleToInt32(number->Number()); } -static inline uint32_t NumberToUint32(Object* number) { +inline uint32_t NumberToUint32(Object* number) { if (number->IsSmi()) return Smi::cast(number)->value(); return DoubleToUint32(number->Number()); } diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h index bf843e5f36..ff3ad8d748 100644 --- a/deps/v8/src/v8globals.h +++ b/deps/v8/src/v8globals.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,7 @@ #define V8_V8GLOBALS_H_ #include "globals.h" +#include "checks.h" namespace v8 { namespace internal { @@ -79,18 +80,20 @@ const Address kFromSpaceZapValue = reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdaf)); const uint64_t kDebugZapValue = V8_UINT64_C(0xbadbaddbbadbaddb); const uint64_t kSlotsZapValue = V8_UINT64_C(0xbeefdeadbeefdeef); +const uint64_t kFreeListZapValue = 0xfeed1eaffeed1eaf; #else const Address kZapValue = reinterpret_cast<Address>(0xdeadbeef); const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddeaf); const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdaf); const uint32_t kSlotsZapValue = 0xbeefdeef; const uint32_t kDebugZapValue = 0xbadbaddb; +const uint32_t kFreeListZapValue = 0xfeed1eaf; #endif -// Number of bits to represent the page size for paged spaces. The value of 13 -// gives 8K bytes per page. -const int kPageSizeBits = 13; +// Number of bits to represent the page size for paged spaces. The value of 20 +// gives 1Mb bytes per page. +const int kPageSizeBits = 20; // On Intel architecture, cache line size is 64 bytes. // On ARM it may be less (32 bytes), but as far this constant is @@ -98,24 +101,18 @@ const int kPageSizeBits = 13; const int kProcessorCacheLineSize = 64; // Constants relevant to double precision floating point numbers. - -// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no -// other bits set. -const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; // If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); // ----------------------------------------------------------------------------- // Forward declarations for frequently used classes -// (sorted alphabetically) class AccessorInfo; class Allocation; class Arguments; class Assembler; class AssertNoAllocation; -class BreakableStatement; class Code; class CodeGenerator; class CodeStub; @@ -125,12 +122,10 @@ class Debugger; class DebugInfo; class Descriptor; class DescriptorArray; -class Expression; class ExternalReference; class FixedArray; -class FunctionEntry; -class FunctionLiteral; class FunctionTemplateInfo; +class MemoryChunk; class SeededNumberDictionary; class UnseededNumberDictionary; class StringDictionary; @@ -139,7 +134,6 @@ class Heap; class HeapObject; class IC; class InterceptorInfo; -class IterationStatement; class JSArray; class JSFunction; class JSObject; @@ -150,32 +144,19 @@ class Map; class MapSpace; class MarkCompactCollector; class NewSpace; -class NodeVisitor; class Object; class MaybeObject; class OldSpace; -class Property; class Foreign; -class RegExpNode; -struct RegExpCompileData; -class RegExpTree; -class RegExpCompiler; -class RegExpVisitor; class Scope; -template<class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; -class SerializedScopeInfo; +class ScopeInfo; class Script; -class Slot; class Smi; template <typename Config, class Allocator = FreeStoreAllocationPolicy> class SplayTree; -class Statement; class String; class Struct; -class SwitchStatement; -class AstVisitor; class Variable; -class VariableProxy; class RelocInfo; class Deserializer; class MessageLocation; @@ -255,12 +236,6 @@ struct CodeDesc { }; -// Callback function on object slots, used for iterating heap object slots in -// HeapObjects, global pointers to heap objects, etc. The callback allows the -// callback function to change the value of the slot. -typedef void (*ObjectSlotCallback)(HeapObject** pointer); - - // Callback function used for iterating objects in heap spaces, // for example, scanning heap objects. typedef int (*HeapObjectCallback)(HeapObject* obj); @@ -307,7 +282,9 @@ enum CallFunctionFlags { NO_CALL_FUNCTION_FLAGS = 0, // Receiver might implicitly be the global objects. If it is, the // hole is passed to the call function stub. - RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0 + RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0, + // The call target is cached in the instruction stream. + RECORD_CALL_TARGET = 1 << 1 }; @@ -317,28 +294,17 @@ enum InlineCacheHolderFlag { }; -// Type of properties. -// Order of properties is significant. -// Must fit in the BitField PropertyDetails::TypeField. -// A copy of this is in mirror-debugger.js. -enum PropertyType { - NORMAL = 0, // only in slow mode - FIELD = 1, // only in fast mode - CONSTANT_FUNCTION = 2, // only in fast mode - CALLBACKS = 3, - HANDLER = 4, // only in lookup results, not in descriptors - INTERCEPTOR = 5, // only in lookup results, not in descriptors - MAP_TRANSITION = 6, // only in fast mode - ELEMENTS_TRANSITION = 7, - CONSTANT_TRANSITION = 8, // only in fast mode - NULL_DESCRIPTOR = 9, // only in fast mode - // All properties before MAP_TRANSITION are real. - FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, - // There are no IC stubs for NULL_DESCRIPTORS. Therefore, - // NULL_DESCRIPTOR can be used as the type flag for IC stubs for - // nonexistent properties. - NONEXISTENT = NULL_DESCRIPTOR -}; +// The Store Buffer (GC). +typedef enum { + kStoreBufferFullEvent, + kStoreBufferStartScanningPagesEvent, + kStoreBufferScanningPageEvent +} StoreBufferEvent; + + +typedef void (*StoreBufferCallback)(Heap* heap, + MemoryChunk* page, + StoreBufferEvent event); // Whether to remove map transitions and constant transitions from a @@ -475,21 +441,11 @@ enum CpuFeature { SSE4_1 = 32 + 19, // x86 SAHF = 0, // x86 FPU = 1}; // MIPS -// The Strict Mode (ECMA-262 5th edition, 4.2.2). -enum StrictModeFlag { - kNonStrictMode, - kStrictMode, - // This value is never used, but is needed to prevent GCC 4.5 from failing - // to compile when we assert that a flag is either kNonStrictMode or - // kStrictMode. - kInvalidStrictFlag -}; - // Used to specify if a macro instruction must perform a smi check on tagged // values. enum SmiCheckType { - DONT_DO_SMI_CHECK = 0, + DONT_DO_SMI_CHECK, DO_SMI_CHECK }; @@ -497,20 +453,105 @@ enum SmiCheckType { // Used to specify whether a receiver is implicitly or explicitly // provided to a call. enum CallKind { - CALL_AS_METHOD = 0, + CALL_AS_METHOD, CALL_AS_FUNCTION }; -static const uint32_t kHoleNanUpper32 = 0x7FFFFFFF; -static const uint32_t kHoleNanLower32 = 0xFFFFFFFF; -static const uint32_t kNaNOrInfinityLowerBoundUpper32 = 0x7FF00000; +enum ScopeType { + EVAL_SCOPE, // The top-level scope for an eval source. + FUNCTION_SCOPE, // The top-level scope for a function. + GLOBAL_SCOPE, // The top-level scope for a program or a top-level eval. + CATCH_SCOPE, // The scope introduced by catch. + BLOCK_SCOPE, // The scope introduced by a new block. + WITH_SCOPE // The scope introduced by with. +}; + + +const uint32_t kHoleNanUpper32 = 0x7FFFFFFF; +const uint32_t kHoleNanLower32 = 0xFFFFFFFF; +const uint32_t kNaNOrInfinityLowerBoundUpper32 = 0x7FF00000; const uint64_t kHoleNanInt64 = (static_cast<uint64_t>(kHoleNanUpper32) << 32) | kHoleNanLower32; const uint64_t kLastNonNaNInt64 = (static_cast<uint64_t>(kNaNOrInfinityLowerBoundUpper32) << 32); + +enum VariableMode { + // User declared variables: + VAR, // declared via 'var', and 'function' declarations + + CONST, // declared via 'const' declarations + + CONST_HARMONY, // declared via 'const' declarations in harmony mode + + LET, // declared via 'let' declarations + + // Variables introduced by the compiler: + DYNAMIC, // always require dynamic lookup (we don't know + // the declaration) + + DYNAMIC_GLOBAL, // requires dynamic lookup, but we know that the + // variable is global unless it has been shadowed + // by an eval-introduced variable + + DYNAMIC_LOCAL, // requires dynamic lookup, but we know that the + // variable is local and where it is unless it + // has been shadowed by an eval-introduced + // variable + + INTERNAL, // like VAR, but not user-visible (may or may not + // be in a context) + + TEMPORARY // temporary variables (not user-visible), never + // in a context +}; + + +// ES6 Draft Rev3 10.2 specifies declarative environment records with mutable +// and immutable bindings that can be in two states: initialized and +// uninitialized. In ES5 only immutable bindings have these two states. When +// accessing a binding, it needs to be checked for initialization. However in +// the following cases the binding is initialized immediately after creation +// so the initialization check can always be skipped: +// 1. Var declared local variables. +// var foo; +// 2. A local variable introduced by a function declaration. +// function foo() {} +// 3. Parameters +// function x(foo) {} +// 4. Catch bound variables. +// try {} catch (foo) {} +// 6. Function variables of named function expressions. +// var x = function foo() {} +// 7. Implicit binding of 'this'. +// 8. Implicit binding of 'arguments' in functions. +// +// ES5 specified object environment records which are introduced by ES elements +// such as Program and WithStatement that associate identifier bindings with the +// properties of some object. In the specification only mutable bindings exist +// (which may be non-writable) and have no distinct initialization step. However +// V8 allows const declarations in global code with distinct creation and +// initialization steps which are represented by non-writable properties in the +// global object. As a result also these bindings need to be checked for +// initialization. +// +// The following enum specifies a flag that indicates if the binding needs a +// distinct initialization step (kNeedsInitialization) or if the binding is +// immediately initialized upon creation (kCreatedInitialized). +enum InitializationFlag { + kNeedsInitialization, + kCreatedInitialized +}; + + +enum ClearExceptionFlag { + KEEP_EXCEPTION, + CLEAR_EXCEPTION +}; + + } } // namespace v8::internal #endif // V8_V8GLOBALS_H_ diff --git a/deps/v8/src/v8memory.h b/deps/v8/src/v8memory.h index 901e78d296..f71de82072 100644 --- a/deps/v8/src/v8memory.h +++ b/deps/v8/src/v8memory.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -60,6 +60,10 @@ class Memory { return *reinterpret_cast<int*>(addr); } + static unsigned& unsigned_at(Address addr) { + return *reinterpret_cast<unsigned*>(addr); + } + static double& double_at(Address addr) { return *reinterpret_cast<double*>(addr); } diff --git a/deps/v8/src/v8natives.js b/deps/v8/src/v8natives.js index 588bdb21bb..1d54e28e97 100644 --- a/deps/v8/src/v8natives.js +++ b/deps/v8/src/v8natives.js @@ -60,18 +60,6 @@ function InstallFunctions(object, attributes, functions) { %ToFastProperties(object); } -// Emulates JSC by installing functions on a hidden prototype that -// lies above the current object/prototype. This lets you override -// functions on String.prototype etc. and then restore the old function -// with delete. See http://code.google.com/p/chromium/issues/detail?id=1717 -function InstallFunctionsOnHiddenPrototype(object, attributes, functions) { - %CheckIsBootstrapping(); - var hidden_prototype = new $Object(); - %SetHiddenPrototype(object, hidden_prototype); - InstallFunctions(hidden_prototype, attributes, functions); -} - - // Prevents changes to the prototype of a built-infunction. // The "prototype" property of the function object is made non-configurable, // and the prototype object is made non-extensible. The latter prevents @@ -139,8 +127,9 @@ function GlobalParseInt(string, radix) { // The spec says ToString should be evaluated before ToInt32. string = TO_STRING_INLINE(string); radix = TO_INT32(radix); - if (!(radix == 0 || (2 <= radix && radix <= 36))) + if (!(radix == 0 || (2 <= radix && radix <= 36))) { return $NaN; + } } if (%_HasCachedArrayIndex(string) && @@ -162,28 +151,23 @@ function GlobalParseFloat(string) { function GlobalEval(x) { if (!IS_STRING(x)) return x; - var receiver = this; var global_receiver = %GlobalReceiver(global); - - if (receiver == null && !IS_UNDETECTABLE(receiver)) { - receiver = global_receiver; - } - - var this_is_global_receiver = (receiver === global_receiver); var global_is_detached = (global === global_receiver); // For consistency with JSC we require the global object passed to // eval to be the global object from which 'eval' originated. This // is not mandated by the spec. - if (!this_is_global_receiver || global_is_detached) { - throw new $EvalError('The "this" object passed to eval must ' + + // We only throw if the global has been detached, since we need the + // receiver as this-value for the call. + if (global_is_detached) { + throw new $EvalError('The "this" value passed to eval must ' + 'be the global object from which eval originated'); } var f = %CompileString(x); if (!IS_FUNCTION(f)) return f; - return %_CallFunction(receiver, f); + return %_CallFunction(global_receiver, f); } @@ -193,13 +177,14 @@ function GlobalEval(x) { function SetUpGlobal() { %CheckIsBootstrapping(); // ECMA 262 - 15.1.1.1. - %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE); + %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); // ECMA-262 - 15.1.1.2. - %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE); + %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY); // ECMA-262 - 15.1.1.3. - %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE); + %SetProperty(global, "undefined", void 0, + DONT_ENUM | DONT_DELETE | READ_ONLY); // Set up non-enumerable function on the global object. InstallFunctions(global, DONT_ENUM, $Array( @@ -299,7 +284,8 @@ function ObjectDefineGetter(name, fun) { receiver = %GlobalReceiver(global); } if (!IS_SPEC_FUNCTION(fun)) { - throw new $TypeError('Object.prototype.__defineGetter__: Expecting function'); + throw new $TypeError( + 'Object.prototype.__defineGetter__: Expecting function'); } var desc = new PropertyDescriptor(); desc.setGet(fun); @@ -345,8 +331,9 @@ function ObjectLookupSetter(name) { function ObjectKeys(obj) { - if (!IS_SPEC_OBJECT(obj)) + if (!IS_SPEC_OBJECT(obj)) { throw MakeTypeError("obj_ctor_property_non_object", ["keys"]); + } if (%IsJSProxy(obj)) { var handler = %GetHandler(obj); var names = CallTrap0(handler, "keys", DerivedKeysTrap); @@ -372,6 +359,7 @@ function IsDataDescriptor(desc) { // ES5 8.10.3. function IsGenericDescriptor(desc) { + if (IS_UNDEFINED(desc)) return false; return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); } @@ -476,7 +464,7 @@ function ToPropertyDescriptor(obj) { // For Harmony proxies. function ToCompletePropertyDescriptor(obj) { - var desc = ToPropertyDescriptor(obj) + var desc = ToPropertyDescriptor(obj); if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) { if (!desc.hasValue()) desc.setValue(void 0); if (!desc.hasWritable()) desc.setWritable(false); @@ -672,6 +660,21 @@ function GetOwnProperty(obj, v) { } +// ES5 section 8.12.7. +function Delete(obj, p, should_throw) { + var desc = GetOwnProperty(obj, p); + if (IS_UNDEFINED(desc)) return true; + if (desc.isConfigurable()) { + %DeleteProperty(obj, p, 0); + return true; + } else if (should_throw) { + throw MakeTypeError("define_disallowed", [p]); + } else { + return; + } +} + + // Harmony proxies. function DefineProxyProperty(obj, p, attributes, should_throw) { var handler = %GetHandler(obj); @@ -689,12 +692,7 @@ function DefineProxyProperty(obj, p, attributes, should_throw) { // ES5 8.12.9. -function DefineOwnProperty(obj, p, desc, should_throw) { - if (%IsJSProxy(obj)) { - var attributes = FromGenericPropertyDescriptor(desc); - return DefineProxyProperty(obj, p, attributes, should_throw); - } - +function DefineObjectProperty(obj, p, desc, should_throw) { var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p)); // A false value here means that access checks failed. if (current_or_access === false) return void 0; @@ -708,7 +706,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("define_disallowed", [p]); } else { - return; + return false; } } @@ -738,7 +736,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } // Step 8 @@ -748,7 +746,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } // Step 10a @@ -757,7 +755,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } if (!current.isWritable() && desc.hasValue() && @@ -765,7 +763,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } } @@ -775,14 +773,14 @@ function DefineOwnProperty(obj, p, desc, should_throw) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) { if (should_throw) { throw MakeTypeError("redefine_disallowed", [p]); } else { - return; + return false; } } } @@ -858,19 +856,105 @@ function DefineOwnProperty(obj, p, desc, should_throw) { } +// ES5 section 15.4.5.1. +function DefineArrayProperty(obj, p, desc, should_throw) { + // Note that the length of an array is not actually stored as part of the + // property, hence we use generated code throughout this function instead of + // DefineObjectProperty() to modify its value. + + // Step 3 - Special handling for length property. + if (p == "length") { + var length = obj.length; + if (!desc.hasValue()) { + return DefineObjectProperty(obj, "length", desc, should_throw); + } + var new_length = ToUint32(desc.getValue()); + if (new_length != ToNumber(desc.getValue())) { + throw new $RangeError('defineProperty() array length out of range'); + } + var length_desc = GetOwnProperty(obj, "length"); + if (new_length != length && !length_desc.isWritable()) { + if (should_throw) { + throw MakeTypeError("redefine_disallowed", [p]); + } else { + return false; + } + } + var threw = false; + while (new_length < length--) { + if (!Delete(obj, ToString(length), false)) { + new_length = length + 1; + threw = true; + break; + } + } + // Make sure the below call to DefineObjectProperty() doesn't overwrite + // any magic "length" property by removing the value. + obj.length = new_length; + desc.value_ = void 0; + desc.hasValue_ = false; + if (!DefineObjectProperty(obj, "length", desc, should_throw) || threw) { + if (should_throw) { + throw MakeTypeError("redefine_disallowed", [p]); + } else { + return false; + } + } + return true; + } + + // Step 4 - Special handling for array index. + var index = ToUint32(p); + if (index == ToNumber(p) && index != 4294967295) { + var length = obj.length; + var length_desc = GetOwnProperty(obj, "length"); + if ((index >= length && !length_desc.isWritable()) || + !DefineObjectProperty(obj, p, desc, true)) { + if (should_throw) { + throw MakeTypeError("define_disallowed", [p]); + } else { + return false; + } + } + if (index >= length) { + obj.length = index + 1; + } + return true; + } + + // Step 5 - Fallback to default implementation. + return DefineObjectProperty(obj, p, desc, should_throw); +} + + +// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies. +function DefineOwnProperty(obj, p, desc, should_throw) { + if (%IsJSProxy(obj)) { + var attributes = FromGenericPropertyDescriptor(desc); + return DefineProxyProperty(obj, p, attributes, should_throw); + } else if (IS_ARRAY(obj)) { + return DefineArrayProperty(obj, p, desc, should_throw); + } else { + return DefineObjectProperty(obj, p, desc, should_throw); + } +} + + // ES5 section 15.2.3.2. function ObjectGetPrototypeOf(obj) { - if (!IS_SPEC_OBJECT(obj)) + if (!IS_SPEC_OBJECT(obj)) { throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]); + } return %GetPrototype(obj); } // ES5 section 15.2.3.3 function ObjectGetOwnPropertyDescriptor(obj, p) { - if (!IS_SPEC_OBJECT(obj)) + if (!IS_SPEC_OBJECT(obj)) { throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]); + } var desc = GetOwnProperty(obj, p); return FromPropertyDescriptor(desc); } @@ -883,14 +967,14 @@ function ToStringArray(obj, trap) { } var n = ToUint32(obj.length); var array = new $Array(n); - var names = {} + var names = {}; // TODO(rossberg): use sets once they are ready. for (var index = 0; index < n; index++) { var s = ToString(obj[index]); if (s in names) { - throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]) + throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]); } array[index] = s; - names.s = 0; + names[s] = 0; } return array; } @@ -898,9 +982,10 @@ function ToStringArray(obj, trap) { // ES5 section 15.2.3.4. function ObjectGetOwnPropertyNames(obj) { - if (!IS_SPEC_OBJECT(obj)) - throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]); - + if (!IS_SPEC_OBJECT(obj)) { + throw MakeTypeError("obj_ctor_property_non_object", + ["getOwnPropertyNames"]); + } // Special handling for proxies. if (%IsJSProxy(obj)) { var handler = %GetHandler(obj); @@ -917,8 +1002,9 @@ function ObjectGetOwnPropertyNames(obj) { if (%GetInterceptorInfo(obj) & 1) { var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj); - if (indexedInterceptorNames) + if (indexedInterceptorNames) { propertyNames = propertyNames.concat(indexedInterceptorNames); + } } // Find all the named properties. @@ -944,8 +1030,9 @@ function ObjectGetOwnPropertyNames(obj) { // We need to check for the exact property value since for intrinsic // properties like toString if(propertySet["toString"]) will always // succeed. - if (propertySet[name] === true) + if (propertySet[name] === true) { continue; + } propertySet[name] = true; propertyNames[j++] = name; } @@ -1021,14 +1108,17 @@ function GetOwnEnumerablePropertyNames(properties) { // ES5 section 15.2.3.7. function ObjectDefineProperties(obj, properties) { - if (!IS_SPEC_OBJECT(obj)) + if (!IS_SPEC_OBJECT(obj)) { throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]); + } var props = ToObject(properties); var names = GetOwnEnumerablePropertyNames(props); + var descriptors = new InternalArray(); for (var i = 0; i < names.length; i++) { - var name = names[i]; - var desc = ToPropertyDescriptor(props[name]); - DefineOwnProperty(obj, name, desc, true); + descriptors.push(ToPropertyDescriptor(props[names[i]])); + } + for (var i = 0; i < names.length; i++) { + DefineOwnProperty(obj, names[i], descriptors[i], true); } return obj; } @@ -1042,12 +1132,20 @@ function ProxyFix(obj) { throw MakeTypeError("handler_returned_undefined", [handler, "fix"]); } - if (IS_SPEC_FUNCTION(obj)) { + if (%IsJSFunctionProxy(obj)) { var callTrap = %GetCallTrap(obj); var constructTrap = %GetConstructTrap(obj); var code = DelegateCallAndConstruct(callTrap, constructTrap); %Fix(obj); // becomes a regular function %SetCode(obj, code); + // TODO(rossberg): What about length and other properties? Not specified. + // We just put in some half-reasonable defaults for now. + var prototype = new $Object(); + $Object.defineProperty(prototype, "constructor", + {value: obj, writable: true, enumerable: false, configurable: true}); + // TODO(v8:1530): defineProperty does not handle prototype and length. + %FunctionSetPrototype(obj, prototype); + obj.length = 0; } else { %Fix(obj); } @@ -1237,8 +1335,9 @@ function BooleanToString() { function BooleanValueOf() { // NOTE: Both Boolean objects and values can enter here as // 'this'. This is not as dictated by ECMA-262. - if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) + if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) { throw new $TypeError('Boolean.prototype.valueOf is not generic'); + } return %_ValueOf(this); } @@ -1278,8 +1377,9 @@ function NumberToString(radix) { // 'this'. This is not as dictated by ECMA-262. var number = this; if (!IS_NUMBER(this)) { - if (!IS_NUMBER_WRAPPER(this)) + if (!IS_NUMBER_WRAPPER(this)) { throw new $TypeError('Number.prototype.toString is not generic'); + } // Get the value of this number in case it's an object. number = %_ValueOf(this); } @@ -1312,8 +1412,9 @@ function NumberToLocaleString() { function NumberValueOf() { // NOTE: Both Number objects and values can enter here as // 'this'. This is not as dictated by ECMA-262. - if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) + if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) { throw new $TypeError('Number.prototype.valueOf is not generic'); + } return %_ValueOf(this); } @@ -1339,7 +1440,8 @@ function NumberToExponential(fractionDigits) { if (!IS_UNDEFINED(fractionDigits)) { f = TO_INTEGER(fractionDigits); if (f < 0 || f > 20) { - throw new $RangeError("toExponential() argument must be between 0 and 20"); + throw new $RangeError( + "toExponential() argument must be between 0 and 20"); } } if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { @@ -1383,7 +1485,8 @@ function SetUpNumber() { DONT_ENUM | DONT_DELETE | READ_ONLY); // ECMA-262 section 15.7.3.2. - %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY); + %SetProperty($Number, "MIN_VALUE", 5e-324, + DONT_ENUM | DONT_DELETE | READ_ONLY); // ECMA-262 section 15.7.3.3. %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); @@ -1455,53 +1558,54 @@ function FunctionToString() { // ES5 15.3.4.5 function FunctionBind(this_arg) { // Length is 1. if (!IS_SPEC_FUNCTION(this)) { - throw new $TypeError('Bind must be called on a function'); - } - // this_arg is not an argument that should be bound. - var argc_bound = (%_ArgumentsLength() || 1) - 1; - var fn = this; - - if (argc_bound == 0) { - var result = function() { - if (%_IsConstructCall()) { - // %NewObjectFromBound implicitly uses arguments passed to this - // function. We do not pass the arguments object explicitly to avoid - // materializing it and guarantee that this function will be optimized. - return %NewObjectFromBound(fn, null); - } - return %Apply(fn, this_arg, arguments, 0, %_ArgumentsLength()); - }; - } else { - var bound_args = new InternalArray(argc_bound); - for(var i = 0; i < argc_bound; i++) { - bound_args[i] = %_Arguments(i+1); + throw new $TypeError('Bind must be called on a function'); + } + var boundFunction = function () { + // Poison .arguments and .caller, but is otherwise not detectable. + "use strict"; + // This function must not use any object literals (Object, Array, RegExp), + // since the literals-array is being used to store the bound data. + if (%_IsConstructCall()) { + return %NewObjectFromBound(boundFunction); } + var bindings = %BoundFunctionGetBindings(boundFunction); - var result = function() { - // If this is a construct call we use a special runtime method - // to generate the actual object using the bound function. - if (%_IsConstructCall()) { - // %NewObjectFromBound implicitly uses arguments passed to this - // function. We do not pass the arguments object explicitly to avoid - // materializing it and guarantee that this function will be optimized. - return %NewObjectFromBound(fn, bound_args); - } - - // Combine the args we got from the bind call with the args - // given as argument to the invocation. + var argc = %_ArgumentsLength(); + if (argc == 0) { + return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2); + } + if (bindings.length === 2) { + return %Apply(bindings[0], bindings[1], arguments, 0, argc); + } + var bound_argc = bindings.length - 2; + var argv = new InternalArray(bound_argc + argc); + for (var i = 0; i < bound_argc; i++) { + argv[i] = bindings[i + 2]; + } + for (var j = 0; j < argc; j++) { + argv[i++] = %_Arguments(j); + } + return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc); + }; + + %FunctionRemovePrototype(boundFunction); + var new_length = 0; + if (%_ClassOf(this) == "Function") { + // Function or FunctionProxy. + var old_length = this.length; + // FunctionProxies might provide a non-UInt32 value. If so, ignore it. + if ((typeof old_length === "number") && + ((old_length >>> 0) === old_length)) { var argc = %_ArgumentsLength(); - var args = new InternalArray(argc + argc_bound); - // Add bound arguments. - for (var i = 0; i < argc_bound; i++) { - args[i] = bound_args[i]; - } - // Add arguments from call. - for (var i = 0; i < argc; i++) { - args[argc_bound + i] = %_Arguments(i); - } - return %Apply(fn, this_arg, args, 0, argc + argc_bound); - }; + if (argc > 0) argc--; // Don't count the thisArg as parameter. + new_length = old_length - argc; + if (new_length < 0) new_length = 0; + } } + // This runtime function finds any remaining arguments on the stack, + // so we don't pass the arguments object. + var result = %FunctionBindArguments(boundFunction, this, + this_arg, new_length); // We already have caller and arguments properties on functions, // which are non-configurable. It therefore makes no sence to @@ -1509,17 +1613,7 @@ function FunctionBind(this_arg) { // Length is 1. // that bind should make these throw a TypeError if get or set // is called and make them non-enumerable and non-configurable. // To be consistent with our normal functions we leave this as it is. - - %FunctionRemovePrototype(result); - %FunctionSetBound(result); - // Set the correct length. If this is a function proxy, this.length might - // throw, or return a bogus result. Leave length alone in that case. - // TODO(rossberg): This is underspecified in the current proxy proposal. - try { - var old_length = ToInteger(this.length); - var length = (old_length - argc_bound) > 0 ? old_length - argc_bound : 0; - %BoundFunctionSetLength(result, length); - } catch(x) {} + // TODO(lrn): Do set these to be thrower. return result; } diff --git a/deps/v8/src/v8threads.cc b/deps/v8/src/v8threads.cc index 3881d66fb0..fd8d536401 100644 --- a/deps/v8/src/v8threads.cc +++ b/deps/v8/src/v8threads.cc @@ -154,7 +154,7 @@ namespace internal { bool ThreadManager::RestoreThread() { ASSERT(IsLockedByCurrentThread()); - // First check whether the current thread has been 'lazily archived', ie + // First check whether the current thread has been 'lazily archived', i.e. // not archived at all. If that is the case we put the state storage we // had prepared back in the free list, since we didn't need it after all. if (lazily_archived_thread_.Equals(ThreadId::Current())) { diff --git a/deps/v8/src/v8utils.cc b/deps/v8/src/v8utils.cc index bf0e05d05b..042a60f0b4 100644 --- a/deps/v8/src/v8utils.cc +++ b/deps/v8/src/v8utils.cc @@ -316,7 +316,7 @@ bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const { for (const char* p = data_; p < end; p++) { char c = *p; if ((c & 0x80) != 0) { - // Non-ascii detected: + // Non-ASCII detected: is_ascii = false; // Report the error and abort if appropriate: @@ -329,7 +329,7 @@ bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const { c, filename_, line_no, char_no); // Allow for some context up to kNumberOfLeadingContextChars chars - // before the offending non-ascii char to help the user see where + // before the offending non-ASCII char to help the user see where // the offending char is. const int kNumberOfLeadingContextChars = 10; const char* err_context = p - kNumberOfLeadingContextChars; @@ -345,7 +345,7 @@ bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const { OS::Abort(); } - break; // Non-ascii detected. No need to continue scanning. + break; // Non-ASCII detected. No need to continue scanning. } if (c == '\n') { start_of_line = p; diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h index aada521e4c..c73222a29b 100644 --- a/deps/v8/src/v8utils.h +++ b/deps/v8/src/v8utils.h @@ -142,8 +142,14 @@ inline void CopyWords(T* dst, T* src, int num_words) { } -template <typename T> -static inline void MemsetPointer(T** dest, T* value, int counter) { +template <typename T, typename U> +inline void MemsetPointer(T** dest, U* value, int counter) { +#ifdef DEBUG + T* a = NULL; + U* b = NULL; + a = b; // Fake assignment to check assignability. + USE(a); +#endif // DEBUG #if defined(V8_HOST_ARCH_IA32) #define STOS "stosl" #elif defined(V8_HOST_ARCH_X64) @@ -196,7 +202,7 @@ Vector<const char> ReadFile(FILE* file, // Copy from ASCII/16bit chars to ASCII/16bit chars. template <typename sourcechar, typename sinkchar> -static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { +inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { sinkchar* limit = dest + chars; #ifdef V8_HOST_CAN_READ_UNALIGNED if (sizeof(*dest) == sizeof(*src)) { diff --git a/deps/v8/src/variables.cc b/deps/v8/src/variables.cc index 971061b053..aa6a010fac 100644 --- a/deps/v8/src/variables.cc +++ b/deps/v8/src/variables.cc @@ -37,10 +37,11 @@ namespace internal { // ---------------------------------------------------------------------------- // Implementation Variable. -const char* Variable::Mode2String(Mode mode) { +const char* Variable::Mode2String(VariableMode mode) { switch (mode) { case VAR: return "VAR"; case CONST: return "CONST"; + case CONST_HARMONY: return "CONST"; case LET: return "LET"; case DYNAMIC: return "DYNAMIC"; case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL"; @@ -55,21 +56,26 @@ const char* Variable::Mode2String(Mode mode) { Variable::Variable(Scope* scope, Handle<String> name, - Mode mode, + VariableMode mode, bool is_valid_LHS, - Kind kind) + Kind kind, + InitializationFlag initialization_flag) : scope_(scope), name_(name), mode_(mode), kind_(kind), location_(UNALLOCATED), index_(-1), + initializer_position_(RelocInfo::kNoPosition), local_if_not_shadowed_(NULL), is_valid_LHS_(is_valid_LHS), - is_accessed_from_inner_scope_(false), - is_used_(false) { - // names must be canonicalized for fast equality checks + force_context_allocation_(false), + is_used_(false), + initialization_flag_(initialization_flag) { + // Names must be canonicalized for fast equality checks. ASSERT(name->IsSymbol()); + // Var declared variables never need initialization. + ASSERT(!(mode == VAR && initialization_flag == kNeedsInitialization)); } @@ -79,4 +85,12 @@ bool Variable::is_global() const { return mode_ != TEMPORARY && scope_ != NULL && scope_->is_global_scope(); } + +int Variable::CompareIndex(Variable* const* v, Variable* const* w) { + int x = (*v)->index(); + int y = (*w)->index(); + // Consider sorting them according to type as well? + return x - y; +} + } } // namespace v8::internal diff --git a/deps/v8/src/variables.h b/deps/v8/src/variables.h index 56c8dabd37..f20bd399c5 100644 --- a/deps/v8/src/variables.h +++ b/deps/v8/src/variables.h @@ -40,34 +40,6 @@ namespace internal { class Variable: public ZoneObject { public: - enum Mode { - // User declared variables: - VAR, // declared via 'var', and 'function' declarations - - CONST, // declared via 'const' declarations - - LET, // declared via 'let' declarations - - // Variables introduced by the compiler: - DYNAMIC, // always require dynamic lookup (we don't know - // the declaration) - - DYNAMIC_GLOBAL, // requires dynamic lookup, but we know that the - // variable is global unless it has been shadowed - // by an eval-introduced variable - - DYNAMIC_LOCAL, // requires dynamic lookup, but we know that the - // variable is local and where it is unless it - // has been shadowed by an eval-introduced - // variable - - INTERNAL, // like VAR, but not user-visible (may or may not - // be in a context) - - TEMPORARY // temporary variables (not user-visible), never - // in a context - }; - enum Kind { NORMAL, THIS, @@ -103,12 +75,13 @@ class Variable: public ZoneObject { Variable(Scope* scope, Handle<String> name, - Mode mode, + VariableMode mode, bool is_valid_lhs, - Kind kind); + Kind kind, + InitializationFlag initialization_flag); // Printing support - static const char* Mode2String(Mode mode); + static const char* Mode2String(VariableMode mode); bool IsValidLeftHandSide() { return is_valid_LHS_; } @@ -119,17 +92,20 @@ class Variable: public ZoneObject { Scope* scope() const { return scope_; } Handle<String> name() const { return name_; } - Mode mode() const { return mode_; } - bool is_accessed_from_inner_scope() const { - return is_accessed_from_inner_scope_; + VariableMode mode() const { return mode_; } + bool has_forced_context_allocation() const { + return force_context_allocation_; } - void MarkAsAccessedFromInnerScope() { + void ForceContextAllocation() { ASSERT(mode_ != TEMPORARY); - is_accessed_from_inner_scope_ = true; + force_context_allocation_ = true; } bool is_used() { return is_used_; } void set_is_used(bool flag) { is_used_ = flag; } + int initializer_position() { return initializer_position_; } + void set_initializer_position(int pos) { initializer_position_ = pos; } + bool IsVariable(Handle<String> n) const { return !is_this() && name().is_identical_to(n); } @@ -146,6 +122,13 @@ class Variable: public ZoneObject { mode_ == DYNAMIC_GLOBAL || mode_ == DYNAMIC_LOCAL); } + bool is_const_mode() const { + return (mode_ == CONST || + mode_ == CONST_HARMONY); + } + bool binding_needs_init() const { + return initialization_flag_ == kNeedsInitialization; + } bool is_global() const; bool is_this() const { return kind_ == THIS; } @@ -153,8 +136,7 @@ class Variable: public ZoneObject { // True if the variable is named eval and not known to be shadowed. bool is_possibly_eval() const { - return IsVariable(FACTORY->eval_symbol()) && - (mode_ == DYNAMIC || mode_ == DYNAMIC_GLOBAL); + return IsVariable(FACTORY->eval_symbol()); } Variable* local_if_not_shadowed() const { @@ -168,28 +150,39 @@ class Variable: public ZoneObject { Location location() const { return location_; } int index() const { return index_; } + InitializationFlag initialization_flag() const { + return initialization_flag_; + } void AllocateTo(Location location, int index) { location_ = location; index_ = index; } + static int CompareIndex(Variable* const* v, Variable* const* w); + private: Scope* scope_; Handle<String> name_; - Mode mode_; + VariableMode mode_; Kind kind_; Location location_; int index_; + int initializer_position_; + // If this field is set, this variable references the stored locally bound + // variable, but it might be shadowed by variable bindings introduced by + // non-strict 'eval' calls between the reference scope (inclusive) and the + // binding scope (exclusive). Variable* local_if_not_shadowed_; // Valid as a LHS? (const and this are not valid LHS, for example) bool is_valid_LHS_; // Usage info. - bool is_accessed_from_inner_scope_; // set by variable resolver + bool force_context_allocation_; // set by variable resolver bool is_used_; + InitializationFlag initialization_flag_; }; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 4d0e245354..03b16ce1ff 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -33,9 +33,9 @@ // NOTE these macros are used by the SCons build script so their names // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 3 -#define MINOR_VERSION 6 -#define BUILD_NUMBER 6 -#define PATCH_LEVEL 20 +#define MINOR_VERSION 9 +#define BUILD_NUMBER 5 +#define PATCH_LEVEL 0 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/deps/v8/src/weakmap.js b/deps/v8/src/weakmap.js deleted file mode 100644 index 5fb5151071..0000000000 --- a/deps/v8/src/weakmap.js +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -// This file relies on the fact that the following declaration has been made -// in runtime.js: -// const $Object = global.Object; -const $WeakMap = global.WeakMap; - -// ------------------------------------------------------------------- - -function WeakMapConstructor() { - if (%_IsConstructCall()) { - %WeakMapInitialize(this); - } else { - return new $WeakMap(); - } -} - - -function WeakMapGet(key) { - if (!IS_SPEC_OBJECT(key)) { - throw %MakeTypeError('invalid_weakmap_key', [this, key]); - } - return %WeakMapGet(this, key); -} - - -function WeakMapSet(key, value) { - if (!IS_SPEC_OBJECT(key)) { - throw %MakeTypeError('invalid_weakmap_key', [this, key]); - } - return %WeakMapSet(this, key, value); -} - - -function WeakMapHas(key) { - if (!IS_SPEC_OBJECT(key)) { - throw %MakeTypeError('invalid_weakmap_key', [this, key]); - } - return !IS_UNDEFINED(%WeakMapGet(this, key)); -} - - -function WeakMapDelete(key) { - if (!IS_SPEC_OBJECT(key)) { - throw %MakeTypeError('invalid_weakmap_key', [this, key]); - } - if (!IS_UNDEFINED(%WeakMapGet(this, key))) { - %WeakMapSet(this, key, void 0); - return true; - } else { - return false; - } -} - -// ------------------------------------------------------------------- - -(function () { - %CheckIsBootstrapping(); - // Set up the WeakMap constructor function. - %SetCode($WeakMap, WeakMapConstructor); - - // Set up the constructor property on the WeakMap prototype object. - %SetProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM); - - // Set up the non-enumerable functions on the WeakMap prototype object. - InstallFunctionsOnHiddenPrototype($WeakMap.prototype, DONT_ENUM, $Array( - "get", WeakMapGet, - "set", WeakMapSet, - "has", WeakMapHas, - "delete", WeakMapDelete - )); -})(); diff --git a/deps/v8/src/win32-headers.h b/deps/v8/src/win32-headers.h index fca5c137ef..ca1b1d8b8e 100644 --- a/deps/v8/src/win32-headers.h +++ b/deps/v8/src/win32-headers.h @@ -75,6 +75,9 @@ // makes it impossible to have them elsewhere. #include <winsock2.h> #include <ws2tcpip.h> +#ifndef __MINGW32__ +#include <wspiapi.h> +#endif // __MINGW32__ #include <process.h> // for _beginthreadex() #include <stdlib.h> #endif // V8_WIN32_HEADERS_FULL diff --git a/deps/v8/src/x64/assembler-x64-inl.h b/deps/v8/src/x64/assembler-x64-inl.h index 8db54f0752..8e3caa444a 100644 --- a/deps/v8/src/x64/assembler-x64-inl.h +++ b/deps/v8/src/x64/assembler-x64-inl.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,6 +28,8 @@ #ifndef V8_X64_ASSEMBLER_X64_INL_H_ #define V8_X64_ASSEMBLER_X64_INL_H_ +#include "x64/assembler-x64.h" + #include "cpu.h" #include "debug.h" #include "v8memory.h" @@ -224,7 +226,9 @@ Address RelocInfo::target_address() { Address RelocInfo::target_address_address() { - ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); + ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY + || rmode_ == EMBEDDED_OBJECT + || rmode_ == EXTERNAL_REFERENCE); return reinterpret_cast<Address>(pc_); } @@ -238,10 +242,15 @@ int RelocInfo::target_address_size() { } -void RelocInfo::set_target_address(Address target) { +void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); if (IsCodeTarget(rmode_)) { Assembler::set_target_address_at(pc_, target); + Object* target_code = Code::GetCodeFromTargetAddress(target); + if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } else { Memory::Address_at(pc_) = target; CPU::FlushICache(pc_, sizeof(Address)); @@ -255,7 +264,7 @@ Object* RelocInfo::target_object() { } -Handle<Object> RelocInfo::target_object_handle(Assembler *origin) { +Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); if (rmode_ == EMBEDDED_OBJECT) { return Memory::Object_Handle_at(pc_); @@ -277,10 +286,16 @@ Address* RelocInfo::target_reference_address() { } -void RelocInfo::set_target_object(Object* target) { +void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - *reinterpret_cast<Object**>(pc_) = target; + Memory::Object_at(pc_) = target; CPU::FlushICache(pc_, sizeof(Address)); + if (mode == UPDATE_WRITE_BARRIER && + host() != NULL && + target->IsHeapObject()) { + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), &Memory::Object_at(pc_), HeapObject::cast(target)); + } } @@ -301,11 +316,19 @@ JSGlobalPropertyCell* RelocInfo::target_cell() { } -void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell) { +void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, + WriteBarrierMode mode) { ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; Memory::Address_at(pc_) = address; CPU::FlushICache(pc_, sizeof(Address)); + if (mode == UPDATE_WRITE_BARRIER && + host() != NULL) { + // TODO(1550) We are passing NULL as a slot because cell can never be on + // evacuation candidate. + host()->GetHeap()->incremental_marking()->RecordWrite( + host(), NULL, cell); + } } @@ -344,6 +367,11 @@ void RelocInfo::set_call_address(Address target) { target; CPU::FlushICache(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset, sizeof(Address)); + if (host() != NULL) { + Object* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( + host(), this, HeapObject::cast(target_code)); + } } @@ -368,14 +396,14 @@ Object** RelocInfo::call_object_address() { void RelocInfo::Visit(ObjectVisitor* visitor) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - visitor->VisitPointer(target_object_address()); + visitor->VisitEmbeddedPointer(this); CPU::FlushICache(pc_, sizeof(Address)); } else if (RelocInfo::IsCodeTarget(mode)) { visitor->VisitCodeTarget(this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { visitor->VisitGlobalPropertyCell(this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - visitor->VisitExternalReference(target_reference_address()); + visitor->VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); #ifdef ENABLE_DEBUGGER_SUPPORT // TODO(isolates): Get a cached isolate below. @@ -396,14 +424,14 @@ template<typename StaticVisitor> void RelocInfo::Visit(Heap* heap) { RelocInfo::Mode mode = rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitPointer(heap, target_object_address()); + StaticVisitor::VisitEmbeddedPointer(heap, this); CPU::FlushICache(pc_, sizeof(Address)); } else if (RelocInfo::IsCodeTarget(mode)) { StaticVisitor::VisitCodeTarget(heap, this); } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { StaticVisitor::VisitGlobalPropertyCell(heap, this); } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - StaticVisitor::VisitExternalReference(target_reference_address()); + StaticVisitor::VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); #ifdef ENABLE_DEBUGGER_SUPPORT } else if (heap->isolate()->debug()->has_break_points() && diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index 745fdaeb8f..eb8d7d4d99 100644 --- a/deps/v8/src/x64/assembler-x64.cc +++ b/deps/v8/src/x64/assembler-x64.cc @@ -47,7 +47,7 @@ uint64_t CpuFeatures::found_by_runtime_probing_ = 0; void CpuFeatures::Probe() { - ASSERT(!initialized_); + ASSERT(supported_ == CpuFeatures::kDefaultCpuFeatures); #ifdef DEBUG initialized_ = true; #endif @@ -383,7 +383,7 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) } #endif - // Setup buffer pointers. + // Set up buffer pointers. ASSERT(buffer_ != NULL); pc_ = buffer_; reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); @@ -412,7 +412,7 @@ void Assembler::GetCode(CodeDesc* desc) { // Finalize code (at this point overflow() may be true, but the gap ensures // that we are still not overlapping instructions and relocation info). ASSERT(pc_ <= reloc_info_writer.pos()); // No overlap. - // Setup code descriptor. + // Set up code descriptor. desc->buffer = buffer_; desc->buffer_size = buffer_size_; desc->instr_size = pc_offset(); @@ -426,13 +426,7 @@ void Assembler::GetCode(CodeDesc* desc) { void Assembler::Align(int m) { ASSERT(IsPowerOf2(m)); int delta = (m - (pc_offset() & (m - 1))) & (m - 1); - while (delta >= 9) { - nop(9); - delta -= 9; - } - if (delta > 0) { - nop(delta); - } + Nop(delta); } @@ -441,6 +435,15 @@ void Assembler::CodeTargetAlign() { } +bool Assembler::IsNop(Address addr) { + Address a = addr; + while (*a == 0x66) a++; + if (*a == 0x90) return true; + if (a[0] == 0xf && a[1] == 0x1f) return true; + return false; +} + + void Assembler::bind_to(Label* L, int pos) { ASSERT(!L->is_bound()); // Label may only be bound once. ASSERT(0 <= pos && pos <= pc_offset()); // Position must be valid. @@ -499,7 +502,7 @@ void Assembler::GrowBuffer() { V8::FatalProcessOutOfMemory("Assembler::GrowBuffer"); } - // Setup new buffer. + // Set up new buffer. desc.buffer = NewArray<byte>(desc.buffer_size); desc.instr_size = pc_offset(); desc.reloc_size = @@ -1763,7 +1766,7 @@ void Assembler::notl(Register dst) { } -void Assembler::nop(int n) { +void Assembler::Nop(int n) { // The recommended muti-byte sequences of NOP instructions from the Intel 64 // and IA-32 Architectures Software Developer's Manual. // @@ -1778,73 +1781,64 @@ void Assembler::nop(int n) { // 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 66 0F 1F 84 00 00 00 00 // 00000000H] 00H - ASSERT(1 <= n); - ASSERT(n <= 9); EnsureSpace ensure_space(this); - switch (n) { - case 1: - emit(0x90); - return; - case 2: - emit(0x66); - emit(0x90); - return; - case 3: - emit(0x0f); - emit(0x1f); - emit(0x00); - return; - case 4: - emit(0x0f); - emit(0x1f); - emit(0x40); - emit(0x00); - return; - case 5: - emit(0x0f); - emit(0x1f); - emit(0x44); - emit(0x00); - emit(0x00); - return; - case 6: - emit(0x66); - emit(0x0f); - emit(0x1f); - emit(0x44); - emit(0x00); - emit(0x00); - return; - case 7: - emit(0x0f); - emit(0x1f); - emit(0x80); - emit(0x00); - emit(0x00); - emit(0x00); - emit(0x00); - return; - case 8: - emit(0x0f); - emit(0x1f); - emit(0x84); - emit(0x00); - emit(0x00); - emit(0x00); - emit(0x00); - emit(0x00); - return; - case 9: - emit(0x66); - emit(0x0f); - emit(0x1f); - emit(0x84); - emit(0x00); - emit(0x00); - emit(0x00); - emit(0x00); - emit(0x00); - return; + while (n > 0) { + switch (n) { + case 2: + emit(0x66); + case 1: + emit(0x90); + return; + case 3: + emit(0x0f); + emit(0x1f); + emit(0x00); + return; + case 4: + emit(0x0f); + emit(0x1f); + emit(0x40); + emit(0x00); + return; + case 6: + emit(0x66); + case 5: + emit(0x0f); + emit(0x1f); + emit(0x44); + emit(0x00); + emit(0x00); + return; + case 7: + emit(0x0f); + emit(0x1f); + emit(0x80); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + default: + case 11: + emit(0x66); + n--; + case 10: + emit(0x66); + n--; + case 9: + emit(0x66); + n--; + case 8: + emit(0x0f); + emit(0x1f); + emit(0x84); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + n -= 8; + } } } @@ -2299,6 +2293,13 @@ void Assembler::fsin() { } +void Assembler::fptan() { + EnsureSpace ensure_space(this); + emit(0xD9); + emit(0xF2); +} + + void Assembler::fyl2x() { EnsureSpace ensure_space(this); emit(0xD9); @@ -2306,6 +2307,27 @@ void Assembler::fyl2x() { } +void Assembler::f2xm1() { + EnsureSpace ensure_space(this); + emit(0xD9); + emit(0xF0); +} + + +void Assembler::fscale() { + EnsureSpace ensure_space(this); + emit(0xD9); + emit(0xFD); +} + + +void Assembler::fninit() { + EnsureSpace ensure_space(this); + emit(0xDB); + emit(0xE3); +} + + void Assembler::fadd(int i) { EnsureSpace ensure_space(this); emit_farith(0xDC, 0xC0, i); @@ -2565,7 +2587,8 @@ void Assembler::movdqa(XMMRegister dst, const Operand& src) { void Assembler::extractps(Register dst, XMMRegister src, byte imm8) { - ASSERT(is_uint2(imm8)); + ASSERT(CpuFeatures::IsSupported(SSE4_1)); + ASSERT(is_uint8(imm8)); EnsureSpace ensure_space(this); emit(0x66); emit_optional_rex_32(dst, src); @@ -2983,7 +3006,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { return; } } - RelocInfo rinfo(pc_, rmode, data); + RelocInfo rinfo(pc_, rmode, data, NULL); reloc_info_writer.Write(&rinfo); } diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index 2e373faac5..745850d822 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -45,22 +45,22 @@ namespace internal { // Utility functions // Test whether a 64-bit value is in a specific range. -static inline bool is_uint32(int64_t x) { +inline bool is_uint32(int64_t x) { static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff); return static_cast<uint64_t>(x) <= kMaxUInt32; } -static inline bool is_int32(int64_t x) { +inline bool is_int32(int64_t x) { static const int64_t kMinInt32 = -V8_INT64_C(0x80000000); return is_uint32(x - kMinInt32); } -static inline bool uint_is_int32(uint64_t x) { +inline bool uint_is_int32(uint64_t x) { static const uint64_t kMaxInt32 = V8_UINT64_C(0x7fffffff); return x <= kMaxInt32; } -static inline bool is_uint32(uint64_t x) { +inline bool is_uint32(uint64_t x) { static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff); return x <= kMaxUInt32; } @@ -215,6 +215,12 @@ struct XMMRegister { return names[index]; } + static XMMRegister from_code(int code) { + ASSERT(code >= 0); + ASSERT(code < kNumRegisters); + XMMRegister r = { code }; + return r; + } bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; } bool is(XMMRegister reg) const { return code_ == reg.code_; } int code() const { @@ -630,6 +636,7 @@ class Assembler : public AssemblerBase { // possible to align the pc offset to a multiple // of m, where m must be a power of 2. void Align(int m); + void Nop(int bytes = 1); // Aligns code to something that's optimal for a jump target for the platform. void CodeTargetAlign(); @@ -643,7 +650,6 @@ class Assembler : public AssemblerBase { void push_imm32(int32_t imm32); void push(Register src); void push(const Operand& src); - void push(Handle<Object> handle); void pop(Register dst); void pop(const Operand& dst); @@ -735,6 +741,10 @@ class Assembler : public AssemblerBase { immediate_arithmetic_op_32(0x0, dst, src); } + void addl(const Operand& dst, Register src) { + arithmetic_op_32(0x01, src, dst); + } + void addq(Register dst, Register src) { arithmetic_op(0x03, dst, src); } @@ -1145,7 +1155,6 @@ class Assembler : public AssemblerBase { void hlt(); void int3(); void nop(); - void nop(int n); void rdtsc(); void ret(int imm16); void setcc(Condition cc, Register reg); @@ -1266,7 +1275,11 @@ class Assembler : public AssemblerBase { void fsin(); void fcos(); + void fptan(); void fyl2x(); + void f2xm1(); + void fscale(); + void fninit(); void frndint(); @@ -1388,19 +1401,20 @@ class Assembler : public AssemblerBase { return static_cast<int>(reloc_info_writer.pos() - pc_); } - static bool IsNop(Address addr) { return *addr == 0x90; } + static bool IsNop(Address addr); // Avoid overflows for displacements etc. static const int kMaximalBufferSize = 512*MB; static const int kMinimalBufferSize = 4*KB; + byte byte_at(int pos) { return buffer_[pos]; } + void set_byte_at(int pos, byte value) { buffer_[pos] = value; } + protected: bool emit_debug_code() const { return emit_debug_code_; } private: byte* addr_at(int pos) { return buffer_ + pos; } - byte byte_at(int pos) { return buffer_[pos]; } - void set_byte_at(int pos, byte value) { buffer_[pos] = value; } uint32_t long_at(int pos) { return *reinterpret_cast<uint32_t*>(addr_at(pos)); } diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc index db06909daa..d9361fdd02 100644 --- a/deps/v8/src/x64/builtins-x64.cc +++ b/deps/v8/src/x64/builtins-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -73,309 +73,289 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, } -void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { +static void Generate_JSConstructStubHelper(MacroAssembler* masm, + bool is_api_function, + bool count_constructions) { // ----------- S t a t e ------------- // -- rax: number of arguments // -- rdi: constructor function // ----------------------------------- - Label non_function_call; - // Check that function is not a smi. - __ JumpIfSmi(rdi, &non_function_call); - // Check that function is a JSFunction. - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); - __ j(not_equal, &non_function_call); - - // Jump to the function-specific construct stub. - __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); - __ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kConstructStubOffset)); - __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize)); - __ jmp(rbx); - - // rdi: called object - // rax: number of arguments - __ bind(&non_function_call); - // Set expected number of arguments to zero (not changing rax). - __ Set(rbx, 0); - __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); - __ SetCallKind(rcx, CALL_AS_METHOD); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); -} - - -static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool count_constructions) { // Should never count constructions for api objects. ASSERT(!is_api_function || !count_constructions); - // Enter a construct frame. - __ EnterConstructFrame(); + // Enter a construct frame. + { + FrameScope scope(masm, StackFrame::CONSTRUCT); - // Store a smi-tagged arguments count on the stack. - __ Integer32ToSmi(rax, rax); - __ push(rax); + // Store a smi-tagged arguments count on the stack. + __ Integer32ToSmi(rax, rax); + __ push(rax); - // Push the function to invoke on the stack. - __ push(rdi); + // Push the function to invoke on the stack. + __ push(rdi); - // Try to allocate the object without transitioning into C code. If any of the - // preconditions is not met, the code bails out to the runtime call. - Label rt_call, allocated; - if (FLAG_inline_new) { - Label undo_allocation; + // Try to allocate the object without transitioning into C code. If any of + // the preconditions is not met, the code bails out to the runtime call. + Label rt_call, allocated; + if (FLAG_inline_new) { + Label undo_allocation; #ifdef ENABLE_DEBUGGER_SUPPORT - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(masm->isolate()); - __ movq(kScratchRegister, debug_step_in_fp); - __ cmpq(Operand(kScratchRegister, 0), Immediate(0)); - __ j(not_equal, &rt_call); + ExternalReference debug_step_in_fp = + ExternalReference::debug_step_in_fp_address(masm->isolate()); + __ movq(kScratchRegister, debug_step_in_fp); + __ cmpq(Operand(kScratchRegister, 0), Immediate(0)); + __ j(not_equal, &rt_call); #endif - // Verified that the constructor is a JSFunction. - // Load the initial map and verify that it is in fact a map. - // rdi: constructor - __ movq(rax, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi - STATIC_ASSERT(kSmiTag == 0); - __ JumpIfSmi(rax, &rt_call); - // rdi: constructor - // rax: initial map (if proven valid below) - __ CmpObjectType(rax, MAP_TYPE, rbx); - __ j(not_equal, &rt_call); - - // Check that the constructor is not constructing a JSFunction (see comments - // in Runtime_NewObject in runtime.cc). In which case the initial map's - // instance type would be JS_FUNCTION_TYPE. - // rdi: constructor - // rax: initial map - __ CmpInstanceType(rax, JS_FUNCTION_TYPE); - __ j(equal, &rt_call); - - if (count_constructions) { - Label allocate; - // Decrease generous allocation count. - __ movq(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); - __ decb(FieldOperand(rcx, SharedFunctionInfo::kConstructionCountOffset)); - __ j(not_zero, &allocate); + // Verified that the constructor is a JSFunction. + // Load the initial map and verify that it is in fact a map. + // rdi: constructor + __ movq(rax, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi + ASSERT(kSmiTag == 0); + __ JumpIfSmi(rax, &rt_call); + // rdi: constructor + // rax: initial map (if proven valid below) + __ CmpObjectType(rax, MAP_TYPE, rbx); + __ j(not_equal, &rt_call); + + // Check that the constructor is not constructing a JSFunction (see + // comments in Runtime_NewObject in runtime.cc). In which case the + // initial map's instance type would be JS_FUNCTION_TYPE. + // rdi: constructor + // rax: initial map + __ CmpInstanceType(rax, JS_FUNCTION_TYPE); + __ j(equal, &rt_call); - __ push(rax); - __ push(rdi); + if (count_constructions) { + Label allocate; + // Decrease generous allocation count. + __ movq(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + __ decb(FieldOperand(rcx, + SharedFunctionInfo::kConstructionCountOffset)); + __ j(not_zero, &allocate); - __ push(rdi); // constructor - // The call will replace the stub, so the countdown is only done once. - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); + __ push(rax); + __ push(rdi); - __ pop(rdi); - __ pop(rax); + __ push(rdi); // constructor + // The call will replace the stub, so the countdown is only done once. + __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - __ bind(&allocate); - } + __ pop(rdi); + __ pop(rax); + + __ bind(&allocate); + } - // Now allocate the JSObject on the heap. - __ movzxbq(rdi, FieldOperand(rax, Map::kInstanceSizeOffset)); - __ shl(rdi, Immediate(kPointerSizeLog2)); - // rdi: size of new object - __ AllocateInNewSpace(rdi, - rbx, - rdi, - no_reg, - &rt_call, - NO_ALLOCATION_FLAGS); - // Allocated the JSObject, now initialize the fields. - // rax: initial map - // rbx: JSObject (not HeapObject tagged - the actual address). - // rdi: start of next object - __ movq(Operand(rbx, JSObject::kMapOffset), rax); - __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex); - __ movq(Operand(rbx, JSObject::kPropertiesOffset), rcx); - __ movq(Operand(rbx, JSObject::kElementsOffset), rcx); - // Set extra fields in the newly allocated object. - // rax: initial map - // rbx: JSObject - // rdi: start of next object - { Label loop, entry; - // To allow for truncation. + // Now allocate the JSObject on the heap. + __ movzxbq(rdi, FieldOperand(rax, Map::kInstanceSizeOffset)); + __ shl(rdi, Immediate(kPointerSizeLog2)); + // rdi: size of new object + __ AllocateInNewSpace(rdi, + rbx, + rdi, + no_reg, + &rt_call, + NO_ALLOCATION_FLAGS); + // Allocated the JSObject, now initialize the fields. + // rax: initial map + // rbx: JSObject (not HeapObject tagged - the actual address). + // rdi: start of next object + __ movq(Operand(rbx, JSObject::kMapOffset), rax); + __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex); + __ movq(Operand(rbx, JSObject::kPropertiesOffset), rcx); + __ movq(Operand(rbx, JSObject::kElementsOffset), rcx); + // Set extra fields in the newly allocated object. + // rax: initial map + // rbx: JSObject + // rdi: start of next object + __ lea(rcx, Operand(rbx, JSObject::kHeaderSize)); + __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); if (count_constructions) { + __ movzxbq(rsi, + FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset)); + __ lea(rsi, + Operand(rbx, rsi, times_pointer_size, JSObject::kHeaderSize)); + // rsi: offset of first field after pre-allocated fields + if (FLAG_debug_code) { + __ cmpq(rsi, rdi); + __ Assert(less_equal, + "Unexpected number of pre-allocated property fields."); + } + __ InitializeFieldsWithFiller(rcx, rsi, rdx); __ LoadRoot(rdx, Heap::kOnePointerFillerMapRootIndex); - } else { + } + __ InitializeFieldsWithFiller(rcx, rdi, rdx); + + // Add the object tag to make the JSObject real, so that we can continue + // and jump into the continuation code at any time from now on. Any + // failures need to undo the allocation, so that the heap is in a + // consistent state and verifiable. + // rax: initial map + // rbx: JSObject + // rdi: start of next object + __ or_(rbx, Immediate(kHeapObjectTag)); + + // Check if a non-empty properties array is needed. + // Allocate and initialize a FixedArray if it is. + // rax: initial map + // rbx: JSObject + // rdi: start of next object + // Calculate total properties described map. + __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset)); + __ movzxbq(rcx, + FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset)); + __ addq(rdx, rcx); + // Calculate unused properties past the end of the in-object properties. + __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset)); + __ subq(rdx, rcx); + // Done if no extra properties are to be allocated. + __ j(zero, &allocated); + __ Assert(positive, "Property allocation count failed."); + + // Scale the number of elements by pointer size and add the header for + // FixedArrays to the start of the next object calculation from above. + // rbx: JSObject + // rdi: start of next object (will be start of FixedArray) + // rdx: number of elements in properties array + __ AllocateInNewSpace(FixedArray::kHeaderSize, + times_pointer_size, + rdx, + rdi, + rax, + no_reg, + &undo_allocation, + RESULT_CONTAINS_TOP); + + // Initialize the FixedArray. + // rbx: JSObject + // rdi: FixedArray + // rdx: number of elements + // rax: start of next object + __ LoadRoot(rcx, Heap::kFixedArrayMapRootIndex); + __ movq(Operand(rdi, HeapObject::kMapOffset), rcx); // setup the map + __ Integer32ToSmi(rdx, rdx); + __ movq(Operand(rdi, FixedArray::kLengthOffset), rdx); // and length + + // Initialize the fields to undefined. + // rbx: JSObject + // rdi: FixedArray + // rax: start of next object + // rdx: number of elements + { Label loop, entry; __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); + __ lea(rcx, Operand(rdi, FixedArray::kHeaderSize)); + __ jmp(&entry); + __ bind(&loop); + __ movq(Operand(rcx, 0), rdx); + __ addq(rcx, Immediate(kPointerSize)); + __ bind(&entry); + __ cmpq(rcx, rax); + __ j(below, &loop); } - __ lea(rcx, Operand(rbx, JSObject::kHeaderSize)); - __ jmp(&entry); - __ bind(&loop); - __ movq(Operand(rcx, 0), rdx); - __ addq(rcx, Immediate(kPointerSize)); - __ bind(&entry); - __ cmpq(rcx, rdi); - __ j(less, &loop); - } - // Add the object tag to make the JSObject real, so that we can continue and - // jump into the continuation code at any time from now on. Any failures - // need to undo the allocation, so that the heap is in a consistent state - // and verifiable. - // rax: initial map - // rbx: JSObject - // rdi: start of next object - __ or_(rbx, Immediate(kHeapObjectTag)); - - // Check if a non-empty properties array is needed. - // Allocate and initialize a FixedArray if it is. - // rax: initial map - // rbx: JSObject - // rdi: start of next object - // Calculate total properties described map. - __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset)); - __ movzxbq(rcx, FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset)); - __ addq(rdx, rcx); - // Calculate unused properties past the end of the in-object properties. - __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset)); - __ subq(rdx, rcx); - // Done if no extra properties are to be allocated. - __ j(zero, &allocated); - __ Assert(positive, "Property allocation count failed."); - - // Scale the number of elements by pointer size and add the header for - // FixedArrays to the start of the next object calculation from above. - // rbx: JSObject - // rdi: start of next object (will be start of FixedArray) - // rdx: number of elements in properties array - __ AllocateInNewSpace(FixedArray::kHeaderSize, - times_pointer_size, - rdx, - rdi, - rax, - no_reg, - &undo_allocation, - RESULT_CONTAINS_TOP); - - // Initialize the FixedArray. - // rbx: JSObject - // rdi: FixedArray - // rdx: number of elements - // rax: start of next object - __ LoadRoot(rcx, Heap::kFixedArrayMapRootIndex); - __ movq(Operand(rdi, HeapObject::kMapOffset), rcx); // setup the map - __ Integer32ToSmi(rdx, rdx); - __ movq(Operand(rdi, FixedArray::kLengthOffset), rdx); // and length - - // Initialize the fields to undefined. - // rbx: JSObject - // rdi: FixedArray - // rax: start of next object - // rdx: number of elements - { Label loop, entry; - __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); - __ lea(rcx, Operand(rdi, FixedArray::kHeaderSize)); - __ jmp(&entry); - __ bind(&loop); - __ movq(Operand(rcx, 0), rdx); - __ addq(rcx, Immediate(kPointerSize)); - __ bind(&entry); - __ cmpq(rcx, rax); - __ j(below, &loop); - } + // Store the initialized FixedArray into the properties field of + // the JSObject + // rbx: JSObject + // rdi: FixedArray + __ or_(rdi, Immediate(kHeapObjectTag)); // add the heap tag + __ movq(FieldOperand(rbx, JSObject::kPropertiesOffset), rdi); - // Store the initialized FixedArray into the properties field of - // the JSObject - // rbx: JSObject - // rdi: FixedArray - __ or_(rdi, Immediate(kHeapObjectTag)); // add the heap tag - __ movq(FieldOperand(rbx, JSObject::kPropertiesOffset), rdi); + // Continue with JSObject being successfully allocated + // rbx: JSObject + __ jmp(&allocated); - // Continue with JSObject being successfully allocated - // rbx: JSObject - __ jmp(&allocated); - - // Undo the setting of the new top so that the heap is verifiable. For - // example, the map's unused properties potentially do not match the - // allocated objects unused properties. - // rbx: JSObject (previous new top) - __ bind(&undo_allocation); - __ UndoAllocationInNewSpace(rbx); - } + // Undo the setting of the new top so that the heap is verifiable. For + // example, the map's unused properties potentially do not match the + // allocated objects unused properties. + // rbx: JSObject (previous new top) + __ bind(&undo_allocation); + __ UndoAllocationInNewSpace(rbx); + } - // Allocate the new receiver object using the runtime call. - // rdi: function (constructor) - __ bind(&rt_call); - // Must restore rdi (constructor) before calling runtime. - __ movq(rdi, Operand(rsp, 0)); - __ push(rdi); - __ CallRuntime(Runtime::kNewObject, 1); - __ movq(rbx, rax); // store result in rbx + // Allocate the new receiver object using the runtime call. + // rdi: function (constructor) + __ bind(&rt_call); + // Must restore rdi (constructor) before calling runtime. + __ movq(rdi, Operand(rsp, 0)); + __ push(rdi); + __ CallRuntime(Runtime::kNewObject, 1); + __ movq(rbx, rax); // store result in rbx - // New object allocated. - // rbx: newly allocated object - __ bind(&allocated); - // Retrieve the function from the stack. - __ pop(rdi); + // New object allocated. + // rbx: newly allocated object + __ bind(&allocated); + // Retrieve the function from the stack. + __ pop(rdi); - // Retrieve smi-tagged arguments count from the stack. - __ movq(rax, Operand(rsp, 0)); - __ SmiToInteger32(rax, rax); + // Retrieve smi-tagged arguments count from the stack. + __ movq(rax, Operand(rsp, 0)); + __ SmiToInteger32(rax, rax); - // Push the allocated receiver to the stack. We need two copies - // because we may have to return the original one and the calling - // conventions dictate that the called function pops the receiver. - __ push(rbx); - __ push(rbx); + // Push the allocated receiver to the stack. We need two copies + // because we may have to return the original one and the calling + // conventions dictate that the called function pops the receiver. + __ push(rbx); + __ push(rbx); - // Setup pointer to last argument. - __ lea(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset)); + // Set up pointer to last argument. + __ lea(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset)); - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ movq(rcx, rax); - __ jmp(&entry); - __ bind(&loop); - __ push(Operand(rbx, rcx, times_pointer_size, 0)); - __ bind(&entry); - __ decq(rcx); - __ j(greater_equal, &loop); + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ movq(rcx, rax); + __ jmp(&entry); + __ bind(&loop); + __ push(Operand(rbx, rcx, times_pointer_size, 0)); + __ bind(&entry); + __ decq(rcx); + __ j(greater_equal, &loop); + + // Call the function. + if (is_api_function) { + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + Handle<Code> code = + masm->isolate()->builtins()->HandleApiCallConstruct(); + ParameterCount expected(0); + __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET, + CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); + } else { + ParameterCount actual(rax); + __ InvokeFunction(rdi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } - // Call the function. - if (is_api_function) { - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - Handle<Code> code = - masm->isolate()->builtins()->HandleApiCallConstruct(); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET, - CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); - } else { - ParameterCount actual(rax); - __ InvokeFunction(rdi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); - } + // Restore context from the frame. + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - // Restore context from the frame. - __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, exit; + // If the result is a smi, it is *not* an object in the ECMA sense. + __ JumpIfSmi(rax, &use_receiver); - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; - // If the result is a smi, it is *not* an object in the ECMA sense. - __ JumpIfSmi(rax, &use_receiver); + // If the type of the result (stored in its map) is less than + // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); + __ j(above_equal, &exit); - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); - __ j(above_equal, &exit); + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ movq(rax, Operand(rsp, 0)); - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ movq(rax, Operand(rsp, 0)); + // Restore the arguments count and leave the construct frame. + __ bind(&exit); + __ movq(rbx, Operand(rsp, kPointerSize)); // Get arguments count. - // Restore the arguments count and leave the construct frame. - __ bind(&exit); - __ movq(rbx, Operand(rsp, kPointerSize)); // get arguments count - __ LeaveConstructFrame(); + // Leave construct frame. + } // Remove caller arguments from the stack and return. __ pop(rcx); @@ -413,104 +393,108 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // - Object*** argv // (see Handle::Invoke in execution.cc). - // Platform specific argument handling. After this, the stack contains - // an internal frame and the pushed function and receiver, and - // register rax and rbx holds the argument count and argument array, - // while rdi holds the function pointer and rsi the context. -#ifdef _WIN64 - // MSVC parameters in: - // rcx : entry (ignored) - // rdx : function - // r8 : receiver - // r9 : argc - // [rsp+0x20] : argv - - // Clear the context before we push it when entering the JS frame. - __ Set(rsi, 0); - __ EnterInternalFrame(); - - // Load the function context into rsi. - __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset)); - - // Push the function and the receiver onto the stack. - __ push(rdx); - __ push(r8); + // Open a C++ scope for the FrameScope. + { + // Platform specific argument handling. After this, the stack contains + // an internal frame and the pushed function and receiver, and + // register rax and rbx holds the argument count and argument array, + // while rdi holds the function pointer and rsi the context. - // Load the number of arguments and setup pointer to the arguments. - __ movq(rax, r9); - // Load the previous frame pointer to access C argument on stack - __ movq(kScratchRegister, Operand(rbp, 0)); - __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset)); - // Load the function pointer into rdi. - __ movq(rdi, rdx); +#ifdef _WIN64 + // MSVC parameters in: + // rcx : entry (ignored) + // rdx : function + // r8 : receiver + // r9 : argc + // [rsp+0x20] : argv + + // Clear the context before we push it when entering the internal frame. + __ Set(rsi, 0); + // Enter an internal frame. + FrameScope scope(masm, StackFrame::INTERNAL); + + // Load the function context into rsi. + __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset)); + + // Push the function and the receiver onto the stack. + __ push(rdx); + __ push(r8); + + // Load the number of arguments and setup pointer to the arguments. + __ movq(rax, r9); + // Load the previous frame pointer to access C argument on stack + __ movq(kScratchRegister, Operand(rbp, 0)); + __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset)); + // Load the function pointer into rdi. + __ movq(rdi, rdx); #else // _WIN64 - // GCC parameters in: - // rdi : entry (ignored) - // rsi : function - // rdx : receiver - // rcx : argc - // r8 : argv - - __ movq(rdi, rsi); - // rdi : function - - // Clear the context before we push it when entering the JS frame. - __ Set(rsi, 0); - // Enter an internal frame. - __ EnterInternalFrame(); - - // Push the function and receiver and setup the context. - __ push(rdi); - __ push(rdx); - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + // GCC parameters in: + // rdi : entry (ignored) + // rsi : function + // rdx : receiver + // rcx : argc + // r8 : argv + + __ movq(rdi, rsi); + // rdi : function + + // Clear the context before we push it when entering the internal frame. + __ Set(rsi, 0); + // Enter an internal frame. + FrameScope scope(masm, StackFrame::INTERNAL); + + // Push the function and receiver and setup the context. + __ push(rdi); + __ push(rdx); + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - // Load the number of arguments and setup pointer to the arguments. - __ movq(rax, rcx); - __ movq(rbx, r8); + // Load the number of arguments and setup pointer to the arguments. + __ movq(rax, rcx); + __ movq(rbx, r8); #endif // _WIN64 - // Current stack contents: - // [rsp + 2 * kPointerSize ... ]: Internal frame - // [rsp + kPointerSize] : function - // [rsp] : receiver - // Current register contents: - // rax : argc - // rbx : argv - // rsi : context - // rdi : function - - // Copy arguments to the stack in a loop. - // Register rbx points to array of pointers to handle locations. - // Push the values of these handles. - Label loop, entry; - __ Set(rcx, 0); // Set loop variable to 0. - __ jmp(&entry); - __ bind(&loop); - __ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0)); - __ push(Operand(kScratchRegister, 0)); // dereference handle - __ addq(rcx, Immediate(1)); - __ bind(&entry); - __ cmpq(rcx, rax); - __ j(not_equal, &loop); - - // Invoke the code. - if (is_construct) { - // Expects rdi to hold function pointer. - __ Call(masm->isolate()->builtins()->JSConstructCall(), - RelocInfo::CODE_TARGET); - } else { - ParameterCount actual(rax); - // Function must be in rdi. - __ InvokeFunction(rdi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Current stack contents: + // [rsp + 2 * kPointerSize ... ]: Internal frame + // [rsp + kPointerSize] : function + // [rsp] : receiver + // Current register contents: + // rax : argc + // rbx : argv + // rsi : context + // rdi : function + + // Copy arguments to the stack in a loop. + // Register rbx points to array of pointers to handle locations. + // Push the values of these handles. + Label loop, entry; + __ Set(rcx, 0); // Set loop variable to 0. + __ jmp(&entry); + __ bind(&loop); + __ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0)); + __ push(Operand(kScratchRegister, 0)); // dereference handle + __ addq(rcx, Immediate(1)); + __ bind(&entry); + __ cmpq(rcx, rax); + __ j(not_equal, &loop); + + // Invoke the code. + if (is_construct) { + // Expects rdi to hold function pointer. + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); + __ CallStub(&stub); + } else { + ParameterCount actual(rax); + // Function must be in rdi. + __ InvokeFunction(rdi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + } + // Exit the internal frame. Notice that this also removes the empty + // context and the function left on the stack by the code + // invocation. } - // Exit the JS frame. Notice that this also removes the empty - // context and the function left on the stack by the code - // invocation. - __ LeaveInternalFrame(); // TODO(X64): Is argument correct? Is there a receiver to remove? - __ ret(1 * kPointerSize); // remove receiver + __ ret(1 * kPointerSize); // Remove receiver. } @@ -526,23 +510,24 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { void Builtins::Generate_LazyCompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function onto the stack. - __ push(rdi); - // Push call kind information. - __ push(rcx); + // Push a copy of the function onto the stack. + __ push(rdi); + // Push call kind information. + __ push(rcx); - __ push(rdi); // Function is also the parameter to the runtime call. - __ CallRuntime(Runtime::kLazyCompile, 1); + __ push(rdi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kLazyCompile, 1); - // Restore call kind information. - __ pop(rcx); - // Restore receiver. - __ pop(rdi); + // Restore call kind information. + __ pop(rcx); + // Restore receiver. + __ pop(rdi); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ lea(rax, FieldOperand(rax, Code::kHeaderSize)); @@ -552,23 +537,24 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) { void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function onto the stack. - __ push(rdi); - // Push call kind information. - __ push(rcx); + // Push a copy of the function onto the stack. + __ push(rdi); + // Push call kind information. + __ push(rcx); - __ push(rdi); // Function is also the parameter to the runtime call. - __ CallRuntime(Runtime::kLazyRecompile, 1); + __ push(rdi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kLazyRecompile, 1); - // Restore call kind information. - __ pop(rcx); - // Restore function. - __ pop(rdi); + // Restore call kind information. + __ pop(rcx); + // Restore function. + __ pop(rdi); - // Tear down temporary frame. - __ LeaveInternalFrame(); + // Tear down internal frame. + } // Do a tail-call of the compiled function. __ lea(rax, FieldOperand(rax, Code::kHeaderSize)); @@ -579,14 +565,15 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, Deoptimizer::BailoutType type) { // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Pass the deoptimization type to the runtime system. - __ Push(Smi::FromInt(static_cast<int>(type))); + // Pass the deoptimization type to the runtime system. + __ Push(Smi::FromInt(static_cast<int>(type))); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); - // Tear down temporary frame. - __ LeaveInternalFrame(); + __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + // Tear down internal frame. + } // Get the full codegen state from the stack and untag it. __ SmiToInteger32(rcx, Operand(rsp, 1 * kPointerSize)); @@ -623,9 +610,10 @@ void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { // the registers without worrying about which of them contain // pointers. This seems a bit fragile. __ Pushad(); - __ EnterInternalFrame(); - __ CallRuntime(Runtime::kNotifyOSR, 0); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kNotifyOSR, 0); + } __ Popad(); __ ret(0); } @@ -647,7 +635,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ testq(rax, rax); __ j(not_zero, &done); __ pop(rbx); - __ Push(FACTORY->undefined_value()); + __ Push(masm->isolate()->factory()->undefined_value()); __ push(rbx); __ incq(rax); __ bind(&done); @@ -695,18 +683,21 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ j(above_equal, &shift_arguments); __ bind(&convert_to_object); - __ EnterInternalFrame(); // In order to preserve argument count. - __ Integer32ToSmi(rax, rax); - __ push(rax); + { + // Enter an internal frame in order to preserve argument count. + FrameScope scope(masm, StackFrame::INTERNAL); + __ Integer32ToSmi(rax, rax); + __ push(rax); - __ push(rbx); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ movq(rbx, rax); - __ Set(rdx, 0); // indicate regular JS_FUNCTION + __ push(rbx); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ movq(rbx, rax); + __ Set(rdx, 0); // indicate regular JS_FUNCTION + + __ pop(rax); + __ SmiToInteger32(rax, rax); + } - __ pop(rax); - __ SmiToInteger32(rax, rax); - __ LeaveInternalFrame(); // Restore the function to rdi. __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); __ jmp(&patch_receiver, Label::kNear); @@ -807,166 +798,164 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // rsp+8: arguments // rsp+16: receiver ("this") // rsp+24: function - __ EnterInternalFrame(); - // Stack frame: - // rbp: Old base pointer - // rbp[1]: return address - // rbp[2]: function arguments - // rbp[3]: receiver - // rbp[4]: function - static const int kArgumentsOffset = 2 * kPointerSize; - static const int kReceiverOffset = 3 * kPointerSize; - static const int kFunctionOffset = 4 * kPointerSize; - - __ push(Operand(rbp, kFunctionOffset)); - __ push(Operand(rbp, kArgumentsOffset)); - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); - - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - Label okay; - __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex); - __ movq(rcx, rsp); - // Make rcx the space we have left. The stack might already be overflowed - // here which will cause rcx to become negative. - __ subq(rcx, kScratchRegister); - // Make rdx the space we need for the array when it is unrolled onto the - // stack. - __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2); - // Check if the arguments will overflow the stack. - __ cmpq(rcx, rdx); - __ j(greater, &okay); // Signed comparison. - - // Out of stack space. - __ push(Operand(rbp, kFunctionOffset)); - __ push(rax); - __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); - __ bind(&okay); - // End of stack check. - - // Push current index and limit. - const int kLimitOffset = - StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; - const int kIndexOffset = kLimitOffset - 1 * kPointerSize; - __ push(rax); // limit - __ push(Immediate(0)); // index - - // Get the receiver. - __ movq(rbx, Operand(rbp, kReceiverOffset)); - - // Check that the function is a JS function (otherwise it must be a proxy). - Label push_receiver; - __ movq(rdi, Operand(rbp, kFunctionOffset)); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); - __ j(not_equal, &push_receiver); + { + FrameScope frame_scope(masm, StackFrame::INTERNAL); + // Stack frame: + // rbp: Old base pointer + // rbp[1]: return address + // rbp[2]: function arguments + // rbp[3]: receiver + // rbp[4]: function + static const int kArgumentsOffset = 2 * kPointerSize; + static const int kReceiverOffset = 3 * kPointerSize; + static const int kFunctionOffset = 4 * kPointerSize; + + __ push(Operand(rbp, kFunctionOffset)); + __ push(Operand(rbp, kArgumentsOffset)); + __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); + + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + Label okay; + __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex); + __ movq(rcx, rsp); + // Make rcx the space we have left. The stack might already be overflowed + // here which will cause rcx to become negative. + __ subq(rcx, kScratchRegister); + // Make rdx the space we need for the array when it is unrolled onto the + // stack. + __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2); + // Check if the arguments will overflow the stack. + __ cmpq(rcx, rdx); + __ j(greater, &okay); // Signed comparison. + + // Out of stack space. + __ push(Operand(rbp, kFunctionOffset)); + __ push(rax); + __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); + __ bind(&okay); + // End of stack check. + + // Push current index and limit. + const int kLimitOffset = + StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; + const int kIndexOffset = kLimitOffset - 1 * kPointerSize; + __ push(rax); // limit + __ push(Immediate(0)); // index + + // Get the receiver. + __ movq(rbx, Operand(rbp, kReceiverOffset)); + + // Check that the function is a JS function (otherwise it must be a proxy). + Label push_receiver; + __ movq(rdi, Operand(rbp, kFunctionOffset)); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &push_receiver); + + // Change context eagerly to get the right global object if necessary. + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - // Change context eagerly to get the right global object if necessary. - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + // Do not transform the receiver for strict mode functions. + Label call_to_object, use_global_receiver; + __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + __ testb(FieldOperand(rdx, SharedFunctionInfo::kStrictModeByteOffset), + Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte)); + __ j(not_equal, &push_receiver); - // Do not transform the receiver for strict mode functions. - Label call_to_object, use_global_receiver; - __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); - __ testb(FieldOperand(rdx, SharedFunctionInfo::kStrictModeByteOffset), - Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte)); - __ j(not_equal, &push_receiver); - - // Do not transform the receiver for natives. - __ testb(FieldOperand(rdx, SharedFunctionInfo::kNativeByteOffset), - Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); - __ j(not_equal, &push_receiver); - - // Compute the receiver in non-strict mode. - __ JumpIfSmi(rbx, &call_to_object, Label::kNear); - __ CompareRoot(rbx, Heap::kNullValueRootIndex); - __ j(equal, &use_global_receiver); - __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); - __ j(equal, &use_global_receiver); - - // If given receiver is already a JavaScript object then there's no - // reason for converting it. - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CmpObjectType(rbx, FIRST_SPEC_OBJECT_TYPE, rcx); - __ j(above_equal, &push_receiver); - - // Convert the receiver to an object. - __ bind(&call_to_object); - __ push(rbx); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ movq(rbx, rax); - __ jmp(&push_receiver, Label::kNear); - - // Use the current global receiver object as the receiver. - __ bind(&use_global_receiver); - const int kGlobalOffset = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; - __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); - __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); - __ movq(rbx, FieldOperand(rbx, kGlobalOffset)); - __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); - - // Push the receiver. - __ bind(&push_receiver); - __ push(rbx); - - // Copy all arguments from the array to the stack. - Label entry, loop; - __ movq(rax, Operand(rbp, kIndexOffset)); - __ jmp(&entry); - __ bind(&loop); - __ movq(rdx, Operand(rbp, kArgumentsOffset)); // load arguments - - // Use inline caching to speed up access to arguments. - Handle<Code> ic = - masm->isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET); - // It is important that we do not have a test instruction after the - // call. A test instruction after the call is used to indicate that - // we have generated an inline version of the keyed load. In this - // case, we know that we are not generating a test instruction next. - - // Push the nth argument. - __ push(rax); + // Do not transform the receiver for natives. + __ testb(FieldOperand(rdx, SharedFunctionInfo::kNativeByteOffset), + Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); + __ j(not_equal, &push_receiver); - // Update the index on the stack and in register rax. - __ movq(rax, Operand(rbp, kIndexOffset)); - __ SmiAddConstant(rax, rax, Smi::FromInt(1)); - __ movq(Operand(rbp, kIndexOffset), rax); + // Compute the receiver in non-strict mode. + __ JumpIfSmi(rbx, &call_to_object, Label::kNear); + __ CompareRoot(rbx, Heap::kNullValueRootIndex); + __ j(equal, &use_global_receiver); + __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); + __ j(equal, &use_global_receiver); - __ bind(&entry); - __ cmpq(rax, Operand(rbp, kLimitOffset)); - __ j(not_equal, &loop); + // If given receiver is already a JavaScript object then there's no + // reason for converting it. + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CmpObjectType(rbx, FIRST_SPEC_OBJECT_TYPE, rcx); + __ j(above_equal, &push_receiver); - // Invoke the function. - Label call_proxy; - ParameterCount actual(rax); - __ SmiToInteger32(rax, rax); - __ movq(rdi, Operand(rbp, kFunctionOffset)); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); - __ j(not_equal, &call_proxy); - __ InvokeFunction(rdi, actual, CALL_FUNCTION, - NullCallWrapper(), CALL_AS_METHOD); + // Convert the receiver to an object. + __ bind(&call_to_object); + __ push(rbx); + __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); + __ movq(rbx, rax); + __ jmp(&push_receiver, Label::kNear); - __ LeaveInternalFrame(); - __ ret(3 * kPointerSize); // remove this, receiver, and arguments + // Use the current global receiver object as the receiver. + __ bind(&use_global_receiver); + const int kGlobalOffset = + Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); + __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); + __ movq(rbx, FieldOperand(rbx, kGlobalOffset)); + __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); - // Invoke the function proxy. - __ bind(&call_proxy); - __ push(rdi); // add function proxy as last argument - __ incq(rax); - __ Set(rbx, 0); - __ SetCallKind(rcx, CALL_AS_METHOD); - __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY); - __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); + // Push the receiver. + __ bind(&push_receiver); + __ push(rbx); - __ LeaveInternalFrame(); - __ ret(3 * kPointerSize); // remove this, receiver, and arguments -} + // Copy all arguments from the array to the stack. + Label entry, loop; + __ movq(rax, Operand(rbp, kIndexOffset)); + __ jmp(&entry); + __ bind(&loop); + __ movq(rdx, Operand(rbp, kArgumentsOffset)); // load arguments + + // Use inline caching to speed up access to arguments. + Handle<Code> ic = + masm->isolate()->builtins()->KeyedLoadIC_Initialize(); + __ Call(ic, RelocInfo::CODE_TARGET); + // It is important that we do not have a test instruction after the + // call. A test instruction after the call is used to indicate that + // we have generated an inline version of the keyed load. In this + // case, we know that we are not generating a test instruction next. + + // Push the nth argument. + __ push(rax); + // Update the index on the stack and in register rax. + __ movq(rax, Operand(rbp, kIndexOffset)); + __ SmiAddConstant(rax, rax, Smi::FromInt(1)); + __ movq(Operand(rbp, kIndexOffset), rax); + + __ bind(&entry); + __ cmpq(rax, Operand(rbp, kLimitOffset)); + __ j(not_equal, &loop); + + // Invoke the function. + Label call_proxy; + ParameterCount actual(rax); + __ SmiToInteger32(rax, rax); + __ movq(rdi, Operand(rbp, kFunctionOffset)); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &call_proxy); + __ InvokeFunction(rdi, actual, CALL_FUNCTION, + NullCallWrapper(), CALL_AS_METHOD); + + frame_scope.GenerateLeaveFrame(); + __ ret(3 * kPointerSize); // remove this, receiver, and arguments + + // Invoke the function proxy. + __ bind(&call_proxy); + __ push(rdi); // add function proxy as last argument + __ incq(rax); + __ Set(rbx, 0); + __ SetCallKind(rcx, CALL_AS_METHOD); + __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY); + __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); -// Number of empty elements to allocate for an empty array. -static const int kPreallocatedArrayElements = 4; + // Leave internal frame. + } + __ ret(3 * kPointerSize); // remove this, receiver, and arguments +} // Allocate an empty JSArray. The allocated array is put into the result @@ -979,13 +968,11 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, Register scratch1, Register scratch2, Register scratch3, - int initial_capacity, Label* gc_required) { - ASSERT(initial_capacity >= 0); + const int initial_capacity = JSArray::kPreallocatedArrayElements; + STATIC_ASSERT(initial_capacity >= 0); - // Load the initial map from the array function. - __ movq(scratch1, FieldOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); + __ LoadInitialArrayMap(array_function, scratch2, scratch1); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -1005,9 +992,10 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // result: JSObject // scratch1: initial map // scratch2: start of next object + Factory* factory = masm->isolate()->factory(); __ movq(FieldOperand(result, JSObject::kMapOffset), scratch1); __ Move(FieldOperand(result, JSArray::kPropertiesOffset), - FACTORY->empty_fixed_array()); + factory->empty_fixed_array()); // Field JSArray::kElementsOffset is initialized later. __ Move(FieldOperand(result, JSArray::kLengthOffset), Smi::FromInt(0)); @@ -1015,7 +1003,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // fixed array. if (initial_capacity == 0) { __ Move(FieldOperand(result, JSArray::kElementsOffset), - FACTORY->empty_fixed_array()); + factory->empty_fixed_array()); return; } @@ -1032,15 +1020,14 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // scratch1: elements array // scratch2: start of next object __ Move(FieldOperand(scratch1, HeapObject::kMapOffset), - FACTORY->fixed_array_map()); + factory->fixed_array_map()); __ Move(FieldOperand(scratch1, FixedArray::kLengthOffset), Smi::FromInt(initial_capacity)); // Fill the FixedArray with the hole value. Inline the code if short. // Reconsider loop unfolding if kPreallocatedArrayElements gets changed. static const int kLoopUnfoldLimit = 4; - ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit); - __ Move(scratch3, FACTORY->the_hole_value()); + __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex); if (initial_capacity <= kLoopUnfoldLimit) { // Use a scratch register here to have only one reloc info when unfolding // the loop. @@ -1051,13 +1038,17 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, } } else { Label loop, entry; + __ movq(scratch2, Immediate(initial_capacity)); __ jmp(&entry); __ bind(&loop); - __ movq(Operand(scratch1, 0), scratch3); - __ addq(scratch1, Immediate(kPointerSize)); + __ movq(FieldOperand(scratch1, + scratch2, + times_pointer_size, + FixedArray::kHeaderSize), + scratch3); __ bind(&entry); - __ cmpq(scratch1, scratch2); - __ j(below, &loop); + __ decq(scratch2); + __ j(not_sign, &loop); } } @@ -1073,38 +1064,22 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, // register elements_array is scratched. static void AllocateJSArray(MacroAssembler* masm, Register array_function, // Array function. - Register array_size, // As a smi. + Register array_size, // As a smi, cannot be 0. Register result, Register elements_array, Register elements_array_end, Register scratch, bool fill_with_hole, Label* gc_required) { - Label not_empty, allocated; + __ LoadInitialArrayMap(array_function, scratch, elements_array); - // Load the initial map from the array function. - __ movq(elements_array, - FieldOperand(array_function, - JSFunction::kPrototypeOrInitialMapOffset)); - - // Check whether an empty sized array is requested. - __ testq(array_size, array_size); - __ j(not_zero, ¬_empty); - - // If an empty array is requested allocate a small elements array anyway. This - // keeps the code below free of special casing for the empty array. - int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements); - __ AllocateInNewSpace(size, - result, - elements_array_end, - scratch, - gc_required, - TAG_OBJECT); - __ jmp(&allocated); + if (FLAG_debug_code) { // Assert that array size is not zero. + __ testq(array_size, array_size); + __ Assert(not_zero, "array size is unexpectedly 0"); + } // Allocate the JSArray object together with space for a FixedArray with the // requested elements. - __ bind(¬_empty); SmiIndex index = masm->SmiToIndex(kScratchRegister, array_size, kPointerSizeLog2); __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize, @@ -1122,9 +1097,9 @@ static void AllocateJSArray(MacroAssembler* masm, // elements_array: initial map // elements_array_end: start of next object // array_size: size of array (smi) - __ bind(&allocated); + Factory* factory = masm->isolate()->factory(); __ movq(FieldOperand(result, JSObject::kMapOffset), elements_array); - __ Move(elements_array, FACTORY->empty_fixed_array()); + __ Move(elements_array, factory->empty_fixed_array()); __ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array); // Field JSArray::kElementsOffset is initialized later. __ movq(FieldOperand(result, JSArray::kLengthOffset), array_size); @@ -1143,16 +1118,7 @@ static void AllocateJSArray(MacroAssembler* masm, // elements_array_end: start of next object // array_size: size of array (smi) __ Move(FieldOperand(elements_array, JSObject::kMapOffset), - FACTORY->fixed_array_map()); - Label not_empty_2, fill_array; - __ SmiTest(array_size); - __ j(not_zero, ¬_empty_2); - // Length of the FixedArray is the number of pre-allocated elements even - // though the actual JSArray has length 0. - __ Move(FieldOperand(elements_array, FixedArray::kLengthOffset), - Smi::FromInt(kPreallocatedArrayElements)); - __ jmp(&fill_array); - __ bind(¬_empty_2); + factory->fixed_array_map()); // For non-empty JSArrays the length of the FixedArray and the JSArray is the // same. __ movq(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size); @@ -1161,10 +1127,9 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array: elements array // elements_array_end: start of next object - __ bind(&fill_array); if (fill_with_hole) { Label loop, entry; - __ Move(scratch, FACTORY->the_hole_value()); + __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex); __ lea(elements_array, Operand(elements_array, FixedArray::kHeaderSize - kHeapObjectTag)); __ jmp(&entry); @@ -1193,13 +1158,15 @@ static void AllocateJSArray(MacroAssembler* masm, // Both registers are preserved by this code so no need to differentiate between // a construct call and a normal call. static void ArrayNativeCode(MacroAssembler* masm, - Label *call_generic_code) { - Label argc_one_or_more, argc_two_or_more; + Label* call_generic_code) { + Label argc_one_or_more, argc_two_or_more, empty_array, not_empty_array, + has_non_smi_element; // Check for array construction with zero arguments. __ testq(rax, rax); __ j(not_zero, &argc_one_or_more); + __ bind(&empty_array); // Handle construction of an empty array. AllocateEmptyJSArray(masm, rdi, @@ -1207,7 +1174,6 @@ static void ArrayNativeCode(MacroAssembler* masm, rcx, rdx, r8, - kPreallocatedArrayElements, call_generic_code); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->array_function_native(), 1); @@ -1220,6 +1186,16 @@ static void ArrayNativeCode(MacroAssembler* masm, __ cmpq(rax, Immediate(1)); __ j(not_equal, &argc_two_or_more); __ movq(rdx, Operand(rsp, kPointerSize)); // Get the argument from the stack. + + __ SmiTest(rdx); + __ j(not_zero, ¬_empty_array); + __ pop(r8); // Adjust stack. + __ Drop(1); + __ push(r8); + __ movq(rax, Immediate(0)); // Treat this as a call with argc of zero. + __ jmp(&empty_array); + + __ bind(¬_empty_array); __ JumpUnlessNonNegativeSmi(rdx, call_generic_code); // Handle construction of an empty array of a certain size. Bail out if size @@ -1290,6 +1266,9 @@ static void ArrayNativeCode(MacroAssembler* masm, __ jmp(&entry); __ bind(&loop); __ movq(kScratchRegister, Operand(r9, rcx, times_pointer_size, 0)); + if (FLAG_smi_only_arrays) { + __ JumpIfNotSmi(kScratchRegister, &has_non_smi_element); + } __ movq(Operand(rdx, 0), kScratchRegister); __ addq(rdx, Immediate(kPointerSize)); __ bind(&entry); @@ -1306,6 +1285,45 @@ static void ArrayNativeCode(MacroAssembler* masm, __ push(rcx); __ movq(rax, rbx); __ ret(0); + + __ bind(&has_non_smi_element); + __ UndoAllocationInNewSpace(rbx); + __ jmp(call_generic_code); +} + + +void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : argc + // -- rsp[0] : return address + // -- rsp[8] : last argument + // ----------------------------------- + Label generic_array_code; + + // Get the InternalArray function. + __ LoadGlobalFunction(Context::INTERNAL_ARRAY_FUNCTION_INDEX, rdi); + + if (FLAG_debug_code) { + // Initial map for the builtin InternalArray functions should be maps. + __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + STATIC_ASSERT(kSmiTag == 0); + Condition not_smi = NegateCondition(masm->CheckSmi(rbx)); + __ Check(not_smi, "Unexpected initial map for InternalArray function"); + __ CmpObjectType(rbx, MAP_TYPE, rcx); + __ Check(equal, "Unexpected initial map for InternalArray function"); + } + + // Run the native code for the InternalArray function called as a normal + // function. + ArrayNativeCode(masm, &generic_array_code); + + // Jump to the generic array code in case the specialized code cannot handle + // the construction. + __ bind(&generic_array_code); + Handle<Code> array_code = + masm->isolate()->builtins()->InternalArrayCodeGeneric(); + __ Jump(array_code, RelocInfo::CODE_TARGET); } @@ -1489,6 +1507,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&invoke); __ call(rdx); + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); // Leave frame and return. LeaveArgumentsAdaptorFrame(masm); __ ret(0); @@ -1520,10 +1539,11 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Pass the function to optimize as the argument to the on-stack // replacement runtime function. - __ EnterInternalFrame(); - __ push(rax); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(rax); + __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + } // If the result was -1 it means that we couldn't optimize the // function. Just return and continue in the unoptimized version. @@ -1541,7 +1561,9 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { StackCheckStub stub; __ TailCallStub(&stub); - __ Abort("Unreachable code: returned from tail call."); + if (FLAG_debug_code) { + __ Abort("Unreachable code: returned from tail call."); + } __ bind(&ok); __ ret(0); diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 6499ea0214..9feef086e8 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -68,9 +68,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Get the function info from the stack. __ movq(rdx, Operand(rsp, 1 * kPointerSize)); - int map_index = strict_mode_ == kStrictMode - ? Context::STRICT_MODE_FUNCTION_MAP_INDEX - : Context::FUNCTION_MAP_INDEX; + int map_index = (language_mode_ == CLASSIC_MODE) + ? Context::FUNCTION_MAP_INDEX + : Context::STRICT_MODE_FUNCTION_MAP_INDEX; // Compute the function map in the current global context and set that // as the map of the allocated object. @@ -124,12 +124,12 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { // Get the function from the stack. __ movq(rcx, Operand(rsp, 1 * kPointerSize)); - // Setup the object header. + // Set up the object header. __ LoadRoot(kScratchRegister, Heap::kFunctionContextMapRootIndex); __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister); __ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length)); - // Setup the fixed slots. + // Set up the fixed slots. __ Set(rbx, 0); // Set to NULL. __ movq(Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)), rcx); __ movq(Operand(rax, Context::SlotOffset(Context::PREVIOUS_INDEX)), rsi); @@ -155,6 +155,131 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastNewBlockContextStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [rsp + (1 * kPointerSize)]: function + // [rsp + (2 * kPointerSize)]: serialized scope info + + // Try to allocate the context in new space. + Label gc; + int length = slots_ + Context::MIN_CONTEXT_SLOTS; + __ AllocateInNewSpace(FixedArray::SizeFor(length), + rax, rbx, rcx, &gc, TAG_OBJECT); + + // Get the function from the stack. + __ movq(rcx, Operand(rsp, 1 * kPointerSize)); + + // Get the serialized scope info from the stack. + __ movq(rbx, Operand(rsp, 2 * kPointerSize)); + + // Set up the object header. + __ LoadRoot(kScratchRegister, Heap::kBlockContextMapRootIndex); + __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister); + __ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length)); + + // If this block context is nested in the global context we get a smi + // sentinel instead of a function. The block context should get the + // canonical empty function of the global context as its closure which + // we still have to look up. + Label after_sentinel; + __ JumpIfNotSmi(rcx, &after_sentinel, Label::kNear); + if (FLAG_debug_code) { + const char* message = "Expected 0 as a Smi sentinel"; + __ cmpq(rcx, Immediate(0)); + __ Assert(equal, message); + } + __ movq(rcx, GlobalObjectOperand()); + __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); + __ movq(rcx, ContextOperand(rcx, Context::CLOSURE_INDEX)); + __ bind(&after_sentinel); + + // Set up the fixed slots. + __ movq(ContextOperand(rax, Context::CLOSURE_INDEX), rcx); + __ movq(ContextOperand(rax, Context::PREVIOUS_INDEX), rsi); + __ movq(ContextOperand(rax, Context::EXTENSION_INDEX), rbx); + + // Copy the global object from the previous context. + __ movq(rbx, ContextOperand(rsi, Context::GLOBAL_INDEX)); + __ movq(ContextOperand(rax, Context::GLOBAL_INDEX), rbx); + + // Initialize the rest of the slots to the hole value. + __ LoadRoot(rbx, Heap::kTheHoleValueRootIndex); + for (int i = 0; i < slots_; i++) { + __ movq(ContextOperand(rax, i + Context::MIN_CONTEXT_SLOTS), rbx); + } + + // Return and remove the on-stack parameter. + __ movq(rsi, rax); + __ ret(2 * kPointerSize); + + // Need to collect. Call into runtime system. + __ bind(&gc); + __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1); +} + + +static void GenerateFastCloneShallowArrayCommon( + MacroAssembler* masm, + int length, + FastCloneShallowArrayStub::Mode mode, + Label* fail) { + // Registers on entry: + // + // rcx: boilerplate literal array. + ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS); + + // All sizes here are multiples of kPointerSize. + int elements_size = 0; + if (length > 0) { + elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + ? FixedDoubleArray::SizeFor(length) + : FixedArray::SizeFor(length); + } + int size = JSArray::kSize + elements_size; + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size, rax, rbx, rdx, fail, TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length == 0)) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rax, i), rbx); + } + } + + if (length > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset)); + __ lea(rdx, Operand(rax, JSArray::kSize)); + __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx); + + // Copy the elements array. + if (mode == FastCloneShallowArrayStub::CLONE_ELEMENTS) { + for (int i = 0; i < elements_size; i += kPointerSize) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rdx, i), rbx); + } + } else { + ASSERT(mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS); + int i; + for (i = 0; i < FixedDoubleArray::kHeaderSize; i += kPointerSize) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rdx, i), rbx); + } + while (i < elements_size) { + __ movsd(xmm0, FieldOperand(rcx, i)); + __ movsd(FieldOperand(rdx, i), xmm0); + i += kDoubleSize; + } + ASSERT(i == elements_size); + } + } +} + void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // Stack layout on entry: // @@ -162,29 +287,54 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { // [rsp + (2 * kPointerSize)]: literal index. // [rsp + (3 * kPointerSize)]: literals array. - // All sizes here are multiples of kPointerSize. - int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; - int size = JSArray::kSize + elements_size; - // Load boilerplate object into rcx and check if we need to create a // boilerplate. - Label slow_case; __ movq(rcx, Operand(rsp, 3 * kPointerSize)); __ movq(rax, Operand(rsp, 2 * kPointerSize)); SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2); __ movq(rcx, FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize)); __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex); + Label slow_case; __ j(equal, &slow_case); + FastCloneShallowArrayStub::Mode mode = mode_; + // rcx is boilerplate object. + Factory* factory = masm->isolate()->factory(); + if (mode == CLONE_ANY_ELEMENTS) { + Label double_elements, check_fast_elements; + __ movq(rbx, FieldOperand(rcx, JSArray::kElementsOffset)); + __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), + factory->fixed_cow_array_map()); + __ j(not_equal, &check_fast_elements); + GenerateFastCloneShallowArrayCommon(masm, 0, + COPY_ON_WRITE_ELEMENTS, &slow_case); + __ ret(3 * kPointerSize); + + __ bind(&check_fast_elements); + __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), + factory->fixed_array_map()); + __ j(not_equal, &double_elements); + GenerateFastCloneShallowArrayCommon(masm, length_, + CLONE_ELEMENTS, &slow_case); + __ ret(3 * kPointerSize); + + __ bind(&double_elements); + mode = CLONE_DOUBLE_ELEMENTS; + // Fall through to generate the code to handle double elements. + } + if (FLAG_debug_code) { const char* message; Heap::RootListIndex expected_map_index; - if (mode_ == CLONE_ELEMENTS) { + if (mode == CLONE_ELEMENTS) { message = "Expected (writable) fixed array"; expected_map_index = Heap::kFixedArrayMapRootIndex; + } else if (mode == CLONE_DOUBLE_ELEMENTS) { + message = "Expected (writable) fixed double array"; + expected_map_index = Heap::kFixedDoubleArrayMapRootIndex; } else { - ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS); + ASSERT(mode == COPY_ON_WRITE_ELEMENTS); message = "Expected copy-on-write fixed array"; expected_map_index = Heap::kFixedCOWArrayMapRootIndex; } @@ -196,43 +346,62 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { __ pop(rcx); } - // Allocate both the JS array and the elements array in one big - // allocation. This avoids multiple limit checks. - __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT); + GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case); + __ ret(3 * kPointerSize); - // Copy the JS array part. - for (int i = 0; i < JSArray::kSize; i += kPointerSize) { - if ((i != JSArray::kElementsOffset) || (length_ == 0)) { - __ movq(rbx, FieldOperand(rcx, i)); - __ movq(FieldOperand(rax, i), rbx); - } - } + __ bind(&slow_case); + __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); +} - if (length_ > 0) { - // Get hold of the elements array of the boilerplate and setup the - // elements pointer in the resulting object. - __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset)); - __ lea(rdx, Operand(rax, JSArray::kSize)); - __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx); - // Copy the elements array. - for (int i = 0; i < elements_size; i += kPointerSize) { - __ movq(rbx, FieldOperand(rcx, i)); - __ movq(FieldOperand(rdx, i), rbx); - } +void FastCloneShallowObjectStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [rsp + kPointerSize]: object literal flags. + // [rsp + (2 * kPointerSize)]: constant properties. + // [rsp + (3 * kPointerSize)]: literal index. + // [rsp + (4 * kPointerSize)]: literals array. + + // Load boilerplate object into ecx and check if we need to create a + // boilerplate. + Label slow_case; + __ movq(rcx, Operand(rsp, 4 * kPointerSize)); + __ movq(rax, Operand(rsp, 3 * kPointerSize)); + SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2); + __ movq(rcx, + FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize)); + __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex); + __ j(equal, &slow_case); + + // Check that the boilerplate contains only fast properties and we can + // statically determine the instance size. + int size = JSObject::kHeaderSize + length_ * kPointerSize; + __ movq(rax, FieldOperand(rcx, HeapObject::kMapOffset)); + __ movzxbq(rax, FieldOperand(rax, Map::kInstanceSizeOffset)); + __ cmpq(rax, Immediate(size >> kPointerSizeLog2)); + __ j(not_equal, &slow_case); + + // Allocate the JS object and copy header together with all in-object + // properties from the boilerplate. + __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT); + for (int i = 0; i < size; i += kPointerSize) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rax, i), rbx); } // Return and remove the on-stack parameters. - __ ret(3 * kPointerSize); + __ ret(4 * kPointerSize); __ bind(&slow_case); - __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1); + __ TailCallRuntime(Runtime::kCreateObjectLiteralShallow, 4, 1); } // The stub expects its argument on the stack and returns its result in tos_: // zero for false, and a non-zero value for true. void ToBooleanStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. Label patch; const Register argument = rax; const Register map = rdx; @@ -328,6 +497,25 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } +void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { + __ PushCallerSaved(save_doubles_); + const int argument_count = 1; + __ PrepareCallCFunction(argument_count); +#ifdef _WIN64 + __ LoadAddress(rcx, ExternalReference::isolate_address()); +#else + __ LoadAddress(rdi, ExternalReference::isolate_address()); +#endif + + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction( + ExternalReference::store_buffer_overflow_function(masm->isolate()), + argument_count); + __ PopCallerSaved(save_doubles_); + __ ret(0); +} + + void ToBooleanStub::CheckOddball(MacroAssembler* masm, Type type, Heap::RootListIndex value, @@ -622,12 +810,13 @@ void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm, __ jmp(&heapnumber_allocated); __ bind(&slow_allocate_heapnumber); - __ EnterInternalFrame(); - __ push(rax); - __ CallRuntime(Runtime::kNumberAlloc, 0); - __ movq(rcx, rax); - __ pop(rax); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(rax); + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ movq(rcx, rax); + __ pop(rax); + } __ bind(&heapnumber_allocated); // rcx: allocated 'empty' number @@ -751,6 +940,10 @@ void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { void BinaryOpStub::Generate(MacroAssembler* masm) { + // Explicitly allow generation of nested stubs. It is safe here because + // generation code does not use any raw pointers. + AllowStubCallsScope allow_stub_calls(masm, true); + switch (operands_type_) { case BinaryOpIC::UNINITIALIZED: GenerateTypeTransition(masm); @@ -1414,6 +1607,8 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ cmpq(rbx, Operand(rcx, 0)); __ j(not_equal, &cache_miss, Label::kNear); // Cache hit! + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->transcendental_cache_hit(), 1); __ movq(rax, Operand(rcx, 2 * kIntSize)); if (tagged) { __ fstp(0); // Clear FPU stack. @@ -1424,6 +1619,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { } __ bind(&cache_miss); + __ IncrementCounter(counters->transcendental_cache_miss(), 1); // Update cache with new value. if (tagged) { __ AllocateHeapNumber(rax, rdi, &runtime_call_clear_stack); @@ -1453,11 +1649,12 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ addq(rsp, Immediate(kDoubleSize)); // We return the value in xmm1 without adding it to the cache, but // we cause a scavenging GC so that future allocations will succeed. - __ EnterInternalFrame(); - // Allocate an unused object bigger than a HeapNumber. - __ Push(Smi::FromInt(2 * kDoubleSize)); - __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Allocate an unused object bigger than a HeapNumber. + __ Push(Smi::FromInt(2 * kDoubleSize)); + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace); + } __ Ret(); } @@ -1473,10 +1670,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) { __ bind(&runtime_call); __ AllocateHeapNumber(rax, rdi, &skip_cache); __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1); - __ EnterInternalFrame(); - __ push(rax); - __ CallRuntime(RuntimeFunction(), 1); - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(rax); + __ CallRuntime(RuntimeFunction(), 1); + } __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset)); __ Ret(); } @@ -1488,6 +1686,7 @@ Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { // Add more cases when necessary. case TranscendentalCache::SIN: return Runtime::kMath_sin; case TranscendentalCache::COS: return Runtime::kMath_cos; + case TranscendentalCache::TAN: return Runtime::kMath_tan; case TranscendentalCache::LOG: return Runtime::kMath_log; default: UNIMPLEMENTED(); @@ -1503,7 +1702,9 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { // rcx: Pointer to cache entry. Must be preserved. // st(0): Input double Label done; - if (type_ == TranscendentalCache::SIN || type_ == TranscendentalCache::COS) { + if (type_ == TranscendentalCache::SIN || + type_ == TranscendentalCache::COS || + type_ == TranscendentalCache::TAN) { // Both fsin and fcos require arguments in the range +/-2^63 and // return NaN for infinities and NaN. They can share all code except // the actual fsin/fcos operation. @@ -1573,6 +1774,12 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { case TranscendentalCache::COS: __ fcos(); break; + case TranscendentalCache::TAN: + // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the + // FP register stack. + __ fptan(); + __ fstp(0); // Pop FP register stack. + break; default: UNREACHABLE(); } @@ -1784,152 +1991,259 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm, void MathPowStub::Generate(MacroAssembler* masm) { - // Registers are used as follows: - // rdx = base - // rax = exponent - // rcx = temporary, result - - Label allocate_return, call_runtime; - - // Load input parameters. - __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - __ movq(rax, Operand(rsp, 1 * kPointerSize)); + // Choose register conforming to calling convention (when bailing out). +#ifdef _WIN64 + const Register exponent = rdx; +#else + const Register exponent = rdi; +#endif + const Register base = rax; + const Register scratch = rcx; + const XMMRegister double_result = xmm3; + const XMMRegister double_base = xmm2; + const XMMRegister double_exponent = xmm1; + const XMMRegister double_scratch = xmm4; - // Save 1 in xmm3 - we need this several times later on. - __ Set(rcx, 1); - __ cvtlsi2sd(xmm3, rcx); + Label call_runtime, done, exponent_not_smi, int_exponent; - Label exponent_nonsmi; - Label base_nonsmi; - // If the exponent is a heap number go to that specific case. - __ JumpIfNotSmi(rax, &exponent_nonsmi); - __ JumpIfNotSmi(rdx, &base_nonsmi); + // Save 1 in double_result - we need this several times later on. + __ movq(scratch, Immediate(1)); + __ cvtlsi2sd(double_result, scratch); + + if (exponent_type_ == ON_STACK) { + Label base_is_smi, unpack_exponent; + // The exponent and base are supplied as arguments on the stack. + // This can only happen if the stub is called from non-optimized code. + // Load input parameters from stack. + __ movq(base, Operand(rsp, 2 * kPointerSize)); + __ movq(exponent, Operand(rsp, 1 * kPointerSize)); + __ JumpIfSmi(base, &base_is_smi, Label::kNear); + __ CompareRoot(FieldOperand(base, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &call_runtime); + + __ movsd(double_base, FieldOperand(base, HeapNumber::kValueOffset)); + __ jmp(&unpack_exponent, Label::kNear); + + __ bind(&base_is_smi); + __ SmiToInteger32(base, base); + __ cvtlsi2sd(double_base, base); + __ bind(&unpack_exponent); + + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear); + __ SmiToInteger32(exponent, exponent); + __ jmp(&int_exponent); + + __ bind(&exponent_not_smi); + __ CompareRoot(FieldOperand(exponent, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &call_runtime); + __ movsd(double_exponent, FieldOperand(exponent, HeapNumber::kValueOffset)); + } else if (exponent_type_ == TAGGED) { + __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear); + __ SmiToInteger32(exponent, exponent); + __ jmp(&int_exponent); + + __ bind(&exponent_not_smi); + __ movsd(double_exponent, FieldOperand(exponent, HeapNumber::kValueOffset)); + } - // Optimized version when both exponent and base are smis. - Label powi; - __ SmiToInteger32(rdx, rdx); - __ cvtlsi2sd(xmm0, rdx); - __ jmp(&powi); - // Exponent is a smi and base is a heapnumber. - __ bind(&base_nonsmi); - __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset), - Heap::kHeapNumberMapRootIndex); - __ j(not_equal, &call_runtime); + if (exponent_type_ != INTEGER) { + Label fast_power; + // Detect integer exponents stored as double. + __ cvttsd2si(exponent, double_exponent); + // Skip to runtime if possibly NaN (indicated by the indefinite integer). + __ cmpl(exponent, Immediate(0x80000000u)); + __ j(equal, &call_runtime); + __ cvtlsi2sd(double_scratch, exponent); + // Already ruled out NaNs for exponent. + __ ucomisd(double_exponent, double_scratch); + __ j(equal, &int_exponent); + + if (exponent_type_ == ON_STACK) { + // Detect square root case. Crankshaft detects constant +/-0.5 at + // compile time and uses DoMathPowHalf instead. We then skip this check + // for non-constant cases of +/-0.5 as these hardly occur. + Label continue_sqrt, continue_rsqrt, not_plus_half; + // Test for 0.5. + // Load double_scratch with 0.5. + __ movq(scratch, V8_UINT64_C(0x3FE0000000000000), RelocInfo::NONE); + __ movq(double_scratch, scratch); + // Already ruled out NaNs for exponent. + __ ucomisd(double_scratch, double_exponent); + __ j(not_equal, ¬_plus_half, Label::kNear); + + // Calculates square root of base. Check for the special case of + // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13). + // According to IEEE-754, double-precision -Infinity has the highest + // 12 bits set and the lowest 52 bits cleared. + __ movq(scratch, V8_UINT64_C(0xFFF0000000000000), RelocInfo::NONE); + __ movq(double_scratch, scratch); + __ ucomisd(double_scratch, double_base); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &continue_sqrt, Label::kNear); + __ j(carry, &continue_sqrt, Label::kNear); + + // Set result to Infinity in the special case. + __ xorps(double_result, double_result); + __ subsd(double_result, double_scratch); + __ jmp(&done); + + __ bind(&continue_sqrt); + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorps(double_scratch, double_scratch); + __ addsd(double_scratch, double_base); // Convert -0 to 0. + __ sqrtsd(double_result, double_scratch); + __ jmp(&done); + + // Test for -0.5. + __ bind(¬_plus_half); + // Load double_scratch with -0.5 by substracting 1. + __ subsd(double_scratch, double_result); + // Already ruled out NaNs for exponent. + __ ucomisd(double_scratch, double_exponent); + __ j(not_equal, &fast_power, Label::kNear); + + // Calculates reciprocal of square root of base. Check for the special + // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13). + // According to IEEE-754, double-precision -Infinity has the highest + // 12 bits set and the lowest 52 bits cleared. + __ movq(scratch, V8_UINT64_C(0xFFF0000000000000), RelocInfo::NONE); + __ movq(double_scratch, scratch); + __ ucomisd(double_scratch, double_base); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &continue_rsqrt, Label::kNear); + __ j(carry, &continue_rsqrt, Label::kNear); + + // Set result to 0 in the special case. + __ xorps(double_result, double_result); + __ jmp(&done); + + __ bind(&continue_rsqrt); + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorps(double_exponent, double_exponent); + __ addsd(double_exponent, double_base); // Convert -0 to +0. + __ sqrtsd(double_exponent, double_exponent); + __ divsd(double_result, double_exponent); + __ jmp(&done); + } - __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); + // Using FPU instructions to calculate power. + Label fast_power_failed; + __ bind(&fast_power); + __ fnclex(); // Clear flags to catch exceptions later. + // Transfer (B)ase and (E)xponent onto the FPU register stack. + __ subq(rsp, Immediate(kDoubleSize)); + __ movsd(Operand(rsp, 0), double_exponent); + __ fld_d(Operand(rsp, 0)); // E + __ movsd(Operand(rsp, 0), double_base); + __ fld_d(Operand(rsp, 0)); // B, E + + // Exponent is in st(1) and base is in st(0) + // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B) + // FYL2X calculates st(1) * log2(st(0)) + __ fyl2x(); // X + __ fld(0); // X, X + __ frndint(); // rnd(X), X + __ fsub(1); // rnd(X), X-rnd(X) + __ fxch(1); // X - rnd(X), rnd(X) + // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1 + __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X) + __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X) + __ faddp(1); // 1, 2^(X-rnd(X)), rnd(X) + // FSCALE calculates st(0) * 2^st(1) + __ fscale(); // 2^X, rnd(X) + __ fstp(1); + // Bail out to runtime in case of exceptions in the status word. + __ fnstsw_ax(); + __ testb(rax, Immediate(0x5F)); // Check for all but precision exception. + __ j(not_zero, &fast_power_failed, Label::kNear); + __ fstp_d(Operand(rsp, 0)); + __ movsd(double_result, Operand(rsp, 0)); + __ addq(rsp, Immediate(kDoubleSize)); + __ jmp(&done); - // Optimized version of pow if exponent is a smi. - // xmm0 contains the base. - __ bind(&powi); - __ SmiToInteger32(rax, rax); + __ bind(&fast_power_failed); + __ fninit(); + __ addq(rsp, Immediate(kDoubleSize)); + __ jmp(&call_runtime); + } - // Save exponent in base as we need to check if exponent is negative later. - // We know that base and exponent are in different registers. - __ movq(rdx, rax); + // Calculate power with integer exponent. + __ bind(&int_exponent); + const XMMRegister double_scratch2 = double_exponent; + // Back up exponent as we need to check if exponent is negative later. + __ movq(scratch, exponent); // Back up exponent. + __ movsd(double_scratch, double_base); // Back up base. + __ movsd(double_scratch2, double_result); // Load double_exponent with 1. // Get absolute value of exponent. - Label no_neg; - __ cmpl(rax, Immediate(0)); - __ j(greater_equal, &no_neg, Label::kNear); - __ negl(rax); + Label no_neg, while_true, no_multiply; + __ testl(scratch, scratch); + __ j(positive, &no_neg, Label::kNear); + __ negl(scratch); __ bind(&no_neg); - // Load xmm1 with 1. - __ movaps(xmm1, xmm3); - Label while_true; - Label no_multiply; - __ bind(&while_true); - __ shrl(rax, Immediate(1)); + __ shrl(scratch, Immediate(1)); __ j(not_carry, &no_multiply, Label::kNear); - __ mulsd(xmm1, xmm0); + __ mulsd(double_result, double_scratch); __ bind(&no_multiply); - __ mulsd(xmm0, xmm0); - __ j(not_zero, &while_true); - - // Base has the original value of the exponent - if the exponent is - // negative return 1/result. - __ testl(rdx, rdx); - __ j(positive, &allocate_return); - // Special case if xmm1 has reached infinity. - __ divsd(xmm3, xmm1); - __ movaps(xmm1, xmm3); - __ xorps(xmm0, xmm0); - __ ucomisd(xmm0, xmm1); - __ j(equal, &call_runtime); - - __ jmp(&allocate_return); - - // Exponent (or both) is a heapnumber - no matter what we should now work - // on doubles. - __ bind(&exponent_nonsmi); - __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), - Heap::kHeapNumberMapRootIndex); - __ j(not_equal, &call_runtime); - __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset)); - // Test if exponent is nan. - __ ucomisd(xmm1, xmm1); - __ j(parity_even, &call_runtime); - Label base_not_smi, handle_special_cases; - __ JumpIfNotSmi(rdx, &base_not_smi, Label::kNear); - __ SmiToInteger32(rdx, rdx); - __ cvtlsi2sd(xmm0, rdx); - __ jmp(&handle_special_cases, Label::kNear); + __ mulsd(double_scratch, double_scratch); + __ j(not_zero, &while_true); - __ bind(&base_not_smi); - __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset), - Heap::kHeapNumberMapRootIndex); - __ j(not_equal, &call_runtime); - __ movl(rcx, FieldOperand(rdx, HeapNumber::kExponentOffset)); - __ andl(rcx, Immediate(HeapNumber::kExponentMask)); - __ cmpl(rcx, Immediate(HeapNumber::kExponentMask)); - // base is NaN or +/-Infinity - __ j(greater_equal, &call_runtime); - __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); + // If the exponent is negative, return 1/result. + __ testl(exponent, exponent); + __ j(greater, &done); + __ divsd(double_scratch2, double_result); + __ movsd(double_result, double_scratch2); + // Test whether result is zero. Bail out to check for subnormal result. + // Due to subnormals, x^-y == (1/x)^y does not hold in all cases. + __ xorps(double_scratch2, double_scratch2); + __ ucomisd(double_scratch2, double_result); + // double_exponent aliased as double_scratch2 has already been overwritten + // and may not have contained the exponent value in the first place when the + // input was a smi. We reset it with exponent value before bailing out. + __ j(not_equal, &done); + __ cvtlsi2sd(double_exponent, exponent); + + // Returning or bailing out. + Counters* counters = masm->isolate()->counters(); + if (exponent_type_ == ON_STACK) { + // The arguments are still on the stack. + __ bind(&call_runtime); + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); - // base is in xmm0 and exponent is in xmm1. - __ bind(&handle_special_cases); - Label not_minus_half; - // Test for -0.5. - // Load xmm2 with -0.5. - __ movq(rcx, V8_UINT64_C(0xBFE0000000000000), RelocInfo::NONE); - __ movq(xmm2, rcx); - // xmm2 now has -0.5. - __ ucomisd(xmm2, xmm1); - __ j(not_equal, ¬_minus_half, Label::kNear); - - // Calculates reciprocal of square root. - // sqrtsd returns -0 when input is -0. ECMA spec requires +0. - __ xorps(xmm1, xmm1); - __ addsd(xmm1, xmm0); - __ sqrtsd(xmm1, xmm1); - __ divsd(xmm3, xmm1); - __ movaps(xmm1, xmm3); - __ jmp(&allocate_return); - - // Test for 0.5. - __ bind(¬_minus_half); - // Load xmm2 with 0.5. - // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3. - __ addsd(xmm2, xmm3); - // xmm2 now has 0.5. - __ ucomisd(xmm2, xmm1); - __ j(not_equal, &call_runtime); - // Calculates square root. - // sqrtsd returns -0 when input is -0. ECMA spec requires +0. - __ xorps(xmm1, xmm1); - __ addsd(xmm1, xmm0); // Convert -0 to 0. - __ sqrtsd(xmm1, xmm1); - - __ bind(&allocate_return); - __ AllocateHeapNumber(rcx, rax, &call_runtime); - __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm1); - __ movq(rax, rcx); - __ ret(2 * kPointerSize); + // The stub is called from non-optimized code, which expects the result + // as heap number in eax. + __ bind(&done); + __ AllocateHeapNumber(rax, rcx, &call_runtime); + __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), double_result); + __ IncrementCounter(counters->math_pow(), 1); + __ ret(2 * kPointerSize); + } else { + __ bind(&call_runtime); + // Move base to the correct argument register. Exponent is already in xmm1. + __ movsd(xmm0, double_base); + ASSERT(double_exponent.is(xmm1)); + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(2); + __ CallCFunction( + ExternalReference::power_double_double_function(masm->isolate()), 2); + } + // Return value is in xmm0. + __ movsd(double_result, xmm0); + // Restore context register. + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ bind(&call_runtime); - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); + __ bind(&done); + __ IncrementCounter(counters->math_pow(), 1); + __ ret(0); + } } @@ -2043,6 +2357,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { const int kParameterMapHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize; Label no_parameter_map; + __ xor_(r8, r8); __ testq(rbx, rbx); __ j(zero, &no_parameter_map, Label::kNear); __ lea(r8, Operand(rbx, times_pointer_size, kParameterMapHeaderSize)); @@ -2085,7 +2400,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ movq(FieldOperand(rax, i), rdx); } - // Setup the callee in-object property. + // Set up the callee in-object property. STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); __ movq(rdx, Operand(rsp, 3 * kPointerSize)); __ movq(FieldOperand(rax, JSObject::kHeaderSize + @@ -2100,7 +2415,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Heap::kArgumentsLengthIndex * kPointerSize), rcx); - // Setup the elements pointer in the allocated arguments object. + // Set up the elements pointer in the allocated arguments object. // If we allocated a parameter map, edi will point there, otherwise to the // backing store. __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSize)); @@ -2136,16 +2451,13 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Label parameters_loop, parameters_test; // Load tagged parameter count into r9. - __ movq(r9, Operand(rsp, 1 * kPointerSize)); + __ Integer32ToSmi(r9, rbx); __ Move(r8, Smi::FromInt(Context::MIN_CONTEXT_SLOTS)); - __ addq(r8, Operand(rsp, 3 * kPointerSize)); + __ addq(r8, Operand(rsp, 1 * kPointerSize)); __ subq(r8, r9); __ Move(r11, factory->the_hole_value()); __ movq(rdx, rdi); - __ SmiToInteger64(kScratchRegister, r9); - __ lea(rdi, Operand(rdi, kScratchRegister, - times_pointer_size, - kParameterMapHeaderSize)); + __ lea(rdi, Operand(rdi, rbx, times_pointer_size, kParameterMapHeaderSize)); // r9 = loop variable (tagged) // r8 = mapping index (tagged) // r11 = the hole value @@ -2181,9 +2493,8 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { Label arguments_loop, arguments_test; __ movq(r8, rbx); __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - // Untag rcx and r8 for the loop below. + // Untag rcx for the loop below. __ SmiToInteger64(rcx, rcx); - __ SmiToInteger64(r8, r8); __ lea(kScratchRegister, Operand(r8, times_pointer_size, 0)); __ subq(rdx, kScratchRegister); __ jmp(&arguments_test, Label::kNear); @@ -2307,7 +2618,7 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Get the parameters pointer from the stack. __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - // Setup the elements pointer in the allocated arguments object and + // Set up the elements pointer in the allocated arguments object and // initialize the header in the elements fixed array. __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSizeStrict)); __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi); @@ -2346,10 +2657,6 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { #ifdef V8_INTERPRETED_REGEXP __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); #else // V8_INTERPRETED_REGEXP - if (!FLAG_regexp_entry_native) { - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); - return; - } // Stack frame on entry. // rsp[0]: return address @@ -2455,26 +2762,40 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); // First check for flat two byte string. - __ andb(rbx, Immediate( - kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask)); + __ andb(rbx, Immediate(kIsNotStringMask | + kStringRepresentationMask | + kStringEncodingMask | + kShortExternalStringMask)); STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0); __ j(zero, &seq_two_byte_string, Label::kNear); - // Any other flat string must be a flat ascii string. - __ andb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask)); + // Any other flat string must be a flat ASCII string. None of the following + // string type tests will succeed if subject is not a string or a short + // external string. + __ andb(rbx, Immediate(kIsNotStringMask | + kStringRepresentationMask | + kShortExternalStringMask)); __ j(zero, &seq_ascii_string, Label::kNear); + // rbx: whether subject is a string and if yes, its string representation // Check for flat cons string or sliced string. // A flat cons string is a cons string where the second part is the empty // string. In that case the subject string is just the first part of the cons // string. Also in this case the first part of the cons string is known to be // a sequential string or an external string. // In the case of a sliced string its offset has to be taken into account. - Label cons_string, check_encoding; + Label cons_string, external_string, check_encoding; STATIC_ASSERT(kConsStringTag < kExternalStringTag); STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); + STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); + STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); __ cmpq(rbx, Immediate(kExternalStringTag)); __ j(less, &cons_string, Label::kNear); - __ j(equal, &runtime); + __ j(equal, &external_string); + + // Catch non-string subject or short external string. + STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0); + __ testb(rbx, Immediate(kIsNotStringMask | kShortExternalStringMask)); + __ j(not_zero, &runtime); // String is sliced. __ SmiToInteger32(r14, FieldOperand(rdi, SlicedString::kOffsetOffset)); @@ -2498,16 +2819,16 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { Immediate(kStringRepresentationMask | kStringEncodingMask)); STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0); __ j(zero, &seq_two_byte_string, Label::kNear); - // Any other flat string must be ascii. + // Any other flat string must be sequential ASCII or external. __ testb(FieldOperand(rbx, Map::kInstanceTypeOffset), Immediate(kStringRepresentationMask)); - __ j(not_zero, &runtime); + __ j(not_zero, &external_string); __ bind(&seq_ascii_string); - // rdi: subject string (sequential ascii) + // rdi: subject string (sequential ASCII) // rax: RegExp data (FixedArray) __ movq(r11, FieldOperand(rax, JSRegExp::kDataAsciiCodeOffset)); - __ Set(rcx, 1); // Type is ascii. + __ Set(rcx, 1); // Type is ASCII. __ jmp(&check_code, Label::kNear); __ bind(&seq_two_byte_string); @@ -2523,7 +2844,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ JumpIfSmi(r11, &runtime); // rdi: subject string - // rcx: encoding of subject string (1 if ascii, 0 if two_byte); + // rcx: encoding of subject string (1 if ASCII, 0 if two_byte); // r11: code // Load used arguments before starting to push arguments for call to native // RegExp code to avoid handling changing stack height. @@ -2531,7 +2852,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // rdi: subject string // rbx: previous index - // rcx: encoding of subject string (1 if ascii 0 if two_byte); + // rcx: encoding of subject string (1 if ASCII 0 if two_byte); // r11: code // All checks done. Now push arguments for native regexp code. Counters* counters = masm->isolate()->counters(); @@ -2588,7 +2909,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Keep track on aliasing between argX defined above and the registers used. // rdi: subject string // rbx: previous index - // rcx: encoding of subject string (1 if ascii 0 if two_byte); + // rcx: encoding of subject string (1 if ASCII 0 if two_byte); // r11: code // r14: slice offset // r15: original subject string @@ -2670,12 +2991,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Store last subject and last input. __ movq(rax, Operand(rsp, kSubjectOffset)); __ movq(FieldOperand(rbx, RegExpImpl::kLastSubjectOffset), rax); - __ movq(rcx, rbx); - __ RecordWrite(rcx, RegExpImpl::kLastSubjectOffset, rax, rdi); + __ RecordWriteField(rbx, + RegExpImpl::kLastSubjectOffset, + rax, + rdi, + kDontSaveFPRegs); __ movq(rax, Operand(rsp, kSubjectOffset)); __ movq(FieldOperand(rbx, RegExpImpl::kLastInputOffset), rax); - __ movq(rcx, rbx); - __ RecordWrite(rcx, RegExpImpl::kLastInputOffset, rax, rdi); + __ RecordWriteField(rbx, + RegExpImpl::kLastInputOffset, + rax, + rdi, + kDontSaveFPRegs); // Get the static offsets vector filled by the native regexp code. __ LoadAddress(rcx, @@ -2729,6 +3056,27 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ bind(&termination_exception); __ ThrowUncatchable(TERMINATION, rax); + // External string. Short external strings have already been ruled out. + // rdi: subject string (expected to be external) + // rbx: scratch + __ bind(&external_string); + __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); + __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ testb(rbx, Immediate(kIsIndirectStringMask)); + __ Assert(zero, "external string expected, but not found"); + } + __ movq(rdi, FieldOperand(rdi, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + STATIC_ASSERT(kTwoByteStringTag == 0); + __ testb(rbx, Immediate(kStringEncodingMask)); + __ j(not_zero, &seq_ascii_string); + __ jmp(&seq_two_byte_string); + // Do the runtime call to execute the regexp. __ bind(&runtime); __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); @@ -3132,7 +3480,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ JumpIfNotBothSequentialAsciiStrings( rdx, rax, rcx, rbx, &check_unequal_objects); - // Inline comparison of ascii strings. + // Inline comparison of ASCII strings. if (cc_ == equal) { StringCompareStub::GenerateFlatAsciiStringEquals(masm, rdx, @@ -3231,7 +3579,47 @@ void StackCheckStub::Generate(MacroAssembler* masm) { } +static void GenerateRecordCallTarget(MacroAssembler* masm) { + // Cache the called function in a global property cell. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // rbx : cache cell for call target + // rdi : the function to call + Isolate* isolate = masm->isolate(); + Label initialize, done; + + // Load the cache state into rcx. + __ movq(rcx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); + + // A monomorphic cache hit or an already megamorphic state: invoke the + // function without changing the state. + __ cmpq(rcx, rdi); + __ j(equal, &done, Label::kNear); + __ Cmp(rcx, TypeFeedbackCells::MegamorphicSentinel(isolate)); + __ j(equal, &done, Label::kNear); + + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ Cmp(rcx, TypeFeedbackCells::UninitializedSentinel(isolate)); + __ j(equal, &initialize, Label::kNear); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. + __ Move(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), + TypeFeedbackCells::MegamorphicSentinel(isolate)); + __ jmp(&done, Label::kNear); + + // An uninitialized cache is patched with the function. + __ bind(&initialize); + __ movq(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), rdi); + // No need for a write barrier here - cells are rescanned. + + __ bind(&done); +} + + void CallFunctionStub::Generate(MacroAssembler* masm) { + // rdi : the function to call + // rbx : cache cell for call target Label slow, non_function; // The receiver might implicitly be the global object. This is @@ -3252,10 +3640,6 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ bind(&call); } - // Get the function to call from the stack. - // +2 ~ receiver, return address - __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize)); - // Check that the function really is a JavaScript function. __ JumpIfSmi(rdi, &non_function); // Goto slow case if we do not have a function. @@ -3292,7 +3676,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ push(rcx); __ Set(rax, argc_ + 1); __ Set(rbx, 0); - __ SetCallKind(rcx, CALL_AS_FUNCTION); + __ SetCallKind(rcx, CALL_AS_METHOD); __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY); { Handle<Code> adaptor = @@ -3314,11 +3698,83 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { } +void CallConstructStub::Generate(MacroAssembler* masm) { + // rax : number of arguments + // rbx : cache cell for call target + // rdi : constructor function + Label slow, non_function_call; + + // Check that function is not a smi. + __ JumpIfSmi(rdi, &non_function_call); + // Check that function is a JSFunction. + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + } + + // Jump to the function-specific construct stub. + __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + __ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kConstructStubOffset)); + __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize)); + __ jmp(rbx); + + // rdi: called object + // rax: number of arguments + // rcx: object map + Label do_call; + __ bind(&slow); + __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE); + __ j(not_equal, &non_function_call); + __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); + __ jmp(&do_call); + + __ bind(&non_function_call); + __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); + __ bind(&do_call); + // Set expected number of arguments to zero (not changing rax). + __ Set(rbx, 0); + __ SetCallKind(rcx, CALL_AS_METHOD); + __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), + RelocInfo::CODE_TARGET); +} + + bool CEntryStub::NeedsImmovableCode() { return false; } +bool CEntryStub::IsPregenerated() { +#ifdef _WIN64 + return result_size_ == 1; +#else + return true; +#endif +} + + +void CodeStub::GenerateStubsAheadOfTime() { + CEntryStub::GenerateAheadOfTime(); + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(); + // It is important that the store buffer overflow stubs are generated first. + RecordWriteStub::GenerateFixedRegStubsAheadOfTime(); +} + + +void CodeStub::GenerateFPStubs() { +} + + +void CEntryStub::GenerateAheadOfTime() { + CEntryStub stub(1, kDontSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + CEntryStub save_doubles(1, kSaveFPRegs); + save_doubles.GetCode()->set_is_pregenerated(true); +} + + void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { // Throw exception in eax. __ Throw(rax); @@ -3545,11 +4001,11 @@ void CEntryStub::Generate(MacroAssembler* masm) { void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { - Label invoke, exit; + Label invoke, handler_entry, exit; Label not_outermost_js, not_outermost_js_2; { // NOLINT. Scope block confuses linter. MacroAssembler::NoRootArrayScope uninitialized_root_register(masm); - // Setup frame. + // Set up frame. __ push(rbp); __ movq(rbp, rsp); @@ -3605,20 +4061,23 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ Push(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)); __ bind(&cont); - // Call a faked try-block that does the invoke. - __ call(&invoke); - - // Caught exception: Store result (exception) in the pending - // exception field in the JSEnv and return a failure sentinel. + // Jump to a faked try block that does the invoke, with a faked catch + // block that sets the pending exception. + __ jmp(&invoke); + __ bind(&handler_entry); + handler_offset_ = handler_entry.pos(); + // Caught exception: Store result (exception) in the pending exception + // field in the JSEnv and return a failure sentinel. ExternalReference pending_exception(Isolate::kPendingExceptionAddress, isolate); __ Store(pending_exception, rax); __ movq(rax, Failure::Exception(), RelocInfo::NONE); __ jmp(&exit); - // Invoke: Link this frame into the handler chain. + // Invoke: Link this frame into the handler chain. There's only one + // handler block in this code object, so its index is 0. __ bind(&invoke); - __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); + __ PushTryHandler(StackHandler::JS_ENTRY, 0); // Clear any pending exceptions. __ LoadRoot(rax, Heap::kTheHoleValueRootIndex); @@ -3627,11 +4086,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Fake a receiver (NULL). __ push(Immediate(0)); // receiver - // Invoke the function by calling through JS entry trampoline - // builtin and pop the faked function when we return. We load the address - // from an external reference instead of inlining the call target address - // directly in the code, because the builtin stubs may not have been - // generated yet at the time this code is generated. + // Invoke the function by calling through JS entry trampoline builtin and + // pop the faked function when we return. We load the address from an + // external reference instead of inlining the call target address directly + // in the code, because the builtin stubs may not have been generated yet + // at the time this code is generated. if (is_construct) { ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline, isolate); @@ -3740,7 +4199,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ bind(&miss); } - __ TryGetFunctionPrototype(rdx, rbx, &slow); + __ TryGetFunctionPrototype(rdx, rbx, &slow, true); // Check that the function prototype is a JS object. __ JumpIfSmi(rbx, &slow); @@ -3757,14 +4216,17 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ StoreRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex); __ StoreRoot(rax, Heap::kInstanceofCacheMapRootIndex); } else { + // Get return address and delta to inlined map check. __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize)); __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize)); - __ movq(Operand(kScratchRegister, kOffsetToMapCheckValue), rax); if (FLAG_debug_code) { __ movl(rdi, Immediate(kWordBeforeMapCheckValue)); __ cmpl(Operand(kScratchRegister, kOffsetToMapCheckValue - 4), rdi); __ Assert(equal, "InstanceofStub unexpected call site cache (check)."); } + __ movq(kScratchRegister, + Operand(kScratchRegister, kOffsetToMapCheckValue)); + __ movq(Operand(kScratchRegister, 0), rax); } __ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset)); @@ -3791,9 +4253,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex); } else { // Store offset of true in the root array at the inline check site. - ASSERT((Heap::kTrueValueRootIndex << kPointerSizeLog2) - kRootRegisterBias - == 0xB0 - 0x100); - __ movl(rax, Immediate(0xB0)); // TrueValue is at -10 * kPointerSize. + int true_offset = 0x100 + + (Heap::kTrueValueRootIndex << kPointerSizeLog2) - kRootRegisterBias; + // Assert it is a 1-byte signed value. + ASSERT(true_offset >= 0 && true_offset < 0x100); + __ movl(rax, Immediate(true_offset)); __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize)); __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize)); __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax); @@ -3812,9 +4276,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex); } else { // Store offset of false in the root array at the inline check site. - ASSERT((Heap::kFalseValueRootIndex << kPointerSizeLog2) - kRootRegisterBias - == 0xB8 - 0x100); - __ movl(rax, Immediate(0xB8)); // FalseValue is at -9 * kPointerSize. + int false_offset = 0x100 + + (Heap::kFalseValueRootIndex << kPointerSizeLog2) - kRootRegisterBias; + // Assert it is a 1-byte signed value. + ASSERT(false_offset >= 0 && false_offset < 0x100); + __ movl(rax, Immediate(false_offset)); __ movq(kScratchRegister, Operand(rsp, 0 * kPointerSize)); __ subq(kScratchRegister, Operand(rsp, 1 * kPointerSize)); __ movb(Operand(kScratchRegister, kOffsetToResultValue), rax); @@ -3904,85 +4370,25 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // If the index is non-smi trigger the non-smi case. __ JumpIfNotSmi(index_, &index_not_smi_); - - // Put smi-tagged index into scratch register. - __ movq(scratch_, index_); __ bind(&got_smi_index_); // Check for index out of range. - __ SmiCompare(scratch_, FieldOperand(object_, String::kLengthOffset)); + __ SmiCompare(index_, FieldOperand(object_, String::kLengthOffset)); __ j(above_equal, index_out_of_range_); - // We need special handling for non-flat strings. - STATIC_ASSERT(kSeqStringTag == 0); - __ testb(result_, Immediate(kStringRepresentationMask)); - __ j(zero, &flat_string); + __ SmiToInteger32(index_, index_); - // Handle non-flat strings. - __ and_(result_, Immediate(kStringRepresentationMask)); - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - __ cmpb(result_, Immediate(kExternalStringTag)); - __ j(greater, &sliced_string); - __ j(equal, &call_runtime_); - - // ConsString. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - Label assure_seq_string; - __ CompareRoot(FieldOperand(object_, ConsString::kSecondOffset), - Heap::kEmptyStringRootIndex); - __ j(not_equal, &call_runtime_); - // Get the first of the two strings and load its instance type. - __ movq(object_, FieldOperand(object_, ConsString::kFirstOffset)); - __ jmp(&assure_seq_string, Label::kNear); + StringCharLoadGenerator::Generate( + masm, object_, index_, result_, &call_runtime_); - // SlicedString, unpack and add offset. - __ bind(&sliced_string); - __ addq(scratch_, FieldOperand(object_, SlicedString::kOffsetOffset)); - __ movq(object_, FieldOperand(object_, SlicedString::kParentOffset)); - - __ bind(&assure_seq_string); - __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset)); - __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); - // If the first cons component is also non-flat, then go to runtime. - STATIC_ASSERT(kSeqStringTag == 0); - __ testb(result_, Immediate(kStringRepresentationMask)); - __ j(not_zero, &call_runtime_); - __ jmp(&flat_string); - - // Check for 1-byte or 2-byte string. - __ bind(&flat_string); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ testb(result_, Immediate(kStringEncodingMask)); - __ j(not_zero, &ascii_string); - - // 2-byte string. - // Load the 2-byte character code into the result register. - __ SmiToInteger32(scratch_, scratch_); - __ movzxwl(result_, FieldOperand(object_, - scratch_, times_2, - SeqTwoByteString::kHeaderSize)); - __ jmp(&got_char_code); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ SmiToInteger32(scratch_, scratch_); - __ movzxbl(result_, FieldOperand(object_, - scratch_, times_1, - SeqAsciiString::kHeaderSize)); - __ bind(&got_char_code); __ Integer32ToSmi(result_, result_); __ bind(&exit_); } void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharCodeAt slow case"); Factory* factory = masm->isolate()->factory(); @@ -3995,7 +4401,6 @@ void StringCharCodeAtGenerator::GenerateSlow( DONT_DO_SMI_CHECK); call_helper.BeforeCall(masm); __ push(object_); - __ push(index_); __ push(index_); // Consumed by runtime conversion function. if (index_flags_ == STRING_INDEX_IS_NUMBER) { __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); @@ -4004,19 +4409,18 @@ void StringCharCodeAtGenerator::GenerateSlow( // NumberToSmi discards numbers that are not exact integers. __ CallRuntime(Runtime::kNumberToSmi, 1); } - if (!scratch_.is(rax)) { + if (!index_.is(rax)) { // Save the conversion result before the pop instructions below // have a chance to overwrite it. - __ movq(scratch_, rax); + __ movq(index_, rax); } - __ pop(index_); __ pop(object_); // Reload the instance type. __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset)); __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); call_helper.AfterCall(masm); // If index is still not a smi, it must be out of range. - __ JumpIfNotSmi(scratch_, index_out_of_range_); + __ JumpIfNotSmi(index_, index_out_of_range_); // Otherwise, return to the fast path. __ jmp(&got_smi_index_); @@ -4026,6 +4430,7 @@ void StringCharCodeAtGenerator::GenerateSlow( __ bind(&call_runtime_); call_helper.BeforeCall(masm); __ push(object_); + __ Integer32ToSmi(index_, index_); __ push(index_); __ CallRuntime(Runtime::kStringCharCodeAt, 2); if (!result_.is(rax)) { @@ -4058,7 +4463,8 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { void StringCharFromCodeGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { __ Abort("Unexpected fallthrough to CharFromCode slow case"); __ bind(&slow_case_); @@ -4085,14 +4491,15 @@ void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) { void StringCharAtGenerator::GenerateSlow( - MacroAssembler* masm, const RuntimeCallHelper& call_helper) { + MacroAssembler* masm, + const RuntimeCallHelper& call_helper) { char_code_at_generator_.GenerateSlow(masm, call_helper); char_from_code_generator_.GenerateSlow(masm, call_helper); } void StringAddStub::Generate(MacroAssembler* masm) { - Label string_add_runtime, call_builtin; + Label call_runtime, call_builtin; Builtins::JavaScript builtin_id = Builtins::ADD; // Load the two arguments. @@ -4101,14 +4508,14 @@ void StringAddStub::Generate(MacroAssembler* masm) { // Make sure that both arguments are strings if not known in advance. if (flags_ == NO_STRING_ADD_FLAGS) { - __ JumpIfSmi(rax, &string_add_runtime); + __ JumpIfSmi(rax, &call_runtime); __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8); - __ j(above_equal, &string_add_runtime); + __ j(above_equal, &call_runtime); // First argument is a a string, test second. - __ JumpIfSmi(rdx, &string_add_runtime); + __ JumpIfSmi(rdx, &call_runtime); __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9); - __ j(above_equal, &string_add_runtime); + __ j(above_equal, &call_runtime); } else { // Here at least one of the arguments is definitely a string. // We convert the one that is not known to be a string. @@ -4174,9 +4581,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ SmiCompare(rbx, Smi::FromInt(2)); __ j(not_equal, &longer_than_two); - // Check that both strings are non-external ascii strings. + // Check that both strings are non-external ASCII strings. __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx, - &string_add_runtime); + &call_runtime); // Get the two characters forming the sub string. __ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize)); @@ -4191,20 +4598,30 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ ret(2 * kPointerSize); __ bind(&make_two_character_string); - __ Set(rbx, 2); - __ jmp(&make_flat_ascii_string); + __ Set(rdi, 2); + __ AllocateAsciiString(rax, rdi, r8, r9, r11, &call_runtime); + // rbx - first byte: first character + // rbx - second byte: *maybe* second character + // Make sure that the second byte of rbx contains the second character. + __ movzxbq(rcx, FieldOperand(rdx, SeqAsciiString::kHeaderSize)); + __ shll(rcx, Immediate(kBitsPerByte)); + __ orl(rbx, rcx); + // Write both characters to the new string. + __ movw(FieldOperand(rax, SeqAsciiString::kHeaderSize), rbx); + __ IncrementCounter(counters->string_add_native(), 1); + __ ret(2 * kPointerSize); __ bind(&longer_than_two); // Check if resulting string will be flat. - __ SmiCompare(rbx, Smi::FromInt(String::kMinNonFlatLength)); + __ SmiCompare(rbx, Smi::FromInt(ConsString::kMinLength)); __ j(below, &string_add_flat_result); // Handle exceptionally long strings in the runtime system. STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0); __ SmiCompare(rbx, Smi::FromInt(String::kMaxLength)); - __ j(above, &string_add_runtime); + __ j(above, &call_runtime); // If result is not supposed to be flat, allocate a cons string object. If - // both strings are ascii the result is an ascii cons string. + // both strings are ASCII the result is an ASCII cons string. // rax: first string // rbx: length of resulting flat string // rdx: second string @@ -4218,8 +4635,8 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ testl(rcx, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii); __ bind(&ascii_data); - // Allocate an acsii cons string. - __ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime); + // Allocate an ASCII cons string. + __ AllocateAsciiConsString(rcx, rdi, no_reg, &call_runtime); __ bind(&allocated); // Fill the fields of the cons string. __ movq(FieldOperand(rcx, ConsString::kLengthOffset), rbx); @@ -4232,7 +4649,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ ret(2 * kPointerSize); __ bind(&non_ascii); // At least one of the strings is two-byte. Check whether it happens - // to contain only ascii characters. + // to contain only ASCII characters. // rcx: first instance type AND second instance type. // r8: first instance type. // r9: second instance type. @@ -4244,111 +4661,103 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ cmpb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag)); __ j(equal, &ascii_data); // Allocate a two byte cons string. - __ AllocateTwoByteConsString(rcx, rdi, no_reg, &string_add_runtime); + __ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime); __ jmp(&allocated); - // Handle creating a flat result. First check that both strings are not - // external strings. + // We cannot encounter sliced strings or cons strings here since: + STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength); + // Handle creating a flat result from either external or sequential strings. + // Locate the first characters' locations. // rax: first string // rbx: length of resulting flat string as smi // rdx: second string // r8: instance type of first string // r9: instance type of first string + Label first_prepared, second_prepared; + Label first_is_sequential, second_is_sequential; __ bind(&string_add_flat_result); - __ SmiToInteger32(rbx, rbx); - __ movl(rcx, r8); - __ and_(rcx, Immediate(kStringRepresentationMask)); - __ cmpl(rcx, Immediate(kExternalStringTag)); - __ j(equal, &string_add_runtime); - __ movl(rcx, r9); - __ and_(rcx, Immediate(kStringRepresentationMask)); - __ cmpl(rcx, Immediate(kExternalStringTag)); - __ j(equal, &string_add_runtime); - // We cannot encounter sliced strings here since: - STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength); - // Now check if both strings are ascii strings. - // rax: first string - // rbx: length of resulting flat string - // rdx: second string - // r8: instance type of first string - // r9: instance type of second string + + __ SmiToInteger32(r14, FieldOperand(rax, SeqString::kLengthOffset)); + // r14: length of first string + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(r8, Immediate(kStringRepresentationMask)); + __ j(zero, &first_is_sequential, Label::kNear); + // Rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ testb(r8, Immediate(kShortExternalStringMask)); + __ j(not_zero, &call_runtime); + __ movq(rcx, FieldOperand(rax, ExternalString::kResourceDataOffset)); + __ jmp(&first_prepared, Label::kNear); + __ bind(&first_is_sequential); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ lea(rcx, FieldOperand(rax, SeqAsciiString::kHeaderSize)); + __ bind(&first_prepared); + + // Check whether both strings have same encoding. + __ xorl(r8, r9); + __ testb(r8, Immediate(kStringEncodingMask)); + __ j(not_zero, &call_runtime); + + __ SmiToInteger32(r15, FieldOperand(rdx, SeqString::kLengthOffset)); + // r15: length of second string + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(r9, Immediate(kStringRepresentationMask)); + __ j(zero, &second_is_sequential, Label::kNear); + // Rule out short external string and load string resource. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ testb(r9, Immediate(kShortExternalStringMask)); + __ j(not_zero, &call_runtime); + __ movq(rdx, FieldOperand(rdx, ExternalString::kResourceDataOffset)); + __ jmp(&second_prepared, Label::kNear); + __ bind(&second_is_sequential); + STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize); + __ lea(rdx, FieldOperand(rdx, SeqAsciiString::kHeaderSize)); + __ bind(&second_prepared); + Label non_ascii_string_add_flat_result; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ testl(r8, Immediate(kStringEncodingMask)); + // r9: instance type of second string + // First string and second string have the same encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ SmiToInteger32(rbx, rbx); + __ testb(r9, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii_string_add_flat_result); - __ testl(r9, Immediate(kStringEncodingMask)); - __ j(zero, &string_add_runtime); __ bind(&make_flat_ascii_string); - // Both strings are ascii strings. As they are short they are both flat. - __ AllocateAsciiString(rcx, rbx, rdi, r14, r11, &string_add_runtime); - // rcx: result string - __ movq(rbx, rcx); + // Both strings are ASCII strings. As they are short they are both flat. + __ AllocateAsciiString(rax, rbx, rdi, r8, r9, &call_runtime); + // rax: result string // Locate first character of result. - __ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument - __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset)); - __ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // rax: first char of first argument - // rbx: result string - // rcx: first character of result - // rdx: second string - // rdi: length of first argument - StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, true); - // Locate first character of second argument. - __ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset)); - __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // rbx: result string - // rcx: next character of result - // rdx: first char of second argument - // rdi: length of second argument - StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, true); - __ movq(rax, rbx); + __ lea(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize)); + // rcx: first char of first string + // rbx: first character of result + // r14: length of first string + StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, true); + // rbx: next character of result + // rdx: first char of second string + // r15: length of second string + StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, true); __ IncrementCounter(counters->string_add_native(), 1); __ ret(2 * kPointerSize); - // Handle creating a flat two byte result. - // rax: first string - known to be two byte - // rbx: length of resulting flat string - // rdx: second string - // r8: instance type of first string - // r9: instance type of first string __ bind(&non_ascii_string_add_flat_result); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ and_(r9, Immediate(kStringEncodingMask)); - __ j(not_zero, &string_add_runtime); - // Both strings are two byte strings. As they are short they are both - // flat. - __ AllocateTwoByteString(rcx, rbx, rdi, r14, r11, &string_add_runtime); - // rcx: result string - __ movq(rbx, rcx); + // Both strings are ASCII strings. As they are short they are both flat. + __ AllocateTwoByteString(rax, rbx, rdi, r8, r9, &call_runtime); + // rax: result string // Locate first character of result. - __ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Locate first character of first argument. - __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset)); - __ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // rax: first char of first argument - // rbx: result string - // rcx: first character of result - // rdx: second argument - // rdi: length of first argument - StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, false); - // Locate first character of second argument. - __ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset)); - __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // rbx: result string - // rcx: next character of result - // rdx: first char of second argument - // rdi: length of second argument - StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, false); - __ movq(rax, rbx); + __ lea(rbx, FieldOperand(rax, SeqTwoByteString::kHeaderSize)); + // rcx: first char of first string + // rbx: first character of result + // r14: length of first string + StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, false); + // rbx: next character of result + // rdx: first char of second string + // r15: length of second string + StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, false); __ IncrementCounter(counters->string_add_native(), 1); __ ret(2 * kPointerSize); // Just jump to runtime to add the two strings. - __ bind(&string_add_runtime); + __ bind(&call_runtime); __ TailCallRuntime(Runtime::kStringAdd, 2, 1); if (call_builtin.is_linked()) { @@ -4566,7 +4975,12 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, __ CompareRoot(candidate, Heap::kUndefinedValueRootIndex); __ j(equal, not_found); - // Must be null (deleted entry). + // Must be the hole (deleted entry). + if (FLAG_debug_code) { + __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); + __ cmpq(kScratchRegister, candidate); + __ Assert(equal, "oddball in symbol table is not undefined or the hole"); + } __ jmp(&next_probe[i]); __ bind(&is_string); @@ -4580,7 +4994,7 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly Register temp = kScratchRegister; - // Check that the candidate is a non-external ascii string. + // Check that the candidate is a non-external ASCII string. __ movzxbl(temp, FieldOperand(map, Map::kInstanceTypeOffset)); __ JumpIfInstanceTypeIsNotSequentialAscii( temp, temp, &next_probe[i]); @@ -4695,8 +5109,12 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ SmiSub(rcx, rcx, rdx); // Overflow doesn't happen. __ cmpq(FieldOperand(rax, String::kLengthOffset), rcx); - Label return_rax; - __ j(equal, &return_rax); + Label not_original_string; + __ j(not_equal, ¬_original_string, Label::kNear); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(kArgumentsSize); + __ bind(¬_original_string); // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character // cache). Two character strings are looked for in the symbol cache. @@ -4715,71 +5133,77 @@ void SubStringStub::Generate(MacroAssembler* masm) { // Get the two characters forming the sub string. __ SmiToInteger32(rdx, rdx); // From index is no longer smi. __ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize)); - __ movzxbq(rcx, + __ movzxbq(rdi, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1)); // Try to lookup two character string in symbol table. Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( - masm, rbx, rcx, rax, rdx, rdi, r14, &make_two_character_string); + masm, rbx, rdi, r9, r11, r14, r15, &make_two_character_string); + __ IncrementCounter(counters->sub_string_native(), 1); __ ret(3 * kPointerSize); __ bind(&make_two_character_string); - // Setup registers for allocating the two character string. - __ movq(rax, Operand(rsp, kStringOffset)); - __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); + // Set up registers for allocating the two character string. + __ movzxwq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize)); + __ AllocateAsciiString(rax, rcx, r11, r14, r15, &runtime); + __ movw(FieldOperand(rax, SeqAsciiString::kHeaderSize), rbx); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(3 * kPointerSize); + + __ bind(&result_longer_than_two); + // rax: string + // rbx: instance type + // rcx: sub string length + // rdx: from index (smi) + // Deal with different string types: update the index if necessary + // and put the underlying string into edi. + Label underlying_unpacked, sliced_string, seq_or_external_string; + // If the string is not indirect, it can only be sequential or external. + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ testb(rbx, Immediate(kIsIndirectStringMask)); + __ j(zero, &seq_or_external_string, Label::kNear); + + __ testb(rbx, Immediate(kSlicedNotConsMask)); + __ j(not_zero, &sliced_string, Label::kNear); + // Cons string. Check whether it is flat, then fetch first part. + // Flat cons strings have an empty second part. + __ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset), + Heap::kEmptyStringRootIndex); + __ j(not_equal, &runtime); + __ movq(rdi, FieldOperand(rax, ConsString::kFirstOffset)); + // Update instance type. + __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); + __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); + __ jmp(&underlying_unpacked, Label::kNear); + + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ addq(rdx, FieldOperand(rax, SlicedString::kOffsetOffset)); + __ movq(rdi, FieldOperand(rax, SlicedString::kParentOffset)); + // Update instance type. + __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); - __ Set(rcx, 2); + __ jmp(&underlying_unpacked, Label::kNear); + + __ bind(&seq_or_external_string); + // Sequential or external string. Just move string to the correct register. + __ movq(rdi, rax); + + __ bind(&underlying_unpacked); if (FLAG_string_slices) { Label copy_routine; + // rdi: underlying subject string + // rbx: instance type of underlying subject string + // rdx: adjusted start index (smi) + // rcx: length // If coming from the make_two_character_string path, the string // is too short to be sliced anyways. - STATIC_ASSERT(2 < SlicedString::kMinLength); - __ jmp(©_routine); - __ bind(&result_longer_than_two); - - // rax: string - // rbx: instance type - // rcx: sub string length - // rdx: from index (smi) - Label allocate_slice, sliced_string, seq_string; __ cmpq(rcx, Immediate(SlicedString::kMinLength)); // Short slice. Copy instead of slicing. __ j(less, ©_routine); - STATIC_ASSERT(kSeqStringTag == 0); - __ testb(rbx, Immediate(kStringRepresentationMask)); - __ j(zero, &seq_string, Label::kNear); - STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); - STATIC_ASSERT(kIsIndirectStringMask != 0); - __ testb(rbx, Immediate(kIsIndirectStringMask)); - // External string. Jump to runtime. - __ j(zero, &runtime); - - __ testb(rbx, Immediate(kSlicedNotConsMask)); - __ j(not_zero, &sliced_string, Label::kNear); - // Cons string. Check whether it is flat, then fetch first part. - __ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset), - Heap::kEmptyStringRootIndex); - __ j(not_equal, &runtime); - __ movq(rdi, FieldOperand(rax, ConsString::kFirstOffset)); - __ jmp(&allocate_slice, Label::kNear); - - __ bind(&sliced_string); - // Sliced string. Fetch parent and correct start index by offset. - __ addq(rdx, FieldOperand(rax, SlicedString::kOffsetOffset)); - __ movq(rdi, FieldOperand(rax, SlicedString::kParentOffset)); - __ jmp(&allocate_slice, Label::kNear); - - __ bind(&seq_string); - // Sequential string. Just move string to the right register. - __ movq(rdi, rax); - - __ bind(&allocate_slice); - // edi: underlying subject string - // ebx: instance type of original subject string - // edx: offset - // ecx: length // Allocate new sliced string. At this point we do not reload the instance // type including the string encoding because we simply rely on the info // provided by the original string. It does not matter if the original @@ -4790,10 +5214,10 @@ void SubStringStub::Generate(MacroAssembler* masm) { STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testb(rbx, Immediate(kStringEncodingMask)); __ j(zero, &two_byte_slice, Label::kNear); - __ AllocateAsciiSlicedString(rax, rbx, no_reg, &runtime); + __ AllocateAsciiSlicedString(rax, rbx, r14, &runtime); __ jmp(&set_slice_header, Label::kNear); __ bind(&two_byte_slice); - __ AllocateTwoByteSlicedString(rax, rbx, no_reg, &runtime); + __ AllocateTwoByteSlicedString(rax, rbx, r14, &runtime); __ bind(&set_slice_header); __ movq(FieldOperand(rax, SlicedString::kOffsetOffset), rdx); __ Integer32ToSmi(rcx, rcx); @@ -4801,82 +5225,85 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ movq(FieldOperand(rax, SlicedString::kParentOffset), rdi); __ movq(FieldOperand(rax, SlicedString::kHashFieldOffset), Immediate(String::kEmptyHashField)); - __ jmp(&return_rax); + __ IncrementCounter(counters->sub_string_native(), 1); + __ ret(kArgumentsSize); __ bind(©_routine); - } else { - __ bind(&result_longer_than_two); } - // rax: string - // rbx: instance type - // rcx: result string length - // Check for flat ascii string - Label non_ascii_flat; - __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &non_ascii_flat); + // rdi: underlying subject string + // rbx: instance type of underlying subject string + // rdx: adjusted start index (smi) + // rcx: length + // The subject string can only be external or sequential string of either + // encoding at this point. + Label two_byte_sequential, sequential_string; + STATIC_ASSERT(kExternalStringTag != 0); + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(rbx, Immediate(kExternalStringTag)); + __ j(zero, &sequential_string); + + // Handle external string. + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ testb(rbx, Immediate(kShortExternalStringMask)); + __ j(not_zero, &runtime); + __ movq(rdi, FieldOperand(rdi, ExternalString::kResourceDataOffset)); + // Move the pointer so that offset-wise, it looks like a sequential string. + STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize); + __ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + + __ bind(&sequential_string); + STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0); + __ testb(rbx, Immediate(kStringEncodingMask)); + __ j(zero, &two_byte_sequential); // Allocate the result. - __ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime); + __ AllocateAsciiString(rax, rcx, r11, r14, r15, &runtime); // rax: result string // rcx: result string length - __ movq(rdx, rsi); // esi used by following code. - // Locate first character of result. - __ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize)); - // Load string argument and locate character of sub string start. - __ movq(rsi, Operand(rsp, kStringOffset)); - __ movq(rbx, Operand(rsp, kFromOffset)); - { - SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_1); - __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale, + __ movq(r14, rsi); // esi used by following code. + { // Locate character of sub string start. + SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_1); + __ lea(rsi, Operand(rdi, smi_as_index.reg, smi_as_index.scale, SeqAsciiString::kHeaderSize - kHeapObjectTag)); } + // Locate first character of result. + __ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize)); // rax: result string // rcx: result length - // rdx: original value of rsi // rdi: first character of result // rsi: character of sub string start + // r14: original value of rsi StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true); - __ movq(rsi, rdx); // Restore rsi. - Counters* counters = masm->isolate()->counters(); + __ movq(rsi, r14); // Restore rsi. __ IncrementCounter(counters->sub_string_native(), 1); __ ret(kArgumentsSize); - __ bind(&non_ascii_flat); - // rax: string - // rbx: instance type & kStringRepresentationMask | kStringEncodingMask - // rcx: result string length - // Check for sequential two byte string - __ cmpb(rbx, Immediate(kSeqStringTag | kTwoByteStringTag)); - __ j(not_equal, &runtime); - + __ bind(&two_byte_sequential); // Allocate the result. - __ AllocateTwoByteString(rax, rcx, rbx, rdx, rdi, &runtime); + __ AllocateTwoByteString(rax, rcx, r11, r14, r15, &runtime); // rax: result string // rcx: result string length - __ movq(rdx, rsi); // esi used by following code. - // Locate first character of result. - __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize)); - // Load string argument and locate character of sub string start. - __ movq(rsi, Operand(rsp, kStringOffset)); - __ movq(rbx, Operand(rsp, kFromOffset)); - { - SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_2); - __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale, + __ movq(r14, rsi); // esi used by following code. + { // Locate character of sub string start. + SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_2); + __ lea(rsi, Operand(rdi, smi_as_index.reg, smi_as_index.scale, SeqAsciiString::kHeaderSize - kHeapObjectTag)); } + // Locate first character of result. + __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize)); // rax: result string // rcx: result length - // rdx: original value of rsi // rdi: first character of result // rsi: character of sub string start + // r14: original value of rsi StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false); - __ movq(rsi, rdx); // Restore esi. - - __ bind(&return_rax); + __ movq(rsi, r14); // Restore esi. __ IncrementCounter(counters->sub_string_native(), 1); __ ret(kArgumentsSize); @@ -5047,7 +5474,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) { // Check that both are sequential ASCII strings. __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &runtime); - // Inline comparison of ascii strings. + // Inline comparison of ASCII strings. __ IncrementCounter(counters->string_compare_native(), 1); // Drop arguments from the stack __ pop(rcx); @@ -5266,44 +5693,57 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { } -void ICCompareStub::GenerateMiss(MacroAssembler* masm) { - // Save the registers. - __ pop(rcx); - __ push(rdx); - __ push(rax); - __ push(rcx); +void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) { + Label miss; + Condition either_smi = masm->CheckEitherSmi(rdx, rax); + __ j(either_smi, &miss, Label::kNear); - // Call the runtime system in a fresh internal frame. - ExternalReference miss = - ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); - __ EnterInternalFrame(); - __ push(rdx); - __ push(rax); - __ Push(Smi::FromInt(op_)); - __ CallExternalReference(miss, 3); - __ LeaveInternalFrame(); + __ movq(rcx, FieldOperand(rax, HeapObject::kMapOffset)); + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + __ Cmp(rcx, known_map_); + __ j(not_equal, &miss, Label::kNear); + __ Cmp(rbx, known_map_); + __ j(not_equal, &miss, Label::kNear); - // Compute the entry point of the rewritten stub. - __ lea(rdi, FieldOperand(rax, Code::kHeaderSize)); + __ subq(rax, rdx); + __ ret(0); - // Restore registers. - __ pop(rcx); - __ pop(rax); - __ pop(rdx); - __ push(rcx); + __ bind(&miss); + GenerateMiss(masm); +} + + +void ICCompareStub::GenerateMiss(MacroAssembler* masm) { + { + // Call the runtime system in a fresh internal frame. + ExternalReference miss = + ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); + + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(rdx); + __ push(rax); + __ push(rdx); + __ push(rax); + __ Push(Smi::FromInt(op_)); + __ CallExternalReference(miss, 3); + + // Compute the entry point of the rewritten stub. + __ lea(rdi, FieldOperand(rax, Code::kHeaderSize)); + __ pop(rax); + __ pop(rdx); + } // Do a tail call to the rewritten stub. __ jmp(rdi); } -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - String* name, - Register r0) { +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle<String> name, + Register r0) { // If names of slots in range from 1 to kProbes - 1 for the hash value are // not equal to the name and kProbes-th slot is not used (its name is the // undefined value), it guarantees the hash table doesn't contain the @@ -5350,12 +5790,10 @@ MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( StringDictionaryLookupStub::NEGATIVE_LOOKUP); __ Push(Handle<Object>(name)); __ push(Immediate(name->Hash())); - MaybeObject* result = masm->TryCallStub(&stub); - if (result->IsFailure()) return result; + __ CallStub(&stub); __ testq(r0, r0); __ j(not_zero, miss); __ jmp(done); - return result; } @@ -5370,6 +5808,11 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, Register name, Register r0, Register r1) { + ASSERT(!elements.is(r0)); + ASSERT(!elements.is(r1)); + ASSERT(!name.is(r0)); + ASSERT(!name.is(r1)); + // Assert that name contains a string. if (FLAG_debug_code) __ AbortIfNotString(name); @@ -5412,6 +5855,8 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. // Stack frame on entry: // esp[0 * kPointerSize]: return address. // esp[1 * kPointerSize]: key's hash. @@ -5497,6 +5942,364 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { } +struct AheadOfTimeWriteBarrierStubList { + Register object, value, address; + RememberedSetAction action; +}; + + +struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { + // Used in RegExpExecStub. + { rbx, rax, rdi, EMIT_REMEMBERED_SET }, + // Used in CompileArrayPushCall. + { rbx, rcx, rdx, EMIT_REMEMBERED_SET }, + // Used in CompileStoreGlobal. + { rbx, rcx, rdx, OMIT_REMEMBERED_SET }, + // Used in StoreStubCompiler::CompileStoreField and + // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField. + { rdx, rcx, rbx, EMIT_REMEMBERED_SET }, + // GenerateStoreField calls the stub with two different permutations of + // registers. This is the second. + { rbx, rcx, rdx, EMIT_REMEMBERED_SET }, + // StoreIC::GenerateNormal via GenerateDictionaryStore. + { rbx, r8, r9, EMIT_REMEMBERED_SET }, + // KeyedStoreIC::GenerateGeneric. + { rbx, rdx, rcx, EMIT_REMEMBERED_SET}, + // KeyedStoreStubCompiler::GenerateStoreFastElement. + { rdi, rdx, rcx, EMIT_REMEMBERED_SET}, + // ElementsTransitionGenerator::GenerateSmiOnlyToObject + // and ElementsTransitionGenerator::GenerateSmiOnlyToObject + // and ElementsTransitionGenerator::GenerateDoubleToObject + { rdx, rbx, rdi, EMIT_REMEMBERED_SET}, + // ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // and ElementsTransitionGenerator::GenerateDoubleToObject + { rdx, r11, r15, EMIT_REMEMBERED_SET}, + // ElementsTransitionGenerator::GenerateDoubleToObject + { r11, rax, r15, EMIT_REMEMBERED_SET}, + // StoreArrayLiteralElementStub::Generate + { rbx, rax, rcx, EMIT_REMEMBERED_SET}, + // Null termination. + { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET} +}; + + +bool RecordWriteStub::IsPregenerated() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + if (object_.is(entry->object) && + value_.is(entry->value) && + address_.is(entry->address) && + remembered_set_action_ == entry->action && + save_fp_regs_mode_ == kDontSaveFPRegs) { + return true; + } + } + return false; +} + + +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() { + StoreBufferOverflowStub stub1(kDontSaveFPRegs); + stub1.GetCode()->set_is_pregenerated(true); + StoreBufferOverflowStub stub2(kSaveFPRegs); + stub2.GetCode()->set_is_pregenerated(true); +} + + +void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() { + for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime; + !entry->object.is(no_reg); + entry++) { + RecordWriteStub stub(entry->object, + entry->value, + entry->address, + entry->action, + kDontSaveFPRegs); + stub.GetCode()->set_is_pregenerated(true); + } +} + + +// Takes the input in 3 registers: address_ value_ and object_. A pointer to +// the value has just been written into the object, now this stub makes sure +// we keep the GC informed. The word in the object where the value has been +// written is in the address register. +void RecordWriteStub::Generate(MacroAssembler* masm) { + Label skip_to_incremental_noncompacting; + Label skip_to_incremental_compacting; + + // The first two instructions are generated with labels so as to get the + // offset fixed up correctly by the bind(Label*) call. We patch it back and + // forth between a compare instructions (a nop in this position) and the + // real branch when we start and stop incremental heap marking. + // See RecordWriteStub::Patch for details. + __ jmp(&skip_to_incremental_noncompacting, Label::kNear); + __ jmp(&skip_to_incremental_compacting, Label::kFar); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&skip_to_incremental_noncompacting); + GenerateIncremental(masm, INCREMENTAL); + + __ bind(&skip_to_incremental_compacting); + GenerateIncremental(masm, INCREMENTAL_COMPACTION); + + // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. + // Will be checked in IncrementalMarking::ActivateGeneratedStub. + masm->set_byte_at(0, kTwoByteNopInstruction); + masm->set_byte_at(2, kFiveByteNopInstruction); +} + + +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { + regs_.Save(masm); + + if (remembered_set_action_ == EMIT_REMEMBERED_SET) { + Label dont_need_remembered_set; + + __ movq(regs_.scratch0(), Operand(regs_.address(), 0)); + __ JumpIfNotInNewSpace(regs_.scratch0(), + regs_.scratch0(), + &dont_need_remembered_set); + + __ CheckPageFlag(regs_.object(), + regs_.scratch0(), + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + not_zero, + &dont_need_remembered_set); + + // First notify the incremental marker if necessary, then update the + // remembered set. + CheckNeedsToInformIncrementalMarker( + masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + + __ bind(&dont_need_remembered_set); + } + + CheckNeedsToInformIncrementalMarker( + masm, kReturnOnNoNeedToInformIncrementalMarker, mode); + InformIncrementalMarker(masm, mode); + regs_.Restore(masm); + __ ret(0); +} + + +void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) { + regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_); +#ifdef _WIN64 + Register arg3 = r8; + Register arg2 = rdx; + Register arg1 = rcx; +#else + Register arg3 = rdx; + Register arg2 = rsi; + Register arg1 = rdi; +#endif + Register address = + arg1.is(regs_.address()) ? kScratchRegister : regs_.address(); + ASSERT(!address.is(regs_.object())); + ASSERT(!address.is(arg1)); + __ Move(address, regs_.address()); + __ Move(arg1, regs_.object()); + if (mode == INCREMENTAL_COMPACTION) { + // TODO(gc) Can we just set address arg2 in the beginning? + __ Move(arg2, address); + } else { + ASSERT(mode == INCREMENTAL); + __ movq(arg2, Operand(address, 0)); + } + __ LoadAddress(arg3, ExternalReference::isolate_address()); + int argument_count = 3; + + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(argument_count); + if (mode == INCREMENTAL_COMPACTION) { + __ CallCFunction( + ExternalReference::incremental_evacuation_record_write_function( + masm->isolate()), + argument_count); + } else { + ASSERT(mode == INCREMENTAL); + __ CallCFunction( + ExternalReference::incremental_marking_record_write_function( + masm->isolate()), + argument_count); + } + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_); +} + + +void RecordWriteStub::CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode) { + Label on_black; + Label need_incremental; + Label need_incremental_pop_object; + + // Let's look at the color of the object: If it is not black we don't have + // to inform the incremental marker. + __ JumpIfBlack(regs_.object(), + regs_.scratch0(), + regs_.scratch1(), + &on_black, + Label::kNear); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&on_black); + + // Get the value from the slot. + __ movq(regs_.scratch0(), Operand(regs_.address(), 0)); + + if (mode == INCREMENTAL_COMPACTION) { + Label ensure_not_white; + + __ CheckPageFlag(regs_.scratch0(), // Contains value. + regs_.scratch1(), // Scratch. + MemoryChunk::kEvacuationCandidateMask, + zero, + &ensure_not_white, + Label::kNear); + + __ CheckPageFlag(regs_.object(), + regs_.scratch1(), // Scratch. + MemoryChunk::kSkipEvacuationSlotsRecordingMask, + zero, + &need_incremental); + + __ bind(&ensure_not_white); + } + + // We need an extra register for this, so we push the object register + // temporarily. + __ push(regs_.object()); + __ EnsureNotWhite(regs_.scratch0(), // The value. + regs_.scratch1(), // Scratch. + regs_.object(), // Scratch. + &need_incremental_pop_object, + Label::kNear); + __ pop(regs_.object()); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object_, + address_, + value_, + save_fp_regs_mode_, + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&need_incremental_pop_object); + __ pop(regs_.object()); + + __ bind(&need_incremental); + + // Fall through when we need to inform the incremental marker. +} + + +void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : element value to store + // -- rbx : array literal + // -- rdi : map of array literal + // -- rcx : element index as smi + // -- rdx : array literal index in function + // -- rsp[0] : return address + // ----------------------------------- + + Label element_done; + Label double_elements; + Label smi_element; + Label slow_elements; + Label fast_elements; + + __ CheckFastElements(rdi, &double_elements); + + // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + __ JumpIfSmi(rax, &smi_element); + __ CheckFastSmiOnlyElements(rdi, &fast_elements); + + // Store into the array literal requires a elements transition. Call into + // the runtime. + + __ bind(&slow_elements); + __ pop(rdi); // Pop return address and remember to put back later for tail + // call. + __ push(rbx); + __ push(rcx); + __ push(rax); + __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); + __ push(rdx); + __ push(rdi); // Return return address so that tail call returns to right + // place. + __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); + + // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + __ bind(&fast_elements); + __ SmiToInteger32(kScratchRegister, rcx); + __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); + __ lea(rcx, FieldOperand(rbx, kScratchRegister, times_pointer_size, + FixedArrayBase::kHeaderSize)); + __ movq(Operand(rcx, 0), rax); + // Update the write barrier for the array store. + __ RecordWrite(rbx, rcx, rax, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ ret(0); + + // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or + // FAST_ELEMENTS, and value is Smi. + __ bind(&smi_element); + __ SmiToInteger32(kScratchRegister, rcx); + __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); + __ movq(FieldOperand(rbx, kScratchRegister, times_pointer_size, + FixedArrayBase::kHeaderSize), rax); + __ ret(0); + + // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + __ bind(&double_elements); + + __ movq(r9, FieldOperand(rbx, JSObject::kElementsOffset)); + __ SmiToInteger32(r11, rcx); + __ StoreNumberToDoubleElements(rax, + r9, + r11, + xmm0, + &slow_elements); + __ ret(0); +} + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/x64/code-stubs-x64.h b/deps/v8/src/x64/code-stubs-x64.h index 4058118eef..30ef3e8c53 100644 --- a/deps/v8/src/x64/code-stubs-x64.h +++ b/deps/v8/src/x64/code-stubs-x64.h @@ -59,6 +59,32 @@ class TranscendentalCacheStub: public CodeStub { }; +class StoreBufferOverflowStub: public CodeStub { + public: + explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) + : save_doubles_(save_fp) { } + + void Generate(MacroAssembler* masm); + + virtual bool IsPregenerated() { return true; } + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + private: + SaveFPRegsMode save_doubles_; + + Major MajorKey() { return StoreBufferOverflow; } + int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } +}; + + +// Flag that indicates how to generate code for the stub GenericBinaryOpStub. +enum GenericBinaryFlags { + NO_GENERIC_BINARY_FLAGS = 0, + NO_SMI_CODE_IN_STUB = 1 << 0 // Omit smi code in stub. +}; + + class UnaryOpStub: public CodeStub { public: UnaryOpStub(Token::Value op, @@ -124,7 +150,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_unary_op_type(operand_type_); } }; @@ -210,7 +236,7 @@ class BinaryOpStub: public CodeStub { return BinaryOpIC::ToState(operands_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle<Code> code) { code->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } @@ -397,13 +423,12 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - String* name, - Register r0); + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle<String> name, + Register r0); static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss, @@ -413,6 +438,8 @@ class StringDictionaryLookupStub: public CodeStub { Register r0, Register r1); + virtual bool SometimesSetsUpAFrame() { return false; } + private: static const int kInlinedProbes = 4; static const int kTotalProbes = 20; @@ -425,7 +452,7 @@ class StringDictionaryLookupStub: public CodeStub { StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; - Major MajorKey() { return StringDictionaryNegativeLookup; } + Major MajorKey() { return StringDictionaryLookup; } int MinorKey() { return DictionaryBits::encode(dictionary_.code()) | @@ -446,6 +473,246 @@ class StringDictionaryLookupStub: public CodeStub { }; +class RecordWriteStub: public CodeStub { + public: + RecordWriteStub(Register object, + Register value, + Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode) + : object_(object), + value_(value), + address_(address), + remembered_set_action_(remembered_set_action), + save_fp_regs_mode_(fp_mode), + regs_(object, // An input reg. + address, // An input reg. + value) { // One scratch reg. + } + + enum Mode { + STORE_BUFFER_ONLY, + INCREMENTAL, + INCREMENTAL_COMPACTION + }; + + virtual bool IsPregenerated(); + static void GenerateFixedRegStubsAheadOfTime(); + virtual bool SometimesSetsUpAFrame() { return false; } + + static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. + static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. + + static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. + static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. + + static Mode GetMode(Code* stub) { + byte first_instruction = stub->instruction_start()[0]; + byte second_instruction = stub->instruction_start()[2]; + + if (first_instruction == kTwoByteJumpInstruction) { + return INCREMENTAL; + } + + ASSERT(first_instruction == kTwoByteNopInstruction); + + if (second_instruction == kFiveByteJumpInstruction) { + return INCREMENTAL_COMPACTION; + } + + ASSERT(second_instruction == kFiveByteNopInstruction); + + return STORE_BUFFER_ONLY; + } + + static void Patch(Code* stub, Mode mode) { + switch (mode) { + case STORE_BUFFER_ONLY: + ASSERT(GetMode(stub) == INCREMENTAL || + GetMode(stub) == INCREMENTAL_COMPACTION); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteNopInstruction; + break; + case INCREMENTAL: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteJumpInstruction; + break; + case INCREMENTAL_COMPACTION: + ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteJumpInstruction; + break; + } + ASSERT(GetMode(stub) == mode); + CPU::FlushICache(stub->instruction_start(), 7); + } + + private: + // This is a helper class for freeing up 3 scratch registers, where the third + // is always rcx (needed for shift operations). The input is two registers + // that must be preserved and one scratch register provided by the caller. + class RegisterAllocation { + public: + RegisterAllocation(Register object, + Register address, + Register scratch0) + : object_orig_(object), + address_orig_(address), + scratch0_orig_(scratch0), + object_(object), + address_(address), + scratch0_(scratch0) { + ASSERT(!AreAliased(scratch0, object, address, no_reg)); + scratch1_ = GetRegThatIsNotRcxOr(object_, address_, scratch0_); + if (scratch0.is(rcx)) { + scratch0_ = GetRegThatIsNotRcxOr(object_, address_, scratch1_); + } + if (object.is(rcx)) { + object_ = GetRegThatIsNotRcxOr(address_, scratch0_, scratch1_); + } + if (address.is(rcx)) { + address_ = GetRegThatIsNotRcxOr(object_, scratch0_, scratch1_); + } + ASSERT(!AreAliased(scratch0_, object_, address_, rcx)); + } + + void Save(MacroAssembler* masm) { + ASSERT(!address_orig_.is(object_)); + ASSERT(object_.is(object_orig_) || address_.is(address_orig_)); + ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); + ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); + ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); + // We don't have to save scratch0_orig_ because it was given to us as + // a scratch register. But if we had to switch to a different reg then + // we should save the new scratch0_. + if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); + if (!rcx.is(scratch0_orig_) && + !rcx.is(object_orig_) && + !rcx.is(address_orig_)) { + masm->push(rcx); + } + masm->push(scratch1_); + if (!address_.is(address_orig_)) { + masm->push(address_); + masm->movq(address_, address_orig_); + } + if (!object_.is(object_orig_)) { + masm->push(object_); + masm->movq(object_, object_orig_); + } + } + + void Restore(MacroAssembler* masm) { + // These will have been preserved the entire time, so we just need to move + // them back. Only in one case is the orig_ reg different from the plain + // one, since only one of them can alias with rcx. + if (!object_.is(object_orig_)) { + masm->movq(object_orig_, object_); + masm->pop(object_); + } + if (!address_.is(address_orig_)) { + masm->movq(address_orig_, address_); + masm->pop(address_); + } + masm->pop(scratch1_); + if (!rcx.is(scratch0_orig_) && + !rcx.is(object_orig_) && + !rcx.is(address_orig_)) { + masm->pop(rcx); + } + if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); + } + + // If we have to call into C then we need to save and restore all caller- + // saved registers that were not already preserved. + + // The three scratch registers (incl. rcx) will be restored by other means + // so we don't bother pushing them here. Rbx, rbp and r12-15 are callee + // save and don't need to be preserved. + void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { + masm->PushCallerSaved(mode, scratch0_, scratch1_, rcx); + } + + inline void RestoreCallerSaveRegisters(MacroAssembler*masm, + SaveFPRegsMode mode) { + masm->PopCallerSaved(mode, scratch0_, scratch1_, rcx); + } + + inline Register object() { return object_; } + inline Register address() { return address_; } + inline Register scratch0() { return scratch0_; } + inline Register scratch1() { return scratch1_; } + + private: + Register object_orig_; + Register address_orig_; + Register scratch0_orig_; + Register object_; + Register address_; + Register scratch0_; + Register scratch1_; + // Third scratch register is always rcx. + + Register GetRegThatIsNotRcxOr(Register r1, + Register r2, + Register r3) { + for (int i = 0; i < Register::kNumAllocatableRegisters; i++) { + Register candidate = Register::FromAllocationIndex(i); + if (candidate.is(rcx)) continue; + if (candidate.is(r1)) continue; + if (candidate.is(r2)) continue; + if (candidate.is(r3)) continue; + return candidate; + } + UNREACHABLE(); + return no_reg; + } + friend class RecordWriteStub; + }; + + enum OnNoNeedToInformIncrementalMarker { + kReturnOnNoNeedToInformIncrementalMarker, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker + }; + + void Generate(MacroAssembler* masm); + void GenerateIncremental(MacroAssembler* masm, Mode mode); + void CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode); + void InformIncrementalMarker(MacroAssembler* masm, Mode mode); + + Major MajorKey() { return RecordWrite; } + + int MinorKey() { + return ObjectBits::encode(object_.code()) | + ValueBits::encode(value_.code()) | + AddressBits::encode(address_.code()) | + RememberedSetActionBits::encode(remembered_set_action_) | + SaveFPRegsModeBits::encode(save_fp_regs_mode_); + } + + void Activate(Code* code) { + code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); + } + + class ObjectBits: public BitField<int, 0, 4> {}; + class ValueBits: public BitField<int, 4, 4> {}; + class AddressBits: public BitField<int, 8, 4> {}; + class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {}; + class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {}; + + Register object_; + Register value_; + Register address_; + RememberedSetAction remembered_set_action_; + SaveFPRegsMode save_fp_regs_mode_; + Label slow_; + RegisterAllocation regs_; +}; + + } } // namespace v8::internal #endif // V8_X64_CODE_STUBS_X64_H_ diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 507bbd44c3..f7e8fc114f 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -30,6 +30,7 @@ #if defined(V8_TARGET_ARCH_X64) #include "codegen.h" +#include "macro-assembler.h" namespace v8 { namespace internal { @@ -38,12 +39,16 @@ namespace internal { // Platform-specific RuntimeCallHelper functions. void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterInternalFrame(); + masm->EnterFrame(StackFrame::INTERNAL); + ASSERT(!masm->has_frame()); + masm->set_has_frame(true); } void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveInternalFrame(); + masm->LeaveFrame(StackFrame::INTERNAL); + ASSERT(masm->has_frame()); + masm->set_has_frame(false); } @@ -139,6 +144,331 @@ ModuloFunction CreateModuloFunction() { #endif +#undef __ + +// ------------------------------------------------------------------------- +// Code generators + +#define __ ACCESS_MASM(masm) + +void ElementsTransitionGenerator::GenerateSmiOnlyToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : value + // -- rbx : target map + // -- rcx : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + // Set transitioned map. + __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); + __ RecordWriteField(rdx, + HeapObject::kMapOffset, + rbx, + rdi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); +} + + +void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- rax : value + // -- rbx : target map + // -- rcx : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + // The fail label is not actually used since we do not allocate. + Label allocated, cow_array; + + // Check backing store for COW-ness. If the negative case, we do not have to + // allocate a new array, since FixedArray and FixedDoubleArray do not differ + // in size. + __ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset)); + __ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset)); + __ CompareRoot(FieldOperand(r8, HeapObject::kMapOffset), + Heap::kFixedCOWArrayMapRootIndex); + __ j(equal, &cow_array); + __ movq(r14, r8); // Destination array equals source array. + + __ bind(&allocated); + // r8 : source FixedArray + // r9 : elements array length + // r14: destination FixedDoubleArray + // Set backing store's map + __ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); + __ movq(FieldOperand(r14, HeapObject::kMapOffset), rdi); + + // Set transitioned map. + __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); + __ RecordWriteField(rdx, + HeapObject::kMapOffset, + rbx, + rdi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + + // Convert smis to doubles and holes to hole NaNs. The Array's length + // remains unchanged. + STATIC_ASSERT(FixedDoubleArray::kLengthOffset == FixedArray::kLengthOffset); + STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize); + + Label loop, entry, convert_hole; + __ movq(r15, BitCast<int64_t, uint64_t>(kHoleNanInt64), RelocInfo::NONE); + // r15: the-hole NaN + __ jmp(&entry); + + // Allocate new array if the source array is a COW array. + __ bind(&cow_array); + __ lea(rdi, Operand(r9, times_pointer_size, FixedArray::kHeaderSize)); + __ AllocateInNewSpace(rdi, r14, r11, r15, fail, TAG_OBJECT); + // Set receiver's backing store. + __ movq(FieldOperand(rdx, JSObject::kElementsOffset), r14); + __ movq(r11, r14); + __ RecordWriteField(rdx, + JSObject::kElementsOffset, + r11, + r15, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Set backing store's length. + __ Integer32ToSmi(r11, r9); + __ movq(FieldOperand(r14, FixedDoubleArray::kLengthOffset), r11); + __ jmp(&allocated); + + // Conversion loop. + __ bind(&loop); + __ movq(rbx, + FieldOperand(r8, r9, times_8, FixedArray::kHeaderSize)); + // r9 : current element's index + // rbx: current element (smi-tagged) + __ JumpIfNotSmi(rbx, &convert_hole); + __ SmiToInteger32(rbx, rbx); + __ cvtlsi2sd(xmm0, rbx); + __ movsd(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), + xmm0); + __ jmp(&entry); + __ bind(&convert_hole); + + if (FLAG_debug_code) { + __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); + __ Assert(equal, "object found in smi-only array"); + } + + __ movq(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), r15); + __ bind(&entry); + __ decq(r9); + __ j(not_sign, &loop); +} + + +void ElementsTransitionGenerator::GenerateDoubleToObject( + MacroAssembler* masm, Label* fail) { + // ----------- S t a t e ------------- + // -- rax : value + // -- rbx : target map + // -- rcx : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label loop, entry, convert_hole, gc_required; + __ push(rax); + + __ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset)); + __ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset)); + // r8 : source FixedDoubleArray + // r9 : number of elements + __ lea(rdi, Operand(r9, times_pointer_size, FixedArray::kHeaderSize)); + __ AllocateInNewSpace(rdi, r11, r14, r15, &gc_required, TAG_OBJECT); + // r11: destination FixedArray + __ LoadRoot(rdi, Heap::kFixedArrayMapRootIndex); + __ movq(FieldOperand(r11, HeapObject::kMapOffset), rdi); + __ Integer32ToSmi(r14, r9); + __ movq(FieldOperand(r11, FixedArray::kLengthOffset), r14); + + // Prepare for conversion loop. + __ movq(rsi, BitCast<int64_t, uint64_t>(kHoleNanInt64), RelocInfo::NONE); + __ LoadRoot(rdi, Heap::kTheHoleValueRootIndex); + // rsi: the-hole NaN + // rdi: pointer to the-hole + __ jmp(&entry); + + // Call into runtime if GC is required. + __ bind(&gc_required); + __ pop(rax); + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + __ jmp(fail); + + // Box doubles into heap numbers. + __ bind(&loop); + __ movq(r14, FieldOperand(r8, + r9, + times_pointer_size, + FixedDoubleArray::kHeaderSize)); + // r9 : current element's index + // r14: current element + __ cmpq(r14, rsi); + __ j(equal, &convert_hole); + + // Non-hole double, copy value into a heap number. + __ AllocateHeapNumber(rax, r15, &gc_required); + // rax: new heap number + __ movq(FieldOperand(rax, HeapNumber::kValueOffset), r14); + __ movq(FieldOperand(r11, + r9, + times_pointer_size, + FixedArray::kHeaderSize), + rax); + __ movq(r15, r9); + __ RecordWriteArray(r11, + rax, + r15, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ jmp(&entry, Label::kNear); + + // Replace the-hole NaN with the-hole pointer. + __ bind(&convert_hole); + __ movq(FieldOperand(r11, + r9, + times_pointer_size, + FixedArray::kHeaderSize), + rdi); + + __ bind(&entry); + __ decq(r9); + __ j(not_sign, &loop); + + // Set transitioned map. + __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); + __ RecordWriteField(rdx, + HeapObject::kMapOffset, + rbx, + rdi, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + // Replace receiver's backing store with newly created and filled FixedArray. + __ movq(FieldOperand(rdx, JSObject::kElementsOffset), r11); + __ RecordWriteField(rdx, + JSObject::kElementsOffset, + r11, + r15, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ pop(rax); + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); +} + + +void StringCharLoadGenerator::Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime) { + // Fetch the instance type of the receiver into result register. + __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for indirect strings. + Label check_sequential; + __ testb(result, Immediate(kIsIndirectStringMask)); + __ j(zero, &check_sequential, Label::kNear); + + // Dispatch on the indirect string shape: slice or cons. + Label cons_string; + __ testb(result, Immediate(kSlicedNotConsMask)); + __ j(zero, &cons_string, Label::kNear); + + // Handle slices. + Label indirect_string_loaded; + __ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset)); + __ addq(index, result); + __ movq(string, FieldOperand(string, SlicedString::kParentOffset)); + __ jmp(&indirect_string_loaded, Label::kNear); + + // Handle cons strings. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ bind(&cons_string); + __ CompareRoot(FieldOperand(string, ConsString::kSecondOffset), + Heap::kEmptyStringRootIndex); + __ j(not_equal, call_runtime); + __ movq(string, FieldOperand(string, ConsString::kFirstOffset)); + + __ bind(&indirect_string_loaded); + __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // Distinguish sequential and external strings. Only these two string + // representations can reach here (slices and flat cons strings have been + // reduced to the underlying sequential or external string). + Label seq_string; + __ bind(&check_sequential); + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(result, Immediate(kStringRepresentationMask)); + __ j(zero, &seq_string, Label::kNear); + + // Handle external strings. + Label ascii_external, done; + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ testb(result, Immediate(kIsIndirectStringMask)); + __ Assert(zero, "external string expected, but not found"); + } + // Rule out short external strings. + STATIC_CHECK(kShortExternalStringTag != 0); + __ testb(result, Immediate(kShortExternalStringTag)); + __ j(not_zero, call_runtime); + // Check encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ testb(result, Immediate(kStringEncodingMask)); + __ movq(result, FieldOperand(string, ExternalString::kResourceDataOffset)); + __ j(not_equal, &ascii_external, Label::kNear); + // Two-byte string. + __ movzxwl(result, Operand(result, index, times_2, 0)); + __ jmp(&done, Label::kNear); + __ bind(&ascii_external); + // Ascii string. + __ movzxbl(result, Operand(result, index, times_1, 0)); + __ jmp(&done, Label::kNear); + + // Dispatch on the encoding: ASCII or two-byte. + Label ascii; + __ bind(&seq_string); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ testb(result, Immediate(kStringEncodingMask)); + __ j(not_zero, &ascii, Label::kNear); + + // Two-byte string. + // Load the two-byte character code into the result register. + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + __ movzxwl(result, FieldOperand(string, + index, + times_2, + SeqTwoByteString::kHeaderSize)); + __ jmp(&done, Label::kNear); + + // ASCII string. + // Load the byte into the result register. + __ bind(&ascii); + __ movzxbl(result, FieldOperand(string, + index, + times_1, + SeqAsciiString::kHeaderSize)); + __ bind(&done); +} #undef __ diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index a0648cec64..2e80751033 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -69,6 +69,21 @@ class CodeGenerator: public AstVisitor { }; +class StringCharLoadGenerator : public AllStatic { + public: + // Generates the code for handling different string types and loading the + // indexed character into |result|. We expect |index| as untagged input and + // |result| as untagged output. + static void Generate(MacroAssembler* masm, + Register string, + Register index, + Register result, + Label* call_runtime); + + private: + DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); +}; + } } // namespace v8::internal #endif // V8_X64_CODEGEN_X64_H_ diff --git a/deps/v8/src/x64/cpu-x64.cc b/deps/v8/src/x64/cpu-x64.cc index ae5045f0df..69e77eee9d 100644 --- a/deps/v8/src/x64/cpu-x64.cc +++ b/deps/v8/src/x64/cpu-x64.cc @@ -41,7 +41,7 @@ namespace v8 { namespace internal { -void CPU::Setup() { +void CPU::SetUp() { CpuFeatures::Probe(); } diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc index 423e6f2441..eec83d9d1e 100644 --- a/deps/v8/src/x64/debug-x64.cc +++ b/deps/v8/src/x64/debug-x64.cc @@ -100,64 +100,65 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, RegList non_object_regs, bool convert_call_to_jmp) { // Enter an internal frame. - __ EnterInternalFrame(); - - // Store the registers containing live values on the expression stack to - // make sure that these are correctly updated during GC. Non object values - // are stored as as two smis causing it to be untouched by GC. - ASSERT((object_regs & ~kJSCallerSaved) == 0); - ASSERT((non_object_regs & ~kJSCallerSaved) == 0); - ASSERT((object_regs & non_object_regs) == 0); - for (int i = 0; i < kNumJSCallerSaved; i++) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - ASSERT(!reg.is(kScratchRegister)); - if ((object_regs & (1 << r)) != 0) { - __ push(reg); + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Store the registers containing live values on the expression stack to + // make sure that these are correctly updated during GC. Non object values + // are stored as as two smis causing it to be untouched by GC. + ASSERT((object_regs & ~kJSCallerSaved) == 0); + ASSERT((non_object_regs & ~kJSCallerSaved) == 0); + ASSERT((object_regs & non_object_regs) == 0); + for (int i = 0; i < kNumJSCallerSaved; i++) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + ASSERT(!reg.is(kScratchRegister)); + if ((object_regs & (1 << r)) != 0) { + __ push(reg); + } + // Store the 64-bit value as two smis. + if ((non_object_regs & (1 << r)) != 0) { + __ movq(kScratchRegister, reg); + __ Integer32ToSmi(reg, reg); + __ push(reg); + __ sar(kScratchRegister, Immediate(32)); + __ Integer32ToSmi(kScratchRegister, kScratchRegister); + __ push(kScratchRegister); + } } - // Store the 64-bit value as two smis. - if ((non_object_regs & (1 << r)) != 0) { - __ movq(kScratchRegister, reg); - __ Integer32ToSmi(reg, reg); - __ push(reg); - __ sar(kScratchRegister, Immediate(32)); - __ Integer32ToSmi(kScratchRegister, kScratchRegister); - __ push(kScratchRegister); - } - } #ifdef DEBUG - __ RecordComment("// Calling from debug break to runtime - come in - over"); + __ RecordComment("// Calling from debug break to runtime - come in - over"); #endif - __ Set(rax, 0); // No arguments (argc == 0). - __ movq(rbx, ExternalReference::debug_break(masm->isolate())); - - CEntryStub ceb(1); - __ CallStub(&ceb); - - // Restore the register values from the expression stack. - for (int i = kNumJSCallerSaved - 1; i >= 0; i--) { - int r = JSCallerSavedCode(i); - Register reg = { r }; - if (FLAG_debug_code) { - __ Set(reg, kDebugZapValue); - } - if ((object_regs & (1 << r)) != 0) { - __ pop(reg); + __ Set(rax, 0); // No arguments (argc == 0). + __ movq(rbx, ExternalReference::debug_break(masm->isolate())); + + CEntryStub ceb(1); + __ CallStub(&ceb); + + // Restore the register values from the expression stack. + for (int i = kNumJSCallerSaved - 1; i >= 0; i--) { + int r = JSCallerSavedCode(i); + Register reg = { r }; + if (FLAG_debug_code) { + __ Set(reg, kDebugZapValue); + } + if ((object_regs & (1 << r)) != 0) { + __ pop(reg); + } + // Reconstruct the 64-bit value from two smis. + if ((non_object_regs & (1 << r)) != 0) { + __ pop(kScratchRegister); + __ SmiToInteger32(kScratchRegister, kScratchRegister); + __ shl(kScratchRegister, Immediate(32)); + __ pop(reg); + __ SmiToInteger32(reg, reg); + __ or_(reg, kScratchRegister); + } } - // Reconstruct the 64-bit value from two smis. - if ((non_object_regs & (1 << r)) != 0) { - __ pop(kScratchRegister); - __ SmiToInteger32(kScratchRegister, kScratchRegister); - __ shl(kScratchRegister, Immediate(32)); - __ pop(reg); - __ SmiToInteger32(reg, reg); - __ or_(reg, kScratchRegister); - } - } - // Get rid of the internal frame. - __ LeaveInternalFrame(); + // Get rid of the internal frame. + } // If this call did not replace a call but patched other code then there will // be an unwanted return address left on the stack. Here we get rid of that. @@ -228,33 +229,56 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { } -void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { +void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // Register state just before return from JS function (from codegen-x64.cc). - // rax is the actual number of arguments not encoded as a smi, see comment - // above IC call. // ----------- S t a t e ------------- - // -- rax: number of arguments + // -- rax: return value // ----------------------------------- - // The number of arguments in rax is not smi encoded. - Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false); + Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true); } -void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { - // Register state just before return from JS function (from codegen-x64.cc). +void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-x64.cc). // ----------- S t a t e ------------- - // -- rax: return value + // -- rdi : function // ----------------------------------- - Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true); + Generate_DebugBreakCallHelper(masm, rdi.bit(), 0, false); +} + + +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-x64.cc). + // ----------- S t a t e ------------- + // -- rdi : function + // -- rbx: cache cell for call target + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, rbx.bit() | rdi.bit(), 0, false); } -void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { - // Register state for stub CallFunction (from CallFunctionStub in ic-x64.cc). +void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { + // Register state for CallConstructStub (from code-stubs-x64.cc). + // rax is the actual number of arguments not encoded as a smi, see comment + // above IC call. // ----------- S t a t e ------------- - // No registers used on entry. + // -- rax: number of arguments // ----------------------------------- - Generate_DebugBreakCallHelper(masm, 0, 0, false); + // The number of arguments in rax is not smi encoded. + Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false); +} + + +void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallConstructStub (from code-stubs-x64.cc). + // rax is the actual number of arguments not encoded as a smi, see comment + // above IC call. + // ----------- S t a t e ------------- + // -- rax: number of arguments + // -- rbx: cache cell for call target + // ----------------------------------- + // The number of arguments in rax is not smi encoded. + Generate_DebugBreakCallHelper(masm, rbx.bit() | rdi.bit(), rax.bit(), false); } @@ -263,9 +287,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) { Label check_codesize; __ bind(&check_codesize); __ RecordDebugBreakSlot(); - for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) { - __ nop(); - } + __ Nop(Assembler::kDebugBreakSlotLength); ASSERT_EQ(Assembler::kDebugBreakSlotLength, masm->SizeOfCodeGeneratedSince(&check_codesize)); } diff --git a/deps/v8/src/x64/deoptimizer-x64.cc b/deps/v8/src/x64/deoptimizer-x64.cc index f322312b41..efa988874e 100644 --- a/deps/v8/src/x64/deoptimizer-x64.cc +++ b/deps/v8/src/x64/deoptimizer-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -87,13 +87,19 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { #endif } + Isolate* isolate = code->GetIsolate(); // Add the deoptimizing code to the list. DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); - DeoptimizerData* data = code->GetIsolate()->deoptimizer_data(); + DeoptimizerData* data = isolate->deoptimizer_data(); node->set_next(data->deoptimizing_code_list_); data->deoptimizing_code_list_ = node; + // We might be in the middle of incremental marking with compaction. + // Tell collector to treat this code object in a special way and + // ignore all slots that might have been recorded on it. + isolate->heap()->mark_compact_collector()->InvalidateCode(code); + // Set the code for the function to non-optimized version. function->ReplaceCode(function->shared()->code()); @@ -105,7 +111,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { } -void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, +void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { Address call_target_address = pc_after - kIntSize; @@ -131,14 +138,18 @@ void Deoptimizer::PatchStackCheckCodeAt(Address pc_after, ASSERT(*(call_target_address - 3) == 0x73 && // jae *(call_target_address - 2) == 0x07 && // offset *(call_target_address - 1) == 0xe8); // call - *(call_target_address - 3) = 0x90; // nop - *(call_target_address - 2) = 0x90; // nop + *(call_target_address - 3) = 0x66; // 2 byte nop part 1 + *(call_target_address - 2) = 0x90; // 2 byte nop part 2 Assembler::set_target_address_at(call_target_address, replacement_code->entry()); + + unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, call_target_address, replacement_code); } -void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, +void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code, + Address pc_after, Code* check_code, Code* replacement_code) { Address call_target_address = pc_after - kIntSize; @@ -146,13 +157,16 @@ void Deoptimizer::RevertStackCheckCodeAt(Address pc_after, Assembler::target_address_at(call_target_address)); // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to // restore the conditional branch. - ASSERT(*(call_target_address - 3) == 0x90 && // nop - *(call_target_address - 2) == 0x90 && // nop + ASSERT(*(call_target_address - 3) == 0x66 && // 2 byte nop part 1 + *(call_target_address - 2) == 0x90 && // 2 byte nop part 2 *(call_target_address - 1) == 0xe8); // call *(call_target_address - 3) = 0x73; // jae *(call_target_address - 2) = 0x07; // offset Assembler::set_target_address_at(call_target_address, check_code->entry()); + + check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, call_target_address, check_code); } @@ -192,12 +206,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() { ASSERT(Translation::BEGIN == opcode); USE(opcode); int count = iterator.Next(); + iterator.Skip(1); // Drop JS frame count. ASSERT(count == 1); USE(count); opcode = static_cast<Translation::Opcode>(iterator.Next()); USE(opcode); - ASSERT(Translation::FRAME == opcode); + ASSERT(Translation::JS_FRAME == opcode); unsigned node_id = iterator.Next(); USE(node_id); ASSERT(node_id == ast_id); @@ -233,9 +248,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_ = new FrameDescription*[1]; output_[0] = new(output_frame_size) FrameDescription( output_frame_size, function_); -#ifdef DEBUG - output_[0]->SetKind(Code::OPTIMIZED_FUNCTION); -#endif + output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT); // Clear the incoming parameters in the optimized frame to avoid // confusing the garbage collector. @@ -300,7 +313,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_[0] = input_; output_[0]->SetPc(reinterpret_cast<intptr_t>(from_)); } else { - // Setup the frame pointer and the context pointer. + // Set up the frame pointer and the context pointer. output_[0]->SetRegister(rbp.code(), input_->GetRegister(rbp.code())); output_[0]->SetRegister(rsi.code(), input_->GetRegister(rsi.code())); @@ -324,13 +337,117 @@ void Deoptimizer::DoComputeOsrOutputFrame() { } -void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, - int frame_index) { - // Read the ast node id, function, and frame height for this output frame. - Translation::Opcode opcode = - static_cast<Translation::Opcode>(iterator->Next()); - USE(opcode); - ASSERT(Translation::FRAME == opcode); +void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; + unsigned input_frame_size = input_->GetFrameSize(); + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, function); + output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR); + + // Arguments adaptor can not be topmost or bottommost. + ASSERT(frame_index > 0 && frame_index < output_count_ - 1); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address of the frame is computed from the previous + // frame's top and this frame's size. + intptr_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + unsigned input_offset = input_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + input_offset -= (parameter_count * kPointerSize); + + // Read caller's PC from the previous frame. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t callers_pc = output_[frame_index - 1]->GetPc(); + output_frame->SetFrameSlot(output_offset, callers_pc); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; caller's pc\n", + top_address + output_offset, output_offset, callers_pc); + } + + // Read caller's FP from the previous frame, and set this frame's FP. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t value = output_[frame_index - 1]->GetFp(); + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + output_frame->SetFp(fp_value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; caller's fp\n", + fp_value, output_offset, value); + } + + // A marker value is used in place of the context. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + intptr_t context = reinterpret_cast<intptr_t>( + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); + output_frame->SetFrameSlot(output_offset, context); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; context (adaptor sentinel)\n", + top_address + output_offset, output_offset, context); + } + + // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(function); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; function\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + input_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* adaptor_trampoline = + builtins->builtin(Builtins::kArgumentsAdaptorTrampoline); + intptr_t pc_value = reinterpret_cast<intptr_t>( + adaptor_trampoline->instruction_start() + + isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value()); + output_frame->SetPc(pc_value); +} + + +void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, + int frame_index) { int node_id = iterator->Next(); JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); unsigned height = iterator->Next(); @@ -350,9 +467,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator, // Allocate and store the output frame description. FrameDescription* output_frame = new(output_frame_size) FrameDescription(output_frame_size, function); -#ifdef DEBUG - output_frame->SetKind(Code::FUNCTION); -#endif + output_frame->SetFrameType(StackFrame::JAVA_SCRIPT); bool is_bottommost = (0 == frame_index); bool is_topmost = (output_count_ - 1 == frame_index); @@ -598,7 +713,10 @@ void Deoptimizer::EntryGenerator::Generate() { Isolate* isolate = masm()->isolate(); - __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6); + } // Preserve deoptimizer object in register rax and get the input // frame descriptor pointer. __ movq(rbx, Operand(rax, Deoptimizer::input_offset())); @@ -644,8 +762,11 @@ void Deoptimizer::EntryGenerator::Generate() { __ PrepareCallCFunction(2); __ movq(arg1, rax); __ LoadAddress(arg2, ExternalReference::isolate_address()); - __ CallCFunction( - ExternalReference::compute_output_frames_function(isolate), 2); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction( + ExternalReference::compute_output_frames_function(isolate), 2); + } __ pop(rax); // Replace the current frame with the output frames. diff --git a/deps/v8/src/x64/disasm-x64.cc b/deps/v8/src/x64/disasm-x64.cc index 1b8871fd47..5cbdad7ac3 100644 --- a/deps/v8/src/x64/disasm-x64.cc +++ b/deps/v8/src/x64/disasm-x64.cc @@ -109,6 +109,7 @@ static const ByteMnemonic zero_operands_instr[] = { { 0xC3, UNSET_OP_ORDER, "ret" }, { 0xC9, UNSET_OP_ORDER, "leave" }, { 0xF4, UNSET_OP_ORDER, "hlt" }, + { 0xFC, UNSET_OP_ORDER, "cld" }, { 0xCC, UNSET_OP_ORDER, "int3" }, { 0x60, UNSET_OP_ORDER, "pushad" }, { 0x61, UNSET_OP_ORDER, "popad" }, @@ -910,15 +911,19 @@ int DisassemblerX64::RegisterFPUInstruction(int escape_opcode, switch (modrm_byte) { case 0xE0: mnem = "fchs"; break; case 0xE1: mnem = "fabs"; break; + case 0xE3: mnem = "fninit"; break; case 0xE4: mnem = "ftst"; break; case 0xE8: mnem = "fld1"; break; case 0xEB: mnem = "fldpi"; break; case 0xED: mnem = "fldln2"; break; case 0xEE: mnem = "fldz"; break; + case 0xF0: mnem = "f2xm1"; break; case 0xF1: mnem = "fyl2x"; break; + case 0xF2: mnem = "fptan"; break; case 0xF5: mnem = "fprem1"; break; case 0xF7: mnem = "fincstp"; break; case 0xF8: mnem = "fprem"; break; + case 0xFD: mnem = "fscale"; break; case 0xFE: mnem = "fsin"; break; case 0xFF: mnem = "fcos"; break; default: UnimplementedInstruction(); @@ -1034,7 +1039,18 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { } } else { get_modrm(*current, &mod, ®op, &rm); - if (opcode == 0x28) { + if (opcode == 0x1f) { + current++; + if (rm == 4) { // SIB byte present. + current++; + } + if (mod == 1) { // Byte displacement. + current += 1; + } else if (mod == 2) { // 32-bit displacement. + current += 4; + } // else no immediate displacement. + AppendToBuffer("nop"); + } else if (opcode == 0x28) { AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop)); current += PrintRightXMMOperand(current); } else if (opcode == 0x29) { @@ -1178,7 +1194,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { int mod, regop, rm; get_modrm(*current, &mod, ®op, &rm); current++; - if (regop == 4) { // SIB byte present. + if (rm == 4) { // SIB byte present. current++; } if (mod == 1) { // Byte displacement. diff --git a/deps/v8/src/x64/frames-x64.h b/deps/v8/src/x64/frames-x64.h index 7012c76f0b..3e3d63d62b 100644 --- a/deps/v8/src/x64/frames-x64.h +++ b/deps/v8/src/x64/frames-x64.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -31,32 +31,32 @@ namespace v8 { namespace internal { -static const int kNumRegs = 16; -static const RegList kJSCallerSaved = +const int kNumRegs = 16; +const RegList kJSCallerSaved = 1 << 0 | // rax 1 << 1 | // rcx 1 << 2 | // rdx 1 << 3 | // rbx - used as a caller-saved register in JavaScript code 1 << 7; // rdi - callee function -static const int kNumJSCallerSaved = 5; +const int kNumJSCallerSaved = 5; typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; // Number of registers for which space is reserved in safepoints. -static const int kNumSafepointRegisters = 16; +const int kNumSafepointRegisters = 16; // ---------------------------------------------------- class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = 0 * kPointerSize; - static const int kContextOffset = 1 * kPointerSize; - static const int kFPOffset = 2 * kPointerSize; - static const int kStateOffset = 3 * kPointerSize; - static const int kPCOffset = 4 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kCodeOffset = 1 * kPointerSize; + static const int kStateOffset = 2 * kPointerSize; + static const int kContextOffset = 3 * kPointerSize; + static const int kFPOffset = 4 * kPointerSize; - static const int kSize = kPCOffset + kPointerSize; + static const int kSize = kFPOffset + kPointerSize; }; @@ -87,6 +87,9 @@ class ExitFrameConstants : public AllStatic { class StandardFrameConstants : public AllStatic { public: + // Fixed part of the frame consists of return address, caller fp, + // context and function. + static const int kFixedFrameSize = 4 * kPointerSize; static const int kExpressionsOffset = -3 * kPointerSize; static const int kMarkerOffset = -2 * kPointerSize; static const int kContextOffset = -1 * kPointerSize; @@ -112,6 +115,8 @@ class JavaScriptFrameConstants : public AllStatic { class ArgumentsAdaptorFrameConstants : public AllStatic { public: static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset; + static const int kFrameSize = + StandardFrameConstants::kFixedFrameSize + kPointerSize; }; diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index 556523fada..292c7b4698 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -44,11 +44,6 @@ namespace internal { #define __ ACCESS_MASM(masm_) -static unsigned GetPropertyId(Property* property) { - return property->id(); -} - - class JumpPatchSite BASE_EMBEDDED { public: explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { @@ -111,7 +106,7 @@ class JumpPatchSite BASE_EMBEDDED { // formal parameter count expected by the function. // // The live registers are: -// o rdi: the JS function object being called (ie, ourselves) +// o rdi: the JS function object being called (i.e. ourselves) // o rsi: our context // o rbp: our caller's frame pointer // o rsp: stack pointer (pointing to return address) @@ -122,6 +117,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ASSERT(info_ == NULL); info_ = info; scope_ = info->scope(); + handler_table_ = + isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); @@ -132,11 +129,32 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } #endif + // We can optionally optimize based on counters rather than statistical + // sampling. + if (info->ShouldSelfOptimize()) { + if (FLAG_trace_opt) { + PrintF("[adding self-optimization header to %s]\n", + *info->function()->debug_name()->ToCString()); + } + MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell( + Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt)); + JSGlobalPropertyCell* cell; + if (maybe_cell->To(&cell)) { + __ movq(rax, Handle<JSGlobalPropertyCell>(cell), + RelocInfo::EMBEDDED_OBJECT); + __ SmiAddConstant(FieldOperand(rax, JSGlobalPropertyCell::kValueOffset), + Smi::FromInt(-1)); + Handle<Code> compile_stub( + isolate()->builtins()->builtin(Builtins::kLazyRecompile)); + __ j(zero, compile_stub, RelocInfo::CODE_TARGET); + } + } + // Strict mode functions and builtins need to replace the receiver // with undefined when called as functions (without an explicit // receiver object). rcx is zero for method calls and non-zero for // function calls. - if (info->is_strict_mode() || info->is_native()) { + if (!info->is_classic_mode() || info->is_native()) { Label ok; __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); @@ -147,6 +165,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { __ bind(&ok); } + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + __ push(rbp); // Caller's frame pointer. __ movq(rbp, rsp); __ push(rsi); // Callee's context. @@ -195,11 +218,9 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Store it in the context. int context_offset = Context::SlotOffset(var->index()); __ movq(Operand(rsi, context_offset), rax); - // Update the write barrier. This clobbers all involved - // registers, so we have use a third register to avoid - // clobbering rsi. - __ movq(rcx, rsi); - __ RecordWrite(rcx, context_offset, rax, rbx); + // Update the write barrier. This clobbers rax and rbx. + __ RecordWriteContextSlot( + rsi, context_offset, rax, rbx, kDontSaveFPRegs); } } } @@ -226,9 +247,15 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // function, receiver address, parameter count. // The stub will rewrite receiver and parameter count if the previous // stack frame was an arguments adapter frame. - ArgumentsAccessStub stub( - is_strict_mode() ? ArgumentsAccessStub::NEW_STRICT - : ArgumentsAccessStub::NEW_NON_STRICT_SLOW); + ArgumentsAccessStub::Type type; + if (!is_classic_mode()) { + type = ArgumentsAccessStub::NEW_STRICT; + } else if (function()->has_duplicate_parameters()) { + type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; + } else { + type = ArgumentsAccessStub::NEW_NON_STRICT_FAST; + } + ArgumentsAccessStub stub(type); __ CallStub(&stub); SetVar(arguments, rax, rbx, rdx); @@ -250,8 +277,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - int ignored = 0; - EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); + VariableProxy* proxy = scope()->function(); + ASSERT(proxy->var()->mode() == CONST || + proxy->var()->mode() == CONST_HARMONY); + ASSERT(proxy->var()->location() != Variable::UNALLOCATED); + EmitDeclaration(proxy, proxy->var()->mode(), NULL); } VisitDeclarations(scope()->declarations()); } @@ -377,7 +407,7 @@ void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { void FullCodeGenerator::TestContext::Plug(Variable* var) const { codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -399,7 +429,7 @@ void FullCodeGenerator::StackValueContext::Plug( void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -432,7 +462,7 @@ void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -491,7 +521,7 @@ void FullCodeGenerator::TestContext::DropAndPlug(int count, // For simplicity we always test the accumulator register. __ Drop(count); __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -555,7 +585,7 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const { void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -638,15 +668,16 @@ void FullCodeGenerator::SetVar(Variable* var, ASSERT(!scratch1.is(src)); MemOperand location = VarOperand(var, scratch0); __ movq(location, src); + // Emit the write barrier code if the location is in the heap. if (var->IsContextSlot()) { int offset = Context::SlotOffset(var->index()); - __ RecordWrite(scratch0, offset, src, scratch1); + __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); } } -void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, +void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false) { @@ -657,13 +688,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, Label skip; if (should_normalize) __ jmp(&skip, Label::kNear); - - ForwardBailoutStack* current = forward_bailout_stack_; - while (current != NULL) { - PrepareForBailout(current->expr(), state); - current = current->parent(); - } - + PrepareForBailout(expr, TOS_REG); if (should_normalize) { __ CompareRoot(rax, Heap::kTrueValueRootIndex); Split(equal, if_true, if_false, NULL); @@ -673,16 +698,17 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, - FunctionLiteral* function, - int* global_count) { + VariableMode mode, + FunctionLiteral* function) { // If it was not possible to allocate the variable at compile time, we // need to "declare" it at runtime to make sure it actually exists in the // local context. Variable* variable = proxy->var(); + bool binding_needs_init = (function == NULL) && + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (variable->location()) { case Variable::UNALLOCATED: - ++(*global_count); + ++global_count_; break; case Variable::PARAMETER: @@ -691,7 +717,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ movq(StackOperand(variable), result_register()); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); __ movq(StackOperand(variable), kScratchRegister); @@ -715,10 +741,16 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, VisitForAccumulatorValue(function); __ movq(ContextOperand(rsi, variable->index()), result_register()); int offset = Context::SlotOffset(variable->index()); - __ movq(rbx, rsi); - __ RecordWrite(rbx, offset, result_register(), rcx); + // We know that we have written a function, which is not a smi. + __ RecordWriteContextSlot(rsi, + offset, + result_register(), + rcx, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); __ movq(ContextOperand(rsi, variable->index()), kScratchRegister); @@ -731,11 +763,13 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); __ push(rsi); __ Push(variable->name()); - // Declaration nodes are always introduced in one of three modes. - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; + // Declaration nodes are always introduced in one of four modes. + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); + PropertyAttributes attr = + (mode == CONST || mode == CONST_HARMONY) ? READ_ONLY : NONE; __ Push(Smi::FromInt(attr)); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -743,7 +777,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, // must not destroy the current value. if (function != NULL) { VisitForStackValue(function); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { __ PushRoot(Heap::kTheHoleValueRootIndex); } else { __ Push(Smi::FromInt(0)); // Indicates no initial value. @@ -755,9 +789,6 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } - - void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. __ push(rsi); // The context is the first argument. @@ -882,11 +913,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&done_convert); __ push(rax); + // Check for proxies. + Label call_runtime; + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CmpObjectType(rax, LAST_JS_PROXY_TYPE, rcx); + __ j(below_equal, &call_runtime); + // Check cache validity in generated code. This is a fast case for // the JSObject::IsSimpleEnum cache validity checks. If we cannot // guarantee cache validity, call the runtime system to check cache // validity or get the property names in a fixed array. - Label next, call_runtime; + Label next; Register empty_fixed_array_value = r8; __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); Register empty_descriptor_array_value = r9; @@ -953,7 +990,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - // Setup the four remaining stack slots. + // Set up the four remaining stack slots. __ push(rax); // Map. __ push(rdx); // Enumeration cache. __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); @@ -962,9 +999,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ jmp(&loop); // We got a fixed array in register rax. Iterate through that. + Label non_proxy; __ bind(&fixed_array); - __ Push(Smi::FromInt(0)); // Map (0) - force slow check. - __ push(rax); + __ Move(rbx, Smi::FromInt(1)); // Smi indicates slow check + __ movq(rcx, Operand(rsp, 0 * kPointerSize)); // Get enumerated object + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ CmpObjectType(rcx, LAST_JS_PROXY_TYPE, rcx); + __ j(above, &non_proxy); + __ Move(rbx, Smi::FromInt(0)); // Zero indicates proxy + __ bind(&non_proxy); + __ push(rbx); // Smi + __ push(rax); // Array __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset)); __ push(rax); // Fixed array length (as smi). __ Push(Smi::FromInt(0)); // Initial index. @@ -983,17 +1028,22 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { index.scale, FixedArray::kHeaderSize)); - // Get the expected map from the stack or a zero map in the + // Get the expected map from the stack or a smi in the // permanent slow case into register rdx. __ movq(rdx, Operand(rsp, 3 * kPointerSize)); // Check if the expected map still matches that of the enumerable. - // If not, we have to filter the key. + // If not, we may have to filter the key. Label update_each; __ movq(rcx, Operand(rsp, 4 * kPointerSize)); __ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); __ j(equal, &update_each, Label::kNear); + // For proxies, no filtering is done. + // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. + __ Cmp(rdx, Smi::FromInt(0)); + __ j(equal, &update_each, Label::kNear); + // Convert the entry to a string or null if it isn't a property // anymore. If the property has been removed while iterating, we // just skip it. @@ -1047,7 +1097,7 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, !pretenure && scope()->is_function_scope() && info->num_literals() == 0) { - FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(info->language_mode()); __ Push(info); __ CallStub(&stub); } else { @@ -1077,7 +1127,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); @@ -1091,7 +1141,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, // If no outer scope calls eval, we do not need to check more // context extensions. If we have reached an eval scope, we check // all extensions from this point. - if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; + if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; s = s->outer_scope(); } @@ -1137,7 +1187,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); @@ -1168,16 +1218,23 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (var->mode() == Variable::DYNAMIC_GLOBAL) { + if (var->mode() == DYNAMIC_GLOBAL) { EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + } else if (var->mode() == DYNAMIC_LOCAL) { Variable* local = var->local_if_not_shadowed(); __ movq(rax, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == Variable::CONST) { + if (local->mode() == CONST || + local->mode() == CONST_HARMONY || + local->mode() == LET) { __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); __ j(not_equal, done); - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + if (local->mode() == CONST) { + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + } else { // LET || CONST_HARMONY + __ Push(var->name()); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } } __ jmp(done); } @@ -1208,23 +1265,63 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { case Variable::LOCAL: case Variable::CONTEXT: { Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot"); - if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { - context()->Plug(var); - } else { - // Let and const need a read barrier. - Label done; - GetVar(rax, var); - __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &done, Label::kNear); - if (var->mode() == Variable::LET) { - __ Push(var->name()); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - } else { // Variable::CONST - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + if (var->binding_needs_init()) { + // var->scope() may be NULL when the proxy is located in eval code and + // refers to a potential outside binding. Currently those bindings are + // always looked up dynamically, i.e. in that case + // var->location() == LOOKUP. + // always holds. + ASSERT(var->scope() != NULL); + + // Check if the binding really needs an initialization check. The check + // can be skipped in the following situation: we have a LET or CONST + // binding in harmony mode, both the Variable and the VariableProxy have + // the same declaration scope (i.e. they are both in global code, in the + // same function or in the same eval code) and the VariableProxy is in + // the source physically located after the initializer of the variable. + // + // We cannot skip any initialization checks for CONST in non-harmony + // mode because const variables may be declared but never initialized: + // if (false) { const x; }; var y = x; + // + // The condition on the declaration scopes is a conservative check for + // nested functions that access a binding and are called before the + // binding is initialized: + // function() { f(); let x = 1; function f() { x = 2; } } + // + bool skip_init_check; + if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { + skip_init_check = false; + } else { + // Check that we always have valid source position. + ASSERT(var->initializer_position() != RelocInfo::kNoPosition); + ASSERT(proxy->position() != RelocInfo::kNoPosition); + skip_init_check = var->mode() != CONST && + var->initializer_position() < proxy->position(); + } + + if (!skip_init_check) { + // Let and const need a read barrier. + Label done; + GetVar(rax, var); + __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); + __ j(not_equal, &done, Label::kNear); + if (var->mode() == LET || var->mode() == CONST_HARMONY) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + __ Push(var->name()); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } else { + // Uninitalized const bindings outside of harmony mode are unholed. + ASSERT(var->mode() == CONST); + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + } + __ bind(&done); + context()->Plug(rax); + break; } - __ bind(&done); - context()->Plug(rax); } + context()->Plug(var); break; } @@ -1302,10 +1399,11 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Comment cmnt(masm_, "[ ObjectLiteral"); + Handle<FixedArray> constant_properties = expr->constant_properties(); __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(expr->literal_index())); - __ Push(expr->constant_properties()); + __ Push(constant_properties); int flags = expr->fast_elements() ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags; @@ -1313,10 +1411,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ? ObjectLiteral::kHasFunction : ObjectLiteral::kNoFlags; __ Push(Smi::FromInt(flags)); + int properties_count = constant_properties->length() / 2; if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); + } else { + FastCloneShallowObjectStub stub(properties_count); + __ CallStub(&stub); } // If result_saved is true the result is on top of the stack. If @@ -1350,9 +1453,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ Move(rcx, key->handle()); __ movq(rdx, Operand(rsp, 0)); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, key->id()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1404,24 +1507,42 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); int length = subexprs->length(); + Handle<FixedArray> constant_elements = expr->constant_elements(); + ASSERT_EQ(2, constant_elements->length()); + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); + bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(constant_elements->get(1))); __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(expr->literal_index())); - __ Push(expr->constant_elements()); - if (expr->constant_elements()->map() == - isolate()->heap()->fixed_cow_array_map()) { + __ Push(constant_elements); + Heap* heap = isolate()->heap(); + if (has_constant_fast_elements && + constant_elements_values->map() == heap->fixed_cow_array_map()) { + // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // change, so it's possible to specialize the stub in advance. + __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); + FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, + length); __ CallStub(&stub); - __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); } else if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::CLONE_ELEMENTS, length); + ASSERT(constant_elements_kind == FAST_ELEMENTS || + constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + FLAG_smi_only_arrays); + // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // change, so it's possible to specialize the stub in advance. + FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements + ? FastCloneShallowArrayStub::CLONE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); __ CallStub(&stub); } @@ -1444,14 +1565,28 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - // Store the subexpression value in the array's elements. - __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. - __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); - int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ movq(FieldOperand(rbx, offset), result_register()); - - // Update the write barrier for the array store. - __ RecordWrite(rbx, offset, result_register(), rcx); + if (constant_elements_kind == FAST_ELEMENTS) { + // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot + // transition and don't need to call the runtime stub. + int offset = FixedArray::kHeaderSize + (i * kPointerSize); + __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. + __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); + // Store the subexpression value in the array's elements. + __ movq(FieldOperand(rbx, offset), result_register()); + // Update the write barrier for the array store. + __ RecordWriteField(rbx, offset, result_register(), rcx, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + INLINE_SMI_CHECK); + } else { + // Store the subexpression value in the array's elements. + __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. + __ movq(rdi, FieldOperand(rbx, JSObject::kMapOffset)); + __ Move(rcx, Smi::FromInt(i)); + __ Move(rdx, Smi::FromInt(expr->literal_index())); + StoreArrayLiteralElementStub stub; + __ CallStub(&stub); + } PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } @@ -1582,14 +1717,14 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { Literal* key = prop->key()->AsLiteral(); __ Move(rcx, key->handle()); Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ call(ic, RelocInfo::CODE_TARGET, prop->id()); } void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1698,9 +1833,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ movq(rdx, rax); __ pop(rax); // Restore value. __ Move(rcx, prop->key()->AsLiteral()->handle()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic); break; } @@ -1711,9 +1846,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ movq(rcx, rax); __ pop(rdx); __ pop(rax); // Restore value. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic); break; } @@ -1729,9 +1864,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // Global var, const, or let. __ Move(rcx, var->name()); __ movq(rdx, GlobalObjectOperand()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { // Const initializers need a write barrier. @@ -1756,13 +1891,13 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { __ push(rax); // Value. __ push(rsi); // Context. __ Push(var->name()); - __ Push(Smi::FromInt(strict_mode_flag())); + __ Push(Smi::FromInt(language_mode())); __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { ASSERT(var->IsStackAllocated() || var->IsContextSlot()); @@ -1777,12 +1912,14 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ movq(location, rax); if (var->IsContextSlot()) { __ movq(rdx, rax); - __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); + __ RecordWriteContextSlot( + rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); } } - } else if (var->mode() != Variable::CONST) { - // Assignment to var or initializing assignment to let. + } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { + // Assignment to var or initializing assignment to let/const + // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, rcx); if (FLAG_debug_code && op == Token::INIT_LET) { @@ -1795,14 +1932,15 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ movq(location, rax); if (var->IsContextSlot()) { __ movq(rdx, rax); - __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); + __ RecordWriteContextSlot( + rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs); } } else { ASSERT(var->IsLookupSlot()); __ push(rax); // Value. __ push(rsi); // Context. __ Push(var->name()); - __ Push(Smi::FromInt(strict_mode_flag())); + __ Push(Smi::FromInt(language_mode())); __ CallRuntime(Runtime::kStoreContextSlot, 4); } } @@ -1834,9 +1972,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { } else { __ pop(rdx); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1874,9 +2012,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { } // Record source code position before IC call. SetSourcePosition(expr->position()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1981,6 +2119,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, flags); + __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -1990,8 +2129,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } -void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, - int arg_count) { +void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { __ push(Operand(rsp, arg_count * kPointerSize)); @@ -2002,17 +2140,14 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, // Push the receiver of the enclosing function and do runtime call. __ push(Operand(rbp, (2 + info_->scope()->num_parameters()) * kPointerSize)); - // Push the strict mode flag. In harmony mode every eval call - // is a strict mode eval call. - StrictModeFlag strict_mode = strict_mode_flag(); - if (FLAG_harmony_block_scoping) { - strict_mode = kStrictMode; - } - __ Push(Smi::FromInt(strict_mode)); + // Push the language mode. + __ Push(Smi::FromInt(language_mode())); - __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP - ? Runtime::kResolvePossiblyDirectEvalNoLookup - : Runtime::kResolvePossiblyDirectEval, 4); + // Push the start position of the scope the calls resides in. + __ Push(Smi::FromInt(scope()->start_position())); + + // Do the runtime call. + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); } @@ -2043,27 +2178,10 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly in - // generated code. If we succeed, there is no need to perform a - // context lookup in the runtime system. - Label done; - Variable* var = proxy->var(); - if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { - Label slow; - EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); - // Push the function and resolve eval. - __ push(rax); - EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); - __ jmp(&done); - __ bind(&slow); - } - // Push a copy of the function (found below the arguments) and resolve // eval. __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); - EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - __ bind(&done); + EmitResolvePossiblyDirectEval(arg_count); // The runtime call returns a pair of values in rax (function) and // rdx (receiver). Touch up the stack with the right values. @@ -2073,6 +2191,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); + __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2175,14 +2294,28 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ Set(rax, arg_count); __ movq(rdi, Operand(rsp, arg_count * kPointerSize)); - Handle<Code> construct_builtin = - isolate()->builtins()->JSConstructCall(); - __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); + // Record call targets in unoptimized code, but not in the snapshot. + CallFunctionFlags flags; + if (!Serializer::enabled()) { + flags = RECORD_CALL_TARGET; + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->id(), cell); + __ Move(rbx, cell); + } else { + flags = NO_CALL_FUNCTION_FLAGS; + } + + CallConstructStub stub(flags); + __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); context()->Plug(rax); } -void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2194,7 +2327,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ JumpIfSmi(rax, if_true); __ jmp(if_false); @@ -2202,7 +2335,8 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2214,7 +2348,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Condition non_negative_smi = masm()->CheckNonNegativeSmi(rax); Split(non_negative_smi, if_true, if_false, fall_through); @@ -2222,7 +2356,8 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2246,14 +2381,15 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ cmpq(rbx, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); __ j(below, if_false); __ cmpq(rbx, Immediate(LAST_NONCALLABLE_SPEC_OBJECT_TYPE)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(below_equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2267,14 +2403,15 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rbx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(above_equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2290,7 +2427,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); __ testb(FieldOperand(rbx, Map::kBitFieldOffset), Immediate(1 << Map::kIsUndetectable)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(not_zero, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2298,7 +2435,8 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - ZoneList<Expression*>* args) { + CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2374,12 +2512,13 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf)); __ jmp(if_true); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2393,14 +2532,15 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2414,14 +2554,15 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2435,7 +2576,7 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2443,8 +2584,8 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { -void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label materialize_true, materialize_false; Label* if_true = NULL; @@ -2467,14 +2608,15 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { __ bind(&check_frame_marker); __ Cmp(Operand(rax, StandardFrameConstants::kMarkerOffset), Smi::FromInt(StackFrame::CONSTRUCT)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -2490,14 +2632,15 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { __ pop(rbx); __ cmpq(rax, rbx); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(equal, if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitArguments(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); // ArgumentsAccessStub expects the key in rdx and the formal @@ -2511,8 +2654,8 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label exit; // Get the number of formal parameters. @@ -2534,7 +2677,8 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); Label done, null, function, non_function_constructor; @@ -2545,20 +2689,24 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { // Check that the object is a JS object but take special care of JS // functions to make sure they have 'Function' as their class. + // Assume that there are only two callable types, and one of them is at + // either end of the type range for JS object types. Saves extra comparisons. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rax); // Map is now in rax. __ j(below, &null); - - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - __ CmpInstanceType(rax, FIRST_CALLABLE_SPEC_OBJECT_TYPE); - __ j(above_equal, &function); - - // Check if the constructor in the map is a function. + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + __ j(equal, &function); + + __ CmpInstanceType(rax, LAST_SPEC_OBJECT_TYPE); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + __ j(equal, &function); + // Assume that there is no larger type. + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); + + // Check if the constructor in the map is a JS function. __ movq(rax, FieldOperand(rax, Map::kConstructorOffset)); __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); __ j(not_equal, &non_function_constructor); @@ -2590,7 +2738,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitLog(CallRuntime* expr) { // Conditionally generate a log call. // Args: // 0 (literal string): The type of logging (corresponds to the flags). @@ -2598,6 +2746,7 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { // 1 (string): Format string. Access the string at argument index 2 // with '%2s' (see Logger::LogRuntime for all the formats). // 2 (array): Arguments to the format string. + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 3); if (CodeGenerator::ShouldGenerateLog(args->at(0))) { VisitForStackValue(args->at(1)); @@ -2610,8 +2759,8 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label slow_allocate_heapnumber; Label heapnumber_allocated; @@ -2630,9 +2779,12 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. __ PrepareCallCFunction(1); #ifdef _WIN64 - __ LoadAddress(rcx, ExternalReference::isolate_address()); + __ movq(rcx, ContextOperand(context_register(), Context::GLOBAL_INDEX)); + __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); + #else - __ LoadAddress(rdi, ExternalReference::isolate_address()); + __ movq(rdi, ContextOperand(context_register(), Context::GLOBAL_INDEX)); + __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); #endif __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); @@ -2652,9 +2804,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2664,9 +2817,10 @@ void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpExecStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 4); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2677,7 +2831,8 @@ void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); // Load the object. @@ -2695,18 +2850,20 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { // Load the arguments on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); - MathPowStub stub; + MathPowStub stub(MathPowStub::ON_STACK); __ CallStub(&stub); context()->Plug(rax); } -void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); // Load the object. @@ -2726,14 +2883,15 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { // Update the write barrier. Save the value as it will be // overwritten by the write barrier code and is needed afterward. __ movq(rdx, rax); - __ RecordWrite(rbx, JSValue::kValueOffset, rdx, rcx); + __ RecordWriteField(rbx, JSValue::kValueOffset, rdx, rcx, kDontSaveFPRegs); __ bind(&done); context()->Plug(rax); } -void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 1); // Load the argument on the stack and call the stub. @@ -2745,7 +2903,8 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2763,7 +2922,8 @@ void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2771,7 +2931,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Register object = rbx; Register index = rax; - Register scratch = rcx; Register result = rdx; __ pop(object); @@ -2781,7 +2940,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Label done; StringCharCodeAtGenerator generator(object, index, - scratch, result, &need_conversion, &need_conversion, @@ -2810,7 +2968,8 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2818,8 +2977,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Register object = rbx; Register index = rax; - Register scratch1 = rcx; - Register scratch2 = rdx; + Register scratch = rdx; Register result = rax; __ pop(object); @@ -2829,8 +2987,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Label done; StringCharAtGenerator generator(object, index, - scratch1, - scratch2, + scratch, result, &need_conversion, &need_conversion, @@ -2859,7 +3016,8 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -2871,7 +3029,8 @@ void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -2883,10 +3042,11 @@ void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -2894,10 +3054,23 @@ void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); + ASSERT(args->length() == 1); + VisitForStackValue(args->at(0)); + __ CallStub(&stub); + context()->Plug(rax); +} + + +void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -2905,10 +3078,11 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::LOG, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallStub(&stub); @@ -2916,8 +3090,9 @@ void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { // Load the argument on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallRuntime(Runtime::kMath_sqrt, 1); @@ -2925,7 +3100,8 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() >= 2); int arg_count = args->length() - 2; // 2 ~ receiver and function. @@ -2934,18 +3110,31 @@ void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { } VisitForAccumulatorValue(args->last()); // Function. + // Check for proxy. + Label proxy, done; + __ CmpObjectType(rax, JS_FUNCTION_PROXY_TYPE, rbx); + __ j(equal, &proxy); + // InvokeFunction requires the function in rdi. Move it in there. __ movq(rdi, result_register()); ParameterCount count(arg_count); __ InvokeFunction(rdi, count, CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + __ jmp(&done); + + __ bind(&proxy); + __ push(rax); + __ CallRuntime(Runtime::kCall, args->length()); + __ bind(&done); + context()->Plug(rax); } -void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { RegExpConstructResultStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2955,7 +3144,8 @@ void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSwapElements(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3010,14 +3200,33 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ movq(Operand(index_2, 0), object); __ movq(Operand(index_1, 0), temp); - Label new_space; - __ InNewSpace(elements, temp, equal, &new_space); + Label no_remembered_set; + __ CheckPageFlag(elements, + temp, + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + not_zero, + &no_remembered_set, + Label::kNear); + // Possible optimization: do a check that both values are Smis + // (or them and test against Smi mask.) + + // We are swapping two objects in an array and the incremental marker never + // pauses in the middle of scanning a single object. Therefore the + // incremental marker is not disturbed, so we don't need to call the + // RecordWrite stub that notifies the incremental marker. + __ RememberedSetHelper(elements, + index_1, + temp, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + __ RememberedSetHelper(elements, + index_2, + temp, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + + __ bind(&no_remembered_set); - __ movq(object, elements); - __ RecordWriteHelper(object, index_1, temp); - __ RecordWriteHelper(elements, index_2, temp); - - __ bind(&new_space); // We are done. Drop elements from the stack, and return undefined. __ addq(rsp, Immediate(3 * kPointerSize)); __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); @@ -3031,7 +3240,8 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); ASSERT_NE(NULL, args->at(0)->AsLiteral()); @@ -3087,7 +3297,8 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); Register right = rax; @@ -3125,7 +3336,8 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3139,7 +3351,7 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { __ testl(FieldOperand(rax, String::kHashFieldOffset), Immediate(String::kContainsCachedArrayIndexMask)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ j(zero, if_true); __ jmp(if_false); @@ -3147,7 +3359,8 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3163,10 +3376,11 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { Label bailout, return_result, done, one_char_separator, long_separator, non_trivial_array, not_size_one_array, loop, loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // We will leave the separator on the stack until the end of the function. VisitForStackValue(args->at(1)); @@ -3352,7 +3566,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { // One-character separator case __ bind(&one_char_separator); - // Get the separator ascii character value. + // Get the separator ASCII character value. // Register "string" holds the separator. __ movzxbl(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize)); __ Set(index, 0); @@ -3496,14 +3710,16 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { if (property != NULL) { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); - __ Push(Smi::FromInt(strict_mode_flag())); + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ Push(Smi::FromInt(strict_mode_flag)); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(rax); } else if (proxy != NULL) { Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode // but "delete this" is allowed. - ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); + ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); if (var->IsUnallocated()) { __ push(GlobalObjectOperand()); __ Push(var->name()); @@ -3545,17 +3761,41 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { // Unary NOT has no side effects so it's only necessary to visit the // subexpression. Match the optimizing compiler by not branching. VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); } else { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - // Notice that the labels are swapped. - context()->PrepareTest(&materialize_true, &materialize_false, - &if_false, &if_true, &fall_through); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitForControl(expr->expression(), if_true, if_false, fall_through); - context()->Plug(if_false, if_true); // Labels swapped. + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + __ bind(&materialize_true); + PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); + if (context()->IsAccumulatorValue()) { + __ LoadRoot(rax, Heap::kTrueValueRootIndex); + } else { + __ PushRoot(Heap::kTrueValueRootIndex); + } + __ jmp(&done, Label::kNear); + __ bind(&materialize_false); + PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); + if (context()->IsAccumulatorValue()) { + __ LoadRoot(rax, Heap::kFalseValueRootIndex); + } else { + __ PushRoot(Heap::kFalseValueRootIndex); + } + __ bind(&done); } break; } @@ -3760,9 +4000,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ Move(rcx, prop->key()->AsLiteral()->handle()); __ pop(rdx); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3777,9 +4017,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(rcx); __ pop(rdx); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3827,20 +4067,25 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { context()->Plug(rax); } else { // This expression cannot throw a reference error at the top level. - VisitInCurrentContext(expr); + VisitInDuplicateContext(expr); } } void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through) { + Expression* sub_expr, + Handle<String> check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + { AccumulatorValueContext context(this); - VisitForTypeofValue(expr); + VisitForTypeofValue(sub_expr); } - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); if (check->Equals(isolate()->heap()->number_symbol())) { __ JumpIfSmi(rax, if_true); @@ -3875,9 +4120,11 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(not_zero, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->function_symbol())) { __ JumpIfSmi(rax, if_false); - STATIC_ASSERT(LAST_CALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CmpObjectType(rax, FIRST_CALLABLE_SPEC_OBJECT_TYPE, rdx); - Split(above_equal, if_true, if_false, fall_through); + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ CmpObjectType(rax, JS_FUNCTION_TYPE, rdx); + __ j(equal, if_true); + __ CmpInstanceType(rdx, JS_FUNCTION_PROXY_TYPE); + Split(equal, if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(rax, if_false); if (!FLAG_harmony_typeof) { @@ -3895,18 +4142,7 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else { if (if_false != fall_through) __ jmp(if_false); } -} - - -void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through) { - VisitForAccumulatorValue(expr); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); - Split(equal, if_true, if_false, fall_through); + context()->Plug(if_true, if_false); } @@ -3914,6 +4150,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); SetSourcePosition(expr->position()); + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. Label materialize_true, materialize_false; @@ -3923,20 +4163,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { - context()->Plug(if_true, if_false); - return; - } - Token::Value op = expr->op(); VisitForStackValue(expr->left()); switch (op) { case Token::IN: VisitForStackValue(expr->right()); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); __ CompareRoot(rax, Heap::kTrueValueRootIndex); Split(equal, if_true, if_false, fall_through); break; @@ -3945,7 +4178,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForStackValue(expr->right()); InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ testq(rax, rax); // The stub returns 0 for true. Split(zero, if_true, if_false, fall_through); @@ -3959,33 +4192,25 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: case Token::EQ: cc = equal; - __ pop(rdx); break; case Token::LT: cc = less; - __ pop(rdx); break; case Token::GT: - // Reverse left and right sizes to obtain ECMA-262 conversion order. - cc = less; - __ movq(rdx, result_register()); - __ pop(rax); + cc = greater; break; case Token::LTE: - // Reverse left and right sizes to obtain ECMA-262 conversion order. - cc = greater_equal; - __ movq(rdx, result_register()); - __ pop(rax); + cc = less_equal; break; case Token::GTE: cc = greater_equal; - __ pop(rdx); break; case Token::IN: case Token::INSTANCEOF: default: UNREACHABLE(); } + __ pop(rdx); bool inline_smi_code = ShouldInlineSmiCase(op); JumpPatchSite patch_site(masm_); @@ -4005,7 +4230,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { __ call(ic, RelocInfo::CODE_TARGET, expr->id()); patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ testq(rax, rax); Split(cc, if_true, if_false, fall_through); } @@ -4017,8 +4242,9 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } -void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { - Comment cmnt(masm_, "[ CompareToNull"); +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4026,14 +4252,20 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - VisitForAccumulatorValue(expr->expression()); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - __ CompareRoot(rax, Heap::kNullValueRootIndex); - if (expr->is_strict()) { + VisitForAccumulatorValue(sub_expr); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + Heap::RootListIndex nil_value = nil == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; + __ CompareRoot(rax, nil_value); + if (expr->op() == Token::EQ_STRICT) { Split(equal, if_true, if_false, fall_through); } else { + Heap::RootListIndex other_nil_value = nil == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; __ j(equal, if_true); - __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); + __ CompareRoot(rax, other_nil_value); __ j(equal, if_true); __ JumpIfSmi(rax, if_false); // It can be an undetectable object. diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 9d55594dcb..0632ce439f 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -221,7 +221,7 @@ static void GenerateDictionaryStore(MacroAssembler* masm, // Update write barrier. Make sure not to clobber the value. __ movq(scratch0, value); - __ RecordWrite(elements, scratch1, scratch0); + __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs); } @@ -462,30 +462,58 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ movl(rdi, FieldOperand(rax, String::kHashFieldOffset)); __ shr(rdi, Immediate(String::kHashShift)); __ xor_(rcx, rdi); - __ and_(rcx, Immediate(KeyedLookupCache::kCapacityMask)); + int mask = (KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask); + __ and_(rcx, Immediate(mask)); // Load the key (consisting of map and symbol) from the cache and // check for match. + Label load_in_object_property; + static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; + Label hit_on_nth_entry[kEntriesPerBucket]; ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys(masm->isolate()); - __ movq(rdi, rcx); - __ shl(rdi, Immediate(kPointerSizeLog2 + 1)); - __ LoadAddress(kScratchRegister, cache_keys); - __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, 0)); + + for (int i = 0; i < kEntriesPerBucket - 1; i++) { + Label try_next_entry; + __ movq(rdi, rcx); + __ shl(rdi, Immediate(kPointerSizeLog2 + 1)); + __ LoadAddress(kScratchRegister, cache_keys); + int off = kPointerSize * i * 2; + __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, off)); + __ j(not_equal, &try_next_entry); + __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, off + kPointerSize)); + __ j(equal, &hit_on_nth_entry[i]); + __ bind(&try_next_entry); + } + + int off = kPointerSize * (kEntriesPerBucket - 1) * 2; + __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, off)); __ j(not_equal, &slow); - __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, kPointerSize)); + __ cmpq(rax, Operand(kScratchRegister, rdi, times_1, off + kPointerSize)); __ j(not_equal, &slow); // Get field offset, which is a 32-bit integer. ExternalReference cache_field_offsets = ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate()); - __ LoadAddress(kScratchRegister, cache_field_offsets); - __ movl(rdi, Operand(kScratchRegister, rcx, times_4, 0)); - __ movzxbq(rcx, FieldOperand(rbx, Map::kInObjectPropertiesOffset)); - __ subq(rdi, rcx); - __ j(above_equal, &property_array_property); + + // Hit on nth entry. + for (int i = kEntriesPerBucket - 1; i >= 0; i--) { + __ bind(&hit_on_nth_entry[i]); + if (i != 0) { + __ addl(rcx, Immediate(i)); + } + __ LoadAddress(kScratchRegister, cache_field_offsets); + __ movl(rdi, Operand(kScratchRegister, rcx, times_4, 0)); + __ movzxbq(rcx, FieldOperand(rbx, Map::kInObjectPropertiesOffset)); + __ subq(rdi, rcx); + __ j(above_equal, &property_array_property); + if (i != 0) { + __ jmp(&load_in_object_property); + } + } // Load in-object property. + __ bind(&load_in_object_property); __ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset)); __ addq(rcx, rdi); __ movq(rax, FieldOperand(rdx, rcx, times_pointer_size, 0)); @@ -531,14 +559,12 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { Register receiver = rdx; Register index = rax; - Register scratch1 = rbx; - Register scratch2 = rcx; + Register scratch = rcx; Register result = rax; StringCharAtGenerator char_at_generator(receiver, index, - scratch1, - scratch2, + scratch, result, &miss, // When not a string. &miss, // When not a number. @@ -606,45 +632,42 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Label slow, slow_with_tagged_index, fast, array, extra; + Label slow, slow_with_tagged_index, fast, array, extra, check_extra_double; + Label fast_object_with_map_check, fast_object_without_map_check; + Label fast_double_with_map_check, fast_double_without_map_check; + Label transition_smi_elements, finish_object_store, non_double_value; + Label transition_double_elements; // Check that the object isn't a smi. __ JumpIfSmi(rdx, &slow_with_tagged_index); // Get the map from the receiver. - __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset)); // Check that the receiver does not require access checks. We need // to do this because this generic stub does not perform map checks. - __ testb(FieldOperand(rbx, Map::kBitFieldOffset), + __ testb(FieldOperand(r9, Map::kBitFieldOffset), Immediate(1 << Map::kIsAccessCheckNeeded)); __ j(not_zero, &slow_with_tagged_index); // Check that the key is a smi. __ JumpIfNotSmi(rcx, &slow_with_tagged_index); __ SmiToInteger32(rcx, rcx); - __ CmpInstanceType(rbx, JS_ARRAY_TYPE); + __ CmpInstanceType(r9, JS_ARRAY_TYPE); __ j(equal, &array); // Check that the object is some kind of JSObject. - __ CmpInstanceType(rbx, FIRST_JS_RECEIVER_TYPE); + __ CmpInstanceType(r9, FIRST_JS_OBJECT_TYPE); __ j(below, &slow); - __ CmpInstanceType(rbx, JS_PROXY_TYPE); - __ j(equal, &slow); - __ CmpInstanceType(rbx, JS_FUNCTION_PROXY_TYPE); - __ j(equal, &slow); // Object case: Check key against length in the elements array. // rax: value // rdx: JSObject // rcx: index __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); - // Check that the object is in fast mode and writable. - __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), - Heap::kFixedArrayMapRootIndex); - __ j(not_equal, &slow); + // Check array bounds. __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), rcx); // rax: value // rbx: FixedArray // rcx: index - __ j(above, &fast); + __ j(above, &fast_object_with_map_check); // Slow case: call runtime. __ bind(&slow); @@ -666,9 +689,20 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), rcx); __ j(below_equal, &slow); // Increment index to get new length. + __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); + __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); + __ j(not_equal, &check_extra_double); + __ leal(rdi, Operand(rcx, 1)); + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); + __ jmp(&fast_object_without_map_check); + + __ bind(&check_extra_double); + // rdi: elements array's map + __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); + __ j(not_equal, &slow); __ leal(rdi, Operand(rcx, 1)); __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); - __ jmp(&fast); + __ jmp(&fast_double_without_map_check); // Array case: Get the length and the elements array from the JS // array. Check that the array is in fast mode (and writable); if it @@ -678,9 +712,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // rdx: receiver (a JSArray) // rcx: index __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); - __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), - Heap::kFixedArrayMapRootIndex); - __ j(not_equal, &slow); // Check the key against the length in the array, compute the // address to store into and fall through to fast case. @@ -688,30 +719,100 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ j(below_equal, &extra); // Fast case: Do the store. - __ bind(&fast); + __ bind(&fast_object_with_map_check); // rax: value // rbx: receiver's elements array (a FixedArray) // rcx: index + // rdx: receiver (a JSArray) + __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); + __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); + __ j(not_equal, &fast_double_with_map_check); + __ bind(&fast_object_without_map_check); + // Smi stores don't require further checks. Label non_smi_value; + __ JumpIfNotSmi(rax, &non_smi_value); + // It's irrelevant whether array is smi-only or not when writing a smi. __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), rax); - __ JumpIfNotSmi(rax, &non_smi_value, Label::kNear); __ ret(0); + __ bind(&non_smi_value); - // Slow case that needs to retain rcx for use by RecordWrite. - // Update write barrier for the elements array address. - __ movq(rdx, rax); - __ RecordWriteNonSmi(rbx, 0, rdx, rcx); + // Writing a non-smi, check whether array allows non-smi elements. + // r9: receiver's map + __ CheckFastObjectElements(r9, &transition_smi_elements); + __ bind(&finish_object_store); + __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), + rax); + __ movq(rdx, rax); // Preserve the value which is returned. + __ RecordWriteArray( + rbx, rdx, rcx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); __ ret(0); + + __ bind(&fast_double_with_map_check); + // Check for fast double array case. If this fails, call through to the + // runtime. + // rdi: elements array's map + __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); + __ j(not_equal, &slow); + __ bind(&fast_double_without_map_check); + // If the value is a number, store it as a double in the FastDoubleElements + // array. + __ StoreNumberToDoubleElements(rax, rbx, rcx, xmm0, + &transition_double_elements); + __ ret(0); + + __ bind(&transition_smi_elements); + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + + // Transition the array appropriately depending on the value type. + __ movq(r9, FieldOperand(rax, HeapObject::kMapOffset)); + __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &non_double_value); + + // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> + // FAST_DOUBLE_ELEMENTS and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_DOUBLE_ELEMENTS, + rbx, + rdi, + &slow); + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&fast_double_without_map_check); + + __ bind(&non_double_value); + // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + rbx, + rdi, + &slow); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); + + __ bind(&transition_double_elements); + // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a + // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and + // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, + FAST_ELEMENTS, + rbx, + rdi, + &slow); + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); } // The generated code does not accept smi keys. // The generated code falls through if both probes miss. -static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, - int argc, - Code::Kind kind, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, + int argc, + Code::Kind kind, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // rcx : function name // rdx : receiver @@ -721,7 +822,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, - extra_ic_state, + extra_state, NORMAL, argc); Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx, @@ -794,7 +895,7 @@ static void GenerateFunctionTailCall(MacroAssembler* masm, // The generated code falls through if the call should be handled by runtime. -static void GenerateCallNormal(MacroAssembler* masm, int argc) { +void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -821,10 +922,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { } -static void GenerateCallMiss(MacroAssembler* masm, - int argc, - IC::UtilityId id, - Code::ExtraICState extra_ic_state) { +void CallICBase::GenerateMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id, + Code::ExtraICState extra_state) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -846,21 +947,22 @@ static void GenerateCallMiss(MacroAssembler* masm, __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // Enter an internal frame. - __ EnterInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); - // Push the receiver and the name of the function. - __ push(rdx); - __ push(rcx); + // Push the receiver and the name of the function. + __ push(rdx); + __ push(rcx); - // Call the entry. - CEntryStub stub(1); - __ Set(rax, 2); - __ LoadAddress(rbx, ExternalReference(IC_Utility(id), masm->isolate())); - __ CallStub(&stub); + // Call the entry. + CEntryStub stub(1); + __ Set(rax, 2); + __ LoadAddress(rbx, ExternalReference(IC_Utility(id), masm->isolate())); + __ CallStub(&stub); - // Move result to rdi and exit the internal frame. - __ movq(rdi, rax); - __ LeaveInternalFrame(); + // Move result to rdi and exit the internal frame. + __ movq(rdi, rax); + } // Check if the receiver is a global object of some sort. // This can happen only for regular CallIC but not KeyedCallIC. @@ -881,7 +983,7 @@ static void GenerateCallMiss(MacroAssembler* masm, } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state) + CallKind call_kind = CallICBase::Contextual::decode(extra_state) ? CALL_AS_FUNCTION : CALL_AS_METHOD; ParameterCount actual(argc); @@ -913,39 +1015,6 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, } -void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver - // ----------------------------------- - - GenerateCallNormal(masm, argc); - GenerateMiss(masm, argc, Code::kNoExtraICState); -} - - -void CallIC::GenerateMiss(MacroAssembler* masm, - int argc, - Code::ExtraICState extra_ic_state) { - // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state); -} - - void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- // rcx : function name @@ -1002,13 +1071,14 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // This branch is taken when calling KeyedCallIC_Miss is neither required // nor beneficial. __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1); - __ EnterInternalFrame(); - __ push(rcx); // save the key - __ push(rdx); // pass the receiver - __ push(rcx); // pass the key - __ CallRuntime(Runtime::kKeyedGetProperty, 2); - __ pop(rcx); // restore the key - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(rcx); // save the key + __ push(rdx); // pass the receiver + __ push(rcx); // pass the key + __ CallRuntime(Runtime::kKeyedGetProperty, 2); + __ pop(rcx); // restore the key + } __ movq(rdi, rax); __ jmp(&do_call); @@ -1072,27 +1142,12 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ JumpIfSmi(rcx, &miss); Condition cond = masm->IsObjectStringType(rcx, rax, rax); __ j(NegateCondition(cond), &miss); - GenerateCallNormal(masm, argc); + CallICBase::GenerateNormal(masm, argc); __ bind(&miss); GenerateMiss(masm, argc); } -void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) { - // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver - // ----------------------------------- - - GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState); -} - - static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm, Register object, Register key, @@ -1212,7 +1267,12 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { __ movq(mapped_location, rax); __ lea(r9, mapped_location); __ movq(r8, rax); - __ RecordWrite(rbx, r9, r8); + __ RecordWrite(rbx, + r9, + r8, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + INLINE_SMI_CHECK); __ Ret(); __ bind(¬in); // The unmapped lookup expects that the parameter map is in rbx. @@ -1221,7 +1281,12 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { __ movq(unmapped_location, rax); __ lea(r9, unmapped_location); __ movq(r8, rax); - __ RecordWrite(rbx, r9, r8); + __ RecordWrite(rbx, + r9, + r8, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + INLINE_SMI_CHECK); __ Ret(); __ bind(&slow); GenerateMiss(masm, false); @@ -1408,11 +1473,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { // -- rsp[0] : return address // ----------------------------------- // - // This accepts as a receiver anything JSObject::SetElementsLength accepts - // (currently anything except for external and pixel arrays which means - // anything with elements of FixedArray type.), but currently is restricted - // to JSArray. - // Value must be a number, but only smis are accepted as the most common case. + // This accepts as a receiver anything JSArray::SetElementsLength accepts + // (currently anything except for external arrays which means anything with + // elements of FixedArray type). Value must be a number, but only smis are + // accepted as the most common case. Label miss; @@ -1434,6 +1498,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch); __ j(not_equal, &miss); + // Check that the array has fast properties, otherwise the length + // property might have been redefined. + __ movq(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset)); + __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset), + Heap::kHashTableMapRootIndex); + __ j(equal, &miss); + // Check that value is a smi. __ JumpIfNotSmi(value, &miss); @@ -1562,6 +1633,51 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) { } +void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rbx : target map + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + // Must return the modified receiver in eax. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + __ movq(rax, rdx); + __ Ret(); + __ bind(&fail); + } + + __ pop(rbx); + __ push(rdx); + __ push(rbx); // return address + __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1); +} + + +void KeyedStoreIC::GenerateTransitionElementsDoubleToObject( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rbx : target map + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + // Must return the modified receiver in eax. + if (!FLAG_trace_elements_transitions) { + Label fail; + ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); + __ movq(rax, rdx); + __ Ret(); + __ bind(&fail); + } + + __ pop(rbx); + __ push(rdx); + __ push(rbx); // return address + __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1); +} + + #undef __ @@ -1573,11 +1689,9 @@ Condition CompareIC::ComputeCondition(Token::Value op) { case Token::LT: return less; case Token::GT: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return less; + return greater; case Token::LTE: - // Reverse left and right operands to obtain ECMA-262 conversion order. - return greater_equal; + return less_equal; case Token::GTE: return greater_equal; default: @@ -1609,6 +1723,9 @@ void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { rewritten = stub.GetCode(); } else { ICCompareStub stub(op_, state); + if (state == KNOWN_OBJECTS) { + stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map())); + } rewritten = stub.GetCode(); } set_target(*rewritten); diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc index b82dc54f3b..7c445cb994 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.cc +++ b/deps/v8/src/x64/lithium-codegen-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -70,6 +70,12 @@ bool LCodeGen::GenerateCode() { HPhase phase("Code generation", chunk()); ASSERT(is_unused()); status_ = GENERATING; + + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done in GeneratePrologue). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + return GeneratePrologue() && GenerateBody() && GenerateDeferredCode() && @@ -133,7 +139,7 @@ bool LCodeGen::GeneratePrologue() { // when called as functions (without an explicit receiver // object). rcx is zero for method calls and non-zero for function // calls. - if (info_->is_strict_mode() || info_->is_native()) { + if (!info_->is_classic_mode() || info_->is_native()) { Label ok; __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); @@ -205,11 +211,8 @@ bool LCodeGen::GeneratePrologue() { // Store it in the context. int context_offset = Context::SlotOffset(var->index()); __ movq(Operand(rsi, context_offset), rax); - // Update the write barrier. This clobbers all involved - // registers, so we have use a third register to avoid - // clobbering rsi. - __ movq(rcx, rsi); - __ RecordWrite(rcx, context_offset, rax, rbx); + // Update the write barrier. This clobbers rax and rbx. + __ RecordWriteContextSlot(rsi, context_offset, rax, rbx, kSaveFPRegs); } } Comment(";;; End allocate local context"); @@ -260,6 +263,9 @@ bool LCodeGen::GenerateDeferredCode() { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; __ bind(code->entry()); + Comment(";;; Deferred code @%d: %s.", + code->instruction_index(), + code->instr()->Mnemonic()); code->Generate(); __ jmp(code->exit()); } @@ -322,6 +328,12 @@ int LCodeGen::ToInteger32(LConstantOperand* op) const { } +double LCodeGen::ToDouble(LConstantOperand* op) const { + Handle<Object> value = chunk_->LookupLiteral(op); + return value->Number(); +} + + Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const { Handle<Object> literal = chunk_->LookupLiteral(op); ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); @@ -356,7 +368,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - translation->BeginFrame(environment->ast_id(), closure_id, height); + if (environment->is_arguments_adaptor()) { + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + } else { + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); // spilled_registers_ and spilled_double_registers_ are either @@ -492,10 +508,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, // |>------------ translation_size ------------<| int frame_count = 0; + int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; + if (!e->is_arguments_adaptor()) { + ++jsframe_count; + } } - Translation translation(&translations_, frame_count); + Translation translation(&translations_, frame_count, jsframe_count); WriteTranslation(environment, &translation); int deoptimization_index = deoptimizations_.length(); int pc_offset = masm()->pc_offset(); @@ -535,7 +555,6 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) { int length = deoptimizations_.length(); if (length == 0) return; - ASSERT(FLAG_deopt); Handle<DeoptimizationInputData> data = factory()->NewDeoptimizationInputData(length, TENURED); @@ -611,7 +630,7 @@ void LCodeGen::RecordSafepoint( Safepoint::DeoptMode deopt_mode) { ASSERT(kind == expected_safepoint_kind_); - const ZoneList<LOperand*>* operands = pointers->operands(); + const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands(); Safepoint safepoint = safepoints_.DefineSafepoint(masm(), kind, arguments, deopt_mode); @@ -973,11 +992,11 @@ void LCodeGen::DoMulI(LMulI* instr) { DeoptimizeIf(no_condition, instr->environment()); } } else if (right->IsStackSlot()) { - __ or_(kScratchRegister, ToOperand(right)); + __ orl(kScratchRegister, ToOperand(right)); DeoptimizeIf(sign, instr->environment()); } else { // Test the non-zero operand for negative sign. - __ or_(kScratchRegister, ToRegister(right)); + __ orl(kScratchRegister, ToRegister(right)); DeoptimizeIf(sign, instr->environment()); } __ bind(&done); @@ -1142,8 +1161,13 @@ void LCodeGen::DoConstantD(LConstantD* instr) { void LCodeGen::DoConstantT(LConstantT* instr) { - ASSERT(instr->result()->IsRegister()); - __ Move(ToRegister(instr->result()), instr->value()); + Handle<Object> value = instr->value(); + if (value->IsSmi()) { + __ Move(ToRegister(instr->result()), value); + } else { + __ LoadHeapObject(ToRegister(instr->result()), + Handle<HeapObject>::cast(value)); + } } @@ -1457,39 +1481,51 @@ inline Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { } -void LCodeGen::EmitCmpI(LOperand* left, LOperand* right) { - if (right->IsConstantOperand()) { - int32_t value = ToInteger32(LConstantOperand::cast(right)); - if (left->IsRegister()) { - __ cmpl(ToRegister(left), Immediate(value)); - } else { - __ cmpl(ToOperand(left), Immediate(value)); - } - } else if (right->IsRegister()) { - __ cmpl(ToRegister(left), ToRegister(right)); - } else { - __ cmpl(ToRegister(left), ToOperand(right)); - } -} - - void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { LOperand* left = instr->InputAt(0); LOperand* right = instr->InputAt(1); int false_block = chunk_->LookupDestination(instr->false_block_id()); int true_block = chunk_->LookupDestination(instr->true_block_id()); + Condition cc = TokenToCondition(instr->op(), instr->is_double()); - if (instr->is_double()) { - // Don't base result on EFLAGS when a NaN is involved. Instead - // jump to the false block. - __ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right)); - __ j(parity_even, chunk_->GetAssemblyLabel(false_block)); + if (left->IsConstantOperand() && right->IsConstantOperand()) { + // We can statically evaluate the comparison. + double left_val = ToDouble(LConstantOperand::cast(left)); + double right_val = ToDouble(LConstantOperand::cast(right)); + int next_block = + EvalComparison(instr->op(), left_val, right_val) ? true_block + : false_block; + EmitGoto(next_block); } else { - EmitCmpI(left, right); + if (instr->is_double()) { + // Don't base result on EFLAGS when a NaN is involved. Instead + // jump to the false block. + __ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right)); + __ j(parity_even, chunk_->GetAssemblyLabel(false_block)); + } else { + int32_t value; + if (right->IsConstantOperand()) { + value = ToInteger32(LConstantOperand::cast(right)); + __ cmpl(ToRegister(left), Immediate(value)); + } else if (left->IsConstantOperand()) { + value = ToInteger32(LConstantOperand::cast(left)); + if (right->IsRegister()) { + __ cmpl(ToRegister(right), Immediate(value)); + } else { + __ cmpl(ToOperand(right), Immediate(value)); + } + // We transposed the operands. Reverse the condition. + cc = ReverseCondition(cc); + } else { + if (right->IsRegister()) { + __ cmpl(ToRegister(left), ToRegister(right)); + } else { + __ cmpl(ToRegister(left), ToOperand(right)); + } + } + } + EmitBranch(true_block, false_block, cc); } - - Condition cc = TokenToCondition(instr->op(), instr->is_double()); - EmitBranch(true_block, false_block, cc); } @@ -1514,30 +1550,33 @@ void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { } -void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) { +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { Register reg = ToRegister(instr->InputAt(0)); - int false_block = chunk_->LookupDestination(instr->false_block_id()); + // If the expression is known to be untagged or a smi, then it's definitely + // not null, and it can't be a an undetectable object. if (instr->hydrogen()->representation().IsSpecialization() || instr->hydrogen()->type().IsSmi()) { - // If the expression is known to untagged or smi, then it's definitely - // not null, and it can't be a an undetectable object. - // Jump directly to the false block. EmitGoto(false_block); return; } int true_block = chunk_->LookupDestination(instr->true_block_id()); - - __ CompareRoot(reg, Heap::kNullValueRootIndex); - if (instr->is_strict()) { + Heap::RootListIndex nil_value = instr->nil() == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; + __ CompareRoot(reg, nil_value); + if (instr->kind() == kStrictEquality) { EmitBranch(true_block, false_block, equal); } else { + Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); __ j(equal, true_label); - __ CompareRoot(reg, Heap::kUndefinedValueRootIndex); + __ CompareRoot(reg, other_nil_value); __ j(equal, true_label); __ JumpIfSmi(reg, false_label); // Check for undetectable objects by looking in the bit field in @@ -1590,6 +1629,30 @@ void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { } +Condition LCodeGen::EmitIsString(Register input, + Register temp1, + Label* is_not_string) { + __ JumpIfSmi(input, is_not_string); + Condition cond = masm_->IsObjectStringType(input, temp1, temp1); + + return cond; +} + + +void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { + Register reg = ToRegister(instr->InputAt(0)); + Register temp = ToRegister(instr->TempAt(0)); + + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + Label* false_label = chunk_->GetAssemblyLabel(false_block); + + Condition true_cond = EmitIsString(reg, temp, false_label); + + EmitBranch(true_block, false_block, true_cond); +} + + void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1621,6 +1684,21 @@ void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { } +void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) { + Token::Value op = instr->op(); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + Handle<Code> ic = CompareIC::GetUninitialized(op); + CallCode(ic, RelocInfo::CODE_TARGET, instr); + + Condition condition = TokenToCondition(op, false); + __ testq(rax, rax); + + EmitBranch(true_block, false_block, condition); +} + + static InstanceType TestType(HHasInstanceTypeAndBranch* instr) { InstanceType from = instr->from(); InstanceType to = instr->to(); @@ -1684,35 +1762,49 @@ void LCodeGen::DoHasCachedArrayIndexAndBranch( // Branches to a label or falls through with the answer in the z flag. -// Trashes the temp register and possibly input (if it and temp are aliased). +// Trashes the temp register. void LCodeGen::EmitClassOfTest(Label* is_true, Label* is_false, Handle<String> class_name, Register input, - Register temp) { + Register temp, + Register temp2) { + ASSERT(!input.is(temp)); + ASSERT(!input.is(temp2)); + ASSERT(!temp.is(temp2)); + __ JumpIfSmi(input, is_false); - __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp); - __ j(below, is_false); - // Map is now in temp. - // Functions have class 'Function'. - __ CmpInstanceType(temp, FIRST_CALLABLE_SPEC_OBJECT_TYPE); if (class_name->IsEqualTo(CStrVector("Function"))) { - __ j(above_equal, is_true); + // Assuming the following assertions, we can use the same compares to test + // for both being a function type and being in the object type range. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); + __ CmpObjectType(input, FIRST_SPEC_OBJECT_TYPE, temp); + __ j(below, is_false); + __ j(equal, is_true); + __ CmpInstanceType(temp, LAST_SPEC_OBJECT_TYPE); + __ j(equal, is_true); } else { - __ j(above_equal, is_false); + // Faster code path to avoid two compares: subtract lower bound from the + // actual type and do a signed compare with the width of the type range. + __ movq(temp, FieldOperand(input, HeapObject::kMapOffset)); + __ movq(temp2, FieldOperand(temp, Map::kInstanceTypeOffset)); + __ subb(temp2, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); + __ cmpb(temp2, + Immediate(static_cast<int8_t>(LAST_NONCALLABLE_SPEC_OBJECT_TYPE - + FIRST_NONCALLABLE_SPEC_OBJECT_TYPE))); + __ j(above, is_false); } + // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range. // Check if the constructor in the map is a function. __ movq(temp, FieldOperand(temp, Map::kConstructorOffset)); - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last type and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - // Objects with a non-function constructor have class 'Object'. __ CmpObjectType(temp, JS_FUNCTION_TYPE, kScratchRegister); if (class_name->IsEqualTo(CStrVector("Object"))) { @@ -1741,6 +1833,7 @@ void LCodeGen::EmitClassOfTest(Label* is_true, void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { Register input = ToRegister(instr->InputAt(0)); Register temp = ToRegister(instr->TempAt(0)); + Register temp2 = ToRegister(instr->TempAt(1)); Handle<String> class_name = instr->hydrogen()->class_name(); int true_block = chunk_->LookupDestination(instr->true_block_id()); @@ -1749,7 +1842,7 @@ void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); - EmitClassOfTest(true_label, false_label, class_name, input, temp); + EmitClassOfTest(true_label, false_label, class_name, input, temp, temp2); EmitBranch(true_block, false_block, equal); } @@ -1790,9 +1883,8 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { virtual void Generate() { codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_); } - + virtual LInstruction* instr() { return instr_; } Label* map_check() { return &map_check_; } - private: LInstanceOfKnownGlobal* instr_; Label map_check_; @@ -1816,9 +1908,10 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { Register map = ToRegister(instr->TempAt(0)); __ movq(map, FieldOperand(object, HeapObject::kMapOffset)); __ bind(deferred->map_check()); // Label for calculating code patching. - __ movq(kScratchRegister, factory()->the_hole_value(), - RelocInfo::EMBEDDED_OBJECT); - __ cmpq(map, kScratchRegister); // Patched to cached map. + Handle<JSGlobalPropertyCell> cache_cell = + factory()->NewJSGlobalPropertyCell(factory()->the_hole_value()); + __ movq(kScratchRegister, cache_cell, RelocInfo::GLOBAL_PROPERTY_CELL); + __ cmpq(map, Operand(kScratchRegister, 0)); __ j(not_equal, &cache_miss, Label::kNear); // Patched to load either true or false. __ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex); @@ -1856,7 +1949,7 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, InstanceofStub stub(flags); __ push(ToRegister(instr->InputAt(0))); - __ Push(instr->function()); + __ PushHeapObject(instr->function()); static const int kAdditionalDelta = 10; int delta = @@ -1900,9 +1993,6 @@ void LCodeGen::DoCmpT(LCmpT* instr) { CallCode(ic, RelocInfo::CODE_TARGET, instr); Condition condition = TokenToCondition(op, false); - if (op == Token::GT || op == Token::LTE) { - condition = ReverseCondition(condition); - } Label true_value, done; __ testq(rax, rax); __ j(condition, &true_value, Label::kNear); @@ -1929,14 +2019,8 @@ void LCodeGen::DoReturn(LReturn* instr) { void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { Register result = ToRegister(instr->result()); - if (result.is(rax)) { - __ load_rax(instr->hydrogen()->cell().location(), - RelocInfo::GLOBAL_PROPERTY_CELL); - } else { - __ movq(result, instr->hydrogen()->cell(), RelocInfo::GLOBAL_PROPERTY_CELL); - __ movq(result, Operand(result, 0)); - } - if (instr->hydrogen()->check_hole_value()) { + __ LoadGlobalCell(result, instr->hydrogen()->cell()); + if (instr->hydrogen()->RequiresHoleCheck()) { __ CompareRoot(result, Heap::kTheHoleValueRootIndex); DeoptimizeIf(equal, instr->environment()); } @@ -1956,25 +2040,28 @@ void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) { void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { - Register value = ToRegister(instr->InputAt(0)); - Register temp = ToRegister(instr->TempAt(0)); - ASSERT(!value.is(temp)); - bool check_hole = instr->hydrogen()->check_hole_value(); - if (!check_hole && value.is(rax)) { - __ store_rax(instr->hydrogen()->cell().location(), - RelocInfo::GLOBAL_PROPERTY_CELL); - return; - } + Register value = ToRegister(instr->value()); + Handle<JSGlobalPropertyCell> cell_handle = instr->hydrogen()->cell(); + // If the cell we are storing to contains the hole it could have // been deleted from the property dictionary. In that case, we need // to update the property details in the property dictionary to mark // it as no longer deleted. We deoptimize in that case. - __ movq(temp, instr->hydrogen()->cell(), RelocInfo::GLOBAL_PROPERTY_CELL); - if (check_hole) { - __ CompareRoot(Operand(temp, 0), Heap::kTheHoleValueRootIndex); + if (instr->hydrogen()->RequiresHoleCheck()) { + // We have a temp because CompareRoot might clobber kScratchRegister. + Register cell = ToRegister(instr->TempAt(0)); + ASSERT(!value.is(cell)); + __ movq(cell, cell_handle, RelocInfo::GLOBAL_PROPERTY_CELL); + __ CompareRoot(Operand(cell, 0), Heap::kTheHoleValueRootIndex); DeoptimizeIf(equal, instr->environment()); + // Store the value. + __ movq(Operand(cell, 0), value); + } else { + // Store the value. + __ movq(kScratchRegister, cell_handle, RelocInfo::GLOBAL_PROPERTY_CELL); + __ movq(Operand(kScratchRegister, 0), value); } - __ movq(Operand(temp, 0), value); + // Cells are always rescanned, so no write barrier here. } @@ -1983,7 +2070,7 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) { ASSERT(ToRegister(instr->value()).is(rax)); __ Move(rcx, instr->name()); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr); @@ -1994,18 +2081,53 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { Register context = ToRegister(instr->context()); Register result = ToRegister(instr->result()); __ movq(result, ContextOperand(context, instr->slot_index())); + if (instr->hydrogen()->RequiresHoleCheck()) { + __ CompareRoot(result, Heap::kTheHoleValueRootIndex); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(equal, instr->environment()); + } else { + Label is_not_hole; + __ j(not_equal, &is_not_hole, Label::kNear); + __ LoadRoot(result, Heap::kUndefinedValueRootIndex); + __ bind(&is_not_hole); + } + } } void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { Register context = ToRegister(instr->context()); Register value = ToRegister(instr->value()); - __ movq(ContextOperand(context, instr->slot_index()), value); - if (instr->needs_write_barrier()) { + + Operand target = ContextOperand(context, instr->slot_index()); + + Label skip_assignment; + if (instr->hydrogen()->RequiresHoleCheck()) { + __ CompareRoot(target, Heap::kTheHoleValueRootIndex); + if (instr->hydrogen()->DeoptimizesOnHole()) { + DeoptimizeIf(equal, instr->environment()); + } else { + __ j(not_equal, &skip_assignment); + } + } + __ movq(target, value); + + if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; int offset = Context::SlotOffset(instr->slot_index()); Register scratch = ToRegister(instr->TempAt(0)); - __ RecordWrite(context, offset, value, scratch); + __ RecordWriteContextSlot(context, + offset, + value, + scratch, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } + + __ bind(&skip_assignment); } @@ -2025,9 +2147,9 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, Register object, Handle<Map> type, Handle<String> name) { - LookupResult lookup; + LookupResult lookup(isolate()); type->LookupInDescriptors(NULL, *name, &lookup); - ASSERT(lookup.IsProperty() && + ASSERT(lookup.IsFound() && (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION)); if (lookup.type() == FIELD) { int index = lookup.GetLocalFieldIndexFromMap(*type); @@ -2043,7 +2165,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, } } else { Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type)); - LoadHeapObject(result, Handle<HeapObject>::cast(function)); + __ LoadHeapObject(result, function); } } @@ -2223,17 +2345,15 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( LLoadKeyedFastDoubleElement* instr) { XMMRegister result(ToDoubleRegister(instr->result())); - if (instr->hydrogen()->RequiresHoleCheck()) { - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + - sizeof(kHoleNanLower32); - Operand hole_check_operand = BuildFastArrayOperand( - instr->elements(), - instr->key(), - FAST_DOUBLE_ELEMENTS, - offset); - __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); - DeoptimizeIf(equal, instr->environment()); - } + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + + sizeof(kHoleNanLower32); + Operand hole_check_operand = BuildFastArrayOperand( + instr->elements(), + instr->key(), + FAST_DOUBLE_ELEMENTS, + offset); + __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); + DeoptimizeIf(equal, instr->environment()); Operand double_load_operand = BuildFastArrayOperand( instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, @@ -2305,6 +2425,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -2451,7 +2572,7 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { RecordPosition(pointers->position()); SafepointGenerator safepoint_generator( this, pointers, Safepoint::kLazyDeopt); - v8::internal::ParameterCount actual(rax); + ParameterCount actual(rax); __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator, CALL_AS_METHOD); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2466,7 +2587,7 @@ void LCodeGen::DoPushArgument(LPushArgument* instr) { void LCodeGen::DoThisFunction(LThisFunction* instr) { Register result = ToRegister(instr->result()); - __ movq(result, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ LoadHeapObject(result, instr->hydrogen()->closure()); } @@ -2501,35 +2622,48 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, int arity, LInstruction* instr, CallKind call_kind) { - // Change context if needed. - bool change_context = - (info()->closure()->context() != function->context()) || - scope()->contains_with() || - (scope()->num_heap_slots() > 0); - if (change_context) { - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - } - - // Set rax to arguments count if adaption is not needed. Assumes that rax - // is available to write to at this point. - if (!function->NeedsArgumentsAdaption()) { - __ Set(rax, arity); - } + bool can_invoke_directly = !function->NeedsArgumentsAdaption() || + function->shared()->formal_parameter_count() == arity; LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); - // Invoke function. - __ SetCallKind(rcx, call_kind); - if (*function == *info()->closure()) { - __ CallSelf(); + if (can_invoke_directly) { + __ LoadHeapObject(rdi, function); + + // Change context if needed. + bool change_context = + (info()->closure()->context() != function->context()) || + scope()->contains_with() || + (scope()->num_heap_slots() > 0); + if (change_context) { + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + } + + // Set rax to arguments count if adaption is not needed. Assumes that rax + // is available to write to at this point. + if (!function->NeedsArgumentsAdaption()) { + __ Set(rax, arity); + } + + // Invoke function. + __ SetCallKind(rcx, call_kind); + if (*function == *info()->closure()) { + __ CallSelf(); + } else { + __ call(FieldOperand(rdi, JSFunction::kCodeEntryOffset)); + } + + // Set up deoptimization. + RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); } else { - __ call(FieldOperand(rdi, JSFunction::kCodeEntryOffset)); + // We need to adapt arguments. + SafepointGenerator generator( + this, pointers, Safepoint::kLazyDeopt); + ParameterCount count(arity); + __ InvokeFunction(function, count, CALL_FUNCTION, generator, call_kind); } - // Setup deoptimization. - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); - // Restore context. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); } @@ -2537,7 +2671,6 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) { ASSERT(ToRegister(instr->result()).is(rax)); - __ Move(rdi, instr->function()); CallKnownFunction(instr->function(), instr->arity(), instr, @@ -2618,6 +2751,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { virtual void Generate() { codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_); } + virtual LInstruction* instr() { return instr_; } private: LUnaryMathOperation* instr_; }; @@ -2751,65 +2885,101 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); + + // Note that according to ECMA-262 15.8.2.13: + // Math.pow(-Infinity, 0.5) == Infinity + // Math.sqrt(-Infinity) == NaN + Label done, sqrt; + // Check base for -Infinity. According to IEEE-754, double-precision + // -Infinity has the highest 12 bits set and the lowest 52 bits cleared. + __ movq(kScratchRegister, V8_INT64_C(0xFFF0000000000000), RelocInfo::NONE); + __ movq(xmm_scratch, kScratchRegister); + __ ucomisd(xmm_scratch, input_reg); + // Comparing -Infinity with NaN results in "unordered", which sets the + // zero flag as if both were equal. However, it also sets the carry flag. + __ j(not_equal, &sqrt, Label::kNear); + __ j(carry, &sqrt, Label::kNear); + // If input is -Infinity, return Infinity. + __ xorps(input_reg, input_reg); + __ subsd(input_reg, xmm_scratch); + __ jmp(&done, Label::kNear); + + // Square root. + __ bind(&sqrt); __ xorps(xmm_scratch, xmm_scratch); __ addsd(input_reg, xmm_scratch); // Convert -0 to +0. __ sqrtsd(input_reg, input_reg); + __ bind(&done); } void LCodeGen::DoPower(LPower* instr) { - LOperand* left = instr->InputAt(0); - XMMRegister left_reg = ToDoubleRegister(left); - ASSERT(!left_reg.is(xmm1)); - LOperand* right = instr->InputAt(1); - XMMRegister result_reg = ToDoubleRegister(instr->result()); Representation exponent_type = instr->hydrogen()->right()->representation(); - if (exponent_type.IsDouble()) { - __ PrepareCallCFunction(2); - // Move arguments to correct registers - __ movaps(xmm0, left_reg); - ASSERT(ToDoubleRegister(right).is(xmm1)); - __ CallCFunction( - ExternalReference::power_double_double_function(isolate()), 2); - } else if (exponent_type.IsInteger32()) { - __ PrepareCallCFunction(2); - // Move arguments to correct registers: xmm0 and edi (not rdi). - // On Windows, the registers are xmm0 and edx. - __ movaps(xmm0, left_reg); + // Having marked this as a call, we can use any registers. + // Just make sure that the input/output registers are the expected ones. + + // Choose register conforming to calling convention (when bailing out). #ifdef _WIN64 - ASSERT(ToRegister(right).is(rdx)); + Register exponent = rdx; #else - ASSERT(ToRegister(right).is(rdi)); + Register exponent = rdi; #endif - __ CallCFunction( - ExternalReference::power_double_int_function(isolate()), 2); + ASSERT(!instr->InputAt(1)->IsRegister() || + ToRegister(instr->InputAt(1)).is(exponent)); + ASSERT(!instr->InputAt(1)->IsDoubleRegister() || + ToDoubleRegister(instr->InputAt(1)).is(xmm1)); + ASSERT(ToDoubleRegister(instr->InputAt(0)).is(xmm2)); + ASSERT(ToDoubleRegister(instr->result()).is(xmm3)); + + if (exponent_type.IsTagged()) { + Label no_deopt; + __ JumpIfSmi(exponent, &no_deopt); + __ CmpObjectType(exponent, HEAP_NUMBER_TYPE, rcx); + DeoptimizeIf(not_equal, instr->environment()); + __ bind(&no_deopt); + MathPowStub stub(MathPowStub::TAGGED); + __ CallStub(&stub); + } else if (exponent_type.IsInteger32()) { + MathPowStub stub(MathPowStub::INTEGER); + __ CallStub(&stub); } else { - ASSERT(exponent_type.IsTagged()); - Register right_reg = ToRegister(right); + ASSERT(exponent_type.IsDouble()); + MathPowStub stub(MathPowStub::DOUBLE); + __ CallStub(&stub); + } +} - Label non_smi, call; - __ JumpIfNotSmi(right_reg, &non_smi); - __ SmiToInteger32(right_reg, right_reg); - __ cvtlsi2sd(xmm1, right_reg); - __ jmp(&call); - __ bind(&non_smi); - __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , kScratchRegister); - DeoptimizeIf(not_equal, instr->environment()); - __ movsd(xmm1, FieldOperand(right_reg, HeapNumber::kValueOffset)); - - __ bind(&call); - __ PrepareCallCFunction(2); - // Move arguments to correct registers xmm0 and xmm1. - __ movaps(xmm0, left_reg); - // Right argument is already in xmm1. - __ CallCFunction( - ExternalReference::power_double_double_function(isolate()), 2); - } - // Return value is in xmm0. - __ movaps(result_reg, xmm0); - // Restore context register. +void LCodeGen::DoRandom(LRandom* instr) { + // Having marked this instruction as a call we can use any + // registers. + ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + + // Choose the right register for the first argument depending on + // calling convention. +#ifdef _WIN64 + ASSERT(ToRegister(instr->InputAt(0)).is(rcx)); + Register global_object = rcx; +#else + ASSERT(ToRegister(instr->InputAt(0)).is(rdi)); + Register global_object = rdi; +#endif + + __ PrepareCallCFunction(1); + __ movq(global_object, + FieldOperand(global_object, GlobalObject::kGlobalContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + + // Convert 32 random bits in rax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm2, rcx); + __ movd(xmm1, rax); + __ cvtss2sd(xmm2, xmm2); + __ xorps(xmm1, xmm2); + __ subsd(xmm1, xmm2); } @@ -2821,6 +2991,14 @@ void LCodeGen::DoMathLog(LUnaryMathOperation* instr) { } +void LCodeGen::DoMathTan(LUnaryMathOperation* instr) { + ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::UNTAGGED); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); +} + + void LCodeGen::DoMathCos(LUnaryMathOperation* instr) { ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); TranscendentalCacheStub stub(TranscendentalCache::COS, @@ -2860,6 +3038,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { case kMathSin: DoMathSin(instr); break; + case kMathTan: + DoMathTan(instr); + break; case kMathLog: DoMathLog(instr); break; @@ -2909,13 +3090,13 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { void LCodeGen::DoCallFunction(LCallFunction* instr) { + ASSERT(ToRegister(instr->function()).is(rdi)); ASSERT(ToRegister(instr->result()).is(rax)); int arity = instr->arity(); - CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT); + CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ Drop(1); } @@ -2933,7 +3114,6 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) { ASSERT(ToRegister(instr->result()).is(rax)); - __ Move(rdi, instr->target()); CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION); } @@ -2942,9 +3122,9 @@ void LCodeGen::DoCallNew(LCallNew* instr) { ASSERT(ToRegister(instr->InputAt(0)).is(rdi)); ASSERT(ToRegister(instr->result()).is(rax)); - Handle<Code> builtin = isolate()->builtins()->JSConstructCall(); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ Set(rax, instr->arity()); - CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr); + CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr); } @@ -2963,21 +3143,36 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { } // Do the store. + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; if (instr->is_in_object()) { __ movq(FieldOperand(object, offset), value); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { Register temp = ToRegister(instr->TempAt(0)); // Update the write barrier for the object for in-object properties. - __ RecordWrite(object, offset, value, temp); + __ RecordWriteField(object, + offset, + value, + temp, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } else { Register temp = ToRegister(instr->TempAt(0)); __ movq(temp, FieldOperand(object, JSObject::kPropertiesOffset)); __ movq(FieldOperand(temp, offset), value); - if (instr->needs_write_barrier()) { + if (instr->hydrogen()->NeedsWriteBarrier()) { // Update the write barrier for the properties array. // object is used as a scratch register. - __ RecordWrite(temp, offset, value, object); + __ RecordWriteField(temp, + offset, + value, + object, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } } @@ -2988,7 +3183,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { ASSERT(ToRegister(instr->value()).is(rax)); __ Move(rcx, instr->hydrogen()->name()); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); @@ -3025,6 +3220,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3076,12 +3272,20 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { } if (instr->hydrogen()->NeedsWriteBarrier()) { + HType type = instr->hydrogen()->value()->type(); + SmiCheck check_needed = + type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; // Compute address of modified element and store it into key register. __ lea(key, FieldOperand(elements, key, times_pointer_size, FixedArray::kHeaderSize)); - __ RecordWrite(elements, key, value); + __ RecordWrite(elements, + key, + value, + kSaveFPRegs, + EMIT_REMEMBERED_SET, + check_needed); } } @@ -3110,13 +3314,54 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(rcx)); ASSERT(ToRegister(instr->value()).is(rax)); - Handle<Code> ic = instr->strict_mode() + Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() : isolate()->builtins()->KeyedStoreIC_Initialize(); CallCode(ic, RelocInfo::CODE_TARGET, instr); } +void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { + Register object_reg = ToRegister(instr->object()); + Register new_map_reg = ToRegister(instr->new_map_reg()); + + Handle<Map> from_map = instr->original_map(); + Handle<Map> to_map = instr->transitioned_map(); + ElementsKind from_kind = from_map->elements_kind(); + ElementsKind to_kind = to_map->elements_kind(); + + Label not_applicable; + __ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map); + __ j(not_equal, ¬_applicable); + __ movq(new_map_reg, to_map, RelocInfo::EMBEDDED_OBJECT); + if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + __ movq(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg); + // Write barrier. + ASSERT_NE(instr->temp_reg(), NULL); + __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, + ToRegister(instr->temp_reg()), kDontSaveFPRegs); + } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && + to_kind == FAST_DOUBLE_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(rdx)); + ASSERT(new_map_reg.is(rbx)); + __ movq(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), + RelocInfo::CODE_TARGET, instr); + } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { + Register fixed_object_reg = ToRegister(instr->temp_reg()); + ASSERT(fixed_object_reg.is(rdx)); + ASSERT(new_map_reg.is(rbx)); + __ movq(fixed_object_reg, object_reg); + CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(), + RelocInfo::CODE_TARGET, instr); + } else { + UNREACHABLE(); + } + __ bind(¬_applicable); +} + + void LCodeGen::DoStringAdd(LStringAdd* instr) { EmitPushTaggedOperand(instr->left()); EmitPushTaggedOperand(instr->right()); @@ -3131,85 +3376,19 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharCodeAt* instr_; }; - Register string = ToRegister(instr->string()); - Register index = ToRegister(instr->index()); - Register result = ToRegister(instr->result()); - DeferredStringCharCodeAt* deferred = new DeferredStringCharCodeAt(this, instr); - // Fetch the instance type of the receiver into result register. - __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); - __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); - - // We need special handling for indirect strings. - Label check_sequential; - __ testb(result, Immediate(kIsIndirectStringMask)); - __ j(zero, &check_sequential, Label::kNear); - - // Dispatch on the indirect string shape: slice or cons. - Label cons_string; - __ testb(result, Immediate(kSlicedNotConsMask)); - __ j(zero, &cons_string, Label::kNear); - - // Handle slices. - Label indirect_string_loaded; - __ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset)); - __ addq(index, result); - __ movq(string, FieldOperand(string, SlicedString::kParentOffset)); - __ jmp(&indirect_string_loaded, Label::kNear); - - // Handle conses. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - __ bind(&cons_string); - __ CompareRoot(FieldOperand(string, ConsString::kSecondOffset), - Heap::kEmptyStringRootIndex); - __ j(not_equal, deferred->entry()); - __ movq(string, FieldOperand(string, ConsString::kFirstOffset)); - - __ bind(&indirect_string_loaded); - __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); - __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); - - // Check whether the string is sequential. The only non-sequential - // shapes we support have just been unwrapped above. - __ bind(&check_sequential); - STATIC_ASSERT(kSeqStringTag == 0); - __ testb(result, Immediate(kStringRepresentationMask)); - __ j(not_zero, deferred->entry()); - - // Dispatch on the encoding: ASCII or two-byte. - Label ascii_string; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ testb(result, Immediate(kStringEncodingMask)); - __ j(not_zero, &ascii_string, Label::kNear); - - // Two-byte string. - // Load the two-byte character code into the result register. - Label done; - STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); - __ movzxwl(result, FieldOperand(string, - index, - times_2, - SeqTwoByteString::kHeaderSize)); - __ jmp(&done, Label::kNear); - - // ASCII string. - // Load the byte into the result register. - __ bind(&ascii_string); - __ movzxbl(result, FieldOperand(string, - index, - times_1, - SeqAsciiString::kHeaderSize)); - __ bind(&done); + StringCharLoadGenerator::Generate(masm(), + ToRegister(instr->string()), + ToRegister(instr->index()), + ToRegister(instr->result()), + deferred->entry()); __ bind(deferred->exit()); } @@ -3251,6 +3430,7 @@ void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStringCharFromCode* instr_; }; @@ -3327,6 +3507,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } + virtual LInstruction* instr() { return instr_; } private: LNumberTagD* instr_; }; @@ -3385,6 +3566,7 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagD(Register input_reg, XMMRegister result_reg, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env) { Label load_smi, done; @@ -3412,6 +3594,15 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, } // Heap number to XMM conversion. __ movsd(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset)); + if (deoptimize_on_minus_zero) { + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ ucomisd(xmm_scratch, result_reg); + __ j(not_equal, &done, Label::kNear); + __ movmskpd(kScratchRegister, result_reg); + __ testq(kScratchRegister, Immediate(1)); + DeoptimizeIf(not_zero, env); + } __ jmp(&done, Label::kNear); // Smi to XMM conversion @@ -3422,16 +3613,6 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, } -class DeferredTaggedToI: public LDeferredCode { - public: - DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } - private: - LTaggedToI* instr_; -}; - - void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { Label done, heap_number; Register input_reg = ToRegister(instr->InputAt(0)); @@ -3480,6 +3661,16 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { void LCodeGen::DoTaggedToI(LTaggedToI* instr) { + class DeferredTaggedToI: public LDeferredCode { + public: + DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LTaggedToI* instr_; + }; + LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); ASSERT(input->Equals(instr->result())); @@ -3503,6 +3694,7 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { EmitNumberUntagD(input_reg, result_reg, instr->hydrogen()->deoptimize_on_undefined(), + instr->hydrogen()->deoptimize_on_minus_zero(), instr->environment()); } @@ -3608,20 +3800,37 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { void LCodeGen::DoCheckFunction(LCheckFunction* instr) { - ASSERT(instr->InputAt(0)->IsRegister()); - Register reg = ToRegister(instr->InputAt(0)); - __ Cmp(reg, instr->hydrogen()->target()); + Register reg = ToRegister(instr->value()); + Handle<JSFunction> target = instr->hydrogen()->target(); + if (isolate()->heap()->InNewSpace(*target)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(target); + __ movq(kScratchRegister, cell, RelocInfo::GLOBAL_PROPERTY_CELL); + __ cmpq(reg, Operand(kScratchRegister, 0)); + } else { + __ Cmp(reg, target); + } DeoptimizeIf(not_equal, instr->environment()); } +void LCodeGen::DoCheckMapCommon(Register reg, + Handle<Map> map, + CompareMapMode mode, + LEnvironment* env) { + Label success; + __ CompareMap(reg, map, &success, mode); + DeoptimizeIf(not_equal, env); + __ bind(&success); +} + + void LCodeGen::DoCheckMap(LCheckMap* instr) { LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister()); Register reg = ToRegister(input); - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), - instr->hydrogen()->map()); - DeoptimizeIf(not_equal, instr->environment()); + Handle<Map> map = instr->hydrogen()->map(); + DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment()); } @@ -3676,18 +3885,6 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { } -void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) { - if (heap()->InNewSpace(*object)) { - Handle<JSGlobalPropertyCell> cell = - factory()->NewJSGlobalPropertyCell(object); - __ movq(result, cell, RelocInfo::GLOBAL_PROPERTY_CELL); - __ movq(result, Operand(result, 0)); - } else { - __ Move(result, object); - } -} - - void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Register reg = ToRegister(instr->TempAt(0)); @@ -3695,32 +3892,51 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { Handle<JSObject> current_prototype = instr->prototype(); // Load prototype object. - LoadHeapObject(reg, current_prototype); + __ LoadHeapObject(reg, current_prototype); // Check prototype maps up to the holder. while (!current_prototype.is_identical_to(holder)) { - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle<Map>(current_prototype->map())); - DeoptimizeIf(not_equal, instr->environment()); + DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); current_prototype = Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype())); // Load next prototype object. - LoadHeapObject(reg, current_prototype); + __ LoadHeapObject(reg, current_prototype); } // Check the holder map. - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle<Map>(current_prototype->map())); - DeoptimizeIf(not_equal, instr->environment()); + DoCheckMapCommon(reg, Handle<Map>(current_prototype->map()), + ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment()); } void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { - // Setup the parameters to the stub/runtime call. + Heap* heap = isolate()->heap(); + ElementsKind boilerplate_elements_kind = + instr->hydrogen()->boilerplate_elements_kind(); + + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to FAST_ELEMENTS. + if (boilerplate_elements_kind != FAST_ELEMENTS) { + __ LoadHeapObject(rax, instr->hydrogen()->boilerplate_object()); + __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); + // Load the map's "bit field 2". + __ movb(rbx, FieldOperand(rbx, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ and_(rbx, Immediate(Map::kElementsKindMask)); + __ cmpb(rbx, Immediate(boilerplate_elements_kind << + Map::kElementsKindShift)); + DeoptimizeIf(not_equal, instr->environment()); + } + + // Set up the parameters to the stub/runtime call. __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(rax, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(instr->hydrogen()->literal_index())); - __ Push(instr->hydrogen()->constant_elements()); + // Boilerplate already exists, constant elements are never accessed. + // Pass an empty fixed array. + __ Push(Handle<FixedArray>(heap->empty_fixed_array())); // Pick the right runtime function or stub to call. int length = instr->hydrogen()->length(); @@ -3736,26 +3952,108 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr); } else { FastCloneShallowArrayStub::Mode mode = - FastCloneShallowArrayStub::CLONE_ELEMENTS; + boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS + ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ELEMENTS; FastCloneShallowArrayStub stub(mode, length); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } -void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { - // Setup the parameters to the stub/runtime call. +void LCodeGen::EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset) { + ASSERT(!source.is(rcx)); + ASSERT(!result.is(rcx)); + + // Increase the offset so that subsequent objects end up right after + // this one. + int current_offset = *offset; + int size = object->map()->instance_size(); + *offset += size; + + // Copy object header. + ASSERT(object->properties()->length() == 0); + ASSERT(object->elements()->length() == 0 || + object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); + int inobject_properties = object->map()->inobject_properties(); + int header_size = size - inobject_properties * kPointerSize; + for (int i = 0; i < header_size; i += kPointerSize) { + __ movq(rcx, FieldOperand(source, i)); + __ movq(FieldOperand(result, current_offset + i), rcx); + } + + // Copy in-object properties. + for (int i = 0; i < inobject_properties; i++) { + int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + __ lea(rcx, Operand(result, *offset)); + __ movq(FieldOperand(result, total_offset), rcx); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(rcx, Handle<HeapObject>::cast(value)); + __ movq(FieldOperand(result, total_offset), rcx); + } else { + __ movq(rcx, value, RelocInfo::NONE); + __ movq(FieldOperand(result, total_offset), rcx); + } + } +} + + +void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { + int size = instr->hydrogen()->total_size(); + + // Allocate all objects that are part of the literal in one big + // allocation. This avoids multiple limit checks. + Label allocated, runtime_allocate; + __ AllocateInNewSpace(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ Push(Smi::FromInt(size)); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + + __ bind(&allocated); + int offset = 0; + __ LoadHeapObject(rbx, instr->hydrogen()->boilerplate()); + EmitDeepCopy(instr->hydrogen()->boilerplate(), rax, rbx, &offset); + ASSERT_EQ(size, offset); +} + + +void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { + Handle<FixedArray> constant_properties = + instr->hydrogen()->constant_properties(); + + // Set up the parameters to the stub/runtime call. __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ push(FieldOperand(rax, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(instr->hydrogen()->literal_index())); - __ Push(instr->hydrogen()->constant_properties()); - __ Push(Smi::FromInt(instr->hydrogen()->fast_elements() ? 1 : 0)); + __ Push(constant_properties); + int flags = instr->hydrogen()->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + flags |= instr->hydrogen()->has_function() + ? ObjectLiteral::kHasFunction + : ObjectLiteral::kNoFlags; + __ Push(Smi::FromInt(flags)); // Pick the right runtime function to call. + int properties_count = constant_properties->length() / 2; if (instr->hydrogen()->depth() > 1) { CallRuntime(Runtime::kCreateObjectLiteral, 4, instr); - } else { + } else if (flags != ObjectLiteral::kFastElements || + properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) { CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr); + } else { + FastCloneShallowObjectStub stub(properties_count); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } } @@ -3825,8 +4123,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { Handle<SharedFunctionInfo> shared_info = instr->shared_info(); bool pretenure = instr->hydrogen()->pretenure(); if (!pretenure && shared_info->num_literals() == 0) { - FastNewClosureStub stub( - shared_info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(shared_info->language_mode()); __ Push(shared_info); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); } else { @@ -3850,7 +4147,12 @@ void LCodeGen::DoTypeof(LTypeof* instr) { void LCodeGen::EmitPushTaggedOperand(LOperand* operand) { ASSERT(!operand->IsDoubleRegister()); if (operand->IsConstantOperand()) { - __ Push(ToHandle(LConstantOperand::cast(operand))); + Handle<Object> object = ToHandle(LConstantOperand::cast(operand)); + if (object->IsSmi()) { + __ Push(Handle<Smi>::cast(object)); + } else { + __ PushHeapObject(Handle<HeapObject>::cast(object)); + } } else if (operand->IsRegister()) { __ push(ToRegister(operand)); } else { @@ -3866,12 +4168,11 @@ void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* false_label = chunk_->GetAssemblyLabel(false_block); - Condition final_branch_condition = EmitTypeofIs(true_label, - false_label, - input, - instr->type_literal()); - - EmitBranch(true_block, false_block, final_branch_condition); + Condition final_branch_condition = + EmitTypeofIs(true_label, false_label, input, instr->type_literal()); + if (final_branch_condition != no_condition) { + EmitBranch(true_block, false_block, final_branch_condition); + } } @@ -3916,9 +4217,12 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = not_zero; } else if (type_name->Equals(heap()->function_symbol())) { + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ JumpIfSmi(input, false_label); - __ CmpObjectType(input, FIRST_CALLABLE_SPEC_OBJECT_TYPE, input); - final_branch_condition = above_equal; + __ CmpObjectType(input, JS_FUNCTION_TYPE, input); + __ j(equal, true_label); + __ CmpInstanceType(input, JS_FUNCTION_PROXY_TYPE); + final_branch_condition = equal; } else if (type_name->Equals(heap()->object_symbol())) { __ JumpIfSmi(input, false_label); @@ -3936,7 +4240,6 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, final_branch_condition = zero; } else { - final_branch_condition = never; __ jmp(false_label); } @@ -3978,11 +4281,7 @@ void LCodeGen::EnsureSpaceForLazyDeopt(int space_needed) { int current_pc = masm()->pc_offset(); if (current_pc < last_lazy_deopt_pc_ + space_needed) { int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; - while (padding_size > 0) { - int nop_size = padding_size > 9 ? 9 : padding_size; - __ nop(nop_size); - padding_size -= nop_size; - } + __ Nop(padding_size); } } @@ -4051,6 +4350,7 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) : LDeferredCode(codegen), instr_(instr) { } virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } + virtual LInstruction* instr() { return instr_; } private: LStackCheck* instr_; }; diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h index 43c045f7bc..2890c530b7 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.h +++ b/deps/v8/src/x64/lithium-codegen-x64.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -78,6 +78,7 @@ class LCodeGen BASE_EMBEDDED { XMMRegister ToDoubleRegister(LOperand* op) const; bool IsInteger32Constant(LConstantOperand* op) const; int ToInteger32(LConstantOperand* op) const; + double ToDouble(LConstantOperand* op) const; bool IsTaggedConstant(LConstantOperand* op) const; Handle<Object> ToHandle(LConstantOperand* op) const; Operand ToOperand(LOperand* op) const; @@ -101,7 +102,10 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); - // Parallel move support. + void DoCheckMapCommon(Register reg, Handle<Map> map, + CompareMapMode mode, LEnvironment* env); + +// Parallel move support. void DoParallelMove(LParallelMove* move); void DoGap(LGap* instr); @@ -126,8 +130,8 @@ class LCodeGen BASE_EMBEDDED { bool is_done() const { return status_ == DONE; } bool is_aborted() const { return status_ == ABORTED; } - int strict_mode_flag() const { - return info()->is_strict_mode() ? kStrictMode : kNonStrictMode; + StrictModeFlag strict_mode_flag() const { + return info()->is_classic_mode() ? kNonStrictMode : kStrictMode; } LChunk* chunk() const { return chunk_; } @@ -140,7 +144,8 @@ class LCodeGen BASE_EMBEDDED { Label* if_false, Handle<String> class_name, Register input, - Register temporary); + Register temporary, + Register scratch); int GetStackSlotCount() const { return chunk()->spill_slot_count(); } int GetParameterCount() const { return scope()->num_parameters(); } @@ -189,15 +194,13 @@ class LCodeGen BASE_EMBEDDED { int argc, LInstruction* instr); - // Generate a direct call to a known function. Expects the function - // to be in edi. + // to be in rdi. void CallKnownFunction(Handle<JSFunction> function, int arity, LInstruction* instr, CallKind call_kind); - void LoadHeapObject(Register result, Handle<HeapObject> object); void RecordSafepointWithLazyDeopt(LInstruction* instr, SafepointMode safepoint_mode, @@ -230,6 +233,7 @@ class LCodeGen BASE_EMBEDDED { void DoMathSqrt(LUnaryMathOperation* instr); void DoMathPowHalf(LUnaryMathOperation* instr); void DoMathLog(LUnaryMathOperation* instr); + void DoMathTan(LUnaryMathOperation* instr); void DoMathCos(LUnaryMathOperation* instr); void DoMathSin(LUnaryMathOperation* instr); @@ -248,17 +252,19 @@ class LCodeGen BASE_EMBEDDED { static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); void EmitBranch(int left_block, int right_block, Condition cc); - void EmitCmpI(LOperand* left, LOperand* right); void EmitNumberUntagD(Register input, XMMRegister result, bool deoptimize_on_undefined, + bool deoptimize_on_minus_zero, LEnvironment* env); // Emits optimized code for typeof x == "y". Modifies input register. // Returns the condition on which a final split to // true and false label should be made, to optimize fallthrough. - Condition EmitTypeofIs(Label* true_label, Label* false_label, - Register input, Handle<String> type_name); + Condition EmitTypeofIs(Label* true_label, + Label* false_label, + Register input, + Handle<String> type_name); // Emits optimized code for %_IsObject(x). Preserves input register. // Returns the condition on which a final split to @@ -267,6 +273,13 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsString(x). Preserves input register. + // Returns the condition on which a final split to + // true and false label should be made, to optimize fallthrough. + Condition EmitIsString(Register input, + Register temp1, + Label* is_not_string); + // Emits optimized code for %_IsConstructCall(). // Caller should branch on equal condition. void EmitIsConstructCall(Register temp); @@ -280,6 +293,13 @@ class LCodeGen BASE_EMBEDDED { // register, or a stack slot operand. void EmitPushTaggedOperand(LOperand* operand); + // Emits optimized code to deep-copy the contents of statically known + // object graphs (e.g. object literal boilerplate). + void EmitDeepCopy(Handle<JSObject> object, + Register result, + Register source, + int* offset); + struct JumpTableEntry { explicit inline JumpTableEntry(Address entry) : label(), @@ -346,16 +366,20 @@ class LCodeGen BASE_EMBEDDED { class LDeferredCode: public ZoneObject { public: explicit LDeferredCode(LCodeGen* codegen) - : codegen_(codegen), external_exit_(NULL) { + : codegen_(codegen), + external_exit_(NULL), + instruction_index_(codegen->current_instruction_) { codegen->AddDeferredCode(this); } virtual ~LDeferredCode() { } virtual void Generate() = 0; + virtual LInstruction* instr() = 0; - void SetExit(Label *exit) { external_exit_ = exit; } + void SetExit(Label* exit) { external_exit_ = exit; } Label* entry() { return &entry_; } Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; } + int instruction_index() const { return instruction_index_; } protected: LCodeGen* codegen() const { return codegen_; } @@ -366,6 +390,7 @@ class LDeferredCode: public ZoneObject { Label entry_; Label exit_; Label* external_exit_; + int instruction_index_; }; } } // namespace v8::internal diff --git a/deps/v8/src/x64/lithium-gap-resolver-x64.cc b/deps/v8/src/x64/lithium-gap-resolver-x64.cc index c3c617c456..bf5d31d72e 100644 --- a/deps/v8/src/x64/lithium-gap-resolver-x64.cc +++ b/deps/v8/src/x64/lithium-gap-resolver-x64.cc @@ -198,7 +198,7 @@ void LGapResolver::EmitMove(int index) { if (cgen_->IsInteger32Constant(constant_source)) { __ movl(dst, Immediate(cgen_->ToInteger32(constant_source))); } else { - __ Move(dst, cgen_->ToHandle(constant_source)); + __ LoadObject(dst, cgen_->ToHandle(constant_source)); } } else { ASSERT(destination->IsStackSlot()); @@ -207,7 +207,8 @@ void LGapResolver::EmitMove(int index) { // Allow top 32 bits of an untagged Integer32 to be arbitrary. __ movl(dst, Immediate(cgen_->ToInteger32(constant_source))); } else { - __ Move(dst, cgen_->ToHandle(constant_source)); + __ LoadObject(kScratchRegister, cgen_->ToHandle(constant_source)); + __ movq(dst, kScratchRegister); } } diff --git a/deps/v8/src/x64/lithium-x64.cc b/deps/v8/src/x64/lithium-x64.cc index 5fc56462bb..901e4b7dac 100644 --- a/deps/v8/src/x64/lithium-x64.cc +++ b/deps/v8/src/x64/lithium-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -214,10 +214,11 @@ void LCmpIDAndBranch::PrintDataTo(StringStream* stream) { } -void LIsNullAndBranch::PrintDataTo(StringStream* stream) { +void LIsNilAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if "); InputAt(0)->PrintTo(stream); - stream->Add(is_strict() ? " === null" : " == null"); + stream->Add(kind() == kStrictEquality ? " === " : " == "); + stream->Add(nil() == kNullValue ? "null" : "undefined"); stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); } @@ -229,6 +230,13 @@ void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { } +void LIsStringAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if is_string("); + InputAt(0)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LIsSmiAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_smi("); InputAt(0)->PrintTo(stream); @@ -243,6 +251,14 @@ void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { } +void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { + stream->Add("if string_compare("); + InputAt(0)->PrintTo(stream); + InputAt(1)->PrintTo(stream); + stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); +} + + void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if has_instance_type("); InputAt(0)->PrintTo(stream); @@ -446,6 +462,12 @@ void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) { } +void LTransitionElementsKind::PrintDataTo(StringStream* stream) { + object()->PrintTo(stream); + stream->Add(" %p -> %p", *original_map(), *transitioned_map()); +} + + void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) { LInstructionGap* gap = new LInstructionGap(block); int index = -1; @@ -552,11 +574,6 @@ void LChunkBuilder::Abort(const char* format, ...) { } -LRegister* LChunkBuilder::ToOperand(Register reg) { - return LRegister::Create(Register::ToAllocationIndex(reg)); -} - - LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { return new LUnallocated(LUnallocated::FIXED_REGISTER, Register::ToAllocationIndex(reg)); @@ -647,7 +664,7 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { HInstruction* instr = HInstruction::cast(value); VisitInstruction(instr); } - allocator_->RecordUse(value, operand); + operand->set_virtual_register(value->id()); return operand; } @@ -655,19 +672,13 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { template<int I, int T> LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result) { - allocator_->RecordDefinition(current_instruction_, result); + result->set_virtual_register(current_instruction_->id()); instr->set_result(result); return instr; } template<int I, int T> -LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr) { - return Define(instr, new LUnallocated(LUnallocated::NONE)); -} - - -template<int I, int T> LInstruction* LChunkBuilder::DefineAsRegister( LTemplateInstruction<1, I, T>* instr) { return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); @@ -706,7 +717,9 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - instr->set_environment(CreateEnvironment(hydrogen_env)); + int argument_index_accumulator = 0; + instr->set_environment(CreateEnvironment(hydrogen_env, + &argument_index_accumulator)); return instr; } @@ -736,7 +749,7 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, instr->MarkAsCall(); instr = AssignPointerMap(instr); - if (hinstr->HasSideEffects()) { + if (hinstr->HasObservableSideEffects()) { ASSERT(hinstr->next()->IsSimulate()); HSimulate* sim = HSimulate::cast(hinstr->next()); instr = SetInstructionPendingDeoptimizationEnvironment( @@ -748,7 +761,8 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, // Thus we still need to attach environment to this call even if // call sequence can not deoptimize eagerly. bool needs_environment = - (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || !hinstr->HasSideEffects(); + (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || + !hinstr->HasObservableSideEffects(); if (needs_environment && !instr->HasEnvironment()) { instr = AssignEnvironment(instr); } @@ -772,21 +786,22 @@ LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { LUnallocated* LChunkBuilder::TempRegister() { LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER); - allocator_->RecordTemporary(operand); + operand->set_virtual_register(allocator_->GetVirtualRegister()); + if (!allocator_->AllocationOk()) Abort("Not enough virtual registers."); return operand; } LOperand* LChunkBuilder::FixedTemp(Register reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) { LUnallocated* operand = ToUnallocated(reg); - allocator_->RecordTemporary(operand); + ASSERT(operand->HasFixedPolicy()); return operand; } @@ -806,28 +821,6 @@ LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { } -LInstruction* LChunkBuilder::DoBit(Token::Value op, - HBitwiseBinaryOperation* instr) { - if (instr->representation().IsInteger32()) { - ASSERT(instr->left()->representation().IsInteger32()); - ASSERT(instr->right()->representation().IsInteger32()); - - LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); - LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); - return DefineSameAsFirst(new LBitI(op, left, right)); - } else { - ASSERT(instr->representation().IsTagged()); - ASSERT(instr->left()->representation().IsTagged()); - ASSERT(instr->right()->representation().IsTagged()); - - LOperand* left = UseFixed(instr->left(), rdx); - LOperand* right = UseFixed(instr->right(), rax); - LArithmeticT* result = new LArithmeticT(op, left, right); - return MarkAsCall(DefineFixed(result, rax), instr); - } -} - - LInstruction* LChunkBuilder::DoShift(Token::Value op, HBitwiseBinaryOperation* instr) { if (instr->representation().IsTagged()) { @@ -989,20 +982,24 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { +LEnvironment* LChunkBuilder::CreateEnvironment( + HEnvironment* hydrogen_env, + int* argument_index_accumulator) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); + LEnvironment* outer = + CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber); + ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); int value_count = hydrogen_env->length(); LEnvironment* result = new LEnvironment(hydrogen_env->closure(), + hydrogen_env->is_arguments_adaptor(), ast_id, hydrogen_env->parameter_count(), argument_count_, value_count, outer); - int argument_index = 0; + int argument_index = *argument_index_accumulator; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1018,6 +1015,10 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { result->AddValue(op, value->representation()); } + if (!hydrogen_env->is_arguments_adaptor()) { + *argument_index_accumulator = argument_index; + } + return result; } @@ -1028,16 +1029,25 @@ LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { - HValue* v = instr->value(); - if (v->EmitAtUses()) { - ASSERT(v->IsConstant()); - ASSERT(!v->representation().IsDouble()); - HBasicBlock* successor = HConstant::cast(v)->ToBoolean() + HValue* value = instr->value(); + if (value->EmitAtUses()) { + ASSERT(value->IsConstant()); + ASSERT(!value->representation().IsDouble()); + HBasicBlock* successor = HConstant::cast(value)->ToBoolean() ? instr->FirstSuccessor() : instr->SecondSuccessor(); return new LGoto(successor->block_id()); } - return AssignEnvironment(new LBranch(UseRegister(v))); + + LBranch* result = new LBranch(UseRegister(value)); + // Tagged values that are not known smis or booleans require a + // deoptimization environment. + Representation rep = value->representation(); + HType type = value->type(); + if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean()) { + return AssignEnvironment(result); + } + return result; } @@ -1201,8 +1211,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) { LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { + LOperand* function = UseFixed(instr->function(), rdi); argument_count_ -= instr->argument_count(); - LCallFunction* result = new LCallFunction(); + LCallFunction* result = new LCallFunction(function); return MarkAsCall(DefineFixed(result, rax), instr); } @@ -1228,8 +1239,24 @@ LInstruction* LChunkBuilder::DoShl(HShl* instr) { } -LInstruction* LChunkBuilder::DoBitAnd(HBitAnd* instr) { - return DoBit(Token::BIT_AND, instr); +LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + + LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); + LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand()); + return DefineSameAsFirst(new LBitI(left, right)); + } else { + ASSERT(instr->representation().IsTagged()); + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + + LOperand* left = UseFixed(instr->left(), rdx); + LOperand* right = UseFixed(instr->right(), rax); + LArithmeticT* result = new LArithmeticT(instr->op(), left, right); + return MarkAsCall(DefineFixed(result, rax), instr); + } } @@ -1242,16 +1269,6 @@ LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) { } -LInstruction* LChunkBuilder::DoBitOr(HBitOr* instr) { - return DoBit(Token::BIT_OR, instr); -} - - -LInstruction* LChunkBuilder::DoBitXor(HBitXor* instr) { - return DoBit(Token::BIT_XOR, instr); -} - - LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { if (instr->representation().IsDouble()) { return DoArithmeticD(Token::DIV, instr); @@ -1317,7 +1334,11 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) { LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand()); LOperand* right = UseOrConstant(instr->MostConstantOperand()); LMulI* mul = new LMulI(left, right); - return AssignEnvironment(DefineSameAsFirst(mul)); + if (instr->CheckFlag(HValue::kCanOverflow) || + instr->CheckFlag(HValue::kBailoutOnMinusZero)) { + AssignEnvironment(mul); + } + return DefineSameAsFirst(mul); } else if (instr->representation().IsDouble()) { return DoArithmeticD(Token::MUL, instr); } else { @@ -1385,18 +1406,29 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { UseFixed(instr->right(), rdi); #endif LPower* result = new LPower(left, right); - return MarkAsCall(DefineFixedDouble(result, xmm1), instr, + return MarkAsCall(DefineFixedDouble(result, xmm3), instr, CAN_DEOPTIMIZE_EAGERLY); } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); +#ifdef _WIN64 + LOperand* global_object = UseFixed(instr->global_object(), rcx); +#else + LOperand* global_object = UseFixed(instr->global_object(), rdi); +#endif + LRandom* result = new LRandom(global_object); + return MarkAsCall(DefineFixedDouble(result, xmm1), instr); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { - Token::Value op = instr->token(); ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); - bool reversed = (op == Token::GT || op == Token::LTE); - LOperand* left = UseFixed(instr->left(), reversed ? rax : rdx); - LOperand* right = UseFixed(instr->right(), reversed ? rdx : rax); + LOperand* left = UseFixed(instr->left(), rdx); + LOperand* right = UseFixed(instr->right(), rax); LCmpT* result = new LCmpT(left, right); return MarkAsCall(DefineFixed(result, rax), instr); } @@ -1408,15 +1440,22 @@ LInstruction* LChunkBuilder::DoCompareIDAndBranch( if (r.IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); return new LCmpIDAndBranch(left, right); } else { ASSERT(r.IsDouble()); ASSERT(instr->left()->representation().IsDouble()); ASSERT(instr->right()->representation().IsDouble()); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); + LOperand* left; + LOperand* right; + if (instr->left()->IsConstant() && instr->right()->IsConstant()) { + left = UseRegisterOrConstantAtStart(instr->left()); + right = UseRegisterOrConstantAtStart(instr->right()); + } else { + left = UseRegisterAtStart(instr->left()); + right = UseRegisterAtStart(instr->right()); + } return new LCmpIDAndBranch(left, right); } } @@ -1436,10 +1475,10 @@ LInstruction* LChunkBuilder::DoCompareConstantEqAndBranch( } -LInstruction* LChunkBuilder::DoIsNullAndBranch(HIsNullAndBranch* instr) { +LInstruction* LChunkBuilder::DoIsNilAndBranch(HIsNilAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); - LOperand* temp = instr->is_strict() ? NULL : TempRegister(); - return new LIsNullAndBranch(UseRegisterAtStart(instr->value()), temp); + LOperand* temp = instr->kind() == kStrictEquality ? NULL : TempRegister(); + return new LIsNilAndBranch(UseRegisterAtStart(instr->value()), temp); } @@ -1449,6 +1488,13 @@ LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { } +LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) { + ASSERT(instr->value()->representation().IsTagged()); + LOperand* temp = TempRegister(); + return new LIsStringAndBranch(UseRegisterAtStart(instr->value()), temp); +} + + LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); return new LIsSmiAndBranch(Use(instr->value())); @@ -1463,6 +1509,19 @@ LInstruction* LChunkBuilder::DoIsUndetectableAndBranch( } +LInstruction* LChunkBuilder::DoStringCompareAndBranch( + HStringCompareAndBranch* instr) { + + ASSERT(instr->left()->representation().IsTagged()); + ASSERT(instr->right()->representation().IsTagged()); + LOperand* left = UseFixed(instr->left(), rdx); + LOperand* right = UseFixed(instr->right(), rax); + LStringCompareAndBranch* result = new LStringCompareAndBranch(left, right); + + return MarkAsCall(result, instr); +} + + LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch( HHasInstanceTypeAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); @@ -1488,7 +1547,8 @@ LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( LInstruction* LChunkBuilder::DoClassOfTestAndBranch( HClassOfTestAndBranch* instr) { - return new LClassOfTestAndBranch(UseTempRegister(instr->value()), + return new LClassOfTestAndBranch(UseRegister(instr->value()), + TempRegister(), TempRegister()); } @@ -1515,7 +1575,7 @@ LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LOperand* object = UseRegister(instr->value()); LValueOf* result = new LValueOf(object); - return AssignEnvironment(DefineSameAsFirst(result)); + return DefineSameAsFirst(result); } @@ -1716,7 +1776,7 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { LLoadGlobalCell* result = new LLoadGlobalCell; - return instr->check_hole_value() + return instr->RequiresHoleCheck() ? AssignEnvironment(DefineAsRegister(result)) : DefineAsRegister(result); } @@ -1730,9 +1790,12 @@ LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) { LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) { - LStoreGlobalCell* result = - new LStoreGlobalCell(UseRegister(instr->value()), TempRegister()); - return instr->check_hole_value() ? AssignEnvironment(result) : result; + LOperand* value = UseRegister(instr->value()); + // Use a temp to avoid reloading the cell value address in the case where + // we perform a hole check. + return instr->RequiresHoleCheck() + ? AssignEnvironment(new LStoreGlobalCell(value, TempRegister())) + : new LStoreGlobalCell(value, NULL); } @@ -1746,7 +1809,8 @@ LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) { LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { LOperand* context = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new LLoadContextSlot(context)); + LInstruction* result = DefineAsRegister(new LLoadContextSlot(context)); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } @@ -1763,7 +1827,8 @@ LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { value = UseRegister(instr->value()); temp = NULL; } - return new LStoreContextSlot(context, value, temp); + LInstruction* result = new LStoreContextSlot(context, value, temp); + return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; } @@ -1823,7 +1888,8 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastElement( LOperand* obj = UseRegisterAtStart(instr->object()); LOperand* key = UseRegisterOrConstantAtStart(instr->key()); LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key); - return AssignEnvironment(DefineAsRegister(result)); + if (instr->RequiresHoleCheck()) AssignEnvironment(result); + return DefineAsRegister(result); } @@ -1842,12 +1908,11 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ElementsKind elements_kind = instr->elements_kind(); - Representation representation(instr->representation()); ASSERT( - (representation.IsInteger32() && + (instr->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); @@ -1886,8 +1951,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( LOperand* key = needs_write_barrier ? UseTempRegister(instr->key()) : UseRegisterOrConstantAtStart(instr->key()); - - return AssignEnvironment(new LStoreKeyedFastElement(obj, key, val)); + return new LStoreKeyedFastElement(obj, key, val); } @@ -1907,13 +1971,12 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { - Representation representation(instr->value()->representation()); ElementsKind elements_kind = instr->elements_kind(); ASSERT( - (representation.IsInteger32() && + (instr->value()->representation().IsInteger32() && (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (representation.IsDouble() && + (instr->value()->representation().IsDouble() && ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); @@ -1948,6 +2011,27 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { } +LInstruction* LChunkBuilder::DoTransitionElementsKind( + HTransitionElementsKind* instr) { + if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && + instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + LOperand* object = UseRegister(instr->object()); + LOperand* new_map_reg = TempRegister(); + LOperand* temp_reg = TempRegister(); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, temp_reg); + return DefineSameAsFirst(result); + } else { + LOperand* object = UseFixed(instr->object(), rax); + LOperand* fixed_object_reg = FixedTemp(rdx); + LOperand* new_map_reg = FixedTemp(rbx); + LTransitionElementsKind* result = + new LTransitionElementsKind(object, new_map_reg, fixed_object_reg); + return MarkAsCall(DefineFixed(result, rax), instr); + } +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool needs_write_barrier = instr->NeedsWriteBarrier(); @@ -2010,8 +2094,14 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { } -LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteral, rax), instr); +LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralFast, rax), instr); +} + + +LInstruction* LChunkBuilder::DoObjectLiteralGeneric( + HObjectLiteralGeneric* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, rax), instr); } @@ -2149,6 +2239,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { HEnvironment* outer = current_block_->last_environment(); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner = outer->CopyForInlining(instr->closure(), + instr->arguments_count(), instr->function(), undefined, instr->call_kind()); @@ -2159,7 +2250,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { - HEnvironment* outer = current_block_->last_environment()->outer(); + HEnvironment* outer = current_block_->last_environment()-> + DiscardInlined(false); current_block_->UpdateEnvironment(outer); return NULL; } diff --git a/deps/v8/src/x64/lithium-x64.h b/deps/v8/src/x64/lithium-x64.h index d169bf6dfc..8fb259d969 100644 --- a/deps/v8/src/x64/lithium-x64.h +++ b/deps/v8/src/x64/lithium-x64.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -107,10 +107,12 @@ class LCodeGen; V(Integer32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ - V(IsNullAndBranch) \ + V(IsNilAndBranch) \ V(IsObjectAndBranch) \ + V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ + V(StringCompareAndBranch) \ V(JSArrayLength) \ V(Label) \ V(LazyBailout) \ @@ -132,12 +134,14 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteral) \ + V(ObjectLiteralFast) \ + V(ObjectLiteralGeneric) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(ShiftI) \ @@ -162,6 +166,7 @@ class LCodeGen; V(ThisFunction) \ V(Throw) \ V(ToFastProperties) \ + V(TransitionElementsKind) \ V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ @@ -609,17 +614,18 @@ class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> { }; -class LIsNullAndBranch: public LControlInstruction<1, 1> { +class LIsNilAndBranch: public LControlInstruction<1, 1> { public: - LIsNullAndBranch(LOperand* value, LOperand* temp) { + LIsNilAndBranch(LOperand* value, LOperand* temp) { inputs_[0] = value; temps_[0] = temp; } - DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch, "is-null-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsNullAndBranch) + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch) - bool is_strict() const { return hydrogen()->is_strict(); } + EqualityKind kind() const { return hydrogen()->kind(); } + NilValue nil() const { return hydrogen()->nil(); } virtual void PrintDataTo(StringStream* stream); }; @@ -638,6 +644,20 @@ class LIsObjectAndBranch: public LControlInstruction<1, 0> { }; +class LIsStringAndBranch: public LControlInstruction<1, 1> { + public: + explicit LIsStringAndBranch(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") + DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch) + + virtual void PrintDataTo(StringStream* stream); +}; + + class LIsSmiAndBranch: public LControlInstruction<1, 0> { public: explicit LIsSmiAndBranch(LOperand* value) { @@ -666,6 +686,23 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> { }; +class LStringCompareAndBranch: public LControlInstruction<2, 0> { + public: + explicit LStringCompareAndBranch(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, + "string-compare-and-branch") + DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) + + virtual void PrintDataTo(StringStream* stream); + + Token::Value op() const { return hydrogen()->token(); } +}; + + class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> { public: explicit LHasInstanceTypeAndBranch(LOperand* value) { @@ -705,11 +742,12 @@ class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> { }; -class LClassOfTestAndBranch: public LControlInstruction<1, 1> { +class LClassOfTestAndBranch: public LControlInstruction<1, 2> { public: - LClassOfTestAndBranch(LOperand* value, LOperand* temp) { + LClassOfTestAndBranch(LOperand* value, LOperand* temp, LOperand* temp2) { inputs_[0] = value; temps_[0] = temp; + temps_[1] = temp2; } DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch, @@ -790,18 +828,15 @@ class LBoundsCheck: public LTemplateInstruction<0, 2, 0> { class LBitI: public LTemplateInstruction<1, 2, 0> { public: - LBitI(Token::Value op, LOperand* left, LOperand* right) - : op_(op) { + LBitI(LOperand* left, LOperand* right) { inputs_[0] = left; inputs_[1] = right; } - Token::Value op() const { return op_; } + Token::Value op() const { return hydrogen()->op(); } DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") - - private: - Token::Value op_; + DECLARE_HYDROGEN_ACCESSOR(Bitwise) }; @@ -990,6 +1025,17 @@ class LPower: public LTemplateInstruction<1, 2, 0> { }; +class LRandom: public LTemplateInstruction<1, 1, 0> { + public: + explicit LRandom(LOperand* global_object) { + inputs_[0] = global_object; + } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD: public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) @@ -1206,6 +1252,8 @@ class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> { DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) + + LOperand* value() { return inputs_[0]; } }; @@ -1223,7 +1271,7 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> { LOperand* global_object() { return InputAt(0); } Handle<Object> name() const { return hydrogen()->name(); } LOperand* value() { return InputAt(1); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1257,7 +1305,6 @@ class LStoreContextSlot: public LTemplateInstruction<0, 2, 1> { LOperand* context() { return InputAt(0); } LOperand* value() { return InputAt(1); } int slot_index() { return hydrogen()->slot_index(); } - int needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } virtual void PrintDataTo(StringStream* stream); }; @@ -1274,7 +1321,9 @@ class LPushArgument: public LTemplateInstruction<0, 1, 0> { class LThisFunction: public LTemplateInstruction<1, 0, 0> { + public: DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function") + DECLARE_HYDROGEN_ACCESSOR(ThisFunction) }; @@ -1372,14 +1421,17 @@ class LCallNamed: public LTemplateInstruction<1, 0, 0> { }; -class LCallFunction: public LTemplateInstruction<1, 0, 0> { +class LCallFunction: public LTemplateInstruction<1, 1, 0> { public: - LCallFunction() {} + explicit LCallFunction(LOperand* function) { + inputs_[0] = function; + } DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") DECLARE_HYDROGEN_ACCESSOR(CallFunction) - int arity() const { return hydrogen()->argument_count() - 2; } + LOperand* function() { return inputs_[0]; } + int arity() const { return hydrogen()->argument_count() - 1; } }; @@ -1548,7 +1600,6 @@ class LStoreNamedField: public LTemplateInstruction<0, 2, 1> { Handle<Object> name() const { return hydrogen()->name(); } bool is_in_object() { return hydrogen()->is_in_object(); } int offset() { return hydrogen()->offset(); } - bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } Handle<Map> transition() const { return hydrogen()->transition(); } }; @@ -1568,7 +1619,7 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> { LOperand* object() { return inputs_[0]; } LOperand* value() { return inputs_[1]; } Handle<Object> name() const { return hydrogen()->name(); } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1653,7 +1704,31 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> { LOperand* object() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - bool strict_mode() { return hydrogen()->strict_mode(); } + StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } +}; + + +class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> { + public: + LTransitionElementsKind(LOperand* object, + LOperand* new_map_temp, + LOperand* temp_reg) { + inputs_[0] = object; + temps_[0] = new_map_temp; + temps_[1] = temp_reg; + } + + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, + "transition-elements-kind") + DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) + + virtual void PrintDataTo(StringStream* stream); + + LOperand* object() { return inputs_[0]; } + LOperand* new_map_reg() { return temps_[0]; } + LOperand* temp_reg() { return temps_[1]; } + Handle<Map> original_map() { return hydrogen()->original_map(); } + Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); } }; @@ -1719,6 +1794,8 @@ class LCheckFunction: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return InputAt(0); } + DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function") DECLARE_HYDROGEN_ACCESSOR(CheckFunction) }; @@ -1828,10 +1905,17 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { }; -class LObjectLiteral: public LTemplateInstruction<1, 0, 0> { +class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) +}; + + +class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) }; @@ -2059,7 +2143,6 @@ class LChunkBuilder BASE_EMBEDDED { void Abort(const char* format, ...); // Methods for getting operands for Use / Define / Temp. - LRegister* ToOperand(Register reg); LUnallocated* ToUnallocated(Register reg); LUnallocated* ToUnallocated(XMMRegister reg); @@ -2110,8 +2193,6 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* Define(LTemplateInstruction<1, I, T>* instr, LUnallocated* result); template<int I, int T> - LInstruction* Define(LTemplateInstruction<1, I, T>* instr); - template<int I, int T> LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr); template<int I, int T> LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr, @@ -2146,12 +2227,12 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, + int* argument_index_accumulator); void VisitInstruction(HInstruction* current); void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block); - LInstruction* DoBit(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr); LInstruction* DoArithmeticD(Token::Value op, HArithmeticBinaryOperation* instr); diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 8fcad23272..9b5b35511c 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -44,6 +44,7 @@ MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) : Assembler(arg_isolate, buffer, size), generating_stub_(false), allow_stub_calls_(true), + has_frame_(false), root_array_available_(true) { if (isolate() != NULL) { code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), @@ -54,7 +55,7 @@ MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) static intptr_t RootRegisterDelta(ExternalReference other, Isolate* isolate) { Address roots_register_value = kRootRegisterBias + - reinterpret_cast<Address>(isolate->heap()->roots_address()); + reinterpret_cast<Address>(isolate->heap()->roots_array_start()); intptr_t delta = other.address() - roots_register_value; return delta; } @@ -196,28 +197,47 @@ void MacroAssembler::CompareRoot(const Operand& with, } -void MacroAssembler::RecordWriteHelper(Register object, - Register addr, - Register scratch) { - if (emit_debug_code()) { - // Check that the object is not in new space. - Label not_in_new_space; - InNewSpace(object, scratch, not_equal, ¬_in_new_space, Label::kNear); - Abort("new-space object passed to RecordWriteHelper"); - bind(¬_in_new_space); +void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then) { + if (FLAG_debug_code) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear); + int3(); + bind(&ok); + } + // Load store buffer top. + LoadRoot(scratch, Heap::kStoreBufferTopRootIndex); + // Store pointer to buffer. + movq(Operand(scratch, 0), addr); + // Increment buffer top. + addq(scratch, Immediate(kPointerSize)); + // Write back new top of buffer. + StoreRoot(scratch, Heap::kStoreBufferTopRootIndex); + // Call stub on end of buffer. + Label done; + // Check for end of buffer. + testq(scratch, Immediate(StoreBuffer::kStoreBufferOverflowBit)); + if (and_then == kReturnAtEnd) { + Label buffer_overflowed; + j(not_equal, &buffer_overflowed, Label::kNear); + ret(0); + bind(&buffer_overflowed); + } else { + ASSERT(and_then == kFallThroughAtEnd); + j(equal, &done, Label::kNear); + } + StoreBufferOverflowStub store_buffer_overflow = + StoreBufferOverflowStub(save_fp); + CallStub(&store_buffer_overflow); + if (and_then == kReturnAtEnd) { + ret(0); + } else { + ASSERT(and_then == kFallThroughAtEnd); + bind(&done); } - - // Compute the page start address from the heap object pointer, and reuse - // the 'object' register for it. - and_(object, Immediate(~Page::kPageAlignmentMask)); - - // Compute number of region covering addr. See Page::GetRegionNumberForAddress - // method for more details. - shrl(addr, Immediate(Page::kRegionSizeLog2)); - andl(addr, Immediate(Page::kPageAlignmentMask >> Page::kRegionSizeLog2)); - - // Set dirty mark for region. - bts(Operand(object, Page::kDirtyFlagOffset), addr); } @@ -225,7 +245,7 @@ void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cc, Label* branch, - Label::Distance near_jump) { + Label::Distance distance) { if (Serializer::enabled()) { // Can't do arithmetic on external references if it might get serialized. // The mask isn't really an address. We load it as an external reference in @@ -240,7 +260,7 @@ void MacroAssembler::InNewSpace(Register object, } movq(kScratchRegister, ExternalReference::new_space_start(isolate())); cmpq(scratch, kScratchRegister); - j(cc, branch, near_jump); + j(cc, branch, distance); } else { ASSERT(is_int32(static_cast<int64_t>(HEAP->NewSpaceMask()))); intptr_t new_space_start = @@ -252,127 +272,162 @@ void MacroAssembler::InNewSpace(Register object, lea(scratch, Operand(object, kScratchRegister, times_1, 0)); } and_(scratch, Immediate(static_cast<int32_t>(HEAP->NewSpaceMask()))); - j(cc, branch, near_jump); + j(cc, branch, distance); } } -void MacroAssembler::RecordWrite(Register object, - int offset, - Register value, - Register index) { +void MacroAssembler::RecordWriteField( + Register object, + int offset, + Register value, + Register dst, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { // The compiled code assumes that record write doesn't change the // context register, so we check that none of the clobbered // registers are rsi. - ASSERT(!object.is(rsi) && !value.is(rsi) && !index.is(rsi)); + ASSERT(!value.is(rsi) && !dst.is(rsi)); // First, check if a write barrier is even needed. The tests below - // catch stores of smis and stores into the young generation. + // catch stores of Smis. Label done; - JumpIfSmi(value, &done); - RecordWriteNonSmi(object, offset, value, index); + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done); + } + + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + ASSERT(IsAligned(offset, kPointerSize)); + + lea(dst, FieldOperand(object, offset)); + if (emit_debug_code()) { + Label ok; + testb(dst, Immediate((1 << kPointerSizeLog2) - 1)); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); + } + + RecordWrite( + object, dst, value, save_fp, remembered_set_action, OMIT_SMI_CHECK); + bind(&done); - // Clobber all input registers when running with the debug-code flag - // turned on to provoke errors. This clobbering repeats the - // clobbering done inside RecordWriteNonSmi but it's necessary to - // avoid having the fast case for smis leave the registers - // unchanged. + // Clobber clobbered input registers when running with the debug-code flag + // turned on to provoke errors. if (emit_debug_code()) { - movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE); movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE); - movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE); + movq(dst, BitCast<int64_t>(kZapValue), RelocInfo::NONE); } } -void MacroAssembler::RecordWrite(Register object, - Register address, - Register value) { - // The compiled code assumes that record write doesn't change the - // context register, so we check that none of the clobbered - // registers are rsi. - ASSERT(!object.is(rsi) && !value.is(rsi) && !address.is(rsi)); - +void MacroAssembler::RecordWriteArray(Register object, + Register value, + Register index, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { // First, check if a write barrier is even needed. The tests below - // catch stores of smis and stores into the young generation. + // catch stores of Smis. Label done; - JumpIfSmi(value, &done); - InNewSpace(object, value, equal, &done); + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done); + } - RecordWriteHelper(object, address, value); + // Array access: calculate the destination address. Index is not a smi. + Register dst = index; + lea(dst, Operand(object, index, times_pointer_size, + FixedArray::kHeaderSize - kHeapObjectTag)); + + RecordWrite( + object, dst, value, save_fp, remembered_set_action, OMIT_SMI_CHECK); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered input registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE); - movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE); movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE); + movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE); } } -void MacroAssembler::RecordWriteNonSmi(Register object, - int offset, - Register scratch, - Register index) { - Label done; +void MacroAssembler::RecordWrite(Register object, + Register address, + Register value, + SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + // The compiled code assumes that record write doesn't change the + // context register, so we check that none of the clobbered + // registers are rsi. + ASSERT(!value.is(rsi) && !address.is(rsi)); + ASSERT(!object.is(value)); + ASSERT(!object.is(address)); + ASSERT(!value.is(address)); if (emit_debug_code()) { - Label okay; - JumpIfNotSmi(object, &okay, Label::kNear); - Abort("MacroAssembler::RecordWriteNonSmi cannot deal with smis"); - bind(&okay); - - if (offset == 0) { - // index must be int32. - Register tmp = index.is(rax) ? rbx : rax; - push(tmp); - movl(tmp, index); - cmpq(tmp, index); - Check(equal, "Index register for RecordWrite must be untagged int32."); - pop(tmp); - } + AbortIfSmi(object); } - // Test that the object address is not in the new space. We cannot - // update page dirty marks for new space pages. - InNewSpace(object, scratch, equal, &done); + if (remembered_set_action == OMIT_REMEMBERED_SET && + !FLAG_incremental_marking) { + return; + } - // The offset is relative to a tagged or untagged HeapObject pointer, - // so either offset or offset + kHeapObjectTag must be a - // multiple of kPointerSize. - ASSERT(IsAligned(offset, kPointerSize) || - IsAligned(offset + kHeapObjectTag, kPointerSize)); + if (FLAG_debug_code) { + Label ok; + cmpq(value, Operand(address, 0)); + j(equal, &ok, Label::kNear); + int3(); + bind(&ok); + } - Register dst = index; - if (offset != 0) { - lea(dst, Operand(object, offset)); - } else { - // array access: calculate the destination address in the same manner as - // KeyedStoreIC::GenerateGeneric. - lea(dst, FieldOperand(object, - index, - times_pointer_size, - FixedArray::kHeaderSize)); + // First, check if a write barrier is even needed. The tests below + // catch stores of smis and stores into the young generation. + Label done; + + if (smi_check == INLINE_SMI_CHECK) { + // Skip barrier if writing a smi. + JumpIfSmi(value, &done); } - RecordWriteHelper(object, dst, scratch); + + CheckPageFlag(value, + value, // Used as scratch. + MemoryChunk::kPointersToHereAreInterestingMask, + zero, + &done, + Label::kNear); + + CheckPageFlag(object, + value, // Used as scratch. + MemoryChunk::kPointersFromHereAreInterestingMask, + zero, + &done, + Label::kNear); + + RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); + CallStub(&stub); bind(&done); - // Clobber all input registers when running with the debug-code flag + // Clobber clobbered registers when running with the debug-code flag // turned on to provoke errors. if (emit_debug_code()) { - movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE); - movq(scratch, BitCast<int64_t>(kZapValue), RelocInfo::NONE); - movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE); + movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE); + movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE); } } + void MacroAssembler::Assert(Condition cc, const char* msg) { if (emit_debug_code()) Check(cc, msg); } @@ -400,7 +455,7 @@ void MacroAssembler::Check(Condition cc, const char* msg) { Label L; j(cc, &L, Label::kNear); Abort(msg); - // will not return here + // Control will not return here. bind(&L); } @@ -440,7 +495,7 @@ void MacroAssembler::Abort(const char* msg) { // from the real pointer as a smi. intptr_t p1 = reinterpret_cast<intptr_t>(msg); intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag; - // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag. + // Note: p0 might not be a valid Smi _value_, but it has a valid Smi tag. ASSERT(reinterpret_cast<Object*>(p0)->IsSmi()); #ifdef DEBUG if (msg != NULL) { @@ -448,9 +503,6 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif - // Disable stub call restrictions to always allow calls to abort. - AllowStubCallsScope allow_scope(this, true); - push(rax); movq(kScratchRegister, p0, RelocInfo::NONE); push(kScratchRegister); @@ -458,52 +510,44 @@ void MacroAssembler::Abort(const char* msg) { reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(p1 - p0))), RelocInfo::NONE); push(kScratchRegister); - CallRuntime(Runtime::kAbort, 2); - // will not return here + + if (!has_frame_) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + CallRuntime(Runtime::kAbort, 2); + } else { + CallRuntime(Runtime::kAbort, 2); + } + // Control will not return here. int3(); } void MacroAssembler::CallStub(CodeStub* stub, unsigned ast_id) { - ASSERT(allow_stub_calls()); // calls are not allowed in some stubs + ASSERT(AllowThisStubCall(stub)); // Calls are not allowed in some stubs Call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id); } -MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. - MaybeObject* result = stub->TryGetCode(); - if (!result->IsFailure()) { - call(Handle<Code>(Code::cast(result->ToObjectUnchecked())), - RelocInfo::CODE_TARGET); - } - return result; -} - - void MacroAssembler::TailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. + ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe()); Jump(stub->GetCode(), RelocInfo::CODE_TARGET); } -MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. - MaybeObject* result = stub->TryGetCode(); - if (!result->IsFailure()) { - jmp(Handle<Code>(Code::cast(result->ToObjectUnchecked())), - RelocInfo::CODE_TARGET); - } - return result; -} - - void MacroAssembler::StubReturn(int argc) { ASSERT(argc >= 1 && generating_stub()); ret((argc - 1) * kPointerSize); } +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { + if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false; + return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(); +} + + void MacroAssembler::IllegalOperation(int num_arguments) { if (num_arguments > 0) { addq(rsp, Immediate(num_arguments * kPointerSize)); @@ -540,18 +584,11 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { const Runtime::Function* function = Runtime::FunctionForId(id); Set(rax, function->nargs); LoadAddress(rbx, ExternalReference(function, isolate())); - CEntryStub ces(1); - ces.SaveDoubles(); + CEntryStub ces(1, kSaveFPRegs); CallStub(&ces); } -MaybeObject* MacroAssembler::TryCallRuntime(Runtime::FunctionId id, - int num_arguments) { - return TryCallRuntime(Runtime::FunctionForId(id), num_arguments); -} - - void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments) { // If the expected number of arguments of the runtime function is @@ -573,26 +610,6 @@ void MacroAssembler::CallRuntime(const Runtime::Function* f, } -MaybeObject* MacroAssembler::TryCallRuntime(const Runtime::Function* f, - int num_arguments) { - if (f->nargs >= 0 && f->nargs != num_arguments) { - IllegalOperation(num_arguments); - // Since we did not call the stub, there was no allocation failure. - // Return some non-failure object. - return HEAP->undefined_value(); - } - - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - Set(rax, num_arguments); - LoadAddress(rbx, ExternalReference(f, isolate())); - CEntryStub ces(f->result_size); - return TryCallStub(&ces); -} - - void MacroAssembler::CallExternalReference(const ExternalReference& ext, int num_arguments) { Set(rax, num_arguments); @@ -622,24 +639,6 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } -MaybeObject* MacroAssembler::TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size) { - // ----------- S t a t e ------------- - // -- rsp[0] : return address - // -- rsp[8] : argument num_arguments - 1 - // ... - // -- rsp[8 * num_arguments] : argument 0 (receiver) - // ----------------------------------- - - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - Set(rax, num_arguments); - return TryJumpToExternalReference(ext, result_size); -} - - void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -649,15 +648,6 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } -MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size) { - return TryTailCallExternalReference(ExternalReference(fid, isolate()), - num_arguments, - result_size); -} - - static int Offset(ExternalReference ref0, ExternalReference ref1) { int64_t offset = (ref0.address() - ref1.address()); // Check that fits into int. @@ -680,8 +670,8 @@ void MacroAssembler::PrepareCallApiFunction(int arg_stack_space) { } -MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( - ApiFunction* function, int stack_space) { +void MacroAssembler::CallApiFunctionAndReturn(Address function_address, + int stack_space) { Label empty_result; Label prologue; Label promote_scheduled_exception; @@ -711,8 +701,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( movq(prev_limit_reg, Operand(base_reg, kLimitOffset)); addl(Operand(base_reg, kLevelOffset), Immediate(1)); // Call the api function! - movq(rax, - reinterpret_cast<int64_t>(function->address()), + movq(rax, reinterpret_cast<int64_t>(function_address), RelocInfo::RUNTIME_ENTRY); call(rax); @@ -744,11 +733,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - MaybeObject* result = TryTailCallRuntime(Runtime::kPromoteScheduledException, - 0, 1); - if (result->IsFailure()) { - return result; - } + TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); bind(&empty_result); // It was zero; the result is undefined. @@ -769,8 +754,6 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( call(rax); movq(rax, prev_limit_reg); jmp(&leave_exit_frame); - - return result; } @@ -783,20 +766,11 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, } -MaybeObject* MacroAssembler::TryJumpToExternalReference( - const ExternalReference& ext, int result_size) { - // Set the entry point and jump to the C entry runtime stub. - LoadAddress(rbx, ext); - CEntryStub ces(result_size); - return TryTailCallStub(&ces); -} - - void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, const CallWrapper& call_wrapper) { - // Calls are not allowed in some stubs. - ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); + // You can't call a builtin without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); // Rely on the assertion to check that the number of provided // arguments match the expected number of arguments. Fake a @@ -825,6 +799,57 @@ void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { } +static const Register saved_regs[] = + { rax, rcx, rdx, rbx, rbp, rsi, rdi, r8, r9, r10, r11 }; +static const int kNumberOfSavedRegs = sizeof(saved_regs) / sizeof(Register); + + +void MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, + Register exclusion1, + Register exclusion2, + Register exclusion3) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + for (int i = 0; i < kNumberOfSavedRegs; i++) { + Register reg = saved_regs[i]; + if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { + push(reg); + } + } + // R12 to r15 are callee save on all platforms. + if (fp_mode == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + subq(rsp, Immediate(kDoubleSize * XMMRegister::kNumRegisters)); + for (int i = 0; i < XMMRegister::kNumRegisters; i++) { + XMMRegister reg = XMMRegister::from_code(i); + movsd(Operand(rsp, i * kDoubleSize), reg); + } + } +} + + +void MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, + Register exclusion1, + Register exclusion2, + Register exclusion3) { + if (fp_mode == kSaveFPRegs) { + CpuFeatures::Scope scope(SSE2); + for (int i = 0; i < XMMRegister::kNumRegisters; i++) { + XMMRegister reg = XMMRegister::from_code(i); + movsd(reg, Operand(rsp, i * kDoubleSize)); + } + addq(rsp, Immediate(kDoubleSize * XMMRegister::kNumRegisters)); + } + for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) { + Register reg = saved_regs[i]; + if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { + pop(reg); + } + } +} + + void MacroAssembler::Set(Register dst, int64_t x) { if (x == 0) { xorl(dst, dst); @@ -2089,7 +2114,7 @@ void MacroAssembler::JumpIfNotBothSequentialAsciiStrings( movzxbl(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset)); movzxbl(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset)); - // Check that both are flat ascii strings. + // Check that both are flat ASCII strings. ASSERT(kNotStringTag != 0); const int kFlatAsciiStringMask = kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; @@ -2135,7 +2160,7 @@ void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii( movq(scratch1, first_object_instance_type); movq(scratch2, second_object_instance_type); - // Check that both are flat ascii strings. + // Check that both are flat ASCII strings. ASSERT(kNotStringTag != 0); const int kFlatAsciiStringMask = kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; @@ -2213,6 +2238,43 @@ void MacroAssembler::Push(Handle<Object> source) { } +void MacroAssembler::LoadHeapObject(Register result, + Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + movq(result, cell, RelocInfo::GLOBAL_PROPERTY_CELL); + movq(result, Operand(result, 0)); + } else { + Move(result, object); + } +} + + +void MacroAssembler::PushHeapObject(Handle<HeapObject> object) { + if (isolate()->heap()->InNewSpace(*object)) { + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(object); + movq(kScratchRegister, cell, RelocInfo::GLOBAL_PROPERTY_CELL); + movq(kScratchRegister, Operand(kScratchRegister, 0)); + push(kScratchRegister); + } else { + Push(object); + } +} + + +void MacroAssembler::LoadGlobalCell(Register dst, + Handle<JSGlobalPropertyCell> cell) { + if (dst.is(rax)) { + load_rax(cell.location(), RelocInfo::GLOBAL_PROPERTY_CELL); + } else { + movq(dst, cell, RelocInfo::GLOBAL_PROPERTY_CELL); + movq(dst, Operand(dst, 0)); + } +} + + void MacroAssembler::Push(Smi* source) { intptr_t smi = reinterpret_cast<intptr_t>(source); if (is_int32(smi)) { @@ -2236,6 +2298,13 @@ void MacroAssembler::Test(const Operand& src, Smi* source) { } +void MacroAssembler::TestBit(const Operand& src, int bits) { + int byte_offset = bits / kBitsPerByte; + int bit_in_byte = bits & (kBitsPerByte - 1); + testb(Operand(src, byte_offset), Immediate(1 << bit_in_byte)); +} + + void MacroAssembler::Jump(ExternalReference ext) { LoadAddress(kScratchRegister, ext); jmp(kScratchRegister); @@ -2384,87 +2453,102 @@ Operand MacroAssembler::SafepointRegisterSlot(Register reg) { } -void MacroAssembler::PushTryHandler(CodeLocation try_location, - HandlerType type) { +void MacroAssembler::PushTryHandler(StackHandler::Kind kind, + int handler_index) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - - // The pc (return address) is already on TOS. This code pushes state, - // frame pointer, context, and current handler. - if (try_location == IN_JAVASCRIPT) { - if (type == TRY_CATCH_HANDLER) { - push(Immediate(StackHandler::TRY_CATCH)); - } else { - push(Immediate(StackHandler::TRY_FINALLY)); - } - push(rbp); - push(rsi); - } else { - ASSERT(try_location == IN_JS_ENTRY); - // The frame pointer does not point to a JS frame so we save NULL - // for rbp. We expect the code throwing an exception to check rbp - // before dereferencing it to restore the context. - push(Immediate(StackHandler::ENTRY)); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // We will build up the handler from the bottom by pushing on the stack. + // First push the frame pointer and context. + if (kind == StackHandler::JS_ENTRY) { + // The frame pointer does not point to a JS frame so we save NULL for + // rbp. We expect the code throwing an exception to check rbp before + // dereferencing it to restore the context. push(Immediate(0)); // NULL frame pointer. Push(Smi::FromInt(0)); // No context. + } else { + push(rbp); + push(rsi); } - // Save the current handler. - Operand handler_operand = - ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate())); - push(handler_operand); - // Link this handler. - movq(handler_operand, rsp); + + // Push the state and the code object. + unsigned state = + StackHandler::IndexField::encode(handler_index) | + StackHandler::KindField::encode(kind); + push(Immediate(state)); + Push(CodeObject()); + + // Link the current handler as the next handler. + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + push(ExternalOperand(handler_address)); + // Set this new handler as the current one. + movq(ExternalOperand(handler_address), rsp); } void MacroAssembler::PopTryHandler() { - ASSERT_EQ(0, StackHandlerConstants::kNextOffset); - // Unlink this handler. - Operand handler_operand = - ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate())); - pop(handler_operand); - // Remove the remaining fields. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + pop(ExternalOperand(handler_address)); addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize)); } +void MacroAssembler::JumpToHandlerEntry() { + // Compute the handler entry address and jump to it. The handler table is + // a fixed array of (smi-tagged) code offsets. + // rax = exception, rdi = code object, rdx = state. + movq(rbx, FieldOperand(rdi, Code::kHandlerTableOffset)); + shr(rdx, Immediate(StackHandler::kKindWidth)); + movq(rdx, FieldOperand(rbx, rdx, times_8, FixedArray::kHeaderSize)); + SmiToInteger64(rdx, rdx); + lea(rdi, FieldOperand(rdi, rdx, times_1, Code::kHeaderSize)); + jmp(rdi); +} + + void MacroAssembler::Throw(Register value) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // Keep thrown value in rax. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // The exception is expected in rax. if (!value.is(rax)) { movq(rax, value); } - + // Drop the stack pointer to the top of the top handler. ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); - Operand handler_operand = ExternalOperand(handler_address); - movq(rsp, handler_operand); - // get next in chain - pop(handler_operand); + movq(rsp, ExternalOperand(handler_address)); + // Restore the next handler. + pop(ExternalOperand(handler_address)); + + // Remove the code object and state, compute the handler address in rdi. + pop(rdi); // Code object. + pop(rdx); // Offset and state. + + // Restore the context and frame pointer. pop(rsi); // Context. pop(rbp); // Frame pointer. - pop(rdx); // State. // If the handler is a JS frame, restore the context to the frame. - // (rdx == ENTRY) == (rbp == 0) == (rsi == 0), so we could test any - // of them. + // (kind == ENTRY) == (rbp == 0) == (rsi == 0), so we could test either + // rbp or rsi. Label skip; - cmpq(rdx, Immediate(StackHandler::ENTRY)); - j(equal, &skip, Label::kNear); + testq(rsi, rsi); + j(zero, &skip, Label::kNear); movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); bind(&skip); - ret(0); + JumpToHandlerEntry(); } @@ -2472,40 +2556,17 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Register value) { // Adjust this code if not the case. STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); - // Keep thrown value in rax. - if (!value.is(rax)) { - movq(rax, value); - } - // Fetch top stack handler. - ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); - Load(rsp, handler_address); - - // Unwind the handlers until the ENTRY handler is found. - Label loop, done; - bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY)); - j(equal, &done, Label::kNear); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - movq(rsp, Operand(rsp, kNextOffset)); - jmp(&loop); - bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - Operand handler_operand = ExternalOperand(handler_address); - pop(handler_operand); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + // The exception is expected in rax. if (type == OUT_OF_MEMORY) { // Set external caught exception to false. - ExternalReference external_caught( - Isolate::kExternalCaughtExceptionAddress, isolate()); + ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress, + isolate()); Set(rax, static_cast<int64_t>(false)); Store(external_caught, rax); @@ -2514,16 +2575,38 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, isolate()); movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); Store(pending_exception, rax); + } else if (!value.is(rax)) { + movq(rax, value); } - // Discard the context saved in the handler and clear the context pointer. - pop(rdx); - Set(rsi, 0); + // Drop the stack pointer to the top of the top stack handler. + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); + Load(rsp, handler_address); - pop(rbp); // Restore frame pointer. - pop(rdx); // Discard state. + // Unwind the handlers until the top ENTRY handler is found. + Label fetch_next, check_kind; + jmp(&check_kind, Label::kNear); + bind(&fetch_next); + movq(rsp, Operand(rsp, StackHandlerConstants::kNextOffset)); - ret(0); + bind(&check_kind); + STATIC_ASSERT(StackHandler::JS_ENTRY == 0); + testl(Operand(rsp, StackHandlerConstants::kStateOffset), + Immediate(StackHandler::KindField::kMask)); + j(not_zero, &fetch_next); + + // Set the top handler address to next handler past the top ENTRY handler. + pop(ExternalOperand(handler_address)); + + // Remove the code object and state, compute the handler address in rdi. + pop(rdi); // Code object. + pop(rdx); // Offset and state. + + // Clear the context pointer and frame pointer (0 was saved in the handler). + pop(rsi); + pop(rbp); + + JumpToHandlerEntry(); } @@ -2567,22 +2650,133 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Immediate(Map::kMaximumBitField2FastElementValue)); + j(above, fail, distance); +} + + +void MacroAssembler::CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + j(below_equal, fail, distance); cmpb(FieldOperand(map, Map::kBitField2Offset), Immediate(Map::kMaximumBitField2FastElementValue)); j(above, fail, distance); } +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + cmpb(FieldOperand(map, Map::kBitField2Offset), + Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + j(above, fail, distance); +} + + +void MacroAssembler::StoreNumberToDoubleElements( + Register maybe_number, + Register elements, + Register index, + XMMRegister xmm_scratch, + Label* fail) { + Label smi_value, is_nan, maybe_nan, not_nan, have_double_value, done; + + JumpIfSmi(maybe_number, &smi_value, Label::kNear); + + CheckMap(maybe_number, + isolate()->factory()->heap_number_map(), + fail, + DONT_DO_SMI_CHECK); + + // Double value, canonicalize NaN. + uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32); + cmpl(FieldOperand(maybe_number, offset), + Immediate(kNaNOrInfinityLowerBoundUpper32)); + j(greater_equal, &maybe_nan, Label::kNear); + + bind(¬_nan); + movsd(xmm_scratch, FieldOperand(maybe_number, HeapNumber::kValueOffset)); + bind(&have_double_value); + movsd(FieldOperand(elements, index, times_8, FixedDoubleArray::kHeaderSize), + xmm_scratch); + jmp(&done); + + bind(&maybe_nan); + // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise + // it's an Infinity, and the non-NaN code path applies. + j(greater, &is_nan, Label::kNear); + cmpl(FieldOperand(maybe_number, HeapNumber::kValueOffset), Immediate(0)); + j(zero, ¬_nan); + bind(&is_nan); + // Convert all NaNs to the same canonical NaN value when they are stored in + // the double array. + Set(kScratchRegister, BitCast<uint64_t>( + FixedDoubleArray::canonical_not_the_hole_nan_as_double())); + movq(xmm_scratch, kScratchRegister); + jmp(&have_double_value, Label::kNear); + + bind(&smi_value); + // Value is a smi. convert to a double and store. + // Preserve original value. + SmiToInteger32(kScratchRegister, maybe_number); + cvtlsi2sd(xmm_scratch, kScratchRegister); + movsd(FieldOperand(elements, index, times_8, FixedDoubleArray::kHeaderSize), + xmm_scratch); + bind(&done); +} + + +void MacroAssembler::CompareMap(Register obj, + Handle<Map> map, + Label* early_success, + CompareMapMode mode) { + Cmp(FieldOperand(obj, HeapObject::kMapOffset), map); + if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { + Map* transitioned_fast_element_map( + map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); + ASSERT(transitioned_fast_element_map == NULL || + map->elements_kind() != FAST_ELEMENTS); + if (transitioned_fast_element_map != NULL) { + j(equal, early_success, Label::kNear); + Cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle<Map>(transitioned_fast_element_map)); + } + + Map* transitioned_double_map( + map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); + ASSERT(transitioned_double_map == NULL || + map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); + if (transitioned_double_map != NULL) { + j(equal, early_success, Label::kNear); + Cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle<Map>(transitioned_double_map)); + } + } +} + + void MacroAssembler::CheckMap(Register obj, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type) { + SmiCheckType smi_check_type, + CompareMapMode mode) { if (smi_check_type == DO_SMI_CHECK) { JumpIfSmi(obj, fail); } - Cmp(FieldOperand(obj, HeapObject::kMapOffset), map); + + Label success; + CompareMap(obj, map, &success, mode); j(not_equal, fail); + bind(&success); } @@ -2707,7 +2901,8 @@ Condition MacroAssembler::IsObjectStringType(Register heap_object, void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, - Label* miss) { + Label* miss, + bool miss_on_bound_function) { // Check that the receiver isn't a smi. testl(function, Immediate(kSmiTagMask)); j(zero, miss); @@ -2716,6 +2911,17 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, CmpObjectType(function, JS_FUNCTION_TYPE, result); j(not_equal, miss); + if (miss_on_bound_function) { + movq(kScratchRegister, + FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); + // It's not smi-tagged (stored in the top half of a smi-tagged 8-byte + // field). + TestBit(FieldOperand(kScratchRegister, + SharedFunctionInfo::kCompilerHintsOffset), + SharedFunctionInfo::kBoundFunction); + j(not_zero, miss); + } + // Make sure that the function has an instance prototype. Label non_instance; testb(FieldOperand(result, Map::kBitFieldOffset), @@ -2787,10 +2993,10 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) { #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::DebugBreak() { - ASSERT(allow_stub_calls()); Set(rax, 0); // No arguments. LoadAddress(rbx, ExternalReference(Runtime::kDebugBreak, isolate())); CEntryStub ces(1); + ASSERT(AllowThisStubCall(&ces)); Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); } #endif // ENABLE_DEBUGGER_SUPPORT @@ -2816,27 +3022,34 @@ void MacroAssembler::InvokeCode(Register code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; + bool definitely_mismatches = false; InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, + &definitely_mismatches, flag, Label::kNear, call_wrapper, call_kind); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - SetCallKind(rcx, call_kind); - call(code); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(rcx, call_kind); - jmp(code); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + SetCallKind(rcx, call_kind); + call(code); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(rcx, call_kind); + jmp(code); + } + bind(&done); } - bind(&done); } @@ -2847,28 +3060,35 @@ void MacroAssembler::InvokeCode(Handle<Code> code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; + bool definitely_mismatches = false; Register dummy = rax; InvokePrologue(expected, actual, code, dummy, &done, + &definitely_mismatches, flag, Label::kNear, call_wrapper, call_kind); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - SetCallKind(rcx, call_kind); - Call(code, rmode); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - SetCallKind(rcx, call_kind); - Jump(code, rmode); + if (!definitely_mismatches) { + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + SetCallKind(rcx, call_kind); + Call(code, rmode); + call_wrapper.AfterCall(); + } else { + ASSERT(flag == JUMP_FUNCTION); + SetCallKind(rcx, call_kind); + Jump(code, rmode); + } + bind(&done); } - bind(&done); } @@ -2877,6 +3097,9 @@ void MacroAssembler::InvokeFunction(Register function, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + ASSERT(function.is(rdi)); movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); movq(rsi, FieldOperand(function, JSFunction::kContextOffset)); @@ -2891,34 +3114,24 @@ void MacroAssembler::InvokeFunction(Register function, } -void MacroAssembler::InvokeFunction(JSFunction* function, +void MacroAssembler::InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { - ASSERT(function->is_compiled()); + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + // Get the function and setup the context. - Move(rdi, Handle<JSFunction>(function)); + LoadHeapObject(rdi, function); movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - if (V8::UseCrankshaft()) { - // Since Crankshaft can recompile a function, we need to load - // the Code object every time we call the function. - movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); - ParameterCount expected(function->shared()->formal_parameter_count()); - InvokeCode(rdx, expected, actual, flag, call_wrapper, call_kind); - } else { - // Invoke the cached code. - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - InvokeCode(code, - expected, - actual, - RelocInfo::CODE_TARGET, - flag, - call_wrapper, - call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); + ParameterCount expected(function->shared()->formal_parameter_count()); + InvokeCode(rdx, expected, actual, flag, call_wrapper, call_kind); } @@ -2927,11 +3140,13 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, Handle<Code> code_constant, Register code_register, Label* done, + bool* definitely_mismatches, InvokeFlag flag, Label::Distance near_jump, const CallWrapper& call_wrapper, CallKind call_kind) { bool definitely_matches = false; + *definitely_mismatches = false; Label invoke; if (expected.is_immediate()) { ASSERT(actual.is_immediate()); @@ -2947,6 +3162,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, // arguments. definitely_matches = true; } else { + *definitely_mismatches = true; Set(rbx, expected.immediate()); } } @@ -2983,7 +3199,9 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, SetCallKind(rcx, call_kind); Call(adaptor, RelocInfo::CODE_TARGET); call_wrapper.AfterCall(); - jmp(done, near_jump); + if (!*definitely_mismatches) { + jmp(done, near_jump); + } } else { SetCallKind(rcx, call_kind); Jump(adaptor, RelocInfo::CODE_TARGET); @@ -3022,7 +3240,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { void MacroAssembler::EnterExitFramePrologue(bool save_rax) { - // Setup the frame structure on the stack. + // Set up the frame structure on the stack. // All constants are relative to the frame pointer of the exit frame. ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); @@ -3082,7 +3300,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space, void MacroAssembler::EnterExitFrame(int arg_stack_space, bool save_doubles) { EnterExitFramePrologue(true); - // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame, + // Set up argv in callee-saved register r15. It is reused in LeaveExitFrame, // so it must be retained across the C-call. int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; lea(r15, Operand(rbp, r14, times_pointer_size, offset)); @@ -3616,7 +3834,7 @@ void MacroAssembler::AllocateAsciiString(Register result, subq(scratch1, Immediate(kHeaderAlignment)); } - // Allocate ascii string in new space. + // Allocate ASCII string in new space. AllocateInNewSpace(SeqAsciiString::kHeaderSize, times_1, scratch1, @@ -3772,6 +3990,20 @@ void MacroAssembler::CopyBytes(Register destination, } +void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler) { + Label loop, entry; + jmp(&entry); + bind(&loop); + movq(Operand(start_offset, 0), filler); + addq(start_offset, Immediate(kPointerSize)); + bind(&entry); + cmpq(start_offset, end_offset); + j(less, &loop); +} + + void MacroAssembler::LoadContext(Register dst, int context_chain_length) { if (context_chain_length > 0) { // Move up the chain of contexts to the context containing the slot. @@ -3797,6 +4029,46 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } } + +void MacroAssembler::LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match) { + // Load the global or builtins object from the current context. + movq(scratch, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); + + // Check that the function's map is the same as the expected cached map. + int expected_index = + Context::GetContextMapIndexFromElementsKind(expected_kind); + cmpq(map_in_out, Operand(scratch, Context::SlotOffset(expected_index))); + j(not_equal, no_map_match); + + // Use the transitioned cached map. + int trans_index = + Context::GetContextMapIndexFromElementsKind(transitioned_kind); + movq(map_in_out, Operand(scratch, Context::SlotOffset(trans_index))); +} + + +void MacroAssembler::LoadInitialArrayMap( + Register function_in, Register scratch, Register map_out) { + ASSERT(!function_in.is(map_out)); + Label done; + movq(map_out, FieldOperand(function_in, + JSFunction::kPrototypeOrInitialMapOffset)); + if (!FLAG_smi_only_arrays) { + LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + map_out, + scratch, + &done); + } + bind(&done); +} + #ifdef _WIN64 static const int kRegisterPassedArguments = 4; #else @@ -3871,6 +4143,7 @@ void MacroAssembler::CallCFunction(ExternalReference function, void MacroAssembler::CallCFunction(Register function, int num_arguments) { + ASSERT(has_frame()); // Check stack alignment. if (emit_debug_code()) { CheckStackAlignment(); @@ -3885,6 +4158,17 @@ void MacroAssembler::CallCFunction(Register function, int num_arguments) { } +bool AreAliased(Register r1, Register r2, Register r3, Register r4) { + if (r1.is(r2)) return true; + if (r1.is(r3)) return true; + if (r1.is(r4)) return true; + if (r2.is(r3)) return true; + if (r2.is(r4)) return true; + if (r3.is(r4)) return true; + return false; +} + + CodePatcher::CodePatcher(byte* address, int size) : address_(address), size_(size), @@ -3905,6 +4189,195 @@ CodePatcher::~CodePatcher() { ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); } + +void MacroAssembler::CheckPageFlag( + Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance) { + ASSERT(cc == zero || cc == not_zero); + if (scratch.is(object)) { + and_(scratch, Immediate(~Page::kPageAlignmentMask)); + } else { + movq(scratch, Immediate(~Page::kPageAlignmentMask)); + and_(scratch, object); + } + if (mask < (1 << kBitsPerByte)) { + testb(Operand(scratch, MemoryChunk::kFlagsOffset), + Immediate(static_cast<uint8_t>(mask))); + } else { + testl(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); + } + j(cc, condition_met, condition_met_distance); +} + + +void MacroAssembler::JumpIfBlack(Register object, + Register bitmap_scratch, + Register mask_scratch, + Label* on_black, + Label::Distance on_black_distance) { + ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, rcx)); + GetMarkBits(object, bitmap_scratch, mask_scratch); + + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + // The mask_scratch register contains a 1 at the position of the first bit + // and a 0 at all other positions, including the position of the second bit. + movq(rcx, mask_scratch); + // Make rcx into a mask that covers both marking bits using the operation + // rcx = mask | (mask << 1). + lea(rcx, Operand(mask_scratch, mask_scratch, times_2, 0)); + // Note that we are using a 4-byte aligned 8-byte load. + and_(rcx, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + cmpq(mask_scratch, rcx); + j(equal, on_black, on_black_distance); +} + + +// Detect some, but not all, common pointer-free objects. This is used by the +// incremental write barrier which doesn't care about oddballs (they are always +// marked black immediately so this code is not hit). +void MacroAssembler::JumpIfDataObject( + Register value, + Register scratch, + Label* not_data_object, + Label::Distance not_data_object_distance) { + Label is_data_object; + movq(scratch, FieldOperand(value, HeapObject::kMapOffset)); + CompareRoot(scratch, Heap::kHeapNumberMapRootIndex); + j(equal, &is_data_object, Label::kNear); + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + testb(FieldOperand(scratch, Map::kInstanceTypeOffset), + Immediate(kIsIndirectStringMask | kIsNotStringMask)); + j(not_zero, not_data_object, not_data_object_distance); + bind(&is_data_object); +} + + +void MacroAssembler::GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg) { + ASSERT(!AreAliased(addr_reg, bitmap_reg, mask_reg, rcx)); + movq(bitmap_reg, addr_reg); + // Sign extended 32 bit immediate. + and_(bitmap_reg, Immediate(~Page::kPageAlignmentMask)); + movq(rcx, addr_reg); + int shift = + Bitmap::kBitsPerCellLog2 + kPointerSizeLog2 - Bitmap::kBytesPerCellLog2; + shrl(rcx, Immediate(shift)); + and_(rcx, + Immediate((Page::kPageAlignmentMask >> shift) & + ~(Bitmap::kBytesPerCell - 1))); + + addq(bitmap_reg, rcx); + movq(rcx, addr_reg); + shrl(rcx, Immediate(kPointerSizeLog2)); + and_(rcx, Immediate((1 << Bitmap::kBitsPerCellLog2) - 1)); + movl(mask_reg, Immediate(1)); + shl_cl(mask_reg); +} + + +void MacroAssembler::EnsureNotWhite( + Register value, + Register bitmap_scratch, + Register mask_scratch, + Label* value_is_white_and_not_data, + Label::Distance distance) { + ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, rcx)); + GetMarkBits(value, bitmap_scratch, mask_scratch); + + // If the value is black or grey we don't need to do anything. + ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); + ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); + ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); + ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + Label done; + + // Since both black and grey have a 1 in the first position and white does + // not have a 1 there we only need to check one bit. + testq(Operand(bitmap_scratch, MemoryChunk::kHeaderSize), mask_scratch); + j(not_zero, &done, Label::kNear); + + if (FLAG_debug_code) { + // Check for impossible bit pattern. + Label ok; + push(mask_scratch); + // shl. May overflow making the check conservative. + addq(mask_scratch, mask_scratch); + testq(Operand(bitmap_scratch, MemoryChunk::kHeaderSize), mask_scratch); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); + pop(mask_scratch); + } + + // Value is white. We check whether it is data that doesn't need scanning. + // Currently only checks for HeapNumber and non-cons strings. + Register map = rcx; // Holds map while checking type. + Register length = rcx; // Holds length of object after checking type. + Label not_heap_number; + Label is_data_object; + + // Check for heap-number + movq(map, FieldOperand(value, HeapObject::kMapOffset)); + CompareRoot(map, Heap::kHeapNumberMapRootIndex); + j(not_equal, ¬_heap_number, Label::kNear); + movq(length, Immediate(HeapNumber::kSize)); + jmp(&is_data_object, Label::kNear); + + bind(¬_heap_number); + // Check for strings. + ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); + ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); + // If it's a string and it's not a cons string then it's an object containing + // no GC pointers. + Register instance_type = rcx; + movzxbl(instance_type, FieldOperand(map, Map::kInstanceTypeOffset)); + testb(instance_type, Immediate(kIsIndirectStringMask | kIsNotStringMask)); + j(not_zero, value_is_white_and_not_data); + // It's a non-indirect (non-cons and non-slice) string. + // If it's external, the length is just ExternalString::kSize. + // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). + Label not_external; + // External strings are the only ones with the kExternalStringTag bit + // set. + ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); + ASSERT_EQ(0, kConsStringTag & kExternalStringTag); + testb(instance_type, Immediate(kExternalStringTag)); + j(zero, ¬_external, Label::kNear); + movq(length, Immediate(ExternalString::kSize)); + jmp(&is_data_object, Label::kNear); + + bind(¬_external); + // Sequential string, either ASCII or UC16. + ASSERT(kAsciiStringTag == 0x04); + and_(length, Immediate(kStringEncodingMask)); + xor_(length, Immediate(kStringEncodingMask)); + addq(length, Immediate(0x04)); + // Value now either 4 (if ASCII) or 8 (if UC16), i.e. char-size shifted by 2. + imul(length, FieldOperand(value, String::kLengthOffset)); + shr(length, Immediate(2 + kSmiTagSize + kSmiShiftSize)); + addq(length, Immediate(SeqString::kHeaderSize + kObjectAlignmentMask)); + and_(length, Immediate(~kObjectAlignmentMask)); + + bind(&is_data_object); + // Value is a data object, and it is white. Mark it black. Since we know + // that the object is white we can make it black by flipping one bit. + or_(Operand(bitmap_scratch, MemoryChunk::kHeaderSize), mask_scratch); + + and_(bitmap_scratch, Immediate(~Page::kPageAlignmentMask)); + addl(Operand(bitmap_scratch, MemoryChunk::kLiveBytesOffset), length); + + bind(&done); +} + } } // namespace v8::internal #endif // V8_TARGET_ARCH_X64 diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index ff6edc5b33..52b58153ae 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,7 @@ #define V8_X64_MACRO_ASSEMBLER_X64_H_ #include "assembler.h" +#include "frames.h" #include "v8globals.h" namespace v8 { @@ -49,18 +50,23 @@ enum AllocationFlags { // Default scratch register used by MacroAssembler (and other code that needs // a spare register). The register isn't callee save, and not used by the // function calling convention. -static const Register kScratchRegister = { 10 }; // r10. -static const Register kSmiConstantRegister = { 12 }; // r12 (callee save). -static const Register kRootRegister = { 13 }; // r13 (callee save). +const Register kScratchRegister = { 10 }; // r10. +const Register kSmiConstantRegister = { 12 }; // r12 (callee save). +const Register kRootRegister = { 13 }; // r13 (callee save). // Value of smi in kSmiConstantRegister. -static const int kSmiConstantRegisterValue = 1; +const int kSmiConstantRegisterValue = 1; // Actual value of root register is offset from the root array's start // to take advantage of negitive 8-bit displacement values. -static const int kRootRegisterBias = 128; +const int kRootRegisterBias = 128; // Convenience for platform-independent signatures. typedef Operand MemOperand; +enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; + +bool AreAliased(Register r1, Register r2, Register r3, Register r4); + // Forward declaration. class JumpTarget; @@ -72,6 +78,7 @@ struct SmiIndex { ScaleFactor scale; }; + // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: @@ -134,56 +141,145 @@ class MacroAssembler: public Assembler { void CompareRoot(const Operand& with, Heap::RootListIndex index); void PushRoot(Heap::RootListIndex index); - // --------------------------------------------------------------------------- - // GC Support - - // For page containing |object| mark region covering |addr| dirty. - // RecordWriteHelper only works if the object is not in new - // space. - void RecordWriteHelper(Register object, - Register addr, - Register scratch); - - // Check if object is in new space. The condition cc can be equal or - // not_equal. If it is equal a jump will be done if the object is on new - // space. The register scratch can be object itself, but it will be clobbered. - void InNewSpace(Register object, - Register scratch, - Condition cc, - Label* branch, - Label::Distance near_jump = Label::kFar); + // These functions do not arrange the registers in any particular order so + // they are not useful for calls that can cause a GC. The caller can + // exclude up to 3 registers that do not need to be saved and restored. + void PushCallerSaved(SaveFPRegsMode fp_mode, + Register exclusion1 = no_reg, + Register exclusion2 = no_reg, + Register exclusion3 = no_reg); + void PopCallerSaved(SaveFPRegsMode fp_mode, + Register exclusion1 = no_reg, + Register exclusion2 = no_reg, + Register exclusion3 = no_reg); + +// --------------------------------------------------------------------------- +// GC Support + + + enum RememberedSetFinalAction { + kReturnAtEnd, + kFallThroughAtEnd + }; - // For page containing |object| mark region covering [object+offset] - // dirty. |object| is the object being stored into, |value| is the - // object being stored. If |offset| is zero, then the |scratch| - // register contains the array index into the elements array - // represented as an untagged 32-bit integer. All registers are - // clobbered by the operation. RecordWrite filters out smis so it - // does not update the write barrier if the value is a smi. - void RecordWrite(Register object, - int offset, - Register value, - Register scratch); - - // For page containing |object| mark region covering [address] + // Record in the remembered set the fact that we have a pointer to new space + // at the address pointed to by the addr register. Only works if addr is not + // in new space. + void RememberedSetHelper(Register object, // Used for debug code. + Register addr, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then); + + void CheckPageFlag(Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Check if object is in new space. Jumps if the object is not in new space. + // The register scratch can be object itself, but scratch will be clobbered. + void JumpIfNotInNewSpace(Register object, + Register scratch, + Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, not_equal, branch, distance); + } + + // Check if object is in new space. Jumps if the object is in new space. + // The register scratch can be object itself, but it will be clobbered. + void JumpIfInNewSpace(Register object, + Register scratch, + Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, equal, branch, distance); + } + + // Check if an object has the black incremental marking color. Also uses rcx! + void JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black, + Label::Distance on_black_distance = Label::kFar); + + // Detects conservatively whether an object is data-only, i.e. it does need to + // be scanned by the garbage collector. + void JumpIfDataObject(Register value, + Register scratch, + Label* not_data_object, + Label::Distance not_data_object_distance); + + // Checks the color of an object. If the object is already grey or black + // then we just fall through, since it is already live. If it is white and + // we can determine that it doesn't need to be scanned, then we just mark it + // black and fall through. For the rest we jump to the label so the + // incremental marker can fix its assumptions. + void EnsureNotWhite(Register object, + Register scratch1, + Register scratch2, + Label* object_is_white_and_not_data, + Label::Distance distance); + + // Notify the garbage collector that we wrote a pointer into an object. + // |object| is the object being stored into, |value| is the object being + // stored. value and scratch registers are clobbered by the operation. + // The offset is the offset from the start of the object, not the offset from + // the tagged HeapObject pointer. For use with FieldOperand(reg, off). + void RecordWriteField( + Register object, + int offset, + Register value, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // As above, but the offset has the tag presubtracted. For use with + // Operand(reg, off). + void RecordWriteContextSlot( + Register context, + int offset, + Register value, + Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK) { + RecordWriteField(context, + offset + kHeapObjectTag, + value, + scratch, + save_fp, + remembered_set_action, + smi_check); + } + + // Notify the garbage collector that we wrote a pointer into a fixed array. + // |array| is the array being stored into, |value| is the + // object being stored. |index| is the array index represented as a non-smi. + // All registers are clobbered by the operation RecordWriteArray + // filters out smis so it does not update the write barrier if the + // value is a smi. + void RecordWriteArray( + Register array, + Register value, + Register index, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // For page containing |object| mark region covering |address| // dirty. |object| is the object being stored into, |value| is the - // object being stored. All registers are clobbered by the + // object being stored. The address and value registers are clobbered by the // operation. RecordWrite filters out smis so it does not update // the write barrier if the value is a smi. - void RecordWrite(Register object, - Register address, - Register value); - - // For page containing |object| mark region covering [object+offset] dirty. - // The value is known to not be a smi. - // object is the object being stored into, value is the object being stored. - // If offset is zero, then the scratch register contains the array index into - // the elements array represented as an untagged 32-bit integer. - // All registers are clobbered by the operation. - void RecordWriteNonSmi(Register object, - int offset, - Register value, - Register scratch); + void RecordWrite( + Register object, + Register address, + Register value, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); #ifdef ENABLE_DEBUGGER_SUPPORT // --------------------------------------------------------------------------- @@ -192,15 +288,6 @@ class MacroAssembler: public Assembler { void DebugBreak(); #endif - // --------------------------------------------------------------------------- - // Activation frames - - void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); } - void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); } - - void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); } - void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); } - // Enter specific kind of exit frame; either in normal or // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer @@ -232,16 +319,16 @@ class MacroAssembler: public Assembler { void LoadFromSafepointRegisterSlot(Register dst, Register src); void InitializeRootRegister() { - ExternalReference roots_address = - ExternalReference::roots_address(isolate()); - movq(kRootRegister, roots_address); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + movq(kRootRegister, roots_array_start); addq(kRootRegister, Immediate(kRootRegisterBias)); } // --------------------------------------------------------------------------- // JavaScript invokes - // Setup call kind marking in rcx. The method takes rcx as an + // Set up call kind marking in rcx. The method takes rcx as an // explicit first parameter to make the code more readable at the // call sites. void SetCallKind(Register dst, CallKind kind); @@ -270,7 +357,7 @@ class MacroAssembler: public Assembler { const CallWrapper& call_wrapper, CallKind call_kind); - void InvokeFunction(JSFunction* function, + void InvokeFunction(Handle<JSFunction> function, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper, @@ -639,6 +726,7 @@ class MacroAssembler: public Assembler { void Push(Smi* smi); void Test(const Operand& dst, Smi* source); + // --------------------------------------------------------------------------- // String macros. @@ -657,7 +745,7 @@ class MacroAssembler: public Assembler { Label* on_not_both_flat_ascii, Label::Distance near_jump = Label::kFar); - // Check whether the instance type represents a flat ascii string. Jump to the + // Check whether the instance type represents a flat ASCII string. Jump to the // label if not. If the instance type can be scratched specify same register // for both instance type and scratch. void JumpIfInstanceTypeIsNotSequentialAscii( @@ -684,6 +772,9 @@ class MacroAssembler: public Assembler { // Move if the registers are not identical. void Move(Register target, Register source); + // Bit-field support. + void TestBit(const Operand& dst, int bit_index); + // Handle support void Move(Register dst, Handle<Object> source); void Move(const Operand& dst, Handle<Object> source); @@ -693,6 +784,22 @@ class MacroAssembler: public Assembler { void Cmp(const Operand& dst, Smi* src); void Push(Handle<Object> source); + // Load a heap object and handle the case of new-space objects by + // indirecting via a global cell. + void LoadHeapObject(Register result, Handle<HeapObject> object); + void PushHeapObject(Handle<HeapObject> object); + + void LoadObject(Register result, Handle<Object> object) { + if (object->IsHeapObject()) { + LoadHeapObject(result, Handle<HeapObject>::cast(object)); + } else { + Move(result, object); + } + } + + // Load a global cell into a register. + void LoadGlobalCell(Register dst, Handle<JSGlobalPropertyCell> cell); + // Emit code to discard a non-negative number of pointer-sized elements // from the stack, clobbering only the rsp register. void Drop(int stack_elements); @@ -760,13 +867,46 @@ class MacroAssembler: public Assembler { Label* fail, Label::Distance distance = Label::kFar); - // Check if the map of an object is equal to a specified map and - // branch to label if not. Skip the smi check if not required - // (object is known to be a heap object) + // Check if a map for a JSObject indicates that the object can have both smi + // and HeapObject elements. Jump to the specified label if it does not. + void CheckFastObjectElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + + // Check if a map for a JSObject indicates that the object has fast smi only + // elements. Jump to the specified label if it does not. + void CheckFastSmiOnlyElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); + + // Check to see if maybe_number can be stored as a double in + // FastDoubleElements. If it can, store it at the index specified by index in + // the FastDoubleElements array elements, otherwise jump to fail. Note that + // index must not be smi-tagged. + void StoreNumberToDoubleElements(Register maybe_number, + Register elements, + Register index, + XMMRegister xmm_scratch, + Label* fail); + + // Compare an object's map with the specified map and its transitioned + // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. FLAGS are set with + // result of map compare. If multiple map compares are required, the compare + // sequences branches to early_success. + void CompareMap(Register obj, + Handle<Map> map, + Label* early_success, + CompareMapMode mode = REQUIRE_EXACT_MAP); + + // Check if the map of an object is equal to a specified map and branch to + // label if not. Skip the smi check if not required (object is known to be a + // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match + // against maps that are ElementsKind transition maps of the specified map. void CheckMap(Register obj, Handle<Map> map, Label* fail, - SmiCheckType smi_check_type); + SmiCheckType smi_check_type, + CompareMapMode mode = REQUIRE_EXACT_MAP); // Check if the map of an object is equal to a specified map and branch to a // specified target if equal. Skip the smi check if not required (object is @@ -820,9 +960,8 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Exception handling - // Push a new try handler and link into try handler chain. The return - // address must be pushed before calling this helper. - void PushTryHandler(CodeLocation try_location, HandlerType type); + // Push a new try handler and link it into try handler chain. + void PushTryHandler(StackHandler::Kind kind, int handler_index); // Unlink the stack handler on top of the stack from the try handler chain. void PopTryHandler(); @@ -966,7 +1105,8 @@ class MacroAssembler: public Assembler { // clobbered. void TryGetFunctionPrototype(Register function, Register result, - Label* miss); + Label* miss, + bool miss_on_bound_function = false); // Generates code for reporting that an illegal operation has // occurred. @@ -981,6 +1121,22 @@ class MacroAssembler: public Assembler { // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); + // Conditionally load the cached Array transitioned map of type + // transitioned_kind from the global context if the map in register + // map_in_out is the cached Array map in the global context of + // expected_kind. + void LoadTransitionedArrayMapConditional( + ElementsKind expected_kind, + ElementsKind transitioned_kind, + Register map_in_out, + Register scratch, + Label* no_map_match); + + // Load the initial map for new Arrays from a JSFunction. + void LoadInitialArrayMap(Register function_in, + Register scratch, + Register map_out); + // Load the global function with the given index. void LoadGlobalFunction(int index, Register function); @@ -994,19 +1150,9 @@ class MacroAssembler: public Assembler { // Call a code stub. void CallStub(CodeStub* stub, unsigned ast_id = kNoASTId); - // Call a code stub and return the code object called. Try to generate - // the code if necessary. Do not perform a GC but instead return a retry - // after GC failure. - MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub); - // Tail call a code stub (jump). void TailCallStub(CodeStub* stub); - // Tail call a code stub (jump) and return the code object called. Try to - // generate the code if necessary. Do not perform a GC but instead return - // a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryTailCallStub(CodeStub* stub); - // Return from a code stub after popping its arguments. void StubReturn(int argc); @@ -1016,19 +1162,9 @@ class MacroAssembler: public Assembler { // Call a runtime function and save the value of XMM registers. void CallRuntimeSaveDoubles(Runtime::FunctionId id); - // Call a runtime function, returning the CodeStub object called. - // Try to generate the stub code if necessary. Do not perform a GC - // but instead return a retry after GC failure. - MUST_USE_RESULT MaybeObject* TryCallRuntime(const Runtime::Function* f, - int num_arguments); - // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); - // Convenience function: Same as above, but takes the fid instead. - MUST_USE_RESULT MaybeObject* TryCallRuntime(Runtime::FunctionId id, - int num_arguments); - // Convenience function: call an external reference. void CallExternalReference(const ExternalReference& ext, int num_arguments); @@ -1040,38 +1176,26 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); - MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( - const ExternalReference& ext, int num_arguments, int result_size); - // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); - MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size); - // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext, int result_size); - // Jump to a runtime routine. - MaybeObject* TryJumpToExternalReference(const ExternalReference& ext, - int result_size); - - // Prepares stack to put arguments (aligns and so on). - // WIN64 calling convention requires to put the pointer to the return value - // slot into rcx (rcx must be preserverd until TryCallApiFunctionAndReturn). - // Saves context (rsi). Clobbers rax. Allocates arg_stack_space * kPointerSize + // Prepares stack to put arguments (aligns and so on). WIN64 calling + // convention requires to put the pointer to the return value slot into + // rcx (rcx must be preserverd until CallApiFunctionAndReturn). Saves + // context (rsi). Clobbers rax. Allocates arg_stack_space * kPointerSize // inside the exit frame (not GCed) accessible via StackSpaceOperand. void PrepareCallApiFunction(int arg_stack_space); - // Calls an API function. Allocates HandleScope, extracts - // returned value from handle and propagates exceptions. - // Clobbers r14, r15, rbx and caller-save registers. Restores context. - // On return removes stack_space * kPointerSize (GCed). - MUST_USE_RESULT MaybeObject* TryCallApiFunctionAndReturn( - ApiFunction* function, int stack_space); + // Calls an API function. Allocates HandleScope, extracts returned value + // from handle and propagates exceptions. Clobbers r14, r15, rbx and + // caller-save registers. Restores context. On return removes + // stack_space * kPointerSize (GCed). + void CallApiFunctionAndReturn(Address function_address, int stack_space); // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], @@ -1120,6 +1244,13 @@ class MacroAssembler: public Assembler { int min_length = 0, Register scratch = kScratchRegister); + // Initialize fields with filler values. Fields starting at |start_offset| + // not including end_offset are overwritten with the value in |filler|. At + // the end the loop, |start_offset| takes the value of |end_offset|. + void InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler); + // --------------------------------------------------------------------------- // StatsCounter support @@ -1152,11 +1283,18 @@ class MacroAssembler: public Assembler { bool generating_stub() { return generating_stub_; } void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } bool allow_stub_calls() { return allow_stub_calls_; } + void set_has_frame(bool value) { has_frame_ = value; } + bool has_frame() { return has_frame_; } + inline bool AllowThisStubCall(CodeStub* stub); static int SafepointRegisterStackIndex(Register reg) { return SafepointRegisterStackIndex(reg.code()); } + // Activation support. + void EnterFrame(StackFrame::Type type); + void LeaveFrame(StackFrame::Type type); + private: // Order general registers are pushed by Pushad. // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15. @@ -1166,6 +1304,7 @@ class MacroAssembler: public Assembler { bool generating_stub_; bool allow_stub_calls_; + bool has_frame_; bool root_array_available_; // Returns a register holding the smi value. The register MUST NOT be @@ -1184,15 +1323,12 @@ class MacroAssembler: public Assembler { Handle<Code> code_constant, Register code_register, Label* done, + bool* definitely_mismatches, InvokeFlag flag, Label::Distance near_jump = Label::kFar, const CallWrapper& call_wrapper = NullCallWrapper(), CallKind call_kind = CALL_AS_METHOD); - // Activation support. - void EnterFrame(StackFrame::Type type); - void LeaveFrame(StackFrame::Type type); - void EnterExitFramePrologue(bool save_rax); // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack @@ -1219,6 +1355,24 @@ class MacroAssembler: public Assembler { Register scratch, bool gc_allowed); + // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. + void InNewSpace(Register object, + Register scratch, + Condition cc, + Label* branch, + Label::Distance distance = Label::kFar); + + // Helper for finding the mark bits for an address. Afterwards, the + // bitmap register points at the word with the mark bits and the mask + // the position of the first bit. Uses rcx as scratch and leaves addr_reg + // unchanged. + inline void GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg); + + // Helper for throwing exceptions. Compute a handler address and jump to + // it. See the implementation for register usage. + void JumpToHandlerEntry(); // Compute memory operands for safepoint stack slots. Operand SafepointRegisterSlot(Register reg); @@ -1256,32 +1410,32 @@ class CodePatcher { // Static helper functions. // Generate an Operand for loading a field from an object. -static inline Operand FieldOperand(Register object, int offset) { +inline Operand FieldOperand(Register object, int offset) { return Operand(object, offset - kHeapObjectTag); } // Generate an Operand for loading an indexed field from an object. -static inline Operand FieldOperand(Register object, - Register index, - ScaleFactor scale, - int offset) { +inline Operand FieldOperand(Register object, + Register index, + ScaleFactor scale, + int offset) { return Operand(object, index, scale, offset - kHeapObjectTag); } -static inline Operand ContextOperand(Register context, int index) { +inline Operand ContextOperand(Register context, int index) { return Operand(context, Context::SlotOffset(index)); } -static inline Operand GlobalObjectOperand() { +inline Operand GlobalObjectOperand() { return ContextOperand(rsi, Context::GLOBAL_INDEX); } // Provides access to exit frame stack space (not GCed). -static inline Operand StackSpaceOperand(int index) { +inline Operand StackSpaceOperand(int index) { #ifdef _WIN64 const int kShaddowSpace = 4; return Operand(rsp, (index + kShaddowSpace) * kPointerSize); diff --git a/deps/v8/src/x64/regexp-macro-assembler-x64.cc b/deps/v8/src/x64/regexp-macro-assembler-x64.cc index a782bd7052..16730d21bf 100644 --- a/deps/v8/src/x64/regexp-macro-assembler-x64.cc +++ b/deps/v8/src/x64/regexp-macro-assembler-x64.cc @@ -193,7 +193,7 @@ void RegExpMacroAssemblerX64::CheckCharacterGT(uc16 limit, Label* on_greater) { void RegExpMacroAssemblerX64::CheckAtStart(Label* on_at_start) { Label not_at_start; // Did we start the match at the start of the string at all? - __ cmpb(Operand(rbp, kStartIndex), Immediate(0)); + __ cmpl(Operand(rbp, kStartIndex), Immediate(0)); BranchOrBacktrack(not_equal, ¬_at_start); // If we did, are we still at the start of the input? __ lea(rax, Operand(rsi, rdi, times_1, 0)); @@ -205,7 +205,7 @@ void RegExpMacroAssemblerX64::CheckAtStart(Label* on_at_start) { void RegExpMacroAssemblerX64::CheckNotAtStart(Label* on_not_at_start) { // Did we start the match at the start of the string at all? - __ cmpb(Operand(rbp, kStartIndex), Immediate(0)); + __ cmpl(Operand(rbp, kStartIndex), Immediate(0)); BranchOrBacktrack(not_equal, on_not_at_start); // If we did, are we still at the start of the input? __ lea(rax, Operand(rsi, rdi, times_1, 0)); @@ -226,7 +226,7 @@ void RegExpMacroAssemblerX64::CheckCharacters(Vector<const uc16> str, bool check_end_of_string) { #ifdef DEBUG // If input is ASCII, don't even bother calling here if the string to - // match contains a non-ascii character. + // match contains a non-ASCII character. if (mode_ == ASCII) { ASSERT(String::IsAscii(str.start(), str.length())); } @@ -431,9 +431,14 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase( // Isolate. __ LoadAddress(rcx, ExternalReference::isolate_address()); #endif - ExternalReference compare = - ExternalReference::re_case_insensitive_compare_uc16(masm_.isolate()); - __ CallCFunction(compare, num_arguments); + + { // NOLINT: Can't find a way to open this scope without confusing the + // linter. + AllowExternalCallThatCantCauseGC scope(&masm_); + ExternalReference compare = + ExternalReference::re_case_insensitive_compare_uc16(masm_.isolate()); + __ CallCFunction(compare, num_arguments); + } // Restore original values before reacting on result value. __ Move(code_object_pointer(), masm_.CodeObject()); @@ -706,7 +711,12 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { // registers we need. // Entry code: __ bind(&entry_label_); - // Start new stack frame. + + // Tell the system that we have a stack frame. Because the type is MANUAL, no + // is generated. + FrameScope scope(&masm_, StackFrame::MANUAL); + + // Actually emit code to start a new stack frame. __ push(rbp); __ movq(rbp, rsp); // Save parameters and callee-save registers. Order here should correspond @@ -1238,6 +1248,11 @@ int RegExpMacroAssemblerX64::CheckStackGuardState(Address* return_address, frame_entry<const String*>(re_frame, kInputString) = *subject; frame_entry<const byte*>(re_frame, kInputStart) = new_address; frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length; + } else if (frame_entry<const String*>(re_frame, kInputString) != *subject) { + // Subject string might have been a ConsString that underwent + // short-circuiting during GC. That will not change start_address but + // will change pointer inside the subject handle. + frame_entry<const String*>(re_frame, kInputString) = *subject; } return 0; diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 76d2555798..6e609934c0 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -82,13 +82,12 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( - MacroAssembler* masm, - Label* miss_label, - Register receiver, - String* name, - Register r0, - Register r1) { +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle<String> name, + Register r0, + Register r1) { ASSERT(name->IsSymbol()); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->negative_lookups(), 1); @@ -118,19 +117,14 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( __ j(not_equal, miss_label); Label done; - MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup( - masm, - miss_label, - &done, - properties, - name, - r1); - if (result->IsFailure()) return result; - + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + properties, + name, + r1); __ bind(&done); __ DecrementCounter(counters->negative_lookups_miss(), 1); - - return result; } @@ -211,7 +205,10 @@ void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, int index, Register prototype, Label* miss) { + MacroAssembler* masm, + int index, + Register prototype, + Label* miss) { Isolate* isolate = masm->isolate(); // Check we're still in the same context. __ Move(prototype, isolate->global()); @@ -219,8 +216,8 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( prototype); __ j(not_equal, miss); // Get the global function with the given index. - JSFunction* function = - JSFunction::cast(isolate->global_context()->get(index)); + Handle<JSFunction> function( + JSFunction::cast(isolate->global_context()->get(index))); // Load its initial map. The global functions all have initial maps. __ Move(prototype, Handle<Map>(function->initial_map())); // Load the prototype from the initial map. @@ -312,8 +309,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle<JSObject> holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -333,11 +332,11 @@ static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, Register name, - JSObject* holder_obj) { + Handle<JSObject> holder_obj) { __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!masm->isolate()->heap()->InNewSpace(interceptor)); - __ Move(kScratchRegister, Handle<Object>(interceptor)); + Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); + ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); + __ Move(kScratchRegister, interceptor); __ push(kScratchRegister); __ push(receiver); __ push(holder); @@ -345,11 +344,12 @@ static void PushInterceptorArguments(MacroAssembler* masm, } -static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { +static void CompileCallLoadPropertyWithInterceptor( + MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + Handle<JSObject> holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); ExternalReference ref = @@ -403,9 +403,9 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // Generates call to API function. -static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, - const CallOptimization& optimization, - int argc) { +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + int argc) { // ----------- S t a t e ------------- // -- rsp[0] : return address // -- rsp[8] : object passing the type check @@ -420,29 +420,25 @@ static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, // -- rsp[(argc + 4) * 8] : receiver // ----------------------------------- // Get the function and setup the context. - JSFunction* function = optimization.constant_function(); - __ Move(rdi, Handle<JSFunction>(function)); + Handle<JSFunction> function = optimization.constant_function(); + __ LoadHeapObject(rdi, function); __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); // Pass the additional arguments. __ movq(Operand(rsp, 2 * kPointerSize), rdi); - Object* call_data = optimization.api_call_info()->data(); - Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); - if (masm->isolate()->heap()->InNewSpace(call_data)) { - __ Move(rcx, api_call_info_handle); + Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); + Handle<Object> call_data(api_call_info->data()); + if (masm->isolate()->heap()->InNewSpace(*call_data)) { + __ Move(rcx, api_call_info); __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kDataOffset)); __ movq(Operand(rsp, 3 * kPointerSize), rbx); } else { - __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(call_data)); + __ Move(Operand(rsp, 3 * kPointerSize), call_data); } // Prepare arguments. __ lea(rbx, Operand(rsp, 3 * kPointerSize)); - Object* callback = optimization.api_call_info()->callback(); - Address api_function_address = v8::ToCData<Address>(callback); - ApiFunction fun(api_function_address); - #ifdef _WIN64 // Win64 uses first register--rcx--for returned value. Register arguments_arg = rdx; @@ -465,12 +461,11 @@ static MaybeObject* GenerateFastApiCall(MacroAssembler* masm, // v8::InvocationCallback's argument. __ lea(arguments_arg, StackSpaceOperand(0)); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. - return masm->TryCallApiFunctionAndReturn(&fun, - argc + kFastApiCallArguments + 1); + + // Function address is a foreign pointer outside V8's heap. + Address function_address = v8::ToCData<Address>(api_call_info->callback()); + __ CallApiFunctionAndReturn(function_address, + argc + kFastApiCallArguments + 1); } @@ -485,16 +480,16 @@ class CallInterceptorCompiler BASE_EMBEDDED { name_(name), extra_ic_state_(extra_ic_state) {} - MaybeObject* Compile(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - LookupResult* lookup, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Label* miss) { + void Compile(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Label* miss) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -502,45 +497,27 @@ class CallInterceptorCompiler BASE_EMBEDDED { __ JumpIfSmi(receiver, miss); CallOptimization optimization(lookup); - if (optimization.is_constant_call()) { - return CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, + holder, lookup, name, optimization, miss); } else { - CompileRegular(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - name, - holder, - miss); - return masm->isolate()->heap()->undefined_value(); // Success. + CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, + name, holder, miss); } } private: - MaybeObject* CompileCacheable(MacroAssembler* masm, - JSObject* object, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - JSObject* interceptor_holder, - LookupResult* lookup, - String* name, - const CallOptimization& optimization, - Label* miss_label) { + void CompileCacheable(MacroAssembler* masm, + Handle<JSObject> object, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<JSObject> interceptor_holder, + LookupResult* lookup, + Handle<String> name, + const CallOptimization& optimization, + Label* miss_label) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); @@ -549,16 +526,14 @@ class CallInterceptorCompiler BASE_EMBEDDED { bool can_do_fast_api_call = false; if (optimization.is_simple_api_call() && !lookup->holder()->IsGlobalObject()) { - depth1 = - optimization.GetPrototypeDepthOfExpectedType(object, - interceptor_holder); + depth1 = optimization.GetPrototypeDepthOfExpectedType( + object, interceptor_holder); if (depth1 == kInvalidProtoDepth) { - depth2 = - optimization.GetPrototypeDepthOfExpectedType(interceptor_holder, - lookup->holder()); + depth2 = optimization.GetPrototypeDepthOfExpectedType( + interceptor_holder, Handle<JSObject>(lookup->holder())); } - can_do_fast_api_call = (depth1 != kInvalidProtoDepth) || - (depth2 != kInvalidProtoDepth); + can_do_fast_api_call = + depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth; } Counters* counters = masm->isolate()->counters(); @@ -574,9 +549,9 @@ class CallInterceptorCompiler BASE_EMBEDDED { Label miss_cleanup; Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; Register holder = - stub_compiler_->CheckPrototypes(object, receiver, - interceptor_holder, scratch1, - scratch2, scratch3, name, depth1, miss); + stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, depth1, miss); // Invoke an interceptor and if it provides a value, // branch to |regular_invoke|. @@ -589,10 +564,11 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Check that the maps from interceptor's holder to constant function's // holder haven't changed and thus we can use cached constant function. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { stub_compiler_->CheckPrototypes(interceptor_holder, receiver, - lookup->holder(), scratch1, - scratch2, scratch3, name, depth2, miss); + Handle<JSObject>(lookup->holder()), + scratch1, scratch2, scratch3, + name, depth2, miss); } else { // CheckPrototypes has a side effect of fetching a 'holder' // for API (object which is instanceof for the signature). It's @@ -603,10 +579,7 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - MaybeObject* result = GenerateFastApiCall(masm, - optimization, - arguments_.immediate()); - if (result->IsFailure()) return result; + GenerateFastApiCall(masm, optimization, arguments_.immediate()); } else { CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) ? CALL_AS_FUNCTION @@ -627,33 +600,27 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } - - return masm->isolate()->heap()->undefined_value(); // Success. } void CompileRegular(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, Register receiver, Register scratch1, Register scratch2, Register scratch3, - String* name, - JSObject* interceptor_holder, + Handle<String> name, + Handle<JSObject> interceptor_holder, Label* miss_label) { Register holder = stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, name, - miss_label); + scratch1, scratch2, scratch3, + name, miss_label); - __ EnterInternalFrame(); + FrameScope scope(masm, StackFrame::INTERNAL); // Save the name_ register across the call. __ push(name_); - PushInterceptorArguments(masm, - receiver, - holder, - name_, - interceptor_holder); + PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder); __ CallExternalReference( ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall), @@ -662,27 +629,30 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Restore the name_ register. __ pop(name_); - __ LeaveInternalFrame(); + + // Leave the internal frame. } void LoadWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, - JSObject* holder_obj, + Handle<JSObject> holder_obj, Label* interceptor_succeeded) { - __ EnterInternalFrame(); - __ push(holder); // Save the holder. - __ push(name_); // Save the name. - - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); - - __ pop(name_); // Restore the name. - __ pop(receiver); // Restore the holder. - __ LeaveInternalFrame(); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(holder); // Save the holder. + __ push(name_); // Save the name. + + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); + + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. + // Leave the internal frame. + } __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); __ j(not_equal, interceptor_succeeded); @@ -697,43 +667,33 @@ class CallInterceptorCompiler BASE_EMBEDDED { void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); - Code* code = NULL; - if (kind == Code::LOAD_IC) { - code = masm->isolate()->builtins()->builtin(Builtins::kLoadIC_Miss); - } else { - code = masm->isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Miss); - } - - Handle<Code> ic(code); - __ Jump(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = (kind == Code::LOAD_IC) + ? masm->isolate()->builtins()->LoadIC_Miss() + : masm->isolate()->builtins()->KeyedLoadIC_Miss(); + __ Jump(code, RelocInfo::CODE_TARGET); } void StubCompiler::GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm) { - Code* code = masm->isolate()->builtins()->builtin( - Builtins::kKeyedLoadIC_MissForceGeneric); - Handle<Code> ic(code); - __ Jump(ic, RelocInfo::CODE_TARGET); + Handle<Code> code = + masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); + __ Jump(code, RelocInfo::CODE_TARGET); } // Both name_reg and receiver_reg are preserved on jumps to miss_label, // but may be destroyed if store is successful. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - JSObject* object, + Handle<JSObject> object, int index, - Map* transition, + Handle<Map> transition, Register receiver_reg, Register name_reg, Register scratch, Label* miss_label) { - // Check that the object isn't a smi. - __ JumpIfSmi(receiver_reg, miss_label); - // Check that the map of the object hasn't changed. - __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Handle<Map>(object->map())); - __ j(not_equal, miss_label); + __ CheckMap(receiver_reg, Handle<Map>(object->map()), + miss_label, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -745,12 +705,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); // Perform map transition for the receiver if necessary. - if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { + if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. __ pop(scratch); // Return address. __ push(receiver_reg); - __ Push(Handle<Map>(transition)); + __ Push(transition); __ push(rax); __ push(scratch); __ TailCallExternalReference( @@ -761,11 +721,10 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, return; } - if (transition != NULL) { + if (!transition.is_null()) { // Update the map of the object; no write barrier updating is // needed because the map is never in new space. - __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Handle<Map>(transition)); + __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), transition); } // Adjust for the number of properties stored in the object. Even in the @@ -781,7 +740,8 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the value being stored in the now unused name_reg. __ movq(name_reg, rax); - __ RecordWrite(receiver_reg, offset, name_reg, scratch); + __ RecordWriteField( + receiver_reg, offset, name_reg, scratch, kDontSaveFPRegs); } else { // Write to the properties array. int offset = index * kPointerSize + FixedArray::kHeaderSize; @@ -792,7 +752,8 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Update the write barrier for the array address. // Pass the value being stored in the now unused name_reg. __ movq(name_reg, rax); - __ RecordWrite(scratch, offset, name_reg, receiver_reg); + __ RecordWriteField( + scratch, offset, name_reg, receiver_reg, kDontSaveFPRegs); } // Return the value (register rax). @@ -803,37 +764,53 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( - MacroAssembler* masm, - GlobalObject* global, - String* name, - Register scratch, - Label* miss) { - Object* probe; - { MaybeObject* maybe_probe = global->EnsurePropertyCell(name); - if (!maybe_probe->ToObject(&probe)) return maybe_probe; - } - JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle<GlobalObject> global, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSGlobalPropertyCell> cell = + GlobalObject::EnsurePropertyCell(global, name); ASSERT(cell->value()->IsTheHole()); - __ Move(scratch, Handle<Object>(cell)); + __ Move(scratch, cell); __ Cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), masm->isolate()->factory()->the_hole_value()); __ j(not_equal, miss); - return cell; } +// Calls GenerateCheckPropertyCell for each global object in the prototype chain +// from object to (but not including) holder. +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, + Register scratch, + Label* miss) { + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { + if (current->IsGlobalObject()) { + GenerateCheckPropertyCell(masm, + Handle<GlobalObject>::cast(current), + name, + scratch, + miss); + } + current = Handle<JSObject>(JSObject::cast(current->GetPrototype())); + } +} + #undef __ #define __ ACCESS_MASM((masm())) -Register StubCompiler::CheckPrototypes(JSObject* object, +Register StubCompiler::CheckPrototypes(Handle<JSObject> object, Register object_reg, - JSObject* holder, + Handle<JSObject> holder, Register holder_reg, Register scratch1, Register scratch2, - String* name, + Handle<String> name, int save_at_depth, Label* miss) { // Make sure there's no overlap between holder and object registers. @@ -853,80 +830,56 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Check the maps in the prototype chain. // Traverse the prototype chain from the object and do map checks. - JSObject* current = object; - while (current != holder) { - depth++; + Handle<JSObject> current = object; + while (!current.is_identical_to(holder)) { + ++depth; // Only global objects and objects that do not require access // checks are allowed in stubs. ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - JSObject* prototype = JSObject::cast(current->GetPrototype()); + Handle<JSObject> prototype(JSObject::cast(current->GetPrototype())); if (!current->HasFastProperties() && !current->IsJSGlobalObject() && !current->IsJSGlobalProxy()) { if (!name->IsSymbol()) { - MaybeObject* lookup_result = heap()->LookupSymbol(name); - if (lookup_result->IsFailure()) { - set_failure(Failure::cast(lookup_result)); - return reg; - } else { - name = String::cast(lookup_result->ToObjectUnchecked()); - } + name = factory()->LookupSymbol(name); } - ASSERT(current->property_dictionary()->FindEntry(name) == + ASSERT(current->property_dictionary()->FindEntry(*name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); - if (negative_lookup->IsFailure()) { - set_failure(Failure::cast(negative_lookup)); - return reg; - } + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // from now the object is in holder_reg + reg = holder_reg; // From now on the object will be in holder_reg. __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); - } else if (heap()->InNewSpace(prototype)) { - // Get the map of the current object. - __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - __ Cmp(scratch1, Handle<Map>(current->map())); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - - // Restore scratch register to be the map of the object. - // We load the prototype from the map in the scratch register. + } else { + bool in_new_space = heap()->InNewSpace(*prototype); + Handle<Map> current_map(current->map()); + if (in_new_space) { + // Save the map in scratch1 for later. __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); } - // The prototype is in new space; we cannot store a reference - // to it in the code. Load it from the map. - reg = holder_reg; // from now the object is in holder_reg - __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + __ CheckMap(reg, Handle<Map>(current_map), + miss, DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); - } else { - // Check the map of the current object. - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle<Map>(current->map())); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); + __ CheckAccessGlobalProxy(reg, scratch2, miss); + } + reg = holder_reg; // From now on the object will be in holder_reg. + + if (in_new_space) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ Move(reg, prototype); } - // The prototype is in old space; load it directly. - reg = holder_reg; // from now the object is in holder_reg - __ Move(reg, Handle<JSObject>(prototype)); } if (save_at_depth == depth) { @@ -936,62 +889,46 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // Go to the next object in the prototype chain. current = prototype; } - - // Check the holder map. - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map())); - __ j(not_equal, miss); + ASSERT(current.is_identical_to(holder)); // Log the check depth. LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); - // Perform security check for access to the global object and return - // the holder register. - ASSERT(current == holder); + // Check the holder map. + __ CheckMap(reg, Handle<Map>(holder->map()), + miss, DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); + + // Perform security check for access to the global object. ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); if (current->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); } - // If we've skipped any global objects, it's not enough to verify - // that their maps haven't changed. We also need to check that the - // property cell for the property is still empty. - current = object; - while (current != holder) { - if (current->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(current), - name, - scratch1, - miss); - if (cell->IsFailure()) { - set_failure(Failure::cast(cell)); - return reg; - } - } - current = JSObject::cast(current->GetPrototype()); - } + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); // Return the register containing the holder. return reg; } -void StubCompiler::GenerateLoadField(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadField(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, int index, - String* name, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check the prototype chain. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + Register reg = CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); // Get the value from the properties. GenerateFastPropertyLoad(masm(), rax, reg, holder, index); @@ -999,25 +936,22 @@ void StubCompiler::GenerateLoadField(JSObject* object, } -MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss) { +void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, + Handle<JSObject> holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, - scratch2, scratch3, name, miss); - - Handle<AccessorInfo> callback_handle(callback); + Register reg = CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); // Insert additional parameters into the stack frame above return address. ASSERT(!scratch2.is(reg)); @@ -1025,11 +959,11 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, __ push(receiver); // receiver __ push(reg); // holder - if (heap()->InNewSpace(callback_handle->data())) { - __ Move(scratch1, callback_handle); + if (heap()->InNewSpace(callback->data())) { + __ Move(scratch1, callback); __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data } else { - __ Push(Handle<Object>(callback_handle->data())); + __ Push(Handle<Object>(callback->data())); } __ push(name_reg); // name // Save a pointer to where we pushed the arguments pointer. @@ -1048,11 +982,7 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, __ movq(name_arg, rsp); __ push(scratch2); // Restore return address. - // Do call through the api. - Address getter_address = v8::ToCData<Address>(callback->getter()); - ApiFunction fun(getter_address); - - // 3 elements array for v8::Agruments::values_ and handler for name. + // 3 elements array for v8::Arguments::values_ and handler for name. const int kStackSpace = 4; // Allocate v8::AccessorInfo in non-GCed stack space. @@ -1068,45 +998,42 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object, // could be used to pass arguments. __ lea(accessor_info_arg, StackSpaceOperand(0)); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. - return masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); + Address getter_address = v8::ToCData<Address>(callback->getter()); + __ CallApiFunctionAndReturn(getter_address, kStackSpace); } -void StubCompiler::GenerateLoadConstant(JSObject* object, - JSObject* holder, +void StubCompiler::GenerateLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, Register receiver, Register scratch1, Register scratch2, Register scratch3, - Object* value, - String* name, + Handle<JSFunction> value, + Handle<String> name, Label* miss) { // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, miss); // Check that the maps haven't changed. - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes( + object, receiver, holder, scratch1, scratch2, scratch3, name, miss); // Return the constant value. - __ Move(rax, Handle<Object>(value)); + __ LoadHeapObject(rax, value); __ ret(0); } -void StubCompiler::GenerateLoadInterceptor(JSObject* object, - JSObject* interceptor_holder, +void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, + Handle<JSObject> interceptor_holder, LookupResult* lookup, Register receiver, Register name_reg, Register scratch1, Register scratch2, Register scratch3, - String* name, + Handle<String> name, Label* miss) { ASSERT(interceptor_holder->HasNamedInterceptor()); ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -1118,13 +1045,13 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // and CALLBACKS, so inline only them, other cases may be added // later. bool compile_followup_inline = false; - if (lookup->IsProperty() && lookup->IsCacheable()) { + if (lookup->IsFound() && lookup->IsCacheable()) { if (lookup->type() == FIELD) { compile_followup_inline = true; } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsAccessorInfo() && - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { - compile_followup_inline = true; + lookup->GetCallbackObject()->IsAccessorInfo()) { + compile_followup_inline = + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL; } } @@ -1139,47 +1066,49 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // Save necessary data before invoking an interceptor. // Requires a frame to make GC aware of pushed pointers. - __ EnterInternalFrame(); + { + FrameScope frame_scope(masm(), StackFrame::INTERNAL); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - // CALLBACKS case needs a receiver to be passed into C++ callback. - __ push(receiver); - } - __ push(holder_reg); - __ push(name_reg); - - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method.) - CompileCallLoadPropertyWithInterceptor(masm(), - receiver, - holder_reg, - name_reg, - interceptor_holder); - - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); - __ j(equal, &interceptor_failed); - __ LeaveInternalFrame(); - __ ret(0); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + // CALLBACKS case needs a receiver to be passed into C++ callback. + __ push(receiver); + } + __ push(holder_reg); + __ push(name_reg); - __ bind(&interceptor_failed); - __ pop(name_reg); - __ pop(holder_reg); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - __ pop(receiver); - } + // Invoke an interceptor. Note: map checks from receiver to + // interceptor's holder has been compiled before (see a caller + // of this method.) + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + interceptor_holder); + + // Check if interceptor provided a value for property. If it's + // the case, return immediately. + Label interceptor_failed; + __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); + __ j(equal, &interceptor_failed); + frame_scope.GenerateLeaveFrame(); + __ ret(0); - __ LeaveInternalFrame(); + __ bind(&interceptor_failed); + __ pop(name_reg); + __ pop(holder_reg); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + __ pop(receiver); + } + + // Leave the internal frame. + } // Check that the maps from interceptor's holder to lookup's holder // haven't changed. And load lookup's holder into |holder| register. - if (interceptor_holder != lookup->holder()) { + if (*interceptor_holder != lookup->holder()) { holder_reg = CheckPrototypes(interceptor_holder, holder_reg, - lookup->holder(), + Handle<JSObject>(lookup->holder()), scratch1, scratch2, scratch3, @@ -1191,15 +1120,15 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), rax, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle<JSObject>(lookup->holder()), + lookup->GetFieldIndex()); __ ret(0); } else { // We found CALLBACKS property in prototype chain of interceptor's // holder. ASSERT(lookup->type() == CALLBACKS); - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); - ASSERT(callback != NULL); + Handle<AccessorInfo> callback( + AccessorInfo::cast(lookup->GetCallbackObject())); ASSERT(callback->getter() != NULL); // Tail call to runtime. @@ -1208,7 +1137,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ pop(scratch2); // return address __ push(receiver); __ push(holder_reg); - __ Move(holder_reg, Handle<AccessorInfo>(callback)); + __ Move(holder_reg, callback); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); __ push(holder_reg); __ push(name_reg); @@ -1237,17 +1166,17 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ Cmp(rcx, Handle<String>(name)); + __ Cmp(rcx, name); __ j(not_equal, miss); } } -void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, +void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name, Label* miss) { ASSERT(holder->IsGlobalObject()); @@ -1260,7 +1189,7 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(rdx, miss); } @@ -1269,15 +1198,16 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, } -void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, - Label* miss) { +void CallStubCompiler::GenerateLoadFunctionFromCell( + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Label* miss) { // Get the value from the cell. - __ Move(rdi, Handle<JSGlobalPropertyCell>(cell)); + __ Move(rdi, cell); __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); // Check that the cell contains the same function. - if (heap()->InNewSpace(function)) { + if (heap()->InNewSpace(*function)) { // We can't embed a pointer to a function in new space so we have // to verify that the shared function info is unchanged. This has // the nice side effect that multiple closures based on the same @@ -1290,30 +1220,26 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, // Check the shared function info. Make sure it hasn't changed. __ Move(rax, Handle<SharedFunctionInfo>(function->shared())); __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); - __ j(not_equal, miss); } else { - __ Cmp(rdi, Handle<JSFunction>(function)); - __ j(not_equal, miss); + __ Cmp(rdi, function); } + __ j(not_equal, miss); } -MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = +void CallStubCompiler::GenerateMissBranch() { + Handle<Code> code = isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), kind_, - extra_ic_state_); - Object* obj; - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - __ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET); - return obj; + extra_state_); + __ Jump(code, RelocInfo::CODE_TARGET); } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, +Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -1353,7 +1279,7 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, } // Invoke the function. - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION, @@ -1361,19 +1287,19 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); } -MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPushCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : name // -- rsp[0] : return address @@ -1383,10 +1309,9 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss; - GenerateNameCheck(name, &miss); // Get the receiver from the stack. @@ -1396,14 +1321,8 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // Check that the receiver isn't a smi. __ JumpIfSmi(rdx, &miss); - CheckPrototypes(JSObject::cast(object), - rdx, - holder, - rbx, - rax, - rdi, - name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi, + name, &miss); if (argc == 0) { // Noop, return the length. @@ -1412,53 +1331,85 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } else { Label call_builtin; - // Get the elements array of the object. - __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); + if (argc == 1) { // Otherwise fall through to call builtin. + Label attempt_to_grow_elements, with_write_barrier; - // Check that the elements are in fast mode and writable. - __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), - factory()->fixed_array_map()); - __ j(not_equal, &call_builtin); + // Get the elements array of the object. + __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset)); - if (argc == 1) { // Otherwise fall through to call builtin. - Label exit, with_write_barrier, attempt_to_grow_elements; + // Check that the elements are in fast mode and writable. + __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset), + factory()->fixed_array_map()); + __ j(not_equal, &call_builtin); // Get the array's length into rax and calculate new length. __ SmiToInteger32(rax, FieldOperand(rdx, JSArray::kLengthOffset)); STATIC_ASSERT(FixedArray::kMaxLength < Smi::kMaxValue); __ addl(rax, Immediate(argc)); - // Get the element's length into rcx. - __ SmiToInteger32(rcx, FieldOperand(rbx, FixedArray::kLengthOffset)); + // Get the elements' length into rcx. + __ SmiToInteger32(rcx, FieldOperand(rdi, FixedArray::kLengthOffset)); // Check if we could survive without allocation. __ cmpl(rax, rcx); __ j(greater, &attempt_to_grow_elements); + // Check if value is a smi. + __ movq(rcx, Operand(rsp, argc * kPointerSize)); + __ JumpIfNotSmi(rcx, &with_write_barrier); + // Save new length. __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rax); - // Push the element. - __ movq(rcx, Operand(rsp, argc * kPointerSize)); - __ lea(rdx, FieldOperand(rbx, - rax, times_pointer_size, - FixedArray::kHeaderSize - argc * kPointerSize)); - __ movq(Operand(rdx, 0), rcx); + // Store the value. + __ movq(FieldOperand(rdi, + rax, + times_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize), + rcx); - // Check if value is a smi. __ Integer32ToSmi(rax, rax); // Return new length as smi. - - __ JumpIfNotSmi(rcx, &with_write_barrier); - - __ bind(&exit); __ ret((argc + 1) * kPointerSize); __ bind(&with_write_barrier); - __ InNewSpace(rbx, rcx, equal, &exit); + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + + if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) { + Label fast_object, not_fast_object; + __ CheckFastObjectElements(rbx, ¬_fast_object, Label::kNear); + __ jmp(&fast_object); + // In case of fast smi-only, convert to fast object, otherwise bail out. + __ bind(¬_fast_object); + __ CheckFastSmiOnlyElements(rbx, &call_builtin); + // rdx: receiver + // rbx: map + __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + FAST_ELEMENTS, + rbx, + r10, + &call_builtin); + ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); + __ bind(&fast_object); + } else { + __ CheckFastObjectElements(rbx, &call_builtin); + } + + __ CheckFastObjectElements(rbx, &call_builtin); + + // Save new length. + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rax); + + // Store the value. + __ lea(rdx, FieldOperand(rdi, + rax, times_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize)); + __ movq(Operand(rdx, 0), rcx); - __ RecordWriteHelper(rbx, rdx, rcx); + __ RecordWrite(rdi, rdx, rcx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ Integer32ToSmi(rax, rax); // Return new length as smi. __ ret((argc + 1) * kPointerSize); __ bind(&attempt_to_grow_elements); @@ -1466,6 +1417,15 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ jmp(&call_builtin); } + __ movq(rbx, Operand(rsp, argc * kPointerSize)); + // Growing elements that are SMI-only requires special handling in case + // the new element is non-Smi. For now, delegate to the builtin. + Label no_fast_elements_check; + __ JumpIfSmi(rbx, &no_fast_elements_check); + __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset)); + __ CheckFastObjectElements(rcx, &call_builtin, Label::kFar); + __ bind(&no_fast_elements_check); + ExternalReference new_space_allocation_top = ExternalReference::new_space_allocation_top_address(isolate()); ExternalReference new_space_allocation_limit = @@ -1476,7 +1436,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, __ Load(rcx, new_space_allocation_top); // Check if it's the end of elements. - __ lea(rdx, FieldOperand(rbx, + __ lea(rdx, FieldOperand(rdi, rax, times_pointer_size, FixedArray::kHeaderSize - argc * kPointerSize)); __ cmpq(rdx, rcx); @@ -1489,28 +1449,33 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // We fit and could grow elements. __ Store(new_space_allocation_top, rcx); - __ movq(rcx, Operand(rsp, argc * kPointerSize)); // Push the argument... - __ movq(Operand(rdx, 0), rcx); + __ movq(Operand(rdx, 0), rbx); // ... and fill the rest with holes. __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); for (int i = 1; i < kAllocationDelta; i++) { __ movq(Operand(rdx, i * kPointerSize), kScratchRegister); } + // We know the elements array is in new space so we don't need the + // remembered set, but we just pushed a value onto it so we may have to + // tell the incremental marker to rescan the object that we just grew. We + // don't need to worry about the holes because they are in old space and + // already marked black. + __ RecordWrite(rdi, rdx, rbx, kDontSaveFPRegs, OMIT_REMEMBERED_SET); + // Restore receiver to rdx as finish sequence assumes it's here. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // Increment element's and array's sizes. - __ SmiAddConstant(FieldOperand(rbx, FixedArray::kLengthOffset), + __ SmiAddConstant(FieldOperand(rdi, FixedArray::kLengthOffset), Smi::FromInt(kAllocationDelta)); // Make new length a smi before returning it. __ Integer32ToSmi(rax, rax); __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax); - // Elements are in new space, so write barrier is not required. __ ret((argc + 1) * kPointerSize); } @@ -1522,19 +1487,19 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileArrayPopCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : name // -- rsp[0] : return address @@ -1544,10 +1509,9 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------------------------------- // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value(); + if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null(); Label miss, return_undefined, call_builtin; - GenerateNameCheck(name, &miss); // Get the receiver from the stack. @@ -1557,9 +1521,8 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Check that the receiver isn't a smi. __ JumpIfSmi(rdx, &miss); - CheckPrototypes(JSObject::cast(object), rdx, - holder, rbx, - rax, rdi, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi, + name, &miss); // Get the elements array of the object. __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); @@ -1605,20 +1568,19 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, 1); __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : function name // -- rsp[0] : return address @@ -1628,7 +1590,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); @@ -1636,13 +1598,11 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Label name_miss; Label index_out_of_range; Label* index_out_of_range_label = &index_out_of_range; - if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. @@ -1650,13 +1610,12 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Context::STRING_FUNCTION_INDEX, rax, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + rax, holder, rbx, rdx, rdi, name, &miss); Register receiver = rbx; Register index = rdi; - Register scratch = rdx; Register result = rax; __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); if (argc > 0) { @@ -1665,19 +1624,18 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharCodeAtGenerator char_code_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_code_at_generator.GenerateFast(masm()); + StringCharCodeAtGenerator generator(receiver, + index, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); StubRuntimeCallHelper call_helper; - char_code_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); @@ -1687,22 +1645,21 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( __ bind(&miss); // Restore function name in rcx. - __ Move(rcx, Handle<String>(name)); + __ Move(rcx, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringCharAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringCharAtCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : function name // -- rsp[0] : return address @@ -1712,21 +1669,18 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // ----------------------------------- // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return heap()->undefined_value(); + if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); const int argc = arguments().immediate(); - Label miss; Label name_miss; Label index_out_of_range; Label* index_out_of_range_label = &index_out_of_range; - if (kind_ == Code::CALL_IC && - (CallICBase::StringStubState::decode(extra_ic_state_) == + (CallICBase::StringStubState::decode(extra_state_) == DEFAULT_STRING_STUB)) { index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. @@ -1734,14 +1688,13 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( Context::STRING_FUNCTION_INDEX, rax, &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); + ASSERT(!object.is_identical_to(holder)); + CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())), + rax, holder, rbx, rdx, rdi, name, &miss); Register receiver = rax; Register index = rdi; - Register scratch1 = rbx; - Register scratch2 = rdx; + Register scratch = rdx; Register result = rax; __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); if (argc > 0) { @@ -1750,45 +1703,42 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - StringCharAtGenerator char_at_generator(receiver, - index, - scratch1, - scratch2, - result, - &miss, // When not a string. - &miss, // When not a number. - index_out_of_range_label, - STRING_INDEX_IS_NUMBER); - char_at_generator.GenerateFast(masm()); + StringCharAtGenerator generator(receiver, + index, + scratch, + result, + &miss, // When not a string. + &miss, // When not a number. + index_out_of_range_label, + STRING_INDEX_IS_NUMBER); + generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); if (index_out_of_range.is_linked()) { __ bind(&index_out_of_range); __ LoadRoot(rax, Heap::kEmptyStringRootIndex); __ ret((argc + 1) * kPointerSize); } - __ bind(&miss); // Restore function name in rcx. - __ Move(rcx, Handle<String>(name)); + __ Move(rcx, name); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : function name // -- rsp[0] : return address @@ -1797,25 +1747,23 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + const int argc = arguments().immediate(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - __ JumpIfSmi(rdx, &miss); - - CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } @@ -1830,17 +1778,17 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( // Convert the smi code to uint16. __ SmiAndConstant(code, code, Smi::FromInt(0xffff)); - StringCharFromCodeGenerator char_from_code_generator(code, rax); - char_from_code_generator.GenerateFast(masm()); + StringCharFromCodeGenerator generator(code, rax); + generator.GenerateFast(masm()); __ ret(2 * kPointerSize); StubRuntimeCallHelper call_helper; - char_from_code_generator.GenerateSlow(masm(), call_helper); + generator.GenerateSlow(masm(), call_helper); // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(function, arguments(), JUMP_FUNCTION, @@ -1848,29 +1796,30 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( __ bind(&miss); // rcx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathFloorCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // TODO(872): implement this. - return heap()->undefined_value(); + return Handle<Code>::null(); } -MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileMathAbsCall( + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // -- rcx : function name // -- rsp[0] : return address @@ -1879,28 +1828,25 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - const int argc = arguments().immediate(); - // If the object is not a JSObject or we got an unexpected number of // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); + const int argc = arguments().immediate(); + if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { + if (cell.is_null()) { __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - __ JumpIfSmi(rdx, &miss); - - CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, - &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi, + name, &miss); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + ASSERT(cell->value() == *function); + GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name, + &miss); GenerateLoadFunctionFromCell(cell, function, &miss); } - // Load the (only) argument into rax. __ movq(rax, Operand(rsp, 1 * kPointerSize)); @@ -1957,7 +1903,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, // Tail call the full function. We do not have to patch the receiver // because the function makes no use of it. __ bind(&slow); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(function, arguments(), JUMP_FUNCTION, @@ -1965,33 +1911,31 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ bind(&miss); // rcx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* CallStubCompiler::CompileFastApiCall( +Handle<Code> CallStubCompiler::CompileFastApiCall( const CallOptimization& optimization, - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { + Handle<Object> object, + Handle<JSObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { ASSERT(optimization.is_simple_api_call()); // Bail out if object is a global object as we don't want to // repatch it to global receiver. - if (object->IsGlobalObject()) return heap()->undefined_value(); - if (cell != NULL) return heap()->undefined_value(); - if (!object->IsJSObject()) return heap()->undefined_value(); + if (object->IsGlobalObject()) return Handle<Code>::null(); + if (!cell.is_null()) return Handle<Code>::null(); + if (!object->IsJSObject()) return Handle<Code>::null(); int depth = optimization.GetPrototypeDepthOfExpectedType( - JSObject::cast(object), holder); - if (depth == kInvalidProtoDepth) return heap()->undefined_value(); + Handle<JSObject>::cast(object), holder); + if (depth == kInvalidProtoDepth) return Handle<Code>::null(); Label miss, miss_before_stack_reserved; - GenerateNameCheck(name, &miss_before_stack_reserved); // Get the receiver from the stack. @@ -2010,32 +1954,30 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); // Check that the maps haven't changed and find a Holder as a side effect. - CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rax, rdi, name, depth, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi, + name, depth, &miss); // Move the return address on top of the stack. __ movq(rax, Operand(rsp, 3 * kPointerSize)); __ movq(Operand(rsp, 0 * kPointerSize), rax); - MaybeObject* result = GenerateFastApiCall(masm(), optimization, argc); - if (result->IsFailure()) return result; + GenerateFastApiCall(masm(), optimization, argc); __ bind(&miss); __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, - JSObject* holder, - JSFunction* function, - String* name, +Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object, + Handle<JSObject> holder, + Handle<JSFunction> function, + Handle<String> name, CheckType check) { // ----------- S t a t e ------------- // rcx : function name @@ -2048,16 +1990,14 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, NULL, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, + Handle<JSGlobalPropertyCell>::null(), + function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the receiver from the stack. @@ -2074,14 +2014,13 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); Counters* counters = isolate()->counters(); - SharedFunctionInfo* function_info = function->shared(); switch (check) { case RECEIVER_MAP_CHECK: __ IncrementCounter(counters->call_const(), 1); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rax, rdi, name, &miss); + CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, + rdi, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -2092,28 +2031,25 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, break; case STRING_CHECK: - if (!function->IsBuiltin() && !function_info->strict_mode()) { - // Calling non-strict non-builtins with a value as the receiver - // requires boxing. - __ jmp(&miss); - } else { + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { // Check that the object is a two-byte string or a symbol. __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); __ j(above_equal, &miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::STRING_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); - } - break; - - case NUMBER_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + rax, holder, rbx, rdx, rdi, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case NUMBER_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a smi or a heap number. __ JumpIfSmi(rdx, &fast); @@ -2123,18 +2059,18 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); - } - break; - } - - case BOOLEAN_CHECK: { - if (!function->IsBuiltin() && !function_info->strict_mode()) { + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + rax, holder, rbx, rdx, rdi, name, &miss); + } else { // Calling non-strict non-builtins with a value as the receiver // requires boxing. __ jmp(&miss); - } else { + } + break; + + case BOOLEAN_CHECK: + if (function->IsBuiltin() || !function->shared()->is_classic_mode()) { Label fast; // Check that the object is a boolean. __ CompareRoot(rdx, Heap::kTrueValueRootIndex); @@ -2145,17 +2081,18 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype( masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); + CheckPrototypes( + Handle<JSObject>(JSObject::cast(object->GetPrototype())), + rax, holder, rbx, rdx, rdi, name, &miss); + } else { + // Calling non-strict non-builtins with a value as the receiver + // requires boxing. + __ jmp(&miss); } break; - } - - default: - UNREACHABLE(); } - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(function, arguments(), JUMP_FUNCTION, @@ -2163,17 +2100,16 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, - JSObject* holder, - String* name) { +Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -2184,30 +2120,20 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // Get the receiver from the stack. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - CallInterceptorCompiler compiler(this, arguments(), rcx, extra_ic_state_); - MaybeObject* result = compiler.Compile(masm(), - object, - holder, - name, - &lookup, - rdx, - rbx, - rdi, - rax, - &miss); - if (result->IsFailure()) return result; + CallInterceptorCompiler compiler(this, arguments(), rcx, extra_state_); + compiler.Compile(masm(), object, holder, name, &lookup, rdx, rbx, rdi, rax, + &miss); // Restore receiver. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); @@ -2226,7 +2152,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Invoke the function. __ movq(rdi, rax); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION, @@ -2234,19 +2160,19 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Handle load cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(INTERCEPTOR, name); } -MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +Handle<Code> CallStubCompiler::CompileCallGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<JSFunction> function, + Handle<String> name) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -2258,23 +2184,17 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, // ----------------------------------- if (HasCustomCallGenerator(function)) { - MaybeObject* maybe_result = CompileCustomCall( - object, holder, cell, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; + Handle<Code> code = CompileCustomCall(object, holder, cell, function, name); + // A null handle means bail out to the regular compiler code below. + if (!code.is_null()) return code; } Label miss; - GenerateNameCheck(name, &miss); // Get the number of arguments. const int argc = arguments().immediate(); - GenerateGlobalReceiverCheck(object, holder, name, &miss); - GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy. @@ -2283,45 +2203,37 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); } - // Setup the context (function already in rdi). + // Set up the context (function already in rdi). __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); // Jump to the cached code (tail call). Counters* counters = isolate()->counters(); __ IncrementCounter(counters->call_global_inline(), 1); - ASSERT(function->is_compiled()); ParameterCount expected(function->shared()->formal_parameter_count()); - CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_) + CallKind call_kind = CallICBase::Contextual::decode(extra_state_) ? CALL_AS_FUNCTION : CALL_AS_METHOD; - if (V8::UseCrankshaft()) { - // TODO(kasperl): For now, we always call indirectly through the - // code field in the function to allow recompilation to take effect - // without changing any of the call sites. - __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); - __ InvokeCode(rdx, expected, arguments(), JUMP_FUNCTION, - NullCallWrapper(), call_kind); - } else { - Handle<Code> code(function->code()); - __ InvokeCode(code, expected, arguments(), - RelocInfo::CODE_TARGET, JUMP_FUNCTION, - NullCallWrapper(), call_kind); - } + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); + __ InvokeCode(rdx, expected, arguments(), JUMP_FUNCTION, + NullCallWrapper(), call_kind); + // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); } -MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -2331,12 +2243,7 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, Label miss; // Generate store field code. Preserves receiver and name on jump to miss. - GenerateStoreField(masm(), - object, - index, - transition, - rdx, rcx, rbx, - &miss); + GenerateStoreField(masm(), object, index, transition, rdx, rcx, rbx, &miss); // Handle store cache miss. __ bind(&miss); @@ -2344,13 +2251,14 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, - AccessorInfo* callback, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreCallback( + Handle<JSObject> object, + Handle<AccessorInfo> callback, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -2359,13 +2267,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(rdx, &miss); - // Check that the map of the object hasn't changed. - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), - Handle<Map>(object->map())); - __ j(not_equal, &miss); + __ CheckMap(rdx, Handle<Map>(object->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { @@ -2378,7 +2282,7 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, __ pop(rbx); // remove the return address __ push(rdx); // receiver - __ Push(Handle<AccessorInfo>(callback)); // callback info + __ Push(callback); // callback info __ push(rcx); // name __ push(rax); // value __ push(rbx); // restore return address @@ -2398,8 +2302,9 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, } -MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreInterceptor( + Handle<JSObject> receiver, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -2408,13 +2313,9 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, // ----------------------------------- Label miss; - // Check that the object isn't a smi. - __ JumpIfSmi(rdx, &miss); - // Check that the map of the object hasn't changed. - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), - Handle<Map>(receiver->map())); - __ j(not_equal, &miss); + __ CheckMap(rdx, Handle<Map>(receiver->map()), &miss, + DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); // Perform global security token check if needed. if (receiver->IsJSGlobalProxy()) { @@ -2447,9 +2348,10 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, } -MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, - JSGlobalPropertyCell* cell, - String* name) { +Handle<Code> StoreStubCompiler::CompileStoreGlobal( + Handle<GlobalObject> object, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -2463,17 +2365,20 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, Handle<Map>(object->map())); __ j(not_equal, &miss); + // Compute the cell operand to use. + __ Move(rbx, cell); + Operand cell_operand = FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset); + // Check that the value in the cell is not the hole. If it is, this // cell could have been deleted and reintroducing the global needs // to update the property details in the property dictionary of the // global object. We bail out to the runtime system to do that. - __ Move(rbx, Handle<JSGlobalPropertyCell>(cell)); - __ CompareRoot(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), - Heap::kTheHoleValueRootIndex); + __ CompareRoot(cell_operand, Heap::kTheHoleValueRootIndex); __ j(equal, &miss); // Store the value in the cell. - __ movq(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), rax); + __ movq(cell_operand, rax); + // Cells are always rescanned, so no write barrier here. // Return the value (register rax). Counters* counters = isolate()->counters(); @@ -2491,10 +2396,10 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, } -MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, +Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, int index, - Map* transition, - String* name) { + Handle<Map> transition, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -2507,16 +2412,11 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ IncrementCounter(counters->keyed_store_field(), 1); // Check that the name has not changed. - __ Cmp(rcx, Handle<String>(name)); + __ Cmp(rcx, name); __ j(not_equal, &miss); // Generate store field code. Preserves receiver and name on jump to miss. - GenerateStoreField(masm(), - object, - index, - transition, - rdx, rcx, rbx, - &miss); + GenerateStoreField(masm(), object, index, transition, rdx, rcx, rbx, &miss); // Handle store cache miss. __ bind(&miss); @@ -2525,39 +2425,38 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { +Handle<Code> KeyedStoreStubCompiler::CompileStoreElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Code* stub; + ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - MaybeObject* maybe_stub = - KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(rdx, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = + KeyedStoreElementStub(is_js_array, elements_kind).GetCode(); + + __ DispatchMap(rdx, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss(); __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_stubs, + MapHandleList* transitioned_maps) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -2565,18 +2464,22 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( // -- rsp[0] : return address // ----------------------------------- Label miss; - __ JumpIfSmi(rdx, &miss); + __ JumpIfSmi(rdx, &miss, Label::kNear); - Register map_reg = rbx; - __ movq(map_reg, FieldOperand(rdx, HeapObject::kMapOffset)); + __ movq(rdi, FieldOperand(rdx, HeapObject::kMapOffset)); int receiver_count = receiver_maps->length(); - for (int current = 0; current < receiver_count; ++current) { + for (int i = 0; i < receiver_count; ++i) { // Check map and tail call if there's a match - Handle<Map> map(receiver_maps->at(current)); - __ Cmp(map_reg, map); - __ j(equal, - Handle<Code>(handler_ics->at(current)), - RelocInfo::CODE_TARGET); + __ Cmp(rdi, receiver_maps->at(i)); + if (transitioned_maps->at(i).is_null()) { + __ j(equal, handler_stubs->at(i), RelocInfo::CODE_TARGET); + } else { + Label next_map; + __ j(not_equal, &next_map, Label::kNear); + __ movq(rbx, transitioned_maps->at(i), RelocInfo::EMBEDDED_OBJECT); + __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET); + __ bind(&next_map); + } } __ bind(&miss); @@ -2584,13 +2487,13 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic( __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } -MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last) { +Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> last) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name @@ -2609,15 +2512,8 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - rdx, - &miss); - if (cell->IsFailure()) { - miss.Unuse(); - return cell; - } + GenerateCheckPropertyCell( + masm(), Handle<GlobalObject>::cast(last), name, rdx, &miss); } // Return undefined if maps of the full prototype chain are still the @@ -2629,14 +2525,14 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NONEXISTENT, heap()->empty_string()); + return GetCode(NONEXISTENT, factory()->empty_string()); } -MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, - JSObject* holder, +Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object, + Handle<JSObject> holder, int index, - String* name) { + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name @@ -2653,24 +2549,19 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> LoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> object, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name // -- rsp[0] : return address // ----------------------------------- Label miss; - - MaybeObject* result = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, - rdi, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, callback, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2679,10 +2570,10 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, } -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object, + Handle<JSObject> holder, + Handle<JSFunction> value, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name @@ -2699,32 +2590,22 @@ MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, } -MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name // -- rsp[0] : return address // ----------------------------------- Label miss; - - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); // TODO(368): Compile in the whole chain: all the interceptors in // prototypes and ultimate answer. - GenerateLoadInterceptor(receiver, - holder, - &lookup, - rax, - rcx, - rdx, - rbx, - rdi, - name, - &miss); - + GenerateLoadInterceptor(receiver, holder, &lookup, rax, rcx, rdx, rbx, rdi, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -2733,11 +2614,12 @@ MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete) { +Handle<Code> LoadStubCompiler::CompileLoadGlobal( + Handle<JSObject> object, + Handle<GlobalObject> holder, + Handle<JSGlobalPropertyCell> cell, + Handle<String> name, + bool is_dont_delete) { // ----------- S t a t e ------------- // -- rax : receiver // -- rcx : name @@ -2748,7 +2630,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // If the object is the holder then we know that it's a global // object which can only happen for contextual loads. In this case, // the receiver cannot be a smi. - if (object != holder) { + if (!object.is_identical_to(holder)) { __ JumpIfSmi(rax, &miss); } @@ -2756,7 +2638,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, CheckPrototypes(object, rax, holder, rbx, rdx, rdi, name, &miss); // Get the value from the cell. - __ Move(rbx, Handle<JSGlobalPropertyCell>(cell)); + __ Move(rbx, cell); __ movq(rbx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); // Check for deleted property if property can actually be deleted. @@ -2782,9 +2664,9 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, - JSObject* receiver, - JSObject* holder, +Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, int index) { // ----------- S t a t e ------------- // -- rax : key @@ -2797,7 +2679,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, __ IncrementCounter(counters->keyed_load_field(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss); @@ -2811,34 +2693,27 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- Label miss; - Counters* counters = isolate()->counters(); __ IncrementCounter(counters->keyed_load_callback(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); - MaybeObject* result = GenerateLoadCallback(receiver, holder, rdx, rax, rbx, - rcx, rdi, callback, name, &miss); - if (result->IsFailure()) { - miss.Unuse(); - return result; - } - + GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, callback, + name, &miss); __ bind(&miss); - __ DecrementCounter(counters->keyed_load_callback(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2847,10 +2722,11 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( } -MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> value) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -2862,7 +2738,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, __ IncrementCounter(counters->keyed_load_constant_function(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); GenerateLoadConstant(receiver, holder, rdx, rbx, rcx, rdi, @@ -2876,35 +2752,27 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor( + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- Label miss; - Counters* counters = isolate()->counters(); __ IncrementCounter(counters->keyed_load_interceptor(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); - LookupResult lookup; + LookupResult lookup(isolate()); LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(receiver, - holder, - &lookup, - rdx, - rax, - rcx, - rbx, - rdi, - name, - &miss); + GenerateLoadInterceptor(receiver, holder, &lookup, rdx, rax, rcx, rbx, rdi, + name, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_load_interceptor(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2914,7 +2782,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -2926,7 +2795,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { __ IncrementCounter(counters->keyed_load_array_length(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); GenerateLoadArrayLength(masm(), rdx, rcx, &miss); @@ -2939,7 +2808,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength( + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -2951,7 +2821,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ IncrementCounter(counters->keyed_load_string_length(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss, true); @@ -2964,7 +2834,8 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype( + Handle<String> name) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -2976,7 +2847,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { __ IncrementCounter(counters->keyed_load_function_prototype(), 1); // Check that the name has not changed. - __ Cmp(rax, Handle<String>(name)); + __ Cmp(rax, name); __ j(not_equal, &miss); GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss); @@ -2989,32 +2860,29 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { } -MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadElement( + Handle<Map> receiver_map) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Code* stub; ElementsKind elements_kind = receiver_map->elements_kind(); - MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); - if (!maybe_stub->To(&stub)) return maybe_stub; - __ DispatchMap(rdx, - Handle<Map>(receiver_map), - Handle<Code>(stub), - DO_SMI_CHECK); + Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode(); + + __ DispatchMap(rdx, receiver_map, stub, DO_SMI_CHECK); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss(); __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(NORMAL, factory()->empty_string()); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( - MapList* receiver_maps, - CodeList* handler_ics) { +Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic( + MapHandleList* receiver_maps, + CodeHandleList* handler_ics) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -3028,24 +2896,22 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic( int receiver_count = receiver_maps->length(); for (int current = 0; current < receiver_count; ++current) { // Check map and tail call if there's a match - Handle<Map> map(receiver_maps->at(current)); - __ Cmp(map_reg, map); - __ j(equal, - Handle<Code>(handler_ics->at(current)), - RelocInfo::CODE_TARGET); + __ Cmp(map_reg, receiver_maps->at(current)); + __ j(equal, handler_ics->at(current), RelocInfo::CODE_TARGET); } __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(NORMAL, NULL, MEGAMORPHIC); + return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); } // Specialized stub for constructing objects from functions which only have only // simple assignments of the form this.x = ...; in their body. -MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { +Handle<Code> ConstructStubCompiler::CompileConstructStub( + Handle<JSFunction> function) { // ----------- S t a t e ------------- // -- rax : argc // -- rdi : constructor @@ -3088,12 +2954,8 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // rbx: initial map __ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset)); __ shl(rcx, Immediate(kPointerSizeLog2)); - __ AllocateInNewSpace(rcx, - rdx, - rcx, - no_reg, - &generic_stub_call, - NO_ALLOCATION_FLAGS); + __ AllocateInNewSpace(rcx, rdx, rcx, no_reg, + &generic_stub_call, NO_ALLOCATION_FLAGS); // Allocated the JSObject, now initialize the fields and add the heap tag. // rbx: initial map @@ -3118,7 +2980,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // r9: first in-object property of the JSObject // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. - SharedFunctionInfo* shared = function->shared(); + Handle<SharedFunctionInfo> shared(function->shared()); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { // Check if the argument assigned to the property is actually passed. @@ -3166,10 +3028,8 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // Jump to the generic stub in case the specialized code cannot handle the // construction. __ bind(&generic_stub_call); - Code* code = - isolate()->builtins()->builtin(Builtins::kJSConstructStubGeneric); - Handle<Code> generic_construct_stub(code); - __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); + Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric(); + __ Jump(code, RelocInfo::CODE_TARGET); // Return the generated code. return GetCode(); @@ -3436,6 +3296,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); break; case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3503,6 +3364,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: + case FAST_SMI_ONLY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: @@ -3634,15 +3496,17 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement( } -void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, - bool is_js_array) { +void KeyedStoreStubCompiler::GenerateStoreFastElement( + MacroAssembler* masm, + bool is_js_array, + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Label miss_force_generic; + Label miss_force_generic, transition_elements_kind; // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. @@ -3665,13 +3529,22 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ j(above_equal, &miss_force_generic); } - // Do the store and update the write barrier. Make sure to preserve - // the value in register eax. - __ movq(rdx, rax); - __ SmiToInteger32(rcx, rcx); - __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), - rax); - __ RecordWrite(rdi, 0, rdx, rcx); + if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + __ JumpIfNotSmi(rax, &transition_elements_kind); + __ SmiToInteger32(rcx, rcx); + __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), + rax); + } else { + // Do the store and update the write barrier. + ASSERT(elements_kind == FAST_ELEMENTS); + __ SmiToInteger32(rcx, rcx); + __ lea(rcx, + FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize)); + __ movq(Operand(rcx, 0), rax); + // Make sure to preserve the value in register rax. + __ movq(rdx, rax); + __ RecordWrite(rdi, rcx, rdx, kDontSaveFPRegs); + } // Done. __ ret(0); @@ -3681,6 +3554,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, Handle<Code> ic_force_generic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ jmp(ic_force_generic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ jmp(ic_miss, RelocInfo::CODE_TARGET); } @@ -3693,8 +3570,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Label miss_force_generic, smi_value, is_nan, maybe_nan; - Label have_double_value, not_nan; + Label miss_force_generic, transition_elements_kind; // This stub is meant to be tail-jumped to, the receiver must already // have been verified by the caller to not be a smi. @@ -3715,50 +3591,9 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( __ j(above_equal, &miss_force_generic); // Handle smi values specially - __ JumpIfSmi(rax, &smi_value, Label::kNear); - - __ CheckMap(rax, - masm->isolate()->factory()->heap_number_map(), - &miss_force_generic, - DONT_DO_SMI_CHECK); - - // Double value, canonicalize NaN. - uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32); - __ cmpl(FieldOperand(rax, offset), - Immediate(kNaNOrInfinityLowerBoundUpper32)); - __ j(greater_equal, &maybe_nan, Label::kNear); - - __ bind(¬_nan); - __ movsd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset)); - __ bind(&have_double_value); - __ SmiToInteger32(rcx, rcx); - __ movsd(FieldOperand(rdi, rcx, times_8, FixedDoubleArray::kHeaderSize), - xmm0); - __ ret(0); - - __ bind(&maybe_nan); - // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise - // it's an Infinity, and the non-NaN code path applies. - __ j(greater, &is_nan, Label::kNear); - __ cmpl(FieldOperand(rax, HeapNumber::kValueOffset), Immediate(0)); - __ j(zero, ¬_nan); - __ bind(&is_nan); - // Convert all NaNs to the same canonical NaN value when they are stored in - // the double array. - __ Set(kScratchRegister, BitCast<uint64_t>( - FixedDoubleArray::canonical_not_the_hole_nan_as_double())); - __ movq(xmm0, kScratchRegister); - __ jmp(&have_double_value, Label::kNear); - - __ bind(&smi_value); - // Value is a smi. convert to a double and store. - // Preserve original value. - __ SmiToInteger32(rdx, rax); - __ push(rdx); - __ fild_s(Operand(rsp, 0)); - __ pop(rdx); __ SmiToInteger32(rcx, rcx); - __ fstp_d(FieldOperand(rdi, rcx, times_8, FixedDoubleArray::kHeaderSize)); + __ StoreNumberToDoubleElements(rax, rdi, rcx, xmm0, + &transition_elements_kind); __ ret(0); // Handle store cache miss, replacing the ic with the generic stub. @@ -3766,6 +3601,12 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement( Handle<Code> ic_force_generic = masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); __ jmp(ic_force_generic, RelocInfo::CODE_TARGET); + + __ bind(&transition_elements_kind); + // Restore smi-tagging of rcx. + __ Integer32ToSmi(rcx, rcx); + Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss(); + __ jmp(ic_miss, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/zone-inl.h b/deps/v8/src/zone-inl.h index 4870105f31..9a2618c5a8 100644 --- a/deps/v8/src/zone-inl.h +++ b/deps/v8/src/zone-inl.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,8 +28,11 @@ #ifndef V8_ZONE_INL_H_ #define V8_ZONE_INL_H_ -#include "isolate.h" #include "zone.h" + +#include "counters.h" +#include "isolate.h" +#include "utils.h" #include "v8-counters.h" namespace v8 { @@ -53,6 +56,14 @@ inline void* Zone::New(int size) { // Round up the requested size to fit the alignment. size = RoundUp(size, kAlignment); + // If the allocation size is divisible by 8 then we return an 8-byte aligned + // address. + if (kPointerSize == 4 && kAlignment == 4) { + position_ += ((~size) & 4) & (reinterpret_cast<intptr_t>(position_) & 4); + } else { + ASSERT(kAlignment >= kPointerSize); + } + // Check if the requested size is available without expanding. Address result = position_; diff --git a/deps/v8/src/zone.cc b/deps/v8/src/zone.cc index 2d14d137ef..d5d05ab95f 100644 --- a/deps/v8/src/zone.cc +++ b/deps/v8/src/zone.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,10 +25,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" +#include <string.h> +#include "v8.h" #include "zone-inl.h" -#include "splay-tree-inl.h" namespace v8 { namespace internal { diff --git a/deps/v8/src/zone.h b/deps/v8/src/zone.h index f60ac0d3ef..420afc23f4 100644 --- a/deps/v8/src/zone.h +++ b/deps/v8/src/zone.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,6 +29,10 @@ #define V8_ZONE_H_ #include "allocation.h" +#include "checks.h" +#include "globals.h" +#include "list.h" +#include "splay-tree.h" namespace v8 { namespace internal { @@ -42,6 +46,7 @@ enum ZoneScopeMode { }; class Segment; +class Isolate; // The Zone supports very fast allocation of small chunks of // memory. The chunks cannot be deallocated individually, but instead @@ -86,7 +91,9 @@ class Zone { friend class Isolate; friend class ZoneScope; - // All pointers returned from New() have this alignment. + // All pointers returned from New() have this alignment. In addition, if the + // object being allocated has a size that is divisible by 8 then its alignment + // will be 8. static const int kAlignment = kPointerSize; // Never allocate segments smaller than this size in bytes. diff --git a/deps/v8/test/cctest/SConscript b/deps/v8/test/cctest/SConscript index 5c9267186d..edb859e9cb 100644 --- a/deps/v8/test/cctest/SConscript +++ b/deps/v8/test/cctest/SConscript @@ -111,7 +111,8 @@ SOURCES = { ], 'arch:x64': ['test-assembler-x64.cc', 'test-macro-assembler-x64.cc', - 'test-log-stack-tracer.cc'], + 'test-log-stack-tracer.cc', + 'test-disasm-x64.cc'], 'arch:mips': ['test-assembler-mips.cc', 'test-disasm-mips.cc'], 'os:linux': ['test-platform-linux.cc'], diff --git a/deps/v8/test/cctest/cctest.gyp b/deps/v8/test/cctest/cctest.gyp index 5d0cab3e98..3b8f4f653d 100644 --- a/deps/v8/test/cctest/cctest.gyp +++ b/deps/v8/test/cctest/cctest.gyp @@ -68,6 +68,7 @@ 'test-fixed-dtoa.cc', 'test-flags.cc', 'test-func-name-inference.cc', + 'test-hashing.cc', 'test-hashmap.cc', 'test-heap.cc', 'test-heap-profiler.cc', @@ -91,7 +92,8 @@ 'test-threads.cc', 'test-unbound-queue.cc', 'test-utils.cc', - 'test-version.cc' + 'test-version.cc', + 'test-weakmaps.cc' ], 'conditions': [ ['v8_target_arch=="ia32"', { @@ -134,6 +136,12 @@ 'sources': [ 'test-platform-win32.cc', ], + 'msvs_settings': { + 'VCCLCompilerTool': { + # MSVS wants this for gay-{precision,shortest}.cc. + 'AdditionalOptions': ['/bigobj'], + }, + }, }], ['component=="shared_library"', { # cctest can't be built against a shared library, so we need to diff --git a/deps/v8/test/cctest/cctest.h b/deps/v8/test/cctest/cctest.h index c04d893c10..0b93562216 100644 --- a/deps/v8/test/cctest/cctest.h +++ b/deps/v8/test/cctest/cctest.h @@ -104,7 +104,7 @@ class ApiTestFuzzer: public v8::internal::Thread { FOURTH_PART, LAST_PART = FOURTH_PART }; - static void Setup(PartOfTest part); + static void SetUp(PartOfTest part); static void RunAllTests(); static void TearDown(); // This method switches threads if we are running the Threading test. diff --git a/deps/v8/test/cctest/cctest.status b/deps/v8/test/cctest/cctest.status index 5122da5ae3..2de0afba17 100644 --- a/deps/v8/test/cctest/cctest.status +++ b/deps/v8/test/cctest/cctest.status @@ -33,11 +33,22 @@ test-api/Bug*: FAIL # BUG(382): Weird test. Can't guarantee that it never times out. test-api/ApplyInterruption: PASS || TIMEOUT +# BUG(484): This test which we thought was originally corrected in r5236 +# is re-appearing. Disabled until bug in test is fixed. This only fails +# when snapshot is on, so I am marking it PASS || FAIL +test-heap-profiler/HeapSnapshotsDiff: PASS || FAIL + # These tests always fail. They are here to test test.py. If # they don't fail then test.py has failed. test-serialize/TestThatAlwaysFails: FAIL test-serialize/DependentTestThatAlwaysFails: FAIL +# TODO(gc): Temporarily disabled in the GC branch. +test-log/EquivalenceOfLoggingAndTraversal: PASS || FAIL + +# BUG(1261): Flakey test. +test-profile-generator/RecordStackTraceAtStartProfiling: PASS || FAIL + # We do not yet shrink weak maps after they have been emptied by the GC test-weakmaps/Shrinking: FAIL @@ -74,9 +85,11 @@ test-debug/DebugBreakLoop: SKIP ############################################################################## [ $arch == mips ] -test-deoptimization: SKIP test-serialize: SKIP -# Tests that may time out. -test-api/ExternalArrays: PASS || TIMEOUT -test-api/Threading: PASS || TIMEOUT +############################################################################## +[ $arch == mips && $crankshaft ] + +# Tests that time out with crankshaft. +test-debug/ThreadedDebugging: SKIP +test-debug/DebugBreakLoop: SKIP diff --git a/deps/v8/test/cctest/test-accessors.cc b/deps/v8/test/cctest/test-accessors.cc index d95536d2d5..b1900f9ed3 100644 --- a/deps/v8/test/cctest/test-accessors.cc +++ b/deps/v8/test/cctest/test-accessors.cc @@ -241,7 +241,7 @@ static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name, ApiTestFuzzer::Fuzz(); CHECK(info.This() == info.Holder()); CHECK(info.Data()->Equals(v8::String::New("data"))); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK(info.This() == info.Holder()); CHECK(info.Data()->Equals(v8::String::New("data"))); return v8::Integer::New(17); diff --git a/deps/v8/test/cctest/test-alloc.cc b/deps/v8/test/cctest/test-alloc.cc index 97671923d9..c654dfa8bb 100644 --- a/deps/v8/test/cctest/test-alloc.cc +++ b/deps/v8/test/cctest/test-alloc.cc @@ -1,4 +1,4 @@ -// Copyright 2007-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -72,11 +72,29 @@ static MaybeObject* AllocateAfterFailures() { } CHECK(!heap->AllocateRawAsciiString(100, TENURED)->IsFailure()); + // Old pointer space. + OldSpace* old_pointer_space = heap->old_pointer_space(); + static const int kOldPointerSpaceFillerLength = 10000; + static const int kOldPointerSpaceFillerSize = FixedArray::SizeFor( + kOldPointerSpaceFillerLength); + while (old_pointer_space->Available() > kOldPointerSpaceFillerSize) { + CHECK(!heap->AllocateFixedArray(kOldPointerSpaceFillerLength, TENURED)-> + IsFailure()); + } + CHECK(!heap->AllocateFixedArray(kOldPointerSpaceFillerLength, TENURED)-> + IsFailure()); + // Large object space. - while (!heap->OldGenerationAllocationLimitReached()) { - CHECK(!heap->AllocateFixedArray(10000, TENURED)->IsFailure()); + static const int kLargeObjectSpaceFillerLength = 300000; + static const int kLargeObjectSpaceFillerSize = FixedArray::SizeFor( + kLargeObjectSpaceFillerLength); + ASSERT(kLargeObjectSpaceFillerSize > heap->MaxObjectSizeInPagedSpace()); + while (heap->OldGenerationSpaceAvailable() > kLargeObjectSpaceFillerSize) { + CHECK(!heap->AllocateFixedArray(kLargeObjectSpaceFillerLength, TENURED)-> + IsFailure()); } - CHECK(!heap->AllocateFixedArray(10000, TENURED)->IsFailure()); + CHECK(!heap->AllocateFixedArray(kLargeObjectSpaceFillerLength, TENURED)-> + IsFailure()); // Map space. MapSpace* map_space = heap->map_space(); @@ -175,20 +193,20 @@ unsigned int Pseudorandom() { // Plain old data class. Represents a block of allocated memory. class Block { public: - Block(void* base_arg, int size_arg) + Block(Address base_arg, int size_arg) : base(base_arg), size(size_arg) {} - void *base; + Address base; int size; }; TEST(CodeRange) { - const int code_range_size = 16*MB; - OS::Setup(); + const int code_range_size = 32*MB; + OS::SetUp(); Isolate::Current()->InitializeLoggingAndCounters(); CodeRange* code_range = new CodeRange(Isolate::Current()); - code_range->Setup(code_range_size); + code_range->SetUp(code_range_size); int current_allocated = 0; int total_allocated = 0; List<Block> blocks(1000); @@ -196,11 +214,13 @@ TEST(CodeRange) { while (total_allocated < 5 * code_range_size) { if (current_allocated < code_range_size / 10) { // Allocate a block. - // Geometrically distributed sizes, greater than Page::kPageSize. - size_t requested = (Page::kPageSize << (Pseudorandom() % 6)) + + // Geometrically distributed sizes, greater than Page::kMaxHeapObjectSize. + // TODO(gc): instead of using 3 use some contant based on code_range_size + // kMaxHeapObjectSize. + size_t requested = (Page::kMaxHeapObjectSize << (Pseudorandom() % 3)) + Pseudorandom() % 5000 + 1; size_t allocated = 0; - void* base = code_range->AllocateRawMemory(requested, &allocated); + Address base = code_range->AllocateRawMemory(requested, &allocated); CHECK(base != NULL); blocks.Add(Block(base, static_cast<int>(allocated))); current_allocated += static_cast<int>(allocated); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index c1c8aae592..87d5453756 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -80,6 +80,11 @@ static void ExpectString(const char* code, const char* expected) { CHECK_EQ(expected, *ascii); } +static void ExpectInt32(const char* code, int expected) { + Local<Value> result = CompileRun(code); + CHECK(result->IsInt32()); + CHECK_EQ(expected, result->Int32Value()); +} static void ExpectBoolean(const char* code, bool expected) { Local<Value> result = CompileRun(code); @@ -393,11 +398,11 @@ THREADED_TEST(ScriptUsingStringResource) { CHECK(source->IsExternal()); CHECK_EQ(resource, static_cast<TestResource*>(source->GetExternalStringResource())); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(0, dispose_count); } v8::internal::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(1, dispose_count); } @@ -415,11 +420,11 @@ THREADED_TEST(ScriptUsingAsciiStringResource) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(1, dispose_count); } @@ -441,11 +446,12 @@ THREADED_TEST(ScriptMakingExternalString) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + // TODO(1608): This should use kAbortIncrementalMarking. + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); CHECK_EQ(1, dispose_count); } @@ -467,11 +473,12 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(0, dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + // TODO(1608): This should use kAbortIncrementalMarking. + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); CHECK_EQ(1, dispose_count); } @@ -484,7 +491,7 @@ TEST(MakingExternalStringConditions) { HEAP->CollectGarbage(i::NEW_SPACE); HEAP->CollectGarbage(i::NEW_SPACE); - uint16_t* two_byte_string = AsciiToTwoByteString("small"); + uint16_t* two_byte_string = AsciiToTwoByteString("s1"); Local<String> small_string = String::New(two_byte_string); i::DeleteArray(two_byte_string); @@ -496,7 +503,7 @@ TEST(MakingExternalStringConditions) { // Old space strings should be accepted. CHECK(small_string->CanMakeExternal()); - two_byte_string = AsciiToTwoByteString("small 2"); + two_byte_string = AsciiToTwoByteString("small string 2"); small_string = String::New(two_byte_string); i::DeleteArray(two_byte_string); @@ -530,7 +537,7 @@ TEST(MakingExternalAsciiStringConditions) { HEAP->CollectGarbage(i::NEW_SPACE); HEAP->CollectGarbage(i::NEW_SPACE); - Local<String> small_string = String::New("small"); + Local<String> small_string = String::New("s1"); // We should refuse to externalize newly created small string. CHECK(!small_string->CanMakeExternal()); // Trigger GCs so that the newly allocated string moves to old gen. @@ -539,7 +546,7 @@ TEST(MakingExternalAsciiStringConditions) { // Old space strings should be accepted. CHECK(small_string->CanMakeExternal()); - small_string = String::New("small 2"); + small_string = String::New("small string 2"); // We should refuse externalizing newly created small string. CHECK(!small_string->CanMakeExternal()); for (int i = 0; i < 100; i++) { @@ -572,8 +579,8 @@ THREADED_TEST(UsingExternalString) { i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); CHECK(isymbol->IsSymbol()); } - HEAP->CollectAllGarbage(false); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -590,8 +597,8 @@ THREADED_TEST(UsingExternalAsciiString) { i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); CHECK(isymbol->IsSymbol()); } - HEAP->CollectAllGarbage(false); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -672,11 +679,11 @@ TEST(ExternalStringWithDisposeHandling) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); @@ -693,11 +700,11 @@ TEST(ExternalStringWithDisposeHandling) { Local<Value> value = script->Run(); CHECK(value->IsNumber()); CHECK_EQ(7, value->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllAvailableGarbage(); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_count); } @@ -744,8 +751,8 @@ THREADED_TEST(StringConcat) { CHECK_EQ(68, value->Int32Value()); } i::Isolate::Current()->compilation_cache()->Clear(); - HEAP->CollectAllGarbage(false); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -1182,9 +1189,8 @@ THREADED_TEST(GlobalPrototype) { templ->Set("x", v8_num(200)); templ->SetAccessor(v8_str("m"), GetM); LocalContext env(0, templ); - v8::Handle<v8::Object> obj = env->Global(); - v8::Handle<Script> script = v8_compile("dummy()"); - v8::Handle<Value> result = script->Run(); + v8::Handle<Script> script(v8_compile("dummy()")); + v8::Handle<Value> result(script->Run()); CHECK_EQ(13.4, result->NumberValue()); CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); @@ -1294,6 +1300,279 @@ static v8::Handle<Value> EchoNamedProperty(Local<String> name, return name; } +// Helper functions for Interceptor/Accessor interaction tests + +Handle<Value> SimpleAccessorGetter(Local<String> name, + const AccessorInfo& info) { + Handle<Object> self = info.This(); + return self->Get(String::Concat(v8_str("accessor_"), name)); +} + +void SimpleAccessorSetter(Local<String> name, Local<Value> value, + const AccessorInfo& info) { + Handle<Object> self = info.This(); + self->Set(String::Concat(v8_str("accessor_"), name), value); +} + +Handle<Value> EmptyInterceptorGetter(Local<String> name, + const AccessorInfo& info) { + return Handle<Value>(); +} + +Handle<Value> EmptyInterceptorSetter(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + return Handle<Value>(); +} + +Handle<Value> InterceptorGetter(Local<String> name, + const AccessorInfo& info) { + // Intercept names that start with 'interceptor_'. + String::AsciiValue ascii(name); + char* name_str = *ascii; + char prefix[] = "interceptor_"; + int i; + for (i = 0; name_str[i] && prefix[i]; ++i) { + if (name_str[i] != prefix[i]) return Handle<Value>(); + } + Handle<Object> self = info.This(); + return self->GetHiddenValue(v8_str(name_str + i)); +} + +Handle<Value> InterceptorSetter(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + // Intercept accesses that set certain integer values. + if (value->IsInt32() && value->Int32Value() < 10000) { + Handle<Object> self = info.This(); + self->SetHiddenValue(name, value); + return value; + } + return Handle<Value>(); +} + +void AddAccessor(Handle<FunctionTemplate> templ, + Handle<String> name, + v8::AccessorGetter getter, + v8::AccessorSetter setter) { + templ->PrototypeTemplate()->SetAccessor(name, getter, setter); +} + +void AddInterceptor(Handle<FunctionTemplate> templ, + v8::NamedPropertyGetter getter, + v8::NamedPropertySetter setter) { + templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); +} + +THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddAccessor(parent, v8_str("age"), + SimpleAccessorGetter, SimpleAccessorSetter); + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "child.age = 10;"); + ExpectBoolean("child.hasOwnProperty('age')", false); + ExpectInt32("child.age", 10); + ExpectInt32("child.accessor_age", 10); +} + +THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "var parent = child.__proto__;" + "Object.defineProperty(parent, 'age', " + " {get: function(){ return this.accessor_age; }, " + " set: function(v){ this.accessor_age = v; }, " + " enumerable: true, configurable: true});" + "child.age = 10;"); + ExpectBoolean("child.hasOwnProperty('age')", false); + ExpectInt32("child.age", 10); + ExpectInt32("child.accessor_age", 10); +} + +THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "var parent = child.__proto__;" + "parent.name = 'Alice';"); + ExpectBoolean("child.hasOwnProperty('name')", false); + ExpectString("child.name", "Alice"); + CompileRun("child.name = 'Bob';"); + ExpectString("child.name", "Bob"); + ExpectBoolean("child.hasOwnProperty('name')", true); + ExpectString("parent.name", "Alice"); +} + +THREADED_TEST(SwitchFromInterceptorToAccessor) { + v8::HandleScope scope; + Handle<FunctionTemplate> templ = FunctionTemplate::New(); + AddAccessor(templ, v8_str("age"), + SimpleAccessorGetter, SimpleAccessorSetter); + AddInterceptor(templ, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Obj"), templ->GetFunction()); + CompileRun("var obj = new Obj;" + "function setAge(i){ obj.age = i; };" + "for(var i = 0; i <= 10000; i++) setAge(i);"); + // All i < 10000 go to the interceptor. + ExpectInt32("obj.interceptor_age", 9999); + // The last i goes to the accessor. + ExpectInt32("obj.accessor_age", 10000); +} + +THREADED_TEST(SwitchFromAccessorToInterceptor) { + v8::HandleScope scope; + Handle<FunctionTemplate> templ = FunctionTemplate::New(); + AddAccessor(templ, v8_str("age"), + SimpleAccessorGetter, SimpleAccessorSetter); + AddInterceptor(templ, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Obj"), templ->GetFunction()); + CompileRun("var obj = new Obj;" + "function setAge(i){ obj.age = i; };" + "for(var i = 20000; i >= 9999; i--) setAge(i);"); + // All i >= 10000 go to the accessor. + ExpectInt32("obj.accessor_age", 10000); + // The last i goes to the interceptor. + ExpectInt32("obj.interceptor_age", 9999); +} + +THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddAccessor(parent, v8_str("age"), + SimpleAccessorGetter, SimpleAccessorSetter); + AddInterceptor(child, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "function setAge(i){ child.age = i; };" + "for(var i = 0; i <= 10000; i++) setAge(i);"); + // All i < 10000 go to the interceptor. + ExpectInt32("child.interceptor_age", 9999); + // The last i goes to the accessor. + ExpectInt32("child.accessor_age", 10000); +} + +THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddAccessor(parent, v8_str("age"), + SimpleAccessorGetter, SimpleAccessorSetter); + AddInterceptor(child, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "function setAge(i){ child.age = i; };" + "for(var i = 20000; i >= 9999; i--) setAge(i);"); + // All i >= 10000 go to the accessor. + ExpectInt32("child.accessor_age", 10000); + // The last i goes to the interceptor. + ExpectInt32("child.interceptor_age", 9999); +} + +THREADED_TEST(SwitchFromInterceptorToJSAccessor) { + v8::HandleScope scope; + Handle<FunctionTemplate> templ = FunctionTemplate::New(); + AddInterceptor(templ, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Obj"), templ->GetFunction()); + CompileRun("var obj = new Obj;" + "function setter(i) { this.accessor_age = i; };" + "function getter() { return this.accessor_age; };" + "function setAge(i) { obj.age = i; };" + "Object.defineProperty(obj, 'age', { get:getter, set:setter });" + "for(var i = 0; i <= 10000; i++) setAge(i);"); + // All i < 10000 go to the interceptor. + ExpectInt32("obj.interceptor_age", 9999); + // The last i goes to the JavaScript accessor. + ExpectInt32("obj.accessor_age", 10000); + // The installed JavaScript getter is still intact. + // This last part is a regression test for issue 1651 and relies on the fact + // that both interceptor and accessor are being installed on the same object. + ExpectInt32("obj.age", 10000); + ExpectBoolean("obj.hasOwnProperty('age')", true); + ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); +} + +THREADED_TEST(SwitchFromJSAccessorToInterceptor) { + v8::HandleScope scope; + Handle<FunctionTemplate> templ = FunctionTemplate::New(); + AddInterceptor(templ, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Obj"), templ->GetFunction()); + CompileRun("var obj = new Obj;" + "function setter(i) { this.accessor_age = i; };" + "function getter() { return this.accessor_age; };" + "function setAge(i) { obj.age = i; };" + "Object.defineProperty(obj, 'age', { get:getter, set:setter });" + "for(var i = 20000; i >= 9999; i--) setAge(i);"); + // All i >= 10000 go to the accessor. + ExpectInt32("obj.accessor_age", 10000); + // The last i goes to the interceptor. + ExpectInt32("obj.interceptor_age", 9999); + // The installed JavaScript getter is still intact. + // This last part is a regression test for issue 1651 and relies on the fact + // that both interceptor and accessor are being installed on the same object. + ExpectInt32("obj.age", 10000); + ExpectBoolean("obj.hasOwnProperty('age')", true); + ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); +} + +THREADED_TEST(SwitchFromInterceptorToProperty) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddInterceptor(child, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "function setAge(i){ child.age = i; };" + "for(var i = 0; i <= 10000; i++) setAge(i);"); + // All i < 10000 go to the interceptor. + ExpectInt32("child.interceptor_age", 9999); + // The last i goes to child's own property. + ExpectInt32("child.age", 10000); +} + +THREADED_TEST(SwitchFromPropertyToInterceptor) { + v8::HandleScope scope; + Handle<FunctionTemplate> parent = FunctionTemplate::New(); + Handle<FunctionTemplate> child = FunctionTemplate::New(); + child->Inherit(parent); + AddInterceptor(child, InterceptorGetter, InterceptorSetter); + LocalContext env; + env->Global()->Set(v8_str("Child"), child->GetFunction()); + CompileRun("var child = new Child;" + "function setAge(i){ child.age = i; };" + "for(var i = 20000; i >= 9999; i--) setAge(i);"); + // All i >= 10000 go to child's own property. + ExpectInt32("child.age", 10000); + // The last i goes to the interceptor. + ExpectInt32("child.interceptor_age", 9999); +} THREADED_TEST(NamedPropertyHandlerGetter) { echo_named_call_count = 0; @@ -1567,7 +1846,7 @@ THREADED_TEST(DeepCrossLanguageRecursion) { env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); call_recursively_script = v8_compile("callScriptRecursively()"); - v8::Handle<Value> result = call_recursively_script->Run(); + call_recursively_script->Run(); call_recursively_script = v8::Handle<Script>(); env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); @@ -1666,12 +1945,12 @@ THREADED_TEST(InternalFieldsNativePointers) { // Check reading and writing aligned pointers. obj->SetPointerInInternalField(0, aligned); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); // Check reading and writing unaligned pointers. obj->SetPointerInInternalField(0, unaligned); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); delete[] data; @@ -1697,19 +1976,19 @@ THREADED_TEST(InternalFieldsNativePointersAndExternal) { CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); obj->SetPointerInInternalField(0, aligned); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); obj->SetPointerInInternalField(0, unaligned); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); obj->SetInternalField(0, v8::External::Wrap(aligned)); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); obj->SetInternalField(0, v8::External::Wrap(unaligned)); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); delete[] data; @@ -1722,7 +2001,7 @@ THREADED_TEST(IdentityHash) { // Ensure that the test starts with an fresh heap to test whether the hash // code is based on the address. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); Local<v8::Object> obj = v8::Object::New(); int hash = obj->GetIdentityHash(); int hash1 = obj->GetIdentityHash(); @@ -1732,7 +2011,7 @@ THREADED_TEST(IdentityHash) { // objects should not be assigned the same hash code. If the test below fails // the random number generator should be evaluated. CHECK_NE(hash, hash2); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); int hash3 = v8::Object::New()->GetIdentityHash(); // Make sure that the identity hash is not based on the initial address of // the object alone. If the test below fails the random number generator @@ -1769,7 +2048,7 @@ THREADED_TEST(HiddenProperties) { v8::Local<v8::String> empty = v8_str(""); v8::Local<v8::String> prop_name = v8_str("prop_name"); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); // Make sure delete of a non-existent hidden value works CHECK(obj->DeleteHiddenValue(key)); @@ -1779,7 +2058,7 @@ THREADED_TEST(HiddenProperties) { CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); // Make sure we do not find the hidden property. CHECK(!obj->Has(empty)); @@ -1790,7 +2069,7 @@ THREADED_TEST(HiddenProperties) { CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); CHECK_EQ(2003, obj->Get(empty)->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); // Add another property and delete it afterwards to force the object in // slow case. @@ -1801,7 +2080,7 @@ THREADED_TEST(HiddenProperties) { CHECK(obj->Delete(prop_name)); CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK(obj->DeleteHiddenValue(key)); CHECK(obj->GetHiddenValue(key).IsEmpty()); @@ -1908,19 +2187,30 @@ THREADED_TEST(GlobalHandle) { } -static int NumberOfWeakCalls = 0; +class WeakCallCounter { + public: + explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) { } + int id() { return id_; } + void increment() { number_of_weak_calls_++; } + int NumberOfWeakCalls() { return number_of_weak_calls_; } + private: + int id_; + int number_of_weak_calls_; +}; + + static void WeakPointerCallback(Persistent<Value> handle, void* id) { - CHECK_EQ(reinterpret_cast<void*>(1234), id); - NumberOfWeakCalls++; + WeakCallCounter* counter = reinterpret_cast<WeakCallCounter*>(id); + CHECK_EQ(1234, counter->id()); + counter->increment(); handle.Dispose(); } + THREADED_TEST(ApiObjectGroups) { HandleScope scope; LocalContext env; - NumberOfWeakCalls = 0; - Persistent<Object> g1s1; Persistent<Object> g1s2; Persistent<Object> g1c1; @@ -1928,21 +2218,23 @@ THREADED_TEST(ApiObjectGroups) { Persistent<Object> g2s2; Persistent<Object> g2c1; + WeakCallCounter counter(1234); + { HandleScope scope; g1s1 = Persistent<Object>::New(Object::New()); g1s2 = Persistent<Object>::New(Object::New()); g1c1 = Persistent<Object>::New(Object::New()); - g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); g2s1 = Persistent<Object>::New(Object::New()); g2s2 = Persistent<Object>::New(Object::New()); g2c1 = Persistent<Object>::New(Object::New()); - g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); } Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. @@ -1961,14 +2253,15 @@ THREADED_TEST(ApiObjectGroups) { V8::AddObjectGroup(g2_objects, 2); V8::AddImplicitReferences(g2s2, g2_children, 1); } - // Do a full GC - HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + // Do a single full GC. Use kMakeHeapIterableMask to ensure that + // incremental garbage collection is stopped. + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); // All object should be alive. - CHECK_EQ(0, NumberOfWeakCalls); + CHECK_EQ(0, counter.NumberOfWeakCalls()); // Weaken the root. - root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); // But make children strong roots---all the objects (except for children) // should be collectable now. g1c1.ClearWeak(); @@ -1986,17 +2279,17 @@ THREADED_TEST(ApiObjectGroups) { V8::AddImplicitReferences(g2s2, g2_children, 1); } - HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); // All objects should be gone. 5 global handles in total. - CHECK_EQ(5, NumberOfWeakCalls); + CHECK_EQ(5, counter.NumberOfWeakCalls()); // And now make children weak again and collect them. - g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); - HEAP->CollectGarbage(i::OLD_POINTER_SPACE); - CHECK_EQ(7, NumberOfWeakCalls); + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); + CHECK_EQ(7, counter.NumberOfWeakCalls()); } @@ -2004,7 +2297,7 @@ THREADED_TEST(ApiObjectGroupsCycle) { HandleScope scope; LocalContext env; - NumberOfWeakCalls = 0; + WeakCallCounter counter(1234); Persistent<Object> g1s1; Persistent<Object> g1s2; @@ -2017,18 +2310,18 @@ THREADED_TEST(ApiObjectGroupsCycle) { HandleScope scope; g1s1 = Persistent<Object>::New(Object::New()); g1s2 = Persistent<Object>::New(Object::New()); - g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); g2s1 = Persistent<Object>::New(Object::New()); g2s2 = Persistent<Object>::New(Object::New()); - g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); g3s1 = Persistent<Object>::New(Object::New()); g3s2 = Persistent<Object>::New(Object::New()); - g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); - g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + g3s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); + g3s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); } Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. @@ -2050,14 +2343,14 @@ THREADED_TEST(ApiObjectGroupsCycle) { V8::AddObjectGroup(g3_objects, 2); V8::AddImplicitReferences(g3s1, g3_children, 1); } - // Do a full GC - HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + // Do a single full GC + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); // All object should be alive. - CHECK_EQ(0, NumberOfWeakCalls); + CHECK_EQ(0, counter.NumberOfWeakCalls()); // Weaken the root. - root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); + root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); // Groups are deleted, rebuild groups. { @@ -2075,10 +2368,10 @@ THREADED_TEST(ApiObjectGroupsCycle) { V8::AddImplicitReferences(g3s1, g3_children, 1); } - HEAP->CollectGarbage(i::OLD_POINTER_SPACE); + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); // All objects should be gone. 7 global handles in total. - CHECK_EQ(7, NumberOfWeakCalls); + CHECK_EQ(7, counter.NumberOfWeakCalls()); } @@ -2573,6 +2866,16 @@ THREADED_TEST(isNumberType) { obj = env->Global()->Get(v8_str("obj")); CHECK(!obj->IsInt32()); CHECK(!obj->IsUint32()); + // Positive zero + CompileRun("var obj = 0.0;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(obj->IsInt32()); + CHECK(obj->IsUint32()); + // Positive zero + CompileRun("var obj = -0.0;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(!obj->IsUint32()); } @@ -4172,7 +4475,7 @@ THREADED_TEST(ExtensibleOnUndetectable) { source = v8_str("undetectable.y = 2000;"); script = Script::Compile(source); - Local<Value> result = script->Run(); + script->Run(); ExpectBoolean("undetectable.y == undefined", true); } @@ -4305,6 +4608,47 @@ THREADED_TEST(SimpleExtensions) { } +static const char* kEmbeddedExtensionSource = + "function Ret54321(){return 54321;}~~@@$" + "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; +static const int kEmbeddedExtensionSourceValidLen = 34; + + +THREADED_TEST(ExtensionMissingSourceLength) { + v8::HandleScope handle_scope; + v8::RegisterExtension(new Extension("srclentest_fail", + kEmbeddedExtensionSource)); + const char* extension_names[] = { "srclentest_fail" }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + CHECK_EQ(0, *context); +} + + +THREADED_TEST(ExtensionWithSourceLength) { + for (int source_len = kEmbeddedExtensionSourceValidLen - 1; + source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { + v8::HandleScope handle_scope; + i::ScopedVector<char> extension_name(32); + i::OS::SNPrintF(extension_name, "ext #%d", source_len); + v8::RegisterExtension(new Extension(extension_name.start(), + kEmbeddedExtensionSource, 0, 0, + source_len)); + const char* extension_names[1] = { extension_name.start() }; + v8::ExtensionConfiguration extensions(1, extension_names); + v8::Handle<Context> context = Context::New(&extensions); + if (source_len == kEmbeddedExtensionSourceValidLen) { + Context::Scope lock(context); + v8::Handle<Value> result = Script::Compile(v8_str("Ret54321()"))->Run(); + CHECK_EQ(v8::Integer::New(54321), result); + } else { + // Anything but exactly the right length should fail to compile. + CHECK_EQ(0, *context); + } + } +} + + static const char* kEvalExtensionSource1 = "function UseEval1() {" " var x = 42;" @@ -4483,10 +4827,11 @@ THREADED_TEST(NativeFunctionDeclarationError) { "native\nfunction foo();")); const char* extension_names[] = { name }; v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - ASSERT(context.IsEmpty()); + v8::Handle<Context> context(Context::New(&extensions)); + CHECK(context.IsEmpty()); } + THREADED_TEST(NativeFunctionDeclarationErrorEscape) { v8::HandleScope handle_scope; const char* name = "nativedeclerresc"; @@ -4497,8 +4842,8 @@ THREADED_TEST(NativeFunctionDeclarationErrorEscape) { "nativ\\u0065 function foo();")); const char* extension_names[] = { name }; v8::ExtensionConfiguration extensions(1, extension_names); - v8::Handle<Context> context = Context::New(&extensions); - ASSERT(context.IsEmpty()); + v8::Handle<Context> context(Context::New(&extensions)); + CHECK(context.IsEmpty()); } @@ -4664,7 +5009,7 @@ TEST(RegexpOutOfMemory) { Local<Script> script = Script::Compile(String::New(js_code_causing_huge_string_flattening)); last_location = NULL; - Local<Value> result = script->Run(); + script->Run(); CHECK(false); // Should not return. } @@ -4805,7 +5150,7 @@ static void InvokeScavenge() { static void InvokeMarkSweep() { - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -4898,7 +5243,7 @@ static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { CHECK_EQ(v8::Integer::New(3), args[2]); CHECK_EQ(v8::Undefined(), args[3]); v8::HandleScope scope; - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); return v8::Undefined(); } @@ -5185,67 +5530,109 @@ static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { THREADED_TEST(StringWrite) { + LocalContext context; v8::HandleScope scope; v8::Handle<String> str = v8_str("abcde"); // abc<Icelandic eth><Unicode snowman>. v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); + const int kStride = 4; // Must match stride in for loops in JS below. + CompileRun( + "var left = '';" + "for (var i = 0; i < 0xd800; i += 4) {" + " left = left + String.fromCharCode(i);" + "}"); + CompileRun( + "var right = '';" + "for (var i = 0; i < 0xd800; i += 4) {" + " right = String.fromCharCode(i) + right;" + "}"); + v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); + Handle<String> left_tree = global->Get(v8_str("left")).As<String>(); + Handle<String> right_tree = global->Get(v8_str("right")).As<String>(); CHECK_EQ(5, str2->Length()); + CHECK_EQ(0xd800 / kStride, left_tree->Length()); + CHECK_EQ(0xd800 / kStride, right_tree->Length()); char buf[100]; - char utf8buf[100]; + char utf8buf[0xd800 * 3]; uint16_t wbuf[100]; int len; int charlen; - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); CHECK_EQ(9, len); CHECK_EQ(5, charlen); CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 8, &charlen); CHECK_EQ(8, len); CHECK_EQ(5, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 7, &charlen); CHECK_EQ(5, len); CHECK_EQ(4, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 6, &charlen); CHECK_EQ(5, len); CHECK_EQ(4, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 5, &charlen); CHECK_EQ(5, len); CHECK_EQ(4, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 4, &charlen); CHECK_EQ(3, len); CHECK_EQ(3, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 3, &charlen); CHECK_EQ(3, len); CHECK_EQ(3, charlen); CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); - memset(utf8buf, 0x1, sizeof(utf8buf)); + memset(utf8buf, 0x1, 1000); len = str2->WriteUtf8(utf8buf, 2, &charlen); CHECK_EQ(2, len); CHECK_EQ(2, charlen); CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); + memset(utf8buf, 0x1, sizeof(utf8buf)); + len = left_tree->Utf8Length(); + int utf8_expected = + (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride; + CHECK_EQ(utf8_expected, len); + len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); + CHECK_EQ(utf8_expected, len); + CHECK_EQ(0xd800 / kStride, charlen); + CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); + CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); + CHECK_EQ(0xc0 - kStride, + static_cast<unsigned char>(utf8buf[utf8_expected - 1])); + CHECK_EQ(1, utf8buf[utf8_expected]); + + memset(utf8buf, 0x1, sizeof(utf8buf)); + len = right_tree->Utf8Length(); + CHECK_EQ(utf8_expected, len); + len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); + CHECK_EQ(utf8_expected, len); + CHECK_EQ(0xd800 / kStride, charlen); + CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0])); + CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1])); + CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2])); + CHECK_EQ(1, utf8buf[utf8_expected]); + memset(buf, 0x1, sizeof(buf)); memset(wbuf, 0x1, sizeof(wbuf)); len = str->WriteAscii(buf); @@ -5400,7 +5787,6 @@ THREADED_TEST(ErrorConstruction) { v8::Handle<String> message = v8_str("message"); v8::Handle<Value> range_error = v8::Exception::RangeError(foo); CHECK(range_error->IsObject()); - v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); CHECK(reference_error->IsObject()); @@ -6970,7 +7356,7 @@ THREADED_TEST(CallKnownGlobalReceiver) { // Create new environment reusing the global object. LocalContext env(NULL, instance_template, global_object); env->Global()->Set(v8_str("foo"), foo); - Local<Value> value = Script::Compile(v8_str("foo()"))->Run(); + Script::Compile(v8_str("foo()"))->Run(); } } @@ -7158,6 +7544,60 @@ THREADED_TEST(SetPrototype) { } +// Getting property names of an object with a prototype chain that +// triggers dictionary elements in GetLocalPropertyNames() shouldn't +// crash the runtime. +THREADED_TEST(Regress91517) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope handle_scope; + LocalContext context; + + Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); + t1->SetHiddenPrototype(true); + t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); + Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); + t2->SetHiddenPrototype(true); + t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); + t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New()); + t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); + Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); + t3->SetHiddenPrototype(true); + t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); + Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(); + t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); + + // Force dictionary-based properties. + i::ScopedVector<char> name_buf(1024); + for (int i = 1; i <= 1000; i++) { + i::OS::SNPrintF(name_buf, "sdf%d", i); + t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); + } + + Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); + Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); + Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); + Local<v8::Object> o4 = t4->GetFunction()->NewInstance(); + + // Create prototype chain of hidden prototypes. + CHECK(o4->SetPrototype(o3)); + CHECK(o3->SetPrototype(o2)); + CHECK(o2->SetPrototype(o1)); + + // Call the runtime version of GetLocalPropertyNames() on the natively + // created object through JavaScript. + context->Global()->Set(v8_str("obj"), o4); + CompileRun("var names = %GetLocalPropertyNames(obj);"); + + ExpectInt32("names.length", 1006); + ExpectTrue("names.indexOf(\"baz\") >= 0"); + ExpectTrue("names.indexOf(\"boo\") >= 0"); + ExpectTrue("names.indexOf(\"foo\") >= 0"); + ExpectTrue("names.indexOf(\"fuz1\") >= 0"); + ExpectTrue("names.indexOf(\"fuz2\") >= 0"); + ExpectFalse("names[1005] == undefined"); +} + + THREADED_TEST(FunctionReadOnlyPrototype) { v8::HandleScope handle_scope; LocalContext context; @@ -7241,7 +7681,8 @@ THREADED_TEST(Constructor) { Local<Function> cons = templ->GetFunction(); context->Global()->Set(v8_str("Fun"), cons); Local<v8::Object> inst = cons->NewInstance(); - i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); + i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst)); + CHECK(obj->IsJSObject()); Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); CHECK(value->BooleanValue()); } @@ -7492,9 +7933,11 @@ THREADED_TEST(EvalAliasedDynamic) { " var bar = 2;" " with (x) { return eval('bar'); }" "}" - "f(this)")); + "result4 = f(this)")); script->Run(); - CHECK(try_catch.HasCaught()); + CHECK(!try_catch.HasCaught()); + CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value()); + try_catch.Reset(); } @@ -7508,7 +7951,7 @@ THREADED_TEST(CrossEval) { other->SetSecurityToken(token); current->SetSecurityToken(token); - // Setup reference from current to other. + // Set up reference from current to other. current->Global()->Set(v8_str("other"), other->Global()); // Check that new variables are introduced in other context. @@ -7588,7 +8031,7 @@ THREADED_TEST(EvalInDetachedGlobal) { v8::Persistent<Context> context0 = Context::New(); v8::Persistent<Context> context1 = Context::New(); - // Setup function in context0 that uses eval from context0. + // Set up function in context0 that uses eval from context0. context0->Enter(); v8::Handle<v8::Value> fun = CompileRun("var x = 42;" @@ -7626,7 +8069,7 @@ THREADED_TEST(CrossLazyLoad) { other->SetSecurityToken(token); current->SetSecurityToken(token); - // Setup reference from current to other. + // Set up reference from current to other. current->Global()->Set(v8_str("other"), other->Global()); // Trigger lazy loading in other context. @@ -7710,7 +8153,8 @@ THREADED_TEST(CallAsFunction) { } { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); - Local<ObjectTemplate> instance_template = t->InstanceTemplate(); + Local<ObjectTemplate> instance_template(t->InstanceTemplate()); + USE(instance_template); Local<v8::Object> instance = t->GetFunction()->NewInstance(); context->Global()->Set(v8_str("obj2"), instance); v8::TryCatch try_catch; @@ -7815,7 +8259,7 @@ static int Recurse(int depth, int iterations) { v8::HandleScope scope; if (depth == 0) return CountHandles(); for (int i = 0; i < iterations; i++) { - Local<v8::Number> n = v8::Integer::New(42); + Local<v8::Number> n(v8::Integer::New(42)); } return Recurse(depth - 1, iterations); } @@ -7829,7 +8273,7 @@ THREADED_TEST(HandleIteration) { v8::HandleScope scope1; CHECK_EQ(0, CountHandles()); for (int i = 0; i < kIterations; i++) { - Local<v8::Number> n = v8::Integer::New(42); + Local<v8::Number> n(v8::Integer::New(42)); CHECK_EQ(i + 1, CountHandles()); } @@ -7837,7 +8281,7 @@ THREADED_TEST(HandleIteration) { { v8::HandleScope scope2; for (int j = 0; j < kIterations; j++) { - Local<v8::Number> n = v8::Integer::New(42); + Local<v8::Number> n(v8::Integer::New(42)); CHECK_EQ(j + 1 + kIterations, CountHandles()); } } @@ -7883,7 +8327,7 @@ static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); return v8::Handle<Value>(); } @@ -8340,10 +8784,10 @@ THREADED_TEST(InterceptorStoreIC) { 0, 0, 0, v8_str("data")); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( - "for (var i = 0; i < 1000; i++) {" - " o.x = 42;" - "}"); + CompileRun( + "for (var i = 0; i < 1000; i++) {" + " o.x = 42;" + "}"); } @@ -8469,17 +8913,6 @@ THREADED_TEST(InterceptorCallICInvalidatedCacheable) { } -static v8::Handle<Value> call_ic_function5; -static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - if (v8_str("x")->Equals(name)) - return call_ic_function5; - else - return Local<Value>(); -} - - // This test checks that if interceptor doesn't provide a function, // cached constant function is used THREADED_TEST(InterceptorCallICConstantFunctionUsed) { @@ -8500,6 +8933,17 @@ THREADED_TEST(InterceptorCallICConstantFunctionUsed) { } +static v8::Handle<Value> call_ic_function5; +static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + if (v8_str("x")->Equals(name)) + return call_ic_function5; + else + return Local<Value>(); +} + + // This test checks that if interceptor provides a function, // even if we cached constant function, interceptor's function // is invoked @@ -8523,6 +8967,48 @@ THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { } +static v8::Handle<Value> call_ic_function6; +static v8::Handle<Value> InterceptorCallICGetter6(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + if (v8_str("x")->Equals(name)) + return call_ic_function6; + else + return Local<Value>(); +} + + +// Same test as above, except the code is wrapped in a function +// to test the optimized compiler. +THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetNamedPropertyHandler(InterceptorCallICGetter6); + LocalContext context; + context->Global()->Set(v8_str("o"), templ->NewInstance()); + call_ic_function6 = + v8_compile("function f(x) { return x - 1; }; f")->Run(); + v8::Handle<Value> value = CompileRun( + "function inc(x) { return x + 1; };" + "inc(1);" + "o.x = inc;" + "function test() {" + " var result = 0;" + " for (var i = 0; i < 1000; i++) {" + " result = o.x(42);" + " }" + " return result;" + "};" + "test();" + "test();" + "test();" + "%OptimizeFunctionOnNextCall(test);" + "test()"); + CHECK_EQ(41, value->Int32Value()); +} + + // Test the case when we stored constant function into // a stub, but it got invalidated later on THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { @@ -8613,7 +9099,7 @@ static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); ++(*call_count); if ((*call_count) % 20 == 0) { - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } return v8::Handle<Value>(); } @@ -8769,7 +9255,7 @@ THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "var result = 0;" "for (var i = 0; i < 100; i++) {" " result = o.method(41);" @@ -8796,7 +9282,7 @@ THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -8826,7 +9312,7 @@ THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -8862,7 +9348,7 @@ THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -8899,7 +9385,7 @@ THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); v8::TryCatch try_catch; - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -8938,7 +9424,7 @@ THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); v8::TryCatch try_catch; - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -8967,12 +9453,13 @@ THREADED_TEST(CallICFastApi_TrivialSignature) { v8::Handle<v8::Signature>()); v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); + USE(templ); LocalContext context; v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "var result = 0;" "for (var i = 0; i < 100; i++) {" " result = o.method(41);" @@ -8990,12 +9477,13 @@ THREADED_TEST(CallICFastApi_SimpleSignature) { v8::Signature::New(fun_templ)); v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); + CHECK(!templ.IsEmpty()); LocalContext context; v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -9016,12 +9504,13 @@ THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { v8::Signature::New(fun_templ)); v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); + CHECK(!templ.IsEmpty()); LocalContext context; v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -9047,13 +9536,14 @@ THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { v8::Signature::New(fun_templ)); v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); proto_templ->Set(v8_str("method"), method_templ); - v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); + CHECK(!templ.IsEmpty()); LocalContext context; v8::Handle<v8::Function> fun = fun_templ->GetFunction(); GenerateSomeGarbage(); context->Global()->Set(v8_str("o"), fun->NewInstance()); v8::TryCatch try_catch; - v8::Handle<Value> value = CompileRun( + CompileRun( "o.foo = 17;" "var receiver = {};" "receiver.__proto__ = o;" @@ -9093,7 +9583,7 @@ THREADED_TEST(InterceptorKeyedCallICKeyChange1) { templ->SetNamedPropertyHandler(NoBlockGetterX); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "proto = new Object();" "proto.y = function(x) { return x + 1; };" "proto.z = function(x) { return x - 1; };" @@ -9119,7 +9609,7 @@ THREADED_TEST(InterceptorKeyedCallICKeyChange2) { context->Global()->Set(v8_str("proto1"), templ->NewInstance()); keyed_call_ic_function = v8_compile("function f(x) { return x - 1; }; f")->Run(); - v8::Handle<Value> value = CompileRun( + CompileRun( "o = new Object();" "proto2 = new Object();" "o.y = function(x) { return x + 1; };" @@ -9144,7 +9634,7 @@ THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { templ->SetNamedPropertyHandler(NoBlockGetterX); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "function inc(x) { return x + 1; };" "inc(1);" "function dec(x) { return x - 1; };" @@ -9170,7 +9660,7 @@ THREADED_TEST(InterceptorKeyedCallICFromGlobal) { LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "function len(x) { return x.length; };" "o.__proto__ = this;" "var m = 'parseFloat';" @@ -9194,7 +9684,7 @@ THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { LocalContext context; context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "var o = new Object();" "o.__proto__ = proto;" "o.method = function(x) { return x + 1; };" @@ -9216,7 +9706,7 @@ THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); - v8::Handle<Value> value = CompileRun( + CompileRun( "var proto = new Object();" "o.__proto__ = proto;" "proto.method = function(x) { return x + 1; };" @@ -9737,7 +10227,7 @@ void ApiTestFuzzer::Run() { static unsigned linear_congruential_generator; -void ApiTestFuzzer::Setup(PartOfTest part) { +void ApiTestFuzzer::SetUp(PartOfTest part) { linear_congruential_generator = i::FLAG_testing_prng_seed; fuzzing_ = true; int count = RegisterThreadedTest::count(); @@ -9801,25 +10291,25 @@ void ApiTestFuzzer::TearDown() { // Lets not be needlessly self-referential. TEST(Threading) { - ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); + ApiTestFuzzer::SetUp(ApiTestFuzzer::FIRST_PART); ApiTestFuzzer::RunAllTests(); ApiTestFuzzer::TearDown(); } TEST(Threading2) { - ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); + ApiTestFuzzer::SetUp(ApiTestFuzzer::SECOND_PART); ApiTestFuzzer::RunAllTests(); ApiTestFuzzer::TearDown(); } TEST(Threading3) { - ApiTestFuzzer::Setup(ApiTestFuzzer::THIRD_PART); + ApiTestFuzzer::SetUp(ApiTestFuzzer::THIRD_PART); ApiTestFuzzer::RunAllTests(); ApiTestFuzzer::TearDown(); } TEST(Threading4) { - ApiTestFuzzer::Setup(ApiTestFuzzer::FOURTH_PART); + ApiTestFuzzer::SetUp(ApiTestFuzzer::FOURTH_PART); ApiTestFuzzer::RunAllTests(); ApiTestFuzzer::TearDown(); } @@ -9964,6 +10454,7 @@ THREADED_TEST(LockUnlockLock) { static int GetGlobalObjectsCount() { + i::Isolate::Current()->heap()->EnsureHeapIsIterable(); int count = 0; i::HeapIterator it; for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) @@ -9978,9 +10469,8 @@ static void CheckSurvivingGlobalObjectsCount(int expected) { // the first garbage collection but some of the maps have already // been marked at that point. Therefore some of the maps are not // collected until the second garbage collection. - HEAP->global_context_map(); - HEAP->CollectAllGarbage(false); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); int count = GetGlobalObjectsCount(); #ifdef DEBUG if (count != expected) HEAP->TracePathToGlobal(); @@ -10049,7 +10539,7 @@ THREADED_TEST(NewPersistentHandleFromWeakCallback) { // weak callback of the first handle would be able to 'reallocate' it. handle1.MakeWeak(NULL, NewPersistentHandleCallback); handle2.Dispose(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -10057,7 +10547,7 @@ v8::Persistent<v8::Object> to_be_disposed; void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { to_be_disposed.Dispose(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); handle.Dispose(); } @@ -10073,7 +10563,7 @@ THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { } handle1.MakeWeak(NULL, DisposeAndForceGcCallback); to_be_disposed = handle2; - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { @@ -10099,7 +10589,7 @@ THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { } handle2.MakeWeak(NULL, DisposingCallback); handle3.MakeWeak(NULL, HandleCreatingCallback); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -10141,7 +10631,8 @@ THREADED_TEST(NestedHandleScopeAndContexts) { v8::Persistent<Context> env = Context::New(); env->Enter(); v8::Handle<Value> value = NestedScope(env); - v8::Handle<String> str = value->ToString(); + v8::Handle<String> str(value->ToString()); + CHECK(!str.IsEmpty()); env->Exit(); env.Dispose(); } @@ -10149,7 +10640,8 @@ THREADED_TEST(NestedHandleScopeAndContexts) { THREADED_TEST(ExternalAllocatedMemory) { v8::HandleScope outer; - v8::Persistent<Context> env = Context::New(); + v8::Persistent<Context> env(Context::New()); + CHECK(!env.IsEmpty()); const int kSize = 1024*1024; CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); @@ -10487,7 +10979,8 @@ THREADED_TEST(AccessControlRepeatedContextCreation) { i::Handle<i::FunctionTemplateInfo> constructor( i::FunctionTemplateInfo::cast(internal_template->constructor())); CHECK(!constructor->access_check_info()->IsUndefined()); - v8::Persistent<Context> context0 = Context::New(NULL, global_template); + v8::Persistent<Context> context0(Context::New(NULL, global_template)); + CHECK(!context0.IsEmpty()); CHECK(!constructor->access_check_info()->IsUndefined()); } @@ -10915,7 +11408,7 @@ class RegExpInterruptTest { { v8::Locker lock; // TODO(lrn): Perhaps create some garbage before collecting. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); gc_count_++; } i::OS::Sleep(1); @@ -11037,7 +11530,7 @@ class ApplyInterruptTest { while (gc_during_apply_ < kRequiredGCs) { { v8::Locker lock; - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); gc_count_++; } i::OS::Sleep(1); @@ -11187,6 +11680,7 @@ static void MorphAString(i::String* string, // Test that we can still flatten a string if the components it is built up // from have been turned into 16 bit strings in the mean time. THREADED_TEST(MorphCompositeStringTest) { + char utf_buffer[129]; const char* c_string = "Now is the time for all good men" " to come to the aid of the party"; uint16_t* two_byte_string = AsciiToTwoByteString(c_string); @@ -11215,6 +11709,17 @@ THREADED_TEST(MorphCompositeStringTest) { MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); + // This should UTF-8 without flattening, since everything is ASCII. + Handle<String> cons = v8_compile("cons")->Run().As<String>(); + CHECK_EQ(128, cons->Utf8Length()); + int nchars = -1; + CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars)); + CHECK_EQ(128, nchars); + CHECK_EQ(0, strcmp( + utf_buffer, + "Now is the time for all good men to come to the aid of the party" + "Now is the time for all good men to come to the aid of the party")); + // Now do some stuff to make sure the strings are flattened, etc. CompileRun( "/[^a-z]/.test(cons);" @@ -11666,7 +12171,7 @@ THREADED_TEST(GetCallingContext) { callback_templ->GetFunction()); calling_context0->Exit(); - // Expose context0 in context1 and setup a function that calls the + // Expose context0 in context1 and set up a function that calls the // callback function. calling_context1->Enter(); calling_context1->Global()->Set(v8_str("context0"), @@ -11753,13 +12258,15 @@ THREADED_TEST(PixelArray) { i::Handle<i::ExternalPixelArray> pixels = i::Handle<i::ExternalPixelArray>::cast( FACTORY->NewExternalArray(kElementCount, - v8::kExternalPixelArray, - pixel_data)); - HEAP->CollectAllGarbage(false); // Force GC to trigger verification. + v8::kExternalPixelArray, + pixel_data)); + // Force GC to trigger verification. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); for (int i = 0; i < kElementCount; i++) { pixels->set(i, i % 256); } - HEAP->CollectAllGarbage(false); // Force GC to trigger verification. + // Force GC to trigger verification. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); for (int i = 0; i < kElementCount; i++) { CHECK_EQ(i % 256, pixels->get_scalar(i)); CHECK_EQ(i % 256, pixel_data[i]); @@ -11822,18 +12329,18 @@ THREADED_TEST(PixelArray) { i::Handle<i::Smi> value(i::Smi::FromInt(2)); i::Handle<i::Object> no_failure; - no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); + no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode); ASSERT(!no_failure.is_null()); i::USE(no_failure); CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); *value.location() = i::Smi::FromInt(256); - no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); + no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode); ASSERT(!no_failure.is_null()); i::USE(no_failure); CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); *value.location() = i::Smi::FromInt(-1); - no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); + no_failure = i::JSObject::SetElement(jsobj, 1, value, i::kNonStrictMode); ASSERT(!no_failure.is_null()); i::USE(no_failure); CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); @@ -12235,11 +12742,13 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, i::Handle<ExternalArrayClass> array = i::Handle<ExternalArrayClass>::cast( FACTORY->NewExternalArray(kElementCount, array_type, array_data)); - HEAP->CollectAllGarbage(false); // Force GC to trigger verification. + // Force GC to trigger verification. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); for (int i = 0; i < kElementCount; i++) { array->set(i, static_cast<ElementType>(i)); } - HEAP->CollectAllGarbage(false); // Force GC to trigger verification. + // Force GC to trigger verification. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); for (int i = 0; i < kElementCount; i++) { CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get_scalar(i))); @@ -12357,7 +12866,8 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, " }" "}" "sum;"); - HEAP->CollectAllGarbage(false); // Force GC to trigger verification. + // Force GC to trigger verification. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(28, result->Int32Value()); // Make sure out-of-range loads do not throw. @@ -12546,11 +13056,6 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, const int kLargeElementCount = kXSize * kYSize * 4; ElementType* large_array_data = static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); - i::Handle<ExternalArrayClass> large_array = - i::Handle<ExternalArrayClass>::cast( - FACTORY->NewExternalArray(kLargeElementCount, - array_type, - array_data)); v8::Handle<v8::Object> large_obj = v8::Object::New(); // Set the elements to be the external array. large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, @@ -12949,10 +13454,10 @@ TEST(CaptureStackTrace) { "}\n" "var x;eval('new foo();');"; v8::Handle<v8::String> overview_src = v8::String::New(overview_source); - v8::Handle<Value> overview_result = - v8::Script::New(overview_src, origin)->Run(); - ASSERT(!overview_result.IsEmpty()); - ASSERT(overview_result->IsObject()); + v8::Handle<Value> overview_result( + v8::Script::New(overview_src, origin)->Run()); + CHECK(!overview_result.IsEmpty()); + CHECK(overview_result->IsObject()); // Test getting DETAILED information. const char *detailed_source = @@ -12970,9 +13475,9 @@ TEST(CaptureStackTrace) { v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); v8::Handle<v8::Script> detailed_script( v8::Script::New(detailed_src, &detailed_origin)); - v8::Handle<Value> detailed_result = detailed_script->Run(); - ASSERT(!detailed_result.IsEmpty()); - ASSERT(detailed_result->IsObject()); + v8::Handle<Value> detailed_result(detailed_script->Run()); + CHECK(!detailed_result.IsEmpty()); + CHECK(detailed_result->IsObject()); } @@ -13030,6 +13535,137 @@ TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { } +static void RethrowStackTraceHandler(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + // Use the frame where JavaScript is called from. + v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); + CHECK(!stack_trace.IsEmpty()); + int frame_count = stack_trace->GetFrameCount(); + CHECK_EQ(3, frame_count); + int line_number[] = {1, 2, 5}; + for (int i = 0; i < frame_count; i++) { + CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); + } +} + + +// Test that we only return the stack trace at the site where the exception +// is first thrown (not where it is rethrown). +TEST(RethrowStackTrace) { + v8::HandleScope scope; + LocalContext env; + // We make sure that + // - the stack trace of the ReferenceError in g() is reported. + // - the stack trace is not overwritten when e1 is rethrown by t(). + // - the stack trace of e2 does not overwrite that of e1. + const char* source = + "function g() { error; } \n" + "function f() { g(); } \n" + "function t(e) { throw e; } \n" + "try { \n" + " f(); \n" + "} catch (e1) { \n" + " try { \n" + " error; \n" + " } catch (e2) { \n" + " t(e1); \n" + " } \n" + "} \n"; + v8::V8::AddMessageListener(RethrowStackTraceHandler); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + CompileRun(source); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); + v8::V8::RemoveMessageListeners(RethrowStackTraceHandler); +} + + +static void RethrowPrimitiveStackTraceHandler(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); + CHECK(!stack_trace.IsEmpty()); + int frame_count = stack_trace->GetFrameCount(); + CHECK_EQ(2, frame_count); + int line_number[] = {3, 7}; + for (int i = 0; i < frame_count; i++) { + CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); + } +} + + +// Test that we do not recognize identity for primitive exceptions. +TEST(RethrowPrimitiveStackTrace) { + v8::HandleScope scope; + LocalContext env; + // We do not capture stack trace for non Error objects on creation time. + // Instead, we capture the stack trace on last throw. + const char* source = + "function g() { throw 404; } \n" + "function f() { g(); } \n" + "function t(e) { throw e; } \n" + "try { \n" + " f(); \n" + "} catch (e1) { \n" + " t(e1) \n" + "} \n"; + v8::V8::AddMessageListener(RethrowPrimitiveStackTraceHandler); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + CompileRun(source); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); + v8::V8::RemoveMessageListeners(RethrowPrimitiveStackTraceHandler); +} + + +static void RethrowExistingStackTraceHandler(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + // Use the frame where JavaScript is called from. + v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); + CHECK(!stack_trace.IsEmpty()); + CHECK_EQ(1, stack_trace->GetFrameCount()); + CHECK_EQ(1, stack_trace->GetFrame(0)->GetLineNumber()); +} + + +// Test that the stack trace is captured when the error object is created and +// not where it is thrown. +TEST(RethrowExistingStackTrace) { + v8::HandleScope scope; + LocalContext env; + const char* source = + "var e = new Error(); \n" + "throw e; \n"; + v8::V8::AddMessageListener(RethrowExistingStackTraceHandler); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + CompileRun(source); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); + v8::V8::RemoveMessageListeners(RethrowExistingStackTraceHandler); +} + + +static void RethrowBogusErrorStackTraceHandler(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + // Use the frame where JavaScript is called from. + v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); + CHECK(!stack_trace.IsEmpty()); + CHECK_EQ(1, stack_trace->GetFrameCount()); + CHECK_EQ(2, stack_trace->GetFrame(0)->GetLineNumber()); +} + + +// Test that the stack trace is captured where the bogus Error object is thrown. +TEST(RethrowBogusErrorStackTrace) { + v8::HandleScope scope; + LocalContext env; + const char* source = + "var e = {__proto__: new Error()} \n" + "throw e; \n"; + v8::V8::AddMessageListener(RethrowBogusErrorStackTraceHandler); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + CompileRun(source); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); + v8::V8::RemoveMessageListeners(RethrowBogusErrorStackTraceHandler); +} + + v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { v8::HandleScope scope; v8::Handle<v8::StackTrace> stackTrace = @@ -13070,7 +13706,23 @@ TEST(SourceURLInStackTrace) { // Test that idle notification can be handled and eventually returns true. +// This just checks the contract of the IdleNotification() function, +// and does not verify that it does reasonable work. THREADED_TEST(IdleNotification) { + v8::HandleScope scope; + LocalContext env; + CompileRun("function binom(n, m) {" + " var C = [[1]];" + " for (var i = 1; i <= n; ++i) {" + " C[i] = [1];" + " for (var j = 1; j < i; ++j) {" + " C[i][j] = C[i-1][j-1] + C[i-1][j];" + " }" + " C[i][i] = 1;" + " }" + " return C[n][m];" + "};" + "binom(1000, 500)"); bool rv = false; for (int i = 0; i < 100; i++) { rv = v8::V8::IdleNotification(); @@ -13080,6 +13732,40 @@ THREADED_TEST(IdleNotification) { CHECK(rv == true); } +// Test that idle notification can be handled and eventually returns true. +// This just checks the contract of the IdleNotification() function, +// and does not verify that it does reasonable work. +TEST(IdleNotificationWithHint) { + v8::HandleScope scope; + LocalContext env; + { + i::AlwaysAllocateScope always_allocate; + CompileRun("function binom(n, m) {" + " var C = [[1]];" + " for (var i = 1; i <= n; ++i) {" + " C[i] = [1];" + " for (var j = 1; j < i; ++j) {" + " C[i][j] = C[i-1][j-1] + C[i-1][j];" + " }" + " C[i][i] = 1;" + " }" + " return C[n][m];" + "};" + "binom(1000, 500)"); + } + bool rv = false; + intptr_t old_size = HEAP->SizeOfObjects(); + bool no_idle_work = v8::V8::IdleNotification(10); + for (int i = 0; i < 200; i++) { + rv = v8::V8::IdleNotification(10); + if (rv) + break; + } + CHECK(rv == true); + intptr_t new_size = HEAP->SizeOfObjects(); + CHECK(no_idle_work || new_size < old_size); +} + static uint32_t* stack_limit; @@ -13168,6 +13854,63 @@ THREADED_TEST(GetHeapStatistics) { } +class VisitorImpl : public v8::ExternalResourceVisitor { + public: + VisitorImpl(TestResource* r1, TestResource* r2) + : resource1_(r1), + resource2_(r2), + found_resource1_(false), + found_resource2_(false) {} + virtual ~VisitorImpl() {} + virtual void VisitExternalString(v8::Handle<v8::String> string) { + if (!string->IsExternal()) { + CHECK(string->IsExternalAscii()); + return; + } + v8::String::ExternalStringResource* resource = + string->GetExternalStringResource(); + CHECK(resource); + if (resource1_ == resource) { + CHECK(!found_resource1_); + found_resource1_ = true; + } + if (resource2_ == resource) { + CHECK(!found_resource2_); + found_resource2_ = true; + } + } + void CheckVisitedResources() { + CHECK(found_resource1_); + CHECK(found_resource2_); + } + + private: + v8::String::ExternalStringResource* resource1_; + v8::String::ExternalStringResource* resource2_; + bool found_resource1_; + bool found_resource2_; +}; + +TEST(VisitExternalStrings) { + v8::HandleScope scope; + LocalContext env; + const char* string = "Some string"; + uint16_t* two_byte_string = AsciiToTwoByteString(string); + TestResource* resource1 = new TestResource(two_byte_string); + v8::Local<v8::String> string1 = v8::String::NewExternal(resource1); + TestResource* resource2 = new TestResource(two_byte_string); + v8::Local<v8::String> string2 = v8::String::NewExternal(resource2); + + // We need to add usages for string1 and string2 to avoid warnings in GCC 4.7 + CHECK(string1->IsExternal()); + CHECK(string2->IsExternal()); + + VisitorImpl visitor(resource1, resource2); + v8::V8::VisitExternalResources(&visitor); + visitor.CheckVisitedResources(); +} + + static double DoubleFromBits(uint64_t value) { double target; memcpy(&target, &value, sizeof(target)); @@ -13250,7 +13993,13 @@ THREADED_TEST(QuietSignalingNaNs) { } else { uint64_t stored_bits = DoubleToBits(stored_number); // Check if quiet nan (bits 51..62 all set). +#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) + // Most significant fraction bit for quiet nan is set to 0 + // on MIPS architecture. Allowed by IEEE-754. + CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); +#else CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); +#endif } // Check that Date::New preserves non-NaNs in the date range and @@ -13263,7 +14012,13 @@ THREADED_TEST(QuietSignalingNaNs) { } else { uint64_t stored_bits = DoubleToBits(stored_date); // Check if quiet nan (bits 51..62 all set). +#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) + // Most significant fraction bit for quiet nan is set to 0 + // on MIPS architecture. Allowed by IEEE-754. + CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); +#else CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); +#endif } } } @@ -13272,7 +14027,8 @@ THREADED_TEST(QuietSignalingNaNs) { static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { v8::HandleScope scope; v8::TryCatch tc; - v8::Handle<v8::String> str = args[0]->ToString(); + v8::Handle<v8::String> str(args[0]->ToString()); + USE(str); if (tc.HasCaught()) return tc.ReThrow(); return v8::Undefined(); @@ -13337,7 +14093,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_simple); other_context->Exit(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -13359,7 +14115,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_eval); other_context->Exit(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -13386,7 +14142,7 @@ TEST(Regress528) { other_context->Enter(); CompileRun(source_exception); other_context->Exit(); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); if (GetGlobalObjectsCount() == 1) break; } CHECK_GE(2, gc_count); @@ -13417,6 +14173,17 @@ THREADED_TEST(ScriptOrigin) { CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); } +THREADED_TEST(FunctionGetInferredName) { + v8::HandleScope scope; + LocalContext env; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); + v8::Handle<v8::String> script = v8::String::New( + "var foo = { bar : { baz : function() {}}}; var f = foo.bar.baz;"); + v8::Script::Compile(script, &origin)->Run(); + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("f"))); + CHECK_EQ("foo.bar.baz", *v8::String::AsciiValue(f->GetInferredName())); +} THREADED_TEST(ScriptLineNumber) { v8::HandleScope scope; @@ -13434,6 +14201,41 @@ THREADED_TEST(ScriptLineNumber) { } +THREADED_TEST(ScriptColumnNumber) { + v8::HandleScope scope; + LocalContext env; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), + v8::Integer::New(3), v8::Integer::New(2)); + v8::Handle<v8::String> script = v8::String::New( + "function foo() {}\n\n function bar() {}"); + v8::Script::Compile(script, &origin)->Run(); + v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("foo"))); + v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("bar"))); + CHECK_EQ(14, foo->GetScriptColumnNumber()); + CHECK_EQ(17, bar->GetScriptColumnNumber()); +} + + +THREADED_TEST(FunctionGetScriptId) { + v8::HandleScope scope; + LocalContext env; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), + v8::Integer::New(3), v8::Integer::New(2)); + v8::Handle<v8::String> scriptSource = v8::String::New( + "function foo() {}\n\n function bar() {}"); + v8::Local<v8::Script> script(v8::Script::Compile(scriptSource, &origin)); + script->Run(); + v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("foo"))); + v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("bar"))); + CHECK_EQ(script->Id(), foo->GetScriptId()); + CHECK_EQ(script->Id(), bar->GetScriptId()); +} + + static v8::Handle<Value> GetterWhichReturns42(Local<String> name, const AccessorInfo& info) { return v8_num(42); @@ -13604,26 +14406,26 @@ TEST(GCCallbacks) { v8::V8::AddGCEpilogueCallback(EpilogueCallback); CHECK_EQ(0, prologue_call_count); CHECK_EQ(0, epilogue_call_count); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(1, prologue_call_count); CHECK_EQ(1, epilogue_call_count); v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(2, prologue_call_count); CHECK_EQ(2, epilogue_call_count); CHECK_EQ(1, prologue_call_count_second); CHECK_EQ(1, epilogue_call_count_second); v8::V8::RemoveGCPrologueCallback(PrologueCallback); v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(2, prologue_call_count); CHECK_EQ(2, epilogue_call_count); CHECK_EQ(2, prologue_call_count_second); CHECK_EQ(2, epilogue_call_count_second); v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(2, prologue_call_count); CHECK_EQ(2, epilogue_call_count); CHECK_EQ(2, prologue_call_count_second); @@ -13694,7 +14496,7 @@ THREADED_TEST(RoundRobinGetFromCache) { " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" " for (var i = 0; i < 16; i++) {" " var v = %_GetFromCache(0, keys[i]);" - " if (v !== values[i])" + " if (v.toString() !== values[i].toString())" " return 'Wrong value for ' + " " keys[i] + ': ' + v + ' vs. ' + values[i];" " };" @@ -13840,7 +14642,7 @@ THREADED_TEST(TwoByteStringInAsciiCons) { void FailedAccessCheckCallbackGC(Local<v8::Object> target, v8::AccessType type, Local<v8::Value> data) { - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); } @@ -14414,7 +15216,7 @@ TEST(DontDeleteCellLoadIC) { "})()", "ReferenceError: cell is not defined"); CompileRun("cell = \"new_second\";"); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); ExpectString("readCell()", "new_second"); ExpectString("readCell()", "new_second"); } @@ -14535,8 +15337,8 @@ TEST(RegExp) { // RegExps are objects on which you can set properties. re->Set(v8_str("property"), v8::Integer::New(32)); - v8::Handle<v8::Value> value = CompileRun("re.property"); - ASSERT_EQ(32, value->Int32Value()); + v8::Handle<v8::Value> value(CompileRun("re.property")); + CHECK_EQ(32, value->Int32Value()); v8::TryCatch try_catch; re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); @@ -14887,10 +15689,12 @@ THREADED_TEST(AllowCodeGenFromStrings) { LocalContext context; // eval and the Function constructor allowed by default. + CHECK(context->IsCodeGenerationFromStringsAllowed()); CheckCodeGenerationAllowed(); // Disallow eval and the Function constructor. context->AllowCodeGenerationFromStrings(false); + CHECK(!context->IsCodeGenerationFromStringsAllowed()); CheckCodeGenerationDisallowed(); // Allow again. @@ -14900,10 +15704,12 @@ THREADED_TEST(AllowCodeGenFromStrings) { // Disallow but setting a global callback that will allow the calls. context->AllowCodeGenerationFromStrings(false); V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationAllowed); + CHECK(!context->IsCodeGenerationFromStringsAllowed()); CheckCodeGenerationAllowed(); // Set a callback that disallows the code generation. V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); + CHECK(!context->IsCodeGenerationFromStringsAllowed()); CheckCodeGenerationDisallowed(); } @@ -15182,3 +15988,98 @@ THREADED_TEST(ForeignFunctionReceiver) { foreign_context.Dispose(); } + + +uint8_t callback_fired = 0; + + +void CallCompletedCallback1() { + i::OS::Print("Firing callback 1.\n"); + callback_fired ^= 1; // Toggle first bit. +} + + +void CallCompletedCallback2() { + i::OS::Print("Firing callback 2.\n"); + callback_fired ^= 2; // Toggle second bit. +} + + +Handle<Value> RecursiveCall(const Arguments& args) { + int32_t level = args[0]->Int32Value(); + if (level < 3) { + level++; + i::OS::Print("Entering recursion level %d.\n", level); + char script[64]; + i::Vector<char> script_vector(script, sizeof(script)); + i::OS::SNPrintF(script_vector, "recursion(%d)", level); + CompileRun(script_vector.start()); + i::OS::Print("Leaving recursion level %d.\n", level); + CHECK_EQ(0, callback_fired); + } else { + i::OS::Print("Recursion ends.\n"); + CHECK_EQ(0, callback_fired); + } + return Undefined(); +} + + +TEST(CallCompletedCallback) { + v8::HandleScope scope; + LocalContext env; + v8::Handle<v8::FunctionTemplate> recursive_runtime = + v8::FunctionTemplate::New(RecursiveCall); + env->Global()->Set(v8_str("recursion"), + recursive_runtime->GetFunction()); + // Adding the same callback a second time has no effect. + v8::V8::AddCallCompletedCallback(CallCompletedCallback1); + v8::V8::AddCallCompletedCallback(CallCompletedCallback1); + v8::V8::AddCallCompletedCallback(CallCompletedCallback2); + i::OS::Print("--- Script (1) ---\n"); + Local<Script> script = + v8::Script::Compile(v8::String::New("recursion(0)")); + script->Run(); + CHECK_EQ(3, callback_fired); + + i::OS::Print("\n--- Script (2) ---\n"); + callback_fired = 0; + v8::V8::RemoveCallCompletedCallback(CallCompletedCallback1); + script->Run(); + CHECK_EQ(2, callback_fired); + + i::OS::Print("\n--- Function ---\n"); + callback_fired = 0; + Local<Function> recursive_function = + Local<Function>::Cast(env->Global()->Get(v8_str("recursion"))); + v8::Handle<Value> args[] = { v8_num(0) }; + recursive_function->Call(env->Global(), 1, args); + CHECK_EQ(2, callback_fired); +} + + +void CallCompletedCallbackNoException() { + v8::HandleScope scope; + CompileRun("1+1;"); +} + + +void CallCompletedCallbackException() { + v8::HandleScope scope; + CompileRun("throw 'second exception';"); +} + + +TEST(CallCompletedCallbackOneException) { + v8::HandleScope scope; + LocalContext env; + v8::V8::AddCallCompletedCallback(CallCompletedCallbackNoException); + CompileRun("throw 'exception';"); +} + + +TEST(CallCompletedCallbackTwoExceptions) { + v8::HandleScope scope; + LocalContext env; + v8::V8::AddCallCompletedCallback(CallCompletedCallbackException); + CompileRun("throw 'first exception';"); +} diff --git a/deps/v8/test/cctest/test-assembler-ia32.cc b/deps/v8/test/cctest/test-assembler-ia32.cc index 839b7f562e..815e618499 100644 --- a/deps/v8/test/cctest/test-assembler-ia32.cc +++ b/deps/v8/test/cctest/test-assembler-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -93,15 +93,15 @@ TEST(AssemblerIa321) { Label L, C; __ mov(edx, Operand(esp, 4)); - __ xor_(eax, Operand(eax)); // clear eax + __ xor_(eax, eax); // clear eax __ jmp(&C); __ bind(&L); - __ add(eax, Operand(edx)); - __ sub(Operand(edx), Immediate(1)); + __ add(eax, edx); + __ sub(edx, Immediate(1)); __ bind(&C); - __ test(edx, Operand(edx)); + __ test(edx, edx); __ j(not_zero, &L); __ ret(0); @@ -135,11 +135,11 @@ TEST(AssemblerIa322) { __ jmp(&C); __ bind(&L); - __ imul(eax, Operand(edx)); - __ sub(Operand(edx), Immediate(1)); + __ imul(eax, edx); + __ sub(edx, Immediate(1)); __ bind(&C); - __ test(edx, Operand(edx)); + __ test(edx, edx); __ j(not_zero, &L); __ ret(0); @@ -275,10 +275,10 @@ TEST(AssemblerIa326) { __ subsd(xmm0, xmm1); __ divsd(xmm0, xmm1); // Copy xmm0 to st(0) using eight bytes of stack. - __ sub(Operand(esp), Immediate(8)); + __ sub(esp, Immediate(8)); __ movdbl(Operand(esp, 0), xmm0); __ fld_d(Operand(esp, 0)); - __ add(Operand(esp), Immediate(8)); + __ add(esp, Immediate(8)); __ ret(0); CodeDesc desc; @@ -314,12 +314,12 @@ TEST(AssemblerIa328) { v8::internal::byte buffer[256]; Assembler assm(Isolate::Current(), buffer, sizeof buffer); __ mov(eax, Operand(esp, 4)); - __ cvtsi2sd(xmm0, Operand(eax)); + __ cvtsi2sd(xmm0, eax); // Copy xmm0 to st(0) using eight bytes of stack. - __ sub(Operand(esp), Immediate(8)); + __ sub(esp, Immediate(8)); __ movdbl(Operand(esp, 0), xmm0); __ fld_d(Operand(esp, 0)); - __ add(Operand(esp), Immediate(8)); + __ add(esp, Immediate(8)); __ ret(0); CodeDesc desc; assm.GetCode(&desc); @@ -408,4 +408,72 @@ TEST(AssemblerIa3210) { __ nop(); } + +TEST(AssemblerMultiByteNop) { + InitializeVM(); + v8::HandleScope scope; + v8::internal::byte buffer[1024]; + Assembler assm(Isolate::Current(), buffer, sizeof(buffer)); + __ push(ebx); + __ push(ecx); + __ push(edx); + __ push(edi); + __ push(esi); + __ mov(eax, 1); + __ mov(ebx, 2); + __ mov(ecx, 3); + __ mov(edx, 4); + __ mov(edi, 5); + __ mov(esi, 6); + for (int i = 0; i < 16; i++) { + int before = assm.pc_offset(); + __ Nop(i); + CHECK_EQ(assm.pc_offset() - before, i); + } + + Label fail; + __ cmp(eax, 1); + __ j(not_equal, &fail); + __ cmp(ebx, 2); + __ j(not_equal, &fail); + __ cmp(ecx, 3); + __ j(not_equal, &fail); + __ cmp(edx, 4); + __ j(not_equal, &fail); + __ cmp(edi, 5); + __ j(not_equal, &fail); + __ cmp(esi, 6); + __ j(not_equal, &fail); + __ mov(eax, 42); + __ pop(esi); + __ pop(edi); + __ pop(edx); + __ pop(ecx); + __ pop(ebx); + __ ret(0); + __ bind(&fail); + __ mov(eax, 13); + __ pop(esi); + __ pop(edi); + __ pop(edx); + __ pop(ecx); + __ pop(ebx); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Code* code = Code::cast(HEAP->CreateCode( + desc, + Code::ComputeFlags(Code::STUB), + Handle<Object>(HEAP->undefined_value()))->ToObjectChecked()); + CHECK(code->IsCode()); + + F0 f = FUNCTION_CAST<F0>(code->entry()); + int res = f(); + CHECK_EQ(42, res); +} + + + + #undef __ diff --git a/deps/v8/test/cctest/test-assembler-x64.cc b/deps/v8/test/cctest/test-assembler-x64.cc index 28f7c9b703..d81923fa5c 100644 --- a/deps/v8/test/cctest/test-assembler-x64.cc +++ b/deps/v8/test/cctest/test-assembler-x64.cc @@ -36,6 +36,7 @@ #include "cctest.h" using v8::internal::Assembler; +using v8::internal::Code; using v8::internal::CodeDesc; using v8::internal::FUNCTION_CAST; using v8::internal::Immediate; @@ -53,6 +54,7 @@ using v8::internal::r15; using v8::internal::r8; using v8::internal::r9; using v8::internal::rax; +using v8::internal::rbx; using v8::internal::rbp; using v8::internal::rcx; using v8::internal::rdi; @@ -86,8 +88,18 @@ static const v8::internal::Register arg2 = rsi; #define __ assm. +static v8::Persistent<v8::Context> env; + + +static void InitializeVM() { + if (env.IsEmpty()) { + env = v8::Context::New(); + } +} + + TEST(AssemblerX64ReturnOperation) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -109,7 +121,7 @@ TEST(AssemblerX64ReturnOperation) { } TEST(AssemblerX64StackOperations) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -141,7 +153,7 @@ TEST(AssemblerX64StackOperations) { } TEST(AssemblerX64ArithmeticOperations) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -163,7 +175,7 @@ TEST(AssemblerX64ArithmeticOperations) { } TEST(AssemblerX64ImulOperation) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -191,7 +203,7 @@ TEST(AssemblerX64ImulOperation) { } TEST(AssemblerX64MemoryOperands) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -225,7 +237,7 @@ TEST(AssemblerX64MemoryOperands) { } TEST(AssemblerX64ControlFlow) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -254,7 +266,7 @@ TEST(AssemblerX64ControlFlow) { } TEST(AssemblerX64LoopImmediates) { - OS::Setup(); + OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize, @@ -359,4 +371,73 @@ TEST(AssemblerX64LabelChaining) { __ nop(); } + +TEST(AssemblerMultiByteNop) { + InitializeVM(); + v8::HandleScope scope; + v8::internal::byte buffer[1024]; + Assembler assm(Isolate::Current(), buffer, sizeof(buffer)); + __ push(rbx); + __ push(rcx); + __ push(rdx); + __ push(rdi); + __ push(rsi); + __ movq(rax, Immediate(1)); + __ movq(rbx, Immediate(2)); + __ movq(rcx, Immediate(3)); + __ movq(rdx, Immediate(4)); + __ movq(rdi, Immediate(5)); + __ movq(rsi, Immediate(6)); + for (int i = 0; i < 16; i++) { + int before = assm.pc_offset(); + __ Nop(i); + CHECK_EQ(assm.pc_offset() - before, i); + } + + Label fail; + __ cmpq(rax, Immediate(1)); + __ j(not_equal, &fail); + __ cmpq(rbx, Immediate(2)); + __ j(not_equal, &fail); + __ cmpq(rcx, Immediate(3)); + __ j(not_equal, &fail); + __ cmpq(rdx, Immediate(4)); + __ j(not_equal, &fail); + __ cmpq(rdi, Immediate(5)); + __ j(not_equal, &fail); + __ cmpq(rsi, Immediate(6)); + __ j(not_equal, &fail); + __ movq(rax, Immediate(42)); + __ pop(rsi); + __ pop(rdi); + __ pop(rdx); + __ pop(rcx); + __ pop(rbx); + __ ret(0); + __ bind(&fail); + __ movq(rax, Immediate(13)); + __ pop(rsi); + __ pop(rdi); + __ pop(rdx); + __ pop(rcx); + __ pop(rbx); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Code* code = Code::cast(HEAP->CreateCode( + desc, + Code::ComputeFlags(Code::STUB), + v8::internal::Handle<v8::internal::Object>( + HEAP->undefined_value()))->ToObjectChecked()); + CHECK(code->IsCode()); + + F0 f = FUNCTION_CAST<F0>(code->entry()); + int res = f(); + CHECK_EQ(42, res); +} + + + + #undef __ diff --git a/deps/v8/test/cctest/test-ast.cc b/deps/v8/test/cctest/test-ast.cc index 2aa72078af..80c7fdff78 100644 --- a/deps/v8/test/cctest/test-ast.cc +++ b/deps/v8/test/cctest/test-ast.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -40,7 +40,8 @@ TEST(List) { CHECK_EQ(0, list->length()); ZoneScope zone_scope(Isolate::Current(), DELETE_ON_EXIT); - AstNode* node = new(ZONE) EmptyStatement(); + AstNodeFactory<AstNullVisitor> factory(Isolate::Current()); + AstNode* node = factory.NewEmptyStatement(); list->Add(node); CHECK_EQ(1, list->length()); CHECK_EQ(node, list->at(0)); diff --git a/deps/v8/test/cctest/test-compiler.cc b/deps/v8/test/cctest/test-compiler.cc index 2d9b01204a..9ca0b0a170 100644 --- a/deps/v8/test/cctest/test-compiler.cc +++ b/deps/v8/test/cctest/test-compiler.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -270,8 +270,7 @@ TEST(UncaughtThrow) { CHECK(!fun.is_null()); bool has_pending_exception; Handle<JSObject> global(Isolate::Current()->context()->global()); - Handle<Object> result = - Execution::Call(fun, global, 0, NULL, &has_pending_exception); + Execution::Call(fun, global, 0, NULL, &has_pending_exception); CHECK(has_pending_exception); CHECK_EQ(42.0, Isolate::Current()->pending_exception()-> ToObjectChecked()->Number()); @@ -305,10 +304,11 @@ TEST(C2JSFrames) { Handle<Object> fun1(fun1_object->ToObjectChecked()); CHECK(fun1->IsJSFunction()); - Object** argv[1] = { - Handle<Object>::cast(FACTORY->LookupAsciiSymbol("hello")).location() - }; - Execution::Call(Handle<JSFunction>::cast(fun1), global, 1, argv, + Handle<Object> argv[] = { FACTORY->LookupAsciiSymbol("hello") }; + Execution::Call(Handle<JSFunction>::cast(fun1), + global, + ARRAY_SIZE(argv), + argv, &has_pending_exception); CHECK(!has_pending_exception); } diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc index f567a0f770..b10e6889ec 100644 --- a/deps/v8/test/cctest/test-cpu-profiler.cc +++ b/deps/v8/test/cctest/test-cpu-profiler.cc @@ -216,7 +216,7 @@ TEST(TickEvents) { TEST(CrashIfStoppingLastNonExistentProfile) { InitializeVM(); TestSetup test_setup; - CpuProfiler::Setup(); + CpuProfiler::SetUp(); CpuProfiler::StartProfiling("1"); CpuProfiler::StopProfiling("2"); CpuProfiler::StartProfiling("1"); @@ -268,7 +268,7 @@ TEST(Issue1398) { TEST(DeleteAllCpuProfiles) { InitializeVM(); TestSetup test_setup; - CpuProfiler::Setup(); + CpuProfiler::SetUp(); CHECK_EQ(0, CpuProfiler::GetProfilesCount()); CpuProfiler::DeleteAllProfiles(); CHECK_EQ(0, CpuProfiler::GetProfilesCount()); diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index 45da6dc0bd..d66f094df7 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -409,11 +409,8 @@ Handle<FixedArray> GetDebuggedFunctions() { static Handle<Code> ComputeCallDebugBreak(int argc) { - CALL_HEAP_FUNCTION( - v8::internal::Isolate::Current(), - v8::internal::Isolate::Current()->stub_cache()->ComputeCallDebugBreak( - argc, Code::CALL_IC), - Code); + return Isolate::Current()->stub_cache()->ComputeCallDebugBreak(argc, + Code::CALL_IC); } @@ -425,8 +422,8 @@ void CheckDebuggerUnloaded(bool check_functions) { CHECK_EQ(NULL, Isolate::Current()->debug()->debug_info_list_); // Collect garbage to ensure weak handles are cleared. - HEAP->CollectAllGarbage(false); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); // Iterate the head and check that there are no debugger related objects left. HeapIterator iterator; @@ -859,7 +856,7 @@ static void DebugEventRemoveBreakPoint(v8::DebugEvent event, if (event == v8::Break) { break_point_hit_count++; - v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(data); + CHECK(data->IsFunction()); ClearBreakPoint(debug_event_remove_break_point); } } @@ -944,7 +941,7 @@ static void DebugEventBreakPointCollectGarbage( HEAP->CollectGarbage(v8::internal::NEW_SPACE); } else { // Mark sweep compact. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); } } } @@ -1417,8 +1414,7 @@ TEST(GCDuringBreakPointProcessing) { // Call the function three times with different garbage collections in between // and make sure that the break point survives. static void CallAndGC(v8::Local<v8::Object> recv, - v8::Local<v8::Function> f, - bool force_compaction) { + v8::Local<v8::Function> f) { break_point_hit_count = 0; for (int i = 0; i < 3; i++) { @@ -1432,14 +1428,15 @@ static void CallAndGC(v8::Local<v8::Object> recv, CHECK_EQ(2 + i * 3, break_point_hit_count); // Mark sweep (and perhaps compact) and call function. - HEAP->CollectAllGarbage(force_compaction); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); f->Call(recv, 0, NULL); CHECK_EQ(3 + i * 3, break_point_hit_count); } } -static void TestBreakPointSurviveGC(bool force_compaction) { +// Test that a break point can be set at a return store location. +TEST(BreakPointSurviveGC) { break_point_hit_count = 0; v8::HandleScope scope; DebugLocalContext env; @@ -1450,50 +1447,45 @@ static void TestBreakPointSurviveGC(bool force_compaction) { // Test IC store break point with garbage collection. { - v8::Local<v8::Function> bar = - CompileFunction(&env, "function foo(){}", "foo"); + CompileFunction(&env, "function foo(){}", "foo"); foo = CompileFunction(&env, "function foo(){bar=0;}", "foo"); SetBreakPoint(foo, 0); } - CallAndGC(env->Global(), foo, force_compaction); + CallAndGC(env->Global(), foo); // Test IC load break point with garbage collection. { - v8::Local<v8::Function> bar = - CompileFunction(&env, "function foo(){}", "foo"); + CompileFunction(&env, "function foo(){}", "foo"); foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo"); SetBreakPoint(foo, 0); } - CallAndGC(env->Global(), foo, force_compaction); + CallAndGC(env->Global(), foo); // Test IC call break point with garbage collection. { - v8::Local<v8::Function> bar = - CompileFunction(&env, "function foo(){}", "foo"); + CompileFunction(&env, "function foo(){}", "foo"); foo = CompileFunction(&env, "function bar(){};function foo(){bar();}", "foo"); SetBreakPoint(foo, 0); } - CallAndGC(env->Global(), foo, force_compaction); + CallAndGC(env->Global(), foo); // Test return break point with garbage collection. { - v8::Local<v8::Function> bar = - CompileFunction(&env, "function foo(){}", "foo"); + CompileFunction(&env, "function foo(){}", "foo"); foo = CompileFunction(&env, "function foo(){}", "foo"); SetBreakPoint(foo, 0); } - CallAndGC(env->Global(), foo, force_compaction); + CallAndGC(env->Global(), foo); // Test non IC break point with garbage collection. { - v8::Local<v8::Function> bar = - CompileFunction(&env, "function foo(){}", "foo"); + CompileFunction(&env, "function foo(){}", "foo"); foo = CompileFunction(&env, "function foo(){var bar=0;}", "foo"); SetBreakPoint(foo, 0); } - CallAndGC(env->Global(), foo, force_compaction); + CallAndGC(env->Global(), foo); v8::Debug::SetDebugEventListener(NULL); @@ -1501,13 +1493,6 @@ static void TestBreakPointSurviveGC(bool force_compaction) { } -// Test that a break point can be set at a return store location. -TEST(BreakPointSurviveGC) { - TestBreakPointSurviveGC(false); - TestBreakPointSurviveGC(true); -} - - // Test that break points can be set using the global Debug object. TEST(BreakPointThroughJavaScript) { break_point_hit_count = 0; @@ -2259,7 +2244,7 @@ TEST(ScriptBreakPointLineTopLevel) { } f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); SetScriptBreakPointByNameFromJS("test.html", 3, -1); @@ -2753,7 +2738,7 @@ TEST(DebugStepKeyedLoadLoop) { v8::Handle<v8::Value> args[kArgc] = { a }; foo->Call(env->Global(), kArgc, args); - // Setup break point and step through the function. + // Set up break point and step through the function. SetBreakPoint(foo, 3); step_action = StepNext; break_point_hit_count = 0; @@ -2800,7 +2785,7 @@ TEST(DebugStepKeyedStoreLoop) { v8::Handle<v8::Value> args[kArgc] = { a }; foo->Call(env->Global(), kArgc, args); - // Setup break point and step through the function. + // Set up break point and step through the function. SetBreakPoint(foo, 3); step_action = StepNext; break_point_hit_count = 0; @@ -2844,7 +2829,7 @@ TEST(DebugStepNamedLoadLoop) { // Call function without any break points to ensure inlining is in place. foo->Call(env->Global(), 0, NULL); - // Setup break point and step through the function. + // Set up break point and step through the function. SetBreakPoint(foo, 4); step_action = StepNext; break_point_hit_count = 0; @@ -2879,7 +2864,7 @@ static void DoDebugStepNamedStoreLoop(int expected) { // Call function without any break points to ensure inlining is in place. foo->Call(env->Global(), 0, NULL); - // Setup break point and step through the function. + // Set up break point and step through the function. SetBreakPoint(foo, 3); step_action = StepNext; break_point_hit_count = 0; @@ -3761,8 +3746,7 @@ TEST(BreakOnException) { v8::internal::Isolate::Current()->TraceException(false); // Create functions for testing break on exception. - v8::Local<v8::Function> throws = - CompileFunction(&env, "function throws(){throw 1;}", "throws"); + CompileFunction(&env, "function throws(){throw 1;}", "throws"); v8::Local<v8::Function> caught = CompileFunction(&env, "function caught(){try {throws();} catch(e) {};}", @@ -5557,10 +5541,8 @@ TEST(DebuggerUnload) { v8::HandleScope scope; // Get the test functions again. - v8::Local<v8::Function> foo = - v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo"))); - v8::Local<v8::Function> bar = - v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo"))); + v8::Local<v8::Function> foo(v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("foo")))); foo->Call(env->Global(), 0, NULL); CHECK_EQ(0, break_point_hit_count); @@ -5719,7 +5701,7 @@ void HostDispatchV8Thread::Run() { v8::HandleScope scope; DebugLocalContext env; - // Setup message and host dispatch handlers. + // Set up message and host dispatch handlers. v8::Debug::SetMessageHandler2(HostDispatchMessageHandler); v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */); @@ -5807,7 +5789,7 @@ void DebugMessageDispatchV8Thread::Run() { v8::HandleScope scope; DebugLocalContext env; - // Setup debug message dispatch handler. + // Set up debug message dispatch handler. v8::Debug::SetDebugMessageDispatchHandler(DebugMessageHandler); CompileRun("var y = 1 + 2;\n"); @@ -5861,7 +5843,7 @@ TEST(DebuggerAgent) { bool ok; // Initialize the socket library. - i::Socket::Setup(); + i::Socket::SetUp(); // Test starting and stopping the agent without any client connection. debugger->StartAgent("test", kPort1); @@ -5959,7 +5941,7 @@ TEST(DebuggerAgentProtocolOverflowHeader) { OS::SNPrintF(i::Vector<char>(port_str, kPortBufferLen), "%d", kPort); // Initialize the socket library. - i::Socket::Setup(); + i::Socket::SetUp(); // Create a socket server to receive a debugger agent message. DebuggerAgentProtocolServerThread* server = @@ -6037,7 +6019,9 @@ TEST(DebugGetLoadedScripts) { EmptyExternalStringResource source_ext_str; v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str); - v8::Handle<v8::Script> evil_script = v8::Script::Compile(source); + v8::Handle<v8::Script> evil_script(v8::Script::Compile(source)); + // "use" evil_script to make the compiler happy. + (void) evil_script; Handle<i::ExternalTwoByteString> i_source( i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source))); // This situation can happen if source was an external string disposed @@ -6472,7 +6456,7 @@ TEST(ScriptCollectedEvent) { // Do garbage collection to ensure that only the script in this test will be // collected afterwards. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); script_collected_count = 0; v8::Debug::SetDebugEventListener(DebugEventScriptCollectedEvent, @@ -6484,7 +6468,7 @@ TEST(ScriptCollectedEvent) { // Do garbage collection to collect the script above which is no longer // referenced. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(2, script_collected_count); @@ -6520,7 +6504,7 @@ TEST(ScriptCollectedEventContext) { // Do garbage collection to ensure that only the script in this test will be // collected afterwards. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); v8::Debug::SetMessageHandler2(ScriptCollectedMessageHandler); { @@ -6531,7 +6515,7 @@ TEST(ScriptCollectedEventContext) { // Do garbage collection to collect the script above which is no longer // referenced. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(2, script_collected_message_count); @@ -6685,7 +6669,7 @@ static void BreakMessageHandler(const v8::Debug::Message& message) { break_point_hit_count++; v8::HandleScope scope; - v8::Handle<v8::String> json = message.GetJSON(); + message.GetJSON(); SendContinueCommand(); } else if (message.IsEvent() && message.GetEvent() == v8::AfterCompile) { @@ -6696,7 +6680,7 @@ static void BreakMessageHandler(const v8::Debug::Message& message) { isolate->stack_guard()->DebugBreak(); // Force serialization to trigger some internal JS execution. - v8::Handle<v8::String> json = message.GetJSON(); + message.GetJSON(); // Restore previous state. if (is_debug_break) { @@ -6879,7 +6863,7 @@ TEST(DebugBreakFunctionApply) { foo->Call(env->Global(), 0, NULL); // When keeping the debug break several break will happen. - CHECK_EQ(3, break_point_hit_count); + CHECK_GT(break_point_hit_count, 1); v8::Debug::SetDebugEventListener(NULL); CheckDebuggerUnloaded(); @@ -7302,4 +7286,65 @@ TEST(DebugBreakLoop) { } +v8::Local<v8::Script> inline_script; + +static void DebugBreakInlineListener(v8::DebugEvent event, + v8::Handle<v8::Object> exec_state, + v8::Handle<v8::Object> event_data, + v8::Handle<v8::Value> data) { + if (event != v8::Break) return; + + int expected_frame_count = 4; + int expected_line_number[] = {1, 4, 7, 12}; + + i::Handle<i::Object> compiled_script = v8::Utils::OpenHandle(*inline_script); + i::Handle<i::Script> source_script = i::Handle<i::Script>(i::Script::cast( + i::JSFunction::cast(*compiled_script)->shared()->script())); + + int break_id = v8::internal::Isolate::Current()->debug()->break_id(); + char script[128]; + i::Vector<char> script_vector(script, sizeof(script)); + OS::SNPrintF(script_vector, "%%GetFrameCount(%d)", break_id); + v8::Local<v8::Value> result = CompileRun(script); + + int frame_count = result->Int32Value(); + CHECK_EQ(expected_frame_count, frame_count); + + for (int i = 0; i < frame_count; i++) { + // The 5. element in the returned array of GetFrameDetails contains the + // source position of that frame. + OS::SNPrintF(script_vector, "%%GetFrameDetails(%d, %d)[5]", break_id, i); + v8::Local<v8::Value> result = CompileRun(script); + CHECK_EQ(expected_line_number[i], + i::GetScriptLineNumber(source_script, result->Int32Value())); + } + v8::Debug::SetDebugEventListener(NULL); + v8::V8::TerminateExecution(); +} + + +TEST(DebugBreakInline) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + DebugLocalContext env; + const char* source = + "function debug(b) { \n" + " if (b) debugger; \n" + "} \n" + "function f(b) { \n" + " debug(b) \n" + "}; \n" + "function g(b) { \n" + " f(b); \n" + "}; \n" + "g(false); \n" + "g(false); \n" + "%OptimizeFunctionOnNextCall(g); \n" + "g(true);"; + v8::Debug::SetDebugEventListener(DebugBreakInlineListener); + inline_script = v8::Script::Compile(v8::String::New(source)); + inline_script->Run(); +} + + #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/test/cctest/test-decls.cc b/deps/v8/test/cctest/test-decls.cc index 619839185e..aa733c70bc 100644 --- a/deps/v8/test/cctest/test-decls.cc +++ b/deps/v8/test/cctest/test-decls.cc @@ -232,7 +232,7 @@ TEST(Unknown) { context.Check("const x; x", 1, // access 2, // declaration + initialization - 2, // declaration + initialization + 1, // declaration EXPECT_RESULT, Undefined()); } @@ -240,7 +240,7 @@ TEST(Unknown) { context.Check("const x = 0; x", 1, // access 2, // declaration + initialization - 2, // declaration + initialization + 1, // declaration EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579 } } @@ -285,18 +285,18 @@ TEST(Present) { { PresentPropertyContext context; context.Check("const x; x", - 0, - 0, + 1, // access + 1, // initialization 1, // (re-)declaration - EXPECT_EXCEPTION); // x has already been declared! + EXPECT_RESULT, Undefined()); } { PresentPropertyContext context; context.Check("const x = 0; x", - 0, - 0, + 1, // access + 1, // initialization 1, // (re-)declaration - EXPECT_EXCEPTION); // x has already been declared! + EXPECT_RESULT, Number::New(0)); } } @@ -341,7 +341,7 @@ TEST(Absent) { context.Check("const x; x", 1, // access 2, // declaration + initialization - 2, // declaration + initializetion + 1, // declaration EXPECT_RESULT, Undefined()); } @@ -349,7 +349,7 @@ TEST(Absent) { context.Check("const x = 0; x", 1, // access 2, // declaration + initialization - 2, // declaration + initialization + 1, // declaration EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579 } @@ -429,18 +429,20 @@ TEST(Appearing) { { AppearingPropertyContext context; context.Check("const x; x", - 0, - 1, // declaration + 1, // access 2, // declaration + initialization - EXPECT_EXCEPTION); // x has already been declared! + 1, // declaration + EXPECT_RESULT, Undefined()); } { AppearingPropertyContext context; context.Check("const x = 0; x", - 0, - 1, // declaration + 1, // access 2, // declaration + initialization - EXPECT_EXCEPTION); // x has already been declared! + 1, // declaration + EXPECT_RESULT, Undefined()); + // Result is undefined because declaration succeeded but + // initialization to 0 failed (due to context behavior). } } @@ -496,9 +498,9 @@ TEST(Reappearing) { { ReappearingPropertyContext context; context.Check("const x; var x = 0", 0, - 2, // var declaration + const initialization - 4, // 2 x declaration + 2 x initialization - EXPECT_EXCEPTION); // x has already been declared! + 3, // const declaration+initialization, var initialization + 3, // 2 x declaration + var initialization + EXPECT_RESULT, Undefined()); } } diff --git a/deps/v8/test/cctest/test-deoptimization.cc b/deps/v8/test/cctest/test-deoptimization.cc index 056c981845..ea34a75371 100644 --- a/deps/v8/test/cctest/test-deoptimization.cc +++ b/deps/v8/test/cctest/test-deoptimization.cc @@ -1,4 +1,4 @@ -// Copyright 2007-2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -237,7 +237,7 @@ TEST(DeoptimizeRecursive) { v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); - Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun); + CHECK(!fun.IsEmpty()); } diff --git a/deps/v8/test/cctest/test-dictionary.cc b/deps/v8/test/cctest/test-dictionary.cc index 15a854b363..793e228a97 100644 --- a/deps/v8/test/cctest/test-dictionary.cc +++ b/deps/v8/test/cctest/test-dictionary.cc @@ -38,6 +38,7 @@ using namespace v8::internal; + TEST(ObjectHashTable) { v8::HandleScope scope; LocalContext context; @@ -66,7 +67,8 @@ TEST(ObjectHashTable) { CHECK_EQ(table->NumberOfDeletedElements(), 1); CHECK_EQ(table->Lookup(*a), HEAP->undefined_value()); - // Keys should map back to their respective values. + // Keys should map back to their respective values and also should get + // an identity hash code generated. for (int i = 0; i < 100; i++) { Handle<JSObject> key = FACTORY->NewJSArray(7); Handle<JSObject> value = FACTORY->NewJSArray(11); @@ -74,12 +76,67 @@ TEST(ObjectHashTable) { CHECK_EQ(table->NumberOfElements(), i + 1); CHECK_NE(table->FindEntry(*key), ObjectHashTable::kNotFound); CHECK_EQ(table->Lookup(*key), *value); + CHECK(key->GetIdentityHash(OMIT_CREATION)->ToObjectChecked()->IsSmi()); + } + + // Keys never added to the map which already have an identity hash + // code should not be found. + for (int i = 0; i < 100; i++) { + Handle<JSObject> key = FACTORY->NewJSArray(7); + CHECK(key->GetIdentityHash(ALLOW_CREATION)->ToObjectChecked()->IsSmi()); + CHECK_EQ(table->FindEntry(*key), ObjectHashTable::kNotFound); + CHECK_EQ(table->Lookup(*key), HEAP->undefined_value()); + CHECK(key->GetIdentityHash(OMIT_CREATION)->ToObjectChecked()->IsSmi()); } - // Keys never added to the map should not be found. - for (int i = 0; i < 1000; i++) { - Handle<JSObject> o = FACTORY->NewJSArray(100); - CHECK_EQ(table->FindEntry(*o), ObjectHashTable::kNotFound); - CHECK_EQ(table->Lookup(*o), HEAP->undefined_value()); + // Keys that don't have an identity hash should not be found and also + // should not get an identity hash code generated. + for (int i = 0; i < 100; i++) { + Handle<JSObject> key = FACTORY->NewJSArray(7); + CHECK_EQ(table->Lookup(*key), HEAP->undefined_value()); + CHECK_EQ(key->GetIdentityHash(OMIT_CREATION), HEAP->undefined_value()); } } + + +#ifdef DEBUG +TEST(ObjectHashSetCausesGC) { + v8::HandleScope scope; + LocalContext context; + Handle<ObjectHashSet> table = FACTORY->NewObjectHashSet(1); + Handle<JSObject> key = FACTORY->NewJSArray(0); + + // Simulate a full heap so that generating an identity hash code + // in subsequent calls will request GC. + FLAG_gc_interval = 0; + + // Calling Contains() should not cause GC ever. + CHECK(!table->Contains(*key)); + + // Calling Remove() should not cause GC ever. + CHECK(!table->Remove(*key)->IsFailure()); + + // Calling Add() should request GC by returning a failure. + CHECK(table->Add(*key)->IsRetryAfterGC()); +} +#endif + + +#ifdef DEBUG +TEST(ObjectHashTableCausesGC) { + v8::HandleScope scope; + LocalContext context; + Handle<ObjectHashTable> table = FACTORY->NewObjectHashTable(1); + Handle<JSObject> key = FACTORY->NewJSArray(0); + + // Simulate a full heap so that generating an identity hash code + // in subsequent calls will request GC. + FLAG_gc_interval = 0; + + // Calling Lookup() should not cause GC ever. + CHECK(table->Lookup(*key)->IsUndefined()); + + // Calling Put() should request GC by returning a failure. + CHECK(table->Put(*key, *key)->IsRetryAfterGC()); +} +#endif diff --git a/deps/v8/test/cctest/test-disasm-arm.cc b/deps/v8/test/cctest/test-disasm-arm.cc index 032e6bc0fc..0e9432d95d 100644 --- a/deps/v8/test/cctest/test-disasm-arm.cc +++ b/deps/v8/test/cctest/test-disasm-arm.cc @@ -69,10 +69,10 @@ bool DisassembleAndCompare(byte* pc, const char* compare_string) { } -// Setup V8 to a state where we can at least run the assembler and +// Set up V8 to a state where we can at least run the assembler and // disassembler. Declare the variables and allocate the data structures used // in the rest of the macros. -#define SETUP() \ +#define SET_UP() \ InitializeVM(); \ v8::HandleScope scope; \ byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \ @@ -102,7 +102,7 @@ if (failure) { \ TEST(Type0) { - SETUP(); + SET_UP(); COMPARE(and_(r0, r1, Operand(r2)), "e0010002 and r0, r1, r2"); @@ -329,7 +329,7 @@ TEST(Type0) { TEST(Type1) { - SETUP(); + SET_UP(); COMPARE(and_(r0, r1, Operand(0x00000000)), "e2010000 and r0, r1, #0"); @@ -358,7 +358,7 @@ TEST(Type1) { TEST(Type3) { - SETUP(); + SET_UP(); if (CpuFeatures::IsSupported(ARMv7)) { COMPARE(ubfx(r0, r1, 5, 10), @@ -413,7 +413,7 @@ TEST(Type3) { TEST(Vfp) { - SETUP(); + SET_UP(); if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); @@ -546,7 +546,7 @@ TEST(Vfp) { TEST(LoadStore) { - SETUP(); + SET_UP(); COMPARE(ldrb(r0, MemOperand(r1)), "e5d10000 ldrb r0, [r1, #+0]"); diff --git a/deps/v8/test/cctest/test-disasm-ia32.cc b/deps/v8/test/cctest/test-disasm-ia32.cc index 9f7d0bb6e0..da09505437 100644 --- a/deps/v8/test/cctest/test-disasm-ia32.cc +++ b/deps/v8/test/cctest/test-disasm-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2007-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -63,9 +63,9 @@ TEST(DisasmIa320) { // Short immediate instructions __ adc(eax, 12345678); - __ add(Operand(eax), Immediate(12345678)); + __ add(eax, Immediate(12345678)); __ or_(eax, 12345678); - __ sub(Operand(eax), Immediate(12345678)); + __ sub(eax, Immediate(12345678)); __ xor_(eax, 12345678); __ and_(eax, 12345678); Handle<FixedArray> foo = FACTORY->NewFixedArray(10, TENURED); @@ -75,7 +75,7 @@ TEST(DisasmIa320) { __ mov(ebx, Operand(esp, ecx, times_2, 0)); // [esp+ecx*4] // ---- All instructions that I can think of - __ add(edx, Operand(ebx)); + __ add(edx, ebx); __ add(edx, Operand(12, RelocInfo::NONE)); __ add(edx, Operand(ebx, 0)); __ add(edx, Operand(ebx, 16)); @@ -89,7 +89,7 @@ TEST(DisasmIa320) { __ add(Operand(ebp, ecx, times_4, 12), Immediate(12)); __ nop(); - __ add(Operand(ebx), Immediate(12)); + __ add(ebx, Immediate(12)); __ nop(); __ adc(ecx, 12); __ adc(ecx, 1000); @@ -116,16 +116,16 @@ TEST(DisasmIa320) { CpuFeatures::Scope fscope(RDTSC); __ rdtsc(); } - __ movsx_b(edx, Operand(ecx)); - __ movsx_w(edx, Operand(ecx)); - __ movzx_b(edx, Operand(ecx)); - __ movzx_w(edx, Operand(ecx)); + __ movsx_b(edx, ecx); + __ movsx_w(edx, ecx); + __ movzx_b(edx, ecx); + __ movzx_w(edx, ecx); __ nop(); - __ imul(edx, Operand(ecx)); - __ shld(edx, Operand(ecx)); - __ shrd(edx, Operand(ecx)); - __ bts(Operand(edx), ecx); + __ imul(edx, ecx); + __ shld(edx, ecx); + __ shrd(edx, ecx); + __ bts(edx, ecx); __ bts(Operand(ebx, ecx, times_4, 0), ecx); __ nop(); __ pushad(); @@ -146,9 +146,9 @@ TEST(DisasmIa320) { __ nop(); __ add(edx, Operand(esp, 16)); - __ add(edx, Operand(ecx)); - __ mov_b(edx, Operand(ecx)); - __ mov_b(Operand(ecx), 6); + __ add(edx, ecx); + __ mov_b(edx, ecx); + __ mov_b(ecx, 6); __ mov_b(Operand(ebx, ecx, times_4, 10000), 6); __ mov_b(Operand(esp, 16), edx); __ mov_w(edx, Operand(esp, 16)); @@ -216,22 +216,20 @@ TEST(DisasmIa320) { __ adc(edx, 12345); - __ add(Operand(ebx), Immediate(12)); + __ add(ebx, Immediate(12)); __ add(Operand(edx, ecx, times_4, 10000), Immediate(12)); __ and_(ebx, 12345); __ cmp(ebx, 12345); - __ cmp(Operand(ebx), Immediate(12)); + __ cmp(ebx, Immediate(12)); __ cmp(Operand(edx, ecx, times_4, 10000), Immediate(12)); + __ cmpb(eax, 100); __ or_(ebx, 12345); - __ sub(Operand(ebx), Immediate(12)); + __ sub(ebx, Immediate(12)); __ sub(Operand(edx, ecx, times_4, 10000), Immediate(12)); - __ subb(Operand(edx, ecx, times_4, 10000), 100); - __ subb(Operand(eax), 100); - __ subb(eax, Operand(edx, ecx, times_4, 10000)); __ xor_(ebx, 12345); @@ -244,7 +242,7 @@ TEST(DisasmIa320) { __ stos(); __ sub(edx, Operand(ebx, ecx, times_4, 10000)); - __ sub(edx, Operand(ebx)); + __ sub(edx, ebx); __ test(edx, Immediate(12345)); __ test(edx, Operand(ebx, ecx, times_8, 10000)); @@ -446,11 +444,16 @@ TEST(DisasmIa320) { { if (CpuFeatures::IsSupported(SSE4_1)) { CpuFeatures::Scope scope(SSE4_1); - __ pextrd(Operand(eax), xmm0, 1); - __ pinsrd(xmm1, Operand(eax), 0); + __ pextrd(eax, xmm0, 1); + __ pinsrd(xmm1, eax, 0); } } + // Nop instructions + for (int i = 0; i < 16; i++) { + __ Nop(i); + } + __ ret(0); CodeDesc desc; diff --git a/deps/v8/test/cctest/test-disasm-mips.cc b/deps/v8/test/cctest/test-disasm-mips.cc index 5ad99d7a39..8eadc6483b 100644 --- a/deps/v8/test/cctest/test-disasm-mips.cc +++ b/deps/v8/test/cctest/test-disasm-mips.cc @@ -71,10 +71,10 @@ bool DisassembleAndCompare(byte* pc, const char* compare_string) { } -// Setup V8 to a state where we can at least run the assembler and +// Set up V8 to a state where we can at least run the assembler and // disassembler. Declare the variables and allocate the data structures used // in the rest of the macros. -#define SETUP() \ +#define SET_UP() \ InitializeVM(); \ v8::HandleScope scope; \ byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \ @@ -104,7 +104,7 @@ if (failure) { \ TEST(Type0) { - SETUP(); + SET_UP(); COMPARE(addu(a0, a1, a2), "00a62021 addu a0, a1, a2"); diff --git a/deps/v8/test/cctest/test-disasm-x64.cc b/deps/v8/test/cctest/test-disasm-x64.cc new file mode 100644 index 0000000000..da85eb933e --- /dev/null +++ b/deps/v8/test/cctest/test-disasm-x64.cc @@ -0,0 +1,429 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdlib.h> + +#include "v8.h" + +#include "debug.h" +#include "disasm.h" +#include "disassembler.h" +#include "macro-assembler.h" +#include "serialize.h" +#include "cctest.h" + +using namespace v8::internal; + +static v8::Persistent<v8::Context> env; + +static void InitializeVM() { + if (env.IsEmpty()) { + env = v8::Context::New(); + } +} + + +#define __ assm. + + +static void DummyStaticFunction(Object* result) { +} + + +TEST(DisasmX64) { + InitializeVM(); + v8::HandleScope scope; + v8::internal::byte buffer[2048]; + Assembler assm(Isolate::Current(), buffer, sizeof buffer); + DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging) + + // Short immediate instructions + __ addq(rax, Immediate(12345678)); + __ or_(rax, Immediate(12345678)); + __ subq(rax, Immediate(12345678)); + __ xor_(rax, Immediate(12345678)); + __ and_(rax, Immediate(12345678)); + + // ---- This one caused crash + __ movq(rbx, Operand(rsp, rcx, times_2, 0)); // [rsp+rcx*4] + + // ---- All instructions that I can think of + __ addq(rdx, rbx); + __ addq(rdx, Operand(rbx, 0)); + __ addq(rdx, Operand(rbx, 16)); + __ addq(rdx, Operand(rbx, 1999)); + __ addq(rdx, Operand(rsp, 0)); + __ addq(rdx, Operand(rsp, 16)); + __ addq(rdx, Operand(rsp, 1999)); + __ nop(); + __ addq(rdi, Operand(rbp, rcx, times_4, 0)); + __ addq(rdi, Operand(rbp, rcx, times_4, 12)); + __ addq(Operand(rbp, rcx, times_4, 12), Immediate(12)); + + __ nop(); + __ addq(rbx, Immediate(12)); + __ nop(); + __ nop(); + __ and_(rdx, Immediate(3)); + __ and_(rdx, Operand(rsp, 4)); + __ cmpq(rdx, Immediate(3)); + __ cmpq(rdx, Operand(rsp, 4)); + __ cmpq(Operand(rbp, rcx, times_4, 0), Immediate(1000)); + __ cmpb(rbx, Operand(rbp, rcx, times_2, 0)); + __ cmpb(Operand(rbp, rcx, times_2, 0), rbx); + __ or_(rdx, Immediate(3)); + __ xor_(rdx, Immediate(3)); + __ nop(); + { + CHECK(CpuFeatures::IsSupported(CPUID)); + CpuFeatures::Scope fscope(CPUID); + __ cpuid(); + } + { + CHECK(CpuFeatures::IsSupported(RDTSC)); + CpuFeatures::Scope fscope(RDTSC); + __ rdtsc(); + } + __ movsxbq(rdx, Operand(rcx, 0)); + __ movsxwq(rdx, Operand(rcx, 0)); + __ movzxbl(rdx, Operand(rcx, 0)); + __ movzxwl(rdx, Operand(rcx, 0)); + __ movzxbq(rdx, Operand(rcx, 0)); + __ movzxwq(rdx, Operand(rcx, 0)); + + __ nop(); + __ imul(rdx, rcx); + __ shld(rdx, rcx); + __ shrd(rdx, rcx); + __ bts(Operand(rdx, 0), rcx); + __ bts(Operand(rbx, rcx, times_4, 0), rcx); + __ nop(); + __ push(Immediate(12)); + __ push(Immediate(23456)); + __ push(rcx); + __ push(rsi); + __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ push(Operand(rbx, rcx, times_4, 0)); + __ push(Operand(rbx, rcx, times_4, 0)); + __ push(Operand(rbx, rcx, times_4, 10000)); + __ pop(rdx); + __ pop(rax); + __ pop(Operand(rbx, rcx, times_4, 0)); + __ nop(); + + __ addq(rdx, Operand(rsp, 16)); + __ addq(rdx, rcx); + __ movb(rdx, Operand(rcx, 0)); + __ movb(rcx, Immediate(6)); + __ movb(Operand(rsp, 16), rdx); + __ movw(Operand(rsp, 16), rdx); + __ nop(); + __ movsxwq(rdx, Operand(rsp, 12)); + __ movsxbq(rdx, Operand(rsp, 12)); + __ movsxlq(rdx, Operand(rsp, 12)); + __ movzxwq(rdx, Operand(rsp, 12)); + __ movzxbq(rdx, Operand(rsp, 12)); + __ nop(); + __ movq(rdx, Immediate(1234567)); + __ movq(rdx, Operand(rsp, 12)); + __ movq(Operand(rbx, rcx, times_4, 10000), Immediate(12345)); + __ movq(Operand(rbx, rcx, times_4, 10000), rdx); + __ nop(); + __ decb(rdx); + __ decb(Operand(rax, 10)); + __ decb(Operand(rbx, rcx, times_4, 10000)); + __ decq(rdx); + __ cdq(); + + __ nop(); + __ idivq(rdx); + __ mul(rdx); + __ neg(rdx); + __ not_(rdx); + __ testq(Operand(rbx, rcx, times_4, 10000), rdx); + + __ imul(rdx, Operand(rbx, rcx, times_4, 10000)); + __ imul(rdx, rcx, Immediate(12)); + __ imul(rdx, rcx, Immediate(1000)); + + __ incq(rdx); + __ incq(Operand(rbx, rcx, times_4, 10000)); + __ push(Operand(rbx, rcx, times_4, 10000)); + __ pop(Operand(rbx, rcx, times_4, 10000)); + __ jmp(Operand(rbx, rcx, times_4, 10000)); + + __ lea(rdx, Operand(rbx, rcx, times_4, 10000)); + __ or_(rdx, Immediate(12345)); + __ or_(rdx, Operand(rbx, rcx, times_4, 10000)); + + __ nop(); + + __ rcl(rdx, Immediate(1)); + __ rcl(rdx, Immediate(7)); + __ rcr(rdx, Immediate(1)); + __ rcr(rdx, Immediate(7)); + __ sar(rdx, Immediate(1)); + __ sar(rdx, Immediate(6)); + __ sar_cl(rdx); + __ sbbq(rdx, rbx); + __ shld(rdx, rbx); + __ shl(rdx, Immediate(1)); + __ shl(rdx, Immediate(6)); + __ shl_cl(rdx); + __ shrd(rdx, rbx); + __ shr(rdx, Immediate(1)); + __ shr(rdx, Immediate(7)); + __ shr_cl(rdx); + + + // Immediates + + __ addq(rbx, Immediate(12)); + __ addq(Operand(rdx, rcx, times_4, 10000), Immediate(12)); + + __ and_(rbx, Immediate(12345)); + + __ cmpq(rbx, Immediate(12345)); + __ cmpq(rbx, Immediate(12)); + __ cmpq(Operand(rdx, rcx, times_4, 10000), Immediate(12)); + __ cmpb(rax, Immediate(100)); + + __ or_(rbx, Immediate(12345)); + + __ subq(rbx, Immediate(12)); + __ subq(Operand(rdx, rcx, times_4, 10000), Immediate(12)); + + __ xor_(rbx, Immediate(12345)); + + __ imul(rdx, rcx, Immediate(12)); + __ imul(rdx, rcx, Immediate(1000)); + + __ cld(); + + __ subq(rdx, Operand(rbx, rcx, times_4, 10000)); + __ subq(rdx, rbx); + + __ testq(rdx, Immediate(12345)); + __ testq(Operand(rbx, rcx, times_8, 10000), rdx); + __ testb(Operand(rcx, rbx, times_2, 1000), rdx); + __ testb(Operand(rax, -20), Immediate(0x9A)); + __ nop(); + + __ xor_(rdx, Immediate(12345)); + __ xor_(rdx, Operand(rbx, rcx, times_8, 10000)); + __ bts(Operand(rbx, rcx, times_8, 10000), rdx); + __ hlt(); + __ int3(); + __ ret(0); + __ ret(8); + + // Calls + + Label L1, L2; + __ bind(&L1); + __ nop(); + __ call(&L1); + __ call(&L2); + __ nop(); + __ bind(&L2); + __ call(Operand(rbx, rcx, times_4, 10000)); + __ nop(); + Handle<Code> ic(Isolate::Current()->builtins()->builtin( + Builtins::kLoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ nop(); + __ nop(); + + __ jmp(&L1); + __ jmp(Operand(rbx, rcx, times_4, 10000)); +#ifdef ENABLE_DEBUGGER_SUPPORT + ExternalReference after_break_target = + ExternalReference(Debug_Address::AfterBreakTarget(), + assm.isolate()); +#endif // ENABLE_DEBUGGER_SUPPORT + __ jmp(ic, RelocInfo::CODE_TARGET); + __ nop(); + + + Label Ljcc; + __ nop(); + // long jumps + __ j(overflow, &Ljcc); + __ j(no_overflow, &Ljcc); + __ j(below, &Ljcc); + __ j(above_equal, &Ljcc); + __ j(equal, &Ljcc); + __ j(not_equal, &Ljcc); + __ j(below_equal, &Ljcc); + __ j(above, &Ljcc); + __ j(sign, &Ljcc); + __ j(not_sign, &Ljcc); + __ j(parity_even, &Ljcc); + __ j(parity_odd, &Ljcc); + __ j(less, &Ljcc); + __ j(greater_equal, &Ljcc); + __ j(less_equal, &Ljcc); + __ j(greater, &Ljcc); + __ nop(); + __ bind(&Ljcc); + // short jumps + __ j(overflow, &Ljcc); + __ j(no_overflow, &Ljcc); + __ j(below, &Ljcc); + __ j(above_equal, &Ljcc); + __ j(equal, &Ljcc); + __ j(not_equal, &Ljcc); + __ j(below_equal, &Ljcc); + __ j(above, &Ljcc); + __ j(sign, &Ljcc); + __ j(not_sign, &Ljcc); + __ j(parity_even, &Ljcc); + __ j(parity_odd, &Ljcc); + __ j(less, &Ljcc); + __ j(greater_equal, &Ljcc); + __ j(less_equal, &Ljcc); + __ j(greater, &Ljcc); + + // 0xD9 instructions + __ nop(); + + __ fld(1); + __ fld1(); + __ fldz(); + __ fldpi(); + __ fabs(); + __ fchs(); + __ fprem(); + __ fprem1(); + __ fincstp(); + __ ftst(); + __ fxch(3); + __ fld_s(Operand(rbx, rcx, times_4, 10000)); + __ fstp_s(Operand(rbx, rcx, times_4, 10000)); + __ ffree(3); + __ fld_d(Operand(rbx, rcx, times_4, 10000)); + __ fstp_d(Operand(rbx, rcx, times_4, 10000)); + __ nop(); + + __ fild_s(Operand(rbx, rcx, times_4, 10000)); + __ fistp_s(Operand(rbx, rcx, times_4, 10000)); + __ fild_d(Operand(rbx, rcx, times_4, 10000)); + __ fistp_d(Operand(rbx, rcx, times_4, 10000)); + __ fnstsw_ax(); + __ nop(); + __ fadd(3); + __ fsub(3); + __ fmul(3); + __ fdiv(3); + + __ faddp(3); + __ fsubp(3); + __ fmulp(3); + __ fdivp(3); + __ fcompp(); + __ fwait(); + __ nop(); + { + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ cvttss2si(rdx, Operand(rbx, rcx, times_4, 10000)); + __ cvttss2si(rdx, xmm1); + __ cvttsd2si(rdx, Operand(rbx, rcx, times_4, 10000)); + __ cvttsd2si(rdx, xmm1); + __ cvttsd2siq(rdx, xmm1); + __ addsd(xmm1, xmm0); + __ mulsd(xmm1, xmm0); + __ subsd(xmm1, xmm0); + __ divsd(xmm1, xmm0); + __ movsd(xmm1, Operand(rbx, rcx, times_4, 10000)); + __ movsd(Operand(rbx, rcx, times_4, 10000), xmm1); + __ ucomisd(xmm0, xmm1); + + // 128 bit move instructions. + __ movdqa(xmm0, Operand(rbx, rcx, times_4, 10000)); + __ movdqa(Operand(rbx, rcx, times_4, 10000), xmm0); + } + } + + // cmov. + { + if (CpuFeatures::IsSupported(CMOV)) { + CpuFeatures::Scope use_cmov(CMOV); + __ cmovq(overflow, rax, Operand(rax, 0)); + __ cmovq(no_overflow, rax, Operand(rax, 1)); + __ cmovq(below, rax, Operand(rax, 2)); + __ cmovq(above_equal, rax, Operand(rax, 3)); + __ cmovq(equal, rax, Operand(rbx, 0)); + __ cmovq(not_equal, rax, Operand(rbx, 1)); + __ cmovq(below_equal, rax, Operand(rbx, 2)); + __ cmovq(above, rax, Operand(rbx, 3)); + __ cmovq(sign, rax, Operand(rcx, 0)); + __ cmovq(not_sign, rax, Operand(rcx, 1)); + __ cmovq(parity_even, rax, Operand(rcx, 2)); + __ cmovq(parity_odd, rax, Operand(rcx, 3)); + __ cmovq(less, rax, Operand(rdx, 0)); + __ cmovq(greater_equal, rax, Operand(rdx, 1)); + __ cmovq(less_equal, rax, Operand(rdx, 2)); + __ cmovq(greater, rax, Operand(rdx, 3)); + } + } + + // andpd, etc. + { + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatures::Scope fscope(SSE2); + __ andpd(xmm0, xmm1); + __ andpd(xmm1, xmm2); + + __ movaps(xmm0, xmm1); + __ movaps(xmm1, xmm2); + } + } + + // Nop instructions + for (int i = 0; i < 16; i++) { + __ Nop(i); + } + + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Object* code = HEAP->CreateCode( + desc, + Code::ComputeFlags(Code::STUB), + Handle<Object>(HEAP->undefined_value()))->ToObjectChecked(); + CHECK(code->IsCode()); +#ifdef OBJECT_PRINT + Code::cast(code)->Print(); + byte* begin = Code::cast(code)->instruction_start(); + byte* end = begin + Code::cast(code)->instruction_size(); + disasm::Disassembler::Disassemble(stdout, begin, end); +#endif +} + +#undef __ diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index 6c2afd47f7..f57477e7f4 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -252,6 +252,28 @@ TEST(HeapSnapshotHeapNumbers) { CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); } +TEST(HeapSnapshotSlicedString) { + v8::HandleScope scope; + LocalContext env; + CompileRun( + "parent_string = \"123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789.\";" + "child_string = parent_string.slice(100);"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("strings")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* parent_string = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "parent_string"); + CHECK_NE(NULL, parent_string); + const v8::HeapGraphNode* child_string = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "child_string"); + CHECK_NE(NULL, child_string); + const v8::HeapGraphNode* parent = + GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent"); + CHECK_EQ(parent_string, parent); +} TEST(HeapSnapshotInternalReferences) { v8::HandleScope scope; @@ -294,7 +316,7 @@ TEST(HeapEntryIdsAndGC) { const v8::HeapSnapshot* snapshot1 = v8::HeapProfiler::TakeSnapshot(v8_str("s1")); - HEAP->CollectAllGarbage(true); // Enforce compaction. + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); const v8::HeapSnapshot* snapshot2 = v8::HeapProfiler::TakeSnapshot(v8_str("s2")); @@ -563,6 +585,22 @@ TEST(HeapSnapshotJSONSerializationAborting) { } +static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, + const v8::HeapGraphNode* node, + int level, int max_level) { + if (level > max_level) return; + CHECK_EQ(node, snapshot->GetNodeById(node->GetId())); + for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = node->GetChild(i); + const v8::HeapGraphNode* child = + snapshot->GetNodeById(prop->GetToNode()->GetId()); + CHECK_EQ_UINT64_T(prop->GetToNode()->GetId(), child->GetId()); + CHECK_EQ(prop->GetToNode(), child); + CheckChildrenIds(snapshot, child, level + 1, max_level); + } +} + + TEST(HeapSnapshotGetNodeById) { v8::HandleScope scope; LocalContext env; @@ -570,12 +608,7 @@ TEST(HeapSnapshotGetNodeById) { const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8_str("id")); const v8::HeapGraphNode* root = snapshot->GetRoot(); - CHECK_EQ(root, snapshot->GetNodeById(root->GetId())); - for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) { - const v8::HeapGraphEdge* prop = root->GetChild(i); - CHECK_EQ( - prop->GetToNode(), snapshot->GetNodeById(prop->GetToNode()->GetId())); - } + CheckChildrenIds(snapshot, root, 0, 3); // Check a big id, which should not exist yet. CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL)); } @@ -633,11 +666,13 @@ namespace { class TestRetainedObjectInfo : public v8::RetainedObjectInfo { public: TestRetainedObjectInfo(int hash, + const char* group_label, const char* label, intptr_t element_count = -1, intptr_t size = -1) : disposed_(false), hash_(hash), + group_label_(group_label), label_(label), element_count_(element_count), size_(size) { @@ -652,6 +687,7 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo { return GetHash() == other->GetHash(); } virtual intptr_t GetHash() { return hash_; } + virtual const char* GetGroupLabel() { return group_label_; } virtual const char* GetLabel() { return label_; } virtual intptr_t GetElementCount() { return element_count_; } virtual intptr_t GetSizeInBytes() { return size_; } @@ -663,15 +699,15 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo { if (wrapper->IsString()) { v8::String::AsciiValue ascii(wrapper); if (strcmp(*ascii, "AAA") == 0) - return new TestRetainedObjectInfo(1, "aaa", 100); + return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); else if (strcmp(*ascii, "BBB") == 0) - return new TestRetainedObjectInfo(1, "aaa", 100); + return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); } } else if (class_id == 2) { if (wrapper->IsString()) { v8::String::AsciiValue ascii(wrapper); if (strcmp(*ascii, "CCC") == 0) - return new TestRetainedObjectInfo(2, "ccc"); + return new TestRetainedObjectInfo(2, "ccc-group", "ccc"); } } CHECK(false); @@ -684,6 +720,7 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo { bool disposed_; int category_; int hash_; + const char* group_label_; const char* label_; intptr_t element_count_; intptr_t size_; @@ -736,18 +773,21 @@ TEST(HeapSnapshotRetainedObjectInfo) { delete TestRetainedObjectInfo::instances[i]; } - const v8::HeapGraphNode* natives = GetNode( - snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)"); - CHECK_NE(NULL, natives); - CHECK_EQ(2, natives->GetChildrenCount()); + const v8::HeapGraphNode* native_group_aaa = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group"); + CHECK_NE(NULL, native_group_aaa); + CHECK_EQ(1, native_group_aaa->GetChildrenCount()); const v8::HeapGraphNode* aaa = GetNode( - natives, v8::HeapGraphNode::kNative, "aaa / 100 entries"); + native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries"); CHECK_NE(NULL, aaa); + CHECK_EQ(2, aaa->GetChildrenCount()); + + const v8::HeapGraphNode* native_group_ccc = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group"); const v8::HeapGraphNode* ccc = GetNode( - natives, v8::HeapGraphNode::kNative, "ccc"); + native_group_ccc, v8::HeapGraphNode::kNative, "ccc"); CHECK_NE(NULL, ccc); - CHECK_EQ(2, aaa->GetChildrenCount()); const v8::HeapGraphNode* n_AAA = GetNode( aaa, v8::HeapGraphNode::kString, "AAA"); CHECK_NE(NULL, n_AAA); @@ -765,6 +805,75 @@ TEST(HeapSnapshotRetainedObjectInfo) { } +class GraphWithImplicitRefs { + public: + static const int kObjectsCount = 4; + explicit GraphWithImplicitRefs(LocalContext* env) { + CHECK_EQ(NULL, instance_); + instance_ = this; + for (int i = 0; i < kObjectsCount; i++) { + objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New()); + } + (*env)->Global()->Set(v8_str("root_object"), objects_[0]); + } + ~GraphWithImplicitRefs() { + instance_ = NULL; + } + + static void gcPrologue() { + instance_->AddImplicitReferences(); + } + + private: + void AddImplicitReferences() { + // 0 -> 1 + v8::V8::AddImplicitReferences( + v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1); + // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3 + v8::V8::AddImplicitReferences( + v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2); + } + + v8::Persistent<v8::Value> objects_[kObjectsCount]; + static GraphWithImplicitRefs* instance_; +}; + +GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL; + + +TEST(HeapSnapshotImplicitReferences) { + v8::HandleScope scope; + LocalContext env; + + GraphWithImplicitRefs graph(&env); + v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs")); + + const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot); + // Use kShortcut type to skip intermediate JSGlobalPropertyCell + const v8::HeapGraphNode* obj0 = GetProperty( + global_object, v8::HeapGraphEdge::kShortcut, "root_object"); + CHECK(obj0); + CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType()); + const v8::HeapGraphNode* obj1 = GetProperty( + obj0, v8::HeapGraphEdge::kInternal, "native"); + CHECK(obj1); + int implicit_targets_count = 0; + for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = obj1->GetChild(i); + v8::String::AsciiValue prop_name(prop->GetName()); + if (prop->GetType() == v8::HeapGraphEdge::kInternal && + strcmp("native", *prop_name) == 0) { + ++implicit_targets_count; + } + } + CHECK_EQ(2, implicit_targets_count); + v8::V8::SetGlobalGCPrologueCallback(NULL); +} + + TEST(DeleteAllHeapSnapshots) { v8::HandleScope scope; LocalContext env; @@ -873,6 +982,20 @@ TEST(DocumentURLWithException) { } +TEST(NoHandleLeaks) { + v8::HandleScope scope; + LocalContext env; + + CompileRun("document = { URL:\"abcdefgh\" };"); + + v8::Handle<v8::String> name(v8_str("leakz")); + int count_before = i::HandleScope::NumberOfHandles(); + v8::HeapProfiler::TakeSnapshot(name); + int count_after = i::HandleScope::NumberOfHandles(); + CHECK_EQ(count_before, count_after); +} + + TEST(NodesIteration) { v8::HandleScope scope; LocalContext env; @@ -1001,3 +1124,110 @@ TEST(GetConstructorName) { CHECK_EQ(0, StringCmp( "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6))); } + + +TEST(FastCaseGetter) { + v8::HandleScope scope; + LocalContext env; + + CompileRun("var obj1 = {};\n" + "obj1.__defineGetter__('propWithGetter', function Y() {\n" + " return 42;\n" + "});\n" + "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" + " return this.value_ = value;\n" + "});\n"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter")); + + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_NE(NULL, global); + const v8::HeapGraphNode* obj1 = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "obj1"); + CHECK_NE(NULL, obj1); + const v8::HeapGraphNode* getterFunction = + GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter"); + CHECK_NE(NULL, getterFunction); + const v8::HeapGraphNode* setterFunction = + GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter"); + CHECK_NE(NULL, setterFunction); +} + + +bool HasWeakEdge(const v8::HeapGraphNode* node) { + for (int i = 0; i < node->GetChildrenCount(); ++i) { + const v8::HeapGraphEdge* handle_edge = node->GetChild(i); + if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true; + } + return false; +} + + +bool HasWeakGlobalHandle() { + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); + const v8::HeapGraphNode* gc_roots = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); + CHECK_NE(NULL, gc_roots); + const v8::HeapGraphNode* global_handles = GetNode( + gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); + CHECK_NE(NULL, global_handles); + return HasWeakEdge(global_handles); +} + + +static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { + handle.Dispose(); +} + + +TEST(WeakGlobalHandle) { + v8::HandleScope scope; + LocalContext env; + + CHECK(!HasWeakGlobalHandle()); + + v8::Persistent<v8::Object> handle = + v8::Persistent<v8::Object>::New(v8::Object::New()); + handle.MakeWeak(NULL, PersistentHandleCallback); + + CHECK(HasWeakGlobalHandle()); +} + + +TEST(WeakGlobalContextRefs) { + v8::HandleScope scope; + LocalContext env; + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); + const v8::HeapGraphNode* gc_roots = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); + CHECK_NE(NULL, gc_roots); + const v8::HeapGraphNode* global_handles = GetNode( + gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); + CHECK_NE(NULL, global_handles); + const v8::HeapGraphNode* global_context = GetNode( + global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext"); + CHECK_NE(NULL, global_context); + CHECK(HasWeakEdge(global_context)); +} + + +TEST(SfiAndJsFunctionWeakRefs) { + v8::HandleScope scope; + LocalContext env; + + CompileRun( + "fun = (function (x) { return function () { return x + 1; } })(1);"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("fun")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_NE(NULL, global); + const v8::HeapGraphNode* fun = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun"); + CHECK(HasWeakEdge(fun)); + const v8::HeapGraphNode* shared = + GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); + CHECK(HasWeakEdge(shared)); +} diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index 11b8813063..a4d4be4816 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. #include <stdlib.h> @@ -612,7 +612,7 @@ TEST(ObjectProperties) { CHECK(!obj->HasLocalProperty(*second)); // check string and symbol match - static const char* string1 = "fisk"; + const char* string1 = "fisk"; Handle<String> s1 = FACTORY->NewStringFromAscii(CStrVector(string1)); obj->SetProperty( *s1, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); @@ -620,7 +620,7 @@ TEST(ObjectProperties) { CHECK(obj->HasLocalProperty(*s1_symbol)); // check symbol and string match - static const char* string2 = "fugl"; + const char* string2 = "fugl"; Handle<String> s2_symbol = FACTORY->LookupAsciiSymbol(string2); obj->SetProperty( *s2_symbol, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); @@ -667,22 +667,23 @@ TEST(JSArray) { Handle<JSObject> object = FACTORY->NewJSObject(function); Handle<JSArray> array = Handle<JSArray>::cast(object); // We just initialized the VM, no heap allocation failure yet. - Object* ok = array->Initialize(0)->ToObjectChecked(); + array->Initialize(0)->ToObjectChecked(); // Set array length to 0. - ok = array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); + array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); CHECK_EQ(Smi::FromInt(0), array->length()); - CHECK(array->HasFastElements()); // Must be in fast mode. + // Must be in fast mode. + CHECK(array->HasFastTypeElements()); // array[length] = name. - ok = array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked(); + array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked(); CHECK_EQ(Smi::FromInt(1), array->length()); CHECK_EQ(array->GetElement(0), *name); // Set array length with larger than smi value. Handle<Object> length = FACTORY->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1); - ok = array->SetElementsLength(*length)->ToObjectChecked(); + array->SetElementsLength(*length)->ToObjectChecked(); uint32_t int_length = 0; CHECK(length->ToArrayIndex(&int_length)); @@ -690,8 +691,7 @@ TEST(JSArray) { CHECK(array->HasDictionaryElements()); // Must be in slow mode. // array[length] = name. - ok = array->SetElement( - int_length, *name, kNonStrictMode, true)->ToObjectChecked(); + array->SetElement(int_length, *name, kNonStrictMode, true)->ToObjectChecked(); uint32_t new_int_length = 0; CHECK(array->length()->ToArrayIndex(&new_int_length)); CHECK_EQ(static_cast<double>(int_length), new_int_length - 1); @@ -718,10 +718,8 @@ TEST(JSObjectCopy) { obj->SetProperty( *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); - Object* ok = - obj->SetElement(0, *first, kNonStrictMode, true)->ToObjectChecked(); - - ok = obj->SetElement(1, *second, kNonStrictMode, true)->ToObjectChecked(); + obj->SetElement(0, *first, kNonStrictMode, true)->ToObjectChecked(); + obj->SetElement(1, *second, kNonStrictMode, true)->ToObjectChecked(); // Make the clone. Handle<JSObject> clone = Copy(obj); @@ -739,8 +737,8 @@ TEST(JSObjectCopy) { clone->SetProperty( *second, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); - ok = clone->SetElement(0, *second, kNonStrictMode, true)->ToObjectChecked(); - ok = clone->SetElement(1, *first, kNonStrictMode, true)->ToObjectChecked(); + clone->SetElement(0, *second, kNonStrictMode, true)->ToObjectChecked(); + clone->SetElement(1, *first, kNonStrictMode, true)->ToObjectChecked(); CHECK_EQ(obj->GetElement(1), clone->GetElement(0)); CHECK_EQ(obj->GetElement(0), clone->GetElement(1)); @@ -813,7 +811,7 @@ TEST(Iteration) { // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE objs[next_objs_index++] = FACTORY->NewJSArray(10); - objs[next_objs_index++] = FACTORY->NewJSArray(10, TENURED); + objs[next_objs_index++] = FACTORY->NewJSArray(10, FAST_ELEMENTS, TENURED); // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE objs[next_objs_index++] = @@ -838,49 +836,6 @@ TEST(Iteration) { } -TEST(LargeObjectSpaceContains) { - InitializeVM(); - - HEAP->CollectGarbage(NEW_SPACE); - - Address current_top = HEAP->new_space()->top(); - Page* page = Page::FromAddress(current_top); - Address current_page = page->address(); - Address next_page = current_page + Page::kPageSize; - int bytes_to_page = static_cast<int>(next_page - current_top); - if (bytes_to_page <= FixedArray::kHeaderSize) { - // Alas, need to cross another page to be able to - // put desired value. - next_page += Page::kPageSize; - bytes_to_page = static_cast<int>(next_page - current_top); - } - CHECK(bytes_to_page > FixedArray::kHeaderSize); - - intptr_t* flags_ptr = &Page::FromAddress(next_page)->flags_; - Address flags_addr = reinterpret_cast<Address>(flags_ptr); - - int bytes_to_allocate = - static_cast<int>(flags_addr - current_top) + kPointerSize; - - int n_elements = (bytes_to_allocate - FixedArray::kHeaderSize) / - kPointerSize; - CHECK_EQ(bytes_to_allocate, FixedArray::SizeFor(n_elements)); - FixedArray* array = FixedArray::cast( - HEAP->AllocateFixedArray(n_elements)->ToObjectChecked()); - - int index = n_elements - 1; - CHECK_EQ(flags_ptr, - HeapObject::RawField(array, FixedArray::OffsetOfElementAt(index))); - array->set(index, Smi::FromInt(0)); - // This chould have turned next page into LargeObjectPage: - // CHECK(Page::FromAddress(next_page)->IsLargeObjectPage()); - - HeapObject* addr = HeapObject::FromAddress(next_page + 2 * kPointerSize); - CHECK(HEAP->new_space()->Contains(addr)); - CHECK(!HEAP->lo_space()->Contains(addr)); -} - - TEST(EmptyHandleEscapeFrom) { InitializeVM(); @@ -907,8 +862,7 @@ TEST(Regression39128) { InitializeVM(); // Increase the chance of 'bump-the-pointer' allocation in old space. - bool force_compaction = true; - HEAP->CollectAllGarbage(force_compaction); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); v8::HandleScope scope; @@ -975,12 +929,6 @@ TEST(Regression39128) { return; } CHECK(HEAP->old_pointer_space()->Contains(clone->address())); - - // Step 5: verify validity of region dirty marks. - Address clone_addr = clone->address(); - Page* page = Page::FromAddress(clone_addr); - // Check that region covering inobject property 1 is marked dirty. - CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize))); } @@ -1010,17 +958,18 @@ TEST(TestCodeFlushing) { Handle<JSFunction> function(JSFunction::cast(func_value)); CHECK(function->shared()->is_compiled()); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); + // TODO(1609) Currently incremental marker does not support code flushing. + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); CHECK(function->shared()->is_compiled()); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask); // foo should no longer be in the compilation cache CHECK(!function->shared()->is_compiled() || function->IsOptimized()); @@ -1109,7 +1058,7 @@ TEST(TestInternalWeakLists) { } // Mark compact handles the weak references. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); // Get rid of f3 and f5 in the same way. @@ -1118,21 +1067,21 @@ TEST(TestInternalWeakLists) { HEAP->PerformScavenge(); CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); } - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); CompileRun("f5=null"); for (int j = 0; j < 10; j++) { HEAP->PerformScavenge(); CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); } - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i])); ctx[i]->Exit(); } // Force compilation cache cleanup. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); // Dispose the global contexts one by one. for (int i = 0; i < kNumTestContexts; i++) { @@ -1146,7 +1095,7 @@ TEST(TestInternalWeakLists) { } // Mark compact handles the weak references. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK_EQ(kNumTestContexts - i - 1, CountGlobalContexts()); } @@ -1161,7 +1110,7 @@ static int CountGlobalContextsWithGC(int n) { Handle<Object> object(HEAP->global_contexts_list()); while (!object->IsUndefined()) { count++; - if (count == n) HEAP->CollectAllGarbage(true); + if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags); object = Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK)); } @@ -1180,7 +1129,7 @@ static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context, while (object->IsJSFunction() && !Handle<JSFunction>::cast(object)->IsBuiltin()) { count++; - if (count == n) HEAP->CollectAllGarbage(true); + if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags); object = Handle<Object>( Object::cast(JSFunction::cast(*object)->next_function_link())); } @@ -1238,92 +1187,442 @@ TEST(TestInternalWeakListsTraverseWithGC) { } +TEST(TestSizeOfObjects) { + v8::V8::Initialize(); + + // Get initial heap size after several full GCs, which will stabilize + // the heap size and return with sweeping finished completely. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(HEAP->old_pointer_space()->IsSweepingComplete()); + int initial_size = static_cast<int>(HEAP->SizeOfObjects()); + + { + // Allocate objects on several different old-space pages so that + // lazy sweeping kicks in for subsequent GC runs. + AlwaysAllocateScope always_allocate; + int filler_size = static_cast<int>(FixedArray::SizeFor(8192)); + for (int i = 1; i <= 100; i++) { + HEAP->AllocateFixedArray(8192, TENURED)->ToObjectChecked(); + CHECK_EQ(initial_size + i * filler_size, + static_cast<int>(HEAP->SizeOfObjects())); + } + } + + // The heap size should go back to initial size after a full GC, even + // though sweeping didn't finish yet. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(!HEAP->old_pointer_space()->IsSweepingComplete()); + CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects())); + + // Advancing the sweeper step-wise should not change the heap size. + while (!HEAP->old_pointer_space()->IsSweepingComplete()) { + HEAP->old_pointer_space()->AdvanceSweeper(KB); + CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects())); + } +} + + TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { InitializeVM(); + HEAP->EnsureHeapIsIterable(); intptr_t size_of_objects_1 = HEAP->SizeOfObjects(); - HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapIterator iterator; intptr_t size_of_objects_2 = 0; for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { size_of_objects_2 += obj->Size(); } - // Delta must be within 1% of the larger result. + // Delta must be within 5% of the larger result. + // TODO(gc): Tighten this up by distinguishing between byte + // arrays that are real and those that merely mark free space + // on the heap. if (size_of_objects_1 > size_of_objects_2) { intptr_t delta = size_of_objects_1 - size_of_objects_2; PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " "Iterator: %" V8_PTR_PREFIX "d, " "delta: %" V8_PTR_PREFIX "d\n", size_of_objects_1, size_of_objects_2, delta); - CHECK_GT(size_of_objects_1 / 100, delta); + CHECK_GT(size_of_objects_1 / 20, delta); } else { intptr_t delta = size_of_objects_2 - size_of_objects_1; PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " "Iterator: %" V8_PTR_PREFIX "d, " "delta: %" V8_PTR_PREFIX "d\n", size_of_objects_1, size_of_objects_2, delta); - CHECK_GT(size_of_objects_2 / 100, delta); + CHECK_GT(size_of_objects_2 / 20, delta); } } -class HeapIteratorTestHelper { - public: - HeapIteratorTestHelper(Object* a, Object* b) - : a_(a), b_(b), a_found_(false), b_found_(false) {} - bool a_found() { return a_found_; } - bool b_found() { return b_found_; } - void IterateHeap(HeapIterator::HeapObjectsFiltering mode) { - HeapIterator iterator(mode); - for (HeapObject* obj = iterator.next(); - obj != NULL; - obj = iterator.next()) { - if (obj == a_) - a_found_ = true; - else if (obj == b_) - b_found_ = true; - } +static void FillUpNewSpace(NewSpace* new_space) { + // Fill up new space to the point that it is completely full. Make sure + // that the scavenger does not undo the filling. + v8::HandleScope scope; + AlwaysAllocateScope always_allocate; + intptr_t available = new_space->EffectiveCapacity() - new_space->Size(); + intptr_t number_of_fillers = (available / FixedArray::SizeFor(1000)) - 10; + for (intptr_t i = 0; i < number_of_fillers; i++) { + CHECK(HEAP->InNewSpace(*FACTORY->NewFixedArray(1000, NOT_TENURED))); } - private: - Object* a_; - Object* b_; - bool a_found_; - bool b_found_; -}; +} + + +TEST(GrowAndShrinkNewSpace) { + InitializeVM(); + NewSpace* new_space = HEAP->new_space(); + + // Explicitly growing should double the space capacity. + intptr_t old_capacity, new_capacity; + old_capacity = new_space->Capacity(); + new_space->Grow(); + new_capacity = new_space->Capacity(); + CHECK(2 * old_capacity == new_capacity); + + old_capacity = new_space->Capacity(); + FillUpNewSpace(new_space); + new_capacity = new_space->Capacity(); + CHECK(old_capacity == new_capacity); + + // Explicitly shrinking should not affect space capacity. + old_capacity = new_space->Capacity(); + new_space->Shrink(); + new_capacity = new_space->Capacity(); + CHECK(old_capacity == new_capacity); + + // Let the scavenger empty the new space. + HEAP->CollectGarbage(NEW_SPACE); + CHECK_LE(new_space->Size(), old_capacity); + + // Explicitly shrinking should halve the space capacity. + old_capacity = new_space->Capacity(); + new_space->Shrink(); + new_capacity = new_space->Capacity(); + CHECK(old_capacity == 2 * new_capacity); + + // Consecutive shrinking should not affect space capacity. + old_capacity = new_space->Capacity(); + new_space->Shrink(); + new_space->Shrink(); + new_space->Shrink(); + new_capacity = new_space->Capacity(); + CHECK(old_capacity == new_capacity); +} + -TEST(HeapIteratorFilterUnreachable) { +TEST(CollectingAllAvailableGarbageShrinksNewSpace) { InitializeVM(); v8::HandleScope scope; - CompileRun("a = {}; b = {};"); - v8::Handle<Object> a(ISOLATE->context()->global()->GetProperty( - *FACTORY->LookupAsciiSymbol("a"))->ToObjectChecked()); - v8::Handle<Object> b(ISOLATE->context()->global()->GetProperty( - *FACTORY->LookupAsciiSymbol("b"))->ToObjectChecked()); - CHECK_NE(*a, *b); + NewSpace* new_space = HEAP->new_space(); + intptr_t old_capacity, new_capacity; + old_capacity = new_space->Capacity(); + new_space->Grow(); + new_capacity = new_space->Capacity(); + CHECK(2 * old_capacity == new_capacity); + FillUpNewSpace(new_space); + HEAP->CollectAllAvailableGarbage(); + new_capacity = new_space->Capacity(); + CHECK(old_capacity == new_capacity); +} + +// This just checks the contract of the IdleNotification() function, +// and does not verify that it does reasonable work. +TEST(IdleNotificationAdvancesIncrementalMarking) { + if (!FLAG_incremental_marking || !FLAG_incremental_marking_steps) return; + InitializeVM(); + v8::HandleScope scope; + const char* source = "function binom(n, m) {" + " var C = [[1]];" + " for (var i = 1; i <= n; ++i) {" + " C[i] = [1];" + " for (var j = 1; j < i; ++j) {" + " C[i][j] = C[i-1][j-1] + C[i-1][j];" + " }" + " C[i][i] = 1;" + " }" + " return C[n][m];" + "};" + "binom(1000, 500)"; { - HeapIteratorTestHelper helper(*a, *b); - helper.IterateHeap(HeapIterator::kFilterUnreachable); - CHECK(helper.a_found()); - CHECK(helper.b_found()); + AlwaysAllocateScope aa_scope; + CompileRun(source); } - CHECK(ISOLATE->context()->global()->DeleteProperty( - *FACTORY->LookupAsciiSymbol("a"), JSObject::FORCE_DELETION)); - // We ensure that GC will not happen, so our raw pointer stays valid. - AssertNoAllocation no_alloc; - Object* a_saved = *a; - a.Clear(); - // Verify that "a" object still resides in the heap... + intptr_t old_size = HEAP->SizeOfObjects(); + bool no_idle_work = v8::V8::IdleNotification(900); + while (!v8::V8::IdleNotification(900)) ; + intptr_t new_size = HEAP->SizeOfObjects(); + CHECK(no_idle_work || new_size < old_size); +} + + +static int NumberOfGlobalObjects() { + int count = 0; + HeapIterator iterator; + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { + if (obj->IsGlobalObject()) count++; + } + return count; +} + + +// Test that we don't embed maps from foreign contexts into +// optimized code. +TEST(LeakGlobalContextViaMap) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope outer_scope; + v8::Persistent<v8::Context> ctx1 = v8::Context::New(); + v8::Persistent<v8::Context> ctx2 = v8::Context::New(); + ctx1->Enter(); + + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(4, NumberOfGlobalObjects()); + { - HeapIteratorTestHelper helper(a_saved, *b); - helper.IterateHeap(HeapIterator::kNoFiltering); - CHECK(helper.a_found()); - CHECK(helper.b_found()); + v8::HandleScope inner_scope; + CompileRun("var v = {x: 42}"); + v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); + ctx2->Enter(); + ctx2->Global()->Set(v8_str("o"), v); + v8::Local<v8::Value> res = CompileRun( + "function f() { return o.x; }" + "for (var i = 0; i < 10; ++i) f();" + "%OptimizeFunctionOnNextCall(f);" + "f();"); + CHECK_EQ(42, res->Int32Value()); + ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); + ctx2->Exit(); + ctx1->Exit(); + ctx1.Dispose(); } - // ...but is now unreachable. + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(2, NumberOfGlobalObjects()); + ctx2.Dispose(); + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(0, NumberOfGlobalObjects()); +} + + +// Test that we don't embed functions from foreign contexts into +// optimized code. +TEST(LeakGlobalContextViaFunction) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope outer_scope; + v8::Persistent<v8::Context> ctx1 = v8::Context::New(); + v8::Persistent<v8::Context> ctx2 = v8::Context::New(); + ctx1->Enter(); + + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(4, NumberOfGlobalObjects()); + { - HeapIteratorTestHelper helper(a_saved, *b); - helper.IterateHeap(HeapIterator::kFilterUnreachable); - CHECK(!helper.a_found()); - CHECK(helper.b_found()); + v8::HandleScope inner_scope; + CompileRun("var v = function() { return 42; }"); + v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); + ctx2->Enter(); + ctx2->Global()->Set(v8_str("o"), v); + v8::Local<v8::Value> res = CompileRun( + "function f(x) { return x(); }" + "for (var i = 0; i < 10; ++i) f(o);" + "%OptimizeFunctionOnNextCall(f);" + "f(o);"); + CHECK_EQ(42, res->Int32Value()); + ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); + ctx2->Exit(); + ctx1->Exit(); + ctx1.Dispose(); + } + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(2, NumberOfGlobalObjects()); + ctx2.Dispose(); + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(0, NumberOfGlobalObjects()); +} + + +TEST(LeakGlobalContextViaMapKeyed) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope outer_scope; + v8::Persistent<v8::Context> ctx1 = v8::Context::New(); + v8::Persistent<v8::Context> ctx2 = v8::Context::New(); + ctx1->Enter(); + + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(4, NumberOfGlobalObjects()); + + { + v8::HandleScope inner_scope; + CompileRun("var v = [42, 43]"); + v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); + ctx2->Enter(); + ctx2->Global()->Set(v8_str("o"), v); + v8::Local<v8::Value> res = CompileRun( + "function f() { return o[0]; }" + "for (var i = 0; i < 10; ++i) f();" + "%OptimizeFunctionOnNextCall(f);" + "f();"); + CHECK_EQ(42, res->Int32Value()); + ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); + ctx2->Exit(); + ctx1->Exit(); + ctx1.Dispose(); } + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(2, NumberOfGlobalObjects()); + ctx2.Dispose(); + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(0, NumberOfGlobalObjects()); +} + + +TEST(LeakGlobalContextViaMapProto) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope outer_scope; + v8::Persistent<v8::Context> ctx1 = v8::Context::New(); + v8::Persistent<v8::Context> ctx2 = v8::Context::New(); + ctx1->Enter(); + + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(4, NumberOfGlobalObjects()); + + { + v8::HandleScope inner_scope; + CompileRun("var v = { y: 42}"); + v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); + ctx2->Enter(); + ctx2->Global()->Set(v8_str("o"), v); + v8::Local<v8::Value> res = CompileRun( + "function f() {" + " var p = {x: 42};" + " p.__proto__ = o;" + " return p.x;" + "}" + "for (var i = 0; i < 10; ++i) f();" + "%OptimizeFunctionOnNextCall(f);" + "f();"); + CHECK_EQ(42, res->Int32Value()); + ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); + ctx2->Exit(); + ctx1->Exit(); + ctx1.Dispose(); + } + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(2, NumberOfGlobalObjects()); + ctx2.Dispose(); + HEAP->CollectAllAvailableGarbage(); + CHECK_EQ(0, NumberOfGlobalObjects()); +} + + +TEST(InstanceOfStubWriteBarrier) { + i::FLAG_allow_natives_syntax = true; +#ifdef DEBUG + i::FLAG_verify_heap = true; +#endif + InitializeVM(); + if (!i::V8::UseCrankshaft()) return; + v8::HandleScope outer_scope; + + { + v8::HandleScope scope; + CompileRun( + "function foo () { }" + "function mkbar () { return new (new Function(\"\")) (); }" + "function f (x) { return (x instanceof foo); }" + "function g () { f(mkbar()); }" + "f(new foo()); f(new foo());" + "%OptimizeFunctionOnNextCall(f);" + "f(new foo()); g();"); + } + + IncrementalMarking* marking = HEAP->incremental_marking(); + marking->Abort(); + marking->Start(); + + Handle<JSFunction> f = + v8::Utils::OpenHandle( + *v8::Handle<v8::Function>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); + + CHECK(f->IsOptimized()); + + while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) && + !marking->IsStopped()) { + marking->Step(MB); + } + + CHECK(marking->IsMarking()); + + // Discard any pending GC requests otherwise we will get GC when we enter + // code below. + if (ISOLATE->stack_guard()->IsGCRequest()) { + ISOLATE->stack_guard()->Continue(GC_REQUEST); + } + + { + v8::HandleScope scope; + v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global(); + v8::Handle<v8::Function> g = + v8::Handle<v8::Function>::Cast(global->Get(v8_str("g"))); + g->Call(global, 0, NULL); + } + + HEAP->incremental_marking()->set_should_hurry(true); + HEAP->CollectGarbage(OLD_POINTER_SPACE); +} + + +TEST(PrototypeTransitionClearing) { + InitializeVM(); + v8::HandleScope scope; + + CompileRun( + "var base = {};" + "var live = [];" + "for (var i = 0; i < 10; i++) {" + " var object = {};" + " var prototype = {};" + " object.__proto__ = prototype;" + " if (i >= 3) live.push(object, prototype);" + "}"); + + Handle<JSObject> baseObject = + v8::Utils::OpenHandle( + *v8::Handle<v8::Object>::Cast( + v8::Context::GetCurrent()->Global()->Get(v8_str("base")))); + + // Verify that only dead prototype transitions are cleared. + CHECK_EQ(10, baseObject->map()->NumberOfProtoTransitions()); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK_EQ(10 - 3, baseObject->map()->NumberOfProtoTransitions()); + + // Verify that prototype transitions array was compacted. + FixedArray* trans = baseObject->map()->prototype_transitions(); + for (int i = 0; i < 10 - 3; i++) { + int j = Map::kProtoTransitionHeaderSize + + i * Map::kProtoTransitionElementsPerEntry; + CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap()); + CHECK(trans->get(j + Map::kProtoTransitionPrototypeOffset)->IsJSObject()); + } + + // Make sure next prototype is placed on an old-space evacuation candidate. + Handle<JSObject> prototype; + PagedSpace* space = HEAP->old_pointer_space(); + do { + prototype = FACTORY->NewJSArray(32 * KB, FAST_ELEMENTS, TENURED); + } while (space->FirstPage() == space->LastPage() || + !space->LastPage()->Contains(prototype->address())); + + // Add a prototype on an evacuation candidate and verify that transition + // clearing correctly records slots in prototype transition array. + i::FLAG_always_compact = true; + Handle<Map> map(baseObject->map()); + CHECK(!space->LastPage()->Contains(map->prototype_transitions()->address())); + CHECK(space->LastPage()->Contains(prototype->address())); + baseObject->SetPrototype(*prototype, false)->ToObjectChecked(); + CHECK(map->GetPrototypeTransition(*prototype)->IsMap()); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(map->GetPrototypeTransition(*prototype)->IsMap()); } diff --git a/deps/v8/test/cctest/test-lockers.cc b/deps/v8/test/cctest/test-lockers.cc index 7360da52b1..5035f87642 100644 --- a/deps/v8/test/cctest/test-lockers.cc +++ b/deps/v8/test/cctest/test-lockers.cc @@ -204,7 +204,11 @@ static void StartJoinAndDeleteThreads(const i::List<JoinableThread*>& threads) { // Run many threads all locking on the same isolate TEST(IsolateLockingStress) { +#ifdef V8_TARGET_ARCH_MIPS + const int kNThreads = 50; +#else const int kNThreads = 100; +#endif i::List<JoinableThread*> threads(kNThreads); v8::Isolate* isolate = v8::Isolate::New(); for (int i = 0; i < kNThreads; i++) { @@ -237,7 +241,7 @@ class IsolateNonlockingThread : public JoinableThread { // Run many threads each accessing its own isolate without locking TEST(MultithreadedParallelIsolates) { -#ifdef V8_TARGET_ARCH_ARM +#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) const int kNThreads = 10; #else const int kNThreads = 50; @@ -275,7 +279,11 @@ class IsolateNestedLockingThread : public JoinableThread { // Run many threads with nested locks TEST(IsolateNestedLocking) { +#ifdef V8_TARGET_ARCH_MIPS + const int kNThreads = 50; +#else const int kNThreads = 100; +#endif v8::Isolate* isolate = v8::Isolate::New(); i::List<JoinableThread*> threads(kNThreads); for (int i = 0; i < kNThreads; i++) { @@ -311,7 +319,7 @@ class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread { // Run parallel threads that lock and access different isolates in parallel TEST(SeparateIsolatesLocksNonexclusive) { -#ifdef V8_TARGET_ARCH_ARM +#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) const int kNThreads = 50; #else const int kNThreads = 100; @@ -385,7 +393,7 @@ class LockerUnlockerThread : public JoinableThread { // Use unlocker inside of a Locker, multiple threads. TEST(LockerUnlocker) { -#ifdef V8_TARGET_ARCH_ARM +#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) const int kNThreads = 50; #else const int kNThreads = 100; @@ -438,7 +446,7 @@ class LockTwiceAndUnlockThread : public JoinableThread { // Use Unlocker inside two Lockers. TEST(LockTwiceAndUnlock) { -#ifdef V8_TARGET_ARCH_ARM +#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) const int kNThreads = 50; #else const int kNThreads = 100; @@ -559,7 +567,11 @@ class LockUnlockLockThread : public JoinableThread { // Locker inside an Unlocker inside a Locker. TEST(LockUnlockLockMultithreaded) { +#ifdef V8_TARGET_ARCH_MIPS + const int kNThreads = 50; +#else const int kNThreads = 100; +#endif v8::Isolate* isolate = v8::Isolate::New(); Persistent<v8::Context> context; { @@ -606,7 +618,11 @@ class LockUnlockLockDefaultIsolateThread : public JoinableThread { // Locker inside an Unlocker inside a Locker for default isolate. TEST(LockUnlockLockDefaultIsolateMultithreaded) { +#ifdef V8_TARGET_ARCH_MIPS + const int kNThreads = 50; +#else const int kNThreads = 100; +#endif Persistent<v8::Context> context; { v8::Locker locker_; @@ -639,3 +655,68 @@ TEST(Regress1433) { isolate->Dispose(); } } + + +static const char* kSimpleExtensionSource = + "(function Foo() {" + " return 4;" + "})() "; + +class IsolateGenesisThread : public JoinableThread { + public: + IsolateGenesisThread(int count, const char* extension_names[]) + : JoinableThread("IsolateGenesisThread"), + count_(count), + extension_names_(extension_names) + {} + + virtual void Run() { + v8::Isolate* isolate = v8::Isolate::New(); + { + v8::Isolate::Scope isolate_scope(isolate); + CHECK(!i::Isolate::Current()->has_installed_extensions()); + v8::ExtensionConfiguration extensions(count_, extension_names_); + v8::Persistent<v8::Context> context = v8::Context::New(&extensions); + CHECK(i::Isolate::Current()->has_installed_extensions()); + context.Dispose(); + } + isolate->Dispose(); + } + private: + int count_; + const char** extension_names_; +}; + +// Test installing extensions in separate isolates concurrently. +// http://code.google.com/p/v8/issues/detail?id=1821 +TEST(ExtensionsRegistration) { +#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) + const int kNThreads = 10; +#else + const int kNThreads = 40; +#endif + v8::RegisterExtension(new v8::Extension("test0", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test1", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test2", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test3", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test4", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test5", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test6", + kSimpleExtensionSource)); + v8::RegisterExtension(new v8::Extension("test7", + kSimpleExtensionSource)); + const char* extension_names[] = { "test0", "test1", + "test2", "test3", "test4", + "test5", "test6", "test7" }; + i::List<JoinableThread*> threads(kNThreads); + for (int i = 0; i < kNThreads; i++) { + threads.Add(new IsolateGenesisThread(8, extension_names)); + } + StartJoinAndDeleteThreads(threads); +} diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 72e663c462..6f2324dbb8 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -494,7 +494,7 @@ TEST(EquivalenceOfLoggingAndTraversal) { " (function a(j) { return function b() { return j; } })(100);\n" "})(this);"); v8::V8::PauseProfiler(); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); LOGGER->StringEvent("test-logging-done", ""); // Iterate heap to find compiled functions, will write to log. diff --git a/deps/v8/test/cctest/test-mark-compact.cc b/deps/v8/test/cctest/test-mark-compact.cc index dcb51a0bcb..2535f10c03 100644 --- a/deps/v8/test/cctest/test-mark-compact.cc +++ b/deps/v8/test/cctest/test-mark-compact.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,6 +27,14 @@ #include <stdlib.h> +#ifdef __linux__ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#endif + #include "v8.h" #include "global-handles.h" @@ -44,21 +52,21 @@ static void InitializeVM() { } -TEST(MarkingStack) { +TEST(MarkingDeque) { int mem_size = 20 * kPointerSize; byte* mem = NewArray<byte>(20*kPointerSize); Address low = reinterpret_cast<Address>(mem); Address high = low + mem_size; - MarkingStack s; + MarkingDeque s; s.Initialize(low, high); Address address = NULL; - while (!s.is_full()) { - s.Push(HeapObject::FromAddress(address)); + while (!s.IsFull()) { + s.PushBlack(HeapObject::FromAddress(address)); address += kPointerSize; } - while (!s.is_empty()) { + while (!s.IsEmpty()) { Address value = s.Pop()->address(); address -= kPointerSize; CHECK_EQ(address, value); @@ -78,7 +86,7 @@ TEST(Promotion) { // from new space. FLAG_gc_global = true; FLAG_always_compact = true; - HEAP->ConfigureHeap(2*256*KB, 4*MB, 4*MB); + HEAP->ConfigureHeap(2*256*KB, 8*MB, 8*MB); InitializeVM(); @@ -104,7 +112,7 @@ TEST(Promotion) { TEST(NoPromotion) { - HEAP->ConfigureHeap(2*256*KB, 4*MB, 4*MB); + HEAP->ConfigureHeap(2*256*KB, 8*MB, 8*MB); // Test the situation that some objects in new space are promoted to // the old space @@ -116,9 +124,12 @@ TEST(NoPromotion) { HEAP->CollectGarbage(OLD_POINTER_SPACE); // Allocate a big Fixed array in the new space. - int size = (HEAP->MaxObjectSizeInPagedSpace() - FixedArray::kHeaderSize) / - kPointerSize; - Object* obj = HEAP->AllocateFixedArray(size)->ToObjectChecked(); + int max_size = + Min(HEAP->MaxObjectSizeInPagedSpace(), HEAP->MaxObjectSizeInNewSpace()); + + int length = (max_size - FixedArray::kHeaderSize) / (2*kPointerSize); + Object* obj = i::Isolate::Current()->heap()->AllocateFixedArray(length)-> + ToObjectChecked(); Handle<FixedArray> array(FixedArray::cast(obj)); @@ -139,9 +150,6 @@ TEST(NoPromotion) { // Call mark compact GC, and it should pass. HEAP->CollectGarbage(OLD_POINTER_SPACE); - - // array should not be promoted because the old space is full. - CHECK(HEAP->InSpace(*array, NEW_SPACE)); } @@ -228,6 +236,8 @@ TEST(MarkCompactCollector) { } +// TODO(1600): compaction of map space is temporary removed from GC. +#if 0 static Handle<Map> CreateMap() { return FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); } @@ -252,11 +262,11 @@ TEST(MapCompact) { // be able to trigger map compaction. // To give an additional chance to fail, try to force compaction which // should be impossible right now. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kForceCompactionMask); // And now map pointers should be encodable again. CHECK(HEAP->map_space()->MapPointersEncodable()); } - +#endif static int gc_starts = 0; static int gc_ends = 0; @@ -442,3 +452,100 @@ TEST(EmptyObjectGroups) { global_handles->AddImplicitReferences( Handle<HeapObject>::cast(object).location(), NULL, 0); } + + +// Here is a memory use test that uses /proc, and is therefore Linux-only. We +// do not care how much memory the simulator uses, since it is only there for +// debugging purposes. +#if defined(__linux__) && !defined(USE_SIMULATOR) + + +static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) { + char* end_address = buffer + *position; + uintptr_t result = strtoul(buffer + *position, &end_address, base); + CHECK(result != ULONG_MAX || errno != ERANGE); + CHECK(end_address > buffer + *position); + *position = end_address - buffer; + return result; +} + + +static intptr_t MemoryInUse() { + intptr_t memory_use = 0; + + int fd = open("/proc/self/maps", O_RDONLY); + if (fd < 0) return -1; + + const int kBufSize = 10000; + char buffer[kBufSize]; + int length = read(fd, buffer, kBufSize); + intptr_t line_start = 0; + CHECK_LT(length, kBufSize); // Make the buffer bigger. + CHECK_GT(length, 0); // We have to find some data in the file. + while (line_start < length) { + if (buffer[line_start] == '\n') { + line_start++; + continue; + } + intptr_t position = line_start; + uintptr_t start = ReadLong(buffer, &position, 16); + CHECK_EQ(buffer[position++], '-'); + uintptr_t end = ReadLong(buffer, &position, 16); + CHECK_EQ(buffer[position++], ' '); + CHECK(buffer[position] == '-' || buffer[position] == 'r'); + bool read_permission = (buffer[position++] == 'r'); + CHECK(buffer[position] == '-' || buffer[position] == 'w'); + bool write_permission = (buffer[position++] == 'w'); + CHECK(buffer[position] == '-' || buffer[position] == 'x'); + bool execute_permission = (buffer[position++] == 'x'); + CHECK(buffer[position] == '-' || buffer[position] == 'p'); + bool private_mapping = (buffer[position++] == 'p'); + CHECK_EQ(buffer[position++], ' '); + uintptr_t offset = ReadLong(buffer, &position, 16); + USE(offset); + CHECK_EQ(buffer[position++], ' '); + uintptr_t major = ReadLong(buffer, &position, 16); + USE(major); + CHECK_EQ(buffer[position++], ':'); + uintptr_t minor = ReadLong(buffer, &position, 16); + USE(minor); + CHECK_EQ(buffer[position++], ' '); + uintptr_t inode = ReadLong(buffer, &position, 10); + while (position < length && buffer[position] != '\n') position++; + if ((read_permission || write_permission || execute_permission) && + private_mapping && inode == 0) { + memory_use += (end - start); + } + + line_start = position; + } + close(fd); + return memory_use; +} + + +TEST(BootUpMemoryUse) { + intptr_t initial_memory = MemoryInUse(); + FLAG_crankshaft = false; // Avoid flakiness. + // Only Linux has the proc filesystem and only if it is mapped. If it's not + // there we just skip the test. + if (initial_memory >= 0) { + InitializeVM(); + intptr_t booted_memory = MemoryInUse(); + if (sizeof(initial_memory) == 8) { + if (v8::internal::Snapshot::IsEnabled()) { + CHECK_LE(booted_memory - initial_memory, 6686 * 1024); // 6476. + } else { + CHECK_LE(booted_memory - initial_memory, 6809 * 1024); // 6628. + } + } else { + if (v8::internal::Snapshot::IsEnabled()) { + CHECK_LE(booted_memory - initial_memory, 6532 * 1024); // 6388. + } else { + CHECK_LE(booted_memory - initial_memory, 6686 * 1024); // 6456 + } + } + } +} + +#endif // __linux__ and !USE_SIMULATOR diff --git a/deps/v8/test/cctest/test-parsing.cc b/deps/v8/test/cctest/test-parsing.cc index d98b6753d5..cd8a6aff38 100755 --- a/deps/v8/test/cctest/test-parsing.cc +++ b/deps/v8/test/cctest/test-parsing.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -32,6 +32,7 @@ #include "v8.h" #include "cctest.h" +#include "compiler.h" #include "execution.h" #include "isolate.h" #include "parser.h" @@ -63,9 +64,10 @@ TEST(ScanKeywords) { CHECK(static_cast<int>(sizeof(buffer)) >= length); { i::Utf8ToUC16CharacterStream stream(keyword, length); - i::JavaScriptScanner scanner(&unicode_cache); - // The scanner should parse 'let' as Token::LET for this test. - scanner.SetHarmonyBlockScoping(true); + i::Scanner scanner(&unicode_cache); + // The scanner should parse Harmony keywords for this test. + scanner.SetHarmonyScoping(true); + scanner.SetHarmonyModules(true); scanner.Initialize(&stream); CHECK_EQ(key_token.token, scanner.Next()); CHECK_EQ(i::Token::EOS, scanner.Next()); @@ -73,7 +75,7 @@ TEST(ScanKeywords) { // Removing characters will make keyword matching fail. { i::Utf8ToUC16CharacterStream stream(keyword, length - 1); - i::JavaScriptScanner scanner(&unicode_cache); + i::Scanner scanner(&unicode_cache); scanner.Initialize(&stream); CHECK_EQ(i::Token::IDENTIFIER, scanner.Next()); CHECK_EQ(i::Token::EOS, scanner.Next()); @@ -84,7 +86,7 @@ TEST(ScanKeywords) { memmove(buffer, keyword, length); buffer[length] = chars_to_append[j]; i::Utf8ToUC16CharacterStream stream(buffer, length + 1); - i::JavaScriptScanner scanner(&unicode_cache); + i::Scanner scanner(&unicode_cache); scanner.Initialize(&stream); CHECK_EQ(i::Token::IDENTIFIER, scanner.Next()); CHECK_EQ(i::Token::EOS, scanner.Next()); @@ -94,7 +96,7 @@ TEST(ScanKeywords) { memmove(buffer, keyword, length); buffer[length - 1] = '_'; i::Utf8ToUC16CharacterStream stream(buffer, length); - i::JavaScriptScanner scanner(&unicode_cache); + i::Scanner scanner(&unicode_cache); scanner.Initialize(&stream); CHECK_EQ(i::Token::IDENTIFIER, scanner.Next()); CHECK_EQ(i::Token::EOS, scanner.Next()); @@ -229,7 +231,7 @@ TEST(Preparsing) { CHECK_EQ(11, error_location.end_pos); // Should not crash. const char* message = pre_impl->BuildMessage(); - i::Vector<const char*> args = pre_impl->BuildArgs(); + pre_impl->BuildArgs(); CHECK_GT(strlen(message), 0); } @@ -257,13 +259,14 @@ TEST(StandAlonePreParser) { reinterpret_cast<const i::byte*>(program), static_cast<unsigned>(strlen(program))); i::CompleteParserRecorder log; - i::JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache()); + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); scanner.Initialize(&stream); + int flags = i::kAllowLazy | i::kAllowNativesSyntax; v8::preparser::PreParser::PreParseResult result = v8::preparser::PreParser::PreParseProgram(&scanner, &log, - true, + flags, stack_limit); CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result); i::ScriptDataImpl data(log.ExtractData()); @@ -272,6 +275,43 @@ TEST(StandAlonePreParser) { } +TEST(StandAlonePreParserNoNatives) { + v8::V8::Initialize(); + + int marker; + i::Isolate::Current()->stack_guard()->SetStackLimit( + reinterpret_cast<uintptr_t>(&marker) - 128 * 1024); + + const char* programs[] = { + "%ArgleBargle(glop);", + "var x = %_IsSmi(42);", + NULL + }; + + uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit(); + for (int i = 0; programs[i]; i++) { + const char* program = programs[i]; + i::Utf8ToUC16CharacterStream stream( + reinterpret_cast<const i::byte*>(program), + static_cast<unsigned>(strlen(program))); + i::CompleteParserRecorder log; + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); + scanner.Initialize(&stream); + + // Flags don't allow natives syntax. + v8::preparser::PreParser::PreParseResult result = + v8::preparser::PreParser::PreParseProgram(&scanner, + &log, + i::kAllowLazy, + stack_limit); + CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result); + i::ScriptDataImpl data(log.ExtractData()); + // Data contains syntax error. + CHECK(data.has_error()); + } +} + + TEST(RegressChromium62639) { v8::V8::Initialize(); @@ -310,10 +350,10 @@ TEST(Regress928) { "try { } catch (e) { var foo = function () { /* first */ } }" "var bar = function () { /* second */ }"; - i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program), - static_cast<unsigned>(strlen(program))); - i::ScriptDataImpl* data = - i::ParserApi::PartialPreParse(&stream, NULL, false); + v8::HandleScope handles; + i::Handle<i::String> source( + FACTORY->NewStringFromAscii(i::CStrVector(program))); + i::ScriptDataImpl* data = i::ParserApi::PartialPreParse(source, NULL, false); CHECK(!data->HasError()); data->Initialize(); @@ -356,7 +396,7 @@ TEST(PreParseOverflow) { reinterpret_cast<const i::byte*>(*program), static_cast<unsigned>(kProgramSize)); i::CompleteParserRecorder log; - i::JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache()); + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); scanner.Initialize(&stream); @@ -574,7 +614,7 @@ void TestStreamScanner(i::UC16CharacterStream* stream, i::Token::Value* expected_tokens, int skip_pos = 0, // Zero means not skipping. int skip_to = 0) { - i::JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache()); + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); scanner.Initialize(stream); int i = 0; @@ -655,7 +695,7 @@ void TestScanRegExp(const char* re_source, const char* expected) { i::Utf8ToUC16CharacterStream stream( reinterpret_cast<const i::byte*>(re_source), static_cast<unsigned>(strlen(re_source))); - i::JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache()); + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); scanner.Initialize(&stream); i::Token::Value start = scanner.peek(); @@ -708,25 +748,170 @@ TEST(RegExpScanning) { } -void TestParserSync(i::Handle<i::String> source, bool allow_lazy) { +TEST(ScopePositions) { + // Test the parser for correctly setting the start and end positions + // of a scope. We check the scope positions of exactly one scope + // nested in the global scope of a program. 'inner source' is the + // source code that determines the part of the source belonging + // to the nested scope. 'outer_prefix' and 'outer_suffix' are + // parts of the source that belong to the global scope. + struct SourceData { + const char* outer_prefix; + const char* inner_source; + const char* outer_suffix; + i::ScopeType scope_type; + i::LanguageMode language_mode; + }; + + const SourceData source_data[] = { + { " with ({}) ", "{ block; }", " more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " with ({}) ", "{ block; }", "; more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " with ({}) ", "{\n" + " block;\n" + " }", "\n" + " more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " with ({}) ", "statement;", " more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " with ({}) ", "statement", "\n" + " more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " with ({})\n" + " ", "statement;", "\n" + " more;", i::WITH_SCOPE, i::CLASSIC_MODE }, + { " try {} catch ", "(e) { block; }", " more;", + i::CATCH_SCOPE, i::CLASSIC_MODE }, + { " try {} catch ", "(e) { block; }", "; more;", + i::CATCH_SCOPE, i::CLASSIC_MODE }, + { " try {} catch ", "(e) {\n" + " block;\n" + " }", "\n" + " more;", i::CATCH_SCOPE, i::CLASSIC_MODE }, + { " try {} catch ", "(e) { block; }", " finally { block; } more;", + i::CATCH_SCOPE, i::CLASSIC_MODE }, + { " start;\n" + " ", "{ let block; }", " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " start;\n" + " ", "{ let block; }", "; more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " start;\n" + " ", "{\n" + " let block;\n" + " }", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " start;\n" + " function fun", "(a,b) { infunction; }", " more;", + i::FUNCTION_SCOPE, i::CLASSIC_MODE }, + { " start;\n" + " function fun", "(a,b) {\n" + " infunction;\n" + " }", "\n" + " more;", i::FUNCTION_SCOPE, i::CLASSIC_MODE }, + { " (function fun", "(a,b) { infunction; }", ")();", + i::FUNCTION_SCOPE, i::CLASSIC_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x) { block; }", " more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x) { block; }", "; more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x) {\n" + " block;\n" + " }", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x) statement;", " more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x) statement", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x = 1 ; x < 10; ++ x)\n" + " statement;", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {}) { block; }", " more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {}) { block; }", "; more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {}) {\n" + " block;\n" + " }", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {}) statement;", " more;", + i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {}) statement", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { " for ", "(let x in {})\n" + " statement;", "\n" + " more;", i::BLOCK_SCOPE, i::EXTENDED_MODE }, + { NULL, NULL, NULL, i::EVAL_SCOPE, i::CLASSIC_MODE } + }; + + v8::HandleScope handles; + v8::Persistent<v8::Context> context = v8::Context::New(); + v8::Context::Scope context_scope(context); + + int marker; + i::Isolate::Current()->stack_guard()->SetStackLimit( + reinterpret_cast<uintptr_t>(&marker) - 128 * 1024); + i::FLAG_harmony_scoping = true; + + for (int i = 0; source_data[i].outer_prefix; i++) { + int kPrefixLen = i::StrLength(source_data[i].outer_prefix); + int kInnerLen = i::StrLength(source_data[i].inner_source); + int kSuffixLen = i::StrLength(source_data[i].outer_suffix); + int kProgramSize = kPrefixLen + kInnerLen + kSuffixLen; + i::Vector<char> program = i::Vector<char>::New(kProgramSize + 1); + int length = i::OS::SNPrintF(program, "%s%s%s", + source_data[i].outer_prefix, + source_data[i].inner_source, + source_data[i].outer_suffix); + CHECK(length == kProgramSize); + + // Parse program source. + i::Handle<i::String> source( + FACTORY->NewStringFromAscii(i::CStrVector(program.start()))); + i::Handle<i::Script> script = FACTORY->NewScript(source); + i::Parser parser(script, i::kAllowLazy | i::EXTENDED_MODE, NULL, NULL); + i::CompilationInfo info(script); + info.MarkAsGlobal(); + info.SetLanguageMode(source_data[i].language_mode); + i::FunctionLiteral* function = parser.ParseProgram(&info); + CHECK(function != NULL); + + // Check scope types and positions. + i::Scope* scope = function->scope(); + CHECK(scope->is_global_scope()); + CHECK_EQ(scope->start_position(), 0); + CHECK_EQ(scope->end_position(), kProgramSize); + CHECK_EQ(scope->inner_scopes()->length(), 1); + + i::Scope* inner_scope = scope->inner_scopes()->at(0); + CHECK_EQ(inner_scope->type(), source_data[i].scope_type); + CHECK_EQ(inner_scope->start_position(), kPrefixLen); + // The end position of a token is one position after the last + // character belonging to that token. + CHECK_EQ(inner_scope->end_position(), kPrefixLen + kInnerLen); + } +} + + +void TestParserSync(i::Handle<i::String> source, int flags) { uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit(); + bool harmony_scoping = ((i::kLanguageModeMask & flags) == i::EXTENDED_MODE); // Preparse the data. i::CompleteParserRecorder log; - i::JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache()); + i::Scanner scanner(i::Isolate::Current()->unicode_cache()); i::GenericStringUC16CharacterStream stream(source, 0, source->length()); + scanner.SetHarmonyScoping(harmony_scoping); scanner.Initialize(&stream); v8::preparser::PreParser::PreParseResult result = v8::preparser::PreParser::PreParseProgram( - &scanner, &log, allow_lazy, stack_limit); + &scanner, &log, flags, stack_limit); CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result); i::ScriptDataImpl data(log.ExtractData()); // Parse the data i::Handle<i::Script> script = FACTORY->NewScript(source); - i::Parser parser(script, false, NULL, NULL); - i::FunctionLiteral* function = - parser.ParseProgram(source, true, i::kNonStrictMode); + bool save_harmony_scoping = i::FLAG_harmony_scoping; + i::FLAG_harmony_scoping = harmony_scoping; + i::Parser parser(script, flags, NULL, NULL); + i::CompilationInfo info(script); + info.MarkAsGlobal(); + i::FunctionLiteral* function = parser.ParseProgram(&info); + i::FLAG_harmony_scoping = save_harmony_scoping; i::String* type_string = NULL; if (function == NULL) { @@ -779,6 +964,23 @@ void TestParserSync(i::Handle<i::String> source, bool allow_lazy) { } +void TestParserSyncWithFlags(i::Handle<i::String> source) { + static const int kFlagsCount = 6; + const int flags[kFlagsCount] = { + i::kNoParsingFlags | i::CLASSIC_MODE, + i::kNoParsingFlags | i::STRICT_MODE, + i::kNoParsingFlags | i::EXTENDED_MODE, + i::kAllowLazy | i::CLASSIC_MODE, + i::kAllowLazy | i::STRICT_MODE, + i::kAllowLazy | i::EXTENDED_MODE + }; + + for (int k = 0; k < kFlagsCount; ++k) { + TestParserSync(source, flags[k]); + } +} + + TEST(ParserSync) { const char* context_data[][2] = { { "", "" }, @@ -876,8 +1078,7 @@ TEST(ParserSync) { CHECK(length == kProgramSize); i::Handle<i::String> source = FACTORY->NewStringFromAscii(i::CStrVector(program.start())); - TestParserSync(source, true); - TestParserSync(source, false); + TestParserSyncWithFlags(source); } } } diff --git a/deps/v8/test/cctest/test-platform-linux.cc b/deps/v8/test/cctest/test-platform-linux.cc index 756b9473c9..2a8d497850 100644 --- a/deps/v8/test/cctest/test-platform-linux.cc +++ b/deps/v8/test/cctest/test-platform-linux.cc @@ -67,7 +67,7 @@ TEST(BusyLock) { TEST(VirtualMemory) { - OS::Setup(); + OS::SetUp(); VirtualMemory* vm = new VirtualMemory(1 * MB); CHECK(vm->IsReserved()); void* block_addr = vm->address(); diff --git a/deps/v8/test/cctest/test-platform-win32.cc b/deps/v8/test/cctest/test-platform-win32.cc index 9bd0014c6f..36b30aaceb 100644 --- a/deps/v8/test/cctest/test-platform-win32.cc +++ b/deps/v8/test/cctest/test-platform-win32.cc @@ -13,7 +13,7 @@ using namespace ::v8::internal; TEST(VirtualMemory) { - OS::Setup(); + OS::SetUp(); VirtualMemory* vm = new VirtualMemory(1 * MB); CHECK(vm->IsReserved()); void* block_addr = vm->address(); diff --git a/deps/v8/test/cctest/test-profile-generator.cc b/deps/v8/test/cctest/test-profile-generator.cc index 76fd244e97..def829c297 100644 --- a/deps/v8/test/cctest/test-profile-generator.cc +++ b/deps/v8/test/cctest/test-profile-generator.cc @@ -52,7 +52,7 @@ TEST(TokenEnumerator) { CHECK_EQ(0, te.GetTokenId(*v8::Utils::OpenHandle(*token1))); } CHECK(!i::TokenEnumeratorTester::token_removed(&te)->at(2)); - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK(i::TokenEnumeratorTester::token_removed(&te)->at(2)); CHECK_EQ(1, te.GetTokenId(*v8::Utils::OpenHandle(*token2))); CHECK_EQ(0, te.GetTokenId(*v8::Utils::OpenHandle(*token1))); diff --git a/deps/v8/test/cctest/test-regexp.cc b/deps/v8/test/cctest/test-regexp.cc index 89a911274b..3070e16446 100644 --- a/deps/v8/test/cctest/test-regexp.cc +++ b/deps/v8/test/cctest/test-regexp.cc @@ -530,7 +530,7 @@ class TestConfig { typedef int Key; typedef int Value; static const int kNoKey; - static const int kNoValue; + static int NoValue() { return 0; } static inline int Compare(int a, int b) { if (a < b) return -1; @@ -543,7 +543,6 @@ class TestConfig { const int TestConfig::kNoKey = 0; -const int TestConfig::kNoValue = 0; static unsigned PseudoRandom(int i, int j) { @@ -837,7 +836,8 @@ TEST(MacroAssemblerNativeSimpleUC16) { Handle<Code> code = Handle<Code>::cast(code_object); int captures[4] = {42, 37, 87, 117}; - const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o', '\xa0'}; + const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o', + static_cast<uc16>('\xa0')}; Handle<String> input = factory->NewStringFromTwoByte(Vector<const uc16>(input_data, 6)); Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input); @@ -857,7 +857,8 @@ TEST(MacroAssemblerNativeSimpleUC16) { CHECK_EQ(-1, captures[2]); CHECK_EQ(-1, captures[3]); - const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a', '\xa0'}; + const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a', + static_cast<uc16>('\xa0')}; input = factory->NewStringFromTwoByte(Vector<const uc16>(input_data2, 9)); seq_input = Handle<SeqTwoByteString>::cast(input); start_adr = seq_input->GetCharsAddress(); diff --git a/deps/v8/test/cctest/test-reloc-info.cc b/deps/v8/test/cctest/test-reloc-info.cc index 5bdc4c3e6a..e638201db2 100644 --- a/deps/v8/test/cctest/test-reloc-info.cc +++ b/deps/v8/test/cctest/test-reloc-info.cc @@ -34,7 +34,7 @@ namespace internal { static void WriteRinfo(RelocInfoWriter* writer, byte* pc, RelocInfo::Mode mode, intptr_t data) { - RelocInfo rinfo(pc, mode, data); + RelocInfo rinfo(pc, mode, data, NULL); writer->Write(&rinfo); } diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 8e85444eee..b5c1a09763 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -114,10 +114,6 @@ TEST(ExternalReferenceEncoder) { ExternalReference(isolate->counters()->keyed_load_function_prototype()); CHECK_EQ(make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype), encoder.Encode(keyed_load_function_prototype.address())); - ExternalReference the_hole_value_location = - ExternalReference::the_hole_value_location(isolate); - CHECK_EQ(make_code(UNCLASSIFIED, 2), - encoder.Encode(the_hole_value_location.address())); ExternalReference stack_limit_address = ExternalReference::address_of_stack_limit(isolate); CHECK_EQ(make_code(UNCLASSIFIED, 4), @@ -127,14 +123,15 @@ TEST(ExternalReferenceEncoder) { CHECK_EQ(make_code(UNCLASSIFIED, 5), encoder.Encode(real_stack_limit_address.address())); #ifdef ENABLE_DEBUGGER_SUPPORT - CHECK_EQ(make_code(UNCLASSIFIED, 15), + CHECK_EQ(make_code(UNCLASSIFIED, 16), encoder.Encode(ExternalReference::debug_break(isolate).address())); #endif // ENABLE_DEBUGGER_SUPPORT CHECK_EQ(make_code(UNCLASSIFIED, 10), encoder.Encode( ExternalReference::new_space_start(isolate).address())); CHECK_EQ(make_code(UNCLASSIFIED, 3), - encoder.Encode(ExternalReference::roots_address(isolate).address())); + encoder.Encode( + ExternalReference::roots_array_start(isolate).address())); } @@ -157,15 +154,13 @@ TEST(ExternalReferenceDecoder) { decoder.Decode( make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype))); - CHECK_EQ(ExternalReference::the_hole_value_location(isolate).address(), - decoder.Decode(make_code(UNCLASSIFIED, 2))); CHECK_EQ(ExternalReference::address_of_stack_limit(isolate).address(), decoder.Decode(make_code(UNCLASSIFIED, 4))); CHECK_EQ(ExternalReference::address_of_real_stack_limit(isolate).address(), decoder.Decode(make_code(UNCLASSIFIED, 5))); #ifdef ENABLE_DEBUGGER_SUPPORT CHECK_EQ(ExternalReference::debug_break(isolate).address(), - decoder.Decode(make_code(UNCLASSIFIED, 15))); + decoder.Decode(make_code(UNCLASSIFIED, 16))); #endif // ENABLE_DEBUGGER_SUPPORT CHECK_EQ(ExternalReference::new_space_start(isolate).address(), decoder.Decode(make_code(UNCLASSIFIED, 10))); @@ -365,8 +360,8 @@ TEST(PartialSerialization) { Isolate::Current()->bootstrapper()->NativesSourceLookup(i); } } - HEAP->CollectAllGarbage(true); - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); Object* raw_foo; { @@ -490,7 +485,7 @@ TEST(ContextSerialization) { } // If we don't do this then we end up with a stray root pointing at the // context even after we have disposed of env. - HEAP->CollectAllGarbage(true); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); int file_name_length = StrLength(FLAG_testing_serialization_file) + 10; Vector<char> startup_name = Vector<char>::New(file_name_length + 1); @@ -563,16 +558,19 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { TEST(LinearAllocation) { v8::V8::Initialize(); int new_space_max = 512 * KB; + int paged_space_max = Page::kMaxHeapObjectSize; for (int size = 1000; size < 5 * MB; size += size >> 1) { + size &= ~8; // Round. int new_space_size = (size < new_space_max) ? size : new_space_max; + int paged_space_size = (size < paged_space_max) ? size : paged_space_max; HEAP->ReserveSpace( new_space_size, - size, // Old pointer space. - size, // Old data space. - size, // Code space. - size, // Map space. - size, // Cell space. + paged_space_size, // Old pointer space. + paged_space_size, // Old data space. + HEAP->code_space()->RoundSizeDownToObjectAlignment(paged_space_size), + HEAP->map_space()->RoundSizeDownToObjectAlignment(paged_space_size), + HEAP->cell_space()->RoundSizeDownToObjectAlignment(paged_space_size), size); // Large object space. LinearAllocationScope linear_allocation_scope; const int kSmallFixedArrayLength = 4; @@ -599,7 +597,7 @@ TEST(LinearAllocation) { Object* pointer_last = NULL; for (int i = 0; - i + kSmallFixedArraySize <= size; + i + kSmallFixedArraySize <= paged_space_size; i += kSmallFixedArraySize) { Object* obj = HEAP->AllocateFixedArray(kSmallFixedArrayLength, TENURED)->ToObjectChecked(); @@ -618,7 +616,9 @@ TEST(LinearAllocation) { } Object* data_last = NULL; - for (int i = 0; i + kSmallStringSize <= size; i += kSmallStringSize) { + for (int i = 0; + i + kSmallStringSize <= paged_space_size; + i += kSmallStringSize) { Object* obj = HEAP->AllocateRawAsciiString(kSmallStringLength, TENURED)->ToObjectChecked(); int old_page_fullness = i % Page::kPageSize; @@ -636,7 +636,7 @@ TEST(LinearAllocation) { } Object* map_last = NULL; - for (int i = 0; i + kMapSize <= size; i += kMapSize) { + for (int i = 0; i + kMapSize <= paged_space_size; i += kMapSize) { Object* obj = HEAP->AllocateMap(JS_OBJECT_TYPE, 42 * kPointerSize)->ToObjectChecked(); int old_page_fullness = i % Page::kPageSize; diff --git a/deps/v8/test/cctest/test-sockets.cc b/deps/v8/test/cctest/test-sockets.cc index 4af55dbe9b..ad7354002f 100644 --- a/deps/v8/test/cctest/test-sockets.cc +++ b/deps/v8/test/cctest/test-sockets.cc @@ -129,7 +129,7 @@ TEST(Socket) { bool ok; // Initialize socket support. - ok = Socket::Setup(); + ok = Socket::SetUp(); CHECK(ok); // Send and receive some data. diff --git a/deps/v8/test/cctest/test-spaces.cc b/deps/v8/test/cctest/test-spaces.cc index 0f22ce1a9b..6e495bc169 100644 --- a/deps/v8/test/cctest/test-spaces.cc +++ b/deps/v8/test/cctest/test-spaces.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -32,7 +32,9 @@ using namespace v8::internal; +#if 0 static void VerifyRegionMarking(Address page_start) { +#ifdef ENABLE_CARDMARKING_WRITE_BARRIER Page* p = Page::FromAddress(page_start); p->SetRegionMarks(Page::kAllRegionsCleanMarks); @@ -54,9 +56,13 @@ static void VerifyRegionMarking(Address page_start) { addr += kPointerSize) { CHECK(Page::FromAddress(addr)->IsRegionDirty(addr)); } +#endif } +#endif +// TODO(gc) you can no longer allocate pages like this. Details are hidden. +#if 0 TEST(Page) { byte* mem = NewArray<byte>(2*Page::kPageSize); CHECK(mem != NULL); @@ -89,6 +95,7 @@ TEST(Page) { DeleteArray(mem); } +#endif namespace v8 { @@ -118,91 +125,71 @@ class TestMemoryAllocatorScope { TEST(MemoryAllocator) { - OS::Setup(); + OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); - CHECK(heap->ConfigureHeapDefault()); + CHECK(isolate->heap()->ConfigureHeapDefault()); + MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); - CHECK(memory_allocator->Setup(heap->MaxReserved(), + CHECK(memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize())); - TestMemoryAllocatorScope test_scope(isolate, memory_allocator); + int total_pages = 0; OldSpace faked_space(heap, heap->MaxReserved(), OLD_POINTER_SPACE, NOT_EXECUTABLE); - int total_pages = 0; - int requested = MemoryAllocator::kPagesPerChunk; - int allocated; - // If we request n pages, we should get n or n - 1. - Page* first_page = memory_allocator->AllocatePages( - requested, &allocated, &faked_space); + Page* first_page = + memory_allocator->AllocatePage(&faked_space, NOT_EXECUTABLE); + + first_page->InsertAfter(faked_space.anchor()->prev_page()); CHECK(first_page->is_valid()); - CHECK(allocated == requested || allocated == requested - 1); - total_pages += allocated; + CHECK(first_page->next_page() == faked_space.anchor()); + total_pages++; - Page* last_page = first_page; - for (Page* p = first_page; p->is_valid(); p = p->next_page()) { - CHECK(memory_allocator->IsPageInSpace(p, &faked_space)); - last_page = p; + for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { + CHECK(p->owner() == &faked_space); } // Again, we should get n or n - 1 pages. - Page* others = memory_allocator->AllocatePages( - requested, &allocated, &faked_space); - CHECK(others->is_valid()); - CHECK(allocated == requested || allocated == requested - 1); - total_pages += allocated; - - memory_allocator->SetNextPage(last_page, others); + Page* other = + memory_allocator->AllocatePage(&faked_space, NOT_EXECUTABLE); + CHECK(other->is_valid()); + total_pages++; + other->InsertAfter(first_page); int page_count = 0; - for (Page* p = first_page; p->is_valid(); p = p->next_page()) { - CHECK(memory_allocator->IsPageInSpace(p, &faked_space)); + for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { + CHECK(p->owner() == &faked_space); page_count++; } CHECK(total_pages == page_count); Page* second_page = first_page->next_page(); CHECK(second_page->is_valid()); - - // Freeing pages at the first chunk starting at or after the second page - // should free the entire second chunk. It will return the page it was passed - // (since the second page was in the first chunk). - Page* free_return = memory_allocator->FreePages(second_page); - CHECK(free_return == second_page); - memory_allocator->SetNextPage(first_page, free_return); - - // Freeing pages in the first chunk starting at the first page should free - // the first chunk and return an invalid page. - Page* invalid_page = memory_allocator->FreePages(first_page); - CHECK(!invalid_page->is_valid()); - + memory_allocator->Free(first_page); + memory_allocator->Free(second_page); memory_allocator->TearDown(); delete memory_allocator; } TEST(NewSpace) { - OS::Setup(); + OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); CHECK(heap->ConfigureHeapDefault()); MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); - CHECK(memory_allocator->Setup(heap->MaxReserved(), + CHECK(memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize())); TestMemoryAllocatorScope test_scope(isolate, memory_allocator); NewSpace new_space(heap); - void* chunk = - memory_allocator->ReserveInitialChunk(4 * heap->ReservedSemiSpaceSize()); - CHECK(chunk != NULL); - Address start = RoundUp(static_cast<Address>(chunk), - 2 * heap->ReservedSemiSpaceSize()); - CHECK(new_space.Setup(start, 2 * heap->ReservedSemiSpaceSize())); - CHECK(new_space.HasBeenSetup()); + CHECK(new_space.SetUp(HEAP->ReservedSemiSpaceSize(), + HEAP->ReservedSemiSpaceSize())); + CHECK(new_space.HasBeenSetUp()); while (new_space.Available() >= Page::kMaxHeapObjectSize) { Object* obj = @@ -217,13 +204,13 @@ TEST(NewSpace) { TEST(OldSpace) { - OS::Setup(); + OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); CHECK(heap->ConfigureHeapDefault()); MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); - CHECK(memory_allocator->Setup(heap->MaxReserved(), + CHECK(memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize())); TestMemoryAllocatorScope test_scope(isolate, memory_allocator); @@ -233,13 +220,7 @@ TEST(OldSpace) { NOT_EXECUTABLE); CHECK(s != NULL); - void* chunk = memory_allocator->ReserveInitialChunk( - 4 * heap->ReservedSemiSpaceSize()); - CHECK(chunk != NULL); - Address start = static_cast<Address>(chunk); - size_t size = RoundUp(start, 2 * heap->ReservedSemiSpaceSize()) - start; - - CHECK(s->Setup(start, size)); + CHECK(s->SetUp()); while (s->Available() > 0) { s->AllocateRaw(Page::kMaxHeapObjectSize)->ToObjectUnchecked(); @@ -258,14 +239,12 @@ TEST(LargeObjectSpace) { LargeObjectSpace* lo = HEAP->lo_space(); CHECK(lo != NULL); - Map* faked_map = reinterpret_cast<Map*>(HeapObject::FromAddress(0)); int lo_size = Page::kPageSize; - Object* obj = lo->AllocateRaw(lo_size)->ToObjectUnchecked(); + Object* obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE)->ToObjectUnchecked(); CHECK(obj->IsHeapObject()); HeapObject* ho = HeapObject::cast(obj); - ho->set_map(faked_map); CHECK(lo->Contains(HeapObject::cast(obj))); @@ -275,14 +254,13 @@ TEST(LargeObjectSpace) { while (true) { intptr_t available = lo->Available(); - { MaybeObject* maybe_obj = lo->AllocateRaw(lo_size); + { MaybeObject* maybe_obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE); if (!maybe_obj->ToObject(&obj)) break; } - HeapObject::cast(obj)->set_map(faked_map); CHECK(lo->Available() < available); }; CHECK(!lo->IsEmpty()); - CHECK(lo->AllocateRaw(lo_size)->IsFailure()); + CHECK(lo->AllocateRaw(lo_size, NOT_EXECUTABLE)->IsFailure()); } diff --git a/deps/v8/test/cctest/test-strings.cc b/deps/v8/test/cctest/test-strings.cc index 55c21417d0..e11349bc85 100644 --- a/deps/v8/test/cctest/test-strings.cc +++ b/deps/v8/test/cctest/test-strings.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Check that we can traverse very deep stacks of ConsStrings using // StringInputBuffer. Check that Get(int) works on very deep stacks @@ -355,7 +355,7 @@ TEST(ExternalShortStringAdd) { // Make sure we cover all always-flat lengths and at least one above. static const int kMaxLength = 20; - CHECK_GT(kMaxLength, i::String::kMinNonFlatLength); + CHECK_GT(kMaxLength, i::ConsString::kMinLength); // Allocate two JavaScript arrays for holding short strings. v8::Handle<v8::Array> ascii_external_strings = @@ -502,6 +502,35 @@ TEST(SliceFromCons) { } +class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { + public: + explicit AsciiVectorResource(i::Vector<const char> vector) + : data_(vector) {} + virtual ~AsciiVectorResource() {} + virtual size_t length() const { return data_.length(); } + virtual const char* data() const { return data_.start(); } + private: + i::Vector<const char> data_; +}; + + +TEST(SliceFromExternal) { + FLAG_string_slices = true; + InitializeVM(); + v8::HandleScope scope; + AsciiVectorResource resource( + i::Vector<const char>("abcdefghijklmnopqrstuvwxyz", 26)); + Handle<String> string = FACTORY->NewExternalStringFromAscii(&resource); + CHECK(string->IsExternalString()); + Handle<String> slice = FACTORY->NewSubString(string, 1, 25); + CHECK(slice->IsSlicedString()); + CHECK(string->IsExternalString()); + CHECK_EQ(SlicedString::cast(*slice)->parent(), *string); + CHECK(SlicedString::cast(*slice)->parent()->IsExternalString()); + CHECK(slice->IsFlat()); +} + + TEST(TrivialSlice) { // This tests whether a slice that contains the entire parent string // actually creates a new string (it should not). diff --git a/deps/v8/test/cctest/test-threads.cc b/deps/v8/test/cctest/test-threads.cc index 985b9e5b99..713d1e8425 100644 --- a/deps/v8/test/cctest/test-threads.cc +++ b/deps/v8/test/cctest/test-threads.cc @@ -63,7 +63,7 @@ enum Turn { static Turn turn = FILL_CACHE; -class ThreadA: public v8::internal::Thread { +class ThreadA : public v8::internal::Thread { public: ThreadA() : Thread("ThreadA") { } void Run() { @@ -99,7 +99,7 @@ class ThreadA: public v8::internal::Thread { }; -class ThreadB: public v8::internal::Thread { +class ThreadB : public v8::internal::Thread { public: ThreadB() : Thread("ThreadB") { } void Run() { @@ -111,7 +111,7 @@ class ThreadB: public v8::internal::Thread { v8::Context::Scope context_scope(v8::Context::New()); // Clear the caches by forcing major GC. - HEAP->CollectAllGarbage(false); + HEAP->CollectAllGarbage(v8::internal::Heap::kNoGCFlags); turn = SECOND_TIME_FILL_CACHE; break; } @@ -190,3 +190,19 @@ TEST(ThreadIdValidation) { delete threads[i]; } } + + +class ThreadC : public v8::internal::Thread { + public: + ThreadC() : Thread("ThreadC") { } + void Run() { + Join(); + } +}; + + +TEST(ThreadJoinSelf) { + ThreadC thread; + thread.Start(); + thread.Join(); +} diff --git a/deps/v8/test/cctest/test-utils.cc b/deps/v8/test/cctest/test-utils.cc index e4f70df409..df8ff72e4f 100644 --- a/deps/v8/test/cctest/test-utils.cc +++ b/deps/v8/test/cctest/test-utils.cc @@ -105,7 +105,7 @@ void TestMemCopy(Vector<byte> src, TEST(MemCopy) { v8::V8::Initialize(); - OS::Setup(); + OS::SetUp(); const int N = OS::kMinComplexMemCopy + 128; Vector<byte> buffer1 = Vector<byte>::New(N); Vector<byte> buffer2 = Vector<byte>::New(N); diff --git a/deps/v8/test/cctest/test-weakmaps.cc b/deps/v8/test/cctest/test-weakmaps.cc index db4db25435..56d593628a 100644 --- a/deps/v8/test/cctest/test-weakmaps.cc +++ b/deps/v8/test/cctest/test-weakmaps.cc @@ -50,7 +50,7 @@ static void PutIntoWeakMap(Handle<JSWeakMap> weakmap, Handle<JSObject> key, int value) { Handle<ObjectHashTable> table = PutIntoObjectHashTable( - Handle<ObjectHashTable>(weakmap->table()), + Handle<ObjectHashTable>(ObjectHashTable::cast(weakmap->table())), Handle<JSObject>(JSObject::cast(*key)), Handle<Smi>(Smi::FromInt(value))); weakmap->set_table(*table); @@ -85,13 +85,14 @@ TEST(Weakness) { v8::HandleScope scope; PutIntoWeakMap(weakmap, Handle<JSObject>(JSObject::cast(*key)), 23); } - CHECK_EQ(1, weakmap->table()->NumberOfElements()); + CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); // Force a full GC. HEAP->CollectAllGarbage(false); CHECK_EQ(0, NumberOfWeakCalls); - CHECK_EQ(1, weakmap->table()->NumberOfElements()); - CHECK_EQ(0, weakmap->table()->NumberOfDeletedElements()); + CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements()); // Make the global reference to the key weak. { @@ -107,12 +108,14 @@ TEST(Weakness) { // weak references whereas the second one will also clear weak maps. HEAP->CollectAllGarbage(false); CHECK_EQ(1, NumberOfWeakCalls); - CHECK_EQ(1, weakmap->table()->NumberOfElements()); - CHECK_EQ(0, weakmap->table()->NumberOfDeletedElements()); + CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements()); HEAP->CollectAllGarbage(false); CHECK_EQ(1, NumberOfWeakCalls); - CHECK_EQ(0, weakmap->table()->NumberOfElements()); - CHECK_EQ(1, weakmap->table()->NumberOfDeletedElements()); + CHECK_EQ(0, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); + CHECK_EQ( + 1, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements()); } @@ -122,7 +125,7 @@ TEST(Shrinking) { Handle<JSWeakMap> weakmap = AllocateJSWeakMap(); // Check initial capacity. - CHECK_EQ(32, weakmap->table()->Capacity()); + CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity()); // Fill up weak map to trigger capacity change. { @@ -135,15 +138,17 @@ TEST(Shrinking) { } // Check increased capacity. - CHECK_EQ(128, weakmap->table()->Capacity()); + CHECK_EQ(128, ObjectHashTable::cast(weakmap->table())->Capacity()); // Force a full GC. - CHECK_EQ(32, weakmap->table()->NumberOfElements()); - CHECK_EQ(0, weakmap->table()->NumberOfDeletedElements()); + CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); + CHECK_EQ( + 0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements()); HEAP->CollectAllGarbage(false); - CHECK_EQ(0, weakmap->table()->NumberOfElements()); - CHECK_EQ(32, weakmap->table()->NumberOfDeletedElements()); + CHECK_EQ(0, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); + CHECK_EQ( + 32, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements()); // Check shrunk capacity. - CHECK_EQ(32, weakmap->table()->Capacity()); + CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity()); } diff --git a/deps/v8/test/es5conform/es5conform.status b/deps/v8/test/es5conform/es5conform.status index d095a2471d..12ebf903e9 100644 --- a/deps/v8/test/es5conform/es5conform.status +++ b/deps/v8/test/es5conform/es5conform.status @@ -41,16 +41,6 @@ chapter10/10.4/10.4.2/10.4.2-2-c-1: FAIL_OK # We are compatible with Safari and Firefox. chapter11/11.1/11.1.5: UNIMPLEMENTED -# We do not have a global object called 'global' as required by tests. -chapter15/15.1: FAIL_OK - -# NaN is writable. We are compatible with JSC. -chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-178: FAIL_OK -# Infinity is writable. We are compatible with JSC. -chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-179: FAIL_OK -# undefined is writable. We are compatible with JSC. -chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-180: FAIL_OK - # Our Function object has an "arguments" property which is used as a # non-property in the test. chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-183: FAIL_OK @@ -106,9 +96,6 @@ chapter15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-7: FAIL_OK # SUBSETFAIL chapter15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-11: FAIL_OK -# We do not implement all methods on RegExp. -chapter15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-13: FAIL - # SUBSETFAIL chapter15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-14: FAIL_OK @@ -196,27 +183,6 @@ chapter15/15.4/15.4.4/15.4.4.22/15.4.4.22-9-c-ii-4-s: SKIP # have no effect on the actual array on which reduceRight is called. chapter15/15.4/15.4.4/15.4.4.22/15.4.4.22-9-7: FAIL_OK -# We do not correctly recognize \uFEFF as whitespace -chapter15/15.5/15.5.4/15.5.4.20/15.5.4.20-4-10: FAIL -chapter15/15.5/15.5.4/15.5.4.20/15.5.4.20-4-18: FAIL -chapter15/15.5/15.5.4/15.5.4.20/15.5.4.20-4-34: FAIL - -# RegExp.prototype is not of type RegExp - we are bug compatible with JSC. -chapter15/15.10/15.10.6/15.10.6: FAIL_OK - -# We do not have the properties of a RegExp instance on RegExp.prototype. -# The spec says we should - but we are currently bug compatible with JSC. -chapter15/15.10/15.10.7/15.10.7.1/15.10.7.1-1: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.1/15.10.7.1-2: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.2/15.10.7.2-1: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.2/15.10.7.2-2: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.3/15.10.7.3-1: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.3/15.10.7.3-2: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.4/15.10.7.4-1: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.4/15.10.7.4-2: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.5/15.10.7.5-1: FAIL_OK -chapter15/15.10/15.10.7/15.10.7.5/15.10.7.5-2: FAIL_OK - ############################################################################## # Unimplemented parts of strict mode # Setting expectations to fail only so that the tests trigger as soon as @@ -348,8 +314,3 @@ chapter15/15.3/15.3.2/15.3.2.1/15.3.2.1-11-6-s: FAIL # Array.prototype.reduce - null passed as thisValue to strict callbackfn # Invalid test case: http://es5conform.codeplex.com/workitem/29085 chapter15/15.4/15.4.4/15.4.4.21/15.4.4.21-9-c-ii-4-s: FAIL - -[ $arch == mips ] - -# Skip all tests on MIPS. -*: SKIP diff --git a/deps/v8/test/message/message.status b/deps/v8/test/message/message.status index 70354ceec7..fc2896b1c9 100644 --- a/deps/v8/test/message/message.status +++ b/deps/v8/test/message/message.status @@ -29,10 +29,3 @@ prefix message # All tests in the bug directory are expected to fail. bugs: FAIL - - -############################################################################## -[ $arch == mips ] - -# Skip all tests on MIPS. -*: SKIP diff --git a/deps/v8/test/mjsunit/apply.js b/deps/v8/test/mjsunit/apply.js index c166110df0..413ee937c6 100644 --- a/deps/v8/test/mjsunit/apply.js +++ b/deps/v8/test/mjsunit/apply.js @@ -190,3 +190,10 @@ assertEquals("morseper", "moreseper-prime"); delete(Array.prototype["1"]); + +// Check correct handling of non-array argument lists. +assertSame(this, f0.apply(this, {}), "non-array-1"); +assertSame(this, f0.apply(this, { length:1 }), "non-array-2"); +assertEquals(void 0, f1.apply(this, { length:1 }), "non-array-3"); +assertEquals(void 0, f1.apply(this, { 0:"foo" }), "non-array-4"); +assertEquals("foo", f1.apply(this, { length:1, 0:"foo" }), "non-array-5"); diff --git a/deps/v8/test/mjsunit/array-construct-transition.js b/deps/v8/test/mjsunit/array-construct-transition.js new file mode 100644 index 0000000000..577e321a55 --- /dev/null +++ b/deps/v8/test/mjsunit/array-construct-transition.js @@ -0,0 +1,39 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays + +support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); + +if (support_smi_only_arrays) { + var a = new Array(0, 1, 2); + assertTrue(%HasFastSmiOnlyElements(a)); + var b = new Array(0.5, 1.2, 2.3); + assertTrue(%HasFastDoubleElements(b)); + var c = new Array(0.5, 1.2, new Object()); + assertTrue(%HasFastElements(c)); +} diff --git a/deps/v8/test/mjsunit/array-join.js b/deps/v8/test/mjsunit/array-join.js index 5c837a5ca3..c08c182fee 100644 --- a/deps/v8/test/mjsunit/array-join.js +++ b/deps/v8/test/mjsunit/array-join.js @@ -75,10 +75,10 @@ if (Array.prototype.toString != oldToString) { Array.prototype.toString = oldToString; } -var a = new Array(123123123); -assertEquals(123123122, String(a).length); -assertEquals(123123122, a.join(",").length); -assertEquals(246246244, a.join("oo").length); +var a = new Array(123123); +assertEquals(123122, String(a).length); +assertEquals(123122, a.join(",").length); +assertEquals(246244, a.join("oo").length); a = new Array(Math.pow(2,32) - 1); // Max length. assertEquals("", a.join("")); @@ -90,4 +90,4 @@ a = new Array(100001); for (var i = 0; i < a.length; i++) a[i] = undefined; a[5] = "ab"; a[90000] = "cd"; -assertEquals("abcd", a.join("")); // Must not throw.
\ No newline at end of file +assertEquals("abcd", a.join("")); // Must not throw. diff --git a/deps/v8/test/mjsunit/array-literal-transitions.js b/deps/v8/test/mjsunit/array-literal-transitions.js new file mode 100644 index 0000000000..f657525eb6 --- /dev/null +++ b/deps/v8/test/mjsunit/array-literal-transitions.js @@ -0,0 +1,210 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc +// Test element kind of objects. +// Since --smi-only-arrays affects builtins, its default setting at compile +// time sticks if built with snapshot. If --smi-only-arrays is deactivated +// by default, only a no-snapshot build actually has smi-only arrays enabled +// in this test case. Depending on whether smi-only arrays are actually +// enabled, this test takes the appropriate code path to check smi-only arrays. + +support_smi_only_arrays = %HasFastSmiOnlyElements([1,2,3,4,5,6,7,8,9,10]); + +if (support_smi_only_arrays) { + print("Tests include smi-only arrays."); +} else { + print("Tests do NOT include smi-only arrays."); +} + +// IC and Crankshaft support for smi-only elements in dynamic array literals. +function get(foo) { return foo; } // Used to generate dynamic values. + +function array_literal_test() { + var a0 = [1, 2, 3]; + assertTrue(%HasFastSmiOnlyElements(a0)); + var a1 = [get(1), get(2), get(3)]; + assertTrue(%HasFastSmiOnlyElements(a1)); + + var b0 = [1, 2, get("three")]; + assertTrue(%HasFastElements(b0)); + var b1 = [get(1), get(2), get("three")]; + assertTrue(%HasFastElements(b1)); + + var c0 = [1, 2, get(3.5)]; + assertTrue(%HasFastDoubleElements(c0)); + assertEquals(3.5, c0[2]); + assertEquals(2, c0[1]); + assertEquals(1, c0[0]); + + var c1 = [1, 2, 3.5]; + assertTrue(%HasFastDoubleElements(c1)); + assertEquals(3.5, c1[2]); + assertEquals(2, c1[1]); + assertEquals(1, c1[0]); + + var c2 = [get(1), get(2), get(3.5)]; + assertTrue(%HasFastDoubleElements(c2)); + assertEquals(3.5, c2[2]); + assertEquals(2, c2[1]); + assertEquals(1, c2[0]); + + var object = new Object(); + var d0 = [1, 2, object]; + assertTrue(%HasFastElements(d0)); + assertEquals(object, d0[2]); + assertEquals(2, d0[1]); + assertEquals(1, d0[0]); + + var e0 = [1, 2, 3.5]; + assertTrue(%HasFastDoubleElements(e0)); + assertEquals(3.5, e0[2]); + assertEquals(2, e0[1]); + assertEquals(1, e0[0]); + + var f0 = [1, 2, [1, 2]]; + assertTrue(%HasFastElements(f0)); + assertEquals([1,2], f0[2]); + assertEquals(2, f0[1]); + assertEquals(1, f0[0]); +} + +if (support_smi_only_arrays) { + for (var i = 0; i < 3; i++) { + array_literal_test(); + } + %OptimizeFunctionOnNextCall(array_literal_test); + array_literal_test(); + + function test_large_literal() { + + function d() { + gc(); + return 2.5; + } + + function o() { + gc(); + return new Object(); + } + + large = + [ 0, 1, 2, 3, 4, 5, d(), d(), d(), d(), d(), d(), o(), o(), o(), o() ]; + assertFalse(%HasDictionaryElements(large)); + assertFalse(%HasFastSmiOnlyElements(large)); + assertFalse(%HasFastDoubleElements(large)); + assertTrue(%HasFastElements(large)); + assertEquals(large, + [0, 1, 2, 3, 4, 5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, + new Object(), new Object(), new Object(), new Object()]); + } + + for (var i = 0; i < 3; i++) { + test_large_literal(); + } + %OptimizeFunctionOnNextCall(test_large_literal); + test_large_literal(); + + function deopt_array(use_literal) { + if (use_literal) { + return [.5, 3, 4]; + } else { + return new Array(); + } + } + + deopt_array(false); + deopt_array(false); + deopt_array(false); + %OptimizeFunctionOnNextCall(deopt_array); + var array = deopt_array(false); + assertTrue(2 != %GetOptimizationStatus(deopt_array)); + deopt_array(true); + assertTrue(2 != %GetOptimizationStatus(deopt_array)); + array = deopt_array(false); + assertTrue(2 != %GetOptimizationStatus(deopt_array)); + + // Check that unexpected changes in the objects stored into the boilerplate + // also force a deopt. + function deopt_array_literal_all_smis(a) { + return [0, 1, a]; + } + + deopt_array_literal_all_smis(2); + deopt_array_literal_all_smis(3); + deopt_array_literal_all_smis(4); + array = deopt_array_literal_all_smis(4); + assertEquals(0, array[0]); + assertEquals(1, array[1]); + assertEquals(4, array[2]); + %OptimizeFunctionOnNextCall(deopt_array_literal_all_smis); + array = deopt_array_literal_all_smis(5); + array = deopt_array_literal_all_smis(6); + assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_smis)); + assertEquals(0, array[0]); + assertEquals(1, array[1]); + assertEquals(6, array[2]); + + array = deopt_array_literal_all_smis(.5); + assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_smis)); + assertEquals(0, array[0]); + assertEquals(1, array[1]); + assertEquals(.5, array[2]); + + function deopt_array_literal_all_doubles(a) { + return [0.5, 1, a]; + } + + deopt_array_literal_all_doubles(.5); + deopt_array_literal_all_doubles(.5); + deopt_array_literal_all_doubles(.5); + array = deopt_array_literal_all_doubles(0.5); + assertEquals(0.5, array[0]); + assertEquals(1, array[1]); + assertEquals(0.5, array[2]); + %OptimizeFunctionOnNextCall(deopt_array_literal_all_doubles); + array = deopt_array_literal_all_doubles(5); + array = deopt_array_literal_all_doubles(6); + assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_doubles)); + assertEquals(0.5, array[0]); + assertEquals(1, array[1]); + assertEquals(6, array[2]); + + var foo = new Object(); + array = deopt_array_literal_all_doubles(foo); + assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_doubles)); + assertEquals(0.5, array[0]); + assertEquals(1, array[1]); + assertEquals(foo, array[2]); +} + +(function literals_after_osr() { + var color = [0]; + // Trigger OSR. + while (%GetOptimizationStatus(literals_after_osr) == 2) {} + return [color[0]]; +})(); diff --git a/deps/v8/test/mjsunit/array-tostring.js b/deps/v8/test/mjsunit/array-tostring.js new file mode 100644 index 0000000000..6708657eef --- /dev/null +++ b/deps/v8/test/mjsunit/array-tostring.js @@ -0,0 +1,159 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Array's toString should call the object's own join method, if one exists and +// is callable. Otherwise, just use the original Object.toString function. + +var success = "[test success]"; +var expectedThis; +function testJoin() { + assertEquals(0, arguments.length); + assertSame(expectedThis, this); + return success; +} + + +// On an Array object. + +// Default case. +var a1 = [1, 2, 3]; +assertEquals(a1.join(), a1.toString()); + +// Non-standard "join" function is called correctly. +var a2 = [1, 2, 3]; +a2.join = testJoin; +expectedThis = a2; +assertEquals(success, a2.toString()); + +// Non-callable join function is ignored and Object.prototype.toString is +// used instead. +var a3 = [1, 2, 3]; +a3.join = "not callable"; +assertEquals("[object Array]", a3.toString()); + +// Non-existing join function is treated same as non-callable. +var a4 = [1, 2, 3]; +a4.__proto__ = { toString: Array.prototype.toString }; +// No join on Array. +assertEquals("[object Array]", a4.toString()); + + +// On a non-Array object. + +// Default looks-like-an-array case. +var o1 = {length: 3, 0: 1, 1: 2, 2: 3, + toString: Array.prototype.toString, + join: Array.prototype.join}; +assertEquals(o1.join(), o1.toString()); + + +// Non-standard join is called correctly. +// Check that we don't read, e.g., length before calling join. +var o2 = {toString : Array.prototype.toString, + join: testJoin, + get length() { assertUnreachable(); }, + get 0() { assertUnreachable(); }}; +expectedThis = o2; +assertEquals(success, o2.toString()); + +// Non-standard join is called even if it looks like an array. +var o3 = {length: 3, 0: 1, 1: 2, 2: 3, + toString: Array.prototype.toString, + join: testJoin}; +expectedThis = o3; +assertEquals(success, o3.toString()); + +// Non-callable join works same as for Array. +var o4 = {length: 3, 0: 1, 1: 2, 2: 3, + toString: Array.prototype.toString, + join: "not callable"}; +assertEquals("[object Object]", o4.toString()); + + +// Non-existing join works same as for Array. +var o5 = {length: 3, 0: 1, 1: 2, 2: 3, + toString: Array.prototype.toString + /* no join */}; +assertEquals("[object Object]", o5.toString()); + + +// Test that ToObject is called before getting "join", so the instance +// that "join" is read from is the same one passed as receiver later. +var called_before = false; +expectedThis = null; +Object.defineProperty(Number.prototype, "join", {get: function() { + assertFalse(called_before); + called_before = true; + expectedThis = this; + return testJoin; + }}); +Number.prototype.arrayToString = Array.prototype.toString; +assertEquals(success, (42).arrayToString()); + +// ---------------------------------------------------------- +// Testing Array.prototype.toLocaleString + +// Ensure that it never uses Array.prototype.toString for anything. +Array.prototype.toString = function() { assertUnreachable(); }; + +// Default case. +var la1 = [1, [2, 3], 4]; +assertEquals("1,2,3,4", la1.toLocaleString()); + +// Used on a string (which looks like an array of characters). +String.prototype.toLocaleString = Array.prototype.toLocaleString; +assertEquals("1,2,3,4", "1234".toLocaleString()); + +// If toLocaleString of element is not callable, throw a TypeError. +var la2 = [1, {toLocaleString: "not callable"}, 3]; +assertThrows(function() { la2.toLocaleString(); }, TypeError); + +// If toLocaleString of element is callable, call it. +var la3 = [1, {toLocaleString: function() { return "XX";}}, 3]; +assertEquals("1,XX,3", la3.toLocaleString()); + +// Omitted elements, as well as undefined and null, become empty string. +var la4 = [1, null, 3, undefined, 5,, 7]; +assertEquals("1,,3,,5,,7", la4.toLocaleString()); + + +// ToObject is called first and the same object is being used for the +// rest of the operations. +Object.defineProperty(Number.prototype, "length", { + get: function() { + exptectedThis = this; + return 3; + }}); +for (var i = 0; i < 3; i++) { + Object.defineProperty(Number.prototype, i, { + get: function() { + assertEquals(expectedThis, this); + return +this; + }}); +} +Number.prototype.arrayToLocaleString = Array.prototype.toLocaleString; +assertEquals("42,42,42", (42).arrayToLocaleString());
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/assert-opt-and-deopt.js b/deps/v8/test/mjsunit/assert-opt-and-deopt.js index c9adb5bb15..51cb99adc3 100644 --- a/deps/v8/test/mjsunit/assert-opt-and-deopt.js +++ b/deps/v8/test/mjsunit/assert-opt-and-deopt.js @@ -150,11 +150,6 @@ tracker.AssertDeoptCount(f, 0); f(1); -tracker.AssertOptCount(f, 0); -tracker.AssertIsOptimized(f, false); -tracker.AssertDeoptHappened(f, false); -tracker.AssertDeoptCount(f, 0); - %OptimizeFunctionOnNextCall(f); f(1); @@ -172,6 +167,7 @@ tracker.AssertDeoptCount(f, 1); // Let's trigger optimization for another type. for (var i = 0; i < 5; i++) f("a"); + %OptimizeFunctionOnNextCall(f); f("b"); diff --git a/deps/v8/test/mjsunit/bugs/bug-618.js b/deps/v8/test/mjsunit/bugs/bug-618.js index ae843267ff..0513f87f16 100644 --- a/deps/v8/test/mjsunit/bugs/bug-618.js +++ b/deps/v8/test/mjsunit/bugs/bug-618.js @@ -42,4 +42,4 @@ function C() { assertEquals(23, new C().x); C.prototype.__defineSetter__('x', function(value) { this.y = 23; }); -assertEquals(void 0, new C().x)); +assertEquals(void 0, new C().x); diff --git a/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js b/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js index a407c531a7..fda32eb3d3 100644 --- a/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js +++ b/deps/v8/test/mjsunit/bugs/harmony/debug-blockscopes.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --harmony-block-scoping +// Flags: --expose-debug-as debug --harmony-scoping // The functions used for testing backtraces. They are at the top to make the // testing of source line/column easier. diff --git a/deps/v8/test/mjsunit/compiler/compare.js b/deps/v8/test/mjsunit/compiler/compare.js index 3f96087002..460b0ab003 100644 --- a/deps/v8/test/mjsunit/compiler/compare.js +++ b/deps/v8/test/mjsunit/compiler/compare.js @@ -83,9 +83,9 @@ function TestNonPrimitive(order, f) { } TestNonPrimitive("xy", MaxLT); -TestNonPrimitive("yx", MaxLE); +TestNonPrimitive("xy", MaxLE); TestNonPrimitive("xy", MaxGE); -TestNonPrimitive("yx", MaxGT); +TestNonPrimitive("xy", MaxGT); // Test compare in case of aliased registers. function CmpX(x) { if (x == x) return 42; } diff --git a/deps/v8/test/mjsunit/compiler/inline-arity-mismatch.js b/deps/v8/test/mjsunit/compiler/inline-arity-mismatch.js new file mode 100644 index 0000000000..4a61fa3a62 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/inline-arity-mismatch.js @@ -0,0 +1,62 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// Test inlining at call sites with mismatched arity. + +function f(a) { + return a.x; +} + +function g(a, b) { + return a.x; +} + +function h1(a, b) { + return f(a, a) * g(b); +} + +function h2(a, b) { + return f(a, a) * g(b); +} + + +var o = {x: 2}; + +assertEquals(4, h1(o, o)); +assertEquals(4, h1(o, o)); +assertEquals(4, h2(o, o)); +assertEquals(4, h2(o, o)); +%OptimizeFunctionOnNextCall(h1); +%OptimizeFunctionOnNextCall(h2); +assertEquals(4, h1(o, o)); +assertEquals(4, h2(o, o)); + +var u = {y:0, x:1}; +assertEquals(2, h1(u, o)); +assertEquals(2, h2(o, u)); diff --git a/deps/v8/test/mjsunit/compiler/inline-context-slots.js b/deps/v8/test/mjsunit/compiler/inline-context-slots.js new file mode 100644 index 0000000000..d0e907b1e5 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/inline-context-slots.js @@ -0,0 +1,49 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test inlining of functions with context slots. + +// Flags: --allow-natives-syntax + + +// Caller/callee without a local context. + +(function() { + var X = 5; + var Y = 10; + function F() {} + F.prototype.max = function() { + return X > Y ? X : Y; + } + F.prototype.run = function() { + return this.max(); + } + var f = new F(); + for (var i=0; i<5; i++) f.run(); + %OptimizeFunctionOnNextCall(f.run); + assertEquals(10, f.run()); +})(); diff --git a/deps/v8/test/mjsunit/compiler/lazy-const-lookup.js b/deps/v8/test/mjsunit/compiler/lazy-const-lookup.js new file mode 100644 index 0000000000..b4f15a1c9f --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/lazy-const-lookup.js @@ -0,0 +1,41 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function outer() { + const x = 1; + function inner() { + return x; + } + inner(); + %OptimizeFunctionOnNextCall(inner); + inner(); +} + +outer(); + diff --git a/deps/v8/test/mjsunit/compiler/math-floor-global.js b/deps/v8/test/mjsunit/compiler/math-floor-global.js new file mode 100644 index 0000000000..9ec183fab1 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/math-floor-global.js @@ -0,0 +1,161 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --max-new-space-size=256 --allow-natives-syntax + +// Test inlining of Math.floor when assigned to a global. +var flo = Math.floor; +var test_id = 0; + +function testFloor(expect, input) { + var test = new Function('n', + '"' + (test_id++) + '";return flo(n)'); + assertEquals(expect, test(input)); + assertEquals(expect, test(input)); + assertEquals(expect, test(input)); + %OptimizeFunctionOnNextCall(test); + assertEquals(expect, test(input)); +} + +function zero() { + var x = 0.5; + return (function() { return x - 0.5; })(); +} + +function test() { + testFloor(0, 0); + testFloor(0, zero()); + testFloor(-0, -0); + testFloor(Infinity, Infinity); + testFloor(-Infinity, -Infinity); + testFloor(NaN, NaN); + + // Ensure that a negative zero coming from Math.floor is properly handled + // by other operations. + function ifloor(x) { + return 1 / Math.floor(x); + } + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + %OptimizeFunctionOnNextCall(ifloor); + assertEquals(-Infinity, ifloor(-0)); + + testFloor(0, 0.1); + testFloor(0, 0.49999999999999994); + testFloor(0, 0.5); + testFloor(0, 0.7); + testFloor(-1, -0.1); + testFloor(-1, -0.49999999999999994); + testFloor(-1, -0.5); + testFloor(-1, -0.7); + testFloor(1, 1); + testFloor(1, 1.1); + testFloor(1, 1.5); + testFloor(1, 1.7); + testFloor(-1, -1); + testFloor(-2, -1.1); + testFloor(-2, -1.5); + testFloor(-2, -1.7); + + testFloor(0, Number.MIN_VALUE); + testFloor(-1, -Number.MIN_VALUE); + testFloor(Number.MAX_VALUE, Number.MAX_VALUE); + testFloor(-Number.MAX_VALUE, -Number.MAX_VALUE); + testFloor(Infinity, Infinity); + testFloor(-Infinity, -Infinity); + + // 2^30 is a smi boundary. + var two_30 = 1 << 30; + + testFloor(two_30, two_30); + testFloor(two_30, two_30 + 0.1); + testFloor(two_30, two_30 + 0.5); + testFloor(two_30, two_30 + 0.7); + + testFloor(two_30 - 1, two_30 - 1); + testFloor(two_30 - 1, two_30 - 1 + 0.1); + testFloor(two_30 - 1, two_30 - 1 + 0.5); + testFloor(two_30 - 1, two_30 - 1 + 0.7); + + testFloor(-two_30, -two_30); + testFloor(-two_30, -two_30 + 0.1); + testFloor(-two_30, -two_30 + 0.5); + testFloor(-two_30, -two_30 + 0.7); + + testFloor(-two_30 + 1, -two_30 + 1); + testFloor(-two_30 + 1, -two_30 + 1 + 0.1); + testFloor(-two_30 + 1, -two_30 + 1 + 0.5); + testFloor(-two_30 + 1, -two_30 + 1 + 0.7); + + // 2^52 is a precision boundary. + var two_52 = (1 << 30) * (1 << 22); + + testFloor(two_52, two_52); + testFloor(two_52, two_52 + 0.1); + assertEquals(two_52, two_52 + 0.5); + testFloor(two_52, two_52 + 0.5); + assertEquals(two_52 + 1, two_52 + 0.7); + testFloor(two_52 + 1, two_52 + 0.7); + + testFloor(two_52 - 1, two_52 - 1); + testFloor(two_52 - 1, two_52 - 1 + 0.1); + testFloor(two_52 - 1, two_52 - 1 + 0.5); + testFloor(two_52 - 1, two_52 - 1 + 0.7); + + testFloor(-two_52, -two_52); + testFloor(-two_52, -two_52 + 0.1); + testFloor(-two_52, -two_52 + 0.5); + testFloor(-two_52, -two_52 + 0.7); + + testFloor(-two_52 + 1, -two_52 + 1); + testFloor(-two_52 + 1, -two_52 + 1 + 0.1); + testFloor(-two_52 + 1, -two_52 + 1 + 0.5); + testFloor(-two_52 + 1, -two_52 + 1 + 0.7); +} + + +// Test in a loop to cover the custom IC and GC-related issues. +for (var i = 0; i < 50; i++) { + test(); +} + + +// Regression test for a bug where a negative zero coming from Math.floor +// was not properly handled by other operations. +function floorsum(i, n) { + var ret = Math.floor(n); + while (--i > 0) { + ret += Math.floor(n); + } + return ret; +} +assertEquals(-0, floorsum(1, -0)); +%OptimizeFunctionOnNextCall(floorsum); +// The optimized function will deopt. Run it with enough iterations to try +// to optimize via OSR (triggering the bug). +assertEquals(-0, floorsum(100000, -0)); diff --git a/deps/v8/test/mjsunit/compiler/math-floor-local.js b/deps/v8/test/mjsunit/compiler/math-floor-local.js new file mode 100644 index 0000000000..e44b15c734 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/math-floor-local.js @@ -0,0 +1,161 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --max-new-space-size=256 --allow-natives-syntax + +// Test inlining of Math.floor when assigned to a local. +var test_id = 0; + +function testFloor(expect, input) { + var test = new Function('n', + '"' + (test_id++) + + '";var f = Math.floor; return f(n)'); + assertEquals(expect, test(input)); + assertEquals(expect, test(input)); + assertEquals(expect, test(input)); + %OptimizeFunctionOnNextCall(test); + assertEquals(expect, test(input)); +} + +function zero() { + var x = 0.5; + return (function() { return x - 0.5; })(); +} + +function test() { + testFloor(0, 0); + testFloor(0, zero()); + testFloor(-0, -0); + testFloor(Infinity, Infinity); + testFloor(-Infinity, -Infinity); + testFloor(NaN, NaN); + + // Ensure that a negative zero coming from Math.floor is properly handled + // by other operations. + function ifloor(x) { + return 1 / Math.floor(x); + } + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + assertEquals(-Infinity, ifloor(-0)); + %OptimizeFunctionOnNextCall(ifloor); + assertEquals(-Infinity, ifloor(-0)); + + testFloor(0, 0.1); + testFloor(0, 0.49999999999999994); + testFloor(0, 0.5); + testFloor(0, 0.7); + testFloor(-1, -0.1); + testFloor(-1, -0.49999999999999994); + testFloor(-1, -0.5); + testFloor(-1, -0.7); + testFloor(1, 1); + testFloor(1, 1.1); + testFloor(1, 1.5); + testFloor(1, 1.7); + testFloor(-1, -1); + testFloor(-2, -1.1); + testFloor(-2, -1.5); + testFloor(-2, -1.7); + + testFloor(0, Number.MIN_VALUE); + testFloor(-1, -Number.MIN_VALUE); + testFloor(Number.MAX_VALUE, Number.MAX_VALUE); + testFloor(-Number.MAX_VALUE, -Number.MAX_VALUE); + testFloor(Infinity, Infinity); + testFloor(-Infinity, -Infinity); + + // 2^30 is a smi boundary. + var two_30 = 1 << 30; + + testFloor(two_30, two_30); + testFloor(two_30, two_30 + 0.1); + testFloor(two_30, two_30 + 0.5); + testFloor(two_30, two_30 + 0.7); + + testFloor(two_30 - 1, two_30 - 1); + testFloor(two_30 - 1, two_30 - 1 + 0.1); + testFloor(two_30 - 1, two_30 - 1 + 0.5); + testFloor(two_30 - 1, two_30 - 1 + 0.7); + + testFloor(-two_30, -two_30); + testFloor(-two_30, -two_30 + 0.1); + testFloor(-two_30, -two_30 + 0.5); + testFloor(-two_30, -two_30 + 0.7); + + testFloor(-two_30 + 1, -two_30 + 1); + testFloor(-two_30 + 1, -two_30 + 1 + 0.1); + testFloor(-two_30 + 1, -two_30 + 1 + 0.5); + testFloor(-two_30 + 1, -two_30 + 1 + 0.7); + + // 2^52 is a precision boundary. + var two_52 = (1 << 30) * (1 << 22); + + testFloor(two_52, two_52); + testFloor(two_52, two_52 + 0.1); + assertEquals(two_52, two_52 + 0.5); + testFloor(two_52, two_52 + 0.5); + assertEquals(two_52 + 1, two_52 + 0.7); + testFloor(two_52 + 1, two_52 + 0.7); + + testFloor(two_52 - 1, two_52 - 1); + testFloor(two_52 - 1, two_52 - 1 + 0.1); + testFloor(two_52 - 1, two_52 - 1 + 0.5); + testFloor(two_52 - 1, two_52 - 1 + 0.7); + + testFloor(-two_52, -two_52); + testFloor(-two_52, -two_52 + 0.1); + testFloor(-two_52, -two_52 + 0.5); + testFloor(-two_52, -two_52 + 0.7); + + testFloor(-two_52 + 1, -two_52 + 1); + testFloor(-two_52 + 1, -two_52 + 1 + 0.1); + testFloor(-two_52 + 1, -two_52 + 1 + 0.5); + testFloor(-two_52 + 1, -two_52 + 1 + 0.7); +} + + +// Test in a loop to cover the custom IC and GC-related issues. +for (var i = 0; i < 50; i++) { + test(); +} + + +// Regression test for a bug where a negative zero coming from Math.floor +// was not properly handled by other operations. +function floorsum(i, n) { + var ret = Math.floor(n); + while (--i > 0) { + ret += Math.floor(n); + } + return ret; +} +assertEquals(-0, floorsum(1, -0)); +%OptimizeFunctionOnNextCall(floorsum); +// The optimized function will deopt. Run it with enough iterations to try +// to optimize via OSR (triggering the bug). +assertEquals(-0, floorsum(100000, -0)); diff --git a/deps/v8/test/mjsunit/compiler/regress-96989.js b/deps/v8/test/mjsunit/compiler/regress-96989.js new file mode 100644 index 0000000000..aedeb24318 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/regress-96989.js @@ -0,0 +1,43 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Flags: --allow-natives-syntax + +// Test correct handling of uninitialized const. + +function test() { + for (var i = 41; i < 42; i++) { + var c = t ^ i; + } + const t; + return c; +} + +for (var i=0; i<10; i++) test(); +%OptimizeFunctionOnNextCall(test); +assertEquals(41, test()); diff --git a/deps/v8/test/mjsunit/compiler/regress-deopt-call-as-function.js b/deps/v8/test/mjsunit/compiler/regress-deopt-call-as-function.js new file mode 100644 index 0000000000..c408096955 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/regress-deopt-call-as-function.js @@ -0,0 +1,62 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test deoptimization after inlined call. + +function bar(a, b) {try { return a; } finally { } } + +function test_context() { + function foo(x) { return 42; } + var s, t; + for (var i = 0x7fff0000; i < 0x80000000; i++) { + bar(t = foo(i) ? bar(42 + i - i) : bar(0), s = i + t); + } + return s; +} +assertEquals(0x7fffffff + 42, test_context()); + + +function value_context() { + function foo(x) { return 42; } + var s, t; + for (var i = 0x7fff0000; i < 0x80000000; i++) { + bar(t = foo(i), s = i + t); + } + return s; +} +assertEquals(0x7fffffff + 42, value_context()); + + +function effect_context() { + function foo(x) { return 42; } + var s, t; + for (var i = 0x7fff0000; i < 0x80000000; i++) { + bar(foo(i), s = i + 42); + } + return s; +} +assertEquals(0x7fffffff + 42, effect_context()); diff --git a/deps/v8/test/mjsunit/compiler/regress-funarguments.js b/deps/v8/test/mjsunit/compiler/regress-funarguments.js index cea40bc9ba..c913bd9521 100644 --- a/deps/v8/test/mjsunit/compiler/regress-funarguments.js +++ b/deps/v8/test/mjsunit/compiler/regress-funarguments.js @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax + // Test function.arguments. function A() {} @@ -60,13 +62,16 @@ function hej(x) { return o.g(x, "z"); } -function stress() { - for (var i=0; i<5000000; i++) o.g(i, "g"); - for (var j=0; j<5000000; j++) hej(j); +function opt() { + for (var k=0; k<2; k++) { + for (var i=0; i<5; i++) o.g(i, "g"); + for (var j=0; j<5; j++) hej(j); + } + %OptimizeFunctionOnNextCall(o.g); + %OptimizeFunctionOnNextCall(hej); } -stress(); - +opt(); assertArrayEquals([0, "g"], o.g(0, "g")); assertArrayEquals([1, "f"], o.g(1, "g")); assertArrayEquals([0, "h"], hej(0)); @@ -74,8 +79,7 @@ assertArrayEquals([1, "f"], hej(1)); o = new B(); -stress(); - +opt(); assertArrayEquals([0, "f"], o.g(0, "g")); assertArrayEquals([1, "g"], o.g(1, "g")); assertArrayEquals([0, "f"], hej(0)); diff --git a/deps/v8/test/mjsunit/compiler/regress-inline-callfunctionstub.js b/deps/v8/test/mjsunit/compiler/regress-inline-callfunctionstub.js new file mode 100644 index 0000000000..a39d26df0e --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/regress-inline-callfunctionstub.js @@ -0,0 +1,46 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// Test inlined of calls-as-function two levels deep. +function f() { return 42; } + +var o = {g : function () { return f(); } } +function main(func) { + var v=0; + for (var i=0; i<1; i++) { + if (func()) v = 42; + } +} + +main(o.g); +main(o.g); +main(o.g); +%OptimizeFunctionOnNextCall(main); +main(o.g); + diff --git a/deps/v8/test/mjsunit/compiler/strict-recompile.js b/deps/v8/test/mjsunit/compiler/strict-recompile.js new file mode 100644 index 0000000000..96e8bcab78 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/strict-recompile.js @@ -0,0 +1,51 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function foo() { + try { + var o = {}; + Object.defineProperty(o, 'x', {value: 12, writable: false}); + o.x = 13; + } catch(e) { + return true; + } + return false; +} + +assertFalse(foo()); + +function do_eval(str) { + "use strict"; + return eval(str); +} + +var eval_foo = do_eval('(' + foo + ')'); +for (var i = 0; i < 5; i++) assertTrue(eval_foo()); +%OptimizeFunctionOnNextCall(eval_foo); +assertTrue(eval_foo()); diff --git a/deps/v8/test/mjsunit/const-redecl.js b/deps/v8/test/mjsunit/const-redecl.js index 945970891b..c0b97e6ced 100644 --- a/deps/v8/test/mjsunit/const-redecl.js +++ b/deps/v8/test/mjsunit/const-redecl.js @@ -98,7 +98,8 @@ function TestAll(expected,s,opt_e) { var msg = s; if (opt_e) { e = opt_e; msg += "; " + opt_e; } assertEquals(expected, TestLocal(s,e), "local:'" + msg + "'"); - assertEquals(expected, TestGlobal(s,e), "global:'" + msg + "'"); + // Redeclarations of global consts do not throw, they are silently ignored. + assertEquals(42, TestGlobal(s, 42), "global:'" + msg + "'"); assertEquals(expected, TestContext(s,e), "context:'" + msg + "'"); } @@ -218,3 +219,62 @@ TestAll(0, "var a,b,c,d,e,f,g,h; " + loop, "x"); // Test that const inside with behaves correctly. TestAll(87, "with ({x:42}) { const x = 87; }", "x"); TestAll(undefined, "with ({x:42}) { const x; }", "x"); + + +// Additional tests for how various combinations of re-declarations affect +// the values of the var/const in question. +try { + eval("var undefined;"); +} catch (ex) { + assertUnreachable("undefined (1) has thrown"); +} + +var original_undef = undefined; +var undefined = 1; // Should be silently ignored. +assertEquals(original_undef, undefined, "undefined got overwritten"); +undefined = original_undef; + +var a; const a; const a = 1; +assertEquals(1, a, "a has wrong value"); +a = 2; +assertEquals(2, a, "a should be writable"); + +var b = 1; const b = 2; +assertEquals(2, b, "b has wrong value"); + +var c = 1; const c = 2; const c = 3; +assertEquals(3, c, "c has wrong value"); + +const d = 1; const d = 2; +assertEquals(1, d, "d has wrong value"); + +const e = 1; var e = 2; +assertEquals(1, e, "e has wrong value"); + +const f = 1; const f; +assertEquals(1, f, "f has wrong value"); + +var g; const g = 1; +assertEquals(1, g, "g has wrong value"); +g = 2; +assertEquals(2, g, "g should be writable"); + +const h; var h = 1; +assertEquals(undefined,h, "h has wrong value"); + +eval("Object.defineProperty(this, 'i', { writable: true });" + + "const i = 7;" + + "assertEquals(7, i, \"i has wrong value\");"); + +var global = this; +assertThrows(function() { + Object.defineProperty(global, 'j', { writable: true }) +}, TypeError); +const j = 2; // This is what makes the function above throw, because the +// const declaration gets hoisted and makes the property non-configurable. +assertEquals(2, j, "j has wrong value"); + +var k = 1; const k; +// You could argue about the expected result here. For now, the winning +// argument is that "const k;" is equivalent to "const k = undefined;". +assertEquals(undefined, k, "k has wrong value"); diff --git a/deps/v8/test/mjsunit/cyclic-error-to-string.js b/deps/v8/test/mjsunit/cyclic-error-to-string.js deleted file mode 100644 index 2502b5340f..0000000000 --- a/deps/v8/test/mjsunit/cyclic-error-to-string.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Test printing of cyclic errors which return the empty string for -// compatibility with Safari and Firefox. - -var e = new Error(); -assertEquals('Error', e + ''); - -e = new Error(); -e.name = e; -e.message = e; -e.stack = e; -e.arguments = e; -assertEquals(': ', e + ''); - -e = new Error(); -e.name = [ e ]; -e.message = [ e ]; -e.stack = [ e ]; -e.arguments = [ e ]; -assertEquals(': ', e + ''); diff --git a/deps/v8/test/mjsunit/d8-os.js b/deps/v8/test/mjsunit/d8-os.js index 5640326856..239938cd16 100644 --- a/deps/v8/test/mjsunit/d8-os.js +++ b/deps/v8/test/mjsunit/d8-os.js @@ -54,6 +54,8 @@ function str_error(str) { if (this.os && os.system) { + // Ensure that we have a valid working directory. + os.chdir("/tmp"); try { // Delete the dir if it is lying around from last time. os.system("ls", [TEST_DIR]); @@ -61,52 +63,53 @@ if (this.os && os.system) { } catch (e) { } os.mkdirp(TEST_DIR); - os.chdir(TEST_DIR); try { // Check the chdir worked. os.system('ls', [TEST_DIR]); // Simple create dir. - os.mkdirp("dir"); + os.mkdirp(TEST_DIR + "/dir"); // Create dir in dir. - os.mkdirp("dir/foo"); + os.mkdirp(TEST_DIR + "/dir/foo"); // Check that they are there. - os.system('ls', ['dir/foo']); + os.system('ls', [TEST_DIR + '/dir/foo']); // Check that we can detect when something is not there. - assertThrows("os.system('ls', ['dir/bar']);", "dir not there"); + assertThrows("os.system('ls', [TEST_DIR + '/dir/bar']);", "dir not there"); // Check that mkdirp makes intermediate directories. - os.mkdirp("dir2/foo"); - os.system("ls", ["dir2/foo"]); + os.mkdirp(TEST_DIR + "/dir2/foo"); + os.system("ls", [TEST_DIR + "/dir2/foo"]); // Check that mkdirp doesn't mind if the dir is already there. - os.mkdirp("dir2/foo"); - os.mkdirp("dir2/foo/"); + os.mkdirp(TEST_DIR + "/dir2/foo"); + os.mkdirp(TEST_DIR + "/dir2/foo/"); // Check that mkdirp can cope with trailing / - os.mkdirp("dir3/"); - os.system("ls", ["dir3"]); + os.mkdirp(TEST_DIR + "/dir3/"); + os.system("ls", [TEST_DIR + "/dir3"]); // Check that we get an error if the name is taken by a file. - os.system("sh", ["-c", "echo foo > file1"]); - os.system("ls", ["file1"]); - assertThrows("os.mkdirp('file1');", "mkdir over file1"); - assertThrows("os.mkdirp('file1/foo');", "mkdir over file2"); - assertThrows("os.mkdirp('file1/');", "mkdir over file3"); - assertThrows("os.mkdirp('file1/foo/');", "mkdir over file4"); + os.system("sh", ["-c", "echo foo > " + TEST_DIR + "/file1"]); + os.system("ls", [TEST_DIR + "/file1"]); + assertThrows("os.mkdirp(TEST_DIR + '/file1');", "mkdir over file1"); + assertThrows("os.mkdirp(TEST_DIR + '/file1/foo');", "mkdir over file2"); + assertThrows("os.mkdirp(TEST_DIR + '/file1/');", "mkdir over file3"); + assertThrows("os.mkdirp(TEST_DIR + '/file1/foo/');", "mkdir over file4"); // Create a dir we cannot read. - os.mkdirp("dir4", 0); + os.mkdirp(TEST_DIR + "/dir4", 0); // This test fails if you are root since root can read any dir. - assertThrows("os.chdir('dir4');", "chdir dir4 I"); - os.rmdir("dir4"); - assertThrows("os.chdir('dir4');", "chdir dir4 II"); - // Set umask. + assertThrows("os.chdir(TEST_DIR + '/dir4');", "chdir dir4 I"); + os.rmdir(TEST_DIR + "/dir4"); + assertThrows("os.chdir(TEST_DIR + '/dir4');", "chdir dir4 II"); + + // Set umask. This changes the umask for the whole process and is + // the reason why the test cannot be run multi-threaded. var old_umask = os.umask(0777); // Create a dir we cannot read. - os.mkdirp("dir5"); + os.mkdirp(TEST_DIR + "/dir5"); // This test fails if you are root since root can read any dir. - assertThrows("os.chdir('dir5');", "cd dir5 I"); - os.rmdir("dir5"); - assertThrows("os.chdir('dir5');", "chdir dir5 II"); + assertThrows("os.chdir(TEST_DIR + '/dir5');", "cd dir5 I"); + os.rmdir(TEST_DIR + "/dir5"); + assertThrows("os.chdir(TEST_DIR + '/dir5');", "chdir dir5 II"); os.umask(old_umask); - os.mkdirp("hest/fisk/../fisk/ged"); - os.system("ls", ["hest/fisk/ged"]); + os.mkdirp(TEST_DIR + "/hest/fisk/../fisk/ged"); + os.system("ls", [TEST_DIR + "/hest/fisk/ged"]); os.setenv("FOO", "bar"); var environment = os.system("printenv"); @@ -143,42 +146,43 @@ if (this.os && os.system) { assertEquals("baz\n", os.system("echo", ["baz"])); //} } + + // Too few args. + arg_error("os.umask();"); + arg_error("os.system();"); + arg_error("os.mkdirp();"); + arg_error("os.chdir();"); + arg_error("os.setenv();"); + arg_error("os.rmdir();"); + + // Too many args. + arg_error("os.setenv('FOO=bar');"); + arg_error("os.umask(0, 0);"); + arg_error("os.system('ls', [], -1, -1, -1);"); + arg_error("os.mkdirp('foo', 0, 0)"); + arg_error("os.chdir('foo', 'bar')"); + arg_error("os.rmdir('foo', 'bar');"); + + // Wrong kind of args. + arg_error("os.umask([]);"); + arg_error("os.system('ls', 'foo');"); + arg_error("os.system('ls', 123);"); + arg_error("os.system('ls', [], 'foo');"); + arg_error("os.system('ls', [], -1, 'foo');"); + arg_error("os.mkdirp('foo', 'bar');"); + + // Test broken toString(). + str_error("os.system(e);"); + str_error("os.system('ls', [e]);"); + str_error("os.system('ls', ['.', e]);"); + str_error("os.system('ls', [e, '.']);"); + str_error("os.mkdirp(e);"); + str_error("os.setenv(e, 'goo');"); + str_error("os.setenv('goo', e);"); + str_error("os.chdir(e);"); + str_error("os.rmdir(e);"); + } finally { os.system("rm", ["-r", TEST_DIR]); } - - // Too few args. - arg_error("os.umask();"); - arg_error("os.system();"); - arg_error("os.mkdirp();"); - arg_error("os.chdir();"); - arg_error("os.setenv();"); - arg_error("os.rmdir();"); - - // Too many args. - arg_error("os.setenv('FOO=bar');"); - arg_error("os.umask(0, 0);"); - arg_error("os.system('ls', [], -1, -1, -1);"); - arg_error("os.mkdirp('foo', 0, 0)"); - arg_error("os.chdir('foo', 'bar')"); - arg_error("os.rmdir('foo', 'bar');"); - - // Wrong kind of args. - arg_error("os.umask([]);"); - arg_error("os.system('ls', 'foo');"); - arg_error("os.system('ls', 123);"); - arg_error("os.system('ls', [], 'foo');"); - arg_error("os.system('ls', [], -1, 'foo');"); - arg_error("os.mkdirp('foo', 'bar');"); - - // Test broken toString(). - str_error("os.system(e);"); - str_error("os.system('ls', [e]);"); - str_error("os.system('ls', ['.', e]);"); - str_error("os.system('ls', [e, '.']);"); - str_error("os.mkdirp(e);"); - str_error("os.setenv(e, 'goo');"); - str_error("os.setenv('goo', e);"); - str_error("os.chdir(e);"); - str_error("os.rmdir(e);"); } diff --git a/deps/v8/test/mjsunit/date.js b/deps/v8/test/mjsunit/date.js index a7f6cfa7d5..fa43cbb431 100644 --- a/deps/v8/test/mjsunit/date.js +++ b/deps/v8/test/mjsunit/date.js @@ -157,7 +157,7 @@ testToLocaleTimeString(); // Test that -0 is treated correctly in MakeDay. var d = new Date(); assertDoesNotThrow("d.setDate(-0)"); -assertDoesNotThrow("new Date(-0, -0, -0, -0, -0, -0. -0)"); +assertDoesNotThrow("new Date(-0, -0, -0, -0, -0, -0, -0)"); assertDoesNotThrow("new Date(0x40000000, 0x40000000, 0x40000000," + "0x40000000, 0x40000000, 0x40000000, 0x40000000)") assertDoesNotThrow("new Date(-0x40000001, -0x40000001, -0x40000001," + @@ -178,7 +178,7 @@ assertTrue(isNaN(Date.UTC(-271821, 3, 19, 23, 59, 59, 999))); assertTrue(isNaN(Date.UTC(-271821, 3, 19))); -// Test creation of large date values. +// Test creation with large date values. d = new Date(1969, 12, 1, 99999999999); assertTrue(isNaN(d.getTime())); d = new Date(1969, 12, 1, -99999999999); @@ -188,6 +188,12 @@ assertTrue(isNaN(d.getTime())); d = new Date(1969, 12, 1, -Infinity); assertTrue(isNaN(d.getTime())); + +// Test creation with obscure date values. +assertEquals(8640000000000000, Date.UTC(1970, 0, 1 + 100000001, -24)); +assertEquals(-8640000000000000, Date.UTC(1970, 0, 1 - 100000001, 24)); + + // Parsing ES5 ISO-8601 dates. // When TZ is omitted, it defaults to 'Z' meaning UTC. diff --git a/deps/v8/test/mjsunit/debug-break-inline.js b/deps/v8/test/mjsunit/debug-break-inline.js new file mode 100644 index 0000000000..4418fa8d1b --- /dev/null +++ b/deps/v8/test/mjsunit/debug-break-inline.js @@ -0,0 +1,100 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug --allow-natives-syntax + +// This test tests that deoptimization due to debug breaks works for +// inlined functions where the full-code is generated before the +// debugger is attached. +// +//See http://code.google.com/p/chromium/issues/detail?id=105375 + +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug; + +var count = 0; +var break_count = 0; + +// Debug event listener which sets a breakpoint first time it is hit +// and otherwise counts break points hit and checks that the expected +// state is reached. +function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + break_count++; + if (break_count == 1) { + Debug.setBreakPoint(g, 3); + + for (var i = 0; i < exec_state.frameCount(); i++) { + var frame = exec_state.frame(i); + // When function f is optimized (1 means YES, see runtime.cc) we + // expect an optimized frame for f and g. + if (%GetOptimizationStatus(f) == 1) { + if (i == 1) { + assertTrue(frame.isOptimizedFrame()); + assertTrue(frame.isInlinedFrame()); + assertEquals(4 - i, frame.inlinedFrameIndex()); + } else if (i == 2) { + assertTrue(frame.isOptimizedFrame()); + assertFalse(frame.isInlinedFrame()); + } else { + assertFalse(frame.isOptimizedFrame()); + assertFalse(frame.isInlinedFrame()); + } + } + } + } + } +} + +function f() { + g(); +} + +function g() { + count++; + h(); + var b = 1; // Break point is set here. +} + +function h() { + debugger; +} + +f();f();f(); +%OptimizeFunctionOnNextCall(f); +f(); + +// Add the debug event listener. +Debug.setListener(listener); + +f(); + +assertEquals(5, count); +assertEquals(2, break_count); + +// Get rid of the debug event listener. +Debug.setListener(null); diff --git a/deps/v8/test/mjsunit/debug-evaluate-locals-optimized-double.js b/deps/v8/test/mjsunit/debug-evaluate-locals-optimized-double.js index 8447df536d..7178661f25 100644 --- a/deps/v8/test/mjsunit/debug-evaluate-locals-optimized-double.js +++ b/deps/v8/test/mjsunit/debug-evaluate-locals-optimized-double.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,6 +34,27 @@ var exception = false; var testingConstructCall = false; +var input = [ + {a: 1, b: 2}, + {a: 3, b: 4}, + {a: 5, b: 6}, + {a: 7, b: 8}, + {a: 9, b: 10} +]; + +var expected = [ + { locals: {a0: 1.01, b0: 2.02}, args: { names: ["i", "x0", "y0"], values: [0, 3.03, 4.04] } }, + { locals: {a1: 3.03, b1: 4.04}, args: { names: ["i", "x1", "y1"], values: [1, 5.05, 6.06] } }, + { locals: {a2: 5.05, b2: 6.06}, args: { names: ["i"], values: [2] } }, + { locals: {a3: 7.07, b3: 8.08}, args: { names: ["i", "x3", "y3", "z3"], + values: [3, 9.09, 10.10, undefined] } + }, + { locals: {a4: 9.09, b4: 10.10}, args: { names: ["i", "x4", "y4"], values: [4, 11.11, 12.12] } } +]; + +function arraySum(arr) { + return arr.reduce(function (a, b) { return a + b; }, 0); +} function listener(event, exec_state, event_data, data) { try { @@ -44,42 +65,63 @@ function listener(event, exec_state, event_data, data) { for (var i = 0; i < exec_state.frameCount(); i++) { var frame = exec_state.frame(i); if (i < exec_state.frameCount() - 1) { - var expected_a = i * 2 + 1 + (i * 2 + 1) / 100; - var expected_b = i * 2 + 2 + (i * 2 + 2) / 100; - var expected_x = (i + 1) * 2 + 1 + ((i + 1) * 2 + 1) / 100; - var expected_y = (i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100; - - // All frames except the bottom one has normal variables a and b. - var a = ('a' === frame.localName(0)) ? 0 : 1; - var b = 1 - a; - assertEquals('a', frame.localName(a)); - assertEquals('b', frame.localName(b)); - assertEquals(expected_a, frame.localValue(a).value()); - assertEquals(expected_b, frame.localValue(b).value()); - - // All frames except the bottom one has arguments variables x and y. - assertEquals('x', frame.argumentName(0)); - assertEquals('y', frame.argumentName(1)); - assertEquals(expected_x, frame.argumentValue(0).value()); - assertEquals(expected_y, frame.argumentValue(1).value()); + var expected_args = expected[i].args; + var expected_locals = expected[i].locals; + + // All frames except the bottom one have expected locals. + var locals = {}; + for (var j = 0; j < frame.localCount(); j++) { + locals[frame.localName(j)] = frame.localValue(j).value(); + } + assertPropertiesEqual(expected_locals, locals); + + // All frames except the bottom one have expected arguments. + for (var j = 0; j < expected_args.names.length; j++) { + assertEquals(expected_args.names[j], frame.argumentName(j)); + assertEquals(expected_args.values[j], frame.argumentValue(j).value()); + } // All frames except the bottom one have two scopes. assertEquals(2, frame.scopeCount()); assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType()); assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType()); - assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']); - assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']); - assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']); - assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']); + + Object.keys(expected_locals).forEach(function (name) { + assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]); + }); + + for (var j = 0; j < expected_args.names.length; j++) { + var arg_name = expected_args.names[j]; + var arg_value = expected_args.values[j]; + assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]); + } // Evaluate in the inlined frame. - assertEquals(expected_a, frame.evaluate('a').value()); - assertEquals(expected_x, frame.evaluate('x').value()); - assertEquals(expected_x, frame.evaluate('arguments[0]').value()); - assertEquals(expected_a + expected_b + expected_x + expected_y, - frame.evaluate('a + b + x + y').value()); - assertEquals(expected_x + expected_y, - frame.evaluate('arguments[0] + arguments[1]').value()); + Object.keys(expected_locals).forEach(function (name) { + assertEquals(expected_locals[name], frame.evaluate(name).value()); + }); + + for (var j = 0; j < expected_args.names.length; j++) { + var arg_name = expected_args.names[j]; + var arg_value = expected_args.values[j]; + assertEquals(arg_value, frame.evaluate(arg_name).value()); + assertEquals(arg_value, frame.evaluate('arguments['+j+']').value()); + } + + var expected_args_sum = arraySum(expected_args.values); + var expected_locals_sum = + arraySum(Object.keys(expected_locals). + map(function (k) { return expected_locals[k]; })); + + assertEquals(expected_locals_sum + expected_args_sum, + frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' + + expected_args.names.join('+')).value()); + + var arguments_sum = expected_args.names.map(function(_, idx) { + return "arguments[" + idx + "]"; + }).join('+'); + assertEquals(expected_args_sum, + frame.evaluate(arguments_sum).value()); } else { // The bottom frame only have the global scope. assertEquals(1, frame.scopeCount()); @@ -121,62 +163,64 @@ function listener(event, exec_state, event_data, data) { listenerComplete = true; } } catch (e) { - exception = e + exception = e.toString() + e.stack; }; }; -f();f();f(); +for (var i = 0; i < 4; i++) f(input.length - 1, 11.11, 12.12); %OptimizeFunctionOnNextCall(f); -f(); +f(input.length - 1, 11.11, 12.12); // Add the debug event listener. Debug.setListener(listener); -function h(x, y) { - var a = 1; - var b = 2; - a = a + a / 100; - b = b + b / 100; +function h(i, x0, y0) { + var a0 = input[i].a; + var b0 = input[i].b; + a0 = a0 + a0 / 100; + b0 = b0 + b0 / 100; debugger; // Breakpoint. }; -function g3(x, y) { - var a = 3; - var b = 4; - a = a + a / 100; - b = b + b / 100; - h(a, b); - return a+b; +function g3(i, x1, y1) { + var a1 = input[i].a; + var b1 = input[i].b; + a1 = a1 + a1 / 100; + b1 = b1 + b1 / 100; + h(i - 1, a1, b1); + return a1+b1; }; -function g2(x, y) { - var a = 5; - var b = 6; - a = a + a / 100; - b = b + b / 100; - g3(a, b); +function g2(i) { + var a2 = input[i].a; + var b2 = input[i].b; + a2 = a2 + a2 / 100; + b2 = b2 + b2 / 100; + g3(i - 1, a2, b2); }; -function g1(x, y) { - var a = 7; - var b = 8; - a = a + a / 100; - b = b + b / 100; - g2(a, b); +function g1(i, x3, y3, z3) { + var a3 = input[i].a; + var b3 = input[i].b; + a3 = a3 + a3 / 100; + b3 = b3 + b3 / 100; + g2(i - 1, a3, b3); }; -function f(x, y) { - var a = 9; - var b = 10; - a = a + a / 100; - b = b + b / 100; - g1(a, b); +function f(i, x4, y4) { + var a4 = input[i].a; + var b4 = input[i].b; + a4 = a4 + a4 / 100; + b4 = b4 + b4 / 100; + g1(i - 1, a4, b4); }; // Test calling f normally and as a constructor. -f(11.11, 12.12); +f(input.length - 1, 11.11, 12.12); +f(input.length - 1, 11.11, 12.12, ""); testingConstructCall = true; -new f(11.11, 12.12); +new f(input.length - 1, 11.11, 12.12); +new f(input.length - 1, 11.11, 12.12, ""); // Make sure that the debug event listener vas invoked. assertFalse(exception, "exception in listener " + exception) diff --git a/deps/v8/test/mjsunit/debug-evaluate-locals-optimized.js b/deps/v8/test/mjsunit/debug-evaluate-locals-optimized.js index c3cd5eb2eb..485f752f5c 100644 --- a/deps/v8/test/mjsunit/debug-evaluate-locals-optimized.js +++ b/deps/v8/test/mjsunit/debug-evaluate-locals-optimized.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -34,6 +34,17 @@ var exception = false; var testingConstructCall = false; +var expected = [ + { locals: {a0: 1, b0: 2}, args: { names: ["i", "x0", "y0"], values: [0, 3, 4] } }, + { locals: {a1: 3, b1: 4}, args: { names: ["i", "x1", "y1"], values: [1, 5, 6] } }, + { locals: {a2: 5, b2: 6}, args: { names: ["i"], values: [2] } }, + { locals: {a3: 7, b3: 8}, args: { names: ["i", "x3", "y3", "z3"], values: [3, 9, 10, undefined] } }, + { locals: {a4: 9, b4: 10}, args: { names: ["i", "x4", "y4"], values: [4, 11, 12] } } +]; + +function arraySum(arr) { + return arr.reduce(function (a, b) { return a + b; }, 0); +} function listener(event, exec_state, event_data, data) { try { @@ -44,42 +55,63 @@ function listener(event, exec_state, event_data, data) { for (var i = 0; i < exec_state.frameCount(); i++) { var frame = exec_state.frame(i); if (i < exec_state.frameCount() - 1) { - var expected_a = i * 2 + 1; - var expected_b = i * 2 + 2; - var expected_x = (i + 1) * 2 + 1; - var expected_y = (i + 1) * 2 + 2; - - // All frames except the bottom one has normal variables a and b. - var a = ('a' === frame.localName(0)) ? 0 : 1; - var b = 1 - a; - assertEquals('a', frame.localName(a)); - assertEquals('b', frame.localName(b)); - assertEquals(expected_a, frame.localValue(a).value()); - assertEquals(expected_b, frame.localValue(b).value()); - - // All frames except the bottom one has arguments variables x and y. - assertEquals('x', frame.argumentName(0)); - assertEquals('y', frame.argumentName(1)); - assertEquals(expected_x, frame.argumentValue(0).value()); - assertEquals(expected_y, frame.argumentValue(1).value()); + var expected_args = expected[i].args; + var expected_locals = expected[i].locals; + + // All frames except the bottom one have expected locals. + var locals = {}; + for (var j = 0; j < frame.localCount(); j++) { + locals[frame.localName(j)] = frame.localValue(j).value(); + } + assertPropertiesEqual(expected_locals, locals); + + // All frames except the bottom one have expected arguments. + for (var j = 0; j < expected_args.names.length; j++) { + assertEquals(expected_args.names[j], frame.argumentName(j)); + assertEquals(expected_args.values[j], frame.argumentValue(j).value()); + } // All frames except the bottom one have two scopes. assertEquals(2, frame.scopeCount()); assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType()); assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType()); - assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']); - assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']); - assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']); - assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']); + + Object.keys(expected_locals).forEach(function (name) { + assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]); + }); + + for (var j = 0; j < expected_args.names.length; j++) { + var arg_name = expected_args.names[j]; + var arg_value = expected_args.values[j]; + assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]); + } // Evaluate in the inlined frame. - assertEquals(expected_a, frame.evaluate('a').value()); - assertEquals(expected_x, frame.evaluate('x').value()); - assertEquals(expected_x, frame.evaluate('arguments[0]').value()); - assertEquals(expected_a + expected_b + expected_x + expected_y, - frame.evaluate('a + b + x + y').value()); - assertEquals(expected_x + expected_y, - frame.evaluate('arguments[0] + arguments[1]').value()); + Object.keys(expected_locals).forEach(function (name) { + assertEquals(expected_locals[name], frame.evaluate(name).value()); + }); + + for (var j = 0; j < expected_args.names.length; j++) { + var arg_name = expected_args.names[j]; + var arg_value = expected_args.values[j]; + assertEquals(arg_value, frame.evaluate(arg_name).value()); + assertEquals(arg_value, frame.evaluate('arguments['+j+']').value()); + } + + var expected_args_sum = arraySum(expected_args.values); + var expected_locals_sum = + arraySum(Object.keys(expected_locals). + map(function (k) { return expected_locals[k]; })); + + assertEquals(expected_locals_sum + expected_args_sum, + frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' + + expected_args.names.join('+')).value()); + + var arguments_sum = expected_args.names.map(function(_, idx) { + return "arguments[" + idx + "]"; + }).join('+'); + assertEquals(expected_args_sum, + frame.evaluate(arguments_sum).value()); } else { // The bottom frame only have the global scope. assertEquals(1, frame.scopeCount()); @@ -121,51 +153,53 @@ function listener(event, exec_state, event_data, data) { listenerComplete = true; } } catch (e) { - exception = e.stack; + exception = e.toString() + e.stack; }; }; -f();f();f(); +for (var i = 0; i < 4; i++) f(expected.length - 1, 11, 12); %OptimizeFunctionOnNextCall(f); -f(); +f(expected.length - 1, 11, 12); // Add the debug event listener. Debug.setListener(listener); -function h(x, y) { - var a = 1; - var b = 2; +function h(i, x0, y0) { + var a0 = expected[i].locals.a0; + var b0 = expected[i].locals.b0; debugger; // Breakpoint. -}; - -function g3(x, y) { - var a = 3; - var b = 4; - h(a, b); -}; - -function g2(x, y) { - var a = 5; - var b = 6; - g3(a, b); -}; - -function g1(x, y) { - var a = 7; - var b = 8; - g2(a, b); -}; - -function f(x, y) { - var a = 9; - var b = 10; - g1(a, b); -}; +} + +function g3(i, x1, y1) { + var a1 = expected[i].locals.a1; + var b1 = expected[i].locals.b1; + h(i - 1, a1, b1); +} + +function g2(i) { + var a2 = expected[i].locals.a2; + var b2 = expected[i].locals.b2; + g3(i - 1, a2, b2); +} + +function g1(i, x3, y3, z3) { + var a3 = expected[i].locals.a3; + var b3 = expected[i].locals.b3; + g2(i - 1, a3, b3); +} + +function f(i, x4, y4) { + var a4 = expected[i].locals.a4; + var b4 = expected[i].locals.b4; + g1(i - 1, a4, b4); +} // Test calling f normally and as a constructor. -f(11, 12); +f(expected.length - 1, 11, 12); +f(expected.length - 1, 11, 12, 0); testingConstructCall = true; -new f(11, 12); +new f(expected.length - 1, 11, 12); +new f(expected.length - 1, 11, 12, 0); // Make sure that the debug event listener vas invoked. assertFalse(exception, "exception in listener " + exception) diff --git a/deps/v8/test/mjsunit/debug-scopes.js b/deps/v8/test/mjsunit/debug-scopes.js index 1c23b0bf99..942bd2bb07 100644 --- a/deps/v8/test/mjsunit/debug-scopes.js +++ b/deps/v8/test/mjsunit/debug-scopes.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,13 +25,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug +// Flags: --expose-debug-as debug --allow-natives-syntax // The functions used for testing backtraces. They are at the top to make the // testing of source line/column easier. - // Get the Debug object exposed from the debug context global object. -Debug = debug.Debug; +var Debug = debug.Debug; var test_name; var listener_delegate; @@ -439,6 +438,26 @@ with(with_object) { EndTest(); +// With block in function that is marked for optimization while being executed. +BeginTest("With 7"); + +function with_7() { + with({}) { + %OptimizeFunctionOnNextCall(with_7); + debugger; + } +} + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.With, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({}, 0, exec_state); +}; +with_7(); +EndTest(); + + // Simple closure formed by returning an inner function referering the outer // functions arguments. BeginTest("Closure 1"); @@ -950,6 +969,28 @@ try { EndTest(); +// Catch block in function that is marked for optimization while being executed. +BeginTest("Catch block 7"); +function catch_block_7() { + %OptimizeFunctionOnNextCall(catch_block_7); + try { + throw 'Exception'; + } catch (e) { + debugger; + } +}; + + +listener_delegate = function(exec_state) { + CheckScopeChain([debug.ScopeType.Catch, + debug.ScopeType.Local, + debug.ScopeType.Global], exec_state); + CheckScopeContent({e:'Exception'}, 0, exec_state); +}; +catch_block_7(); +EndTest(); + + assertEquals(begin_test_count, break_count, 'one or more tests did not enter the debugger'); assertEquals(begin_test_count, end_test_count, diff --git a/deps/v8/test/mjsunit/debug-step-3.js b/deps/v8/test/mjsunit/debug-step-3.js new file mode 100644 index 0000000000..9cac0f5619 --- /dev/null +++ b/deps/v8/test/mjsunit/debug-step-3.js @@ -0,0 +1,94 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug + +// This test tests that full code compiled without debug break slots +// is recompiled with debug break slots when debugging is started. + +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug + +var bp; +var done = false; +var step_count = 0; +var set_bp = false + +// Debug event listener which steps until the global variable done is true. +function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + if (!done) exec_state.prepareStep(Debug.StepAction.StepNext); + step_count++; + } +}; + +// Set the global variables state to prpare the stepping test. +function prepare_step_test() { + done = false; + step_count = 0; +} + +// Test function to step through. +function f() { + var a = 0; + if (set_bp) { bp = Debug.setBreakPoint(f, 3); } + var i = 1; + var j = 2; + done = true; +}; + +prepare_step_test(); +f(); + +// Add the debug event listener. +Debug.setListener(listener); + +// Make f set a breakpoint with an activation on the stack. +prepare_step_test(); +set_bp = true; +f(); +// TODO(1782): Fix issue to bring back this assert. +//assertEquals(4, step_count); +Debug.clearBreakPoint(bp); + +// Set a breakpoint on the first var statement (line 1). +set_bp = false; +bp = Debug.setBreakPoint(f, 3); + +// Step through the function ensuring that the var statements are hit as well. +prepare_step_test(); +f(); +assertEquals(4, step_count); + +// Clear the breakpoint and check that no stepping happens. +Debug.clearBreakPoint(bp); +prepare_step_test(); +f(); +assertEquals(0, step_count); + +// Get rid of the debug event listener. +Debug.setListener(null); diff --git a/deps/v8/test/mjsunit/debug-stepout-scope.js b/deps/v8/test/mjsunit/debug-stepout-scope.js new file mode 100644 index 0000000000..9c040da932 --- /dev/null +++ b/deps/v8/test/mjsunit/debug-stepout-scope.js @@ -0,0 +1,423 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug --expose-natives-as=builtins + +// Check that the ScopeIterator can properly recreate the scope at +// every point when stepping through functions. + +var Debug = debug.Debug; + +function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + // Access scope details. + var scope_count = exec_state.frame().scopeCount(); + for (var i = 0; i < scope_count; i++) { + var scope = exec_state.frame().scope(i); + // assertTrue(scope.isScope()); + scope.scopeType(); + scope.scopeObject(); + } + + // Do steps until we reach the global scope again. + if (true) { + exec_state.prepareStep(Debug.StepAction.StepInMin, 1); + } + } +} + +Debug.setListener(listener); + + +function test1() { + debugger; + with ({x:1}) { + x = 2; + } +} +test1(); + + +function test2() { + if (true) { + with ({}) { + debugger; + } + } else { + with ({}) { + return 10; + } + } +} +test2(); + + +function test3() { + if (true) { + debugger; + } else { + with ({}) { + return 10; + } + } +} +test3(); + + +function test4() { + debugger; + with ({x:1}) x = 1 +} +test4(); + + +function test5() { + debugger; + var dummy = 1; + with ({}) { + with ({}) { + dummy = 2; + } + } + dummy = 3; +} +test5(); + + +function test6() { + debugger; + try { + throw 'stuff'; + } catch (e) { + e = 1; + } +} +test6(); + + +function test7() { + debugger; + function foo() {} +} +test7(); + + +function test8() { + debugger; + (function foo() {})(); +} +test8(); + + +var q = 42; +var prefixes = [ "debugger; ", + "if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; +var bodies = [ "1", + "1 ", + "1;", + "1; ", + "q", + "q ", + "q;", + "q; ", + "try { throw 'stuff' } catch (e) { e = 1; }", + "try { throw 'stuff' } catch (e) { e = 1; } ", + "try { throw 'stuff' } catch (e) { e = 1; };", + "try { throw 'stuff' } catch (e) { e = 1; }; " ]; +var with_bodies = [ "with ({}) {}", + "with ({x:1}) x", + "with ({x:1}) x = 1", + "with ({x:1}) x ", + "with ({x:1}) x = 1 ", + "with ({x:1}) x;", + "with ({x:1}) x = 1;", + "with ({x:1}) x; ", + "with ({x:1}) x = 1; " ]; + + +function test9() { + debugger; + for (var i = 0; i < prefixes.length; ++i) { + var pre = prefixes[i]; + for (var j = 0; j < bodies.length; ++j) { + var body = bodies[j]; + eval(pre + body); + eval("'use strict'; " + pre + body); + } + for (var j = 0; j < with_bodies.length; ++j) { + var body = with_bodies[j]; + eval(pre + body); + } + } +} +test9(); + + +function test10() { + debugger; + with ({}) { + return 10; + } +} +test10(); + + +function test11() { + debugger; + try { + throw 'stuff'; + } catch (e) { + return 10; + } +} +test11(); + + +// Test global eval and function constructor. +for (var i = 0; i < prefixes.length; ++i) { + var pre = prefixes[i]; + for (var j = 0; j < bodies.length; ++j) { + var body = bodies[j]; + eval(pre + body); + eval("'use strict'; " + pre + body); + Function(pre + body)(); + } + for (var j = 0; j < with_bodies.length; ++j) { + var body = with_bodies[j]; + eval(pre + body); + Function(pre + body)(); + } +} + + +try { + with({}) { + debugger; + eval("{}$%:^"); + } +} catch(e) { + nop(); +} + +// Return from function constructed with Function constructor. +var anon = 12; +for (var i = 0; i < prefixes.length; ++i) { + var pre = prefixes[i]; + Function(pre + "return 42")(); + Function(pre + "return 42 ")(); + Function(pre + "return 42;")(); + Function(pre + "return 42; ")(); + Function(pre + "return anon")(); + Function(pre + "return anon ")(); + Function(pre + "return anon;")(); + Function(pre + "return anon; ")(); +} + + +function nop() {} + + +function stress() { + debugger; + + L: with ({x:12}) { + break L; + } + + + with ({x: 'outer'}) { + label: { + with ({x: 'inner'}) { + break label; + } + } + } + + + with ({x: 'outer'}) { + label: { + with ({x: 'inner'}) { + break label; + } + } + nop(); + } + + + with ({x: 'outer'}) { + label: { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + break label; + } + } + } + } + + + with ({x: 'outer'}) { + label: { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + break label; + } + } + } + nop(); + } + + + with ({x: 'outer'}) { + for (var i = 0; i < 3; ++i) { + with ({x: 'inner' + i}) { + continue; + } + } + } + + + with ({x: 'outer'}) { + label: for (var i = 0; i < 3; ++i) { + with ({x: 'middle' + i}) { + for (var j = 0; j < 3; ++j) { + with ({x: 'inner' + j}) { + continue label; + } + } + } + } + } + + + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + } + } + + + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + nop(); + } + } + + + with ({x: 'outer'}) { + try { + with ({x: 'middle'}) { + with ({x: 'inner'}) { + throw 0; + } + } + } catch (e) { + } + } + + + try { + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } finally { + } + } + } catch (e) { + } + + + try { + with ({x: 'outer'}) { + try { + with ({x: 'inner'}) { + throw 0; + } + } finally { + nop(); + } + } + } catch (e) { + } + + + function stress1() { + with ({x:12}) { + return x; + } + } + stress1(); + + + function stress2() { + with ({x: 'outer'}) { + with ({x: 'inner'}) { + return x; + } + } + } + stress2(); + + function stress3() { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + return e; + } + } + stress3(); + + + function stress4() { + try { + with ({x: 'inner'}) { + throw 0; + } + } catch (e) { + with ({x: 'inner'}) { + return e; + } + } + } + stress4(); + +} +stress(); + + +// With block as the last(!) statement in global code. +with ({}) { debugger; }
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/element-kind.js b/deps/v8/test/mjsunit/element-kind.js deleted file mode 100644 index 48a029f27e..0000000000 --- a/deps/v8/test/mjsunit/element-kind.js +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Flags: --allow-natives-syntax -// Test element kind of objects - -var element_kind = { - fast_elements : 1, - fast_double_elements : 2, - dictionary_elements : 3, - external_byte_elements : 4, - external_unsigned_byte_elements : 5, - external_short_elements : 6, - external_unsigned_short_elements : 7, - external_int_elements : 8, - external_unsigned_int_elements : 9, - external_float_elements : 10, - external_double_elements : 11, - external_pixel_elements : 12 -} - -// We expect an object to only be of one element kind. -function assertKind(expected, obj){ - assertEquals(expected == element_kind.fast_elements, - %HasFastElements(obj)); - assertEquals(expected == element_kind.fast_double_elements, - %HasFastDoubleElements(obj)); - assertEquals(expected == element_kind.dictionary_elements, - %HasDictionaryElements(obj)); - assertEquals(expected == element_kind.external_byte_elements, - %HasExternalByteElements(obj)); - assertEquals(expected == element_kind.external_unsigned_byte_elements, - %HasExternalUnsignedByteElements(obj)); - assertEquals(expected == element_kind.external_short_elements, - %HasExternalShortElements(obj)); - assertEquals(expected == element_kind.external_unsigned_short_elements, - %HasExternalUnsignedShortElements(obj)); - assertEquals(expected == element_kind.external_int_elements, - %HasExternalIntElements(obj)); - assertEquals(expected == element_kind.external_unsigned_int_elements, - %HasExternalUnsignedIntElements(obj)); - assertEquals(expected == element_kind.external_float_elements, - %HasExternalFloatElements(obj)); - assertEquals(expected == element_kind.external_double_elements, - %HasExternalDoubleElements(obj)); - assertEquals(expected == element_kind.external_pixel_elements, - %HasExternalPixelElements(obj)); - // every external kind is also an external array - assertEquals(expected >= element_kind.external_byte_elements, - %HasExternalArrayElements(obj)); -} - -var me = {}; -assertKind(element_kind.fast_elements, me); -me.dance = 0xD15C0; -me.drink = 0xC0C0A; -assertKind(element_kind.fast_elements, me); - -var you = new Array(); -for(i = 0; i < 1337; i++) { - you[i] = i; -} -assertKind(element_kind.fast_elements, you); - -assertKind(element_kind.dictionary_elements, new Array(0xC0C0A)); - -// fast_double_elements not yet available - - -assertKind(element_kind.external_byte_elements, new Int8Array(9001)); -assertKind(element_kind.external_unsigned_byte_elements, new Uint8Array(007)); -assertKind(element_kind.external_short_elements, new Int16Array(666)); -assertKind(element_kind.external_unsigned_short_elements, new Uint16Array(42)); -assertKind(element_kind.external_int_elements, new Int32Array(0xF)); -assertKind(element_kind.external_unsigned_int_elements, new Uint32Array(23)); -assertKind(element_kind.external_float_elements, new Float32Array(7)); -assertKind(element_kind.external_double_elements, new Float64Array(0)); -assertKind(element_kind.external_pixel_elements, new PixelArray(512)); diff --git a/deps/v8/test/mjsunit/elements-kind-depends.js b/deps/v8/test/mjsunit/elements-kind-depends.js new file mode 100644 index 0000000000..82f188b71e --- /dev/null +++ b/deps/v8/test/mjsunit/elements-kind-depends.js @@ -0,0 +1,74 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays + +function burn() { + var a = new Array(3); + a[0] = 10; + a[1] = 15.5; + a[2] = 20; + return a; +} + +function check(a) { + assertEquals(10, a[0]); + assertEquals(15.5, a[1]); + assertEquals(20, a[2]); +} + +var b; +for (var i = 0; i < 3; ++i) { + b = burn(); + check(b); // all OK +} +%OptimizeFunctionOnNextCall(burn); +b = burn(); +check(b); // fails + + +function loop_test(x) { + for (i=0;i<3;i++) { + x[i] = (i+1) * 0.5; + } +} + +function check2(b) { + assertEquals(0.5, b[0]); + assertEquals(1.0, b[1]); + assertEquals(1.5, b[2]); +} + +for (var i = 0; i < 3; ++i) { + b = [0,1,2]; + loop_test(b); + check2(b); +} +%OptimizeFunctionOnNextCall(loop_test); +b = [0,1,2]; +loop_test(b); +check2(b); diff --git a/deps/v8/test/mjsunit/elements-kind.js b/deps/v8/test/mjsunit/elements-kind.js new file mode 100644 index 0000000000..c0bc333a6b --- /dev/null +++ b/deps/v8/test/mjsunit/elements-kind.js @@ -0,0 +1,344 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc + +// Test element kind of objects. +// Since --smi-only-arrays affects builtins, its default setting at compile +// time sticks if built with snapshot. If --smi-only-arrays is deactivated +// by default, only a no-snapshot build actually has smi-only arrays enabled +// in this test case. Depending on whether smi-only arrays are actually +// enabled, this test takes the appropriate code path to check smi-only arrays. + +support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); + +if (support_smi_only_arrays) { + print("Tests include smi-only arrays."); +} else { + print("Tests do NOT include smi-only arrays."); +} + +var elements_kind = { + fast_smi_only : 'fast smi only elements', + fast : 'fast elements', + fast_double : 'fast double elements', + dictionary : 'dictionary elements', + external_byte : 'external byte elements', + external_unsigned_byte : 'external unsigned byte elements', + external_short : 'external short elements', + external_unsigned_short : 'external unsigned short elements', + external_int : 'external int elements', + external_unsigned_int : 'external unsigned int elements', + external_float : 'external float elements', + external_double : 'external double elements', + external_pixel : 'external pixel elements' +} + +function getKind(obj) { + if (%HasFastSmiOnlyElements(obj)) return elements_kind.fast_smi_only; + if (%HasFastElements(obj)) return elements_kind.fast; + if (%HasFastDoubleElements(obj)) return elements_kind.fast_double; + if (%HasDictionaryElements(obj)) return elements_kind.dictionary; + // Every external kind is also an external array. + assertTrue(%HasExternalArrayElements(obj)); + if (%HasExternalByteElements(obj)) { + return elements_kind.external_byte; + } + if (%HasExternalUnsignedByteElements(obj)) { + return elements_kind.external_unsigned_byte; + } + if (%HasExternalShortElements(obj)) { + return elements_kind.external_short; + } + if (%HasExternalUnsignedShortElements(obj)) { + return elements_kind.external_unsigned_short; + } + if (%HasExternalIntElements(obj)) { + return elements_kind.external_int; + } + if (%HasExternalUnsignedIntElements(obj)) { + return elements_kind.external_unsigned_int; + } + if (%HasExternalFloatElements(obj)) { + return elements_kind.external_float; + } + if (%HasExternalDoubleElements(obj)) { + return elements_kind.external_double; + } + if (%HasExternalPixelElements(obj)) { + return elements_kind.external_pixel; + } +} + +function assertKind(expected, obj, name_opt) { + if (!support_smi_only_arrays && + expected == elements_kind.fast_smi_only) { + expected = elements_kind.fast; + } + assertEquals(expected, getKind(obj), name_opt); +} + +var me = {}; +assertKind(elements_kind.fast, me); +me.dance = 0xD15C0; +me.drink = 0xC0C0A; +assertKind(elements_kind.fast, me); + +if (support_smi_only_arrays) { + var too = [1,2,3]; + assertKind(elements_kind.fast_smi_only, too); + too.dance = 0xD15C0; + too.drink = 0xC0C0A; + assertKind(elements_kind.fast_smi_only, too); +} + +// Make sure the element kind transitions from smionly when a non-smi is stored. +var you = new Array(); +assertKind(elements_kind.fast_smi_only, you); +for (var i = 0; i < 1337; i++) { + var val = i; + if (i == 1336) { + assertKind(elements_kind.fast_smi_only, you); + val = new Object(); + } + you[i] = val; +} +assertKind(elements_kind.fast, you); + +assertKind(elements_kind.dictionary, new Array(0xDECAF)); + +var fast_double_array = new Array(0xDECAF); +for (var i = 0; i < 0xDECAF; i++) fast_double_array[i] = i / 2; +assertKind(elements_kind.fast_double, fast_double_array); + +assertKind(elements_kind.external_byte, new Int8Array(9001)); +assertKind(elements_kind.external_unsigned_byte, new Uint8Array(007)); +assertKind(elements_kind.external_short, new Int16Array(666)); +assertKind(elements_kind.external_unsigned_short, new Uint16Array(42)); +assertKind(elements_kind.external_int, new Int32Array(0xF)); +assertKind(elements_kind.external_unsigned_int, new Uint32Array(23)); +assertKind(elements_kind.external_float, new Float32Array(7)); +assertKind(elements_kind.external_double, new Float64Array(0)); +assertKind(elements_kind.external_pixel, new PixelArray(512)); + +// Crankshaft support for smi-only array elements. +function monomorphic(array) { + for (var i = 0; i < 3; i++) { + array[i] = i + 10; + } + assertKind(elements_kind.fast_smi_only, array); + for (var i = 0; i < 3; i++) { + var a = array[i]; + assertEquals(i + 10, a); + } +} +var smi_only = new Array(1, 2, 3); +for (var i = 0; i < 3; i++) monomorphic(smi_only); +%OptimizeFunctionOnNextCall(monomorphic); +monomorphic(smi_only); + +if (support_smi_only_arrays) { + function construct_smis() { + var a = [0, 0, 0]; + a[0] = 0; // Send the COW array map to the steak house. + assertKind(elements_kind.fast_smi_only, a); + return a; + } + function construct_doubles() { + var a = construct_smis(); + a[0] = 1.5; + assertKind(elements_kind.fast_double, a); + return a; + } + function construct_objects() { + var a = construct_smis(); + a[0] = "one"; + assertKind(elements_kind.fast, a); + return a; + } + + // Test crankshafted transition SMI->DOUBLE. + function convert_to_double(array) { + array[1] = 2.5; + assertKind(elements_kind.fast_double, array); + assertEquals(2.5, array[1]); + } + var smis = construct_smis(); + for (var i = 0; i < 3; i++) convert_to_double(smis); + %OptimizeFunctionOnNextCall(convert_to_double); + smis = construct_smis(); + convert_to_double(smis); + // Test crankshafted transitions SMI->FAST and DOUBLE->FAST. + function convert_to_fast(array) { + array[1] = "two"; + assertKind(elements_kind.fast, array); + assertEquals("two", array[1]); + } + smis = construct_smis(); + for (var i = 0; i < 3; i++) convert_to_fast(smis); + var doubles = construct_doubles(); + for (var i = 0; i < 3; i++) convert_to_fast(doubles); + smis = construct_smis(); + doubles = construct_doubles(); + %OptimizeFunctionOnNextCall(convert_to_fast); + convert_to_fast(smis); + convert_to_fast(doubles); + // Test transition chain SMI->DOUBLE->FAST (crankshafted function will + // transition to FAST directly). + function convert_mixed(array, value, kind) { + array[1] = value; + assertKind(kind, array); + assertEquals(value, array[1]); + } + smis = construct_smis(); + for (var i = 0; i < 3; i++) { + convert_mixed(smis, 1.5, elements_kind.fast_double); + } + doubles = construct_doubles(); + for (var i = 0; i < 3; i++) { + convert_mixed(doubles, "three", elements_kind.fast); + } + smis = construct_smis(); + doubles = construct_doubles(); + %OptimizeFunctionOnNextCall(convert_mixed); + convert_mixed(smis, 1, elements_kind.fast); + convert_mixed(doubles, 1, elements_kind.fast); + assertTrue(%HaveSameMap(smis, doubles)); +} + +// Crankshaft support for smi-only elements in dynamic array literals. +function get(foo) { return foo; } // Used to generate dynamic values. + +function crankshaft_test() { + if (support_smi_only_arrays) { + var a1 = [get(1), get(2), get(3)]; + assertKind(elements_kind.fast_smi_only, a1); + } + var a2 = new Array(get(1), get(2), get(3)); + assertKind(elements_kind.fast_smi_only, a2); + var b = [get(1), get(2), get("three")]; + assertKind(elements_kind.fast, b); + var c = [get(1), get(2), get(3.5)]; + if (support_smi_only_arrays) { + assertKind(elements_kind.fast_double, c); + } +} +for (var i = 0; i < 3; i++) { + crankshaft_test(); +} +%OptimizeFunctionOnNextCall(crankshaft_test); +crankshaft_test(); + +// Elements_kind transitions for arrays. + +// A map can have three different elements_kind transitions: SMI->DOUBLE, +// DOUBLE->OBJECT, and SMI->OBJECT. No matter in which order these three are +// created, they must always end up with the same FAST map. + +// This test is meaningless without FAST_SMI_ONLY_ELEMENTS. +if (support_smi_only_arrays) { + // Preparation: create one pair of identical objects for each case. + var a = [1, 2, 3]; + var b = [1, 2, 3]; + assertTrue(%HaveSameMap(a, b)); + assertKind(elements_kind.fast_smi_only, a); + var c = [1, 2, 3]; + c["case2"] = true; + var d = [1, 2, 3]; + d["case2"] = true; + assertTrue(%HaveSameMap(c, d)); + assertFalse(%HaveSameMap(a, c)); + assertKind(elements_kind.fast_smi_only, c); + var e = [1, 2, 3]; + e["case3"] = true; + var f = [1, 2, 3]; + f["case3"] = true; + assertTrue(%HaveSameMap(e, f)); + assertFalse(%HaveSameMap(a, e)); + assertFalse(%HaveSameMap(c, e)); + assertKind(elements_kind.fast_smi_only, e); + // Case 1: SMI->DOUBLE, DOUBLE->OBJECT, SMI->OBJECT. + a[0] = 1.5; + assertKind(elements_kind.fast_double, a); + a[0] = "foo"; + assertKind(elements_kind.fast, a); + b[0] = "bar"; + assertTrue(%HaveSameMap(a, b)); + // Case 2: SMI->DOUBLE, SMI->OBJECT, DOUBLE->OBJECT. + c[0] = 1.5; + assertKind(elements_kind.fast_double, c); + assertFalse(%HaveSameMap(c, d)); + d[0] = "foo"; + assertKind(elements_kind.fast, d); + assertFalse(%HaveSameMap(c, d)); + c[0] = "bar"; + assertTrue(%HaveSameMap(c, d)); + // Case 3: SMI->OBJECT, SMI->DOUBLE, DOUBLE->OBJECT. + e[0] = "foo"; + assertKind(elements_kind.fast, e); + assertFalse(%HaveSameMap(e, f)); + f[0] = 1.5; + assertKind(elements_kind.fast_double, f); + assertFalse(%HaveSameMap(e, f)); + f[0] = "bar"; + assertKind(elements_kind.fast, f); + assertTrue(%HaveSameMap(e, f)); +} + +// Test if Array.concat() works correctly with DOUBLE elements. +if (support_smi_only_arrays) { + var a = [1, 2]; + assertKind(elements_kind.fast_smi_only, a); + var b = [4.5, 5.5]; + assertKind(elements_kind.fast_double, b); + var c = a.concat(b); + assertEquals([1, 2, 4.5, 5.5], c); + // TODO(1810): Change implementation so that we get DOUBLE elements here? + assertKind(elements_kind.fast, c); +} + +// Test that Array.push() correctly handles SMI elements. +if (support_smi_only_arrays) { + var a = [1, 2]; + assertKind(elements_kind.fast_smi_only, a); + a.push(3, 4, 5); + assertKind(elements_kind.fast_smi_only, a); + assertEquals([1, 2, 3, 4, 5], a); +} + +// Test that Array.splice() and Array.slice() return correct ElementsKinds. +if (support_smi_only_arrays) { + var a = ["foo", "bar"]; + assertKind(elements_kind.fast, a); + var b = a.splice(0, 1); + assertKind(elements_kind.fast, b); + var c = a.slice(0, 1); + assertKind(elements_kind.fast, c); +} + +// Throw away type information in the ICs for next stress run. +gc(); diff --git a/deps/v8/test/mjsunit/elements-transition-hoisting.js b/deps/v8/test/mjsunit/elements-transition-hoisting.js new file mode 100644 index 0000000000..53dc940919 --- /dev/null +++ b/deps/v8/test/mjsunit/elements-transition-hoisting.js @@ -0,0 +1,168 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays + +// Ensure that ElementsKind transitions in various situations are hoisted (or +// not hoisted) correctly, don't change the semantics programs and don't trigger +// deopt through hoisting in important situations. + +support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6)); + +if (support_smi_only_arrays) { + print("Tests include smi-only arrays."); +} else { + print("Tests do NOT include smi-only arrays."); +} + +if (support_smi_only_arrays) { + // Make sure that a simple elements array transitions inside a loop before + // stores to an array gets hoisted in a way that doesn't generate a deopt in + // simple cases.} + function testDoubleConversion4(a) { + var object = new Object(); + a[0] = 0; + var count = 3; + do { + a[0] = object; + } while (--count > 0); + } + + testDoubleConversion4(new Array(5)); + %OptimizeFunctionOnNextCall(testDoubleConversion4); + testDoubleConversion4(new Array(5)); + testDoubleConversion4(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testDoubleConversion4)); + + // Make sure that non-element related map checks that are not preceded by + // transitions in a loop still get hoisted in a way that doesn't generate a + // deopt in simple cases. + function testExactMapHoisting(a) { + var object = new Object(); + a.foo = 0; + a[0] = 0; + a[1] = 1; + var count = 3; + do { + a.foo = object; // This map check should be hoistable + a[1] = object; + result = a.foo == object && a[1] == object; + } while (--count > 0); + } + + testExactMapHoisting(new Array(5)); + %OptimizeFunctionOnNextCall(testExactMapHoisting); + testExactMapHoisting(new Array(5)); + testExactMapHoisting(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting)); + + // Make sure that non-element related map checks do NOT get hoisted if they + // depend on an elements transition before them and it's not possible to hoist + // that transition. + function testExactMapHoisting2(a) { + var object = new Object(); + a.foo = 0; + a[0] = 0; + a[1] = 1; + var count = 3; + do { + if (a.bar === undefined) { + a[1] = 2.5; + } + a.foo = object; // This map check should NOT be hoistable because it + // includes a check for the FAST_ELEMENTS map as well as + // the FAST_DOUBLE_ELEMENTS map, which depends on the + // double transition above in the if, which cannot be + // hoisted. + } while (--count > 0); + } + + testExactMapHoisting2(new Array(5)); + %OptimizeFunctionOnNextCall(testExactMapHoisting2); + testExactMapHoisting2(new Array(5)); + testExactMapHoisting2(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting2)); + + // Make sure that non-element related map checks do get hoisted if they use + // the transitioned map for the check and all transitions that they depend + // upon can hoisted, too. + function testExactMapHoisting3(a) { + var object = new Object(); + a.foo = 0; + a[0] = 0; + a[1] = 1; + var count = 3; + do { + a[1] = 2.5; + a.foo = object; // This map check should be hoistable because all elements + // transitions in the loop can also be hoisted. + } while (--count > 0); + } + + var add_transition = new Array(5); + add_transition.foo = 0; + add_transition[0] = new Object(); // For FAST_ELEMENT transition to be created + testExactMapHoisting3(new Array(5)); + %OptimizeFunctionOnNextCall(testExactMapHoisting3); + testExactMapHoisting3(new Array(5)); + testExactMapHoisting3(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting3)); + + function testDominatingTransitionHoisting1(a) { + var object = new Object(); + a[0] = 0; + var count = 3; + do { + if (a.baz != true) { + a[1] = 2.5; + } + a[0] = object; + } while (--count > 3); + } + + testDominatingTransitionHoisting1(new Array(5)); + %OptimizeFunctionOnNextCall(testDominatingTransitionHoisting1); + testDominatingTransitionHoisting1(new Array(5)); + testDominatingTransitionHoisting1(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testDominatingTransitionHoisting1)); + + function testHoistingWithSideEffect(a) { + var object = new Object(); + a[0] = 0; + var count = 3; + do { + assertTrue(true); + a[0] = object; + } while (--count > 3); + } + + testHoistingWithSideEffect(new Array(5)); + %OptimizeFunctionOnNextCall(testHoistingWithSideEffect); + testHoistingWithSideEffect(new Array(5)); + testHoistingWithSideEffect(new Array(5)); + assertTrue(2 != %GetOptimizationStatus(testHoistingWithSideEffect)); +} diff --git a/deps/v8/test/mjsunit/elements-transition.js b/deps/v8/test/mjsunit/elements-transition.js new file mode 100644 index 0000000000..60e051b3fa --- /dev/null +++ b/deps/v8/test/mjsunit/elements-transition.js @@ -0,0 +1,113 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --smi-only-arrays + +support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6,7,8)); + +if (support_smi_only_arrays) { + print("Tests include smi-only arrays."); +} else { + print("Tests do NOT include smi-only arrays."); +} + +if (support_smi_only_arrays) { + function test(test_double, test_object, set, length) { + // We apply the same operations to two identical arrays. The first array + // triggers an IC miss, upon which the conversion stub is generated, but the + // actual conversion is done in runtime. The second array, arriving at + // the previously patched IC, is then converted using the conversion stub. + var array_1 = new Array(length); + var array_2 = new Array(length); + + assertTrue(%HasFastSmiOnlyElements(array_1)); + assertTrue(%HasFastSmiOnlyElements(array_2)); + for (var i = 0; i < length; i++) { + if (i == length - 5 && test_double) { + // Trigger conversion to fast double elements at length-5. + set(array_1, i, 0.5); + set(array_2, i, 0.5); + assertTrue(%HasFastDoubleElements(array_1)); + assertTrue(%HasFastDoubleElements(array_2)); + } else if (i == length - 3 && test_object) { + // Trigger conversion to fast object elements at length-3. + set(array_1, i, 'object'); + set(array_2, i, 'object'); + assertTrue(%HasFastElements(array_1)); + assertTrue(%HasFastElements(array_2)); + } else if (i != length - 7) { + // Set the element to an integer but leave a hole at length-7. + set(array_1, i, 2*i+1); + set(array_2, i, 2*i+1); + } + } + + for (var i = 0; i < length; i++) { + if (i == length - 5 && test_double) { + assertEquals(0.5, array_1[i]); + assertEquals(0.5, array_2[i]); + } else if (i == length - 3 && test_object) { + assertEquals('object', array_1[i]); + assertEquals('object', array_2[i]); + } else if (i != length - 7) { + assertEquals(2*i+1, array_1[i]); + assertEquals(2*i+1, array_2[i]); + } else { + assertEquals(undefined, array_1[i]); + assertEquals(undefined, array_2[i]); + } + } + + assertEquals(length, array_1.length); + assertEquals(length, array_2.length); + } + + test(false, false, function(a,i,v){ a[i] = v; }, 20); + test(true, false, function(a,i,v){ a[i] = v; }, 20); + test(false, true, function(a,i,v){ a[i] = v; }, 20); + test(true, true, function(a,i,v){ a[i] = v; }, 20); + + test(false, false, function(a,i,v){ a[i] = v; }, 10000); + test(true, false, function(a,i,v){ a[i] = v; }, 10000); + test(false, true, function(a,i,v){ a[i] = v; }, 10000); + test(true, true, function(a,i,v){ a[i] = v; }, 10000); + + // Check COW arrays + function get_cow() { return [1, 2, 3]; } + + function transition(x) { x[0] = 1.5; } + + var ignore = get_cow(); + transition(ignore); // Handled by runtime. + var a = get_cow(); + var b = get_cow(); + transition(a); // Handled by IC. + assertEquals(1.5, a[0]); + assertEquals(1, b[0]); +} else { + print("Test skipped because smi only arrays are not supported."); +} diff --git a/deps/v8/test/mjsunit/error-tostring.js b/deps/v8/test/mjsunit/error-tostring.js new file mode 100644 index 0000000000..a28564144f --- /dev/null +++ b/deps/v8/test/mjsunit/error-tostring.js @@ -0,0 +1,85 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Test default string representation of an Error object. + +var e = new Error(); +assertEquals('Error', e.toString()); + + +// Test printing of cyclic errors which return the empty string for +// compatibility with Safari and Firefox. + +e = new Error(); +e.name = e; +e.message = e; +e.stack = "Does not occur in output"; +e.arguments = "Does not occur in output"; +e.type = "Does not occur in output"; +assertEquals('', e.toString()); + +e = new Error(); +e.name = [ e ]; +e.message = [ e ]; +e.stack = "Does not occur in output"; +e.arguments = "Does not occur in output"; +e.type = "Does not occur in output"; +assertEquals('', e.toString()); + + +// Test the sequence in which getters and toString operations are called +// on a given Error object. Verify the produced string representation. + +function testErrorToString(nameValue, messageValue) { + var seq = []; + var e = { + get name() { + seq.push(1); + return (nameValue === undefined) ? nameValue : { + toString: function() { seq.push(2); return nameValue; } + }; + }, + get message() { + seq.push(3); + return (messageValue === undefined) ? messageValue : { + toString: function() { seq.push(4); return messageValue; } + }; + } + }; + var string = Error.prototype.toString.call(e); + return [string,seq]; +} + +assertEquals(["Error",[1,3]], testErrorToString(undefined, undefined)); +assertEquals(["e1",[1,2,3]], testErrorToString("e1", undefined)); +assertEquals(["e1: null",[1,2,3,4]], testErrorToString("e1", null)); +assertEquals(["e1",[1,2,3,4]], testErrorToString("e1", "")); +assertEquals(["Error: e2",[1,3,4]], testErrorToString(undefined, "e2")); +assertEquals(["null: e2",[1,2,3,4]], testErrorToString(null, "e2")); +assertEquals(["e2",[1,2,3,4]], testErrorToString("", "e2")); +assertEquals(["e1: e2",[1,2,3,4]], testErrorToString("e1", "e2")); diff --git a/deps/v8/test/mjsunit/eval.js b/deps/v8/test/mjsunit/eval.js index b6284ba911..100f21653f 100644 --- a/deps/v8/test/mjsunit/eval.js +++ b/deps/v8/test/mjsunit/eval.js @@ -39,7 +39,7 @@ assertEquals(1, count); try { eval('hest 7 &*^*&^'); - assertTrue(false, 'Did not throw on syntax error.'); + assertUnreachable('Did not throw on syntax error.'); } catch (e) { assertEquals('SyntaxError', e.name); } @@ -108,6 +108,7 @@ foo = 0; result = (function() { var foo = 2; + // Should be non-direct call. return x.eval('foo'); })(); assertEquals(0, result); @@ -115,12 +116,33 @@ assertEquals(0, result); foo = 0; result = (function() { + var foo = 2; + // Should be non-direct call. + return (1,eval)('foo'); + })(); +assertEquals(0, result); + +foo = 0; +result = + (function() { var eval = function(x) { return x; }; var foo = eval(2); + // Should be non-direct call. return e('foo'); })(); assertEquals(0, result); +foo = 0; +result = + (function() { + var foo = 2; + // Should be direct call. + with ({ eval : e }) { + return eval('foo'); + } + })(); +assertEquals(2, result); + result = (function() { var eval = function(x) { return 2 * x; }; @@ -135,19 +157,17 @@ result = })(); assertEquals(this, result); -result = - (function() { - var obj = { f: function(eval) { return eval("this"); } }; - return obj.f(eval); - })(); -assertEquals(this, result); +(function() { + var obj = { f: function(eval) { return eval("this"); } }; + result = obj.f(eval); + assertEquals(obj, result); +})(); -result = - (function() { - var obj = { f: function(eval) { arguments; return eval("this"); } }; - return obj.f(eval); - })(); -assertEquals(this, result); +(function() { + var obj = { f: function(eval) { arguments; return eval("this"); } }; + result = obj.f(eval); + assertEquals(obj, result); +})(); eval = function(x) { return 2 * x; }; result = @@ -156,6 +176,9 @@ result = })(); assertEquals(4, result); + + + // Regression test: calling a function named eval found in a context that is // not the global context should get the global object as receiver. result = diff --git a/deps/v8/test/mjsunit/external-array.js b/deps/v8/test/mjsunit/external-array.js index 81c6cfe8b4..72cfd85956 100644 --- a/deps/v8/test/mjsunit/external-array.js +++ b/deps/v8/test/mjsunit/external-array.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -43,6 +43,50 @@ f(a); assertEquals(0, a[0]); assertEquals(0, a[1]); +// No-parameter constructor should fail right now. +function abfunc1() { + return new ArrayBuffer(); +} +assertThrows(abfunc1); + +// Test derivation from an ArrayBuffer +var ab = new ArrayBuffer(12); +var derived_uint8 = new Uint8Array(ab); +assertEquals(12, derived_uint8.length); +var derived_uint32 = new Uint32Array(ab); +assertEquals(3, derived_uint32.length); +var derived_uint32_2 = new Uint32Array(ab,4); +assertEquals(2, derived_uint32_2.length); +var derived_uint32_3 = new Uint32Array(ab,4,1); +assertEquals(1, derived_uint32_3.length); + +// If a given byteOffset and length references an area beyond the end of the +// ArrayBuffer an exception is raised. +function abfunc3() { + new Uint32Array(ab,4,3); +} +assertThrows(abfunc3); +function abfunc4() { + new Uint32Array(ab,16); +} +assertThrows(abfunc4); + +// The given byteOffset must be a multiple of the element size of the specific +// type, otherwise an exception is raised. +function abfunc5() { + new Uint32Array(ab,5); +} +assertThrows(abfunc5); + +// If length is not explicitly specified, the length of the ArrayBuffer minus +// the byteOffset must be a multiple of the element size of the specific type, +// or an exception is raised. +var ab2 = new ArrayBuffer(13); +function abfunc6() { + new Uint32Array(ab2,4); +} +assertThrows(abfunc6); + // Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is // "constant", but not read-only). a = new Int32Array(2); diff --git a/deps/v8/test/mjsunit/function-bind.js b/deps/v8/test/mjsunit/function-bind.js index e9d02213e0..4a8f2d2a62 100644 --- a/deps/v8/test/mjsunit/function-bind.js +++ b/deps/v8/test/mjsunit/function-bind.js @@ -29,29 +29,31 @@ // Simple tests. function foo(x, y, z) { - return x + y + z; + return [this, arguments.length, x]; } +assertEquals(3, foo.length); + var f = foo.bind(foo); -assertEquals(3, f(1, 1, 1)); +assertEquals([foo, 3, 1], f(1, 2, 3)); assertEquals(3, f.length); -f = foo.bind(foo, 2); -assertEquals(4, f(1, 1)); +f = foo.bind(foo, 1); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo, 2, 2); -assertEquals(5, f(1)); +f = foo.bind(foo, 1, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 2, 2, 2); -assertEquals(6, f()); +f = foo.bind(foo, 1, 2, 3); +assertEquals([foo, 3, 1], f()); assertEquals(0, f.length); // Test that length works correctly even if more than the actual number // of arguments are given when binding. f = foo.bind(foo, 1, 2, 3, 4, 5, 6, 7, 8, 9); -assertEquals(6, f()); +assertEquals([foo, 9, 1], f()); assertEquals(0, f.length); // Use a different bound object. @@ -78,64 +80,97 @@ assertEquals(0, f.length); // When only giving the thisArg, any number of binds should have // the same effect. f = foo.bind(foo); -assertEquals(3, f(1, 1, 1)); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1, 1, 1)); +assertEquals([foo, 3, 1], f(1, 2, 3)); + +var not_foo = {}; +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(1, 2, 3)); assertEquals(3, f.length); // Giving bound parameters should work at any place in the chain. -f = foo.bind(foo, 1).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo, 1).bind(foo).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo).bind(foo,1 ).bind(foo); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo,1 ).bind(not_foo); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo, 1); -assertEquals(3, f(1, 1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1); +assertEquals([foo, 3, 1], f(2, 3)); assertEquals(2, f.length); -// Several parameters can be given, and given in different bind invokations. -f = foo.bind(foo, 1, 1).bind(foo).bind(foo).bind(foo); -assertEquals(3, f(1)); +// Several parameters can be given, and given in different bind invocations. +f = foo.bind(foo, 1, 2).bind(not_foo).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); +assertEquals(1, f.length); + +f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(1)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo, 1, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo, 1, 2).bind(not_foo); +assertEquals([foo, 3, 1], f(1)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo).bind(foo, 1, 1).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo).bind(not_foo).bind(not_foo, 1, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo).bind(foo).bind(foo, 1, 1); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo, 2).bind(not_foo).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo, 1).bind(foo).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo, 2).bind(not_foo); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo).bind(foo, 1).bind(foo); -assertEquals(3, f(1)); +f = foo.bind(foo, 1).bind(not_foo).bind(not_foo).bind(not_foo, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo, 1).bind(foo).bind(foo).bind(foo, 1); -assertEquals(3, f(1)); +f = foo.bind(foo).bind(not_foo, 1).bind(not_foo).bind(not_foo, 2); +assertEquals([foo, 3, 1], f(3)); assertEquals(1, f.length); -f = foo.bind(foo).bind(foo, 1).bind(foo).bind(foo, 1); -assertEquals(3, f(1)); +// The wrong number of arguments can be given to bound functions too. +f = foo.bind(foo); +assertEquals(3, f.length); +assertEquals([foo, 0, undefined], f()); +assertEquals([foo, 1, 1], f(1)); +assertEquals([foo, 2, 1], f(1, 2)); +assertEquals([foo, 3, 1], f(1, 2, 3)); +assertEquals([foo, 4, 1], f(1, 2, 3, 4)); + +f = foo.bind(foo, 1); +assertEquals(2, f.length); +assertEquals([foo, 1, 1], f()); +assertEquals([foo, 2, 1], f(2)); +assertEquals([foo, 3, 1], f(2, 3)); +assertEquals([foo, 4, 1], f(2, 3, 4)); + +f = foo.bind(foo, 1, 2); assertEquals(1, f.length); +assertEquals([foo, 2, 1], f()); +assertEquals([foo, 3, 1], f(3)); +assertEquals([foo, 4, 1], f(3, 4)); + +f = foo.bind(foo, 1, 2, 3); +assertEquals(0, f.length); +assertEquals([foo, 3, 1], f()); +assertEquals([foo, 4, 1], f(4)); + +f = foo.bind(foo, 1, 2, 3, 4); +assertEquals(0, f.length); +assertEquals([foo, 4, 1], f()); // Test constructor calls. @@ -171,13 +206,91 @@ assertEquals(3, obj2.z); // Test bind chains when used as a constructor. - f = bar.bind(bar, 1).bind(bar, 2).bind(bar, 3); obj2 = new f(); assertEquals(1, obj2.x); assertEquals(2, obj2.y); assertEquals(3, obj2.z); -// Test instanceof obj2 is bar, not f. +// Test obj2 is instanceof both bar and f. assertTrue(obj2 instanceof bar); -assertFalse(obj2 instanceof f); +assertTrue(obj2 instanceof f); + +// This-args are not relevant to instanceof. +f = bar.bind(foo.prototype, 1). + bind(String.prototype, 2). + bind(Function.prototype, 3); +var obj3 = new f(); +assertTrue(obj3 instanceof bar); +assertTrue(obj3 instanceof f); +assertFalse(obj3 instanceof foo); +assertFalse(obj3 instanceof Function); +assertFalse(obj3 instanceof String); + +// thisArg is converted to object. +f = foo.bind(undefined); +assertEquals([this, 0, undefined], f()); + +f = foo.bind(null); +assertEquals([this, 0, undefined], f()); + +f = foo.bind(42); +assertEquals([Object(42), 0, undefined], f()); + +f = foo.bind("foo"); +assertEquals([Object("foo"), 0, undefined], f()); + +f = foo.bind(true); +assertEquals([Object(true), 0, undefined], f()); + +// Strict functions don't convert thisArg. +function soo(x, y, z) { + "use strict"; + return [this, arguments.length, x]; +} + +var s = soo.bind(undefined); +assertEquals([undefined, 0, undefined], s()); + +s = soo.bind(null); +assertEquals([null, 0, undefined], s()); + +s = soo.bind(42); +assertEquals([42, 0, undefined], s()); + +s = soo.bind("foo"); +assertEquals(["foo", 0, undefined], s()); + +s = soo.bind(true); +assertEquals([true, 0, undefined], s()); + +// Test that .arguments and .caller are poisoned according to the ES5 spec. + +// Check that property descriptors are correct (unconfigurable, unenumerable, +// and both get and set is the ThrowTypeError function). +var cdesc = Object.getOwnPropertyDescriptor(f, "caller"); +var adesc = Object.getOwnPropertyDescriptor(f, "arguments"); + +assertFalse(cdesc.enumerable); +assertFalse(cdesc.configurable); + +assertFalse(adesc.enumerable); +assertFalse(adesc.configurable); + +assertSame(cdesc.get, cdesc.set); +assertSame(cdesc.get, adesc.get); +assertSame(cdesc.get, adesc.set); + +assertTrue(cdesc.get instanceof Function); +assertEquals(0, cdesc.get.length); +assertThrows(cdesc.get, TypeError); + +assertThrows(function() { return f.caller; }, TypeError); +assertThrows(function() { f.caller = 42; }, TypeError); +assertThrows(function() { return f.arguments; }, TypeError); +assertThrows(function() { f.arguments = 42; }, TypeError); + +// Shouldn't throw. Accessing the functions caller must throw if +// the caller is strict and the callee isn't. A bound function is built-in, +// but not considered strict. +(function foo() { return foo.caller; }).bind()(); diff --git a/deps/v8/test/mjsunit/function-named-self-reference.js b/deps/v8/test/mjsunit/function-named-self-reference.js new file mode 100644 index 0000000000..5b03b094b7 --- /dev/null +++ b/deps/v8/test/mjsunit/function-named-self-reference.js @@ -0,0 +1,45 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +var fn = function fn(val) { + if (val) return val; + + %OptimizeFunctionOnNextCall(fn); + + function run(val) { + var res = fn((val + 1) << 1); + + return res; + } + + return run(0); +} + +var res = fn(); +assertEquals(res, 2); diff --git a/deps/v8/test/mjsunit/fuzz-natives.js b/deps/v8/test/mjsunit/fuzz-natives.js index ff6677e6ad..c4d18d0411 100644 --- a/deps/v8/test/mjsunit/fuzz-natives.js +++ b/deps/v8/test/mjsunit/fuzz-natives.js @@ -163,6 +163,9 @@ var knownProblems = { "PromoteScheduledException": true, "DeleteHandleScopeExtensions": true, + // Vararg with minimum number > 0. + "Call": true, + // Requires integer arguments to be non-negative. "Apply": true, diff --git a/deps/v8/test/mjsunit/global-const-var-conflicts.js b/deps/v8/test/mjsunit/global-const-var-conflicts.js index d38d0ee813..2fca96f9f8 100644 --- a/deps/v8/test/mjsunit/global-const-var-conflicts.js +++ b/deps/v8/test/mjsunit/global-const-var-conflicts.js @@ -26,7 +26,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Check that dynamically introducing conflicting consts/vars -// leads to exceptions. +// is silently ignored (and does not lead to exceptions). var caught = 0; @@ -46,12 +46,12 @@ eval("var c"); try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } assertTrue(typeof c == 'undefined'); try { eval("const c = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } -assertTrue(typeof c == 'undefined'); +assertEquals(1, c); eval("var d = 0"); try { eval("const d"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } -assertEquals(0, d); +assertEquals(undefined, d); try { eval("const d = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } -assertEquals(0, d); +assertEquals(1, d); -assertEquals(8, caught); +assertEquals(0, caught); diff --git a/deps/v8/test/mjsunit/harmony/block-conflicts.js b/deps/v8/test/mjsunit/harmony/block-conflicts.js index 8d3de6f9d6..ee2d9794ee 100644 --- a/deps/v8/test/mjsunit/harmony/block-conflicts.js +++ b/deps/v8/test/mjsunit/harmony/block-conflicts.js @@ -25,10 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-block-scoping +// Flags: --harmony-scoping // Test for conflicting variable bindings. +// TODO(ES6): properly activate extended mode +"use strict"; + function CheckException(e) { var string = e.toString(); assertTrue(string.indexOf("has already been declared") >= 0 || @@ -80,6 +83,11 @@ var letbinds = [ "let x", "let x = function() {}", "let x, y", "let y, x", + "const x = 0", + "const x = undefined", + "const x = function() {}", + "const x = 2, y = 3", + "const y = 4, x = 5", ]; var varbinds = [ "var x", "var x = 0", diff --git a/deps/v8/test/mjsunit/harmony/block-const-assign.js b/deps/v8/test/mjsunit/harmony/block-const-assign.js new file mode 100644 index 0000000000..8297a558a4 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-const-assign.js @@ -0,0 +1,131 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-scoping + +// Test that we throw early syntax errors in harmony mode +// when using an immutable binding in an assigment or with +// prefix/postfix decrement/increment operators. +// TODO(ES6): properly activate extended mode +"use strict"; + + +// Function local const. +function constDecl0(use) { + return "(function() { const constvar = 1; " + use + "; });"; +} + + +function constDecl1(use) { + return "(function() { " + use + "; const constvar = 1; });"; +} + + +// Function local const, assign from eval. +function constDecl2(use) { + use = "eval('(function() { " + use + " })')"; + return "(function() { const constvar = 1; " + use + "; })();"; +} + + +function constDecl3(use) { + use = "eval('(function() { " + use + " })')"; + return "(function() { " + use + "; const constvar = 1; })();"; +} + + +// Block local const. +function constDecl4(use) { + return "(function() { { const constvar = 1; " + use + "; } });"; +} + + +function constDecl5(use) { + return "(function() { { " + use + "; const constvar = 1; } });"; +} + + +// Block local const, assign from eval. +function constDecl6(use) { + use = "eval('(function() {" + use + "})')"; + return "(function() { { const constvar = 1; " + use + "; } })();"; +} + + +function constDecl7(use) { + use = "eval('(function() {" + use + "})')"; + return "(function() { { " + use + "; const constvar = 1; } })();"; +} + + +// Function expression name. +function constDecl8(use) { + return "(function constvar() { " + use + "; });"; +} + + +// Function expression name, assign from eval. +function constDecl9(use) { + use = "eval('(function(){" + use + "})')"; + return "(function constvar() { " + use + "; })();"; +} + +let decls = [ constDecl0, + constDecl1, + constDecl2, + constDecl3, + constDecl4, + constDecl5, + constDecl6, + constDecl7, + constDecl8, + constDecl9 + ]; +let uses = [ 'constvar = 1;', + 'constvar += 1;', + '++constvar;', + 'constvar++;' + ]; + +function Test(d,u) { + 'use strict'; + try { + print(d(u)); + eval(d(u)); + } catch (e) { + assertInstanceof(e, SyntaxError); + assertTrue(e.toString().indexOf("Assignment to constant variable") >= 0); + return; + } + assertUnreachable(); +} + +for (var d = 0; d < decls.length; ++d) { + for (var u = 0; u < uses.length; ++u) { + Test(decls[d], uses[u]); + } +} diff --git a/deps/v8/test/mjsunit/harmony/block-early-errors.js b/deps/v8/test/mjsunit/harmony/block-early-errors.js new file mode 100644 index 0000000000..791f001af0 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-early-errors.js @@ -0,0 +1,55 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-scoping + +function CheckException(e) { + var string = e.toString(); + assertInstanceof(e, SyntaxError); + assertTrue(string.indexOf("Illegal let") >= 0); +} + +function Check(str) { + try { + eval("(function () { " + str + " })"); + assertUnreachable(); + } catch (e) { + CheckException(e); + } + try { + eval("(function () { { " + str + " } })"); + assertUnreachable(); + } catch (e) { + CheckException(e); + } +} + +// Check for early syntax errors when using let +// declarations outside of extended mode. +Check("let x;"); +Check("let x = 1;"); +Check("let x, y;"); diff --git a/deps/v8/test/mjsunit/harmony/block-for.js b/deps/v8/test/mjsunit/harmony/block-for.js new file mode 100644 index 0000000000..e84f0d2fee --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/block-for.js @@ -0,0 +1,146 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-scoping + +// TODO(ES6): properly activate extended mode +"use strict"; + +function props(x) { + var array = []; + for (let p in x) array.push(p); + return array.sort(); +} + +assertEquals(0, props({}).length); +assertEquals(1, props({x:1}).length); +assertEquals(2, props({x:1, y:2}).length); + +assertArrayEquals(["x"], props({x:1})); +assertArrayEquals(["x", "y"], props({x:1, y:2})); +assertArrayEquals(["x", "y", "zoom"], props({x:1, y:2, zoom:3})); + +assertEquals(0, props([]).length); +assertEquals(1, props([1]).length); +assertEquals(2, props([1,2]).length); + +assertArrayEquals(["0"], props([1])); +assertArrayEquals(["0", "1"], props([1,2])); +assertArrayEquals(["0", "1", "2"], props([1,2,3])); + +var o = {}; +var a = []; +let i = "outer_i"; +let s = "outer_s"; +for (let i = 0x0020; i < 0x01ff; i+=2) { + let s = 'char:' + String.fromCharCode(i); + a.push(s); + o[s] = i; +} +assertArrayEquals(a, props(o)); +assertEquals(i, "outer_i"); +assertEquals(s, "outer_s"); + +var a = []; +assertEquals(0, props(a).length); +a[Math.pow(2,30)-1] = 0; +assertEquals(1, props(a).length); +a[Math.pow(2,31)-1] = 0; +assertEquals(2, props(a).length); +a[1] = 0; +assertEquals(3, props(a).length); + +var result = ''; +for (let p in {a : [0], b : 1}) { result += p; } +assertEquals('ab', result); + +var result = ''; +for (let p in {a : {v:1}, b : 1}) { result += p; } +assertEquals('ab', result); + +var result = ''; +for (let p in { get a() {}, b : 1}) { result += p; } +assertEquals('ab', result); + +var result = ''; +for (let p in { get a() {}, set a(x) {}, b : 1}) { result += p; } +assertEquals('ab', result); + + +// Check that there is exactly one variable without initializer +// in a for-in statement with let variables. +// TODO(ES6): properly activate extended mode +assertThrows("function foo() { 'use strict'; for (let in {}) { } }", SyntaxError); +assertThrows("function foo() { 'use strict'; for (let x = 3 in {}) { } }", SyntaxError); +assertThrows("function foo() { 'use strict'; for (let x, y in {}) { } }", SyntaxError); +assertThrows("function foo() { 'use strict'; for (let x = 3, y in {}) { } }", SyntaxError); +assertThrows("function foo() { 'use strict'; for (let x, y = 4 in {}) { } }", SyntaxError); +assertThrows("function foo() { 'use strict'; for (let x = 3, y = 4 in {}) { } }", SyntaxError); + + +// In a normal for statement the iteration variable is not +// freshly allocated for each iteration. +function closures1() { + let a = []; + for (let i = 0; i < 5; ++i) { + a.push(function () { return i; }); + } + for (let j = 0; j < 5; ++j) { + assertEquals(5, a[j]()); + } +} +closures1(); + + +function closures2() { + let a = [], b = []; + for (let i = 0, j = 10; i < 5; ++i, ++j) { + a.push(function () { return i; }); + b.push(function () { return j; }); + } + for (let k = 0; k < 5; ++k) { + assertEquals(5, a[k]()); + assertEquals(15, b[k]()); + } +} +closures2(); + + +// In a for-in statement the iteration variable is fresh +// for earch iteration. +function closures3(x) { + let a = []; + for (let p in x) { + a.push(function () { return p; }); + } + let k = 0; + for (let q in x) { + assertEquals(q, a[k]()); + ++k; + } +} +closures3({a : [0], b : 1, c : {v : 1}, get d() {}, set e(x) {}}); diff --git a/deps/v8/test/mjsunit/harmony/block-leave.js b/deps/v8/test/mjsunit/harmony/block-leave.js index 73eaf29449..a7f6b69475 100644 --- a/deps/v8/test/mjsunit/harmony/block-leave.js +++ b/deps/v8/test/mjsunit/harmony/block-leave.js @@ -25,7 +25,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-block-scoping +// Flags: --harmony-scoping + +// TODO(ES6): properly activate extended mode +"use strict"; // We want to test the context chain shape. In each of the tests cases // below, the outer with is to force a runtime lookup of the identifier 'x' @@ -64,31 +67,30 @@ try { } catch (e) { caught = true; assertEquals(25, e); - with ({y:19}) { - assertEquals(19, y); + (function () { try { // NOTE: This checks that the block scope containing xx has been // removed from the context chain. - xx; + eval('xx'); assertTrue(false); // should not reach here } catch (e2) { assertTrue(e2 instanceof ReferenceError); } - } + })(); } assertTrue(caught); -with ({x: 'outer'}) { +(function(x) { label: { let x = 'inner'; break label; } - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { label: { let x = 'middle'; { @@ -96,20 +98,20 @@ with ({x: 'outer'}) { break label; } } - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { for (var i = 0; i < 10; ++i) { let x = 'inner' + i; continue; } - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { label: for (var i = 0; i < 10; ++i) { let x = 'middle' + i; for (var j = 0; j < 10; ++j) { @@ -117,21 +119,21 @@ with ({x: 'outer'}) { continue label; } } - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { try { let x = 'inner'; throw 0; } catch (e) { - assertEquals('outer', x); + assertEquals('outer', eval('x')); } -} +})('outer'); -with ({x: 'outer'}) { +(function(x) { try { let x = 'middle'; { @@ -139,27 +141,27 @@ with ({x: 'outer'}) { throw 0; } } catch (e) { - assertEquals('outer', x); + assertEquals('outer', eval('x')); } -} +})('outer'); try { - with ({x: 'outer'}) { + (function(x) { try { let x = 'inner'; throw 0; } finally { - assertEquals('outer', x); + assertEquals('outer', eval('x')); } - } + })('outer'); } catch (e) { if (e instanceof MjsUnitAssertionError) throw e; } try { - with ({x: 'outer'}) { + (function(x) { try { let x = 'middle'; { @@ -167,9 +169,9 @@ try { throw 0; } } finally { - assertEquals('outer', x); + assertEquals('outer', eval('x')); } - } + })('outer'); } catch (e) { if (e instanceof MjsUnitAssertionError) throw e; } @@ -179,47 +181,47 @@ try { // from with. function f() {} -with ({x: 'outer'}) { +(function(x) { label: { let x = 'inner'; break label; } f(); // The context could be restored from the stack after the call. - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { for (var i = 0; i < 10; ++i) { let x = 'inner'; continue; } f(); - assertEquals('outer', x); -} + assertEquals('outer', eval('x')); +})('outer'); -with ({x: 'outer'}) { +(function(x) { try { let x = 'inner'; throw 0; } catch (e) { f(); - assertEquals('outer', x); + assertEquals('outer', eval('x')); } -} +})('outer'); try { - with ({x: 'outer'}) { + (function(x) { try { let x = 'inner'; throw 0; } finally { f(); - assertEquals('outer', x); + assertEquals('outer', eval('x')); } - } + })('outer'); } catch (e) { if (e instanceof MjsUnitAssertionError) throw e; } diff --git a/deps/v8/test/mjsunit/harmony/block-let-crankshaft.js b/deps/v8/test/mjsunit/harmony/block-let-crankshaft.js index c2fb96b6a4..1db1792ea6 100644 --- a/deps/v8/test/mjsunit/harmony/block-let-crankshaft.js +++ b/deps/v8/test/mjsunit/harmony/block-let-crankshaft.js @@ -25,10 +25,209 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-block-scoping --allow-natives-syntax +// Flags: --harmony-scoping --allow-natives-syntax + +// TODO(ES6): properly activate extended mode +"use strict"; + +// Check that the following functions are optimizable. +var functions = [ f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, + f15, f16, f17, f18, f19, f20, f21, f22, f23 ]; + +for (var i = 0; i < functions.length; ++i) { + var func = functions[i]; + print("Testing:"); + print(func); + for (var j = 0; j < 10; ++j) { + func(12); + } + %OptimizeFunctionOnNextCall(func); + func(12); + assertTrue(%GetOptimizationStatus(func) != 2); +} + +function f1() { } + +function f2(x) { } + +function f3() { + let x; +} + +function f4() { + function foo() { + } +} + +function f5() { + let x = 1; +} + +function f6() { + const x = 1; +} + +function f7(x) { + return x; +} + +function f8() { + let x; + return x; +} + +function f9() { + function x() { + } + return x; +} + +function f10(x) { + x = 1; +} + +function f11() { + let x; + x = 1; +} + +function f12() { + function x() {}; + x = 1; +} + +function f13(x) { + (function() { x; }); +} + +function f14() { + let x; + (function() { x; }); +} + +function f15() { + function x() { + } + (function() { x; }); +} + +function f16() { + let x = 1; + (function() { x; }); +} + +function f17() { + const x = 1; + (function() { x; }); +} + +function f18(x) { + return x; + (function() { x; }); +} + +function f19() { + let x; + return x; + (function() { x; }); +} + +function f20() { + function x() { + } + return x; + (function() { x; }); +} + +function f21(x) { + x = 1; + (function() { x; }); +} + +function f22() { + let x; + x = 1; + (function() { x; }); +} + +function f23() { + function x() { } + x = 1; + (function() { x; }); +} + // Test that temporal dead zone semantics for function and block scoped -// ket bindings are handled by the optimizing compiler. +// let bindings are handled by the optimizing compiler. + +function TestFunctionLocal(s) { + 'use strict'; + var func = eval("(function baz(){" + s + "; })"); + print("Testing:"); + print(func); + for (var i = 0; i < 5; ++i) { + try { + func(); + assertUnreachable(); + } catch (e) { + assertInstanceof(e, ReferenceError); + } + } + %OptimizeFunctionOnNextCall(func); + try { + func(); + assertUnreachable(); + } catch (e) { + assertInstanceof(e, ReferenceError); + } +} + +function TestFunctionContext(s) { + 'use strict'; + var func = eval("(function baz(){ " + s + "; (function() { x; }); })"); + print("Testing:"); + print(func); + for (var i = 0; i < 5; ++i) { + print(i); + try { + func(); + assertUnreachable(); + } catch (e) { + assertInstanceof(e, ReferenceError); + } + } + print("optimize"); + %OptimizeFunctionOnNextCall(func); + try { + print("call"); + func(); + assertUnreachable(); + } catch (e) { + print("catch"); + assertInstanceof(e, ReferenceError); + } +} + +function TestAll(s) { + TestFunctionLocal(s); + TestFunctionContext(s); +} + +// Use before initialization in declaration statement. +TestAll('let x = x + 1'); +TestAll('let x = x += 1'); +TestAll('let x = x++'); +TestAll('let x = ++x'); +TestAll('const x = x + 1'); + +// Use before initialization in prior statement. +TestAll('x + 1; let x;'); +TestAll('x = 1; let x;'); +TestAll('x += 1; let x;'); +TestAll('++x; let x;'); +TestAll('x++; let x;'); +TestAll('let y = x; const x = 1;'); + function f(x, b) { let y = (b ? y : x) + 42; diff --git a/deps/v8/test/mjsunit/harmony/block-let-declaration.js b/deps/v8/test/mjsunit/harmony/block-let-declaration.js index 49b63481a0..480e033489 100644 --- a/deps/v8/test/mjsunit/harmony/block-let-declaration.js +++ b/deps/v8/test/mjsunit/harmony/block-let-declaration.js @@ -25,41 +25,113 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-block-scoping +// Flags: --harmony-scoping // Test let declarations in various settings. +// TODO(ES6): properly activate extended mode +"use strict"; // Global let x; let y = 2; +const z = 4; // Block local { let y; let x = 3; + const z = 5; } assertEquals(undefined, x); assertEquals(2,y); +assertEquals(4,z); if (true) { let y; assertEquals(undefined, y); } +// Invalid declarations are early errors in harmony mode and thus should trigger +// an exception in eval code during parsing, before even compiling or executing +// the code. Thus the generated function is not called here. function TestLocalThrows(str, expect) { - assertThrows("(function(){" + str + "})()", expect); + assertThrows("(function(){ 'use strict'; " + str + "})", expect); } function TestLocalDoesNotThrow(str) { - assertDoesNotThrow("(function(){" + str + "})()"); + assertDoesNotThrow("(function(){ 'use strict'; " + str + "})()"); } -// Unprotected statement +// Test let declarations in statement positions. TestLocalThrows("if (true) let x;", SyntaxError); +TestLocalThrows("if (true) {} else let x;", SyntaxError); TestLocalThrows("do let x; while (false)", SyntaxError); TestLocalThrows("while (false) let x;", SyntaxError); +TestLocalThrows("label: let x;", SyntaxError); +TestLocalThrows("for (;false;) let x;", SyntaxError); +TestLocalThrows("switch (true) { case true: let x; }", SyntaxError); +TestLocalThrows("switch (true) { default: let x; }", SyntaxError); +// Test const declarations with initialisers in statement positions. +TestLocalThrows("if (true) const x = 1;", SyntaxError); +TestLocalThrows("if (true) {} else const x = 1;", SyntaxError); +TestLocalThrows("do const x = 1; while (false)", SyntaxError); +TestLocalThrows("while (false) const x = 1;", SyntaxError); +TestLocalThrows("label: const x = 1;", SyntaxError); +TestLocalThrows("for (;false;) const x = 1;", SyntaxError); +TestLocalThrows("switch (true) { case true: const x = 1; }", SyntaxError); +TestLocalThrows("switch (true) { default: const x = 1; }", SyntaxError); + +// Test const declarations without initialisers. +TestLocalThrows("const x;", SyntaxError); +TestLocalThrows("const x = 1, y;", SyntaxError); +TestLocalThrows("const x, y = 1;", SyntaxError); + +// Test const declarations without initialisers in statement positions. +TestLocalThrows("if (true) const x;", SyntaxError); +TestLocalThrows("if (true) {} else const x;", SyntaxError); +TestLocalThrows("do const x; while (false)", SyntaxError); +TestLocalThrows("while (false) const x;", SyntaxError); +TestLocalThrows("label: const x;", SyntaxError); +TestLocalThrows("for (;false;) const x;", SyntaxError); +TestLocalThrows("switch (true) { case true: const x; }", SyntaxError); +TestLocalThrows("switch (true) { default: const x; }", SyntaxError); + +// Test var declarations in statement positions. TestLocalDoesNotThrow("if (true) var x;"); +TestLocalDoesNotThrow("if (true) {} else var x;"); TestLocalDoesNotThrow("do var x; while (false)"); TestLocalDoesNotThrow("while (false) var x;"); +TestLocalDoesNotThrow("label: var x;"); +TestLocalDoesNotThrow("for (;false;) var x;"); +TestLocalDoesNotThrow("switch (true) { case true: var x; }"); +TestLocalDoesNotThrow("switch (true) { default: var x; }"); + +// Test function declarations in source element and +// non-strict statement positions. +function f() { + // Non-strict source element positions. + function g0() { + "use strict"; + // Strict source element positions. + function h() { } + { + function h1() { } + } + } + { + function g1() { } + } +} +f(); + +// Test function declarations in statement position in strict mode. +TestLocalThrows("function f() { if (true) function g() {}", SyntaxError); +TestLocalThrows("function f() { if (true) {} else function g() {}", SyntaxError); +TestLocalThrows("function f() { do function g() {} while (false)", SyntaxError); +TestLocalThrows("function f() { while (false) function g() {}", SyntaxError); +TestLocalThrows("function f() { label: function g() {}", SyntaxError); +TestLocalThrows("function f() { for (;false;) function g() {}", SyntaxError); +TestLocalThrows("function f() { switch (true) { case true: function g() {} }", SyntaxError); +TestLocalThrows("function f() { switch (true) { default: function g() {} }", SyntaxError); diff --git a/deps/v8/test/mjsunit/harmony/block-let-semantics.js b/deps/v8/test/mjsunit/harmony/block-let-semantics.js index 198c3b4fb9..d14e7cd369 100644 --- a/deps/v8/test/mjsunit/harmony/block-let-semantics.js +++ b/deps/v8/test/mjsunit/harmony/block-let-semantics.js @@ -25,7 +25,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-block-scoping +// Flags: --harmony-scoping + +// TODO(ES6): properly activate extended mode +"use strict"; // Test temporal dead zone semantics of let bound variables in // function and block scopes. @@ -61,6 +64,7 @@ TestAll('let x = x + 1'); TestAll('let x = x += 1'); TestAll('let x = x++'); TestAll('let x = ++x'); +TestAll('const x = x + 1'); // Use before initialization in prior statement. TestAll('x + 1; let x;'); @@ -68,25 +72,36 @@ TestAll('x = 1; let x;'); TestAll('x += 1; let x;'); TestAll('++x; let x;'); TestAll('x++; let x;'); +TestAll('let y = x; const x = 1;'); TestAll('f(); let x; function f() { return x + 1; }'); TestAll('f(); let x; function f() { x = 1; }'); TestAll('f(); let x; function f() { x += 1; }'); TestAll('f(); let x; function f() { ++x; }'); TestAll('f(); let x; function f() { x++; }'); +TestAll('f(); const x = 1; function f() { return x; }'); TestAll('f()(); let x; function f() { return function() { return x + 1; } }'); TestAll('f()(); let x; function f() { return function() { x = 1; } }'); TestAll('f()(); let x; function f() { return function() { x += 1; } }'); TestAll('f()(); let x; function f() { return function() { ++x; } }'); TestAll('f()(); let x; function f() { return function() { x++; } }'); +TestAll('f()(); const x = 1; function f() { return function() { return x; } }'); -// Use in before initialization with a dynamic lookup. +// Use before initialization with a dynamic lookup. TestAll('eval("x + 1;"); let x;'); TestAll('eval("x = 1;"); let x;'); TestAll('eval("x += 1;"); let x;'); TestAll('eval("++x;"); let x;'); TestAll('eval("x++;"); let x;'); +TestAll('eval("x"); const x = 1;'); + +// Use before initialization with check for eval-shadowed bindings. +TestAll('function f() { eval("var y = 2;"); x + 1; }; f(); let x;'); +TestAll('function f() { eval("var y = 2;"); x = 1; }; f(); let x;'); +TestAll('function f() { eval("var y = 2;"); x += 1; }; f(); let x;'); +TestAll('function f() { eval("var y = 2;"); ++x; }; f(); let x;'); +TestAll('function f() { eval("var y = 2;"); x++; }; f(); let x;'); // Test that variables introduced by function declarations are created and // initialized upon entering a function / block scope. @@ -115,7 +130,7 @@ TestAll('{ function k() { return 0; } }; k(); '); // Test that a function declaration sees the scope it resides in. function f2() { - let m, n; + let m, n, o, p; { m = g; function g() { @@ -132,7 +147,44 @@ function f2() { function h() { return b + c; } - let b = 3; + let c = 3; } assertEquals(5, n()); + + { + o = i; + function i() { + return d; + } + let d = 4; + } + assertEquals(4, o()); + + try { + throw 5; + } catch(e) { + p = j; + function j() { + return e + f; + } + let f = 6; + } + assertEquals(11, p()); +} +f2(); + +// Test that resolution of let bound variables works with scopes that call eval. +function outer() { + function middle() { + function inner() { + return x; + } + eval("1 + 1"); + return x + inner(); + } + + let x = 1; + return middle(); } + +assertEquals(2, outer()); diff --git a/deps/v8/test/mjsunit/harmony/block-scoping.js b/deps/v8/test/mjsunit/harmony/block-scoping.js index 266e380725..31194d99fd 100644 --- a/deps/v8/test/mjsunit/harmony/block-scoping.js +++ b/deps/v8/test/mjsunit/harmony/block-scoping.js @@ -25,9 +25,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --allow-natives-syntax --harmony-block-scoping +// Flags: --allow-natives-syntax --harmony-scoping // Test functionality of block scopes. +// TODO(ES6): properly activate extended mode +"use strict"; + // Hoisting of var declarations. function f1() { { @@ -44,12 +47,16 @@ f1(); function f2(one) { var x = one + 1; let y = one + 2; + const u = one + 4; { let z = one + 3; + const v = one + 5; assertEquals(1, eval('one')); assertEquals(2, eval('x')); assertEquals(3, eval('y')); assertEquals(4, eval('z')); + assertEquals(5, eval('u')); + assertEquals(6, eval('v')); } } f2(1); @@ -59,12 +66,17 @@ f2(1); function f3(one) { var x = one + 1; let y = one + 2; + const u = one + 4; { let z = one + 3; + const v = one + 5; assertEquals(1, one); assertEquals(2, x); assertEquals(3, y); assertEquals(4, z); + assertEquals(5, u); + assertEquals(6, v); + } } f3(1); @@ -74,13 +86,17 @@ f3(1); function f4(one) { var x = one + 1; let y = one + 2; + const u = one + 4; { let z = one + 3; + const v = one + 5; function f() { assertEquals(1, eval('one')); assertEquals(2, eval('x')); assertEquals(3, eval('y')); assertEquals(4, eval('z')); + assertEquals(5, eval('u')); + assertEquals(6, eval('v')); }; } } @@ -91,13 +107,17 @@ f4(1); function f5(one) { var x = one + 1; let y = one + 2; + const u = one + 4; { let z = one + 3; + const v = one + 5; function f() { assertEquals(1, one); assertEquals(2, x); assertEquals(3, y); assertEquals(4, z); + assertEquals(5, u); + assertEquals(6, v); }; } } @@ -107,8 +127,10 @@ f5(1); // Return from block. function f6() { let x = 1; + const u = 3; { let y = 2; + const v = 4; return x + y; } } @@ -120,13 +142,26 @@ function f7(a) { let b = 1; var c = 1; var d = 1; - { // let variables shadowing argument, let and var variables + const e = 1; + { // let variables shadowing argument, let, const and var variables let a = 2; let b = 2; let c = 2; + let e = 2; + assertEquals(2,a); + assertEquals(2,b); + assertEquals(2,c); + assertEquals(2,e); + } + { // const variables shadowing argument, let, const and var variables + const a = 2; + const b = 2; + const c = 2; + const e = 2; assertEquals(2,a); assertEquals(2,b); assertEquals(2,c); + assertEquals(2,e); } try { throw 'stuff1'; @@ -156,6 +191,12 @@ function f7(a) { } catch (c) { // catch variable shadowing var variable assertEquals('stuff3',c); + { + // const variable shadowing catch variable + const c = 3; + assertEquals(3,c); + } + assertEquals('stuff3',c); try { throw 'stuff4'; } catch(c) { @@ -178,14 +219,16 @@ function f7(a) { c = 2; } assertEquals(1,c); - (function(a,b,c) { - // arguments shadowing argument, let and var variable + (function(a,b,c,e) { + // arguments shadowing argument, let, const and var variable a = 2; b = 2; c = 2; + e = 2; assertEquals(2,a); assertEquals(2,b); assertEquals(2,c); + assertEquals(2,e); // var variable shadowing var variable var d = 2; })(1,1); @@ -193,24 +236,30 @@ function f7(a) { assertEquals(1,b); assertEquals(1,c); assertEquals(1,d); + assertEquals(1,e); } f7(1); -// Ensure let variables are block local and var variables function local. +// Ensure let and const variables are block local +// and var variables function local. function f8() { var let_accessors = []; var var_accessors = []; + var const_accessors = []; for (var i = 0; i < 10; i++) { let x = i; var y = i; + const z = i; let_accessors[i] = function() { return x; } var_accessors[i] = function() { return y; } + const_accessors[i] = function() { return z; } } for (var j = 0; j < 10; j++) { y = j + 10; assertEquals(j, let_accessors[j]()); assertEquals(y, var_accessors[j]()); + assertEquals(j, const_accessors[j]()); } } f8(); diff --git a/deps/v8/test/mjsunit/harmony/collections.js b/deps/v8/test/mjsunit/harmony/collections.js new file mode 100644 index 0000000000..412e6f14c3 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/collections.js @@ -0,0 +1,314 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-collections --expose-gc + + +// Test valid getter and setter calls on Sets. +function TestValidSetCalls(m) { + assertDoesNotThrow(function () { m.add(new Object) }); + assertDoesNotThrow(function () { m.has(new Object) }); + assertDoesNotThrow(function () { m.delete(new Object) }); +} +TestValidSetCalls(new Set); + + +// Test valid getter and setter calls on Maps and WeakMaps +function TestValidMapCalls(m) { + assertDoesNotThrow(function () { m.get(new Object) }); + assertDoesNotThrow(function () { m.set(new Object) }); + assertDoesNotThrow(function () { m.has(new Object) }); + assertDoesNotThrow(function () { m.delete(new Object) }); +} +TestValidMapCalls(new Map); +TestValidMapCalls(new WeakMap); + + +// Test invalid getter and setter calls for WeakMap only +function TestInvalidCalls(m) { + assertThrows(function () { m.get(undefined) }, TypeError); + assertThrows(function () { m.set(undefined, 0) }, TypeError); + assertThrows(function () { m.get(null) }, TypeError); + assertThrows(function () { m.set(null, 0) }, TypeError); + assertThrows(function () { m.get(0) }, TypeError); + assertThrows(function () { m.set(0, 0) }, TypeError); + assertThrows(function () { m.get('a-key') }, TypeError); + assertThrows(function () { m.set('a-key', 0) }, TypeError); +} +TestInvalidCalls(new WeakMap); + + +// Test expected behavior for Sets +function TestSet(set, key) { + assertFalse(set.has(key)); + set.add(key); + assertTrue(set.has(key)); + set.delete(key); + assertFalse(set.has(key)); +} +function TestSetBehavior(set) { + for (var i = 0; i < 20; i++) { + TestSet(set, new Object); + TestSet(set, i); + TestSet(set, i / 100); + TestSet(set, 'key-' + i); + } + var keys = [ +0, -0, +Infinity, -Infinity, true, false, null, undefined ]; + for (var i = 0; i < keys.length; i++) { + TestSet(set, keys[i]); + } +} +TestSetBehavior(new Set); + + +// Test expected mapping behavior for Maps and WeakMaps +function TestMapping(map, key, value) { + map.set(key, value); + assertSame(value, map.get(key)); +} +function TestMapBehavior1(m) { + TestMapping(m, new Object, 23); + TestMapping(m, new Object, 'the-value'); + TestMapping(m, new Object, new Object); +} +TestMapBehavior1(new Map); +TestMapBehavior1(new WeakMap); + + +// Test expected mapping behavior for Maps only +function TestMapBehavior2(m) { + for (var i = 0; i < 20; i++) { + TestMapping(m, i, new Object); + TestMapping(m, i / 10, new Object); + TestMapping(m, 'key-' + i, new Object); + } + var keys = [ +0, -0, +Infinity, -Infinity, true, false, null, undefined ]; + for (var i = 0; i < keys.length; i++) { + TestMapping(m, keys[i], new Object); + } +} +TestMapBehavior2(new Map); + + +// Test expected querying behavior of Maps and WeakMaps +function TestQuery(m) { + var key = new Object; + TestMapping(m, key, 'to-be-present'); + assertTrue(m.has(key)); + assertFalse(m.has(new Object)); + TestMapping(m, key, undefined); + assertFalse(m.has(key)); + assertFalse(m.has(new Object)); +} +TestQuery(new Map); +TestQuery(new WeakMap); + + +// Test expected deletion behavior of Maps and WeakMaps +function TestDelete(m) { + var key = new Object; + TestMapping(m, key, 'to-be-deleted'); + assertTrue(m.delete(key)); + assertFalse(m.delete(key)); + assertFalse(m.delete(new Object)); + assertSame(m.get(key), undefined); +} +TestDelete(new Map); +TestDelete(new WeakMap); + + +// Test GC of Maps and WeakMaps with entry +function TestGC1(m) { + var key = new Object; + m.set(key, 'not-collected'); + gc(); + assertSame('not-collected', m.get(key)); +} +TestGC1(new Map); +TestGC1(new WeakMap); + + +// Test GC of Maps and WeakMaps with chained entries +function TestGC2(m) { + var head = new Object; + for (key = head, i = 0; i < 10; i++, key = m.get(key)) { + m.set(key, new Object); + } + gc(); + var count = 0; + for (key = head; key != undefined; key = m.get(key)) { + count++; + } + assertEquals(11, count); +} +TestGC2(new Map); +TestGC2(new WeakMap); + + +// Test property attribute [[Enumerable]] +function TestEnumerable(func) { + function props(x) { + var array = []; + for (var p in x) array.push(p); + return array.sort(); + } + assertArrayEquals([], props(func)); + assertArrayEquals([], props(func.prototype)); + assertArrayEquals([], props(new func())); +} +TestEnumerable(Set); +TestEnumerable(Map); +TestEnumerable(WeakMap); + + +// Test arbitrary properties on Maps and WeakMaps +function TestArbitrary(m) { + function TestProperty(map, property, value) { + map[property] = value; + assertEquals(value, map[property]); + } + for (var i = 0; i < 20; i++) { + TestProperty(m, i, 'val' + i); + TestProperty(m, 'foo' + i, 'bar' + i); + } + TestMapping(m, new Object, 'foobar'); +} +TestArbitrary(new Map); +TestArbitrary(new WeakMap); + + +// Test direct constructor call +assertTrue(Set() instanceof Set); +assertTrue(Map() instanceof Map); +assertTrue(WeakMap() instanceof WeakMap); + + +// Test whether NaN values as keys are treated correctly. +var s = new Set; +assertFalse(s.has(NaN)); +assertFalse(s.has(NaN + 1)); +assertFalse(s.has(23)); +s.add(NaN); +assertTrue(s.has(NaN)); +assertTrue(s.has(NaN + 1)); +assertFalse(s.has(23)); +var m = new Map; +assertFalse(m.has(NaN)); +assertFalse(m.has(NaN + 1)); +assertFalse(m.has(23)); +m.set(NaN, 'a-value'); +assertTrue(m.has(NaN)); +assertTrue(m.has(NaN + 1)); +assertFalse(m.has(23)); + + +// Test some common JavaScript idioms for Sets +var s = new Set; +assertTrue(s instanceof Set); +assertTrue(Set.prototype.add instanceof Function) +assertTrue(Set.prototype.has instanceof Function) +assertTrue(Set.prototype.delete instanceof Function) + + +// Test some common JavaScript idioms for Maps +var m = new Map; +assertTrue(m instanceof Map); +assertTrue(Map.prototype.set instanceof Function) +assertTrue(Map.prototype.get instanceof Function) +assertTrue(Map.prototype.has instanceof Function) +assertTrue(Map.prototype.delete instanceof Function) + + +// Test some common JavaScript idioms for WeakMaps +var m = new WeakMap; +assertTrue(m instanceof WeakMap); +assertTrue(WeakMap.prototype.set instanceof Function) +assertTrue(WeakMap.prototype.get instanceof Function) +assertTrue(WeakMap.prototype.has instanceof Function) +assertTrue(WeakMap.prototype.delete instanceof Function) + + +// Regression test for WeakMap prototype. +assertTrue(WeakMap.prototype.constructor === WeakMap) +assertTrue(Object.getPrototypeOf(WeakMap.prototype) === Object.prototype) + + +// Regression test for issue 1617: The prototype of the WeakMap constructor +// needs to be unique (i.e. different from the one of the Object constructor). +assertFalse(WeakMap.prototype === Object.prototype); +var o = Object.create({}); +assertFalse("get" in o); +assertFalse("set" in o); +assertEquals(undefined, o.get); +assertEquals(undefined, o.set); +var o = Object.create({}, { myValue: { + value: 10, + enumerable: false, + configurable: true, + writable: true +}}); +assertEquals(10, o.myValue); + + +// Regression test for issue 1884: Invoking any of the methods for Harmony +// maps, sets, or weak maps, with a wrong type of receiver should be throwing +// a proper TypeError. +var alwaysBogus = [ undefined, null, true, "x", 23, {} ]; +var bogusReceiversTestSet = [ + { proto: Set.prototype, + funcs: [ 'add', 'has', 'delete' ], + receivers: alwaysBogus.concat([ new Map, new WeakMap ]), + }, + { proto: Map.prototype, + funcs: [ 'get', 'set', 'has', 'delete' ], + receivers: alwaysBogus.concat([ new Set, new WeakMap ]), + }, + { proto: WeakMap.prototype, + funcs: [ 'get', 'set', 'has', 'delete' ], + receivers: alwaysBogus.concat([ new Set, new Map ]), + }, +]; +function TestBogusReceivers(testSet) { + for (var i = 0; i < testSet.length; i++) { + var proto = testSet[i].proto; + var funcs = testSet[i].funcs; + var receivers = testSet[i].receivers; + for (var j = 0; j < funcs.length; j++) { + var func = proto[funcs[j]]; + for (var k = 0; k < receivers.length; k++) { + assertThrows(function () { func.call(receivers[k], {}) }, TypeError); + } + } + } +} +TestBogusReceivers(bogusReceiversTestSet); + + +// Stress Test +// There is a proposed stress-test available at the es-discuss mailing list +// which cannot be reasonably automated. Check it out by hand if you like: +// https://mail.mozilla.org/pipermail/es-discuss/2011-May/014096.html
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/harmony/debug-blockscopes.js b/deps/v8/test/mjsunit/harmony/debug-blockscopes.js index 0230e84b5e..10aac2dbd4 100644 --- a/deps/v8/test/mjsunit/harmony/debug-blockscopes.js +++ b/deps/v8/test/mjsunit/harmony/debug-blockscopes.js @@ -25,13 +25,15 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --harmony-block-scoping +// Flags: --expose-debug-as debug --harmony-scoping // The functions used for testing backtraces. They are at the top to make the // testing of source line/column easier. +// TODO(ES6): properly activate extended mode +"use strict"; // Get the Debug object exposed from the debug context global object. -Debug = debug.Debug; +var Debug = debug.Debug; var test_name; var listener_delegate; @@ -76,6 +78,7 @@ function EndTest() { end_test_count++; } +var global_object = this; // Check that the scope chain contains the expected types of scopes. function CheckScopeChain(scopes, exec_state) { @@ -89,7 +92,7 @@ function CheckScopeChain(scopes, exec_state) { if (scopes[i] == debug.ScopeType.Global) { // Objects don't have same class (one is "global", other is "Object", // so just check the properties directly. - assertPropertiesEqual(this, scope.scopeObject().value()); + assertPropertiesEqual(global_object, scope.scopeObject().value()); } } @@ -329,138 +332,139 @@ local_7(1); EndTest(); -// Single empty with block. -BeginTest("With block 1"); +// Simple closure formed by returning an inner function referering to an outer +// block local variable and an outer function's parameter. +BeginTest("Closure 1"); -function with_block_1() { - with({}) { - debugger; +function closure_1(a) { + var x = 2; + let y = 3; + if (true) { + let z = 4; + function f() { + debugger; + return a + x + y + z; + }; + return f; } } listener_delegate = function(exec_state) { - CheckScopeChain([debug.ScopeType.With, - debug.ScopeType.Local, + CheckScopeChain([debug.ScopeType.Local, + debug.ScopeType.Block, + debug.ScopeType.Closure, debug.ScopeType.Global], exec_state); CheckScopeContent({}, 0, exec_state); - CheckScopeContent({}, 1, exec_state); + CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); }; -with_block_1(); +closure_1(1)(); EndTest(); -// Nested empty with blocks. -BeginTest("With block 2"); +// Simple for-in loop over the keys of an object. +BeginTest("For loop 1"); -function with_block_2() { - with({}) { - with({}) { - debugger; - } +function for_loop_1() { + for (let x in {y:undefined}) { + debugger; } } listener_delegate = function(exec_state) { - CheckScopeChain([debug.ScopeType.With, - debug.ScopeType.With, + CheckScopeChain([debug.ScopeType.Block, debug.ScopeType.Local, debug.ScopeType.Global], exec_state); - CheckScopeContent({}, 0, exec_state); - CheckScopeContent({}, 1, exec_state); - CheckScopeContent({}, 2, exec_state); + CheckScopeContent({x:'y'}, 0, exec_state); + // The function scope contains a temporary iteration variable. + CheckScopeContent({x:'y'}, 1, exec_state); }; -with_block_2(); +for_loop_1(); EndTest(); -// With block using an in-place object literal. -BeginTest("With block 3"); +// For-in loop over the keys of an object with a block scoped let variable +// shadowing the iteration variable. +BeginTest("For loop 2"); -function with_block_3() { - with({a:1,b:2}) { +function for_loop_2() { + for (let x in {y:undefined}) { + let x = 3; debugger; } } listener_delegate = function(exec_state) { - CheckScopeChain([debug.ScopeType.With, + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.Block, debug.ScopeType.Local, debug.ScopeType.Global], exec_state); - CheckScopeContent({a:1,b:2}, 0, exec_state); + CheckScopeContent({x:3}, 0, exec_state); + CheckScopeContent({x:'y'}, 1, exec_state); + // The function scope contains a temporary iteration variable. + CheckScopeContent({x:'y'}, 2, exec_state); }; -with_block_3(); +for_loop_2(); EndTest(); -// Nested with blocks using in-place object literals. -BeginTest("With block 4"); +// Simple for loop. +BeginTest("For loop 3"); -function with_block_4() { - with({a:1,b:2}) { - with({a:2,b:1}) { - debugger; - } +function for_loop_3() { + for (let x = 3; x < 4; ++x) { + debugger; } } listener_delegate = function(exec_state) { - CheckScopeChain([debug.ScopeType.With, - debug.ScopeType.With, + CheckScopeChain([debug.ScopeType.Block, debug.ScopeType.Local, debug.ScopeType.Global], exec_state); - CheckScopeContent({a:2,b:1}, 0, exec_state); - CheckScopeContent({a:1,b:2}, 1, exec_state); + CheckScopeContent({x:3}, 0, exec_state); + CheckScopeContent({}, 1, exec_state); }; -with_block_4(); +for_loop_3(); EndTest(); -// With block and a block local variable. -BeginTest("With block 5"); +// For loop with a block scoped let variable shadowing the iteration variable. +BeginTest("For loop 4"); -function with_block_5() { - with({a:1}) { - let a = 2; +function for_loop_4() { + for (let x = 3; x < 4; ++x) { + let x = 5; debugger; } } listener_delegate = function(exec_state) { CheckScopeChain([debug.ScopeType.Block, - debug.ScopeType.With, + debug.ScopeType.Block, debug.ScopeType.Local, debug.ScopeType.Global], exec_state); - CheckScopeContent({a:2}, 0, exec_state); - CheckScopeContent({a:1}, 1, exec_state); + CheckScopeContent({x:5}, 0, exec_state); + CheckScopeContent({x:3}, 1, exec_state); + CheckScopeContent({}, 2, exec_state); }; -with_block_5(); +for_loop_4(); EndTest(); -// Simple closure formed by returning an inner function referering to an outer -// block local variable and an outer function's parameter. -BeginTest("Closure 1"); +// For loop with two variable declarations. +BeginTest("For loop 5"); -function closure_1(a) { - var x = 2; - let y = 3; - if (true) { - let z = 4; - function f() { - debugger; - return a + x + y + z; - }; - return f; +function for_loop_5() { + for (let x = 3, y = 5; x < 4; ++x) { + debugger; } } listener_delegate = function(exec_state) { - CheckScopeChain([debug.ScopeType.Local, - debug.ScopeType.Block, - debug.ScopeType.Closure, + CheckScopeChain([debug.ScopeType.Block, + debug.ScopeType.Local, debug.ScopeType.Global], exec_state); - CheckScopeContent({}, 0, exec_state); - CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); + CheckScopeContent({x:3,y:5}, 0, exec_state); + CheckScopeContent({}, 1, exec_state); }; -closure_1(1)(); +for_loop_5(); EndTest(); diff --git a/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js b/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js index 549960a289..d6ce8b2b6a 100644 --- a/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js +++ b/deps/v8/test/mjsunit/harmony/debug-evaluate-blockscopes.js @@ -25,11 +25,17 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --harmony-block-scoping +// Flags: --expose-debug-as debug --harmony-scoping // Test debug evaluation for functions without local context, but with // nested catch contexts. +// TODO(ES6): properly activate extended mode +"use strict"; + +var x; +var result; + function f() { { // Line 1. let i = 1; // Line 2. @@ -42,7 +48,7 @@ function f() { }; // Get the Debug object exposed from the debug context global object. -Debug = debug.Debug +var Debug = debug.Debug // Set breakpoint on line 6. var bp = Debug.setBreakPoint(f, 6); diff --git a/deps/v8/test/mjsunit/harmony/proxies-example-membrane.js b/deps/v8/test/mjsunit/harmony/proxies-example-membrane.js new file mode 100644 index 0000000000..c6e7f9f9b1 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/proxies-example-membrane.js @@ -0,0 +1,512 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony + + +// A simple no-op handler. Adapted from: +// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#examplea_no-op_forwarding_proxy + +function createHandler(obj) { + return { + getOwnPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + if (desc !== undefined) desc.configurable = true; + return desc; + }, + getPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + //var desc = Object.getPropertyDescriptor(obj, name); // not in ES5 + if (desc !== undefined) desc.configurable = true; + return desc; + }, + getOwnPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + }, + getPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + //return Object.getPropertyNames(obj); // not in ES5 + }, + defineProperty: function(name, desc) { + Object.defineProperty(obj, name, desc); + }, + delete: function(name) { + return delete obj[name]; + }, + fix: function() { + if (Object.isFrozen(obj)) { + var result = {}; + Object.getOwnPropertyNames(obj).forEach(function(name) { + result[name] = Object.getOwnPropertyDescriptor(obj, name); + }); + return result; + } + // As long as obj is not frozen, the proxy won't allow itself to be fixed + return undefined; // will cause a TypeError to be thrown + }, + has: function(name) { return name in obj; }, + hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); }, + get: function(receiver, name) { return obj[name]; }, + set: function(receiver, name, val) { + obj[name] = val; // bad behavior when set fails in non-strict mode + return true; + }, + enumerate: function() { + var result = []; + for (var name in obj) { result.push(name); }; + return result; + }, + keys: function() { return Object.keys(obj); } + }; +} + + + +// Auxiliary definitions enabling tracking of object identity in output. + +var objectMap = new WeakMap; +var objectCounter = 0; + +function registerObject(x, s) { + if (x === Object(x) && !objectMap.has(x)) + objectMap.set(x, ++objectCounter + (s == undefined ? "" : ":" + s)); +} + +registerObject(this, "global"); +registerObject(Object.prototype, "Object.prototype"); + +function str(x) { + if (x === Object(x)) return "[" + typeof x + " " + objectMap.get(x) + "]"; + if (typeof x == "string") return "\"" + x + "\""; + return "" + x; +} + + + +// A simple membrane. Adapted from: +// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane + +function createSimpleMembrane(target) { + var enabled = true; + + function wrap(obj) { + registerObject(obj); + print("wrap enter", str(obj)); + try { + var x = wrap2(obj); + registerObject(x, "wrapped"); + print("wrap exit", str(obj), "as", str(x)); + return x; + } catch(e) { + print("wrap exception", str(e)); + throw e; + } + } + + function wrap2(obj) { + if (obj !== Object(obj)) { + return obj; + } + + function wrapCall(fun, that, args) { + registerObject(that); + print("wrapCall enter", fun, str(that)); + try { + var x = wrapCall2(fun, that, args); + print("wrapCall exit", fun, str(that), "returning", str(x)); + return x; + } catch(e) { + print("wrapCall exception", fun, str(that), str(e)); + throw e; + } + } + + function wrapCall2(fun, that, args) { + if (!enabled) { throw new Error("disabled"); } + try { + return wrap(fun.apply(that, Array.prototype.map.call(args, wrap))); + } catch (e) { + throw wrap(e); + } + } + + var baseHandler = createHandler(obj); + var handler = Proxy.create(Object.freeze({ + get: function(receiver, name) { + return function() { + var arg = (name === "get" || name == "set") ? arguments[1] : ""; + print("handler enter", name, arg); + var x = wrapCall(baseHandler[name], baseHandler, arguments); + print("handler exit", name, arg, "returning", str(x)); + return x; + } + } + })); + registerObject(baseHandler, "basehandler"); + registerObject(handler, "handler"); + + if (typeof obj === "function") { + function callTrap() { + print("call trap enter", str(obj), str(this)); + var x = wrapCall(obj, wrap(this), arguments); + print("call trap exit", str(obj), str(this), "returning", str(x)); + return x; + } + function constructTrap() { + if (!enabled) { throw new Error("disabled"); } + try { + function forward(args) { return obj.apply(this, args) } + return wrap(new forward(Array.prototype.map.call(arguments, wrap))); + } catch (e) { + throw wrap(e); + } + } + return Proxy.createFunction(handler, callTrap, constructTrap); + } else { + var prototype = wrap(Object.getPrototypeOf(obj)); + return Proxy.create(handler, prototype); + } + } + + var gate = Object.freeze({ + enable: function() { enabled = true; }, + disable: function() { enabled = false; } + }); + + return Object.freeze({ + wrapper: wrap(target), + gate: gate + }); +} + + +var o = { + a: 6, + b: {bb: 8}, + f: function(x) { return x }, + g: function(x) { return x.a }, + h: function(x) { this.q = x } +}; +o[2] = {c: 7}; +var m = createSimpleMembrane(o); +var w = m.wrapper; +print("o =", str(o)) +print("w =", str(w)); + +var f = w.f; +var x = f(66); +var x = f({a: 1}); +var x = w.f({a: 1}); +var a = x.a; +assertEquals(6, w.a); +assertEquals(8, w.b.bb); +assertEquals(7, w[2]["c"]); +assertEquals(undefined, w.c); +assertEquals(1, w.f(1)); +assertEquals(1, w.f({a: 1}).a); +assertEquals(2, w.g({a: 2})); +assertEquals(3, (w.r = {a: 3}).a); +assertEquals(3, w.r.a); +assertEquals(3, o.r.a); +w.h(3); +assertEquals(3, w.q); +assertEquals(3, o.q); +assertEquals(4, (new w.h(4)).q); + +var wb = w.b; +var wr = w.r; +var wf = w.f; +var wf3 = w.f(3); +var wfx = w.f({a: 6}); +var wgx = w.g({a: {aa: 7}}); +var wh4 = new w.h(4); +m.gate.disable(); +assertEquals(3, wf3); +assertThrows(function() { w.a }, Error); +assertThrows(function() { w.r }, Error); +assertThrows(function() { w.r = {a: 4} }, Error); +assertThrows(function() { o.r.a }, Error); +assertEquals("object", typeof o.r); +assertEquals(5, (o.r = {a: 5}).a); +assertEquals(5, o.r.a); +assertThrows(function() { w[1] }, Error); +assertThrows(function() { w.c }, Error); +assertThrows(function() { wb.bb }, Error); +assertThrows(function() { wr.a }, Error); +assertThrows(function() { wf(4) }, Error); +assertThrows(function() { wfx.a }, Error); +assertThrows(function() { wgx.aa }, Error); +assertThrows(function() { wh4.q }, Error); + +m.gate.enable(); +assertEquals(6, w.a); +assertEquals(5, w.r.a); +assertEquals(5, o.r.a); +assertEquals(7, w.r = 7); +assertEquals(7, w.r); +assertEquals(7, o.r); +assertEquals(8, w.b.bb); +assertEquals(7, w[2]["c"]); +assertEquals(undefined, w.c); +assertEquals(8, wb.bb); +assertEquals(3, wr.a); +assertEquals(4, wf(4)); +assertEquals(3, wf3); +assertEquals(6, wfx.a); +assertEquals(7, wgx.aa); +assertEquals(4, wh4.q); + + +// An identity-preserving membrane. Adapted from: +// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving_membrane + +function createMembrane(wetTarget) { + var wet2dry = WeakMap(); + var dry2wet = WeakMap(); + + function asDry(obj) { + registerObject(obj) + print("asDry enter", str(obj)) + try { + var x = asDry2(obj); + registerObject(x, "dry"); + print("asDry exit", str(obj), "as", str(x)); + return x; + } catch(e) { + print("asDry exception", str(e)); + throw e; + } + } + function asDry2(wet) { + if (wet !== Object(wet)) { + // primitives provide only irrevocable knowledge, so don't + // bother wrapping it. + return wet; + } + var dryResult = wet2dry.get(wet); + if (dryResult) { return dryResult; } + + var wetHandler = createHandler(wet); + var dryRevokeHandler = Proxy.create(Object.freeze({ + get: function(receiver, name) { + return function() { + var arg = (name === "get" || name == "set") ? arguments[1] : ""; + print("dry handler enter", name, arg); + var optWetHandler = dry2wet.get(dryRevokeHandler); + try { + var x = asDry(optWetHandler[name].apply( + optWetHandler, Array.prototype.map.call(arguments, asWet))); + print("dry handler exit", name, arg, "returning", str(x)); + return x; + } catch (eWet) { + var x = asDry(eWet); + print("dry handler exception", name, arg, "throwing", str(x)); + throw x; + } + }; + } + })); + dry2wet.set(dryRevokeHandler, wetHandler); + + if (typeof wet === "function") { + function callTrap() { + print("dry call trap enter", str(this)); + var x = asDry(wet.apply( + asWet(this), Array.prototype.map.call(arguments, asWet))); + print("dry call trap exit", str(this), "returning", str(x)); + return x; + } + function constructTrap() { + function forward(args) { return wet.apply(this, args) } + return asDry(new forward(Array.prototype.map.call(arguments, asWet))); + } + dryResult = + Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap); + } else { + dryResult = + Proxy.create(dryRevokeHandler, asDry(Object.getPrototypeOf(wet))); + } + wet2dry.set(wet, dryResult); + dry2wet.set(dryResult, wet); + return dryResult; + } + + function asWet(obj) { + registerObject(obj) + print("asWet enter", str(obj)) + try { + var x = asWet2(obj) + registerObject(x, "wet") + print("asWet exit", str(obj), "as", str(x)) + return x + } catch(e) { + print("asWet exception", str(e)) + throw e + } + } + function asWet2(dry) { + if (dry !== Object(dry)) { + // primitives provide only irrevocable knowledge, so don't + // bother wrapping it. + return dry; + } + var wetResult = dry2wet.get(dry); + if (wetResult) { return wetResult; } + + var dryHandler = createHandler(dry); + var wetRevokeHandler = Proxy.create(Object.freeze({ + get: function(receiver, name) { + return function() { + var arg = (name === "get" || name == "set") ? arguments[1] : ""; + print("wet handler enter", name, arg); + var optDryHandler = wet2dry.get(wetRevokeHandler); + try { + var x = asWet(optDryHandler[name].apply( + optDryHandler, Array.prototype.map.call(arguments, asDry))); + print("wet handler exit", name, arg, "returning", str(x)); + return x; + } catch (eDry) { + var x = asWet(eDry); + print("wet handler exception", name, arg, "throwing", str(x)); + throw x; + } + }; + } + })); + wet2dry.set(wetRevokeHandler, dryHandler); + + if (typeof dry === "function") { + function callTrap() { + print("wet call trap enter", str(this)); + var x = asWet(dry.apply( + asDry(this), Array.prototype.map.call(arguments, asDry))); + print("wet call trap exit", str(this), "returning", str(x)); + return x; + } + function constructTrap() { + function forward(args) { return dry.apply(this, args) } + return asWet(new forward(Array.prototype.map.call(arguments, asDry))); + } + wetResult = + Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap); + } else { + wetResult = + Proxy.create(wetRevokeHandler, asWet(Object.getPrototypeOf(dry))); + } + dry2wet.set(dry, wetResult); + wet2dry.set(wetResult, dry); + return wetResult; + } + + var gate = Object.freeze({ + revoke: function() { + dry2wet = wet2dry = Object.freeze({ + get: function(key) { throw new Error("revoked"); }, + set: function(key, val) { throw new Error("revoked"); } + }); + } + }); + + return Object.freeze({ wrapper: asDry(wetTarget), gate: gate }); +} + + +var receiver +var argument +var o = { + a: 6, + b: {bb: 8}, + f: function(x) { receiver = this; argument = x; return x }, + g: function(x) { receiver = this; argument = x; return x.a }, + h: function(x) { receiver = this; argument = x; this.q = x }, + s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this } +} +o[2] = {c: 7} +var m = createMembrane(o) +var w = m.wrapper +print("o =", str(o)) +print("w =", str(w)) + +var f = w.f +var x = f(66) +var x = f({a: 1}) +var x = w.f({a: 1}) +var a = x.a +assertEquals(6, w.a) +assertEquals(8, w.b.bb) +assertEquals(7, w[2]["c"]) +assertEquals(undefined, w.c) +assertEquals(1, w.f(1)) +assertSame(o, receiver) +assertEquals(1, w.f({a: 1}).a) +assertSame(o, receiver) +assertEquals(2, w.g({a: 2})) +assertSame(o, receiver) +assertSame(w, w.f(w)) +assertSame(o, receiver) +assertSame(o, argument) +assertSame(o, w.f(o)) +assertSame(o, receiver) +// Note that argument !== o, since o isn't dry, so gets wrapped wet again. +assertEquals(3, (w.r = {a: 3}).a) +assertEquals(3, w.r.a) +assertEquals(3, o.r.a) +w.h(3) +assertEquals(3, w.q) +assertEquals(3, o.q) +assertEquals(4, (new w.h(4)).q) +assertEquals(5, w.s(5).x.y) +assertSame(o, receiver) + +var wb = w.b +var wr = w.r +var wf = w.f +var wf3 = w.f(3) +var wfx = w.f({a: 6}) +var wgx = w.g({a: {aa: 7}}) +var wh4 = new w.h(4) +var ws5 = w.s(5) +var ws5x = ws5.x +m.gate.revoke() +assertEquals(3, wf3) +assertThrows(function() { w.a }, Error) +assertThrows(function() { w.r }, Error) +assertThrows(function() { w.r = {a: 4} }, Error) +assertThrows(function() { o.r.a }, Error) +assertEquals("object", typeof o.r) +assertEquals(5, (o.r = {a: 5}).a) +assertEquals(5, o.r.a) +assertThrows(function() { w[1] }, Error) +assertThrows(function() { w.c }, Error) +assertThrows(function() { wb.bb }, Error) +assertEquals(3, wr.a) +assertThrows(function() { wf(4) }, Error) +assertEquals(6, wfx.a) +assertEquals(7, wgx.aa) +assertThrows(function() { wh4.q }, Error) +assertThrows(function() { ws5.x }, Error) +assertThrows(function() { ws5x.y }, Error) diff --git a/deps/v8/test/mjsunit/harmony/proxies-for.js b/deps/v8/test/mjsunit/harmony/proxies-for.js new file mode 100644 index 0000000000..3d419c6dca --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/proxies-for.js @@ -0,0 +1,168 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-proxies + + +// Helper. + +function TestWithProxies(test, x, y, z) { + test(Proxy.create, x, y, z) + test(function(h) {return Proxy.createFunction(h, function() {})}, x, y, z) +} + + +// Iterate over a proxy. + +function TestForIn(properties, handler) { + TestWithProxies(TestForIn2, properties, handler) +} + +function TestForIn2(create, properties, handler) { + var p = create(handler) + var found = [] + for (var x in p) found.push(x) + assertArrayEquals(properties, found) +} + +TestForIn(["0", "a"], { + enumerate: function() { return [0, "a"] } +}) + +TestForIn(["null", "a"], { + enumerate: function() { return this.enumerate2() }, + enumerate2: function() { return [null, "a"] } +}) + +TestForIn(["b", "d"], { + getPropertyNames: function() { return ["a", "b", "c", "d", "e"] }, + getPropertyDescriptor: function(k) { + switch (k) { + case "a": return {enumerable: false, value: "3"}; + case "b": return {enumerable: true, get get() {}}; + case "c": return {value: 4}; + case "d": return {get enumerable() { return true }}; + default: return undefined; + } + } +}) + +TestForIn(["b", "a", "0", "c"], Proxy.create({ + get: function(pr, pk) { + return function() { return ["b", "a", 0, "c"] } + } +})) + + + +// Iterate over an object with a proxy prototype. + +function TestForInDerived(properties, handler) { + TestWithProxies(TestForInDerived2, properties, handler) +} + +function TestForInDerived2(create, properties, handler) { + var p = create(handler) + var o = Object.create(p) + o.z = 0 + var found = [] + for (var x in o) found.push(x) + assertArrayEquals(["z"].concat(properties), found) + + var oo = Object.create(o) + oo.y = 0 + var found = [] + for (var x in oo) found.push(x) + assertArrayEquals(["y", "z"].concat(properties), found) +} + +TestForInDerived(["0", "a"], { + enumerate: function() { return [0, "a"] }, + getPropertyDescriptor: function(k) { + return k == "0" || k == "a" ? {} : undefined + } +}) + +TestForInDerived(["null", "a"], { + enumerate: function() { return this.enumerate2() }, + enumerate2: function() { return [null, "a"] }, + getPropertyDescriptor: function(k) { + return k == "null" || k == "a" ? {} : undefined + } +}) + +TestForInDerived(["b", "d"], { + getPropertyNames: function() { return ["a", "b", "c", "d", "e"] }, + getPropertyDescriptor: function(k) { + switch (k) { + case "a": return {enumerable: false, value: "3"}; + case "b": return {enumerable: true, get get() {}}; + case "c": return {value: 4}; + case "d": return {get enumerable() { return true }}; + default: return undefined; + } + } +}) + + + +// Throw exception in enumerate trap. + +function TestForInThrow(handler) { + TestWithProxies(TestForInThrow2, handler) +} + +function TestForInThrow2(create, handler) { + var p = create(handler) + var o = Object.create(p) + assertThrows(function(){ for (var x in p) {} }, "myexn") + assertThrows(function(){ for (var x in o) {} }, "myexn") +} + +TestForInThrow({ + enumerate: function() { throw "myexn" } +}) + +TestForInThrow({ + enumerate: function() { return this.enumerate2() }, + enumerate2: function() { throw "myexn" } +}) + +TestForInThrow({ + getPropertyNames: function() { throw "myexn" } +}) + +TestForInThrow({ + getPropertyNames: function() { return ["a"] }, + getPropertyDescriptor: function() { throw "myexn" } +}) + +TestForInThrow(Proxy.create({ + get: function(pr, pk) { + return function() { throw "myexn" } + } +})) diff --git a/deps/v8/test/mjsunit/harmony/proxies-function.js b/deps/v8/test/mjsunit/harmony/proxies-function.js new file mode 100644 index 0000000000..6b8d098442 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/proxies-function.js @@ -0,0 +1,748 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-proxies --allow-natives-syntax + + +// Helper. + +function CreateFrozen(handler, callTrap, constructTrap) { + if (handler.fix === undefined) handler.fix = function() { return {} } + var f = Proxy.createFunction(handler, callTrap, constructTrap) + Object.freeze(f) + return f +} + + +// Ensures that checking the "length" property of a function proxy doesn't +// crash due to lack of a [[Get]] method. +var handler = { + get : function(r, n) { return n == "length" ? 2 : undefined } +} + + +// Calling (call, Function.prototype.call, Function.prototype.apply, +// Function.prototype.bind). + +var global_object = this +var receiver + +function TestCall(isStrict, callTrap) { + assertEquals(42, callTrap(5, 37)) + // TODO(rossberg): strict mode seems to be broken on x64... + // assertSame(isStrict ? undefined : global_object, receiver) + + var handler = { + get: function(r, k) { + return k == "length" ? 2 : Function.prototype[k] + } + } + var f = Proxy.createFunction(handler, callTrap) + var o = {f: f} + global_object.f = f + + receiver = 333 + assertEquals(42, f(11, 31)) + // TODO(rossberg): strict mode seems to be broken on x64... + // assertSame(isStrict ? undefined : global_object, receiver) + receiver = 333 + assertEquals(42, o.f(10, 32)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, o["f"](9, 33)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, (1, o).f(8, 34)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, (1, o)["f"](7, 35)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, f.call(o, 32, 10)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, f.call(undefined, 33, 9)) + assertSame(isStrict ? undefined : global_object, receiver) + receiver = 333 + assertEquals(42, f.call(null, 33, 9)) + assertSame(isStrict ? null : global_object, receiver) + receiver = 333 + assertEquals(44, f.call(2, 21, 23)) + assertSame(2, receiver.valueOf()) + receiver = 333 + assertEquals(42, Function.prototype.call.call(f, o, 20, 22)) + assertSame(o, receiver) + receiver = 333 + assertEquals(43, Function.prototype.call.call(f, null, 20, 23)) + assertSame(isStrict ? null : global_object, receiver) + assertEquals(44, Function.prototype.call.call(f, 2, 21, 23)) + assertEquals(2, receiver.valueOf()) + receiver = 333 + assertEquals(32, f.apply(o, [16, 16])) + assertSame(o, receiver) + receiver = 333 + assertEquals(32, Function.prototype.apply.call(f, o, [17, 15])) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Call(o, 11, 31, f)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Call(null, 11, 31, f)) + assertSame(isStrict ? null : global_object, receiver) + receiver = 333 + assertEquals(42, %Apply(f, o, [11, 31], 0, 2)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Apply(f, null, [11, 31], 0, 2)) + assertSame(isStrict ? null : global_object, receiver) + receiver = 333 + assertEquals(42, %_CallFunction(o, 11, 31, f)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %_CallFunction(null, 11, 31, f)) + assertSame(isStrict ? null : global_object, receiver) + + var ff = Function.prototype.bind.call(f, o, 12) + assertTrue(ff.length <= 1) // TODO(rossberg): Not spec'ed yet, be lax. + receiver = 333 + assertEquals(42, ff(30)) + assertSame(o, receiver) + receiver = 333 + assertEquals(33, Function.prototype.call.call(ff, {}, 21)) + assertSame(o, receiver) + receiver = 333 + assertEquals(32, Function.prototype.apply.call(ff, {}, [20])) + assertSame(o, receiver) + receiver = 333 + assertEquals(23, %Call({}, 11, ff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(23, %Call({}, 11, 3, ff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(24, %Apply(ff, {}, [12, 13], 0, 1)) + assertSame(o, receiver) + receiver = 333 + assertEquals(24, %Apply(ff, {}, [12, 13], 0, 2)) + assertSame(o, receiver) + receiver = 333 + assertEquals(34, %_CallFunction({}, 22, ff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(34, %_CallFunction({}, 22, 3, ff)) + assertSame(o, receiver) + + var fff = Function.prototype.bind.call(ff, o, 30) + assertEquals(0, fff.length) + receiver = 333 + assertEquals(42, fff()) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, Function.prototype.call.call(fff, {})) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, Function.prototype.apply.call(fff, {})) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Call({}, fff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Call({}, 11, 3, fff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Apply(fff, {}, [], 0, 0)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Apply(fff, {}, [12, 13], 0, 0)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %Apply(fff, {}, [12, 13], 0, 2)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %_CallFunction({}, fff)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %_CallFunction({}, 3, 4, 5, fff)) + assertSame(o, receiver) + + var f = CreateFrozen({}, callTrap) + receiver = 333 + assertEquals(42, f(11, 31)) + assertSame(isStrict ? undefined : global_object, receiver) + var o = {f: f} + receiver = 333 + assertEquals(42, o.f(10, 32)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, o["f"](9, 33)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, (1, o).f(8, 34)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, (1, o)["f"](7, 35)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, Function.prototype.call.call(f, o, 20, 22)) + assertSame(o, receiver) + receiver = 333 + assertEquals(32, Function.prototype.apply.call(f, o, [17, 15])) + assertSame(o, receiver) + receiver = 333 + assertEquals(23, %Call(o, 11, 12, f)) + assertSame(o, receiver) + receiver = 333 + assertEquals(27, %Apply(f, o, [12, 13, 14], 1, 2)) + assertSame(o, receiver) + receiver = 333 + assertEquals(42, %_CallFunction(o, 18, 24, f)) + assertSame(o, receiver) +} + +TestCall(false, function(x, y) { + receiver = this + return x + y +}) + +TestCall(true, function(x, y) { + "use strict" + receiver = this + return x + y +}) + +TestCall(false, function() { + receiver = this + return arguments[0] + arguments[1] +}) + +TestCall(false, Proxy.createFunction(handler, function(x, y) { + receiver = this + return x + y +})) + +TestCall(true, Proxy.createFunction(handler, function(x, y) { + "use strict" + receiver = this + return x + y +})) + +TestCall(false, CreateFrozen(handler, function(x, y) { + receiver = this + return x + y +})) + + + +// Using intrinsics as call traps. + +function TestCallIntrinsic(type, callTrap) { + var f = Proxy.createFunction({}, callTrap) + var x = f() + assertTrue(typeof x == type) +} + +TestCallIntrinsic("boolean", Boolean) +TestCallIntrinsic("number", Number) +TestCallIntrinsic("string", String) +TestCallIntrinsic("object", Object) +TestCallIntrinsic("function", Function) + + + +// Throwing from call trap. + +function TestCallThrow(callTrap) { + var f = Proxy.createFunction({}, callTrap) + assertThrows(function(){ f(11) }, "myexn") + assertThrows(function(){ ({x: f}).x(11) }, "myexn") + assertThrows(function(){ ({x: f})["x"](11) }, "myexn") + assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn") + assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn") + assertThrows(function(){ %Call({}, f) }, "myexn") + assertThrows(function(){ %Call({}, 1, 2, f) }, "myexn") + assertThrows(function(){ %Apply({}, f, [], 3, 0) }, "myexn") + assertThrows(function(){ %Apply({}, f, [3, 4], 0, 1) }, "myexn") + assertThrows(function(){ %_CallFunction({}, f) }, "myexn") + assertThrows(function(){ %_CallFunction({}, 1, 2, f) }, "myexn") + + var f = CreateFrozen({}, callTrap) + assertThrows(function(){ f(11) }, "myexn") + assertThrows(function(){ ({x: f}).x(11) }, "myexn") + assertThrows(function(){ ({x: f})["x"](11) }, "myexn") + assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn") + assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn") + assertThrows(function(){ %Call({}, f) }, "myexn") + assertThrows(function(){ %Call({}, 1, 2, f) }, "myexn") + assertThrows(function(){ %Apply({}, f, [], 3, 0) }, "myexn") + assertThrows(function(){ %Apply({}, f, [3, 4], 0, 1) }, "myexn") + assertThrows(function(){ %_CallFunction({}, f) }, "myexn") + assertThrows(function(){ %_CallFunction({}, 1, 2, f) }, "myexn") +} + +TestCallThrow(function() { throw "myexn" }) +TestCallThrow(Proxy.createFunction({}, function() { throw "myexn" })) +TestCallThrow(CreateFrozen({}, function() { throw "myexn" })) + + + +// Construction (new). + +var prototype = {myprop: 0} +var receiver + +var handlerWithPrototype = { + fix: function() { return { prototype: { value: prototype } }; }, + get: function(r, n) { + if (n == "length") return 2; + assertEquals("prototype", n); + return prototype; + } +} + +var handlerSansPrototype = { + fix: function() { return { length: { value: 2 } } }, + get: function(r, n) { + if (n == "length") return 2; + assertEquals("prototype", n); + return undefined; + } +} + +function ReturnUndef(x, y) { + "use strict"; + receiver = this; + this.sum = x + y; +} + +function ReturnThis(x, y) { + "use strict"; + receiver = this; + this.sum = x + y; + return this; +} + +function ReturnNew(x, y) { + "use strict"; + receiver = this; + return {sum: x + y}; +} + +function ReturnNewWithProto(x, y) { + "use strict"; + receiver = this; + var result = Object.create(prototype); + result.sum = x + y; + return result; +} + +function TestConstruct(proto, constructTrap) { + TestConstruct2(proto, constructTrap, handlerWithPrototype) + TestConstruct2(proto, constructTrap, handlerSansPrototype) +} + +function TestConstruct2(proto, constructTrap, handler) { + var f = Proxy.createFunction(handler, function() {}, constructTrap) + var o = new f(11, 31) + assertEquals(undefined, receiver) + assertEquals(42, o.sum) + assertSame(proto, Object.getPrototypeOf(o)) + + var f = CreateFrozen(handler, function() {}, constructTrap) + var o = new f(11, 32) + assertEquals(undefined, receiver) + assertEquals(43, o.sum) + assertSame(proto, Object.getPrototypeOf(o)) +} + +TestConstruct(Object.prototype, ReturnNew) +TestConstruct(prototype, ReturnNewWithProto) + +TestConstruct(Object.prototype, Proxy.createFunction(handler, ReturnNew)) +TestConstruct(prototype, Proxy.createFunction(handler, ReturnNewWithProto)) + +TestConstruct(Object.prototype, CreateFrozen(handler, ReturnNew)) +TestConstruct(prototype, CreateFrozen(handler, ReturnNewWithProto)) + + + +// Construction with derived construct trap. + +function TestConstructFromCall(proto, returnsThis, callTrap) { + TestConstructFromCall2(prototype, returnsThis, callTrap, handlerWithPrototype) + TestConstructFromCall2(proto, returnsThis, callTrap, handlerSansPrototype) +} + +function TestConstructFromCall2(proto, returnsThis, callTrap, handler) { + // TODO(rossberg): handling of prototype for derived construct trap will be + // fixed in a separate change. Commenting out checks below for now. + var f = Proxy.createFunction(handler, callTrap) + var o = new f(11, 31) + if (returnsThis) assertEquals(o, receiver) + assertEquals(42, o.sum) + // assertSame(proto, Object.getPrototypeOf(o)) + + var g = CreateFrozen(handler, callTrap) + // assertSame(f.prototype, g.prototype) + var o = new g(11, 32) + if (returnsThis) assertEquals(o, receiver) + assertEquals(43, o.sum) + // assertSame(proto, Object.getPrototypeOf(o)) +} + +TestConstructFromCall(Object.prototype, true, ReturnUndef) +TestConstructFromCall(Object.prototype, true, ReturnThis) +TestConstructFromCall(Object.prototype, false, ReturnNew) +TestConstructFromCall(prototype, false, ReturnNewWithProto) + +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnUndef)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handler, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handler, ReturnNewWithProto)) + +TestConstructFromCall(Object.prototype, true, CreateFrozen({}, ReturnUndef)) +TestConstructFromCall(Object.prototype, true, CreateFrozen({}, ReturnThis)) +TestConstructFromCall(Object.prototype, false, CreateFrozen({}, ReturnNew)) +TestConstructFromCall(prototype, false, CreateFrozen({}, ReturnNewWithProto)) + +ReturnUndef.prototype = prototype +ReturnThis.prototype = prototype +ReturnNew.prototype = prototype +ReturnNewWithProto.prototype = prototype + +TestConstructFromCall(prototype, true, ReturnUndef) +TestConstructFromCall(prototype, true, ReturnThis) +TestConstructFromCall(Object.prototype, false, ReturnNew) +TestConstructFromCall(prototype, false, ReturnNewWithProto) + +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnUndef)) +TestConstructFromCall(Object.prototype, true, + Proxy.createFunction(handler, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handler, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handler, ReturnNewWithProto)) + +TestConstructFromCall(prototype, true, + Proxy.createFunction(handlerWithPrototype, ReturnUndef)) +TestConstructFromCall(prototype, true, + Proxy.createFunction(handlerWithPrototype, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + Proxy.createFunction(handlerWithPrototype, ReturnNew)) +TestConstructFromCall(prototype, false, + Proxy.createFunction(handlerWithPrototype, + ReturnNewWithProto)) + +TestConstructFromCall(prototype, true, + CreateFrozen(handlerWithPrototype, ReturnUndef)) +TestConstructFromCall(prototype, true, + CreateFrozen(handlerWithPrototype, ReturnThis)) +TestConstructFromCall(Object.prototype, false, + CreateFrozen(handlerWithPrototype, ReturnNew)) +TestConstructFromCall(prototype, false, + CreateFrozen(handlerWithPrototype, ReturnNewWithProto)) + + + +// Throwing from the construct trap. + +function TestConstructThrow(trap) { + TestConstructThrow2(Proxy.createFunction({ fix: function() {return {};} }, + trap)) + TestConstructThrow2(Proxy.createFunction({ fix: function() {return {};} }, + function() {}, + trap)) +} + +function TestConstructThrow2(f) { + assertThrows(function(){ new f(11) }, "myexn") + Object.freeze(f) + assertThrows(function(){ new f(11) }, "myexn") +} + +TestConstructThrow(function() { throw "myexn" }) +TestConstructThrow(Proxy.createFunction({}, function() { throw "myexn" })) +TestConstructThrow(CreateFrozen({}, function() { throw "myexn" })) + + + +// Using function proxies as getters and setters. + +var value +var receiver + +function TestAccessorCall(getterCallTrap, setterCallTrap) { + var handler = { fix: function() { return {} } } + var pgetter = Proxy.createFunction(handler, getterCallTrap) + var psetter = Proxy.createFunction(handler, setterCallTrap) + + var o = {} + var oo = Object.create(o) + Object.defineProperty(o, "a", {get: pgetter, set: psetter}) + Object.defineProperty(o, "b", {get: pgetter}) + Object.defineProperty(o, "c", {set: psetter}) + Object.defineProperty(o, "3", {get: pgetter, set: psetter}) + Object.defineProperty(oo, "a", {value: 43}) + + receiver = "" + assertEquals(42, o.a) + assertSame(o, receiver) + receiver = "" + assertEquals(42, o.b) + assertSame(o, receiver) + receiver = "" + assertEquals(undefined, o.c) + assertEquals("", receiver) + receiver = "" + assertEquals(42, o["a"]) + assertSame(o, receiver) + receiver = "" + assertEquals(42, o[3]) + assertSame(o, receiver) + + receiver = "" + assertEquals(43, oo.a) + assertEquals("", receiver) + receiver = "" + assertEquals(42, oo.b) + assertSame(oo, receiver) + receiver = "" + assertEquals(undefined, oo.c) + assertEquals("", receiver) + receiver = "" + assertEquals(43, oo["a"]) + assertEquals("", receiver) + receiver = "" + assertEquals(42, oo[3]) + assertSame(oo, receiver) + + receiver = "" + assertEquals(50, o.a = 50) + assertSame(o, receiver) + assertEquals(50, value) + receiver = "" + assertEquals(51, o.b = 51) + assertEquals("", receiver) + assertEquals(50, value) // no setter + assertThrows(function() { "use strict"; o.b = 51 }, TypeError) + receiver = "" + assertEquals(52, o.c = 52) + assertSame(o, receiver) + assertEquals(52, value) + receiver = "" + assertEquals(53, o["a"] = 53) + assertSame(o, receiver) + assertEquals(53, value) + receiver = "" + assertEquals(54, o[3] = 54) + assertSame(o, receiver) + assertEquals(54, value) + + value = 0 + receiver = "" + assertEquals(60, oo.a = 60) + assertEquals("", receiver) + assertEquals(0, value) // oo has own 'a' + assertEquals(61, oo.b = 61) + assertSame("", receiver) + assertEquals(0, value) // no setter + assertThrows(function() { "use strict"; oo.b = 61 }, TypeError) + receiver = "" + assertEquals(62, oo.c = 62) + assertSame(oo, receiver) + assertEquals(62, value) + receiver = "" + assertEquals(63, oo["c"] = 63) + assertSame(oo, receiver) + assertEquals(63, value) + receiver = "" + assertEquals(64, oo[3] = 64) + assertSame(oo, receiver) + assertEquals(64, value) +} + +TestAccessorCall( + function() { receiver = this; return 42 }, + function(x) { receiver = this; value = x } +) + +TestAccessorCall( + function() { "use strict"; receiver = this; return 42 }, + function(x) { "use strict"; receiver = this; value = x } +) + +TestAccessorCall( + Proxy.createFunction({}, function() { receiver = this; return 42 }), + Proxy.createFunction({}, function(x) { receiver = this; value = x }) +) + +TestAccessorCall( + CreateFrozen({}, function() { receiver = this; return 42 }), + CreateFrozen({}, function(x) { receiver = this; value = x }) +) + + + +// Passing a proxy function to higher-order library functions. + +function TestHigherOrder(f) { + assertEquals(6, [6, 2].map(f)[0]) + assertEquals(4, [5, 2].reduce(f, 4)) + assertTrue([1, 2].some(f)) + assertEquals("a.b.c", "a.b.c".replace(".", f)) +} + +TestHigherOrder(function(x) { return x }) +TestHigherOrder(function(x) { "use strict"; return x }) +TestHigherOrder(Proxy.createFunction({}, function(x) { return x })) +TestHigherOrder(CreateFrozen({}, function(x) { return x })) + + + +// TODO(rossberg): Ultimately, I want to have the following test function +// run through, but it currently fails on so many cases (some not even +// involving proxies), that I leave that for later... +/* +function TestCalls() { + var handler = { + get: function(r, k) { + return k == "length" ? 2 : Function.prototype[k] + } + } + var bind = Function.prototype.bind + var o = {} + + var traps = [ + function(x, y) { + return {receiver: this, result: x + y, strict: false} + }, + function(x, y) { "use strict"; + return {receiver: this, result: x + y, strict: true} + }, + function() { + var x = arguments[0], y = arguments[1] + return {receiver: this, result: x + y, strict: false} + }, + Proxy.createFunction(handler, function(x, y) { + return {receiver: this, result: x + y, strict: false} + }), + Proxy.createFunction(handler, function() { + var x = arguments[0], y = arguments[1] + return {receiver: this, result: x + y, strict: false} + }), + Proxy.createFunction(handler, function(x, y) { "use strict" + return {receiver: this, result: x + y, strict: true} + }), + CreateFrozen(handler, function(x, y) { + return {receiver: this, result: x + y, strict: false} + }), + CreateFrozen(handler, function(x, y) { "use strict" + return {receiver: this, result: x + y, strict: true} + }), + ] + var creates = [ + function(trap) { return trap }, + function(trap) { return CreateFrozen({}, callTrap) }, + function(trap) { return Proxy.createFunction(handler, callTrap) }, + function(trap) { + return Proxy.createFunction(handler, CreateFrozen({}, callTrap)) + }, + function(trap) { + return Proxy.createFunction(handler, Proxy.createFunction(handler, callTrap)) + }, + ] + var binds = [ + function(f, o, x, y) { return f }, + function(f, o, x, y) { return bind.call(f, o) }, + function(f, o, x, y) { return bind.call(f, o, x) }, + function(f, o, x, y) { return bind.call(f, o, x, y) }, + function(f, o, x, y) { return bind.call(f, o, x, y, 5) }, + function(f, o, x, y) { return bind.call(bind.call(f, o), {}, x, y) }, + function(f, o, x, y) { return bind.call(bind.call(f, o, x), {}, y) }, + function(f, o, x, y) { return bind.call(bind.call(f, o, x, y), {}, 5) }, + ] + var calls = [ + function(f, x, y) { return f(x, y) }, + function(f, x, y) { var g = f; return g(x, y) }, + function(f, x, y) { with ({}) return f(x, y) }, + function(f, x, y) { var g = f; with ({}) return g(x, y) }, + function(f, x, y, o) { with (o) return f(x, y) }, + function(f, x, y, o) { return f.call(o, x, y) }, + function(f, x, y, o) { return f.apply(o, [x, y]) }, + function(f, x, y, o) { return Function.prototype.call.call(f, o, x, y) }, + function(f, x, y, o) { return Function.prototype.apply.call(f, o, [x, y]) }, + function(f, x, y, o) { return %_CallFunction(o, x, y, f) }, + function(f, x, y, o) { return %Call(o, x, y, f) }, + function(f, x, y, o) { return %Apply(f, o, [null, x, y, null], 1, 2) }, + function(f, x, y, o) { return %Apply(f, o, arguments, 2, 2) }, + function(f, x, y, o) { if (typeof o == "object") return o.f(x, y) }, + function(f, x, y, o) { if (typeof o == "object") return o["f"](x, y) }, + function(f, x, y, o) { if (typeof o == "object") return (1, o).f(x, y) }, + function(f, x, y, o) { if (typeof o == "object") return (1, o)["f"](x, y) }, + ] + var receivers = [o, global_object, undefined, null, 2, "bla", true] + var expectedNonStricts = [o, global_object, global_object, global_object] + + for (var t = 0; t < traps.length; ++t) { + for (var i = 0; i < creates.length; ++i) { + for (var j = 0; j < binds.length; ++j) { + for (var k = 0; k < calls.length; ++k) { + for (var m = 0; m < receivers.length; ++m) { + for (var n = 0; n < receivers.length; ++n) { + var bound = receivers[m] + var receiver = receivers[n] + var func = binds[j](creates[i](traps[t]), bound, 31, 11) + var expected = j > 0 ? bound : receiver + var expectedNonStrict = expectedNonStricts[j > 0 ? m : n] + o.f = func + global_object.f = func + var x = calls[k](func, 11, 31, receiver) + if (x !== undefined) { + assertEquals(42, x.result) + if (calls[k].length < 4) + assertSame(x.strict ? undefined : global_object, x.receiver) + else if (x.strict) + assertSame(expected, x.receiver) + else if (expectedNonStrict === undefined) + assertSame(expected, x.receiver.valueOf()) + else + assertSame(expectedNonStrict, x.receiver) + } + } + } + } + } + } + } +} + +TestCalls() +*/ diff --git a/deps/v8/test/mjsunit/harmony/proxies-hash.js b/deps/v8/test/mjsunit/harmony/proxies-hash.js new file mode 100644 index 0000000000..abfc0f5f0e --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/proxies-hash.js @@ -0,0 +1,122 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-proxies --harmony-collections + + +// Helper. + +function TestWithProxies(test, construct, handler) { + test(construct, handler, Proxy.create) + test(construct, handler, function(h) { + return Proxy.createFunction(h, function() {}) + }) +} + + +// Sets. + +function TestSet(construct, fix) { + TestWithProxies(TestSet2, construct, fix) +} + +function TestSet2(construct, fix, create) { + var handler = {fix: function() { return {} }} + var p1 = create(handler) + var p2 = create(handler) + var p3 = create(handler) + fix(p3) + + var s = construct(); + s.add(p1); + s.add(p2); + assertTrue(s.has(p1)); + assertTrue(s.has(p2)); + assertFalse(s.has(p3)); + + fix(p1) + fix(p2) + assertTrue(s.has(p1)); + assertTrue(s.has(p2)); + assertFalse(s.has(p3)); + + s.delete(p2); + assertTrue(s.has(p1)); + assertFalse(s.has(p2)); + assertFalse(s.has(p3)); +} + +TestSet(Set, Object.seal) +TestSet(Set, Object.freeze) +TestSet(Set, Object.preventExtensions) + + +// Maps and weak maps. + +function TestMap(construct, fix) { + TestWithProxies(TestMap2, construct, fix) +} + +function TestMap2(construct, fix, create) { + var handler = {fix: function() { return {} }} + var p1 = create(handler) + var p2 = create(handler) + var p3 = create(handler) + fix(p3) + + var m = construct(); + m.set(p1, 123); + m.set(p2, 321); + assertTrue(m.has(p1)); + assertTrue(m.has(p2)); + assertFalse(m.has(p3)); + assertSame(123, m.get(p1)); + assertSame(321, m.get(p2)); + + fix(p1) + fix(p2) + assertTrue(m.has(p1)); + assertTrue(m.has(p2)); + assertFalse(m.has(p3)); + assertSame(123, m.get(p1)); + assertSame(321, m.get(p2)); + + m.delete(p2); + assertTrue(m.has(p1)); + assertFalse(m.has(p2)); + assertFalse(m.has(p3)); + assertSame(123, m.get(p1)); + assertSame(undefined, m.get(p2)); +} + +TestMap(Map, Object.seal) +TestMap(Map, Object.freeze) +TestMap(Map, Object.preventExtensions) + +TestMap(WeakMap, Object.seal) +TestMap(WeakMap, Object.freeze) +TestMap(WeakMap, Object.preventExtensions) diff --git a/deps/v8/test/mjsunit/harmony/proxies.js b/deps/v8/test/mjsunit/harmony/proxies.js index 3c4e5f61c5..50c8613b63 100644 --- a/deps/v8/test/mjsunit/harmony/proxies.js +++ b/deps/v8/test/mjsunit/harmony/proxies.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -28,70 +28,167 @@ // Flags: --harmony-proxies -// TODO(rossberg): for-in for proxies not implemented. -// TODO(rossberg): inheritance from proxies not implemented. -// TODO(rossberg): function proxies as constructors not implemented. +// Helper. +function TestWithProxies(test, x, y, z) { + test(Proxy.create, x, y, z) + test(function(h) {return Proxy.createFunction(h, function() {})}, x, y, z) +} -// Helper. -function TestWithProxies(test, handler) { - test(handler, Proxy.create) - test(handler, function(h) {return Proxy.createFunction(h, function() {})}) + +// Getting property descriptors (Object.getOwnPropertyDescriptor). + +var key + +function TestGetOwnProperty(handler) { + TestWithProxies(TestGetOwnProperty2, handler) +} + +function TestGetOwnProperty2(create, handler) { + var p = create(handler) + assertEquals(42, Object.getOwnPropertyDescriptor(p, "a").value) + assertEquals("a", key) + assertEquals(42, Object.getOwnPropertyDescriptor(p, 99).value) + assertEquals("99", key) +} + +TestGetOwnProperty({ + getOwnPropertyDescriptor: function(k) { + key = k + return {value: 42, configurable: true} + } +}) + +TestGetOwnProperty({ + getOwnPropertyDescriptor: function(k) { + return this.getOwnPropertyDescriptor2(k) + }, + getOwnPropertyDescriptor2: function(k) { + key = k + return {value: 42, configurable: true} + } +}) + +TestGetOwnProperty({ + getOwnPropertyDescriptor: function(k) { + key = k + return {get value() { return 42 }, get configurable() { return true }} + } +}) + +TestGetOwnProperty(Proxy.create({ + get: function(pr, pk) { + return function(k) { key = k; return {value: 42, configurable: true} } + } +})) + + +function TestGetOwnPropertyThrow(handler) { + TestWithProxies(TestGetOwnPropertyThrow2, handler) +} + +function TestGetOwnPropertyThrow2(create, handler) { + var p = create(handler) + assertThrows(function(){ Object.getOwnPropertyDescriptor(p, "a") }, "myexn") + assertThrows(function(){ Object.getOwnPropertyDescriptor(p, 77) }, "myexn") } +TestGetOwnPropertyThrow({ + getOwnPropertyDescriptor: function(k) { throw "myexn" } +}) + +TestGetOwnPropertyThrow({ + getOwnPropertyDescriptor: function(k) { + return this.getPropertyDescriptor2(k) + }, + getOwnPropertyDescriptor2: function(k) { throw "myexn" } +}) + +TestGetOwnPropertyThrow({ + getOwnPropertyDescriptor: function(k) { + return {get value() { throw "myexn" }} + } +}) + +TestGetOwnPropertyThrow(Proxy.create({ + get: function(pr, pk) { + return function(k) { throw "myexn" } + } +})) + + -// Getters. +// Getters (dot, brackets). + +var key function TestGet(handler) { TestWithProxies(TestGet2, handler) } -function TestGet2(handler, create) { +function TestGet2(create, handler) { var p = create(handler) assertEquals(42, p.a) + assertEquals("a", key) assertEquals(42, p["b"]) + assertEquals("b", key) + assertEquals(42, p[99]) + assertEquals("99", key) + assertEquals(42, (function(n) { return p[n] })("c")) + assertEquals("c", key) + assertEquals(42, (function(n) { return p[n] })(101)) + assertEquals("101", key) - // TODO(rossberg): inheritance from proxies not yet implemented. - // var o = Object.create(p, {x: {value: 88}}) - // assertEquals(42, o.a) - // assertEquals(42, o["b"]) - // assertEquals(88, o.x) - // assertEquals(88, o["x"]) + var o = Object.create(p, {x: {value: 88}}) + assertEquals(42, o.a) + assertEquals("a", key) + assertEquals(42, o["b"]) + assertEquals("b", key) + assertEquals(42, o[99]) + assertEquals("99", key) + assertEquals(88, o.x) + assertEquals(88, o["x"]) + assertEquals(42, (function(n) { return o[n] })("c")) + assertEquals("c", key) + assertEquals(42, (function(n) { return o[n] })(101)) + assertEquals("101", key) + assertEquals(88, (function(n) { return o[n] })("x")) } TestGet({ - get: function(r, k) { return 42 } + get: function(r, k) { key = k; return 42 } }) TestGet({ get: function(r, k) { return this.get2(r, k) }, - get2: function(r, k) { return 42 } + get2: function(r, k) { key = k; return 42 } }) TestGet({ - getPropertyDescriptor: function(k) { return {value: 42} } + getPropertyDescriptor: function(k) { key = k; return {value: 42} } }) TestGet({ getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) }, - getPropertyDescriptor2: function(k) { return {value: 42} } + getPropertyDescriptor2: function(k) { key = k; return {value: 42} } }) TestGet({ getPropertyDescriptor: function(k) { + key = k; return {get value() { return 42 }} } }) TestGet({ get: undefined, - getPropertyDescriptor: function(k) { return {value: 42} } + getPropertyDescriptor: function(k) { key = k; return {value: 42} } }) TestGet(Proxy.create({ get: function(pr, pk) { - return function(r, k) { return 42 } + return function(r, k) { key = k; return 42 } } })) @@ -100,14 +197,43 @@ function TestGetCall(handler) { TestWithProxies(TestGetCall2, handler) } -function TestGetCall2(handler, create) { +function TestGetCall2(create, handler) { var p = create(handler) assertEquals(55, p.f()) + assertEquals(55, p["f"]()) assertEquals(55, p.f("unused", "arguments")) assertEquals(55, p.f.call(p)) + assertEquals(55, p["f"].call(p)) + assertEquals(55, p[101].call(p)) assertEquals(55, p.withargs(45, 5)) assertEquals(55, p.withargs.call(p, 11, 22)) + assertEquals(55, (function(n) { return p[n]() })("f")) + assertEquals(55, (function(n) { return p[n].call(p) })("f")) + assertEquals(55, (function(n) { return p[n](15, 20) })("withargs")) + assertEquals(55, (function(n) { return p[n].call(p, 13, 21) })("withargs")) assertEquals("6655", "66" + p) // calls p.toString + + var o = Object.create(p, {g: {value: function(x) { return x + 88 }}}) + assertEquals(55, o.f()) + assertEquals(55, o["f"]()) + assertEquals(55, o.f("unused", "arguments")) + assertEquals(55, o.f.call(o)) + assertEquals(55, o.f.call(p)) + assertEquals(55, o["f"].call(p)) + assertEquals(55, o[101].call(p)) + assertEquals(55, o.withargs(45, 5)) + assertEquals(55, o.withargs.call(p, 11, 22)) + assertEquals(90, o.g(2)) + assertEquals(91, o.g.call(o, 3)) + assertEquals(92, o.g.call(p, 4)) + assertEquals(55, (function(n) { return o[n]() })("f")) + assertEquals(55, (function(n) { return o[n].call(o) })("f")) + assertEquals(55, (function(n) { return o[n](15, 20) })("withargs")) + assertEquals(55, (function(n) { return o[n].call(o, 13, 21) })("withargs")) + assertEquals(93, (function(n) { return o[n](5) })("g")) + assertEquals(94, (function(n) { return o[n].call(o, 6) })("g")) + assertEquals(95, (function(n) { return o[n].call(p, 7) })("g")) + assertEquals("6655", "66" + o) // calls o.toString } TestGetCall({ @@ -168,10 +294,20 @@ function TestGetThrow(handler) { TestWithProxies(TestGetThrow2, handler) } -function TestGetThrow2(handler, create) { +function TestGetThrow2(create, handler) { var p = create(handler) assertThrows(function(){ p.a }, "myexn") assertThrows(function(){ p["b"] }, "myexn") + assertThrows(function(){ p[3] }, "myexn") + assertThrows(function(){ (function(n) { p[n] })("c") }, "myexn") + assertThrows(function(){ (function(n) { p[n] })(99) }, "myexn") + + var o = Object.create(p, {x: {value: 88}, '4': {value: 89}}) + assertThrows(function(){ o.a }, "myexn") + assertThrows(function(){ o["b"] }, "myexn") + assertThrows(function(){ o[3] }, "myexn") + assertThrows(function(){ (function(n) { o[n] })("c") }, "myexn") + assertThrows(function(){ (function(n) { o[n] })(99) }, "myexn") } TestGetThrow({ @@ -220,11 +356,11 @@ TestGetThrow(Proxy.create({ var key var val -function TestSet(handler, create) { +function TestSet(handler) { TestWithProxies(TestSet2, handler) } -function TestSet2(handler, create) { +function TestSet2(create, handler) { var p = create(handler) assertEquals(42, p.a = 42) assertEquals("a", key) @@ -232,6 +368,16 @@ function TestSet2(handler, create) { assertEquals(43, p["b"] = 43) assertEquals("b", key) assertEquals(43, val) + assertEquals(44, p[77] = 44) + assertEquals("77", key) + assertEquals(44, val) + + assertEquals(45, (function(n) { return p[n] = 45 })("c")) + assertEquals("c", key) + assertEquals(45, val) + assertEquals(46, (function(n) { return p[n] = 46 })(99)) + assertEquals("99", key) + assertEquals(46, val) } TestSet({ @@ -304,15 +450,17 @@ TestSet(Proxy.create({ })) - -function TestSetThrow(handler, create) { +function TestSetThrow(handler) { TestWithProxies(TestSetThrow2, handler) } -function TestSetThrow2(handler, create) { +function TestSetThrow2(create, handler) { var p = create(handler) assertThrows(function(){ p.a = 42 }, "myexn") assertThrows(function(){ p["b"] = 42 }, "myexn") + assertThrows(function(){ p[22] = 42 }, "myexn") + assertThrows(function(){ (function(n) { p[n] = 45 })("c") }, "myexn") + assertThrows(function(){ (function(n) { p[n] = 46 })(99) }, "myexn") } TestSetThrow({ @@ -424,6 +572,124 @@ TestSetThrow(Proxy.create({ })) +var key +var val + +function TestSetForDerived(handler) { + TestWithProxies(TestSetForDerived2, handler) +} + +function TestSetForDerived2(create, handler) { + var p = create(handler) + var o = Object.create(p, {x: {value: 88, writable: true}, + '1': {value: 89, writable: true}}) + + key = "" + assertEquals(48, o.x = 48) + assertEquals("", key) // trap not invoked + assertEquals(48, o.x) + + assertEquals(47, o[1] = 47) + assertEquals("", key) // trap not invoked + assertEquals(47, o[1]) + + assertEquals(49, o.y = 49) + assertEquals("y", key) + assertEquals(49, o.y) + + assertEquals(50, o[2] = 50) + assertEquals("2", key) + assertEquals(50, o[2]) + + assertEquals(44, o.p_writable = 44) + assertEquals("p_writable", key) + assertEquals(44, o.p_writable) + + assertEquals(45, o.p_nonwritable = 45) + assertEquals("p_nonwritable", key) + assertEquals(45, o.p_nonwritable) + + assertEquals(46, o.p_setter = 46) + assertEquals("p_setter", key) + assertEquals(46, val) // written to parent + assertFalse(Object.prototype.hasOwnProperty.call(o, "p_setter")) + + val = "" + assertEquals(47, o.p_nosetter = 47) + assertEquals("p_nosetter", key) + assertEquals("", val) // not written at all + assertFalse(Object.prototype.hasOwnProperty.call(o, "p_nosetter")); + + key = "" + assertThrows(function(){ "use strict"; o.p_nosetter = 50 }, TypeError) + assertEquals("p_nosetter", key) + assertEquals("", val) // not written at all + + assertThrows(function(){ o.p_nonconf = 53 }, TypeError) + assertEquals("p_nonconf", key) + + assertThrows(function(){ o.p_throw = 51 }, "myexn") + assertEquals("p_throw", key) + + assertThrows(function(){ o.p_setterthrow = 52 }, "myexn") + assertEquals("p_setterthrow", key) +} + +TestSetForDerived({ + getPropertyDescriptor: function(k) { + key = k; + switch (k) { + case "p_writable": return {writable: true, configurable: true} + case "p_nonwritable": return {writable: false, configurable: true} + case "p_setter":return {set: function(x) { val = x }, configurable: true} + case "p_nosetter": return {get: function() { return 1 }, configurable: true} + case "p_nonconf":return {} + case "p_throw": throw "myexn" + case "p_setterthrow": return {set: function(x) { throw "myexn" }} + default: return undefined + } + } +}) + + +// Evil proxy-induced side-effects shouldn't crash. +// TODO(rossberg): proper behaviour isn't really spec'ed yet, so ignore results. + +TestWithProxies(function(create) { + var calls = 0 + var handler = { + getPropertyDescriptor: function() { + ++calls + return (calls % 2 == 1) + ? {get: function() { return 5 }, configurable: true} + : {set: function() { return false }, configurable: true} + } + } + var p = create(handler) + var o = Object.create(p) + // Make proxy prototype property read-only after CanPut check. + try { o.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + +TestWithProxies(function(create) { + var handler = { + getPropertyDescriptor: function() { + Object.defineProperty(o, "x", {get: function() { return 5 }}); + return {set: function() {}} + } + } + var p = create(handler) + var o = Object.create(p) + // Make object property read-only after CanPut check. + try { o.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + + + +// TODO(rossberg): TestSetReject, returning false +// TODO(rossberg): TestGetProperty, TestSetProperty + + // Property definition (Object.defineProperty and Object.defineProperties). @@ -434,7 +700,7 @@ function TestDefine(handler) { TestWithProxies(TestDefine2, handler) } -function TestDefine2(handler, create) { +function TestDefine2(create, handler) { var p = create(handler) assertEquals(p, Object.defineProperty(p, "a", {value: 44})) assertEquals("a", key) @@ -453,6 +719,12 @@ function TestDefine2(handler, create) { assertEquals(46, desc.value) assertEquals(false, desc.enumerable) + assertEquals(p, Object.defineProperty(p, 101, {value: 47, enumerable: false})) + assertEquals("101", key) + assertEquals(2, Object.getOwnPropertyNames(desc).length) + assertEquals(47, desc.value) + assertEquals(false, desc.enumerable) + var attributes = {configurable: true, mine: 66, minetoo: 23} assertEquals(p, Object.defineProperty(p, "d", attributes)) assertEquals("d", key) @@ -474,20 +746,20 @@ function TestDefine2(handler, create) { assertEquals("zzz", key) assertEquals(0, Object.getOwnPropertyNames(desc).length) -// TODO(rossberg): This test requires for-in on proxies. -// var d = create({ -// get: function(r, k) { return (k === "value") ? 77 : void 0 }, -// getOwnPropertyNames: function() { return ["value"] } -// }) -// assertEquals(1, Object.getOwnPropertyNames(d).length) -// assertEquals(77, d.value) -// assertEquals(p, Object.defineProperty(p, "p", d)) -// assertEquals("p", key) -// assertEquals(1, Object.getOwnPropertyNames(desc).length) -// assertEquals(77, desc.value) + var d = create({ + get: function(r, k) { return (k === "value") ? 77 : void 0 }, + getOwnPropertyNames: function() { return ["value"] }, + enumerate: function() { return ["value"] } + }) + assertEquals(1, Object.getOwnPropertyNames(d).length) + assertEquals(77, d.value) + assertEquals(p, Object.defineProperty(p, "p", d)) + assertEquals("p", key) + assertEquals(1, Object.getOwnPropertyNames(desc).length) + assertEquals(77, desc.value) var props = { - 'bla': {}, + '11': {}, blub: {get: function() { return true }}, '': {get value() { return 20 }}, last: {value: 21, configurable: true, mine: "eyes"} @@ -524,21 +796,21 @@ function TestDefineThrow(handler) { TestWithProxies(TestDefineThrow2, handler) } -function TestDefineThrow2(handler, create) { +function TestDefineThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.defineProperty(p, "a", {value: 44})}, "myexn") - -// TODO(rossberg): These tests require for-in on proxies. -// var d1 = create({ -// get: function(r, k) { throw "myexn" }, -// getOwnPropertyNames: function() { return ["value"] } -// }) -// assertThrows(function(){ Object.defineProperty(p, "p", d1) }, "myexn") -// var d2 = create({ -// get: function(r, k) { return 77 }, -// getOwnPropertyNames: function() { throw "myexn" } -// }) -// assertThrows(function(){ Object.defineProperty(p, "p", d2) }, "myexn") + assertThrows(function(){ Object.defineProperty(p, 0, {value: 44})}, "myexn") + + var d1 = create({ + get: function(r, k) { throw "myexn" }, + getOwnPropertyNames: function() { return ["value"] } + }) + assertThrows(function(){ Object.defineProperty(p, "p", d1) }, "myexn") + var d2 = create({ + get: function(r, k) { return 77 }, + getOwnPropertyNames: function() { throw "myexn" } + }) + assertThrows(function(){ Object.defineProperty(p, "p", d2) }, "myexn") var props = {bla: {get value() { throw "otherexn" }}} assertThrows(function(){ Object.defineProperties(p, props) }, "otherexn") @@ -573,12 +845,14 @@ function TestDelete(handler) { TestWithProxies(TestDelete2, handler) } -function TestDelete2(handler, create) { +function TestDelete2(create, handler) { var p = create(handler) assertEquals(true, delete p.a) assertEquals("a", key) assertEquals(true, delete p["b"]) assertEquals("b", key) + assertEquals(true, delete p[1]) + assertEquals("1", key) assertEquals(false, delete p.z1) assertEquals("z1", key) @@ -591,6 +865,8 @@ function TestDelete2(handler, create) { assertEquals("c", key) assertEquals(true, delete p["d"]) assertEquals("d", key) + assertEquals(true, delete p[2]) + assertEquals("2", key) assertThrows(function(){ delete p.z3 }, TypeError) assertEquals("z3", key) @@ -619,15 +895,17 @@ function TestDeleteThrow(handler) { TestWithProxies(TestDeleteThrow2, handler) } -function TestDeleteThrow2(handler, create) { +function TestDeleteThrow2(create, handler) { var p = create(handler) assertThrows(function(){ delete p.a }, "myexn") assertThrows(function(){ delete p["b"] }, "myexn"); + assertThrows(function(){ delete p[3] }, "myexn"); (function() { "use strict" assertThrows(function(){ delete p.c }, "myexn") assertThrows(function(){ delete p["d"] }, "myexn") + assertThrows(function(){ delete p[4] }, "myexn"); })() } @@ -658,7 +936,7 @@ function TestDescriptor(handler) { TestWithProxies(TestDescriptor2, handler) } -function TestDescriptor2(handler, create) { +function TestDescriptor2(create, handler) { var p = create(handler) var descs = [ {configurable: true}, @@ -697,7 +975,7 @@ function TestDescriptorThrow(handler) { TestWithProxies(TestDescriptorThrow2, handler) } -function TestDescriptorThrow2(handler, create) { +function TestDescriptorThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.getOwnPropertyDescriptor(p, "a") }, "myexn") } @@ -721,7 +999,7 @@ function TestComparison(eq) { TestWithProxies(TestComparison2, eq) } -function TestComparison2(eq, create) { +function TestComparison2(create, eq) { var p1 = create({}) var p2 = create({}) @@ -764,7 +1042,7 @@ function TestIn(handler) { TestWithProxies(TestIn2, handler) } -function TestIn2(handler, create) { +function TestIn2(create, handler) { var p = create(handler) assertTrue("a" in p) assertEquals("a", key) @@ -778,6 +1056,7 @@ function TestIn2(handler, create) { assertEquals(0, ("zzz" in p) ? 2 : 0) assertEquals(2, !("zzz" in p) ? 2 : 0) + // Test compilation in conditionals. if ("b" in p) { } else { assertTrue(false) @@ -830,7 +1109,7 @@ TestIn({ }) TestIn({ - get: undefined, + has: undefined, getPropertyDescriptor: function(k) { key = k; return k < "z" ? {value: 42} : void 0 } @@ -847,9 +1126,10 @@ function TestInThrow(handler) { TestWithProxies(TestInThrow2, handler) } -function TestInThrow2(handler, create) { +function TestInThrow2(create, handler) { var p = create(handler) assertThrows(function(){ return "a" in o }, "myexn") + assertThrows(function(){ return 99 in o }, "myexn") assertThrows(function(){ return !("a" in o) }, "myexn") assertThrows(function(){ return ("a" in o) ? 2 : 3 }, "myexn") assertThrows(function(){ if ("b" in o) {} }, "myexn") @@ -876,7 +1156,7 @@ TestInThrow({ }) TestInThrow({ - get: undefined, + has: undefined, getPropertyDescriptor: function(k) { throw "myexn" } }) @@ -891,6 +1171,158 @@ TestInThrow(Proxy.create({ })) +function TestInForDerived(handler) { + TestWithProxies(TestInForDerived2, handler) +} + +function TestInForDerived2(create, handler) { + var p = create(handler) + var o = Object.create(p) + + assertTrue("a" in o) + assertEquals("a", key) + assertTrue(99 in o) + assertEquals("99", key) + assertFalse("z" in o) + assertEquals("z", key) + + assertEquals(2, ("a" in o) ? 2 : 0) + assertEquals(0, !("a" in o) ? 2 : 0) + assertEquals(0, ("zzz" in o) ? 2 : 0) + assertEquals(2, !("zzz" in o) ? 2 : 0) + + if ("b" in o) { + } else { + assertTrue(false) + } + assertEquals("b", key) + + if ("zz" in o) { + assertTrue(false) + } + assertEquals("zz", key) + + if (!("c" in o)) { + assertTrue(false) + } + assertEquals("c", key) + + if (!("zzz" in o)) { + } else { + assertTrue(false) + } + assertEquals("zzz", key) +} + +TestInForDerived({ + getPropertyDescriptor: function(k) { + key = k; return k < "z" ? {value: 42, configurable: true} : void 0 + } +}) + +TestInForDerived({ + getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) }, + getPropertyDescriptor2: function(k) { + key = k; return k < "z" ? {value: 42, configurable: true} : void 0 + } +}) + +TestInForDerived({ + getPropertyDescriptor: function(k) { + key = k; + return k < "z" ? {get value() { return 42 }, configurable: true} : void 0 + } +}) + +/* TODO(rossberg): this will work once we implement the newest proposal + * regarding default traps for getPropertyDescriptor. +TestInForDerived({ + getOwnPropertyDescriptor: function(k) { + key = k; return k < "z" ? {value: 42, configurable: true} : void 0 + } +}) + +TestInForDerived({ + getOwnPropertyDescriptor: function(k) { + return this.getOwnPropertyDescriptor2(k) + }, + getOwnPropertyDescriptor2: function(k) { + key = k; return k < "z" ? {value: 42, configurable: true} : void 0 + } +}) + +TestInForDerived({ + getOwnPropertyDescriptor: function(k) { + key = k; + return k < "z" ? {get value() { return 42 }, configurable: true} : void 0 + } +}) +*/ + +TestInForDerived(Proxy.create({ + get: function(pr, pk) { + return function(k) { + key = k; return k < "z" ? {value: 42, configurable: true} : void 0 + } + } +})) + + + +// Property descriptor conversion. + +var descget + +function TestDescriptorGetOrder(handler) { + var p = Proxy.create(handler) + var o = Object.create(p, {b: {value: 0}}) + TestDescriptorGetOrder2(function(n) { return p[n] }, "vV") + TestDescriptorGetOrder2(function(n) { return n in p }, "") + TestDescriptorGetOrder2(function(n) { return o[n] }, "vV") + TestDescriptorGetOrder2(function(n) { return n in o }, "eEcCvVwWgs") +} + +function TestDescriptorGetOrder2(f, access) { + descget = "" + assertTrue(f("a")) + assertEquals(access, descget) + descget = "" + assertTrue(f(99)) + assertEquals(access, descget) + descget = "" + assertFalse(!!f("z")) + assertEquals("", descget) +} + +TestDescriptorGetOrder({ + getPropertyDescriptor: function(k) { + if (k >= "z") return void 0 + // Return a proxy as property descriptor, so that we can log accesses. + return Proxy.create({ + get: function(r, attr) { + descget += attr[0].toUpperCase() + return true + }, + has: function(attr) { + descget += attr[0] + switch (attr) { + case "writable": + case "enumerable": + case "configurable": + case "value": + return true + case "get": + case "set": + return false + default: + assertUnreachable() + } + } + }) + } +}) + + // Own Properties (Object.prototype.hasOwnProperty). @@ -900,7 +1332,7 @@ function TestHasOwn(handler) { TestWithProxies(TestHasOwn2, handler) } -function TestHasOwn2(handler, create) { +function TestHasOwn2(create, handler) { var p = create(handler) assertTrue(Object.prototype.hasOwnProperty.call(p, "a")) assertEquals("a", key) @@ -958,7 +1390,7 @@ function TestHasOwnThrow(handler) { TestWithProxies(TestHasOwnThrow2, handler) } -function TestHasOwnThrow2(handler, create) { +function TestHasOwnThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.prototype.hasOwnProperty.call(p, "a")}, "myexn") @@ -1005,84 +1437,173 @@ TestHasOwnThrow(Proxy.create({ // Instanceof (instanceof) -function TestInstanceof() { - var o = {} +function TestProxyInstanceof() { + var o1 = {} var p1 = Proxy.create({}) - var p2 = Proxy.create({}, o) + var p2 = Proxy.create({}, o1) var p3 = Proxy.create({}, p2) + var o2 = Object.create(p2) var f0 = function() {} - f0.prototype = o + f0.prototype = o1 var f1 = function() {} f1.prototype = p1 var f2 = function() {} f2.prototype = p2 - - assertTrue(o instanceof Object) - assertFalse(o instanceof f0) - assertFalse(o instanceof f1) - assertFalse(o instanceof f2) + var f3 = function() {} + f3.prototype = o2 + + assertTrue(o1 instanceof Object) + assertFalse(o1 instanceof f0) + assertFalse(o1 instanceof f1) + assertFalse(o1 instanceof f2) + assertFalse(o1 instanceof f3) assertFalse(p1 instanceof Object) assertFalse(p1 instanceof f0) assertFalse(p1 instanceof f1) assertFalse(p1 instanceof f2) + assertFalse(p1 instanceof f3) assertTrue(p2 instanceof Object) assertTrue(p2 instanceof f0) assertFalse(p2 instanceof f1) assertFalse(p2 instanceof f2) + assertFalse(p2 instanceof f3) assertTrue(p3 instanceof Object) assertTrue(p3 instanceof f0) assertFalse(p3 instanceof f1) assertTrue(p3 instanceof f2) + assertFalse(p3 instanceof f3) + assertTrue(o2 instanceof Object) + assertTrue(o2 instanceof f0) + assertFalse(o2 instanceof f1) + assertTrue(o2 instanceof f2) + assertFalse(o2 instanceof f3) var f = Proxy.createFunction({}, function() {}) assertTrue(f instanceof Function) } -TestInstanceof() +TestProxyInstanceof() + + +function TestInstanceofProxy() { + var o0 = Object.create(null) + var o1 = {} + var o2 = Object.create(o0) + var o3 = Object.create(o1) + var o4 = Object.create(o2) + var o5 = Object.create(o3) + + function handler(o) { return {get: function() { return o } } } + var f0 = Proxy.createFunction(handler(o0), function() {}) + var f1 = Proxy.createFunction(handler(o1), function() {}) + var f2 = Proxy.createFunction(handler(o2), function() {}) + var f3 = Proxy.createFunction(handler(o3), function() {}) + var f4 = Proxy.createFunction(handler(o4), function() {}) + var f5 = Proxy.createFunction(handler(o4), function() {}) + + assertFalse(null instanceof f0) + assertFalse(o0 instanceof f0) + assertFalse(o0 instanceof f1) + assertFalse(o0 instanceof f2) + assertFalse(o0 instanceof f3) + assertFalse(o0 instanceof f4) + assertFalse(o0 instanceof f5) + assertFalse(o1 instanceof f0) + assertFalse(o1 instanceof f1) + assertFalse(o1 instanceof f2) + assertFalse(o1 instanceof f3) + assertFalse(o1 instanceof f4) + assertFalse(o1 instanceof f5) + assertTrue(o2 instanceof f0) + assertFalse(o2 instanceof f1) + assertFalse(o2 instanceof f2) + assertFalse(o2 instanceof f3) + assertFalse(o2 instanceof f4) + assertFalse(o2 instanceof f5) + assertFalse(o3 instanceof f0) + assertTrue(o3 instanceof f1) + assertFalse(o3 instanceof f2) + assertFalse(o3 instanceof f3) + assertFalse(o3 instanceof f4) + assertFalse(o3 instanceof f5) + assertTrue(o4 instanceof f0) + assertFalse(o4 instanceof f1) + assertTrue(o4 instanceof f2) + assertFalse(o4 instanceof f3) + assertFalse(o4 instanceof f4) + assertFalse(o4 instanceof f5) + assertFalse(o5 instanceof f0) + assertTrue(o5 instanceof f1) + assertFalse(o5 instanceof f2) + assertTrue(o5 instanceof f3) + assertFalse(o5 instanceof f4) + assertFalse(o5 instanceof f5) + + var f = Proxy.createFunction({}, function() {}) + var ff = Proxy.createFunction(handler(Function), function() {}) + assertTrue(f instanceof Function) + assertFalse(f instanceof ff) +} + +TestInstanceofProxy() // Prototype (Object.getPrototypeOf, Object.prototype.isPrototypeOf). function TestPrototype() { - var o = {} + var o1 = {} var p1 = Proxy.create({}) - var p2 = Proxy.create({}, o) + var p2 = Proxy.create({}, o1) var p3 = Proxy.create({}, p2) - var p4 = Proxy.create({}, 666) + var p4 = Proxy.create({}, null) + var o2 = Object.create(p3) - assertSame(Object.getPrototypeOf(o), Object.prototype) + assertSame(Object.getPrototypeOf(o1), Object.prototype) assertSame(Object.getPrototypeOf(p1), null) - assertSame(Object.getPrototypeOf(p2), o) + assertSame(Object.getPrototypeOf(p2), o1) assertSame(Object.getPrototypeOf(p3), p2) assertSame(Object.getPrototypeOf(p4), null) + assertSame(Object.getPrototypeOf(o2), p3) - assertTrue(Object.prototype.isPrototypeOf(o)) + assertTrue(Object.prototype.isPrototypeOf(o1)) assertFalse(Object.prototype.isPrototypeOf(p1)) assertTrue(Object.prototype.isPrototypeOf(p2)) assertTrue(Object.prototype.isPrototypeOf(p3)) assertFalse(Object.prototype.isPrototypeOf(p4)) - assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o)) + assertTrue(Object.prototype.isPrototypeOf(o2)) + assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o1)) assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p1)) assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p2)) assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p3)) assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p4)) - assertFalse(Object.prototype.isPrototypeOf.call(o, o)) - assertFalse(Object.prototype.isPrototypeOf.call(o, p1)) - assertTrue(Object.prototype.isPrototypeOf.call(o, p2)) - assertTrue(Object.prototype.isPrototypeOf.call(o, p3)) - assertFalse(Object.prototype.isPrototypeOf.call(o, p4)) + assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o2)) + assertFalse(Object.prototype.isPrototypeOf.call(o1, o1)) + assertFalse(Object.prototype.isPrototypeOf.call(o1, p1)) + assertTrue(Object.prototype.isPrototypeOf.call(o1, p2)) + assertTrue(Object.prototype.isPrototypeOf.call(o1, p3)) + assertFalse(Object.prototype.isPrototypeOf.call(o1, p4)) + assertTrue(Object.prototype.isPrototypeOf.call(o1, o2)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p1)) - assertFalse(Object.prototype.isPrototypeOf.call(p1, o)) + assertFalse(Object.prototype.isPrototypeOf.call(p1, o1)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p2)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p3)) assertFalse(Object.prototype.isPrototypeOf.call(p1, p4)) + assertFalse(Object.prototype.isPrototypeOf.call(p1, o2)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p1)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p2)) assertTrue(Object.prototype.isPrototypeOf.call(p2, p3)) assertFalse(Object.prototype.isPrototypeOf.call(p2, p4)) + assertTrue(Object.prototype.isPrototypeOf.call(p2, o2)) assertFalse(Object.prototype.isPrototypeOf.call(p3, p2)) + assertTrue(Object.prototype.isPrototypeOf.call(p3, o2)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, o1)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, p1)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, p2)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, p3)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, p4)) + assertFalse(Object.prototype.isPrototypeOf.call(o2, o2)) var f = Proxy.createFunction({}, function() {}) assertSame(Object.getPrototypeOf(f), Function.prototype) @@ -1097,12 +1618,12 @@ TestPrototype() // Property names (Object.getOwnPropertyNames, Object.keys). function TestPropertyNames(names, handler) { - TestWithProxies(TestPropertyNames2, [names, handler]) + TestWithProxies(TestPropertyNames2, handler, names) } -function TestPropertyNames2(names_handler, create) { - var p = create(names_handler[1]) - assertArrayEquals(names_handler[0], Object.getOwnPropertyNames(p)) +function TestPropertyNames2(create, handler, names) { + var p = create(handler) + assertArrayEquals(names, Object.getOwnPropertyNames(p)) } TestPropertyNames([], { @@ -1129,7 +1650,7 @@ function TestPropertyNamesThrow(handler) { TestWithProxies(TestPropertyNamesThrow2, handler) } -function TestPropertyNamesThrow2(handler, create) { +function TestPropertyNamesThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.getOwnPropertyNames(p) }, "myexn") } @@ -1145,12 +1666,12 @@ TestPropertyNamesThrow({ function TestKeys(names, handler) { - TestWithProxies(TestKeys2, [names, handler]) + TestWithProxies(TestKeys2, handler, names) } -function TestKeys2(names_handler, create) { - var p = create(names_handler[1]) - assertArrayEquals(names_handler[0], Object.keys(p)) +function TestKeys2(create, handler, names) { + var p = create(handler) + assertArrayEquals(names, Object.keys(p)) } TestKeys([], { @@ -1174,7 +1695,9 @@ TestKeys(["[object Object]"], { TestKeys(["a", "0"], { getOwnPropertyNames: function() { return ["a", 23, "zz", "", 0] }, - getOwnPropertyDescriptor: function(k) { return {enumerable: k.length == 1} } + getOwnPropertyDescriptor: function(k) { + return k == "" ? undefined : {enumerable: k.length == 1} + } }) TestKeys(["23", "zz", ""], { @@ -1188,10 +1711,12 @@ TestKeys(["23", "zz", ""], { TestKeys(["a", "b", "c", "5"], { get getOwnPropertyNames() { - return function() { return ["0", 4, "a", "b", "c", 5] } + return function() { return ["0", 4, "a", "b", "c", 5, "ety"] } }, get getOwnPropertyDescriptor() { - return function(k) { return {enumerable: k >= "44"} } + return function(k) { + return k == "ety" ? undefined : {enumerable: k >= "44"} + } } }) @@ -1207,7 +1732,7 @@ function TestKeysThrow(handler) { TestWithProxies(TestKeysThrow2, handler) } -function TestKeysThrow2(handler, create) { +function TestKeysThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.keys(p) }, "myexn") } @@ -1267,7 +1792,6 @@ TestKeysThrow([], { // Fixing (Object.freeze, Object.seal, Object.preventExtensions, // Object.isFrozen, Object.isSealed, Object.isExtensible) -// TODO(rossberg): use TestWithProxies to include funciton proxies function TestFix(names, handler) { var proto = {p: 77} var assertFixing = function(o, s, f, e) { @@ -1314,19 +1838,27 @@ function TestFix(names, handler) { Object.keys(p3).sort()) assertEquals(proto, Object.getPrototypeOf(p3)) assertEquals(77, p3.p) + + var p = Proxy.create(handler, proto) + var o = Object.create(p) + assertFixing(p, false, false, true) + assertFixing(o, false, false, true) + Object.freeze(o) + assertFixing(p, false, false, true) + assertFixing(o, true, true, false) } TestFix([], { fix: function() { return {} } }) -TestFix(["a", "b", "c", "d", "zz"], { +TestFix(["a", "b", "c", "3", "zz"], { fix: function() { return { a: {value: "a", writable: true, configurable: false, enumerable: true}, b: {value: 33, writable: false, configurable: false, enumerable: true}, c: {value: 0, writable: true, configurable: true, enumerable: true}, - d: {value: true, writable: false, configurable: true, enumerable: true}, + '3': {value: true, writable: false, configurable: true, enumerable: true}, zz: {value: 0, enumerable: false} } } @@ -1377,8 +1909,8 @@ function TestFixThrow(handler) { TestWithProxies(TestFixThrow2, handler) } -function TestFixThrow2(handler) { - var p = Proxy.create(handler, {}) +function TestFixThrow2(create, handler) { + var p = create(handler, {}) assertThrows(function(){ Object.seal(p) }, "myexn") assertThrows(function(){ Object.freeze(p) }, "myexn") assertThrows(function(){ Object.preventExtensions(p) }, "myexn") @@ -1404,6 +1936,135 @@ TestFixThrow({ }) +// Freeze a proxy in the middle of operations on it. +// TODO(rossberg): actual behaviour not specified consistently at the moment, +// just make sure that we do not crash. +function TestReentrantFix(f) { + TestWithProxies(f, Object.freeze) + TestWithProxies(f, Object.seal) + TestWithProxies(f, Object.preventExtensions) +} + +TestReentrantFix(function(create, freeze) { + var handler = { + get get() { freeze(p); return undefined }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while getting get trap. + try { p.x } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + get: function() { freeze(p); return 3 }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while executing get trap. + try { p.x } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + getPropertyDescriptor: function() { freeze(p); return undefined }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while executing default get trap. + try { p.x } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + getPropertyDescriptor: function() { freeze(p); return {get: function(){}} }, + fix: function() { return {} } + } + var p = create(handler) + var o = Object.create(p) + // Freeze while getting a property from prototype. + try { o.x } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + get set() { freeze(p); return undefined }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while getting set trap. + try { p.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + set: function() { freeze(p); return true }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while executing set trap. + try { p.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + getOwnPropertyDescriptor: function() { freeze(p); return undefined }, + fix: function() { return {} } + } + var p = create(handler) + // Freeze while executing default set trap. + try { p.x } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + getPropertyDescriptor: function() { freeze(p); return {set: function(){}} }, + fix: function() { return {} } + } + var p = create(handler) + var o = Object.create(p) + // Freeze while setting a property in prototype, dropping the property! + try { o.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + getPropertyDescriptor: function() { freeze(p); return {set: function(){}} }, + fix: function() { return {x: {get: function(){}}} } + } + var p = create(handler) + var o = Object.create(p) + // Freeze while setting a property in prototype, making it read-only! + try { o.x = 4 } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + get fix() { freeze(p); return function(){} } + } + var p = create(handler) + // Freeze while getting fix trap. + try { Object.freeze(p) } catch (e) { assertInstanceof(e, Error) } + p = create(handler) + try { Object.seal(p) } catch (e) { assertInstanceof(e, Error) } + p = create(handler) + try { Object.preventExtensions(p) } catch (e) { assertInstanceof(e, Error) } +}) + +TestReentrantFix(function(create, freeze) { + var handler = { + fix: function() { freeze(p); return {} } + } + var p = create(handler) + // Freeze while executing fix trap. + try { Object.freeze(p) } catch (e) { assertInstanceof(e, Error) } + p = create(handler) + try { Object.seal(p) } catch (e) { assertInstanceof(e, Error) } + p = create(handler) + try { Object.preventExtensions(p) } catch (e) { assertInstanceof(e, Error) } +}) + + // String conversion (Object.prototype.toString, // Object.prototype.toLocaleString, @@ -1426,6 +2087,13 @@ function TestToString(handler) { assertEquals("my_proxy", Object.prototype.toLocaleString.call(f)) assertEquals("toString", key) assertDoesNotThrow(function(){ Function.prototype.toString.call(f) }) + + var o = Object.create(p) + key = "" + assertEquals("[object Object]", Object.prototype.toString.call(o)) + assertEquals("", key) + assertEquals("my_proxy", Object.prototype.toLocaleString.call(o)) + assertEquals("toString", key) } TestToString({ @@ -1452,6 +2120,10 @@ function TestToStringThrow(handler) { var f = Proxy.createFunction(handler, function() {}) assertEquals("[object Function]", Object.prototype.toString.call(f)) assertThrows(function(){ Object.prototype.toLocaleString.call(f) }, "myexn") + + var o = Object.create(p) + assertEquals("[object Object]", Object.prototype.toString.call(o)) + assertThrows(function(){ Object.prototype.toLocaleString.call(o) }, "myexn") } TestToStringThrow({ @@ -1485,7 +2157,7 @@ function TestValueOf(handler) { TestWithProxies(TestValueOf2, handler) } -function TestValueOf2(handler, create) { +function TestValueOf2(create, handler) { var p = create(handler) assertSame(p, Object.prototype.valueOf.call(p)) } @@ -1502,7 +2174,7 @@ function TestIsEnumerable(handler) { TestWithProxies(TestIsEnumerable2, handler) } -function TestIsEnumerable2(handler, create) { +function TestIsEnumerable2(create, handler) { var p = create(handler) assertTrue(Object.prototype.propertyIsEnumerable.call(p, "a")) assertEquals("a", key) @@ -1510,6 +2182,11 @@ function TestIsEnumerable2(handler, create) { assertEquals("2", key) assertFalse(Object.prototype.propertyIsEnumerable.call(p, "z")) assertEquals("z", key) + + var o = Object.create(p) + key = "" + assertFalse(Object.prototype.propertyIsEnumerable.call(o, "a")) + assertEquals("", key) // trap not invoked } TestIsEnumerable({ @@ -1546,7 +2223,7 @@ function TestIsEnumerableThrow(handler) { TestWithProxies(TestIsEnumerableThrow2, handler) } -function TestIsEnumerableThrow2(handler, create) { +function TestIsEnumerableThrow2(create, handler) { var p = create(handler) assertThrows(function(){ Object.prototype.propertyIsEnumerable.call(p, "a") }, "myexn") @@ -1580,103 +2257,3 @@ TestIsEnumerableThrow(Proxy.create({ return function(k) { throw "myexn" } } })) - - - -// Calling (call, Function.prototype.call, Function.prototype.apply, -// Function.prototype.bind). - -var global = this -var receiver - -function TestCall(isStrict, callTrap) { - assertEquals(42, callTrap(5, 37)) -// TODO(rossberg): unrelated bug: this does not succeed for optimized code. -// assertEquals(isStrict ? undefined : global, receiver) - - var f = Proxy.createFunction({fix: function() { return {} }}, callTrap) - receiver = 333 - assertEquals(42, f(11, 31)) - assertEquals(isStrict ? undefined : global, receiver) - var o = {} - assertEquals(42, Function.prototype.call.call(f, o, 20, 22)) - assertEquals(o, receiver) - assertEquals(43, Function.prototype.call.call(f, null, 20, 23)) - assertEquals(isStrict ? null : global, receiver) - assertEquals(44, Function.prototype.call.call(f, 2, 21, 23)) - assertEquals(2, receiver.valueOf()) - receiver = 333 - assertEquals(32, Function.prototype.apply.call(f, o, [17, 15])) - assertEquals(o, receiver) - var ff = Function.prototype.bind.call(f, o, 12) - receiver = 333 - assertEquals(42, ff(30)) - assertEquals(o, receiver) - receiver = 333 - assertEquals(32, Function.prototype.apply.call(ff, {}, [20])) - assertEquals(o, receiver) - - Object.freeze(f) - receiver = 333 - assertEquals(42, f(11, 31)) -// TODO(rossberg): unrelated bug: this does not succeed for optimized code. -// assertEquals(isStrict ? undefined : global, receiver) - receiver = 333 - assertEquals(42, Function.prototype.call.call(f, o, 20, 22)) - assertEquals(o, receiver) - receiver = 333 - assertEquals(32, Function.prototype.apply.call(f, o, [17, 15])) - assertEquals(o, receiver) - receiver = 333 - assertEquals(42, ff(30)) - assertEquals(o, receiver) - receiver = 333 - assertEquals(32, Function.prototype.apply.call(ff, {}, [20])) - assertEquals(o, receiver) -} - -TestCall(false, function(x, y) { - receiver = this; return x + y -}) - -TestCall(true, function(x, y) { - "use strict"; - receiver = this; return x + y -}) - -TestCall(false, Proxy.createFunction({}, function(x, y) { - receiver = this; return x + y -})) - -TestCall(true, Proxy.createFunction({}, function(x, y) { - "use strict"; - receiver = this; return x + y -})) - -var p = Proxy.createFunction({fix: function() {return {}}}, function(x, y) { - receiver = this; return x + y -}) -TestCall(false, p) -Object.freeze(p) -TestCall(false, p) - - -function TestCallThrow(callTrap) { - var f = Proxy.createFunction({fix: function() {return {}}}, callTrap) - assertThrows(function(){ f(11) }, "myexn") - assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn") - assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn") - - Object.freeze(f) - assertThrows(function(){ f(11) }, "myexn") - assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn") - assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn") -} - -TestCallThrow(function() { throw "myexn" }) -TestCallThrow(Proxy.createFunction({}, function() { throw "myexn" })) - -var p = Proxy.createFunction( - {fix: function() {return {}}}, function() { throw "myexn" }) -Object.freeze(p) -TestCallThrow(p) diff --git a/deps/v8/test/mjsunit/harmony/weakmaps.js b/deps/v8/test/mjsunit/harmony/weakmaps.js deleted file mode 100644 index 7b5dcaf0c1..0000000000 --- a/deps/v8/test/mjsunit/harmony/weakmaps.js +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Flags: --harmony-weakmaps --expose-gc - - -// Test valid getter and setter calls -var m = new WeakMap; -assertDoesNotThrow(function () { m.get(new Object) }); -assertDoesNotThrow(function () { m.set(new Object) }); -assertDoesNotThrow(function () { m.has(new Object) }); -assertDoesNotThrow(function () { m.delete(new Object) }); - - -// Test invalid getter and setter calls -var m = new WeakMap; -assertThrows(function () { m.get(undefined) }, TypeError); -assertThrows(function () { m.set(undefined, 0) }, TypeError); -assertThrows(function () { m.get(0) }, TypeError); -assertThrows(function () { m.set(0, 0) }, TypeError); -assertThrows(function () { m.get('a-key') }, TypeError); -assertThrows(function () { m.set('a-key', 0) }, TypeError); - - -// Test expected mapping behavior -var m = new WeakMap; -function TestMapping(map, key, value) { - map.set(key, value); - assertSame(value, map.get(key)); -} -TestMapping(m, new Object, 23); -TestMapping(m, new Object, 'the-value'); -TestMapping(m, new Object, new Object); - - -// Test expected querying behavior -var m = new WeakMap; -var key = new Object; -TestMapping(m, key, 'to-be-present'); -assertTrue(m.has(key)); -assertFalse(m.has(new Object)); -TestMapping(m, key, undefined); -assertFalse(m.has(key)); -assertFalse(m.has(new Object)); - - -// Test expected deletion behavior -var m = new WeakMap; -var key = new Object; -TestMapping(m, key, 'to-be-deleted'); -assertTrue(m.delete(key)); -assertFalse(m.delete(key)); -assertFalse(m.delete(new Object)); -assertSame(m.get(key), undefined); - - -// Test GC of map with entry -var m = new WeakMap; -var key = new Object; -m.set(key, 'not-collected'); -gc(); -assertSame('not-collected', m.get(key)); - - -// Test GC of map with chained entries -var m = new WeakMap; -var head = new Object; -for (key = head, i = 0; i < 10; i++, key = m.get(key)) { - m.set(key, new Object); -} -gc(); -var count = 0; -for (key = head; key != undefined; key = m.get(key)) { - count++; -} -assertEquals(11, count); - - -// Test property attribute [[Enumerable]] -var m = new WeakMap; -function props(x) { - var array = []; - for (var p in x) array.push(p); - return array.sort(); -} -assertArrayEquals([], props(WeakMap)); -assertArrayEquals([], props(WeakMap.prototype)); -assertArrayEquals([], props(m)); - - -// Test arbitrary properties on weak maps -var m = new WeakMap; -function TestProperty(map, property, value) { - map[property] = value; - assertEquals(value, map[property]); -} -for (i = 0; i < 20; i++) { - TestProperty(m, i, 'val' + i); - TestProperty(m, 'foo' + i, 'bar' + i); -} -TestMapping(m, new Object, 'foobar'); - - -// Test direct constructor call -var m = WeakMap(); -assertTrue(m instanceof WeakMap); - - -// Test some common JavaScript idioms -var m = new WeakMap; -assertTrue(m instanceof WeakMap); -assertTrue(WeakMap.prototype.set instanceof Function) -assertTrue(WeakMap.prototype.get instanceof Function) -assertTrue(WeakMap.prototype.has instanceof Function) -assertTrue(WeakMap.prototype.delete instanceof Function) - - -// Regression test for WeakMap prototype. -assertTrue(WeakMap.prototype.constructor === WeakMap) -assertTrue(Object.getPrototypeOf(WeakMap.prototype) === Object.prototype) - - -// Regression test for issue 1617: The prototype of the WeakMap constructor -// needs to be unique (i.e. different from the one of the Object constructor). -assertFalse(WeakMap.prototype === Object.prototype); -var o = Object.create({}); -assertFalse("get" in o); -assertFalse("set" in o); -assertEquals(undefined, o.get); -assertEquals(undefined, o.set); -var o = Object.create({}, { myValue: { - value: 10, - enumerable: false, - configurable: true, - writable: true -}}); -assertEquals(10, o.myValue); - - -// Stress Test -// There is a proposed stress-test available at the es-discuss mailing list -// which cannot be reasonably automated. Check it out by hand if you like: -// https://mail.mozilla.org/pipermail/es-discuss/2011-May/014096.html diff --git a/deps/v8/test/mjsunit/math-min-max.js b/deps/v8/test/mjsunit/math-min-max.js index 0833c5c809..7717b3bff2 100644 --- a/deps/v8/test/mjsunit/math-min-max.js +++ b/deps/v8/test/mjsunit/math-min-max.js @@ -115,3 +115,67 @@ assertEquals(NaN, Math.max(1, 'oxen')); assertEquals(Infinity, 1/Math.max(ZERO, -0)); assertEquals(Infinity, 1/Math.max(-0, ZERO)); + +function run(crankshaft_test) { + crankshaft_test(1); + crankshaft_test(1); + %OptimizeFunctionOnNextCall(crankshaft_test); + crankshaft_test(-0); +} + +function crankshaft_test_1(arg) { + var v1 = 1; + var v2 = 5; + var v3 = 1.5; + var v4 = 5.5; + var v5 = 2; + var v6 = 6; + var v7 = 0; + var v8 = -0; + + var v9 = 9.9; + var v0 = 10.1; + // Integer32 representation. + assertEquals(v2, Math.max(v1++, v2++)); + assertEquals(v1, Math.min(v1++, v2++)); + // Tagged representation. + assertEquals(v4, Math.max(v3, v4)); + assertEquals(v3, Math.min(v3, v4)); + assertEquals(v6, Math.max(v5, v6)); + assertEquals(v5, Math.min(v5, v6)); + // Double representation. + assertEquals(v0, Math.max(v0++, v9++)); + assertEquals(v9, Math.min(v0++, v9++)); + // Minus zero. + assertEquals(Infinity, 1/Math.max(v7, v8)); + assertEquals(-Infinity, 1/Math.min(v7, v8)); + // NaN. + assertEquals(NaN, Math.max(NaN, v8)); + assertEquals(NaN, Math.min(NaN, v9)); + assertEquals(NaN, Math.max(v8, NaN)); + assertEquals(NaN, Math.min(v9, NaN)); + // Minus zero as Integer32. + assertEquals((arg === -0) ? -Infinity : 1, 1/Math.min(arg, v2)); +} + +run(crankshaft_test_1); + +function crankshaft_test_2() { + var v9 = {}; + v9.valueOf = function() { return 6; } + // Deopt expected due to non-heapnumber objects. + assertEquals(6, Math.min(v9, 12)); +} + +run(crankshaft_test_2); + +// Test overriding Math.min and Math.max +Math.min = function(a, b) { return a + b; } +Math.max = function(a, b) { return a - b; } + +function crankshaft_test_3() { + assertEquals(8, Math.min(3, 5)); + assertEquals(3, Math.max(5, 2)); +} + +run(crankshaft_test_3); diff --git a/deps/v8/test/mjsunit/math-pow.js b/deps/v8/test/mjsunit/math-pow.js index 30d0cbdced..fb5f8a1f90 100644 --- a/deps/v8/test/mjsunit/math-pow.js +++ b/deps/v8/test/mjsunit/math-pow.js @@ -25,118 +25,149 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax // Tests the special cases specified by ES 15.8.2.13 -// Simple sanity check -assertEquals(4, Math.pow(2, 2)); -assertEquals(2147483648, Math.pow(2, 31)); -assertEquals(0.25, Math.pow(2, -2)); -assertEquals(0.0625, Math.pow(2, -4)); -assertEquals(1, Math.pow(1, 100)); -assertEquals(0, Math.pow(0, 1000)); - -// Spec tests -assertEquals(NaN, Math.pow(2, NaN)); -assertEquals(NaN, Math.pow(+0, NaN)); -assertEquals(NaN, Math.pow(-0, NaN)); -assertEquals(NaN, Math.pow(Infinity, NaN)); -assertEquals(NaN, Math.pow(-Infinity, NaN)); - -assertEquals(1, Math.pow(NaN, +0)); -assertEquals(1, Math.pow(NaN, -0)); - -assertEquals(NaN, Math.pow(NaN, NaN)); -assertEquals(NaN, Math.pow(NaN, 2.2)); -assertEquals(NaN, Math.pow(NaN, 1)); -assertEquals(NaN, Math.pow(NaN, -1)); -assertEquals(NaN, Math.pow(NaN, -2.2)); -assertEquals(NaN, Math.pow(NaN, Infinity)); -assertEquals(NaN, Math.pow(NaN, -Infinity)); - -assertEquals(Infinity, Math.pow(1.1, Infinity)); -assertEquals(Infinity, Math.pow(-1.1, Infinity)); -assertEquals(Infinity, Math.pow(2, Infinity)); -assertEquals(Infinity, Math.pow(-2, Infinity)); - -// Because +0 == -0, we need to compare 1/{+,-}0 to {+,-}Infinity -assertEquals(+Infinity, 1/Math.pow(1.1, -Infinity)); -assertEquals(+Infinity, 1/Math.pow(-1.1, -Infinity)); -assertEquals(+Infinity, 1/Math.pow(2, -Infinity)); -assertEquals(+Infinity, 1/Math.pow(-2, -Infinity)); - -assertEquals(NaN, Math.pow(1, Infinity)); -assertEquals(NaN, Math.pow(1, -Infinity)); -assertEquals(NaN, Math.pow(-1, Infinity)); -assertEquals(NaN, Math.pow(-1, -Infinity)); - -assertEquals(+0, Math.pow(0.1, Infinity)); -assertEquals(+0, Math.pow(-0.1, Infinity)); -assertEquals(+0, Math.pow(0.999, Infinity)); -assertEquals(+0, Math.pow(-0.999, Infinity)); - -assertEquals(Infinity, Math.pow(0.1, -Infinity)); -assertEquals(Infinity, Math.pow(-0.1, -Infinity)); -assertEquals(Infinity, Math.pow(0.999, -Infinity)); -assertEquals(Infinity, Math.pow(-0.999, -Infinity)); - -assertEquals(Infinity, Math.pow(Infinity, 0.1)); -assertEquals(Infinity, Math.pow(Infinity, 2)); - -assertEquals(+Infinity, 1/Math.pow(Infinity, -0.1)); -assertEquals(+Infinity, 1/Math.pow(Infinity, -2)); - -assertEquals(-Infinity, Math.pow(-Infinity, 3)); -assertEquals(-Infinity, Math.pow(-Infinity, 13)); - -assertEquals(Infinity, Math.pow(-Infinity, 3.1)); -assertEquals(Infinity, Math.pow(-Infinity, 2)); - -assertEquals(-Infinity, 1/Math.pow(-Infinity, -3)); -assertEquals(-Infinity, 1/Math.pow(-Infinity, -13)); - -assertEquals(+Infinity, 1/Math.pow(-Infinity, -3.1)); -assertEquals(+Infinity, 1/Math.pow(-Infinity, -2)); - -assertEquals(+Infinity, 1/Math.pow(+0, 1.1)); -assertEquals(+Infinity, 1/Math.pow(+0, 2)); - -assertEquals(Infinity, Math.pow(+0, -1.1)); -assertEquals(Infinity, Math.pow(+0, -2)); - -assertEquals(-Infinity, 1/Math.pow(-0, 3)); -assertEquals(-Infinity, 1/Math.pow(-0, 13)); - -assertEquals(+Infinity, 1/Math.pow(-0, 3.1)); -assertEquals(+Infinity, 1/Math.pow(-0, 2)); - -assertEquals(-Infinity, Math.pow(-0, -3)); -assertEquals(-Infinity, Math.pow(-0, -13)); - -assertEquals(Infinity, Math.pow(-0, -3.1)); -assertEquals(Infinity, Math.pow(-0, -2)); - -assertEquals(NaN, Math.pow(-0.00001, 1.1)); -assertEquals(NaN, Math.pow(-0.00001, -1.1)); -assertEquals(NaN, Math.pow(-1.1, 1.1)); -assertEquals(NaN, Math.pow(-1.1, -1.1)); -assertEquals(NaN, Math.pow(-2, 1.1)); -assertEquals(NaN, Math.pow(-2, -1.1)); -assertEquals(NaN, Math.pow(-1000, 1.1)); -assertEquals(NaN, Math.pow(-1000, -1.1)); - -assertEquals(+Infinity, 1/Math.pow(-0, 0.5)); -assertEquals(+Infinity, 1/Math.pow(-0, 0.6)); -assertEquals(-Infinity, 1/Math.pow(-0, 1)); -assertEquals(-Infinity, 1/Math.pow(-0, 10000000001)); - -assertEquals(+Infinity, Math.pow(-0, -0.5)); -assertEquals(+Infinity, Math.pow(-0, -0.6)); -assertEquals(-Infinity, Math.pow(-0, -1)); -assertEquals(-Infinity, Math.pow(-0, -10000000001)); - - - -// Tests from Sputnik S8.5_A13_T1. -assertTrue((1*((Math.pow(2,53))-1)*(Math.pow(2,-1074))) === 4.4501477170144023e-308); -assertTrue((1*(Math.pow(2,52))*(Math.pow(2,-1074))) === 2.2250738585072014e-308); -assertTrue((-1*(Math.pow(2,52))*(Math.pow(2,-1074))) === -2.2250738585072014e-308); +function test() { + // Simple sanity check + assertEquals(4, Math.pow(2, 2)); + assertEquals(2147483648, Math.pow(2, 31)); + assertEquals(0.25, Math.pow(2, -2)); + assertEquals(0.0625, Math.pow(2, -4)); + assertEquals(1, Math.pow(1, 100)); + assertEquals(0, Math.pow(0, 1000)); + + // Spec tests + assertEquals(NaN, Math.pow(2, NaN)); + assertEquals(NaN, Math.pow(+0, NaN)); + assertEquals(NaN, Math.pow(-0, NaN)); + assertEquals(NaN, Math.pow(Infinity, NaN)); + assertEquals(NaN, Math.pow(-Infinity, NaN)); + + assertEquals(1, Math.pow(NaN, +0)); + assertEquals(1, Math.pow(NaN, -0)); + + assertEquals(NaN, Math.pow(NaN, NaN)); + assertEquals(NaN, Math.pow(NaN, 2.2)); + assertEquals(NaN, Math.pow(NaN, 1)); + assertEquals(NaN, Math.pow(NaN, -1)); + assertEquals(NaN, Math.pow(NaN, -2.2)); + assertEquals(NaN, Math.pow(NaN, Infinity)); + assertEquals(NaN, Math.pow(NaN, -Infinity)); + + assertEquals(Infinity, Math.pow(1.1, Infinity)); + assertEquals(Infinity, Math.pow(-1.1, Infinity)); + assertEquals(Infinity, Math.pow(2, Infinity)); + assertEquals(Infinity, Math.pow(-2, Infinity)); + + // Because +0 == -0, we need to compare 1/{+,-}0 to {+,-}Infinity + assertEquals(+Infinity, 1/Math.pow(1.1, -Infinity)); + assertEquals(+Infinity, 1/Math.pow(-1.1, -Infinity)); + assertEquals(+Infinity, 1/Math.pow(2, -Infinity)); + assertEquals(+Infinity, 1/Math.pow(-2, -Infinity)); + + assertEquals(NaN, Math.pow(1, Infinity)); + assertEquals(NaN, Math.pow(1, -Infinity)); + assertEquals(NaN, Math.pow(-1, Infinity)); + assertEquals(NaN, Math.pow(-1, -Infinity)); + + assertEquals(+0, Math.pow(0.1, Infinity)); + assertEquals(+0, Math.pow(-0.1, Infinity)); + assertEquals(+0, Math.pow(0.999, Infinity)); + assertEquals(+0, Math.pow(-0.999, Infinity)); + + assertEquals(Infinity, Math.pow(0.1, -Infinity)); + assertEquals(Infinity, Math.pow(-0.1, -Infinity)); + assertEquals(Infinity, Math.pow(0.999, -Infinity)); + assertEquals(Infinity, Math.pow(-0.999, -Infinity)); + + assertEquals(Infinity, Math.pow(Infinity, 0.1)); + assertEquals(Infinity, Math.pow(Infinity, 2)); + + assertEquals(+Infinity, 1/Math.pow(Infinity, -0.1)); + assertEquals(+Infinity, 1/Math.pow(Infinity, -2)); + + assertEquals(-Infinity, Math.pow(-Infinity, 3)); + assertEquals(-Infinity, Math.pow(-Infinity, 13)); + + assertEquals(Infinity, Math.pow(-Infinity, 3.1)); + assertEquals(Infinity, Math.pow(-Infinity, 2)); + + assertEquals(-Infinity, 1/Math.pow(-Infinity, -3)); + assertEquals(-Infinity, 1/Math.pow(-Infinity, -13)); + + assertEquals(+Infinity, 1/Math.pow(-Infinity, -3.1)); + assertEquals(+Infinity, 1/Math.pow(-Infinity, -2)); + + assertEquals(+Infinity, 1/Math.pow(+0, 1.1)); + assertEquals(+Infinity, 1/Math.pow(+0, 2)); + + assertEquals(Infinity, Math.pow(+0, -1.1)); + assertEquals(Infinity, Math.pow(+0, -2)); + + assertEquals(-Infinity, 1/Math.pow(-0, 3)); + assertEquals(-Infinity, 1/Math.pow(-0, 13)); + + assertEquals(+Infinity, 1/Math.pow(-0, 3.1)); + assertEquals(+Infinity, 1/Math.pow(-0, 2)); + + assertEquals(-Infinity, Math.pow(-0, -3)); + assertEquals(-Infinity, Math.pow(-0, -13)); + + assertEquals(Infinity, Math.pow(-0, -3.1)); + assertEquals(Infinity, Math.pow(-0, -2)); + + assertEquals(NaN, Math.pow(-0.00001, 1.1)); + assertEquals(NaN, Math.pow(-0.00001, -1.1)); + assertEquals(NaN, Math.pow(-1.1, 1.1)); + assertEquals(NaN, Math.pow(-1.1, -1.1)); + assertEquals(NaN, Math.pow(-2, 1.1)); + assertEquals(NaN, Math.pow(-2, -1.1)); + assertEquals(NaN, Math.pow(-1000, 1.1)); + assertEquals(NaN, Math.pow(-1000, -1.1)); + + assertEquals(+Infinity, 1/Math.pow(-0, 0.5)); + assertEquals(+Infinity, 1/Math.pow(-0, 0.6)); + assertEquals(-Infinity, 1/Math.pow(-0, 1)); + assertEquals(-Infinity, 1/Math.pow(-0, 10000000001)); + + assertEquals(+Infinity, Math.pow(-0, -0.5)); + assertEquals(+Infinity, Math.pow(-0, -0.6)); + assertEquals(-Infinity, Math.pow(-0, -1)); + assertEquals(-Infinity, Math.pow(-0, -10000000001)); + + assertEquals(4, Math.pow(16, 0.5)); + assertEquals(NaN, Math.pow(-16, 0.5)); + assertEquals(0.25, Math.pow(16, -0.5)); + assertEquals(NaN, Math.pow(-16, -0.5)); + + // Test detecting and converting integer value as double. + assertEquals(8, Math.pow(2, Math.sqrt(9))); + + // Tests from Mozilla 15.8.2.13. + assertEquals(2, Math.pow.length); + assertEquals(NaN, Math.pow()); + assertEquals(1, Math.pow(null, null)); + assertEquals(NaN, Math.pow(void 0, void 0)); + assertEquals(1, Math.pow(true, false)); + assertEquals(0, Math.pow(false, true)); + assertEquals(Infinity, Math.pow(-Infinity, Infinity)); + assertEquals(0, Math.pow(-Infinity, -Infinity)); + assertEquals(1, Math.pow(0, 0)); + assertEquals(0, Math.pow(0, Infinity)); + assertEquals(NaN, Math.pow(NaN, 0.5)); + assertEquals(NaN, Math.pow(NaN, -0.5)); + + // Tests from Sputnik S8.5_A13_T1. + assertTrue( + (1*((Math.pow(2,53))-1)*(Math.pow(2,-1074))) === 4.4501477170144023e-308); + assertTrue( + (1*(Math.pow(2,52))*(Math.pow(2,-1074))) === 2.2250738585072014e-308); + assertTrue( + (-1*(Math.pow(2,52))*(Math.pow(2,-1074))) === -2.2250738585072014e-308); +} + +test(); +test(); +%OptimizeFunctionOnNextCall(test); +test();
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/mjsunit.js b/deps/v8/test/mjsunit/mjsunit.js index faa5a43829..6f6e3230d5 100644 --- a/deps/v8/test/mjsunit/mjsunit.js +++ b/deps/v8/test/mjsunit/mjsunit.js @@ -223,7 +223,7 @@ var assertUnreachable; assertSame = function assertSame(expected, found, name_opt) { if (found === expected) { if (expected !== 0 || (1 / expected) == (1 / found)) return; - } else if (isNaN(expected) && isNaN(found)) { + } else if ((expected !== expected) && (found !== found)) { return; } fail(PrettyPrint(expected), found, name_opt); diff --git a/deps/v8/test/mjsunit/mjsunit.status b/deps/v8/test/mjsunit/mjsunit.status index bae09b4e38..2d22fe9075 100644 --- a/deps/v8/test/mjsunit/mjsunit.status +++ b/deps/v8/test/mjsunit/mjsunit.status @@ -34,9 +34,10 @@ bugs: FAIL # Fails. regress/regress-1119: FAIL -############################################################################# -# Fails due to r10102 which reverts precise stepping on the 3.6 branch. -debug-step-2: FAIL +############################################################################## + +# NewGC: BUG(1719) slow to collect arrays over several contexts. +regress/regress-524: SKIP ############################################################################## # Too slow in debug mode with --stress-opt @@ -46,16 +47,16 @@ regress/regress-create-exception: PASS, SKIP if $mode == debug ############################################################################## # This one uses a built-in that's only present in debug mode. It takes -# too long to run in debug mode on ARM. -fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm) +# too long to run in debug mode on ARM and MIPS. +fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm || $arch == mips) big-object-literal: PASS, SKIP if ($arch == arm) # Issue 488: this test sometimes times out. array-constructor: PASS || TIMEOUT -# Very slow on ARM, contains no architecture dependent code. -unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm) +# Very slow on ARM and MIPS, contains no architecture dependent code. +unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm || $arch == mips) # Skip long running test in debug and allow it to timeout in release mode. regress/regress-524: (PASS || TIMEOUT), SKIP if $mode == debug @@ -64,6 +65,12 @@ regress/regress-524: (PASS || TIMEOUT), SKIP if $mode == debug debug-liveedit-check-stack: SKIP debug-liveedit-patch-positions-replace: SKIP +############################################################################## +[ $isolates ] + +# This test sets the umask on a per-process basis and hence cannot be +# used in multi-threaded runs. +d8-os: SKIP ############################################################################## [ $arch == arm ] @@ -119,11 +126,23 @@ regress/regress-1132: SKIP ############################################################################## [ $arch == mips ] -# Run those tests, but expect them to time out. -array-sort: PASS || TIMEOUT + +# Slow tests which times out in debug mode. +try: PASS, SKIP if $mode == debug +debug-scripts-request: PASS, SKIP if $mode == debug +array-constructor: PASS, SKIP if $mode == debug + +# Times out often in release mode on MIPS. +compiler/regress-stacktrace-methods: PASS, PASS || TIMEOUT if $mode == release +array-splice: PASS || TIMEOUT + +# Long running test. mirror-object: PASS || TIMEOUT +string-indexof-2: PASS || TIMEOUT -# Skip long-running tests. +# BUG(3251035): Timeouts in long looping crankshaft optimization +# tests. Skipping because having them timeout takes too long on the +# buildbot. compiler/alloc-number: SKIP compiler/array-length: SKIP compiler/assignment-deopt: SKIP @@ -148,12 +167,8 @@ regress/regress-634: SKIP regress/regress-create-exception: SKIP regress/regress-3218915: SKIP regress/regress-3247124: SKIP -regress/regress-1132: SKIP -regress/regress-1257: SKIP -regress/regress-91008: SKIP -############################################################################## -[ $isolates ] -# d8-os writes temporary files that might interfer with each other when running -# in multible threads. Skip this if running with isolates testing. -d8-os: SKIP +# Requires bigger stack size in the Genesis and if stack size is increased, +# the test requires too much time to run. However, the problem test covers +# should be platform-independent. +regress/regress-1132: SKIP diff --git a/deps/v8/test/mjsunit/object-define-properties.js b/deps/v8/test/mjsunit/object-define-properties.js index 128df694d3..6d5032e044 100644 --- a/deps/v8/test/mjsunit/object-define-properties.js +++ b/deps/v8/test/mjsunit/object-define-properties.js @@ -54,3 +54,19 @@ var x = Object.defineProperties(obj, desc); assertEquals(x.foo, 10); assertEquals(x.bar, 42); + + +// Make sure that all property descriptors are calculated before any +// modifications are done. + +var object = {}; + +assertThrows(function() { + Object.defineProperties(object, { + foo: { value: 1 }, + bar: { value: 2, get: function() { return 3; } } + }); + }, TypeError); + +assertEquals(undefined, object.foo); +assertEquals(undefined, object.bar); diff --git a/deps/v8/test/mjsunit/optimized-typeof.js b/deps/v8/test/mjsunit/optimized-typeof.js new file mode 100644 index 0000000000..b0c0725c51 --- /dev/null +++ b/deps/v8/test/mjsunit/optimized-typeof.js @@ -0,0 +1,47 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function typeofDirectly() { + return typeof({}) === "undefined"; +} + +typeofDirectly(); +typeofDirectly(); +%OptimizeFunctionOnNextCall(typeofDirectly); +typeofDirectly(); + +function typeofViaVariable() { + var foo = typeof({}) + return foo === "undefined"; +} + +typeofViaVariable(); +typeofViaVariable(); +%OptimizeFunctionOnNextCall(typeofViaVariable); +typeofViaVariable(); diff --git a/deps/v8/test/mjsunit/regexp-static.js b/deps/v8/test/mjsunit/regexp-static.js index 0f849687cc..8f283f6cee 100644 --- a/deps/v8/test/mjsunit/regexp-static.js +++ b/deps/v8/test/mjsunit/regexp-static.js @@ -25,18 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Test that we throw exceptions when calling test and exec with no -// input. This is not part of the spec, but we do it for -// compatibility with JSC. -assertThrows("/a/.test()"); -assertThrows("/a/.exec()"); - -// Test that we do not throw exceptions once the static RegExp.input -// field has been set. -RegExp.input = "a"; -assertDoesNotThrow("/a/.test()"); -assertDoesNotThrow("/a/.exec()"); - // Test the (deprecated as of JS 1.5) properties of the RegExp function. var re = /((\d+)\.(\d+))/; var s = 'abc123.456def'; @@ -166,3 +154,8 @@ assertTrue(typeof RegExp.input == typeof String(), "RegExp.input coerces values var foo = "lsdfj sldkfj sdklfj læsdfjl sdkfjlsdk fjsdl fjsdljskdj flsj flsdkj flskd regexp: /foobar/\nldkfj sdlkfj sdkl"; assertTrue(/^([a-z]+): (.*)/.test(foo.substring(foo.indexOf("regexp:"))), "regexp: setup"); assertEquals("regexp", RegExp.$1, "RegExp.$1"); + + +// Check that calling with no argument is the same as calling with undefined. +assertTrue(/^undefined$/.test()); +assertEquals(["undefined"], /^undefined$/.exec()); diff --git a/deps/v8/test/mjsunit/regress/regress-100702.js b/deps/v8/test/mjsunit/regress/regress-100702.js new file mode 100644 index 0000000000..46494ab71d --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-100702.js @@ -0,0 +1,44 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Regression test for correct handling of non-object receiver values +// passed to built-in array functions. + +String.prototype.isThatMe = function () { + assertFalse(this === str); +}; + +var str = "abc"; +str.isThatMe(); +str.isThatMe.call(str); + +var arr = [1]; +arr.forEach("".isThatMe, str); +arr.filter("".isThatMe, str); +arr.some("".isThatMe, str); +arr.every("".isThatMe, str); +arr.map("".isThatMe, str); diff --git a/deps/v8/test/mjsunit/regress/regress-108296.js b/deps/v8/test/mjsunit/regress/regress-108296.js new file mode 100644 index 0000000000..38ecda778c --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-108296.js @@ -0,0 +1,52 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// This test checks that young immediates embedded into code objects +// are referenced through a cell. + +function f (k, a, b) { + // Create control flow for a.foo. Control flow resolution will + // be generated as a part of a gap move. Gap move operate on immediates as + // a.foo is a CONSTANT_FUNCTION. + var x = k ? a.foo : a.foo; + return x.prototype; +} + +var a = { }; + +// Make sure that foo is a CONSTANT_FUNCTION but not be pretenured. +a.foo = (function () { return function () {}; })(); + +// Ensure that both branches of ternary operator have monomorphic type feedback. +f(true, a, a); +f(true, a, a); +f(false, a, a); +f(false, a, a); +%OptimizeFunctionOnNextCall(f); +f(true, a, a); diff --git a/deps/v8/test/mjsunit/regress/regress-109195.js b/deps/v8/test/mjsunit/regress/regress-109195.js new file mode 100644 index 0000000000..97538aa167 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-109195.js @@ -0,0 +1,65 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug +var Debug = debug.Debug; + +function listener(event, exec_state, event_data, data) { + for (var i = 0, n = exec_state.frameCount(); i < n; i++) { + exec_state.frame().scopeCount(i); + } + exec_state.prepareStep(Debug.StepAction.Continue, 1); +} + +Debug.setListener(listener); + +var F = function () { + 1, function () { + var d = 0; + (function () { d; }); + debugger; + }(); +}; + +var src = "(" + F.toString() + ")()"; +eval(src); + +Function.prototype.__defineGetter__("f", function () { + debugger; + return 0; +}); + +var G = function () { + 1, function () { + var d = 0; + (function () { d; }); + debugger; + }['f']; +}; + +var src = "(" + G.toString() + ")()"; +eval(src); diff --git a/deps/v8/test/mjsunit/regress/regress-110509.js b/deps/v8/test/mjsunit/regress/regress-110509.js new file mode 100644 index 0000000000..132bd233be --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-110509.js @@ -0,0 +1,41 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// Verify that LRandom preserves rsi correctly. + +function foo() { + Math.random(); + new Function(""); +} + +foo(); +foo(); +foo(); +%OptimizeFunctionOnNextCall(foo); +foo(); diff --git a/deps/v8/test/mjsunit/regress/regress-1110.js b/deps/v8/test/mjsunit/regress/regress-1110.js index 43b8d77aeb..124f520ca6 100644 --- a/deps/v8/test/mjsunit/regress/regress-1110.js +++ b/deps/v8/test/mjsunit/regress/regress-1110.js @@ -28,10 +28,9 @@ // Test that the illegal continue is thrown at parse time. try { - function Crash() { continue;if (Crash) { - } } + eval("function Crash() { assertUnreachable(); continue;if (Crash) { } }"); Crash(); - assertTrue(false); + assertUnreachable(); } catch (e) { assertTrue(e instanceof SyntaxError); assertTrue(/continue/.test(e.message)); diff --git a/deps/v8/test/mjsunit/regress/regress-1170.js b/deps/v8/test/mjsunit/regress/regress-1170.js index 95684c5418..66ed9f29e2 100644 --- a/deps/v8/test/mjsunit/regress/regress-1170.js +++ b/deps/v8/test/mjsunit/regress/regress-1170.js @@ -49,7 +49,7 @@ try { exception = true; assertTrue(/TypeError/.test(e)); } -assertTrue(exception); +assertFalse(exception); exception = false; try { diff --git a/deps/v8/test/mjsunit/regress/regress-1213575.js b/deps/v8/test/mjsunit/regress/regress-1213575.js index 9d82064e47..f3a11dbaab 100644 --- a/deps/v8/test/mjsunit/regress/regress-1213575.js +++ b/deps/v8/test/mjsunit/regress/regress-1213575.js @@ -25,17 +25,16 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Make sure that a const definition always -// conflicts with a defined setter. This avoid -// trying to pass 'the hole' to the setter. +// Make sure that a const definition does not try +// to pass 'the hole' to a defined setter. -this.__defineSetter__('x', function(value) { assertTrue(false); }); +this.__defineSetter__('x', function(value) { assertTrue(value === 1); }); var caught = false; try { - eval('const x'); + eval('const x = 1'); } catch(e) { assertTrue(e instanceof TypeError); caught = true; } -assertTrue(caught); +assertFalse(caught); diff --git a/deps/v8/test/mjsunit/regress/regress-1217.js b/deps/v8/test/mjsunit/regress/regress-1217.js new file mode 100644 index 0000000000..6530549864 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1217.js @@ -0,0 +1,50 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Check that RegExp.prototype is itself a RegExp object. + +var proto = RegExp.prototype; +assertEquals("[object RegExp]", Object.prototype.toString.call(proto)); + +assertEquals("", proto.source); +assertEquals(false, proto.global); +assertEquals(false, proto.multiline); +assertEquals(false, proto.ignoreCase); +assertEquals(0, proto.lastIndex); + +assertEquals("/(?:)/", proto.toString()); + +var execResult = proto.exec("argle"); +assertEquals(1, execResult.length); +assertEquals("", execResult[0]); +assertEquals("argle", execResult.input); +assertEquals(0, execResult.index); + +assertTrue(proto.test("argle")); + +// We disallow re-compiling the RegExp.prototype object. +assertThrows(function(){ proto.compile("something"); }, TypeError); diff --git a/deps/v8/test/mjsunit/regress/regress-1229.js b/deps/v8/test/mjsunit/regress/regress-1229.js index e16d278b38..3d166d5b4f 100644 --- a/deps/v8/test/mjsunit/regress/regress-1229.js +++ b/deps/v8/test/mjsunit/regress/regress-1229.js @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,59 +29,116 @@ // Check that %NewObjectFromBound works correctly when called from optimized // frame. -function foo(x, y, z) { +function foo1(x, y, z) { assertEquals(1, x); assertEquals(2, y); assertEquals(3, z); } -var bound_arg = [1]; +function foo2(x, y, z) { + assertEquals(1, x); + assertEquals(2, y); + assertEquals(undefined, z); +} + +function foo3(x, y, z) { + assertEquals(1, x); + assertEquals(2, y); + assertEquals(3, z); +} -function f(y, z) { - return %NewObjectFromBound(foo, bound_arg); + +var foob1 = foo1.bind({}, 1); +var foob2 = foo2.bind({}, 1); +var foob3 = foo3.bind({}, 1); + + +function f1(y, z) { + return %NewObjectFromBound(foob1); +} + +function f2(y, z) { + return %NewObjectFromBound(foob2); +} + +function f3(y, z) { + return %NewObjectFromBound(foob3); } // Check that %NewObjectFromBound looks at correct frame for inlined function. -function g(z, y) { - return f(y, z); /* f should be inlined into g, note rotated arguments */ +function g1(z, y) { + return f1(y, z); /* f should be inlined into g, note rotated arguments */ +} + +function g2(z, y, x) { + return f2(y); /* f should be inlined into g, note argument count mismatch */ +} + +function g3(z, y, x) { + return f3(x, y, z); /* f should be inlined into g, note argument count mismatch */ } // Check that %NewObjectFromBound looks at correct frame for inlined function. function ff(x) { } -function h(z2, y2) { +function h1(z2, y2) { + var local_z = z2 >> 1; + ff(local_z); + var local_y = y2 >> 1; + ff(local_y); + return f1(local_y, local_z); /* f should be inlined into h */ +} + +function h2(z2, y2, x2) { + var local_z = z2 >> 1; + ff(local_z); + var local_y = y2 >> 1; + ff(local_y); + return f2(local_y); /* f should be inlined into h */ +} + +function h3(z2, y2, x2) { var local_z = z2 >> 1; ff(local_z); var local_y = y2 >> 1; ff(local_y); - return f(local_y, local_z); /* f should be inlined into h */ + var local_x = x2 >> 1; + ff(local_x); + return f3(local_x, local_y, local_z); /* f should be inlined into h */ } -for (var i = 0; i < 5; i++) f(2, 3); -%OptimizeFunctionOnNextCall(f); -f(2, 3); -for (var i = 0; i < 5; i++) g(3, 2); -%OptimizeFunctionOnNextCall(g); -g(3, 2); +function invoke(f, args) { + for (var i = 0; i < 5; i++) f.apply(this, args); + %OptimizeFunctionOnNextCall(f); + f.apply(this, args); +} -for (var i = 0; i < 5; i++) h(6, 4); -%OptimizeFunctionOnNextCall(h); -h(6, 4); +invoke(f1, [2, 3]); +invoke(f2, [2]); +invoke(f3, [2, 3, 4]); +invoke(g1, [3, 2]); +invoke(g2, [3, 2, 4]); +invoke(g3, [4, 3, 2]); +invoke(h1, [6, 4]); +invoke(h2, [6, 4, 8]); +invoke(h3, [8, 6, 4]); // Check that %_IsConstructCall returns correct value when inlined var NON_CONSTRUCT_MARKER = {}; var CONSTRUCT_MARKER = {}; -function baz() { +function baz(x) { return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER; } function bar(x, y, z) { + var non_construct = baz(0); /* baz should be inlined */ + assertSame(non_construct, NON_CONSTRUCT_MARKER); var non_construct = baz(); /* baz should be inlined */ - assertEquals(non_construct, NON_CONSTRUCT_MARKER); - var construct = new baz(); - assertEquals(construct, CONSTRUCT_MARKER); + assertSame(non_construct, NON_CONSTRUCT_MARKER); + var non_construct = baz(0, 0); /* baz should be inlined */ + assertSame(non_construct, NON_CONSTRUCT_MARKER); + var construct = new baz(0); + assertSame(construct, CONSTRUCT_MARKER); } -for (var i = 0; i < 5; i++) new bar(1, 2, 3); -%OptimizeFunctionOnNextCall(bar); -bar(1, 2, 3); +invoke(bar, [1, 2, 3]); diff --git a/deps/v8/test/mjsunit/regress/regress-1415.js b/deps/v8/test/mjsunit/regress/regress-1415.js new file mode 100644 index 0000000000..f993e9b3d8 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1415.js @@ -0,0 +1,42 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Surrogate pair range. +// U+D800 +assertThrows(function(){ decodeURIComponent("%ED%A0%80"); }, URIError); +// U+DBFF +assertThrows(function(){ decodeURIComponent("%ED%AF%BF"); }, URIError); +// U+DC00 +assertThrows(function(){ decodeURIComponent("%ED%B0%80"); }, URIError); +// U+DFFF +assertThrows(function(){ decodeURIComponent("%ED%BF%BF"); }, URIError); + +// Overlong encodings +// U+007F in two bytes. +assertThrows(function(){ decodeURIComponent("%C1%BF"); }, URIError); +// U+07FF in three bytes. +assertThrows(function(){ decodeURIComponent("%E0%9F%BF"); }, URIError); diff --git a/deps/v8/test/mjsunit/regress/regress-1530.js b/deps/v8/test/mjsunit/regress/regress-1530.js new file mode 100644 index 0000000000..db2114450e --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1530.js @@ -0,0 +1,69 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that redefining the 'prototype' property of a function object +// does actually set the internal value and does not screw up any +// shadowing between said property and the internal value. + +var f = function() {}; + +// Verify that normal assignment of 'prototype' property works properly +// and updates the internal value. +var x = { foo: 'bar' }; +f.prototype = x; +assertSame(f.prototype, x); +assertSame(f.prototype.foo, 'bar'); +assertSame(new f().foo, 'bar'); +assertSame(Object.getPrototypeOf(new f()), x); +assertSame(Object.getOwnPropertyDescriptor(f, 'prototype').value, x); + +// Verify that 'prototype' behaves like a data property when it comes to +// redefining with Object.defineProperty() and the internal value gets +// updated. +var y = { foo: 'baz' }; +Object.defineProperty(f, 'prototype', { value: y, writable: true }); +assertSame(f.prototype, y); +assertSame(f.prototype.foo, 'baz'); +assertSame(new f().foo, 'baz'); +assertSame(Object.getPrototypeOf(new f()), y); +assertSame(Object.getOwnPropertyDescriptor(f, 'prototype').value, y); + +// Verify that the previous redefinition didn't screw up callbacks and +// the internal value still gets updated. +var z = { foo: 'other' }; +f.prototype = z; +assertSame(f.prototype, z); +assertSame(f.prototype.foo, 'other'); +assertSame(new f().foo, 'other'); +assertSame(Object.getPrototypeOf(new f()), z); +assertSame(Object.getOwnPropertyDescriptor(f, 'prototype').value, z); + +// Verify that non-writability of other properties is respected. +assertThrows("Object.defineProperty(f, 'name', { value: {} })"); +assertThrows("Object.defineProperty(f, 'length', { value: {} })"); +assertThrows("Object.defineProperty(f, 'caller', { value: {} })"); +assertThrows("Object.defineProperty(f, 'arguments', { value: {} })"); diff --git a/deps/v8/test/mjsunit/regress/regress-1639-2.js b/deps/v8/test/mjsunit/regress/regress-1639-2.js new file mode 100644 index 0000000000..c439dd8fff --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1639-2.js @@ -0,0 +1,93 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug + +function sendCommand(state, cmd) { + // Get the debug command processor in paused state. + var dcp = state.debugCommandProcessor(false); + var request = JSON.stringify(cmd); + var response = dcp.processDebugJSONRequest(request); +} + +var state = 0; + +function listener(event, exec_state, event_data, data) { + try { + if (event == Debug.DebugEvent.Break) { + var line = event_data.sourceLineText(); + print('break: ' + line); + print('event data: ' + event_data.toJSONProtocol()); + print(); + assertEquals('// BREAK', line.substr(-8), + "should not break outside evaluate"); + + switch (state) { + case 0: + state = 1; + // While in the debugger and stepping through a set of instructions + // executed in the evaluate command, the stepping must stop at the end + // of the said set of instructions and not step further into native + // debugger code. + sendCommand(exec_state, { + seq : 0, + type : "request", + command : "evaluate", + arguments : { + 'expression' : 'print("A"); debugger; print("B"); // BREAK', + 'global' : true + } + }); + break; + case 1: + sendCommand(exec_state, { + seq : 0, + type : "request", + command : "continue", + arguments : { + stepaction : "next" + } + }); + break; + } + } + } catch (e) { + print(e); + } +} + +// Add the debug event listener. +Debug.setListener(listener); + +function a() { +} // BREAK + +// Set a break point and call to invoke the debug event listener. +Debug.setBreakPoint(a, 0, 0); +a(); diff --git a/deps/v8/test/mjsunit/regress/regress-1692.js b/deps/v8/test/mjsunit/regress/regress-1692.js new file mode 100644 index 0000000000..06bd66cf7f --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1692.js @@ -0,0 +1,89 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that Object.prototype.propertyIsEnumerable handles array indices +// correctly. + +var p = Object.create({}, { + a : { value : 42, enumerable : true }, + b : { value : 42, enumerable : false }, + 1 : { value : 42, enumerable : true }, + 2 : { value : 42, enumerable : false }, + f : { get: function(){}, enumerable: true }, + g : { get: function(){}, enumerable: false }, + 11 : { get: function(){}, enumerable: true }, + 12 : { get: function(){}, enumerable: false } +}); +var o = Object.create(p, { + c : { value : 42, enumerable : true }, + d : { value : 42, enumerable : false }, + 3 : { value : 42, enumerable : true }, + 4 : { value : 42, enumerable : false }, + h : { get: function(){}, enumerable: true }, + k : { get: function(){}, enumerable: false }, + 13 : { get: function(){}, enumerable: true }, + 14 : { get: function(){}, enumerable: false } +}); + +// Inherited properties are ignored. +assertFalse(o.propertyIsEnumerable("a")); +assertFalse(o.propertyIsEnumerable("b")); +assertFalse(o.propertyIsEnumerable("1")); +assertFalse(o.propertyIsEnumerable("2")); + +// Own properties. +assertTrue(o.propertyIsEnumerable("c")); +assertFalse(o.propertyIsEnumerable("d")); +assertTrue(o.propertyIsEnumerable("3")); +assertFalse(o.propertyIsEnumerable("4")); + +// Inherited accessors. +assertFalse(o.propertyIsEnumerable("f")); +assertFalse(o.propertyIsEnumerable("g")); +assertFalse(o.propertyIsEnumerable("11")); +assertFalse(o.propertyIsEnumerable("12")); + +// Own accessors. +assertTrue(o.propertyIsEnumerable("h")); +assertFalse(o.propertyIsEnumerable("k")); +assertTrue(o.propertyIsEnumerable("13")); +assertFalse(o.propertyIsEnumerable("14")); + +// Nonexisting properties. +assertFalse(o.propertyIsEnumerable("xxx")); +assertFalse(o.propertyIsEnumerable("999")); + +// String object properties. +var o = Object("string"); +// Non-string property on String object. +o[10] = 42; +assertTrue(o.propertyIsEnumerable(10)); +assertFalse(o.propertyIsEnumerable(0)); + +// Fast elements. +var o = [1,2,3,4,5]; +assertTrue(o.propertyIsEnumerable(3)); diff --git a/deps/v8/test/mjsunit/regress/regress-1708.js b/deps/v8/test/mjsunit/regress/regress-1708.js new file mode 100644 index 0000000000..ab50e07864 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1708.js @@ -0,0 +1,63 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Regression test of a very rare corner case where left-trimming an +// array caused invalid marking bit patterns on lazily swept pages. + +// Flags: --expose-gc --noincremental-marking --max-new-space-size 1000 + +(function() { + var head = new Array(1); + var tail = head; + + // Fill heap to increase old-space size and trigger lazy sweeping on + // some of the old-space pages. + for (var i = 0; i < 200; i++) { + tail[1] = new Array(1000); + tail = tail[1]; + } + array = new Array(100); + gc(); gc(); + + // At this point "array" should have been promoted to old-space and be + // located in a lazy swept page with intact marking bits. Now shift + // the array to trigger left-trimming operations. + assertEquals(100, array.length); + for (var i = 0; i < 50; i++) { + array.shift(); + } + assertEquals(50, array.length); + + // At this point "array" should have been trimmed from the left with + // marking bits being correctly transfered to the new object start. + // Scavenging operations cause lazy sweeping to advance and verify + // that marking bit patterns are still sane. + for (var i = 0; i < 200; i++) { + tail[1] = new Array(1000); + tail = tail[1]; + } +})(); diff --git a/deps/v8/test/mjsunit/regress/regress-1711.js b/deps/v8/test/mjsunit/regress/regress-1711.js new file mode 100644 index 0000000000..15591b1e01 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1711.js @@ -0,0 +1,38 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// string.split needs to evaluate the separator's toString even if limit +// is 0 because toString may have side effects. + +var side_effect = false; +var separator = new Object(); +separator.toString = function() { + side_effect = true; + return undefined; +} +'subject'.split(separator, 0); +assertTrue(side_effect); diff --git a/deps/v8/test/mjsunit/regress/regress-1713.js b/deps/v8/test/mjsunit/regress/regress-1713.js new file mode 100644 index 0000000000..0af1144a15 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1713.js @@ -0,0 +1,127 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --always-compact --expose-gc + +var O = { get f() { return 0; } }; + +var CODE = []; + +var R = []; + +function Allocate4Kb(N) { + var arr = []; + do {arr.push(new Array(1024));} while (--N > 0); + return arr; +} + +function AllocateXMb(X) { + return Allocate4Kb((1024 * X) / 4); +} + +function Node(v, next) { this.v = v; this.next = next; } + +Node.prototype.execute = function (O) { + var n = this; + while (n.next !== null) n = n.next; + n.v(O); +}; + +function LongList(N, x) { + if (N == 0) return new Node(x, null); + return new Node(new Array(1024), LongList(N - 1, x)); +} + +var L = LongList(1024, function (O) { + for (var i = 0; i < 5; i++) O.f; +}); + + + +function Incremental(O, x) { + if (!x) { + return; + } + function CreateCode(i) { + var f = new Function("return O.f_" + i); + CODE.push(f); + f(); // compile + f(); // compile + f(); // compile + } + + for (var i = 0; i < 1e4; i++) CreateCode(i); + gc(); + gc(); + gc(); + + print(">>> 1 <<<"); + + L.execute(O); + + try {} catch (e) {} + + L = null; + print(">>> 2 <<<"); + AllocateXMb(8); + //rint("1"); + //llocateXMb(8); + //rint("1"); + //llocateXMb(8); + +} + +function foo(O, x) { + Incremental(O, x); + + print('f'); + + for (var i = 0; i < 5; i++) O.f; + + + print('g'); + + bar(x); +} + +function bar(x) { + if (!x) return; + %DeoptimizeFunction(foo); + AllocateXMb(8); + AllocateXMb(8); +} + +var O1 = {}; +var O2 = {}; +var O3 = {}; +var O4 = {f:0}; + +foo(O1, false); +foo(O2, false); +foo(O3, false); +%OptimizeFunctionOnNextCall(foo); +foo(O4, true); diff --git a/deps/v8/test/mjsunit/regress/regress-1748.js b/deps/v8/test/mjsunit/regress/regress-1748.js new file mode 100644 index 0000000000..e287e55496 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1748.js @@ -0,0 +1,35 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that /^/ only matches at beginning of string. +// Bug in x64 caused it to match when executing the RegExp on a part +// of a string that starts at a multiplum of 256. + +var str = Array(10000).join("X"); +str.replace(/^|X/g, function(m, i, s) { + if (i > 0) assertEquals("X", m, "at position 0x" + i.toString(16)); +});
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/regress/regress-1757.js b/deps/v8/test/mjsunit/regress/regress-1757.js new file mode 100644 index 0000000000..f7a5516cac --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1757.js @@ -0,0 +1,32 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --string-slices --expose-externalize-string + +var a = "abcdefghijklmnopqrstuvqxy"+"z"; +externalizeString(a, true); +assertEquals('b', a.substring(1).charAt(0));
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/regress/regress-1849.js b/deps/v8/test/mjsunit/regress/regress-1849.js new file mode 100644 index 0000000000..176f918b93 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1849.js @@ -0,0 +1,39 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See: http://code.google.com/p/v8/issues/detail?id=1878 + +// Flags: --allow-natives-syntax + +var count = 1e5; +var arr = new Array(count); +assertFalse(%HasFastDoubleElements(arr)); +for (var i = 0; i < count; i++) { + arr[i] = 0; +} +assertFalse(%HasFastDoubleElements(arr)); +assertTrue(%HasFastSmiOnlyElements(arr)); diff --git a/deps/v8/test/mjsunit/regress/regress-1878.js b/deps/v8/test/mjsunit/regress/regress-1878.js new file mode 100644 index 0000000000..1b3c63aeb1 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1878.js @@ -0,0 +1,34 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See: http://code.google.com/p/v8/issues/detail?id=1878 + +// Flags: --allow-natives-syntax --expose_natives_as=natives + +var a = Array(); +var ai = natives.InternalArray(); +assertFalse(%HaveSameMap(ai, a)); diff --git a/deps/v8/test/mjsunit/regress/regress-1898.js b/deps/v8/test/mjsunit/regress/regress-1898.js new file mode 100644 index 0000000000..5440446fbf --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1898.js @@ -0,0 +1,37 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function f(x) { + Math.log(Math.min(0.1, Math.abs(x))); +} + +f(0.1); +f(0.1); +%OptimizeFunctionOnNextCall(f); +f(0.1); diff --git a/deps/v8/test/mjsunit/regress/regress-1924.js b/deps/v8/test/mjsunit/regress/regress-1924.js new file mode 100644 index 0000000000..80395414e0 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1924.js @@ -0,0 +1,42 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// For http://code.google.com/p/v8/issues/detail?id=1924 + +a: break a; +a: b: break a; +a: b: break b; +assertThrows("a: break a a", SyntaxError) +assertThrows("a: break a 1", SyntaxError) +assertThrows("a: break a ''", SyntaxError) +assertThrows("a: break a var b", SyntaxError) +assertThrows("a: break a {}", SyntaxError) + +a: if (0) break a; +b: if (0) {break b;} else {} +c: if (0) break c; else {} +d: if (0) break d; else break d; diff --git a/deps/v8/test/mjsunit/regress/regress-221.js b/deps/v8/test/mjsunit/regress/regress-221.js deleted file mode 100644 index d3f2e35539..0000000000 --- a/deps/v8/test/mjsunit/regress/regress-221.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2009 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Test that direct eval calls handle the case where eval has been -// deleted correctly. - -// See http://code.google.com/p/v8/issues/detail?id=221 - -assertThrows('eval(delete eval)'); - diff --git a/deps/v8/test/mjsunit/regress/regress-397.js b/deps/v8/test/mjsunit/regress/regress-397.js index 111f4a6e53..0e4143d032 100644 --- a/deps/v8/test/mjsunit/regress/regress-397.js +++ b/deps/v8/test/mjsunit/regress/regress-397.js @@ -25,10 +25,19 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax // See http://code.google.com/p/v8/issues/detail?id=397 -assertEquals("Infinity", String(Math.pow(Infinity, 0.5))); -assertEquals(0, Math.pow(Infinity, -0.5)); -assertEquals("Infinity", String(Math.pow(-Infinity, 0.5))); -assertEquals(0, Math.pow(-Infinity, -0.5)); +function test() { + assertEquals("Infinity", String(Math.pow(Infinity, 0.5))); + assertEquals(0, Math.pow(Infinity, -0.5)); + + assertEquals("Infinity", String(Math.pow(-Infinity, 0.5))); + assertEquals(0, Math.pow(-Infinity, -0.5)); +} + +test(); +test(); +%OptimizeFunctionOnNextCall(test); +test(); diff --git a/deps/v8/test/mjsunit/regress/regress-877615.js b/deps/v8/test/mjsunit/regress/regress-877615.js index d35aba62d3..bec5a4d1b8 100644 --- a/deps/v8/test/mjsunit/regress/regress-877615.js +++ b/deps/v8/test/mjsunit/regress/regress-877615.js @@ -25,13 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Number.prototype.toLocaleString = function() { return 'invalid'}; -assertEquals([1].toLocaleString(), 'invalid'); // invalid +Number.prototype.toLocaleString = function() { return 'invalid'; }; +assertEquals('invalid', [1].toLocaleString()); // invalid Number.prototype.toLocaleString = 'invalid'; -assertEquals([1].toLocaleString(), '1'); // 1 +assertThrows(function() { [1].toLocaleString(); }); // Not callable. +delete Number.prototype.toLocaleString; Number.prototype.toString = function() { return 'invalid' }; -assertEquals([1].toLocaleString(), '1'); // 1 -assertEquals([1].toString(), '1'); // 1 - +assertEquals([1].toLocaleString(), 'invalid'); // Uses ToObject on elements. +assertEquals([1].toString(), '1'); // Uses ToString directly on elements. diff --git a/deps/v8/test/mjsunit/regress/regress-91517.js b/deps/v8/test/mjsunit/regress/regress-91517.js deleted file mode 100644 index 68a768c46c..0000000000 --- a/deps/v8/test/mjsunit/regress/regress-91517.js +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2011 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Getting property names of an object with a prototype chain that -// triggers dictionary elements in GetLocalPropertyNames() shouldn't -// crash the runtime - -// Flags: --allow-natives-syntax - -function Object1() { - this.foo = 1; -} - -function Object2() { - this.fuz = 2; - this.objects = new Object(); - this.fuz1 = 2; - this.fuz2 = 2; - this.fuz3 = 2; - this.fuz4 = 2; - this.fuz5 = 2; - this.fuz6 = 2; - this.fuz7 = 2; - this.fuz8 = 2; - this.fuz9 = 2; - this.fuz10 = 2; - this.fuz11 = 2; - this.fuz12 = 2; - this.fuz13 = 2; - this.fuz14 = 2; - this.fuz15 = 2; - this.fuz16 = 2; - this.fuz17 = 2; - // Force dictionary-based properties - for (x=1;x<1000;x++) { - this["sdf" + x] = 2; - } -} - -function Object3() { - this.boo = 3; -} - -function Object4() { - this.baz = 4; -} - -obj1 = new Object1(); -obj2 = new Object2(); -obj3 = new Object3(); -obj4 = new Object4(); - -%SetHiddenPrototype(obj4, obj3); -%SetHiddenPrototype(obj3, obj2); -%SetHiddenPrototype(obj2, obj1); - -function contains(a, obj) { - for(var i = 0; i < a.length; i++) { - if(a[i] === obj){ - return true; - } - } - return false; -} -names = %GetLocalPropertyNames(obj4); -assertEquals(1021, names.length); -assertTrue(contains(names, "baz")); -assertTrue(contains(names, "boo")); -assertTrue(contains(names, "foo")); -assertTrue(contains(names, "fuz")); -assertTrue(contains(names, "fuz1")); -assertTrue(contains(names, "fuz2")); -assertTrue(contains(names, "fuz3")); -assertTrue(contains(names, "fuz4")); -assertTrue(contains(names, "fuz5")); -assertTrue(contains(names, "fuz6")); -assertTrue(contains(names, "fuz7")); -assertTrue(contains(names, "fuz8")); -assertTrue(contains(names, "fuz9")); -assertTrue(contains(names, "fuz10")); -assertTrue(contains(names, "fuz11")); -assertTrue(contains(names, "fuz12")); -assertTrue(contains(names, "fuz13")); -assertTrue(contains(names, "fuz14")); -assertTrue(contains(names, "fuz15")); -assertTrue(contains(names, "fuz16")); -assertTrue(contains(names, "fuz17")); -assertFalse(names[1020] == undefined); diff --git a/deps/v8/test/mjsunit/regress/regress-94873.js b/deps/v8/test/mjsunit/regress/regress-94873.js new file mode 100644 index 0000000000..41ca9921c6 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-94873.js @@ -0,0 +1,78 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug; + +function sendCommand(state, cmd) { + // Get the debug command processor in paused state. + var dcp = state.debugCommandProcessor(false); + var request = JSON.stringify(cmd); + var response = dcp.processDebugJSONRequest(request); + return JSON.parse(response); +} + +function listener(event, exec_state, event_data, data) { + try { + if (event == Debug.DebugEvent.Break) { + var line = event_data.sourceLineText(); + print('break: ' + line); + + var frame = sendCommand(exec_state, { + seq: 0, + type: "request", + command: "frame" + }); + + sendCommand(exec_state, { + seq: 0, + type: "request", + command: "evaluate", + arguments: { + expression: "obj.x.toString()", + additional_context: [{ + name: "obj", + handle: frame.body.receiver.ref + }] + } + }); + } + } catch (e) { + print(e); + } +} + +Debug.setListener(listener); + +function a(x, y) { + this.x = x; + this.y = y; +} + +Debug.setBreakPoint(a, 0, 0); +new a(1, 2);
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/regress/regress-95113.js b/deps/v8/test/mjsunit/regress/regress-95113.js index f01b27004c..468bff84c2 100644 --- a/deps/v8/test/mjsunit/regress/regress-95113.js +++ b/deps/v8/test/mjsunit/regress/regress-95113.js @@ -32,7 +32,7 @@ function get_double_array() { var i = 0; while (!%HasFastDoubleElements(a)) { a[i] = i; - i++; + i += 0.5; } assertTrue(%HasFastDoubleElements(a)); a.length = 1; diff --git a/deps/v8/test/mjsunit/regress/regress-98773.js b/deps/v8/test/mjsunit/regress/regress-98773.js new file mode 100644 index 0000000000..eb24eb5d1e --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-98773.js @@ -0,0 +1,39 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Calling Array.sort on an external array is not supposed to crash. + +var array = new Int16Array(23); +array[7] = 7; array[9] = 9; +assertEquals(23, array.length); +assertEquals(7, array[7]); +assertEquals(9, array[9]); + +Array.prototype.sort.call(array); +assertEquals(23, array.length); +assertEquals(7, array[21]); +assertEquals(9, array[22]); diff --git a/deps/v8/test/mjsunit/regress/regress-99167.js b/deps/v8/test/mjsunit/regress/regress-99167.js new file mode 100644 index 0000000000..5053ae5d24 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-99167.js @@ -0,0 +1,33 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-gc --max-new-space-size=1024 + +eval("function Node() { this.a = 1; this.a = 3; }"); +new Node; +for (var i = 0; i < 4; ++i) gc(); +for (var i = 0; i < 100000; ++i) new Node; diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-100859.js b/deps/v8/test/mjsunit/regress/regress-crbug-100859.js new file mode 100644 index 0000000000..6824426271 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-100859.js @@ -0,0 +1,39 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This used to trigger a crash because of an unhandled stack overflow. +function setx() { + setx(typeof new Uint16Array('x') === 'object'); +} +var exception = false; +try { + setx(); +} catch (ex) { + assertTrue(ex instanceof RangeError); + exception = true; +} +assertTrue(exception); diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-107996.js b/deps/v8/test/mjsunit/regress/regress-crbug-107996.js new file mode 100644 index 0000000000..dfe07e59de --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-107996.js @@ -0,0 +1,64 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug + +Debug = debug.Debug; + +Debug.setListener(listener); + +var fourteen; +var four_in_debugger = []; + +function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + for (var i = 0; i < exec_state.frameCount(); i++) { + var frame = exec_state.frame(i); + four_in_debugger[i] = frame.evaluate("four", false).value(); + } + } +} + +function f1() { + var three = 3; + var four = 4; + (function f2() { + var seven = 7; + (function f3() { + debugger; + fourteen = three + four + seven; + })(); + })(); +} + +f1(); +assertEquals(14, fourteen); +assertEquals(4, four_in_debugger[0]); +assertEquals(4, four_in_debugger[1]); +assertEquals(4, four_in_debugger[2]); + +Debug.setListener(null); diff --git a/deps/v8/test/mjsunit/regress/regress-debug-code-recompilation.js b/deps/v8/test/mjsunit/regress/regress-debug-code-recompilation.js new file mode 100644 index 0000000000..1a608b14a3 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-debug-code-recompilation.js @@ -0,0 +1,47 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --hydrogen-filter=Debug.setBreakPoint --expose-debug-as debug + +Debug = debug.Debug + +function f() {a=1;b=2}; +function g() { + a=1; + b=2; +} + +bp = Debug.setBreakPoint(f, 0, 0); +Debug.clearBreakPoint(bp); +%OptimizeFunctionOnNextCall(Debug.setBreakPoint); +bp = Debug.setBreakPoint(f, 0, 0); +Debug.clearBreakPoint(bp); +bp = Debug.setBreakPoint(f, 0, 0); +Debug.clearBreakPoint(bp); +%OptimizeFunctionOnNextCall(Debug.setBreakPoint); +bp = Debug.setBreakPoint(f, 0, 0); +Debug.clearBreakPoint(bp); diff --git a/deps/v8/test/mjsunit/regress/regress-deopt-gc.js b/deps/v8/test/mjsunit/regress/regress-deopt-gc.js index 7b7c29a31e..a74e2c5ea4 100644 --- a/deps/v8/test/mjsunit/regress/regress-deopt-gc.js +++ b/deps/v8/test/mjsunit/regress/regress-deopt-gc.js @@ -42,7 +42,7 @@ function deopt() { // Make sure we don't inline this function try { var a = 42; } catch(o) {}; %DeoptimizeFunction(opt_me); - gc(true); + gc(); } diff --git a/deps/v8/test/mjsunit/regress/regress-smi-only-concat.js b/deps/v8/test/mjsunit/regress/regress-smi-only-concat.js new file mode 100644 index 0000000000..a9a6d89b06 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-smi-only-concat.js @@ -0,0 +1,37 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// This tests that concatenating a fast smi-only array and a fast object array +// results in a fast object array. + +var fast_array = ['a', 'b']; +var array = fast_array.concat(fast_array); + +assertTrue(%HasFastElements(fast_array)); +assertTrue(%HasFastElements(array));
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/regress/short-circuit.js b/deps/v8/test/mjsunit/regress/short-circuit.js new file mode 100644 index 0000000000..25363d6b31 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/short-circuit.js @@ -0,0 +1,32 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var arr = []; + +for (var i = 0; i < 28000; i++) { + arr.push(new RegExp("prefix" + i.toString() + i.toString() + i.toString())); +} diff --git a/deps/v8/test/mjsunit/stack-traces-2.js b/deps/v8/test/mjsunit/stack-traces-2.js new file mode 100644 index 0000000000..165c4dfcec --- /dev/null +++ b/deps/v8/test/mjsunit/stack-traces-2.js @@ -0,0 +1,87 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --builtins-in-stack-traces + + +// Poisonous object that throws a reference error if attempted converted to +// a primitive values. +var thrower = { valueOf: function() { FAIL; }, + toString: function() { FAIL; } }; + +// Tests that a native constructor function is included in the +// stack trace. +function testTraceNativeConstructor(nativeFunc) { + var nativeFuncName = nativeFunc.name; + try { + new nativeFunc(thrower); + assertUnreachable(nativeFuncName); + } catch (e) { + assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName); + } +} + +// Tests that a native conversion function is included in the +// stack trace. +function testTraceNativeConversion(nativeFunc) { + var nativeFuncName = nativeFunc.name; + try { + nativeFunc(thrower); + assertUnreachable(nativeFuncName); + } catch (e) { + assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName); + } +} + + +function testNotOmittedBuiltin(throwing, included) { + try { + throwing(); + assertUnreachable(included); + } catch (e) { + assertTrue(e.stack.indexOf(included) >= 0, included); + } +} + + +testTraceNativeConversion(String); // Does ToString on argument. +testTraceNativeConversion(Number); // Does ToNumber on argument. +testTraceNativeConversion(RegExp); // Does ToString on argument. + +testTraceNativeConstructor(String); // Does ToString on argument. +testTraceNativeConstructor(Number); // Does ToNumber on argument. +testTraceNativeConstructor(RegExp); // Does ToString on argument. +testTraceNativeConstructor(Date); // Does ToNumber on argument. + +// QuickSort has builtins object as receiver, and is non-native +// builtin. Should not be omitted with the --builtins-in-stack-traces flag. +testNotOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) { + (b < a) - (a < b); }); + }, "QuickSort"); + +// Not omitted even though ADD from runtime.js is a non-native builtin. +testNotOmittedBuiltin(function(){ thrower + 2; }, "ADD");
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/stack-traces.js b/deps/v8/test/mjsunit/stack-traces.js index 47a5cc594a..536e71bbb5 100644 --- a/deps/v8/test/mjsunit/stack-traces.js +++ b/deps/v8/test/mjsunit/stack-traces.js @@ -194,6 +194,46 @@ function testErrorsDuringFormatting() { } +// Poisonous object that throws a reference error if attempted converted to +// a primitive values. +var thrower = { valueOf: function() { FAIL; }, + toString: function() { FAIL; } }; + +// Tests that a native constructor function is included in the +// stack trace. +function testTraceNativeConstructor(nativeFunc) { + var nativeFuncName = nativeFunc.name; + try { + new nativeFunc(thrower); + assertUnreachable(nativeFuncName); + } catch (e) { + assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName); + } +} + +// Tests that a native conversion function is included in the +// stack trace. +function testTraceNativeConversion(nativeFunc) { + var nativeFuncName = nativeFunc.name; + try { + nativeFunc(thrower); + assertUnreachable(nativeFuncName); + } catch (e) { + assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName); + } +} + + +function testOmittedBuiltin(throwing, omitted) { + try { + throwing(); + assertUnreachable(omitted); + } catch (e) { + assertTrue(e.stack.indexOf(omitted) < 0, omitted); + } +} + + testTrace("testArrayNative", testArrayNative, ["Array.map (native)"]); testTrace("testNested", testNested, ["at one", "at two", "at three"]); testTrace("testMethodNameInference", testMethodNameInference, ["at Foo.bar"]); @@ -217,3 +257,21 @@ testTrace("testStrippedCustomError", testStrippedCustomError, ["hep-hey"], testCallerCensorship(); testUnintendedCallerCensorship(); testErrorsDuringFormatting(); + +testTraceNativeConversion(String); // Does ToString on argument. +testTraceNativeConversion(Number); // Does ToNumber on argument. +testTraceNativeConversion(RegExp); // Does ToString on argument. + +testTraceNativeConstructor(String); // Does ToString on argument. +testTraceNativeConstructor(Number); // Does ToNumber on argument. +testTraceNativeConstructor(RegExp); // Does ToString on argument. +testTraceNativeConstructor(Date); // Does ToNumber on argument. + +// Omitted because QuickSort has builtins object as receiver, and is non-native +// builtin. +testOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) { + (b < a) - (a < b); }); + }, "QuickSort"); + +// Omitted because ADD from runtime.js is non-native builtin. +testOmittedBuiltin(function(){ thrower + 2; }, "ADD");
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/strict-mode-implicit-receiver.js b/deps/v8/test/mjsunit/strict-mode-implicit-receiver.js index 338f6d10f5..8284eddc2d 100644 --- a/deps/v8/test/mjsunit/strict-mode-implicit-receiver.js +++ b/deps/v8/test/mjsunit/strict-mode-implicit-receiver.js @@ -168,12 +168,7 @@ outer_eval_conversion3(return_this, 'object'); outer_eval_conversion3(strict_eval, 'undefined'); outer_eval_conversion3(non_strict_eval, 'object'); -// TODO(ager): I'm not sure this is in accordance with the spec. At -// the moment, any call to eval where eval is not bound in the global -// context is treated as an indirect call to eval which means that the -// global context is used and the global object is passed as the -// receiver. -outer_eval_conversion3(eval, 'object'); +outer_eval_conversion3(eval, 'undefined'); function test_constant_function() { var o = { f: function() { "use strict"; return this; } }; diff --git a/deps/v8/test/mjsunit/strict-mode.js b/deps/v8/test/mjsunit/strict-mode.js index 30234ba6fa..9c9bdfd52d 100644 --- a/deps/v8/test/mjsunit/strict-mode.js +++ b/deps/v8/test/mjsunit/strict-mode.js @@ -1051,14 +1051,20 @@ function CheckPillDescriptor(func, name) { } assertThrows(function() { strict.caller; }, TypeError); assertThrows(function() { strict.arguments; }, TypeError); + assertThrows(function() { strict.caller = 42; }, TypeError); + assertThrows(function() { strict.arguments = 42; }, TypeError); var another = new Function("'use strict'"); assertThrows(function() { another.caller; }, TypeError); assertThrows(function() { another.arguments; }, TypeError); + assertThrows(function() { another.caller = 42; }, TypeError); + assertThrows(function() { another.arguments = 42; }, TypeError); var third = (function() { "use strict"; return function() {}; })(); assertThrows(function() { third.caller; }, TypeError); assertThrows(function() { third.arguments; }, TypeError); + assertThrows(function() { third.caller = 42; }, TypeError); + assertThrows(function() { third.arguments = 42; }, TypeError); CheckPillDescriptor(strict, "caller"); CheckPillDescriptor(strict, "arguments"); diff --git a/deps/v8/test/mjsunit/string-external-cached.js b/deps/v8/test/mjsunit/string-external-cached.js new file mode 100644 index 0000000000..6e24285331 --- /dev/null +++ b/deps/v8/test/mjsunit/string-external-cached.js @@ -0,0 +1,121 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-externalize-string --expose-gc +// Test data pointer caching of external strings. + +function test() { + // Test string.charAt. + var charat_str = new Array(5); + charat_str[0] = "0123456789ABCDEF0123456789ABCDEF\ +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\ +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\ +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\ +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + charat_str[1] = "0123456789ABCDEF"; + for (var i = 0; i < 6; i++) charat_str[1] += charat_str[1]; + try { // String can only be externalized once + externalizeString(charat_str[0], false); + externalizeString(charat_str[1], true); + } catch (ex) { } + charat_str[2] = charat_str[0].slice(0, -1); + charat_str[3] = charat_str[1].slice(0, -1); + charat_str[4] = charat_str[0] + charat_str[0]; + + for (var i = 0; i < 5; i++) { + assertEquals('B', charat_str[i].charAt(6*16 + 11)); + assertEquals('C', charat_str[i].charAt(6*16 + 12)); + assertEquals('A', charat_str[i].charAt(3*16 + 10)); + assertEquals('B', charat_str[i].charAt(3*16 + 11)); + } + + charat_short = "012"; + try { // String can only be externalized once + externalizeString(charat_short, true); + } catch (ex) { } + assertEquals("1", charat_short.charAt(1)); + + // Test regexp and short substring. + var re = /(A|B)/; + var rere = /(T.{1,2}B)/; + var ascii = "ABCDEFGHIJKLMNOPQRST"; + var twobyte = "_ABCDEFGHIJKLMNOPQRST"; + try { + externalizeString(ascii, false); + externalizeString(twobyte, true); + } catch (ex) { } + assertTrue(isAsciiString(ascii)); + assertFalse(isAsciiString(twobyte)); + var ascii_slice = ascii.slice(1,-1); + var twobyte_slice = twobyte.slice(2,-1); + var ascii_cons = ascii + ascii; + var twobyte_cons = twobyte + twobyte; + for (var i = 0; i < 2; i++) { + assertEquals(["A", "A"], re.exec(ascii)); + assertEquals(["B", "B"], re.exec(ascii_slice)); + assertEquals(["TAB", "TAB"], rere.exec(ascii_cons)); + assertEquals(["A", "A"], re.exec(twobyte)); + assertEquals(["B", "B"], re.exec(twobyte_slice)); + assertEquals(["T_AB", "T_AB"], rere.exec(twobyte_cons)); + assertEquals("DEFG", ascii_slice.substr(2, 4)); + assertEquals("DEFG", twobyte_slice.substr(2, 4)); + assertEquals("DEFG", ascii_cons.substr(3, 4)); + assertEquals("DEFG", twobyte_cons.substr(4, 4)); + } + + // Test adding external strings + var short_ascii = "E="; + var long_ascii = "MCsquared"; + var short_twobyte = "E\u1234"; + var long_twobyte = "MCsquare\u1234"; + try { // String can only be externalized once + externalizeString(short_ascii, false); + externalizeString(long_ascii, false); + externalizeString(short_twobyte, true); + externalizeString(long_twobyte, true); + assertTrue(isAsciiString(short_asii) && isAsciiString(long_ascii)); + assertFalse(isAsciiString(short_twobyte) || isAsciiString(long_twobyte)); + } catch (ex) { } + assertEquals("E=MCsquared", short_ascii + long_ascii); + assertTrue(isAsciiString(short_ascii + long_ascii)); + assertEquals("MCsquaredE=", long_ascii + short_ascii); + assertEquals("E\u1234MCsquare\u1234", short_twobyte + long_twobyte); + assertFalse(isAsciiString(short_twobyte + long_twobyte)); + assertEquals("E=MCsquared", "E=" + long_ascii); + assertEquals("E\u1234MCsquared", short_twobyte + "MCsquared"); + assertEquals("E\u1234MCsquared", short_twobyte + long_ascii); + assertFalse(isAsciiString(short_twobyte + long_ascii)); +} + +// Run the test many times to ensure IC-s don't break things. +for (var i = 0; i < 10; i++) { + test(); +} + +// Clean up string to make Valgrind happy. +gc(); +gc(); diff --git a/deps/v8/test/mjsunit/string-externalize.js b/deps/v8/test/mjsunit/string-externalize.js index da897869c5..d52a7e2baf 100644 --- a/deps/v8/test/mjsunit/string-externalize.js +++ b/deps/v8/test/mjsunit/string-externalize.js @@ -44,7 +44,7 @@ function test() { assertFalse(isAsciiString(twoByteExternalWithAsciiData)); var realTwoByteExternalString = - "\u1234\u1234" + (function() { return "\u1234"; })(); + "\u1234\u1234\u1234\u1234" + (function() { return "\u1234"; })(); externalizeString(realTwoByteExternalString); assertFalse(isAsciiString(realTwoByteExternalString)); diff --git a/deps/v8/test/mjsunit/string-replace-one-char.js b/deps/v8/test/mjsunit/string-replace-one-char.js new file mode 100644 index 0000000000..cb4167ba7f --- /dev/null +++ b/deps/v8/test/mjsunit/string-replace-one-char.js @@ -0,0 +1,92 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Make sure the strings are long enough to trigger the one-char string replace. +var prefix1024 = "0123456789ABCDEF"; +for (var i = 0; i < 6; i++) prefix1024 += prefix1024; + +function test_replace(result, expected, search, replace) { + assertEquals(expected, result.replace(search, replace)); +} + +// '$' in the replace string. +test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz", + prefix1024 + "abcdefghijk#l#mnopqrstuvwxyz", + "l", "#$&#"); + +test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz\u1234", + prefix1024 + "abcdefghijk\u2012l\u2012mnopqrstuvwxyz\u1234", + "l", "\u2012$&\u2012"); + +test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz", + prefix1024 + "abcdefghijk$mnopqrstuvwxyz", + "l", "$$"); + +test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz\u1234", + prefix1024 + "abcdefghijk$mnopqrstuvwxyz\u1234", + "l", "$$"); + +// Zero length replace string. +test_replace(prefix1024 + "abcdefghijklmnopqrstuvwxyz", + prefix1024 + "abcdefghijklmnopqstuvwxyz", + "r", ""); + +test_replace(prefix1024 + "abcdefghijklmnopq\u1234stuvwxyz", + prefix1024 + "abcdefghijklmnopqstuvwxyz", + "\u1234", ""); + +// Search char not found. +var not_found_1 = prefix1024 + "abcdefghijklmnopqrstuvwxyz"; +test_replace(not_found_1, not_found_1, "+", "-"); + +var not_found_2 = prefix1024 + "abcdefghijklm\u1234nopqrstuvwxyz"; +test_replace(not_found_2, not_found_2, "+", "---"); + +var not_found_3 = prefix1024 + "abcdefghijklmnopqrstuvwxyz"; +test_replace(not_found_3, not_found_3, "\u1234", "ZZZ"); + +// Deep cons tree. +var nested_1 = ""; +for (var i = 0; i < 100000; i++) nested_1 += "y"; +var nested_1_result = prefix1024 + nested_1 + "aa"; +nested_1 = prefix1024 + nested_1 + "z"; +test_replace(nested_1, nested_1_result, "z", "aa"); + +var nested_2 = "\u2244"; +for (var i = 0; i < 100000; i++) nested_2 += "y"; +var nested_2_result = prefix1024 + nested_2 + "aa"; +nested_2 = prefix1024 + nested_2 + "\u2012"; +test_replace(nested_2, nested_2_result, "\u2012", "aa"); + +// Sliced string as input. A cons string is always flattened before sliced. +var slice_1 = ("ab" + prefix1024 + "cdefghijklmnopqrstuvwxyz").slice(1, -1); +var slice_1_result = "b" + prefix1024 + "cdefghijklmnopqrstuvwxQ"; +test_replace(slice_1, slice_1_result, "y", "Q"); + +var slice_2 = (prefix1024 + "abcdefghijklmno\u1234\u1234p").slice(1, -1); +var slice_2_result = prefix1024.substr(1) + "abcdefghijklmnoQ\u1234"; +test_replace(slice_2, slice_2_result, "\u1234", "Q"); diff --git a/deps/v8/test/mjsunit/string-slices-regexp.js b/deps/v8/test/mjsunit/string-slices-regexp.js index a8cadaedd5..98b8ef9d49 100644 --- a/deps/v8/test/mjsunit/string-slices-regexp.js +++ b/deps/v8/test/mjsunit/string-slices-regexp.js @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -24,11 +24,6 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Flags: --string-slices - -//assertEquals('345"12345 6"1234567"123', -// '12345""12345 6""1234567""1234'.slice(2,-1).replace(/""/g, '"')); var foo = "lsdfj sldkfj sdklfj læsdfjl sdkfjlsdk fjsdl fjsdljskdj flsj flsdkj flskd regexp: /foobar/\nldkfj sdlkfj sdkl"; for(var i = 0; i < 1000; i++) { diff --git a/deps/v8/test/mjsunit/string-slices.js b/deps/v8/test/mjsunit/string-slices.js index 8cc1f81e77..5b1dc360ab 100755 --- a/deps/v8/test/mjsunit/string-slices.js +++ b/deps/v8/test/mjsunit/string-slices.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --string-slices --expose-externalize-string +// Flags: --expose-externalize-string --allow-natives-syntax var s = 'abcdefghijklmn'; assertEquals(s, s.substr()); @@ -100,14 +100,7 @@ for (var i = 5; i < 25; i++) { // Keep creating strings to to force allocation failure on substring creation. var x = "0123456789ABCDEF"; -x += x; // 2^5 -x += x; -x += x; -x += x; -x += x; -x += x; // 2^10 -x += x; -x += x; +for (var i = 0; i < 8; i++) x += x; var xl = x.length; var cache = []; for (var i = 0; i < 1000; i++) { @@ -119,14 +112,7 @@ for (var i = 0; i < 1000; i++) { // Same with two-byte strings var x = "\u2028123456789ABCDEF"; -x += x; // 2^5 -x += x; -x += x; -x += x; -x += x; -x += x; // 2^10 -x += x; -x += x; +for (var i = 0; i < 8; i++) x += x; var xl = x.length; var cache = []; for (var i = 0; i < 1000; i++) { @@ -174,6 +160,23 @@ for ( var i = 0; i < 1000; i++) { f(flat, cons, slice, i); } +// Short substrings. +flat = "abcdefghijkl12345"; +cons = flat + flat.toUpperCase(); +/x/.exec(cons); // Flatten cons +slice = "abcdefghijklmn12345".slice(1, -1); +assertEquals("cdefg", flat.substr(2, 5)); +assertEquals("cdefg", cons.substr(2, 5)); +assertEquals("cdefg", slice.substr(1, 5)); + +flat = "abc\u1234defghijkl12345"; +cons = flat + flat.toUpperCase(); +/x/.exec(cons); // Flatten cons +slice = "abc\u1234defghijklmn12345".slice(1, -1); +assertEquals("c\u1234def", flat.substr(2, 5)); +assertEquals("c\u1234def", cons.substr(2, 5)); +assertEquals("c\u1234def", slice.substr(1, 5)); + // Concatenate substrings. var ascii = 'abcdefghijklmnop'; var utf = '\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB'; @@ -189,11 +192,34 @@ assertEquals("\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9abcdefghijklmnop", assertEquals("\u03B2\u03B3\u03B4\u03B5\u03B4\u03B5\u03B6\u03B7", utf.substring(5,1) + utf.substring(3,7)); -/* // Externalizing strings. -var a = "123456789qwertyuiopasdfghjklzxcvbnm"; -var b = a.slice(1,-1); +var a = "123456789" + "qwertyuiopasdfghjklzxcvbnm"; +var b = "23456789qwertyuiopasdfghjklzxcvbn" assertEquals(a.slice(1,-1), b); -externalizeString(a); + +assertTrue(isAsciiString(a)); +externalizeString(a, true); +assertFalse(isAsciiString(a)); + assertEquals(a.slice(1,-1), b); -*/ +assertTrue(/3456789qwe/.test(a)); +assertEquals(5, a.indexOf("678")); +assertEquals("12345", a.split("6")[0]); + +// Create a slice with an external string as parent string. +var c = a.slice(1,-1); + +function test_crankshaft() { + for (var i = 0; i < 20; i++) { + assertEquals(b.charAt(i), a.charAt(i + 1)); + assertEquals(b.charAt(i), c.charAt(i)); + assertEquals(b.charAt(4), c.charAt(4)); + assertTrue(/3456789qwe/.test(c)); + assertEquals(4, c.indexOf("678")); + assertEquals("2345", c.split("6")[0]); + } +} + +test_crankshaft(); +%OptimizeFunctionOnNextCall(test_crankshaft); +test_crankshaft();
\ No newline at end of file diff --git a/deps/v8/test/mjsunit/switch.js b/deps/v8/test/mjsunit/switch.js index 180f994a8e..6a61fe5940 100644 --- a/deps/v8/test/mjsunit/switch.js +++ b/deps/v8/test/mjsunit/switch.js @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax + function f0() { switch (0) { // switch deliberately left empty @@ -126,6 +128,42 @@ assertEquals(3, f4(1), "fallthrough-switch.1"); assertEquals(3, f4(2), "fallthrough-switch.2"); assertEquals(5, f4(3), "fallthrough-switch.3"); +function f4_string(tag, x) { + switch(tag) { + case 'zero': + x++; + case 'two': + x++; + } + return x; +} + +// Symbols +assertEquals(2, f4_string('zero', 0), "fallthrough-string-switch.0"); +assertEquals(1, f4_string('one', 1), "fallthrough-string-switch.1"); +assertEquals(3, f4_string('two', 2), "fallthrough-string-switch.2"); + +// Strings +assertEquals(2, f4_string('_zero'.slice(1), 0), "fallthrough-string-switch.3"); +assertEquals(1, f4_string('_one'.slice(1), 1), "fallthrough-string-switch.4"); +assertEquals(3, f4_string('_two'.slice(1), 2), "fallthrough-string-switch.5"); + +// Oddball +assertEquals(3, f4_string(null, 3), "fallthrough-string-switch.6"); + +// Test for regression +function regress_string(value) { + var json = 1; + switch (typeof value) { + case 'object': + break; + + default: + + } + return json; +}; +assertEquals(1, regress_string('object'), 'regression-string'); function f5(x) { switch(x) { @@ -287,3 +325,138 @@ var verylong_size = 1000; var verylong = makeVeryLong(verylong_size); assertEquals(verylong_size * 2 + 1, verylong()); + +// +// Test suite below aims to cover all possible combinations of following: +// +// clauses | tags | type feedback | optimization +// ========================================================= +// strings | symbol | all | on +// smis | string | target | off +// mixed | oddball | non-target | +// | smis | none | +// | heapnum | | +// ========================================================= + +// Function-with-switch generator +var test_id = 0, + clause_values = { + string: ['abc', 'def', 'ghi', 'jkl'], + smi: [1, 2, 3, 4], + mixed: ['abc', 1, 'def', 2, 'ghi', 3, 'jkl', 4] + }; + +function switch_gen(clause_type, feedback, optimize) { + var values = clause_values[clause_type]; + + function opt(fn) { + if (feedback === 'all') { + values.forEach(fn); + } else if (Array.isArray(feedback)) { + // Non-target + values.filter(function(v) { + return feedback.indexOf(v) === -1; + }).forEach(fn); + } else if (feedback !== undefined) { + // Target + fn(feedback); + } else { + // None + } + + if (optimize) %OptimizeFunctionOnNextCall(fn); + + return fn; + }; + + return opt(new Function( + 'tag', + '"' + (test_id++) + '";' + + 'switch(tag) {' + + values.map(function(value) { + return 'case ' + JSON.stringify(value) + ': return' + + JSON.stringify('ok-' + value); + }).join(';') + + '}' + )); +}; + +function test_switch(clause_type, test_type, feedback, optimize) { + var pairs = [], + fn = switch_gen(clause_type, feedback, optimize); + + if (Array.isArray(test_type)) { + pairs = test_type.map(function(v) { + return { + value: v, + expected: 'ok-' + v + }; + }); + } else if (test_type === 'symbols') { + pairs = clause_values.string.map(function(v) { + return { + value: v, + expected: clause_type !== 'smi' ? 'ok-' + v : undefined + }; + }); + } else if (test_type === 'strings') { + pairs = clause_values.string.map(function(v) { + return { + value: ('%%' + v).slice(2), + expected: clause_type !== 'smi' ? 'ok-' + v : undefined + }; + }); + } else if (test_type === 'oddball') { + pairs = [ + { value: null, expected: undefined }, + { value: NaN, expected: undefined }, + { value: undefined, expected: undefined } + ]; + } else if (test_type === 'smi') { + pairs = clause_values.smi.map(function(v) { + return { + value: v, + expected: clause_type !== 'string' ? 'ok-' + v : undefined + }; + }); + } else if (test_type === 'heapnum') { + pairs = clause_values.smi.map(function(v) { + return { + value: ((v * 17)/16) - ((v*17)%16/16), + expected: clause_type !== 'string' ? 'ok-' + v : undefined + }; + }); + } + + pairs.forEach(function(pair) { + assertEquals(fn(pair.value), pair.expected); + }); +}; + +// test_switch(clause_type, test_type, feedback, optimize); + +function test_switches(opt) { + var test_types = ['symbols', 'strings', 'oddball', 'smi', 'heapnum']; + + function test(clause_type) { + var values = clause_values[clause_type]; + + test_types.forEach(function(test_type) { + test_switch(clause_type, test_type, 'all', opt); + test_switch(clause_type, test_type, 'none', opt); + + // Targeting specific clause feedback + values.forEach(function(value) { + test_switch(clause_type, test_type, [value], value, opt); + test_switch(clause_type, test_type, value, value, opt); + }); + }); + }; + + test('string'); + test('smi'); + test('mixed'); +}; + +test_switches(false); +test_switches(true); diff --git a/deps/v8/test/mjsunit/to_number_order.js b/deps/v8/test/mjsunit/to_number_order.js index d17e600050..50e4bc762e 100644 --- a/deps/v8/test/mjsunit/to_number_order.js +++ b/deps/v8/test/mjsunit/to_number_order.js @@ -161,7 +161,7 @@ assertEquals("fiskfisk", x, "Compare objects b >= b valueOf order"); x = ""; assertFalse(a > b, "Compare objects a > b"); -assertEquals("fiskhest", x, "Compare objects a > b valueOf order"); +assertEquals("hestfisk", x, "Compare objects a > b valueOf order"); x = ""; assertFalse(a > void(0), "Compare objects a > undefined"); @@ -195,7 +195,7 @@ function identical_object_comparison() { x = ""; assertFalse(a > b, "Compare objects a > b"); - assertEquals("fiskhest", x, "Compare objects a > b valueOf order"); + assertEquals("hestfisk", x, "Compare objects a > b valueOf order"); x = ""; assertFalse(a > void(0), "Compare objects a > undefined"); diff --git a/deps/v8/test/mjsunit/unbox-double-arrays.js b/deps/v8/test/mjsunit/unbox-double-arrays.js index feecaec8f0..fd7db28a0d 100644 --- a/deps/v8/test/mjsunit/unbox-double-arrays.js +++ b/deps/v8/test/mjsunit/unbox-double-arrays.js @@ -77,8 +77,6 @@ function testOneArrayType(allocator) { assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } @@ -89,8 +87,6 @@ function testOneArrayType(allocator) { assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } @@ -101,8 +97,6 @@ function testOneArrayType(allocator) { assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } @@ -113,32 +107,40 @@ function testOneArrayType(allocator) { assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } function test_various_loads5(a, value_5, value_6, value_7) { assertTrue(%HasFastDoubleElements(a)); + if (value_5 != undefined) { + assertEquals(value_5, a[5]); + }; + if (value_6 != undefined) { + assertEquals(value_6, a[6]); + assertEquals(value_6, a[computed_6()]); // Test non-constant key + } + assertEquals(value_7, a[7]); + assertEquals(large_array_size, a.length); + assertTrue(%HasFastDoubleElements(a)); + } + + function test_various_loads6(a, value_5, value_6, value_7) { + assertTrue(%HasFastDoubleElements(a)); assertEquals(value_5, a[5]); assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } - function test_various_loads6(a, value_5, value_6, value_7) { + function test_various_loads7(a, value_5, value_6, value_7) { assertTrue(%HasFastDoubleElements(a)); assertEquals(value_5, a[5]); assertEquals(value_6, a[6]); assertEquals(value_6, a[computed_6()]); // Test non-constant key assertEquals(value_7, a[7]); - assertEquals(undefined, a[large_array_size-1]); - assertEquals(undefined, a[-1]); assertEquals(large_array_size, a.length); assertTrue(%HasFastDoubleElements(a)); } @@ -248,6 +250,8 @@ function testOneArrayType(allocator) { expected_array_value(7)); // Make sure Crankshaft code handles the hole correctly (bailout) + var large_array = new allocator(large_array_size); + force_to_fast_double_array(large_array); test_various_stores(large_array, expected_array_value(5), expected_array_value(6), @@ -273,7 +277,12 @@ function testOneArrayType(allocator) { undefined, expected_array_value(7)); + %DeoptimizeFunction(test_various_loads6); + gc(); + // Test stores for non-NaN. + var large_array = new allocator(large_array_size); + force_to_fast_double_array(large_array); %OptimizeFunctionOnNextCall(test_various_stores); test_various_stores(large_array, expected_array_value(5), @@ -285,7 +294,19 @@ function testOneArrayType(allocator) { expected_array_value(6), expected_array_value(7)); - test_various_loads6(large_array, + test_various_loads7(large_array, + expected_array_value(5), + expected_array_value(6), + expected_array_value(7)); + + test_various_loads7(large_array, + expected_array_value(5), + expected_array_value(6), + expected_array_value(7)); + + %OptimizeFunctionOnNextCall(test_various_loads7); + + test_various_loads7(large_array, expected_array_value(5), expected_array_value(6), expected_array_value(7)); @@ -301,7 +322,7 @@ function testOneArrayType(allocator) { -NaN, expected_array_value(7)); - test_various_loads6(large_array, + test_various_loads7(large_array, NaN, -NaN, expected_array_value(7)); @@ -317,7 +338,7 @@ function testOneArrayType(allocator) { -Infinity, expected_array_value(7)); - test_various_loads6(large_array, + test_various_loads7(large_array, Infinity, -Infinity, expected_array_value(7)); @@ -434,7 +455,6 @@ large_array3[3] = Infinity; large_array3[4] = -Infinity; function call_apply() { - assertTrue(%HasFastDoubleElements(large_array3)); called_by_apply.apply({}, large_array3); } @@ -449,7 +469,6 @@ call_apply(); function test_for_in() { // Due to previous tests, keys 0..25 and 95 should be present. next_expected = 0; - assertTrue(%HasFastDoubleElements(large_array3)); for (x in large_array3) { assertTrue(next_expected++ == x); if (next_expected == 25) { diff --git a/deps/v8/test/mjsunit/undeletable-functions.js b/deps/v8/test/mjsunit/undeletable-functions.js index 04fd06068d..635ea6fa9a 100644 --- a/deps/v8/test/mjsunit/undeletable-functions.js +++ b/deps/v8/test/mjsunit/undeletable-functions.js @@ -25,11 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Test that we match JSC in making some functions undeletable. -// See http://code.google.com/p/chromium/issues/detail?id=1717 -// The functions on these prototypes are not just undeletable. It is -// possible to override them with new definitions, then get the old -// version back by deleting the new definition. +// Test that we match ECMAScript in making most builtin functions +// deletable and only specific ones undeletable or read-only. var array; @@ -37,7 +34,7 @@ array = [ "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight"]; -CheckJSCSemantics(Array.prototype, array, "Array prototype"); +CheckEcmaSemantics(Array.prototype, array, "Array prototype"); var old_Array_prototype = Array.prototype; var new_Array_prototype = {}; @@ -57,12 +54,12 @@ array = [ "setUTCMinutes", "setHours", "setUTCHours", "setDate", "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear", "toGMTString", "toUTCString", "getYear", "setYear", "toISOString", "toJSON"]; -CheckJSCSemantics(Date.prototype, array, "Date prototype"); +CheckEcmaSemantics(Date.prototype, array, "Date prototype"); array = [ "random", "abs", "acos", "asin", "atan", "ceil", "cos", "exp", "floor", "log", "round", "sin", "sqrt", "tan", "atan2", "pow", "max", "min"]; -CheckJSCSemantics(Math, array, "Math1"); +CheckEcmaSemantics(Math, array, "Math1"); CheckEcmaSemantics(Date, ["UTC", "parse", "now"], "Date"); @@ -76,6 +73,8 @@ array = [ "execScript"]; CheckEcmaSemantics(this, array, "Global"); CheckReadOnlyAttr(this, "Infinity"); +CheckReadOnlyAttr(this, "NaN"); +CheckReadOnlyAttr(this, "undefined"); array = ["exec", "test", "toString", "compile"]; CheckEcmaSemantics(RegExp.prototype, array, "RegExp prototype"); @@ -112,7 +111,7 @@ array = [ "toUpperCase", "toLocaleUpperCase", "link", "anchor", "fontcolor", "fontsize", "big", "blink", "bold", "fixed", "italics", "small", "strike", "sub", "sup", "toJSON", "toString", "valueOf"]; -CheckJSCSemantics(String.prototype, array, "String prototype"); +CheckEcmaSemantics(String.prototype, array, "String prototype"); CheckEcmaSemantics(String, ["fromCharCode"], "String"); @@ -124,14 +123,6 @@ function CheckEcmaSemantics(type, props, name) { } -function CheckJSCSemantics(type, props, name) { - print(name); - for (var i = 0; i < props.length; i++) { - CheckNotDeletable(type, props[i]); - } -} - - function CheckDontDelete(type, props, name) { print(name); for (var i = 0; i < props.length; i++) { @@ -154,21 +145,6 @@ function CheckDeletable(type, prop) { } -function CheckNotDeletable(type, prop) { - var old = type[prop]; - if (!type[prop]) return; - assertTrue(type.hasOwnProperty(prop), "inherited: " + prop); - var deleted = delete type[prop]; - assertTrue(deleted, "delete operator returned false: " + prop); - assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop); - type[prop] = "foo"; - assertEquals("foo", type[prop], "not overwritable: " + prop); - deleted = delete type[prop]; - assertTrue(deleted, "delete operator returned false 2nd time: " + prop); - assertEquals(old.toString(), type[prop].toString(), "delete didn't restore the old value: " + prop); -} - - function CheckDontDeleteAttr(type, prop) { var old = type[prop]; if (!type[prop]) return; @@ -189,7 +165,7 @@ function CheckReadOnlyAttr(type, prop) { assertFalse(deleted, "delete operator returned true: " + prop); assertTrue(type.hasOwnProperty(prop), "not there after delete: " + prop); type[prop] = "foo"; - assertEquals("foo", type[prop], "overwritable: " + prop); + assertEquals(old, type[prop], "overwritable: " + prop); } print("OK"); diff --git a/deps/v8/test/mozilla/mozilla.status b/deps/v8/test/mozilla/mozilla.status index 3a27130990..bc096d5ca1 100644 --- a/deps/v8/test/mozilla/mozilla.status +++ b/deps/v8/test/mozilla/mozilla.status @@ -1,4 +1,4 @@ -# Copyright 2009 the V8 project authors. All rights reserved. +# Copyright 2011 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -69,7 +69,6 @@ js1_5/Array/regress-465980-02: SKIP ecma_3/Date/15.9.3.2-1: SKIP js1_2/function/Number: SKIP - ##################### SLOW TESTS ##################### # This takes a long time to run (~100 seconds). It should only be run @@ -123,14 +122,14 @@ ecma/Date/15.9.2.2-4: PASS || FAIL ecma/Date/15.9.2.2-5: PASS || FAIL ecma/Date/15.9.2.2-6: PASS || FAIL -# 1026139: These date tests fail on arm -ecma/Date/15.9.5.29-1: PASS || ($ARM && FAIL) -ecma/Date/15.9.5.34-1: PASS || ($ARM && FAIL) -ecma/Date/15.9.5.28-1: PASS || ($ARM && FAIL) +# 1026139: These date tests fail on arm and mips +ecma/Date/15.9.5.29-1: PASS || (($ARM || $MIPS) && FAIL) +ecma/Date/15.9.5.34-1: PASS || (($ARM || $MIPS) && FAIL) +ecma/Date/15.9.5.28-1: PASS || (($ARM || $MIPS) && FAIL) -# 1050186: Arm vm is broken; probably unrelated to dates -ecma/Array/15.4.4.5-3: PASS || ($ARM && FAIL) -ecma/Date/15.9.5.22-2: PASS || ($ARM && FAIL) +# 1050186: Arm/MIPS vm is broken; probably unrelated to dates +ecma/Array/15.4.4.5-3: PASS || (($ARM || $MIPS) && FAIL) +ecma/Date/15.9.5.22-2: PASS || (($ARM || $MIPS) && FAIL) # Flaky test that fails due to what appears to be a bug in the test. # Occurs depending on current time @@ -227,7 +226,7 @@ ecma/String/15.5.4.12-4: FAIL_OK ecma/String/15.5.4.12-5: FAIL_OK # Creates a linked list of arrays until we run out of memory or timeout. -js1_5/Regress/regress-312588: FAIL || TIMEOUT +js1_5/Regress/regress-312588: SKIP # Runs out of memory because it compiles huge functions. @@ -301,6 +300,11 @@ js1_2/regexp/RegExp_multiline_as_array: FAIL_OK js1_2/regexp/beginLine: FAIL_OK js1_2/regexp/endLine: FAIL_OK +# We no longer let calls to test and exec with no argument implicitly +# use the previous input. +js1_2/regexp/RegExp_input: FAIL_OK +js1_2/regexp/RegExp_input_as_array: FAIL_OK + # To be compatible with safari typeof a regexp yields 'function'; # in firefox it yields 'object'. @@ -411,12 +415,6 @@ js1_5/extensions/regress-435345-01: FAIL_OK js1_5/extensions/regress-455413: FAIL_OK -# The spec specifies reverse evaluation order for < and >=. -# See section 11.8.2 and 11.8.5. -# We implement the spec here but the test tests the more straigtforward order. -ecma_3/Operators/order-01: FAIL_OK - - # Uses Mozilla-specific QName, XML, XMLList and Iterator. js1_5/Regress/regress-407323: FAIL_OK js1_5/Regress/regress-407957: FAIL_OK @@ -619,6 +617,10 @@ js1_5/Expressions/regress-394673: FAIL # We do not correctly handle assignments within "with" /ecma_3/Statements/12.10-01: FAIL +# We do not throw an exception when a const is redeclared. +# (We only fail section 1 of the test.) +js1_5/Regress/regress-103602: FAIL + ##################### MOZILLA EXTENSION TESTS ##################### ecma/extensions/15.1.2.1-1: FAIL_OK @@ -861,3 +863,59 @@ ecma/Expressions/11.7.3: SKIP ecma/Expressions/11.10-3: SKIP ecma/Expressions/11.7.1: SKIP ecma_3/RegExp/regress-209067: SKIP + +[ $arch == mips ] + +# Times out and print so much output that we need to skip it to not +# hang the builder. +js1_5/extensions/regress-342960: SKIP + +# BUG(3251229): Times out when running new crankshaft test script. +ecma_3/RegExp/regress-311414: SKIP +ecma/Date/15.9.5.8: SKIP +ecma/Date/15.9.5.10-2: SKIP +ecma/Date/15.9.5.11-2: SKIP +ecma/Date/15.9.5.12-2: SKIP +js1_5/Array/regress-99120-02: SKIP +js1_5/extensions/regress-371636: SKIP +js1_5/Regress/regress-203278-1: SKIP +js1_5/Regress/regress-404755: SKIP +js1_5/Regress/regress-451322: SKIP + + +# BUG(1040): Allow this test to timeout. +js1_5/GC/regress-203278-2: PASS || TIMEOUT + + +[ $fast == yes && $arch == mips ] + +# In fast mode on mips we try to skip all tests that would time out, +# since running the tests takes so long in the first place. + +js1_5/Regress/regress-280769-2: SKIP +js1_5/Regress/regress-280769-3: SKIP +js1_5/Regress/regress-244470: SKIP +js1_5/Regress/regress-203278-1: SKIP +js1_5/Regress/regress-290575: SKIP +js1_5/Regress/regress-159334: SKIP +js1_5/Regress/regress-321971: SKIP +js1_5/Regress/regress-347306-01: SKIP +js1_5/Regress/regress-280769-1: SKIP +js1_5/Regress/regress-280769-5: SKIP +js1_5/GC/regress-306788: SKIP +js1_5/GC/regress-278725: SKIP +js1_5/GC/regress-203278-3: SKIP +js1_5/GC/regress-311497: SKIP +js1_5/Array/regress-99120-02: SKIP +ecma/Date/15.9.5.22-1: SKIP +ecma/Date/15.9.5.20: SKIP +ecma/Date/15.9.5.12-2: SKIP +ecma/Date/15.9.5.8: SKIP +ecma/Date/15.9.5.9: SKIP +ecma/Date/15.9.5.11-2: SKIP +ecma/Expressions/11.7.2: SKIP +ecma/Expressions/11.10-2: SKIP +ecma/Expressions/11.7.3: SKIP +ecma/Expressions/11.10-3: SKIP +ecma/Expressions/11.7.1: SKIP +ecma_3/RegExp/regress-209067: SKIP diff --git a/deps/v8/test/preparser/preparser.status b/deps/v8/test/preparser/preparser.status index db177782ef..6f15fedd8f 100644 --- a/deps/v8/test/preparser/preparser.status +++ b/deps/v8/test/preparser/preparser.status @@ -31,9 +31,3 @@ prefix preparser # escapes (we need to parse to distinguish octal escapes from valid # back-references). strict-octal-regexp: FAIL - -############################################################################## -[ $arch == mips ] - -# Skip all tests on MIPS. -*: SKIP diff --git a/deps/v8/test/preparser/strict-identifiers.pyt b/deps/v8/test/preparser/strict-identifiers.pyt index 72808e25bf..aa3d5210d8 100644 --- a/deps/v8/test/preparser/strict-identifiers.pyt +++ b/deps/v8/test/preparser/strict-identifiers.pyt @@ -138,6 +138,38 @@ setter_arg = StrictTemplate("setter-param-$id", """ var x = {set foo($id) { }}; """) +label_normal = Template("label-normal-$id", """ + $id: ''; +""") + +label_strict = StrictTemplate("label-strict-$id", """ + $id: ''; +""") + +break_normal = Template("break-normal-$id", """ + for (;;) { + break $id; + } +""") + +break_strict = StrictTemplate("break-strict-$id", """ + for (;;) { + break $id; + } +""") + +continue_normal = Template("continue-normal-$id", """ + for (;;) { + continue $id; + } +""") + +continue_strict = StrictTemplate("continue-strict-$id", """ + for (;;) { + continue $id; + } +""") + non_strict_use = Template("nonstrict-$id", """ var $id = 42; $id++; @@ -162,6 +194,7 @@ non_strict_use = Template("nonstrict-$id", """ function $id($id) { } x = {$id: 42}; x = {get $id() {}, set $id(value) {}}; + $id: ''; """) identifier_name_source = """ @@ -197,6 +230,12 @@ for id in ["eval", "arguments"]: prefix_var({"id": id, "op":"--", "opname":"dec"}, "strict_lhs_prefix") postfix_var({"id": id, "op":"++", "opname":"inc"}, "strict_lhs_postfix") postfix_var({"id": id, "op":"--", "opname":"dec"}, "strict_lhs_postfix") + label_normal({"id": id}, None) + label_strict({"id": id}, None) + break_normal({"id": id}, None) + break_strict({"id": id}, None) + continue_normal({"id": id}, None) + continue_strict({"id": id}, None) non_strict_use({"id": id}, None) @@ -205,10 +244,13 @@ for id in ["eval", "arguments"]: for reserved_word in reserved_words + strict_reserved_words: if (reserved_word in strict_reserved_words): message = "strict_reserved_word" + label_message = None elif (reserved_word == "const"): message = "unexpected_token" + label_message = message else: message = "reserved_word" + label_message = message arg_name_own({"id":reserved_word}, message) arg_name_nested({"id":reserved_word}, message) setter_arg({"id": reserved_word}, message) @@ -225,6 +267,19 @@ for reserved_word in reserved_words + strict_reserved_words: read_var({"id": reserved_word}, message) identifier_name({"id": reserved_word}, None); identifier_name_strict({"id": reserved_word}, None); + label_normal({"id": reserved_word}, label_message) + break_normal({"id": reserved_word}, label_message) + continue_normal({"id": reserved_word}, label_message) + if (reserved_word == "const"): + # The error message for this case is different because + # ParseLabelledStatementOrExpression will try to parse this as an expression + # first, effectively disallowing the use in ParseVariableDeclarations, i.e. + # the preparser never sees that 'const' was intended to be a label. + label_strict({"id": reserved_word}, "strict_const") + else: + label_strict({"id": reserved_word}, message) + break_strict({"id": reserved_word}, message) + continue_strict({"id": reserved_word}, message) # Future reserved words in strict mode behave like normal identifiers diff --git a/deps/v8/test/sputnik/sputnik.status b/deps/v8/test/sputnik/sputnik.status index 868509d7c5..a587a6d4a2 100644 --- a/deps/v8/test/sputnik/sputnik.status +++ b/deps/v8/test/sputnik/sputnik.status @@ -30,10 +30,6 @@ def FAIL_OK = FAIL, OKAY ############################### BUGS ################################### -# A bound function should fail on access to 'caller' and 'arguments'. -S15.3.4.5_A1: FAIL -S15.3.4.5_A2: FAIL - # '__proto__' should be treated as a normal property in JSON. S15.12.2_A1: FAIL @@ -46,11 +42,8 @@ S15.8.2.16_A7: PASS || FAIL_OK S15.8.2.18_A7: PASS || FAIL_OK S15.8.2.13_A23: PASS || FAIL_OK -# We allow calls to regexp exec() with no arguments to fail for -# compatibility reasons. -S15.10.6.2_A1_T16: FAIL_OK -S15.10.6.2_A12: FAIL_OK -S15.10.6.3_A1_T16: FAIL_OK +# Sputnik tests (r97) assume RegExp.prototype is an Object, not a RegExp. +S15.10.6_A2: FAIL_OK # We are silent in some regexp cases where the spec wants us to give # errors, for compatibility. @@ -159,6 +152,10 @@ S11.1.5_A4.2: FAIL_OK S9.9_A1: FAIL_OK S9.9_A2: FAIL_OK +# The expected evaluation order of comparison operations changed. +S11.8.2_A2.3_T1: FAIL_OK +S11.8.3_A2.3_T1: FAIL_OK + # Calls builtins without an explicit receiver which means that # undefined is passed to the builtin. The tests expect the global # object to be passed which was true in ES3 but not in ES5. @@ -176,6 +173,23 @@ S15.5.4.13_A1_T3: FAIL_OK S15.5.4.14_A1_T3: FAIL_OK S15.5.4.15_A1_T3: FAIL_OK +# NaN, Infinity and undefined are read-only according to ES5. +S15.1.1.1_A2_T1: FAIL_OK # NaN +S15.1.1.1_A2_T2: FAIL_OK # NaN +S15.1.1.2_A2_T1: FAIL_OK # Infinity +# S15.1.1.2_A2_T2 would fail if it weren't bogus in r97. sputnik bug #45. +S15.1.1.3_A2_T1: FAIL_OK # undefined +S15.1.1.3_A2_T2: FAIL_OK # undefined + +# Function.prototype.apply can handle arbitrary object as argument list. +S15.3.4.3_A6_T1: FAIL_OK +S15.3.4.3_A6_T4: FAIL_OK + +# Array.prototype.to[Locale]String is generic in ES5. +S15.4.4.2_A2_T1: FAIL_OK +S15.4.4.3_A2_T1: FAIL_OK + + ##################### SKIPPED TESTS ##################### # These tests take a looong time to run in debug mode. @@ -194,53 +208,6 @@ S15.3_A3_T1: FAIL # Invalid test case (recent change adding var changes semantics) S15.3_A3_T3: FAIL -# These tests fail because we had to add bugs to be compatible with JSC. See -# http://code.google.com/p/chromium/issues/detail?id=1717 -S15.5.4.1_A1_T2: FAIL_OK -S15.5.4_A1: FAIL_OK -S15.5.4_A3: FAIL_OK -S15.9.5.10_A1_T2: FAIL_OK -S15.9.5.11_A1_T2: FAIL_OK -S15.9.5.12_A1_T2: FAIL_OK -S15.9.5.13_A1_T2: FAIL_OK -S15.9.5.14_A1_T2: FAIL_OK -S15.9.5.15_A1_T2: FAIL_OK -S15.9.5.16_A1_T2: FAIL_OK -S15.9.5.17_A1_T2: FAIL_OK -S15.9.5.18_A1_T2: FAIL_OK -S15.9.5.19_A1_T2: FAIL_OK -S15.9.5.20_A1_T2: FAIL_OK -S15.9.5.21_A1_T2: FAIL_OK -S15.9.5.22_A1_T2: FAIL_OK -S15.9.5.23_A1_T2: FAIL_OK -S15.9.5.24_A1_T2: FAIL_OK -S15.9.5.25_A1_T2: FAIL_OK -S15.9.5.26_A1_T2: FAIL_OK -S15.9.5.27_A1_T2: FAIL_OK -S15.9.5.28_A1_T2: FAIL_OK -S15.9.5.29_A1_T2: FAIL_OK -S15.9.5.2_A1_T2: FAIL_OK -S15.9.5.30_A1_T2: FAIL_OK -S15.9.5.31_A1_T2: FAIL_OK -S15.9.5.32_A1_T2: FAIL_OK -S15.9.5.33_A1_T2: FAIL_OK -S15.9.5.34_A1_T2: FAIL_OK -S15.9.5.35_A1_T2: FAIL_OK -S15.9.5.36_A1_T2: FAIL_OK -S15.9.5.37_A1_T2: FAIL_OK -S15.9.5.38_A1_T2: FAIL_OK -S15.9.5.39_A1_T2: FAIL_OK -S15.9.5.3_A1_T2: FAIL_OK -S15.9.5.40_A1_T2: FAIL_OK -S15.9.5.41_A1_T2: FAIL_OK -S15.9.5.42_A1_T2: FAIL_OK -S15.9.5.4_A1_T2: FAIL_OK -S15.9.5.5_A1_T2: FAIL_OK -S15.9.5.6_A1_T2: FAIL_OK -S15.9.5.7_A1_T2: FAIL_OK -S15.9.5.8_A1_T2: FAIL_OK -S15.9.5.9_A1_T2: FAIL_OK - [ $arch == arm ] # BUG(3251225): Tests that timeout with --nocrankshaft. @@ -257,5 +224,14 @@ S15.1.3.2_A2.5_T1: SKIP [ $arch == mips ] -# Skip all tests on MIPS. -*: SKIP +# BUG(3251225): Tests that timeout with --nocrankshaft. +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.1_A2.4_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.4_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.3_A2.3_T1: SKIP +S15.1.3.4_A2.3_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP diff --git a/deps/v8/test/test262/README b/deps/v8/test/test262/README index ea6b4a71a6..094356fcf0 100644 --- a/deps/v8/test/test262/README +++ b/deps/v8/test/test262/README @@ -4,11 +4,11 @@ tests from http://hg.ecmascript.org/tests/test262 -at revision 128 as 'data' in this directory. Using later version +at revision 271 as 'data' in this directory. Using later version may be possible but the tests are only known to pass (and indeed run) with that revision. -hg clone -r 128 http://hg.ecmascript.org/tests/test262 data +hg clone -r 271 http://hg.ecmascript.org/tests/test262 data If you do update to a newer revision you may have to change the test harness adapter code since it uses internal functionality from the diff --git a/deps/v8/test/test262/test262.status b/deps/v8/test/test262/test262.status index 8cee210763..1da988efc1 100644 --- a/deps/v8/test/test262/test262.status +++ b/deps/v8/test/test262/test262.status @@ -25,1330 +25,213 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +prefix test262 +def FAIL_OK = FAIL, OKAY -# -# ietestcenter tests. -# +############################### BUGS ################################### -prefix ietestcenter +# '__proto__' should be treated as a normal property in JSON. +S15.12.2_A1: FAIL +# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1196 +S8.7_A5_T2: FAIL -# -# Deliberate differences for compatibility with other browsers -# -# 15.9.5.43-0-9 and 15.9.5.43-0-10. V8 doesn't throw RangeError -# from Date.prototype.toISOString when string is not a finite number. -# This is compatible with Firefox and Safari. -15.9.5.43-0-9: PASS || FAIL -15.9.5.43-0-10: PASS || FAIL +# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1624 +S10.4.2.1_A1: FAIL -# -# Unanalyzed failures which may be bugs or deliberate differences -# +# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1475 +15.2.3.6-4-405: FAIL +15.2.3.6-4-410: FAIL +15.2.3.6-4-415: FAIL +15.2.3.6-4-420: FAIL -# Bug? Strict Mode - TypeError is thrown when changing the value of a Value -# Property of the Global Object under strict mode (NaN) -10.2.1.1.3-4-16-s: FAIL -# Bug? Strict Mode - TypeError is thrown when changing the value of a Value -# Property of the Global Object under strict mode (undefined) -10.2.1.1.3-4-18-s: FAIL -# Invalid test: https://bugs.ecmascript.org/show_bug.cgi?id=76 -10.4.2-2-c-1: FAIL -# BUG: 11.8.2 Greater-than Operator - Partial left to right order enforced -# when using Greater-than operator: valueOf > valueOf -11.8.2-1: FAIL -# BUG: 11.8.2 Greater-than Operator - Partial left to right order enforced -# when using Greater-than operator: valueOf > toString -11.8.2-2: FAIL -# BUG: 11.8.2 Greater-than Operator - Partial left to right order enforced -# when using Greater-than operator: toString > valueOf -11.8.2-3: FAIL -# BUG: 11.8.2 Greater-than Operator - Partial left to right order enforced -# when using Greater-than operator: toString > toString -11.8.2-4: FAIL -# BUG: 11.8.3 Less-than-or-equal Operator - Partial left to right order -# enforced when using Less-than-or-equal operator: valueOf <= valueOf -11.8.3-1: FAIL -# BUG: 11.8.3 Less-than-or-equal Operator - Partial left to right order -# enforced when using Less-than-or-equal operator: valueOf <= toString -11.8.3-2: FAIL -# BUG: 11.8.3 Less-than-or-equal Operator - Partial left to right order -# enforced when using Less-than-or-equal operator: toString <= valueOf -11.8.3-3: FAIL -# BUG: 11.8.3 Less-than-or-equal Operator - Partial left to right order -# enforced when using Less-than-or-equal operator: toString <= toString -11.8.3-4: FAIL -# BUG: 11.8.3 Less-than-or-equal Operator - Partial left to right order -# enforced when using Less-than-or-equal operator: valueOf <= valueOf -11.8.3-5: FAIL -# Bug? simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Global.undefined) -11.13.1-4-27-s: FAIL -# Bug? simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Global.Infinity) -11.13.1-4-3-s: FAIL -# BUG: Global.NaN is a data property with default attribute values -15.1.1.1-0: FAIL -# BUG: Global.Infinity is a data property with default attribute values -15.1.1.2-0: FAIL -# BUG: Global.undefined is a data property with default attribute values -15.1.1.3-0: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (Global.NaN) -15.2.3.3-4-178: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (Global.Infinity) -15.2.3.3-4-179: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (Global.undefined) -15.2.3.3-4-180: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (RegExp.prototype.source) -# There is no RegExp.prototype.source -15.2.3.3-4-212: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (RegExp.prototype.global) -# There is no RegExp.prototype.global -15.2.3.3-4-213: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (RegExp.prototype.ignoreCase) -# There is no RegExp.prototype.ignoreCase -15.2.3.3-4-214: FAIL -# BUG: Object.getOwnPropertyDescriptor returns data desc (all false) -# for properties on built-ins (RegExp.prototype.multiline) -15.2.3.3-4-215: FAIL -# Bug? Object.defineProperty - Update [[Enumerable]] attribute of 'name' -# property to true successfully when [[Enumerable]] attribute of 'name' -# is false and [[Configurable]] attribute of 'name' is true, the 'desc' -# is a generic descriptor which only contains [[Enumerable]] attribute -# as true, 'name' property is an index data property (8.12.9 step 8) -15.2.3.6-4-82-18: FAIL -# Bug? Object.defineProperty - Update [[Enumerable]] attribute of 'name' -# property to false successfully when [[Enumerable]] and [[Configurable]] -# attributes of 'name' property are true, the 'desc' is a generic -# descriptor which only contains [Enumerable]] attribute as false and -# 'name' property is an index accessor property (8.12.9 step 8) -15.2.3.6-4-82-19: FAIL -# Bug? Object.defineProperty - Update [[Enumerable]] attribute of 'name' -# property to false successfully when [[Enumerable]] and [[Configurable]] -# attributes of 'name' property are true, the 'desc' is a generic -# descriptor which contains [Enumerable]] attribute as false and -# [[Configurable]] property is true, 'name' property is an index accessor -# property (8.12.9 step 8) -15.2.3.6-4-82-20: FAIL -# Bug? Object.defineProperty - Update [[Configurable]] attribute of 'name' -# property to false successfully when [[Enumerable]] and [[Configurable]] -# attributes of 'name' property are true, the 'desc' is a generic -# descriptor which only contains [[Configurable]] attribute as false, -# 'name' property is an index accessor property (8.12.9 step 8) -15.2.3.6-4-82-21: FAIL -# Bug? Object.defineProperty - Update [[Configurable]] attribute of 'name' -# property to false successfully when [[Enumerable]] and [[Configurable]] -# attributes of 'name' property are true, the 'desc' is a generic -# descriptor which contains [[Enumerable]] attribute as true and -# [[Configurable]] attribute is false, 'name' property is an index accessor -# property (8.12.9 step 8) -15.2.3.6-4-82-22: FAIL -# Bug? Object.defineProperty - Update [[Enumerable]] and [[Configurable]] -# attributes of 'name' property to false successfully when [[Enumerable]] -# and [[Configurable]] attributes of 'name' property are true, the 'desc' -# is a generic descriptor which contains [[Enumerable]] and -# [[Configurable]] attributes as false, 'name' property is an index -# accessor property (8.12.9 step 8) -15.2.3.6-4-82-23: FAIL -# Bug? Object.defineProperty - Update [[Enumerable]] attributes of 'name' -# property to true successfully when [[Enumerable]] attribute of 'name' is -# false and [[Configurable]] attribute of 'name' is true, the 'desc' is a -# generic descriptor which only contains [[Enumerable]] attribute as true, -# 'name' property is an index accessor property (8.12.9 step 8) -15.2.3.6-4-82-24: FAIL -# Bug? Object.defineProperty - 'O' is an Array, test the length property of 'O' -# is own data property (15.4.5.1 step 1) -15.2.3.6-4-116: FAIL -# Bug? Object.defineProperty - 'O' is an Array, test the length property of 'O' -# is own data property that overrides an inherited data property (15.4.5.1 -# step 1) -15.2.3.6-4-117: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test that RangeError exception is thrown when [[Value]] field of -# 'desc' is undefined (15.4.5.1 step 3.c) -15.2.3.6-4-125: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is null (15.4.5.1 step 3.c) -15.2.3.6-4-126: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is a boolean with value false -# (15.4.5.1 step 3.c) -15.2.3.6-4-127: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is a boolean with value true -# (15.4.5.1 step 3.c) -15.2.3.6-4-128: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is not thrown when the [[Value]] field of -# 'desc' is 0 (15.4.5.1 step 3.c) -15.2.3.6-4-129: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is not thrown when the [[Value]] field of -# 'desc' is +0 (15.4.5.1 step 3.c) -15.2.3.6-4-130: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is not thrown when the [[Value]] field of -# 'desc' is -0 (15.4.5.1 step 3.c) -15.2.3.6-4-131: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is not thrown when the [[Value]] field of -# 'desc' is a positive number (15.4.5.1 step 3.c) -15.2.3.6-4-132: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a negative number (15.4.5.1 step 3.c) -15.2.3.6-4-133: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is +Infinity (15.4.5.1 step 3.c) -15.2.3.6-4-134: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is -Infinity (15.4.5.1 step 3.c) -15.2.3.6-4-135: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is NaN (15.4.5.1 step 3.c) -15.2.3.6-4-136: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is not thrown when the [[Value]] field of -# 'desc' is a string containing a positive number (15.4.5.1 step 3.c) -15.2.3.6-4-137: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a string containing a negative number (15.4.5.1 step 3.c) -15.2.3.6-4-138: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a string containing a decimal number (15.4.5.1 step 3.c) -15.2.3.6-4-139: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a string containing +Infinity (15.4.5.1 step 3.c) -15.2.3.6-4-140: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a string containing -Infinity (15.4.5.1 step 3.c) -15.2.3.6-4-141: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is a string containing an -# exponential number (15.4.5.1 step 3.c) -15.2.3.6-4-142: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is a string containing a hex -# number (15.4.5.1 step 3.c) -15.2.3.6-4-143: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is a string containing a number -# with leading zeros (15.4.5.1 step 3.c) -15.2.3.6-4-144: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError exception is thrown when the [[Value]] field of -# 'desc' is a string which doesn't convert to a number (15.4.5.1 step 3.c) -15.2.3.6-4-145: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is an object which has an own -# toString method (15.4.5.1 step 3.c) -15.2.3.6-4-146: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is an Object which has an own -# valueOf method (15.4.5.1 step 3.c) -15.2.3.6-4-147: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is an Object which has an own -# valueOf method that returns an object and toString method that returns a -# string (15.4.5.1 step 3.c) -15.2.3.6-4-148: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is an Object which has an own -# toString and valueOf method (15.4.5.1 step 3.c) -15.2.3.6-4-149: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test TypeError is thrown when the [[Value]] field of 'desc' is an -# Object that both toString and valueOf wouldn't return primitive value -# (15.4.5.1 step 3.c) -15.2.3.6-4-150: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', and the [[Value]] field of 'desc' is an Object with an own toString -# method and an inherited valueOf method (15.4.5.1 step 3.c), test that the -# inherited valueOf method is used -15.2.3.6-4-151: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError is thrown when the [[Value]] field of 'desc' is a -# positive non-integer values (15.4.5.1 step 3.c) -15.2.3.6-4-152: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length prosperty -# of 'O', test RangeError is thrown when the [[Value]] field of 'desc' is a -# negative non-integer values (15.4.5.1 step 3.c) -15.2.3.6-4-153: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is boundary value 2^32 - 2 -# (15.4.5.1 step 3.c) -15.2.3.6-4-154: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test the [[Value]] field of 'desc' is boundary value 2^32 - 1 -# (15.4.5.1 step 3.c) -15.2.3.6-4-155: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# boundary value 2^32 (15.4.5.1 step 3.c) -15.2.3.6-4-156: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# boundary value 2^32 + 1 (15.4.5.1 step 3.c) -15.2.3.6-4-157: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', set the [[Value]] field of 'desc' to a value greater than the -# existing value of length (15.4.5.1 step 3.f) -15.2.3.6-4-159: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', set the [[Value]] field of 'desc' to a value lesser than the -# existing value of length and test that indexes beyond the new length are -# deleted(15.4.5.1 step 3.f) -15.2.3.6-4-161: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to true after deleting properties with large index named if the -# [[Writable]] field of 'desc' is absent (15.4.5.1 step 3.h) -15.2.3.6-4-165: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to true after deleting properties with large index named if the -# [[Writable]] field of 'desc' is true (15.4.5.1 step 3.h) -15.2.3.6-4-166: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to false after deleting properties with large index named if the -# [[Writable]] field of 'desc' is false (15.4.5.1 step 3.i.ii) -15.2.3.6-4-167: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', whose writable attribute is being changed to false and the [[Value]] -# field of 'desc' is less than value of the length property and also lesser -# than an index of the array which is set to configurable:false, test that -# new length is set to a value greater than the non-deletable index by 1, -# writable attribute of length is set to false and TypeError exception is -# thrown (15.4.5.1 step 3.i.iii) -15.2.3.6-4-168: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property and also lesser than an index of the array which is set to -# configurable: false, test that new length is set to a value greater than -# the non-deletable index by 1, and TypeError is thrown (15.4.5.1 step -# 3.l.i) -15.2.3.6-4-169: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property and also lesser than an index of the array which is set to -# configurable: false, test that new length is set to a value greater than -# the non-deletable index by 1, writable attribute of length is set to -# false and TypeError exception is thrown (15.4.5.1 step 3.l.ii) -15.2.3.6-4-170: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of an inherited data -# property with large index named in 'O' can't stop deleting index named -# properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-171: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own data property with -# large index named in 'O' that overrides an inherited data property can -# stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-172: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own data property with -# large index named in 'O' that overrides an inherited accessor property -# can stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-173: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' can stop deleting index named properties -# (15.4.5.1 step 3.l.ii) -15.2.3.6-4-174: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of an inherited accessor -# property with large index named in 'O' can't stop deleting index named -# properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-175: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' that overrides an inherited data property -# can stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-176: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' that overrides an inherited accessor -# property can stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.6-4-177: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the configurable large index named property of 'O' is -# deleted (15.4.5.1 step 3.l.ii) -15.2.3.6-4-178: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is greater than value of the length -# property, test value of the length property is same as [[Value]] -# (15.4.5.1 step 3.l.iii.1) -15.2.3.6-4-179-1: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to false at last when the [[Writable]] field of 'desc' is false and 'O' -# doesn't contain non-configurable large index named property (15.4.5.1 -# step 3.m) -15.2.3.6-4-181: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, 'name' is boundary value 2^32 - 2 (15.4.5.1 step 4.a) -15.2.3.6-4-183: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, test TypeError is thrown if the [[Writable]] attribute of the -# length property in 'O' is false and value of 'name' equals to value of -# the length property (15.4.5.1 step 4.b) -15.2.3.6-4-188: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, test TypeError is thrown if the [[Writable]] attribute of the -# length property in 'O' is false and value of 'name' is greater than value -# of the length property (15.4.5.1 step 4.b) -15.2.3.6-4-189: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, 'desc' is accessor descriptor, test updating all attribute -# values of 'name' (15.4.5.1 step 4.c) -15.2.3.6-4-209: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, name is accessor property and 'desc' is accessor descriptor, -# test updating the [[Enumerable]] attribute value of 'name' (15.4.5.1 step -# 4.c) -15.2.3.6-4-271: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, name is accessor property and 'desc' is accessor descriptor, -# test updating the [[Configurable]] attribute value of 'name' (15.4.5.1 -# step 4.c) -15.2.3.6-4-272: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, name is accessor property and 'desc' is accessor descriptor, -# test updating multiple attribute values of 'name' (15.4.5.1 step 4.c) -15.2.3.6-4-273: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, test the length property of 'O' is set as ToUint32('name') + 1 -# if ToUint32('name') equals to value of the length property in 'O' -# (15.4.5.1 step 4.e.ii) -15.2.3.6-4-275: FAIL -# Bug? Object.defineProperty - 'O' is an Array, 'name' is an array index named -# property, test the length property of 'O' is set as ToUint32('name') + 1 -# if ToUint32('name') is greater than value of the length property in 'O' -# (15.4.5.1 step 4.e.ii) -15.2.3.6-4-276: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own accessor property of 'O' which is also -# defined in [[ParameterMap]] of 'O', and 'desc' is accessor descriptor, -# test updating multiple attribute values of 'name' (10.6 -# [[DefineOwnProperty]] step 3 and 5.a.i) -15.2.3.6-4-291-1: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object, 'name' is own -# accessor property of 'O', and 'desc' is accessor descriptor, test -# updating multiple attribute values of 'name' (10.6 [[DefineOwnProperty]] -# step 3) -15.2.3.6-4-291: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own property of 'O' which is also defined in -# [[ParameterMap]] of 'O', and 'desc' is data descriptor, test updating -# multiple attribute values of 'name' (10.6 [[DefineOwnProperty]] step 3 -# and 5.b) +# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1772 15.2.3.6-4-292-1: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own data property of 'O' which is also -# defined in [[ParameterMap]] of 'O', test TypeError is thrown when -# updating the [[Value]] attribute value of 'name' which is defined as -# unwritable and non-configurable (10.6 [[DefineOwnProperty]] step 4 and -# step 5b) 15.2.3.6-4-293-2: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own data property of 'O' which is also -# defined in [[ParameterMap]] of 'O', test TypeError is not thrown when -# updating the [[Value]] attribute value of 'name' which is defined as -# non-writable and configurable (10.6 [[DefineOwnProperty]] step 3 and step -# 5.b) 15.2.3.6-4-293-3: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own data property of 'O' which is also -# defined in [[ParameterMap]] of 'O', test TypeError is thrown when -# updating the [[Writable]] attribute value of 'name' which is defined as -# non-configurable (10.6 [[DefineOwnProperty]] step 4 and 5b) 15.2.3.6-4-294-1: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own data property of 'O' which is also -# defined in [[ParameterMap]] of 'O', test TypeError is thrown when -# updating the [[Enumerable]] attribute value of 'name' which is defined as -# non-configurable (10.6 [[DefineOwnProperty]] step 4 and step 5b) 15.2.3.6-4-295-1: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object of a function that has -# formal parameters, 'name' is own data property of 'O' which is also -# defined in [[ParameterMap]] of 'O', test TypeError is thrown when -# updating the [[Configurable]] attribute value of 'name' which is defined -# as non-configurable (10.6 [[DefineOwnProperty]] step 4 and step 5b) 15.2.3.6-4-296-1: FAIL -# Bug? Object.defineProperty - 'O' is an Arguments object, 'name' is an index -# named accessor property of 'O' but not defined in [[ParameterMap]] of -# 'O', and 'desc' is accessor descriptor, test updating multiple attribute -# values of 'name' (10.6 [[DefineOwnProperty]] step 3) -15.2.3.6-4-303: FAIL -# Bug? ES5 Attributes - indexed property 'P' with attributes [[Writable]]: true, -# [[Enumerable]]: true, [[Configurable]]: false is writable using simple -# assignment, 'O' is an Arguments object 15.2.3.6-4-333-11: FAIL -# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are -# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an -# accessor property, 'A' is an Array object (8.12.9 - step 9.b.i) -15.2.3.6-4-360-1: FAIL -# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are -# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an -# accessor property, 'O' is an Arguments object (8.12.9 - step 9.b.i) -15.2.3.6-4-360-6: FAIL -# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are -# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an -# accessor property, 'O' is the global object (8.12.9 - step 9.b.i) -15.2.3.6-4-360-7: FAIL -# Bug? ES5 Attributes - [[Value]] attribute of data property is the activex host -# object -15.2.3.6-4-401: FAIL -# Bug? ES5 Attributes - Failed to add a property to an object when the object's -# object has a property with same name and [[Writable]] attribute is set to -# false (Number instance) -15.2.3.6-4-405: FAIL -# Bug? ES5 Attributes - Failed to add a property to an object when the object's -# prototype has a property with the same name and [[Writable]] set to false -# (JSON) -15.2.3.6-4-410: FAIL -# Bug? ES5 Attributes - Failed to add properties to an object when the object's -# prototype has properties with the same name and [[Writable]] set to false -# (Object.create) -15.2.3.6-4-415: FAIL -# Bug? ES5 Attributes - Failed to add a property to an object when the object's -# prototype has a property with the same name and [[Writable]] set to -# false(Function.prototype.bind) -15.2.3.6-4-420: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.indexOf are correct -15.2.3.6-4-612: FAIL -# Bug? ES5 Attributes - all attributes in Object.lastIndexOf are correct -15.2.3.6-4-613: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.every are correct -15.2.3.6-4-614: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.some are correct -15.2.3.6-4-615: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.forEach are correct -15.2.3.6-4-616: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.map are correct -15.2.3.6-4-617: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.filter are correct -15.2.3.6-4-618: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.reduce are correct -15.2.3.6-4-619: FAIL -# Bug? ES5 Attributes - all attributes in Array.prototype.reduceRight are -# correct -15.2.3.6-4-620: FAIL -# Bug? ES5 Attributes - all attributes in String.prototype.trim are correct -15.2.3.6-4-621: FAIL -# Bug? ES5 Attributes - all attributes in Date.prototype.toISOString are correct -15.2.3.6-4-623: FAIL -# Bug? ES5 Attributes - all attributes in Date.prototype.toJSON are correct -15.2.3.6-4-624: FAIL -# Bug? Object.defineProperties - 'O' is an Array, test the length property of -# 'O' is own data property (15.4.5.1 step 1) -15.2.3.7-6-a-112: FAIL -# Bug? Object.defineProperties - 'O' is an Array, test the length property of -# 'O' is own data property that overrides an inherited data property -# (15.4.5.1 step 1) -15.2.3.7-6-a-113: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test RangeError is thrown when setting the [[Value]] field of 'desc' -# to undefined (15.4.5.1 step 3.c) -15.2.3.7-6-a-121: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test setting the [[Value]] field of 'desc' to null actuall is set to -# 0 (15.4.5.1 step 3.c) -15.2.3.7-6-a-122: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a boolean with value false -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-123: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a boolean with value true -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-124: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is 0 (15.4.5.1 step 3.c) -15.2.3.7-6-a-125: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is +0 (15.4.5.1 step 3.c) -15.2.3.7-6-a-126: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is -0 (15.4.5.1 step 3.c) -15.2.3.7-6-a-127: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is positive number (15.4.5.1 -# step 3.c) -15.2.3.7-6-a-128: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is negative number (15.4.5.1 -# step 3.c) -15.2.3.7-6-a-129: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is +Infinity (15.4.5.1 step -# 3.c) -15.2.3.7-6-a-130: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is -Infinity (15.4.5.1 step -# 3.c) -15.2.3.7-6-a-131: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is NaN (15.4.5.1 step 3.c) -15.2.3.7-6-a-132: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing a -# positive number (15.4.5.1 step 3.c) -15.2.3.7-6-a-133: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing a -# negative number (15.4.5.1 step 3.c) -15.2.3.7-6-a-134: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing a -# decimal number (15.4.5.1 step 3.c) -15.2.3.7-6-a-135: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing -# +Infinity (15.4.5.1 step 3.c) -15.2.3.7-6-a-136: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing -# -Infinity (15.4.5.1 step 3.c) -15.2.3.7-6-a-137: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing an -# exponential number (15.4.5.1 step 3.c) -15.2.3.7-6-a-138: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing an hex -# number (15.4.5.1 step 3.c) -15.2.3.7-6-a-139: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is a string containing an -# leading zero number (15.4.5.1 step 3.c) -15.2.3.7-6-a-140: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test the [[Value]] field of 'desc' is a string which doesn't convert -# to a number (15.4.5.1 step 3.c) -15.2.3.7-6-a-141: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test the [[Value]] field of 'desc' is an Object which has an own -# toString method (15.4.5.1 step 3.c) -15.2.3.7-6-a-142: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is an Object which has an own -# valueOf method (15.4.5.1 step 3.c) -15.2.3.7-6-a-143: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is an Object which has an own -# valueOf method that returns an object and toString method that returns a -# string (15.4.5.1 step 3.c) -15.2.3.7-6-a-144: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is an Object which has an own -# toString and valueOf method (15.4.5.1 step 3.c) -15.2.3.7-6-a-145: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test TypeError is thrown when the [[Value]] field of 'desc' is an -# Object that both toString and valueOf wouldn't return primitive value -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-146: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test using inherited valueOf method when the [[Value]] field of -# 'desc' is an Objec with an own toString and inherited valueOf methods -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-147: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# positive non-integer values (15.4.5.1 step 3.c) -15.2.3.7-6-a-148: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# negative non-integer values (15.4.5.1 step 3.c) -15.2.3.7-6-a-149: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is boundary value 2^32 - 2 -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-150: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test the [[Value]] field of 'desc' is boundary value 2^32 - 1 -# (15.4.5.1 step 3.c) -15.2.3.7-6-a-151: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# boundary value 2^32 (15.4.5.1 step 3.c) -15.2.3.7-6-a-152: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'name' is the length property -# of 'O', test RangeError is thrown when the [[Value]] field of 'desc' is -# boundary value 2^32 + 1 (15.4.5.1 step 3.c) -15.2.3.7-6-a-153: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test the [[Value]] field of 'desc' which is greater than value of -# the length property is defined into 'O' without deleting any property -# with large index named (15.4.5.1 step 3.f) -15.2.3.7-6-a-155: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', test the [[Value]] field of 'desc' which is less than value of the -# length property is defined into 'O' with deleting properties with large -# index named (15.4.5.1 step 3.f) -15.2.3.7-6-a-157: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to true at last after deleting properties with large index named if the -# [[Writable]] field of 'desc' is absent (15.4.5.1 step 3.h) -15.2.3.7-6-a-161: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to true at last after deleting properties with large index named if the -# [[Writable]] field of 'desc' is true (15.4.5.1 step 3.h) -15.2.3.7-6-a-162: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to false at last after deleting properties with large index named if the -# [[Writable]] field of 'desc' is false (15.4.5.1 step 3.i.ii) -15.2.3.7-6-a-163: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property in 'O' -# is set as true before deleting properties with large index named -# (15.4.5.1 step 3.i.iii) -15.2.3.7-6-a-164: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the length property is decreased by 1 (15.4.5.1 step -# 3.l.i) -15.2.3.7-6-a-165: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own data property with -# large index named in 'O' can stop deleting index named properties -# (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-166: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of inherited data property -# with large index named in 'O' can't stop deleting index named properties -# (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-167: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own data property with -# large index named in 'O' that overrides inherited data property can stop -# deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-168: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own data property with -# large index named in 'O' that overrides inherited accessor property can -# stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-169: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' can stop deleting index named properties -# (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-170: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of inherited accessor -# property with large index named in 'O' can't stop deleting index named -# properties (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-171: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' that overrides inherited data property can -# stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-172: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Configurable]] attribute of own accessor property -# with large index named in 'O' that overrides inherited accessor property -# can stop deleting index named properties (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-173: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the configurable large index named property of 'O' can be -# deleted (15.4.5.1 step 3.l.ii) -15.2.3.7-6-a-174: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test value of the length property is set to the last -# non-configurable index named property of 'O' plus 1 (15.4.5.1 step -# 3.l.iii.1) -15.2.3.7-6-a-175: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to false at last when the [[Writable]] field of 'desc' is false and 'O' -# contains non-configurable large index named property (15.4.5.1 step -# 3.l.iii.2) -15.2.3.7-6-a-176: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is the length property of -# 'O', the [[Value]] field of 'desc' is less than value of the length -# property, test the [[Writable]] attribute of the length property is set -# to false at last when the [[Writable]] field of 'desc' is false and 'O' -# doesn't contain non-configurable large index named property (15.4.5.1 -# step 3.m) -15.2.3.7-6-a-177: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property, 'P' is boundary value 2^32 - 2 (15.4.5.1 step 4.a) -15.2.3.7-6-a-179: FAIL -# Bug? Object.defineProperties - TypeError is thrown if 'O' is an Array, 'P' is -# an array index named property,[[Writable]] attribute of the length -# property in 'O' is false, value of 'P' is equal to value of the length -# property in 'O' (15.4.5.1 step 4.b) -15.2.3.7-6-a-184: FAIL -# Bug? Object.defineProperties - TypeError is thrown if 'O' is an Array, 'P' is -# an array index named property,[[Writable]] attribute of the length -# property in 'O' is false, value of 'P' is bigger than value of the length -# property in 'O' (15.4.5.1 step 4.b) -15.2.3.7-6-a-185: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property, 'desc' is accessor descriptor, test updating all attribute -# values of 'P' (15.4.5.1 step 4.c) -15.2.3.7-6-a-205: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property that already exists on 'O' is accessor property and 'desc' is -# accessor descriptor, test updating the [[Enumerable]] attribute value of -# 'P' (15.4.5.1 step 4.c) -15.2.3.7-6-a-260: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property that already exists on 'O' is accessor property and 'desc' is -# accessor descriptor, test updating the [[Configurable]] attribute value -# of 'P' (15.4.5.1 step 4.c) -15.2.3.7-6-a-261: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property that already exists on 'O' is accessor property and 'desc' is -# accessor descriptor, test updating multiple attribute values of 'P' -# (15.4.5.1 step 4.c) -15.2.3.7-6-a-262: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property, test the length property of 'O' is set as ToUint32('P') + 1 if -# ToUint32('P') equals to value of the length property in 'O' (15.4.5.1 -# step 4.e.ii) -15.2.3.7-6-a-264: FAIL -# Bug? Object.defineProperties - 'O' is an Array, 'P' is an array index named -# property, test the length property of 'O' is set as ToUint32('P') + 1 if -# ToUint32('P') is greater than value of the length property in 'O' -# (15.4.5.1 step 4.e.ii) -15.2.3.7-6-a-265: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own accessor -# property of 'O' which is also defined in [[ParameterMap]] of 'O', and -# 'desc' is accessor descriptor, test updating multiple attribute values of -# 'P' (10.6 [[DefineOwnProperty]] step 3) -15.2.3.7-6-a-280: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own data -# property of 'O' which is also defined in [[ParameterMap]] of 'O', and -# 'desc' is data descriptor, test updating multiple attribute values of 'P' -# (10.6 [[DefineOwnProperty]] step 3) 15.2.3.7-6-a-281: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own data -# property of 'O' which is also defined in [[ParameterMap]] of 'O', test -# TypeError is thrown when updating the [[Value]] attribute value of 'P' -# whose writable and configurable attributes are false (10.6 -# [[DefineOwnProperty]] step 4) 15.2.3.7-6-a-282: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own data -# property of 'O' which is also defined in [[ParameterMap]] of 'O', test -# TypeError is thrown when updating the [[Writable]] attribute value of 'P' -# which is defined as non-configurable (10.6 [[DefineOwnProperty]] step 4) 15.2.3.7-6-a-283: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own data -# property of 'O' which is also defined in [[ParameterMap]] of 'O', test -# TypeError is thrown when updating the [[Enumerable]] attribute value of -# 'P' which is defined as non-configurable (10.6 [[DefineOwnProperty]] step -# 4) 15.2.3.7-6-a-284: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is own data -# property of 'O' which is also defined in [[ParameterMap]] of 'O', test -# TypeError is thrown when updating the [[Configurable]] attribute value of -# 'P' which is defined as non-configurable (10.6 [[DefineOwnProperty]] step -# 4) 15.2.3.7-6-a-285: FAIL -# Bug? Object.defineProperties - 'O' is an Arguments object, 'P' is an array -# index named accessor property of 'O' but not defined in [[ParameterMap]] -# of 'O', and 'desc' is accessor descriptor, test updating multiple -# attribute values of 'P' (10.6 [[DefineOwnProperty]] step 3) -15.2.3.7-6-a-292: FAIL -# Bug? Strict Mode - 'this' value is a string which cannot be converted to -# wrapper objects when the function is called with an array of arguments -15.3.4.3-1-s: FAIL -# Bug? Strict Mode - 'this' value is a number which cannot be converted to -# wrapper objects when the function is called with an array of arguments -15.3.4.3-2-s: FAIL -# Bug? Strict Mode - 'this' value is a boolean which cannot be converted to -# wrapper objects when the function is called with an array of arguments -15.3.4.3-3-s: FAIL -# Bug? Function.prototype.bind - [[Get]] attribute of 'caller' property in 'F' -# is thrower -15.3.4.5-20-2: FAIL -# Bug? Function.prototype.bind - [[Set]] attribute of 'caller' property in 'F' -# is thrower -15.3.4.5-20-3: FAIL -# Bug? Function.prototype.bind - [[Get]] attribute of 'arguments' property in -# 'F' is thrower -15.3.4.5-21-2: FAIL -# Bug? Function.prototype.bind - [[Set]] attribute of 'arguments' property in -# 'F' is thrower -15.3.4.5-21-3: FAIL -# Bug? Array.prototype.indexOf - decreasing length of array does not delete -# non-configurable properties -15.4.4.14-9-a-19: FAIL -# Bug? Array.prototype.indexOf - element to be retrieved is own accessor -# property that overrides an inherited data property on an Array -15.4.4.14-9-b-i-11: FAIL -# Bug? Array.prototype.indexOf - element to be retrieved is own accessor -# property that overrides an inherited accessor property on an Array -15.4.4.14-9-b-i-13: FAIL -# Bug? Array.prototype.indexOf - element to be retrieved is own accessor -# property without a get function on an Array -15.4.4.14-9-b-i-17: FAIL -# Bug? Array.prototype.indexOf - element to be retrieved is own accessor -# property without a get function that overrides an inherited accessor -# property on an Array -15.4.4.14-9-b-i-19: FAIL -# Bug? Array.prototype.indexOf - side-effects are visible in subsequent -# iterations on an Array -15.4.4.14-9-b-i-28: FAIL -# Bug? Array.prototype.indexOf - terminates iteration on unhandled exception on -# an Array -15.4.4.14-9-b-i-30: FAIL -# Bug? Array.prototype.lastIndexOf - deleting property of prototype causes -# prototype index property not to be visited on an Array -15.4.4.15-8-a-14: FAIL -# Bug? Array.prototype.lastIndexOf - decreasing length of array does not delete -# non-configurable properties -15.4.4.15-8-a-19: FAIL -# Bug? Array.prototype.lastIndexOf - element to be retrieved is own accessor -# property that overrides an inherited data property on an Array -15.4.4.15-8-b-i-11: FAIL -# Bug? Array.prototype.lastIndexOf - element to be retrieved is own accessor -# property that overrides an inherited accessor property on an Array -15.4.4.15-8-b-i-13: FAIL -# Bug? Array.prototype.lastIndexOf - element to be retrieved is own accessor -# property without a get function on an Array -15.4.4.15-8-b-i-17: FAIL -# Bug? Array.prototype.lastIndexOf - side-effects are visible in subsequent -# iterations on an Array -15.4.4.15-8-b-i-28: FAIL -# Bug? Array.prototype.lastIndexOf terminates iteration on unhandled exception -# on an Array -15.4.4.15-8-b-i-30: FAIL -# Bug? Array.prototype.every applied to boolean primitive -15.4.4.16-1-3: FAIL -# Bug? Array.prototype.every applied to number primitive -15.4.4.16-1-5: FAIL -# Bug? Array.prototype.every applied to string primitive -15.4.4.16-1-7: FAIL -# Bug? Array.prototype.every - side effects produced by step 2 are visible when -# an exception occurs -15.4.4.16-4-8: FAIL -# Bug? Array.prototype.every - side effects produced by step 3 are visible when -# an exception occurs -15.4.4.16-4-9: FAIL -# Bug? Array.prototype.every - the exception is not thrown if exception was -# thrown by step 2 -15.4.4.16-4-10: FAIL -# Bug? Array.prototype.every - the exception is not thrown if exception was -# thrown by step 3 -15.4.4.16-4-11: FAIL -# Bug? Array.prototype.every - calling with no callbackfn is the same as passing -# undefined for callbackfn -15.4.4.16-4-15: FAIL -# Bug? Array.prototype.every - decreasing length of array does not delete -# non-configurable properties -15.4.4.16-7-b-16: FAIL -# Bug? Array.prototype.every - element to be retrieved is own accessor property -# on an Array -15.4.4.16-7-c-i-10: FAIL -# Bug? Array.prototype.every - element to be retrieved is own accessor property -# that overrides an inherited data property on an Array -15.4.4.16-7-c-i-12: FAIL -# Bug? Array.prototype.every - element to be retrieved is own accessor property -# that overrides an inherited accessor property on an Array -15.4.4.16-7-c-i-14: FAIL -# Bug? Array.prototype.every - element to be retrieved is own accessor property -# without a get function on an Array -15.4.4.16-7-c-i-18: FAIL -# Bug? Array.prototype.every - element to be retrieved is own accessor property -# without a get function that overrides an inherited accessor property on -# an Array -15.4.4.16-7-c-i-20: FAIL -# Bug? Array.prototype.every - element changed by getter on previous iterations -# is observed on an Array -15.4.4.16-7-c-i-28: FAIL -# Bug? Array.prototype.some applied to boolean primitive -15.4.4.17-1-3: FAIL -# Bug? Array.prototype.some applied to number primitive -15.4.4.17-1-5: FAIL -# Bug? Array.prototype.some applied to applied to string primitive -15.4.4.17-1-7: FAIL -# Bug? Array.prototype.some - side effects produced by step 2 are visible when -# an exception occurs -15.4.4.17-4-8: FAIL -# Bug? Array.prototype.some - side effects produced by step 3 are visible when -# an exception occurs -15.4.4.17-4-9: FAIL -# Bug? Array.prototype.some - the exception is not thrown if exception was -# thrown by step 2 -15.4.4.17-4-10: FAIL -# Bug? Array.prototype.some - the exception is not thrown if exception was -# thrown by step 3 -15.4.4.17-4-11: FAIL -# Bug? Array.prototype.some - calling with no callbackfn is the same as passing -# undefined for callbackfn -15.4.4.17-4-15: FAIL -# Bug? Array.prototype.some - decreasing length of array does not delete -# non-configurable properties -15.4.4.17-7-b-16: FAIL -# Bug? Array.prototype.some - element to be retrieved is own accessor property -# on an Array -15.4.4.17-7-c-i-10: FAIL -# Bug? Array.prototype.some - element to be retrieved is own accessor property -# that overrides an inherited data property on an Array -15.4.4.17-7-c-i-12: FAIL -# Bug? Array.prototype.some - element to be retrieved is own accessor property -# that overrides an inherited accessor property on an Array -15.4.4.17-7-c-i-14: FAIL -# Bug? Array.prototype.some - element to be retrieved is own accessor property -# without a get function on an Array -15.4.4.17-7-c-i-18: FAIL -# Bug? Array.prototype.some - element to be retrieved is own accessor property -# without a get function that overrides an inherited accessor property on -# an Array -15.4.4.17-7-c-i-20: FAIL -# Bug? Array.prototype.some - element changed by getter on previous iterations -# is observed on an Array -15.4.4.17-7-c-i-28: FAIL -# Bug? Array.prototype.forEach applied to boolean primitive -15.4.4.18-1-3: FAIL -# Bug? Array.prototype.forEach applied to number primitive -15.4.4.18-1-5: FAIL -# Bug? Array.prototype.forEach applied to string primitive -15.4.4.18-1-7: FAIL -# Bug? Array.prototype.forEach - side effects produced by step 2 are visible -# when an exception occurs -15.4.4.18-4-8: FAIL -# Bug? Array.prototype.forEach - side effects produced by step 3 are visible -# when an exception occurs -15.4.4.18-4-9: FAIL -# Bug? Array.prototype.forEach - the exception is not thrown if exception was -# thrown by step 2 -15.4.4.18-4-10: FAIL -# Bug? Array.prototype.forEach - the exception is not thrown if exception was -# thrown by step 3 -15.4.4.18-4-11: FAIL -# Bug? Array.prototype.forEach - calling with no callbackfn is the same as -# passing undefined for callbackfn -15.4.4.18-4-15: FAIL -# Bug? Array.prototype.forEach - decreasing length of array does not delete -# non-configurable properties -15.4.4.18-7-b-16: FAIL -# Bug? Array.prototype.forEach - element to be retrieved is own accessor -# property on an Array -15.4.4.18-7-c-i-10: FAIL -# Bug? Array.prototype.forEach - element to be retrieved is own accessor -# property that overrides an inherited data property on an Array -15.4.4.18-7-c-i-12: FAIL -# Bug? Array.prototype.forEach - element to be retrieved is own accessor -# property that overrides an inherited accessor property on an Array -15.4.4.18-7-c-i-14: FAIL -# Bug? Array.prototype.forEach - element to be retrieved is own accessor -# property without a get function on an Array -15.4.4.18-7-c-i-18: FAIL -# Bug? Array.prototype.forEach - element to be retrieved is own accessor -# property without a get function that overrides an inherited accessor -# property on an Array -15.4.4.18-7-c-i-20: FAIL -# Bug? Array.prototype.forEach - element changed by getter on previous -# iterations is observed on an Array -15.4.4.18-7-c-i-28: FAIL -# Bug? Array.prototype.map - applied to boolean primitive -15.4.4.19-1-3: FAIL -# Bug? Array.prototype.map - applied to number primitive -15.4.4.19-1-5: FAIL -# Bug? Array.prototype.map - applied to string primitive -15.4.4.19-1-7: FAIL -# Bug? Array.prototype.map - Side effects produced by step 2 are visible when an -# exception occurs -15.4.4.19-4-8: FAIL -# Bug? Array.prototype.map - Side effects produced by step 3 are visible when an -# exception occurs -15.4.4.19-4-9: FAIL -# Bug? Array.prototype.map - the exception is not thrown if exception was thrown -# by step 2 -15.4.4.19-4-10: FAIL -# Bug? Array.prototype.map - the exception is not thrown if exception was thrown -# by step 3 -15.4.4.19-4-11: FAIL -# Bug? Array.prototype.map - calling with no callbackfn is the same as passing -# undefined for callbackfn -15.4.4.19-4-15: FAIL -# Bug? Array.prototype.map - decreasing length of array does not delete -# non-configurable properties -15.4.4.19-8-b-16: FAIL -# Bug? Array.prototype.map - element to be retrieved is own accessor property on -# an Array -15.4.4.19-8-c-i-10: FAIL -# Bug? Array.prototype.map - element to be retrieved is own accessor property -# that overrides an inherited data property on an Array -15.4.4.19-8-c-i-12: FAIL -# Bug? Array.prototype.map - element to be retrieved is own accessor property -# that overrides an inherited accessor property on an Array -15.4.4.19-8-c-i-14: FAIL -# Bug? Array.prototype.map - element to be retrieved is own accessor property -# without a get function on an Array -15.4.4.19-8-c-i-18: FAIL -# Bug? Array.prototype.map - element to be retrieved is own accessor property -# without a get function that overrides an inherited accessor property on -# an Array -15.4.4.19-8-c-i-19: FAIL -# Bug? Array.prototype.map - element changed by getter on previous iterations is -# observed on an Array -15.4.4.19-8-c-i-28: FAIL -# Bug? Array.prototype.filter applied to boolean primitive -15.4.4.20-1-3: FAIL -# Bug? Array.prototype.filter applied to number primitive -15.4.4.20-1-5: FAIL -# Bug? Array.prototype.filter applied to string primitive -15.4.4.20-1-7: FAIL -# Bug? Array.prototype.filter - side effects produced by step 2 are visible when -# an exception occurs -15.4.4.20-4-8: FAIL -# Bug? Array.prototype.filter - side effects produced by step 3 are visible when -# an exception occurs -15.4.4.20-4-9: FAIL -# Bug? Array.prototype.filter - the exception is not thrown if exception was -# thrown by step 2 -15.4.4.20-4-10: FAIL -# Bug? Array.prototype.filter - the exception is not thrown if exception was -# thrown by step 3 -15.4.4.20-4-11: FAIL -# Bug? Array.prototype.filter - calling with no callbackfn is the same as -# passing undefined for callbackfn -15.4.4.20-4-15: FAIL -# Bug? Array.prototype.filter - properties can be added to prototype after -# current position are visited on an Array-like object -15.4.4.20-9-b-6: FAIL -# Bug? Array.prototype.filter - decreasing length of array does not delete -# non-configurable properties -15.4.4.20-9-b-16: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own data property -# that overrides an inherited accessor property on an Array -15.4.4.20-9-c-i-6: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own accessor property -# on an Array -15.4.4.20-9-c-i-10: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own accessor property -# that overrides an inherited data property on an Array -15.4.4.20-9-c-i-12: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own accessor property -# that overrides an inherited accessor property on an Array -15.4.4.20-9-c-i-14: FAIL -# Bug? Array.prototype.filter - element to be retrieved is inherited accessor -# property on an Array -15.4.4.20-9-c-i-16: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own accessor property -# without a get function on an Array -15.4.4.20-9-c-i-18: FAIL -# Bug? Array.prototype.filter - element to be retrieved is own accessor property -# without a get function that overrides an inherited accessor property on -# an Array -15.4.4.20-9-c-i-20: FAIL -# Bug? Array.prototype.filter - element to be retrieved is inherited accessor -# property without a get function on an Array -15.4.4.20-9-c-i-22: FAIL -# Bug? Array.prototype.filter - element changed by getter on previous iterations -# is observed on an Array -15.4.4.20-9-c-i-28: FAIL -# Bug? Array.prototype.reduce applied to boolean primitive -15.4.4.21-1-3: FAIL -# Bug? Array.prototype.reduce applied to number primitive -15.4.4.21-1-5: FAIL -# Bug? Array.prototype.reduce applied to string primitive -15.4.4.21-1-7: FAIL -# Bug? Array.prototype.reduce - side effects produced by step 2 are visible when -# an exception occurs -15.4.4.21-4-8: FAIL -# Bug? Array.prototype.reduce - side effects produced by step 3 are visible when -# an exception occurs -15.4.4.21-4-9: FAIL -# Bug? Array.prototype.reduce - the exception is not thrown if exception was -# thrown by step 2 -15.4.4.21-4-10: FAIL -# Bug? Array.prototype.reduce - the exception is not thrown if exception was -# thrown by step 3 -15.4.4.21-4-11: FAIL -# Bug? Array.prototype.reduce - calling with no callbackfn is the same as -# passing undefined for callbackfn -15.4.4.21-4-15: FAIL -# Bug? Array.prototype.reduce - decreasing length of array in step 8 does not -# delete non-configurable properties -15.4.4.21-9-b-16: FAIL -# Bug? Array.prototype.reduce - decreasing length of array does not delete -# non-configurable properties -15.4.4.21-9-b-29: FAIL -# Bug? Array.prototype.reduceRight applied to boolean primitive -15.4.4.22-1-3: FAIL -# Bug? Array.prototype.reduceRight applied to number primitive -15.4.4.22-1-5: FAIL -# Bug? Array.prototype.reduceRight applied to string primitive -15.4.4.22-1-7: FAIL -# Bug? Array.prototype.reduceRight - side effects produced by step 2 are visible -# when an exception occurs -15.4.4.22-4-8: FAIL -# Bug? Array.prototype.reduceRight - side effects produced by step 3 are visible -# when an exception occurs -15.4.4.22-4-9: FAIL -# Bug? Array.prototype.reduceRight - the exception is not thrown if exception -# was thrown by step 2 -15.4.4.22-4-10: FAIL -# Bug? Array.prototype.reduceRight - the exception is not thrown if exception -# was thrown by step 3 -15.4.4.22-4-11: FAIL -# Bug? Array.prototype.reduceRight - calling with no callbackfn is the same as -# passing undefined for callbackfn -15.4.4.22-4-15: FAIL -# Bug? Array.prototype.reduceRight - element to be retrieved is own accessor -# property that overrides an inherited data property on an Array -15.4.4.22-8-b-iii-1-12: FAIL -# Bug? Array.prototype.reduceRight - element to be retrieved is own accessor -# property without a get function on an Array -15.4.4.22-8-b-iii-1-18: FAIL -# Bug? Array.prototype.reduceRight - element to be retrieved is own accessor -# property without a get function that overrides an inherited accessor -# property on an Array -15.4.4.22-8-b-iii-1-20: FAIL -# Bug? Array.prototype.reduceRight - element changed by getter on current -# iteration is observed in subsequent iterations on an Array -15.4.4.22-8-b-iii-1-30: FAIL -# Bug? Array.prototype.reduceRight - Exception in getter terminate iteration on -# an Array -15.4.4.22-8-b-iii-1-33: FAIL -# Bug? Array.prototype.reduceRight - modifications to length don't change number -# of iterations in step 9 -15.4.4.22-8-b-2: FAIL -# Bug? Array.prototype.reduceRight - deleting own property in step 8 causes -# deleted index property not to be visited on an Array -15.4.4.22-9-b-9: FAIL -# Bug? Array.prototype.reduceRight - deleting own property with prototype -# property in step 8 causes prototype index property to be visited on an -# Array -15.4.4.22-9-b-13: FAIL -# Bug? Array.prototype.reduceRight - decreasing length of array in step 8 does -# not delete non-configurable properties -15.4.4.22-9-b-16: FAIL -# Bug? Array.prototype.reduceRight - deleting property of prototype causes -# deleted index property not to be visited on an Array -15.4.4.22-9-b-24: FAIL -# Bug? Array.prototype.reduceRight - deleting own property with prototype -# property causes prototype index property to be visited on an Array -15.4.4.22-9-b-26: FAIL -# Bug? Array.prototype.reduceRight - decreasing length of array does not delete -# non-configurable properties -15.4.4.22-9-b-29: FAIL -# Bug? Array.prototype.reduceRight - element changed by getter on previous -# iterations is observed on an Array -15.4.4.22-9-c-i-30: FAIL -# Bug? Array.prototype.reduceRight - modifications to length will change number -# of iterations + +# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1790 15.4.4.22-9-9: FAIL -# Bug? String.prototype.trim - 'S' is a string with all WhiteSpace -15.5.4.20-3-2: FAIL -# Bug? String.prototype.trim - 'S' is a string with all union of WhiteSpace and -# LineTerminator -15.5.4.20-3-3: FAIL -# Bug? String.prototype.trim - 'S' is a string start with union of all -# LineTerminator and all WhiteSpace -15.5.4.20-3-4: FAIL -# Bug? String.prototype.trim - 'S' is a string end with union of all -# LineTerminator and all WhiteSpace -15.5.4.20-3-5: FAIL -# Bug? String.prototype.trim - 'S' is a string start with union of all -# LineTerminator and all WhiteSpace and end with union of all -# LineTerminator and all WhiteSpace -15.5.4.20-3-6: FAIL -# Bug? String.prototype.trim handles whitepace and lineterminators (\\uFEFFabc) -15.5.4.20-4-10: FAIL -# Bug? String.prototype.trim handles whitepace and lineterminators (abc\\uFEFF) -15.5.4.20-4-18: FAIL -# Bug? String.prototype.trim handles whitepace and lineterminators -# (\\uFEFF\\uFEFF) -15.5.4.20-4-34: FAIL -# Bug? Date.prototype.toISOString - RangeError is thrown when value of date is -# Date(1970, 0, -99999999, 0, 0, 0, -1), the time zone is UTC(0) -15.9.5.43-0-8: FAIL -# Bug? Date.prototype.toISOString - RangeError is not thrown when value of date -# is Date(1970, 0, 100000001, 0, 0, 0, -1), the time zone is UTC(0) -15.9.5.43-0-11: FAIL -# Bug? Date.prototype.toISOString - RangeError is not thrown when value of date -# is Date(1970, 0, 100000001, 0, 0, 0, 0), the time zone is UTC(0) -15.9.5.43-0-12: FAIL -# Bug? Date.prototype.toISOString - RangeError is thrown when value of date is -# Date(1970, 0, 100000001, 0, 0, 0, 1), the time zone is UTC(0) -15.9.5.43-0-13: FAIL -# Bug? Date.prototype.toISOString - when value of year is -Infinity -# Date.prototype.toISOString throw the RangeError -15.9.5.43-0-14: FAIL -# Bug? Date.prototype.toISOString - value of year is Infinity -# Date.prototype.toISOString throw the RangeError -15.9.5.43-0-15: FAIL -# Bug? RegExp - the thrown error is SyntaxError instead of RegExpError when 'F' -# contains any character other than 'g', 'i', or 'm' -15.10.4.1-3: FAIL -# Bug? RegExp.prototype is itself a RegExp -15.10.6: FAIL -# Bug? RegExp.prototype.source is of type String -15.10.7.1-1: FAIL -# Bug? RegExp.prototype.source is a data property with default attribute values -# (false) -15.10.7.1-2: FAIL -# Bug? RegExp.prototype.global is of type Boolean -15.10.7.2-1: FAIL -# Bug? RegExp.prototype.global is a data property with default attribute values -# (false) -15.10.7.2-2: FAIL -# Bug? RegExp.prototype.ignoreCase is of type Boolean -15.10.7.3-1: FAIL -# Bug? RegExp.prototype.ignoreCase is a data property with default attribute -# values (false) -15.10.7.3-2: FAIL -# Bug? RegExp.prototype.multiline is of type Boolean -15.10.7.4-1: FAIL -# Bug? RegExp.prototype.multiline is a data property with default attribute -# values (false) -15.10.7.4-2: FAIL -# Bug? RegExp.prototype.lastIndex is of type Number -15.10.7.5-1: FAIL -# Bug? RegExp.prototype.lastIndex is a data property with specified attribute -# values -15.10.7.5-2: FAIL -# Bug? Error.prototype.toString return the value of 'msg' when 'name' is empty -# string and 'msg' isn't undefined -15.11.4.4-8-1: FAIL + +# Invalid test cases (recent change adding var changes semantics) +S8.3_A1_T1: FAIL +S15.3_A3_T1: FAIL +S15.3_A3_T3: FAIL + +##################### DELIBERATE INCOMPATIBILITIES ##################### + +# We deliberately treat arguments to parseInt() with a leading zero as +# octal numbers in order to not break the web. +S15.1.2.2_A5.1_T1: FAIL_OK + +# This tests precision of trignometric functions. We're slightly off +# from the implementation in libc (~ 1e-17) but it's not clear if we +# or they are closer to the right answer, or if it even matters. +S15.8.2.16_A7: PASS || FAIL_OK +S15.8.2.18_A7: PASS || FAIL_OK +S15.8.2.13_A23: PASS || FAIL_OK + +# Sputnik tests (r97) assume RegExp.prototype is an Object, not a RegExp. +S15.10.6_A2: FAIL_OK + +# We are silent in some regexp cases where the spec wants us to give +# errors, for compatibility. +S15.10.2.11_A1_T2: FAIL +S15.10.2.11_A1_T3: FAIL + +# We are more lenient in which string character escapes we allow than +# the spec (7.8.4 p. 19) wants us to be. This is for compatibility. +S7.8.4_A4.3_T3: FAIL_OK +S7.8.4_A4.3_T4: FAIL_OK +S7.8.4_A4.3_T5: FAIL_OK +S7.8.4_A4.3_T6: FAIL_OK +S7.8.4_A6.1_T4: FAIL_OK +S7.8.4_A6.2_T1: FAIL_OK +S7.8.4_A6.2_T2: FAIL_OK +S7.8.4_A6.4_T1: FAIL_OK +S7.8.4_A6.4_T2: FAIL_OK +S7.8.4_A7.1_T4: FAIL_OK +S7.8.4_A7.2_T1: FAIL_OK +S7.8.4_A7.2_T2: FAIL_OK +S7.8.4_A7.2_T3: FAIL_OK +S7.8.4_A7.2_T4: FAIL_OK +S7.8.4_A7.2_T5: FAIL_OK +S7.8.4_A7.2_T6: FAIL_OK +S7.8.4_A7.4_T1: FAIL_OK +S7.8.4_A7.4_T2: FAIL_OK + +# Sputnik expects unicode escape sequences in RegExp flags to be interpreted. +# The specification requires them to be passed uninterpreted to the RegExp +# constructor. We now implement that. +S7.8.5_A3.1_T7: FAIL_OK +S7.8.5_A3.1_T8: FAIL_OK +S7.8.5_A3.1_T9: FAIL_OK + +# We allow some keywords to be used as identifiers. +S7.5.3_A1.15: FAIL_OK +S7.5.3_A1.18: FAIL_OK +S7.5.3_A1.21: FAIL_OK +S7.5.3_A1.22: FAIL_OK +S7.5.3_A1.23: FAIL_OK +S7.5.3_A1.24: FAIL_OK +S7.5.3_A1.26: FAIL_OK + +# This checks for non-262 behavior +S7.6_D1: PASS || FAIL_OK +S7.6_D2: PASS || FAIL_OK +S8.4_D1.1: PASS || FAIL_OK +S8.4_D2.1: PASS || FAIL_OK +S8.4_D2.2: PASS || FAIL_OK +S8.4_D2.3: PASS || FAIL_OK +S8.4_D2.4: PASS || FAIL_OK +S8.4_D2.5: PASS || FAIL_OK +S8.4_D2.6: PASS || FAIL_OK +S8.4_D2.7: PASS || FAIL_OK +S11.4.3_D1.2: PASS || FAIL_OK +S12.6.4_A14_T1: PASS || FAIL_OK +S12.6.4_D1: PASS || FAIL_OK +S12.6.4_R1: PASS || FAIL_OK +S12.6.4_R2: PASS || FAIL_OK +S13.2_D1.2: PASS || FAIL_OK +S13_D1_T1: PASS || FAIL_OK +S14_D4_T3: PASS || FAIL_OK +S14_D7: PASS || FAIL_OK +S15.1.2.2_D1.2: PASS || FAIL_OK +S15.5.2_D2: PASS || FAIL_OK +S15.5.4.11_D1.1_T1: PASS || FAIL_OK +S15.5.4.11_D1.1_T2: PASS || FAIL_OK +S15.5.4.11_D1.1_T3: PASS || FAIL_OK +S15.5.4.11_D1.1_T4: PASS || FAIL_OK + +# We allow function declarations within statements +S12.6.2_A13_T1: FAIL_OK +S12.6.2_A13_T2: FAIL_OK +S12.6.4_A13_T1: FAIL_OK +S12.6.4_A13_T2: FAIL_OK +S15.3.4.2_A1_T1: FAIL_OK + +# Linux and Mac defaults to extended 80 bit floating point format in the FPU. +# We follow the other major JS engines by keeping this default. +S8.5_A2.2: PASS, FAIL if $system == linux, FAIL if $system == macos +S8.5_A2.1: PASS, FAIL if $system == linux, FAIL if $system == macos + +############################# ES3 TESTS ################################ +# These tests check for ES3 semantics, and differ from ES5. +# When we follow ES5 semantics, it's ok to fail the test. + +# Allow keywords as names of properties in object initialisers and +# in dot-notation property access. +S11.1.5_A4.1: FAIL_OK +S11.1.5_A4.2: FAIL_OK + +# Calls builtins without an explicit receiver which means that +# undefined is passed to the builtin. The tests expect the global +# object to be passed which was true in ES3 but not in ES5. +S11.1.1_A2: FAIL_OK +S15.5.4.4_A1_T3: FAIL_OK +S15.5.4.5_A1_T3: FAIL_OK +S15.5.4.6_A1_T3: FAIL_OK +S15.5.4.7_A1_T3: FAIL_OK +S15.5.4.8_A1_T3: FAIL_OK +S15.5.4.9_A1_T3: FAIL_OK +S15.5.4.10_A1_T3: FAIL_OK +S15.5.4.11_A1_T3: FAIL_OK +S15.5.4.12_A1_T3: FAIL_OK +S15.5.4.13_A1_T3: FAIL_OK +S15.5.4.14_A1_T3: FAIL_OK +S15.5.4.15_A1_T3: FAIL_OK + +# NaN, Infinity and undefined are read-only according to ES5. +S15.1.1.1_A2_T1: FAIL_OK # NaN +S15.1.1.1_A2_T2: FAIL_OK # NaN +S15.1.1.2_A2_T1: FAIL_OK # Infinity +# S15.1.1.2_A2_T2 would fail if it weren't bogus in r97. sputnik bug #45. +S15.1.1.3_A2_T1: FAIL_OK # undefined +S15.1.1.3_A2_T2: FAIL_OK # undefined + +# Array.prototype.to[Locale]String is generic in ES5. +S15.4.4.2_A2_T1: FAIL_OK +S15.4.4.3_A2_T1: FAIL_OK + +############################ SKIPPED TESTS ############################# + +# These tests take a looong time to run in debug mode. +S15.1.3.2_A2.5_T1: PASS, SKIP if $mode == debug +S15.1.3.1_A2.5_T1: PASS, SKIP if $mode == debug + +[ $arch == arm ] + +# BUG(3251225): Tests that timeout with --nocrankshaft. +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.1_A2.4_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.4_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.3_A2.3_T1: SKIP +S15.1.3.4_A2.3_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP + +[ $arch == mips ] + +# BUG(3251225): Tests that timeout with --nocrankshaft. +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.1_A2.4_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.4_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP +S15.1.3.3_A2.3_T1: SKIP +S15.1.3.4_A2.3_T1: SKIP +S15.1.3.1_A2.5_T1: SKIP +S15.1.3.2_A2.5_T1: SKIP diff --git a/deps/v8/test/test262/testcfg.py b/deps/v8/test/test262/testcfg.py index 9482046034..aefda196a3 100644 --- a/deps/v8/test/test262/testcfg.py +++ b/deps/v8/test/test262/testcfg.py @@ -43,10 +43,10 @@ class Test262TestCase(test.TestCase): self.root = root def IsNegative(self): - return self.filename.endswith('-n.js') + return '@negative' in self.GetSource() def GetLabel(self): - return "%s test262 %s %s" % (self.mode, self.GetGroup(), self.GetName()) + return "%s test262 %s" % (self.mode, self.GetName()) def IsFailureOutput(self, output): if output.exit_code != 0: @@ -55,7 +55,6 @@ class Test262TestCase(test.TestCase): def GetCommand(self): result = self.context.GetVmCommand(self, self.mode) - result += ['-e', 'var window = this'] result += self.framework result.append(self.filename) return result @@ -63,9 +62,6 @@ class Test262TestCase(test.TestCase): def GetName(self): return self.path[-1] - def GetGroup(self): - return self.path[0] - def GetSource(self): return open(self.filename).read() @@ -75,13 +71,14 @@ class Test262TestConfiguration(test.TestConfiguration): def __init__(self, context, root): super(Test262TestConfiguration, self).__init__(context, root) - def AddIETestCenter(self, tests, current_path, path, mode): - current_root = join(self.root, 'data', 'test', 'suite', 'ietestcenter') + def ListTests(self, current_path, path, mode, variant_flags): + testroot = join(self.root, 'data', 'test', 'suite') harness = [join(self.root, 'data', 'test', 'harness', f) for f in TEST_262_HARNESS] harness += [join(self.root, 'harness-adapt.js')] - for root, dirs, files in os.walk(current_root): - for dotted in [x for x in dirs if x.startswith('.')]: + tests = [] + for root, dirs, files in os.walk(testroot): + for dotted in [x for x in dirs if x.startswith('.')]: dirs.remove(dotted) dirs.sort() root_path = root[len(self.root):].split(os.path.sep) @@ -89,25 +86,11 @@ class Test262TestConfiguration(test.TestConfiguration): files.sort() for file in files: if file.endswith('.js'): - if self.Contains(path, root_path): - test_path = ['ietestcenter', file[:-3]] + test_path = ['test262', file[:-3]] + if self.Contains(path, test_path): test = Test262TestCase(join(root, file), test_path, self.context, self.root, mode, harness) tests.append(test) - - def AddSputnikConvertedTests(self, tests, current_path, path, mode): - # To be enabled - pass - - def AddSputnikTests(self, tests, current_path, path, mode): - # To be enabled - pass - - def ListTests(self, current_path, path, mode, variant_flags): - tests = [] - self.AddIETestCenter(tests, current_path, path, mode) - self.AddSputnikConvertedTests(tests, current_path, path, mode) - self.AddSputnikTests(tests, current_path, path, mode) return tests def GetBuildRequirements(self): diff --git a/deps/v8/tools/bash-completion.sh b/deps/v8/tools/bash-completion.sh new file mode 100644 index 0000000000..9f65c67731 --- /dev/null +++ b/deps/v8/tools/bash-completion.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2012 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Inspired by and based on: +# http://src.chromium.org/viewvc/chrome/trunk/src/tools/bash-completion + +# Flag completion rule for bash. +# To load in your shell, "source path/to/this/file". + +v8_source=$(readlink -f $(dirname $BASH_SOURCE)/..) + +_v8_flag() { + local cur defines targets + cur="${COMP_WORDS[COMP_CWORD]}" + defines=$(cat src/flag-definitions.h \ + | grep "^DEFINE" \ + | grep -v "DEFINE_implication" \ + | sed -e 's/_/-/g') + targets=$(echo "$defines" \ + | sed -ne 's/^DEFINE-[^(]*(\([^,]*\).*/--\1/p'; \ + echo "$defines" \ + | sed -ne 's/^DEFINE-bool(\([^,]*\).*/--no\1/p'; \ + cat src/d8.cc \ + | grep "strcmp(argv\[i\]" \ + | sed -ne 's/^[^"]*"--\([^"]*\)".*/--\1/p') + COMPREPLY=($(compgen -W "$targets" -- "$cur")) + return 0 +} + +complete -F _v8_flag -f d8 diff --git a/deps/v8/tools/gc-nvp-trace-processor.py b/deps/v8/tools/gc-nvp-trace-processor.py index 511ab2bcdf..fe5a7f361e 100755 --- a/deps/v8/tools/gc-nvp-trace-processor.py +++ b/deps/v8/tools/gc-nvp-trace-processor.py @@ -219,13 +219,17 @@ def other_scope(r): if r['gc'] == 's': # there is no 'other' scope for scavenging collections. return 0 - return r['pause'] - r['mark'] - r['sweep'] - r['compact'] - r['external'] + return r['pause'] - r['mark'] - r['sweep'] - r['external'] def scavenge_scope(r): if r['gc'] == 's': return r['pause'] - r['external'] return 0 + +def real_mutator(r): + return r['mutator'] - r['stepstook'] + plots = [ [ Set('style fill solid 0.5 noborder'), @@ -234,9 +238,24 @@ plots = [ Plot(Item('Scavenge', scavenge_scope, lc = 'green'), Item('Marking', 'mark', lc = 'purple'), Item('Sweep', 'sweep', lc = 'blue'), - Item('Compaction', 'compact', lc = 'red'), Item('External', 'external', lc = '#489D43'), - Item('Other', other_scope, lc = 'grey')) + Item('Other', other_scope, lc = 'grey'), + Item('IGC Steps', 'stepstook', lc = '#FF6347')) + ], + [ + Set('style fill solid 0.5 noborder'), + Set('style histogram rowstacked'), + Set('style data histograms'), + Plot(Item('Scavenge', scavenge_scope, lc = 'green'), + Item('Marking', 'mark', lc = 'purple'), + Item('Sweep', 'sweep', lc = 'blue'), + Item('External', 'external', lc = '#489D43'), + Item('Other', other_scope, lc = '#ADD8E6'), + Item('External', 'external', lc = '#D3D3D3')) + ], + + [ + Plot(Item('Mutator', real_mutator, lc = 'black', style = 'lines')) ], [ Set('style histogram rowstacked'), @@ -275,7 +294,7 @@ def freduce(f, field, trace, init): return reduce(lambda t,r: f(t, r[field]), trace, init) def calc_total(trace, field): - return freduce(lambda t,v: t + v, field, trace, 0) + return freduce(lambda t,v: t + long(v), field, trace, long(0)) def calc_max(trace, field): return freduce(lambda t,r: max(t, r), field, trace, 0) @@ -288,8 +307,9 @@ def process_trace(filename): trace = parse_gc_trace(filename) marksweeps = filter(lambda r: r['gc'] == 'ms', trace) - markcompacts = filter(lambda r: r['gc'] == 'mc', trace) scavenges = filter(lambda r: r['gc'] == 's', trace) + globalgcs = filter(lambda r: r['gc'] != 's', trace) + charts = plot_all(plots, trace, filename) @@ -302,7 +322,7 @@ def process_trace(filename): else: avg = 0 if n > 1: - dev = math.sqrt(freduce(lambda t,r: (r - avg) ** 2, field, trace, 0) / + dev = math.sqrt(freduce(lambda t,r: t + (r - avg) ** 2, field, trace, 0) / (n - 1)) else: dev = 0 @@ -311,6 +331,31 @@ def process_trace(filename): '<td>%d</td><td>%d [dev %f]</td></tr>' % (prefix, n, total, max, avg, dev)) + def HumanReadable(size): + suffixes = ['bytes', 'kB', 'MB', 'GB'] + power = 1 + for i in range(len(suffixes)): + if size < power*1024: + return "%.1f" % (float(size) / power) + " " + suffixes[i] + power *= 1024 + + def throughput(name, trace): + total_live_after = calc_total(trace, 'total_size_after') + total_live_before = calc_total(trace, 'total_size_before') + total_gc = calc_total(trace, 'pause') + if total_gc == 0: + return + out.write('GC %s Throughput (after): %s / %s ms = %s/ms<br/>' % + (name, + HumanReadable(total_live_after), + total_gc, + HumanReadable(total_live_after / total_gc))) + out.write('GC %s Throughput (before): %s / %s ms = %s/ms<br/>' % + (name, + HumanReadable(total_live_before), + total_gc, + HumanReadable(total_live_before / total_gc))) + with open(filename + '.html', 'w') as out: out.write('<html><body>') @@ -320,15 +365,17 @@ def process_trace(filename): stats(out, 'Total in GC', trace, 'pause') stats(out, 'Scavenge', scavenges, 'pause') stats(out, 'MarkSweep', marksweeps, 'pause') - stats(out, 'MarkCompact', markcompacts, 'pause') stats(out, 'Mark', filter(lambda r: r['mark'] != 0, trace), 'mark') stats(out, 'Sweep', filter(lambda r: r['sweep'] != 0, trace), 'sweep') - stats(out, 'Compact', filter(lambda r: r['compact'] != 0, trace), 'compact') stats(out, 'External', filter(lambda r: r['external'] != 0, trace), 'external') out.write('</table>') + throughput('TOTAL', trace) + throughput('MS', marksweeps) + throughput('OLDSPACE', globalgcs) + out.write('<br/>') for chart in charts: out.write('<img src="%s">' % chart) out.write('</body></html>') diff --git a/deps/v8/tools/gcmole/gccause.lua b/deps/v8/tools/gcmole/gccause.lua index a6fe542137..b9891767de 100644 --- a/deps/v8/tools/gcmole/gccause.lua +++ b/deps/v8/tools/gcmole/gccause.lua @@ -48,6 +48,8 @@ local function TrackCause(name, lvl) T[f] = true TrackCause(f, (lvl or 0) + 1) end + + if f == '<GC>' then break end end end end diff --git a/deps/v8/tools/gen-postmortem-metadata.py b/deps/v8/tools/gen-postmortem-metadata.py new file mode 100644 index 0000000000..4aa8f5d5e5 --- /dev/null +++ b/deps/v8/tools/gen-postmortem-metadata.py @@ -0,0 +1,478 @@ +#!/usr/bin/env python + +# +# Copyright 2012 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# Emits a C++ file to be compiled and linked into libv8 to support postmortem +# debugging tools. Most importantly, this tool emits constants describing V8 +# internals: +# +# v8dbg_type_CLASS__TYPE = VALUE Describes class type values +# v8dbg_class_CLASS__FIELD__TYPE = OFFSET Describes class fields +# v8dbg_parent_CLASS__PARENT Describes class hierarchy +# v8dbg_frametype_NAME = VALUE Describes stack frame values +# v8dbg_off_fp_NAME = OFFSET Frame pointer offsets +# v8dbg_prop_NAME = OFFSET Object property offsets +# v8dbg_NAME = VALUE Miscellaneous values +# +# These constants are declared as global integers so that they'll be present in +# the generated libv8 binary. +# + +import re +import sys + +# +# Miscellaneous constants, tags, and masks used for object identification. +# +consts_misc = [ + { 'name': 'FirstNonstringType', 'value': 'FIRST_NONSTRING_TYPE' }, + + { 'name': 'IsNotStringMask', 'value': 'kIsNotStringMask' }, + { 'name': 'StringTag', 'value': 'kStringTag' }, + { 'name': 'NotStringTag', 'value': 'kNotStringTag' }, + + { 'name': 'StringEncodingMask', 'value': 'kStringEncodingMask' }, + { 'name': 'TwoByteStringTag', 'value': 'kTwoByteStringTag' }, + { 'name': 'AsciiStringTag', 'value': 'kAsciiStringTag' }, + + { 'name': 'StringRepresentationMask', + 'value': 'kStringRepresentationMask' }, + { 'name': 'SeqStringTag', 'value': 'kSeqStringTag' }, + { 'name': 'ConsStringTag', 'value': 'kConsStringTag' }, + { 'name': 'ExternalStringTag', 'value': 'kExternalStringTag' }, + + { 'name': 'FailureTag', 'value': 'kFailureTag' }, + { 'name': 'FailureTagMask', 'value': 'kFailureTagMask' }, + { 'name': 'HeapObjectTag', 'value': 'kHeapObjectTag' }, + { 'name': 'HeapObjectTagMask', 'value': 'kHeapObjectTagMask' }, + { 'name': 'SmiTag', 'value': 'kSmiTag' }, + { 'name': 'SmiTagMask', 'value': 'kSmiTagMask' }, + { 'name': 'SmiValueShift', 'value': 'kSmiTagSize' }, + { 'name': 'PointerSizeLog2', 'value': 'kPointerSizeLog2' }, + + { 'name': 'prop_idx_content', + 'value': 'DescriptorArray::kContentArrayIndex' }, + { 'name': 'prop_idx_first', + 'value': 'DescriptorArray::kFirstIndex' }, + { 'name': 'prop_type_field', + 'value': 'FIELD' }, + { 'name': 'prop_type_first_phantom', + 'value': 'MAP_TRANSITION' }, + { 'name': 'prop_type_mask', + 'value': 'PropertyDetails::TypeField::kMask' }, + + { 'name': 'off_fp_context', + 'value': 'StandardFrameConstants::kContextOffset' }, + { 'name': 'off_fp_marker', + 'value': 'StandardFrameConstants::kMarkerOffset' }, + { 'name': 'off_fp_function', + 'value': 'JavaScriptFrameConstants::kFunctionOffset' }, + { 'name': 'off_fp_args', + 'value': 'JavaScriptFrameConstants::kLastParameterOffset' }, +]; + +# +# The following useful fields are missing accessors, so we define fake ones. +# +extras_accessors = [ + 'HeapObject, map, Map, kMapOffset', + 'JSObject, elements, Object, kElementsOffset', + 'FixedArray, data, uintptr_t, kHeaderSize', + 'Map, instance_attributes, int, kInstanceAttributesOffset', + 'Map, instance_descriptors, int, kInstanceDescriptorsOrBitField3Offset', + 'Map, inobject_properties, int, kInObjectPropertiesOffset', + 'Map, instance_size, int, kInstanceSizeOffset', + 'HeapNumber, value, double, kValueOffset', + 'ConsString, first, String, kFirstOffset', + 'ConsString, second, String, kSecondOffset', + 'ExternalString, resource, Object, kResourceOffset', + 'SeqAsciiString, chars, char, kHeaderSize', +]; + +# +# The following is a whitelist of classes we expect to find when scanning the +# source code. This list is not exhaustive, but it's still useful to identify +# when this script gets out of sync with the source. See load_objects(). +# +expected_classes = [ + 'ConsString', 'FixedArray', 'HeapNumber', 'JSArray', 'JSFunction', + 'JSObject', 'JSRegExp', 'JSValue', 'Map', 'Oddball', 'Script', + 'SeqAsciiString', 'SharedFunctionInfo' +]; + + +# +# The following structures store high-level representations of the structures +# for which we're going to emit descriptive constants. +# +types = {}; # set of all type names +typeclasses = {}; # maps type names to corresponding class names +klasses = {}; # known classes, including parents +fields = []; # field declarations + +header = ''' +/* + * This file is generated by %s. Do not edit directly. + */ + +#include "v8.h" +#include "frames.h" +#include "frames-inl.h" /* for architecture-specific frame constants */ + +using namespace v8::internal; + +extern "C" { + +/* stack frame constants */ +#define FRAME_CONST(value, klass) \ + int v8dbg_frametype_##klass = StackFrame::value; + +STACK_FRAME_TYPE_LIST(FRAME_CONST) + +#undef FRAME_CONST + +''' % sys.argv[0]; + +footer = ''' +} +''' + +# +# Loads class hierarchy and type information from "objects.h". +# +def load_objects(): + objfilename = sys.argv[2]; + objfile = open(objfilename, 'r'); + in_insttype = False; + + typestr = ''; + + # + # Construct a dictionary for the classes we're sure should be present. + # + checktypes = {}; + for klass in expected_classes: + checktypes[klass] = True; + + # + # Iterate objects.h line-by-line to collect type and class information. + # For types, we accumulate a string representing the entire InstanceType + # enum definition and parse it later because it's easier to do so + # without the embedded newlines. + # + for line in objfile: + if (line.startswith('enum InstanceType {')): + in_insttype = True; + continue; + + if (in_insttype and line.startswith('};')): + in_insttype = False; + continue; + + line = re.sub('//.*', '', line.rstrip().lstrip()); + + if (in_insttype): + typestr += line; + continue; + + match = re.match('class (\w[^\s:]*)(: public (\w[^\s{]*))?\s*{', + line); + + if (match): + klass = match.group(1); + pklass = match.group(3); + klasses[klass] = { 'parent': pklass }; + + # + # Process the instance type declaration. + # + entries = typestr.split(','); + for entry in entries: + types[re.sub('\s*=.*', '', entry).lstrip()] = True; + + # + # Infer class names for each type based on a systematic transformation. + # For example, "JS_FUNCTION_TYPE" becomes "JSFunction". We find the + # class for each type rather than the other way around because there are + # fewer cases where one type maps to more than one class than the other + # way around. + # + for type in types: + # + # Symbols and Strings are implemented using the same classes. + # + usetype = re.sub('SYMBOL_', 'STRING_', type); + + # + # REGEXP behaves like REG_EXP, as in JS_REGEXP_TYPE => JSRegExp. + # + usetype = re.sub('_REGEXP_', '_REG_EXP_', usetype); + + # + # Remove the "_TYPE" suffix and then convert to camel case, + # except that a "JS" prefix remains uppercase (as in + # "JS_FUNCTION_TYPE" => "JSFunction"). + # + if (not usetype.endswith('_TYPE')): + continue; + + usetype = usetype[0:len(usetype) - len('_TYPE')]; + parts = usetype.split('_'); + cctype = ''; + + if (parts[0] == 'JS'): + cctype = 'JS'; + start = 1; + else: + cctype = ''; + start = 0; + + for ii in range(start, len(parts)): + part = parts[ii]; + cctype += part[0].upper() + part[1:].lower(); + + # + # Mapping string types is more complicated. Both types and + # class names for Strings specify a representation (e.g., Seq, + # Cons, External, or Sliced) and an encoding (TwoByte or Ascii), + # In the simplest case, both of these are explicit in both + # names, as in: + # + # EXTERNAL_ASCII_STRING_TYPE => ExternalAsciiString + # + # However, either the representation or encoding can be omitted + # from the type name, in which case "Seq" and "TwoByte" are + # assumed, as in: + # + # STRING_TYPE => SeqTwoByteString + # + # Additionally, sometimes the type name has more information + # than the class, as in: + # + # CONS_ASCII_STRING_TYPE => ConsString + # + # To figure this out dynamically, we first check for a + # representation and encoding and add them if they're not + # present. If that doesn't yield a valid class name, then we + # strip out the representation. + # + if (cctype.endswith('String')): + if (cctype.find('Cons') == -1 and + cctype.find('External') == -1 and + cctype.find('Sliced') == -1): + if (cctype.find('Ascii') != -1): + cctype = re.sub('AsciiString$', + 'SeqAsciiString', cctype); + else: + cctype = re.sub('String$', + 'SeqString', cctype); + + if (cctype.find('Ascii') == -1): + cctype = re.sub('String$', 'TwoByteString', + cctype); + + if (not (cctype in klasses)): + cctype = re.sub('Ascii', '', cctype); + cctype = re.sub('TwoByte', '', cctype); + + # + # Despite all that, some types have no corresponding class. + # + if (cctype in klasses): + typeclasses[type] = cctype; + if (cctype in checktypes): + del checktypes[cctype]; + + if (len(checktypes) > 0): + for klass in checktypes: + print('error: expected class \"%s\" not found' % klass); + + sys.exit(1); + + +# +# For a given macro call, pick apart the arguments and return an object +# describing the corresponding output constant. See load_fields(). +# +def parse_field(call): + # Replace newlines with spaces. + for ii in range(0, len(call)): + if (call[ii] == '\n'): + call[ii] == ' '; + + idx = call.find('('); + kind = call[0:idx]; + rest = call[idx + 1: len(call) - 1]; + args = re.split('\s*,\s*', rest); + + consts = []; + + if (kind == 'ACCESSORS' or kind == 'ACCESSORS_GCSAFE'): + klass = args[0]; + field = args[1]; + dtype = args[2]; + offset = args[3]; + + return ({ + 'name': 'class_%s__%s__%s' % (klass, field, dtype), + 'value': '%s::%s' % (klass, offset) + }); + + assert(kind == 'SMI_ACCESSORS'); + klass = args[0]; + field = args[1]; + offset = args[2]; + + return ({ + 'name': 'class_%s__%s__%s' % (klass, field, 'SMI'), + 'value': '%s::%s' % (klass, offset) + }); + +# +# Load field offset information from objects-inl.h. +# +def load_fields(): + inlfilename = sys.argv[3]; + inlfile = open(inlfilename, 'r'); + + # + # Each class's fields and the corresponding offsets are described in the + # source by calls to macros like "ACCESSORS" (and friends). All we do + # here is extract these macro invocations, taking into account that they + # may span multiple lines and may contain nested parentheses. We also + # call parse_field() to pick apart the invocation. + # + prefixes = [ 'ACCESSORS', 'ACCESSORS_GCSAFE', 'SMI_ACCESSORS' ]; + current = ''; + opens = 0; + + for line in inlfile: + if (opens > 0): + # Continuation line + for ii in range(0, len(line)): + if (line[ii] == '('): + opens += 1; + elif (line[ii] == ')'): + opens -= 1; + + if (opens == 0): + break; + + current += line[0:ii + 1]; + continue; + + for prefix in prefixes: + if (not line.startswith(prefix + '(')): + continue; + + if (len(current) > 0): + fields.append(parse_field(current)); + current = ''; + + for ii in range(len(prefix), len(line)): + if (line[ii] == '('): + opens += 1; + elif (line[ii] == ')'): + opens -= 1; + + if (opens == 0): + break; + + current += line[0:ii + 1]; + + if (len(current) > 0): + fields.append(parse_field(current)); + current = ''; + + for body in extras_accessors: + fields.append(parse_field('ACCESSORS(%s)' % body)); + +# +# Emit a block of constants. +# +def emit_set(out, consts): + for ii in range(0, len(consts)): + out.write('int v8dbg_%s = %s;\n' % + (consts[ii]['name'], consts[ii]['value'])); + out.write('\n'); + +# +# Emit the whole output file. +# +def emit_config(): + out = file(sys.argv[1], 'w'); + + out.write(header); + + out.write('/* miscellaneous constants */\n'); + emit_set(out, consts_misc); + + out.write('/* class type information */\n'); + consts = []; + keys = typeclasses.keys(); + keys.sort(); + for typename in keys: + klass = typeclasses[typename]; + consts.append({ + 'name': 'type_%s__%s' % (klass, typename), + 'value': typename + }); + + emit_set(out, consts); + + out.write('/* class hierarchy information */\n'); + consts = []; + keys = klasses.keys(); + keys.sort(); + for klassname in keys: + pklass = klasses[klassname]['parent']; + if (pklass == None): + continue; + + consts.append({ + 'name': 'parent_%s__%s' % (klassname, pklass), + 'value': 0 + }); + + emit_set(out, consts); + + out.write('/* field information */\n'); + emit_set(out, fields); + + out.write(footer); + +if (len(sys.argv) < 4): + print('usage: %s output.cc objects.h objects-inl.h' % sys.argv[0]); + sys.exit(2); + +load_objects(); +load_fields(); +emit_config(); diff --git a/deps/v8/tools/grokdump.py b/deps/v8/tools/grokdump.py index 6bc49c68a8..9977289872 100755 --- a/deps/v8/tools/grokdump.py +++ b/deps/v8/tools/grokdump.py @@ -52,6 +52,7 @@ Examples: $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp """ + DEBUG=False @@ -233,6 +234,80 @@ MINIDUMP_CONTEXT_X86 = Descriptor([ MD_CONTEXT_X86_EXTENDED_REGISTERS)) ]) +MD_CONTEXT_AMD64 = 0x00100000 +MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001) +MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002) +MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004) +MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008) +MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010) + +MINIDUMP_CONTEXT_AMD64 = Descriptor([ + ("p1_home", ctypes.c_uint64), + ("p2_home", ctypes.c_uint64), + ("p3_home", ctypes.c_uint64), + ("p4_home", ctypes.c_uint64), + ("p5_home", ctypes.c_uint64), + ("p6_home", ctypes.c_uint64), + ("context_flags", ctypes.c_uint32), + ("mx_csr", ctypes.c_uint32), + # MD_CONTEXT_AMD64_CONTROL. + ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), + # MD_CONTEXT_AMD64_SEGMENTS + ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), + ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), + ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), + ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)), + # MD_CONTEXT_AMD64_CONTROL. + ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)), + ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)), + # MD_CONTEXT_AMD64_DEBUG_REGISTERS. + ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + # MD_CONTEXT_AMD64_INTEGER. + ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + # MD_CONTEXT_AMD64_CONTROL. + ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), + # MD_CONTEXT_AMD64_INTEGER. + ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)), + # MD_CONTEXT_AMD64_CONTROL. + ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)), + # MD_CONTEXT_AMD64_FLOATING_POINT + ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), + MD_CONTEXT_AMD64_FLOATING_POINT)), + ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26), + MD_CONTEXT_AMD64_FLOATING_POINT)), + ("vector_control", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_FLOATING_POINT)), + # MD_CONTEXT_AMD64_DEBUG_REGISTERS. + ("debug_control", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_DEBUG_REGISTERS)), + ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64, + MD_CONTEXT_AMD64_DEBUG_REGISTERS)) +]) + MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([ ("start", ctypes.c_uint64), ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype) @@ -269,6 +344,12 @@ MINIDUMP_THREAD_LIST = Descriptor([ ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) ]) +MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ + ("processor_architecture", ctypes.c_uint16) +]) + +MD_CPU_ARCHITECTURE_X86 = 0 +MD_CPU_ARCHITECTURE_AMD64 = 9 class MinidumpReader(object): """Minidump (.dmp) reader.""" @@ -288,20 +369,34 @@ class MinidumpReader(object): for _ in xrange(self.header.stream_count): directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) offset += MINIDUMP_DIRECTORY.size + self.arch = None self.exception = None self.exception_context = None self.memory_list = None self.memory_list64 = None self.thread_map = {} + + # Find MDRawSystemInfo stream and determine arch. + for d in directories: + if d.stream_type == MD_SYSTEM_INFO_STREAM: + system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( + self.minidump, d.location.rva) + self.arch = system_info.processor_architecture + assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86] + assert not self.arch is None + for d in directories: DebugPrint(d) - # TODO(vitalyr): extract system info including CPU features. if d.stream_type == MD_EXCEPTION_STREAM: self.exception = MINIDUMP_EXCEPTION_STREAM.Read( self.minidump, d.location.rva) DebugPrint(self.exception) - self.exception_context = MINIDUMP_CONTEXT_X86.Read( - self.minidump, self.exception.thread_context.rva) + if self.arch == MD_CPU_ARCHITECTURE_X86: + self.exception_context = MINIDUMP_CONTEXT_X86.Read( + self.minidump, self.exception.thread_context.rva) + elif self.arch == MD_CPU_ARCHITECTURE_AMD64: + self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( + self.minidump, self.exception.thread_context.rva) DebugPrint(self.exception_context) elif d.stream_type == MD_THREAD_LIST_STREAM: thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) @@ -335,6 +430,16 @@ class MinidumpReader(object): location = self.FindLocation(address) return ctypes.c_uint32.from_buffer(self.minidump, location).value + def ReadU64(self, address): + location = self.FindLocation(address) + return ctypes.c_uint64.from_buffer(self.minidump, location).value + + def ReadUIntPtr(self, address): + if self.arch == MD_CPU_ARCHITECTURE_AMD64: + return self.ReadU64(address) + elif self.arch == MD_CPU_ARCHITECTURE_X86: + return self.ReadU32(address) + def ReadBytes(self, address, size): location = self.FindLocation(address) return self.minidump[location:location + size] @@ -355,10 +460,15 @@ class MinidumpReader(object): def GetDisasmLines(self, address, size): location = self.FindLocation(address) if location is None: return [] + arch = None + if self.arch == MD_CPU_ARCHITECTURE_X86: + arch = "ia32" + elif self.arch == MD_CPU_ARCHITECTURE_AMD64: + arch = "x64" return disasm.GetDisasmLines(self.minidump_name, location, size, - "ia32", + arch, False) @@ -366,13 +476,40 @@ class MinidumpReader(object): self.minidump.close() self.minidump_file.close() + def ExceptionIP(self): + if self.arch == MD_CPU_ARCHITECTURE_AMD64: + return self.exception_context.rip + elif self.arch == MD_CPU_ARCHITECTURE_X86: + return self.exception_context.eip + + def ExceptionSP(self): + if self.arch == MD_CPU_ARCHITECTURE_AMD64: + return self.exception_context.rsp + elif self.arch == MD_CPU_ARCHITECTURE_X86: + return self.exception_context.esp + + def FormatIntPtr(self, value): + if self.arch == MD_CPU_ARCHITECTURE_AMD64: + return "%016x" % value + elif self.arch == MD_CPU_ARCHITECTURE_X86: + return "%08x" % value + + def PointerSize(self): + if self.arch == MD_CPU_ARCHITECTURE_AMD64: + return 8 + elif self.arch == MD_CPU_ARCHITECTURE_X86: + return 4 + + def Register(self, name): + return self.exception_context.__getattribute__(name) + # List of V8 instance types. Obtained by adding the code below to any .cc file. # -# #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T); +# #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T); # struct P { # P() { -# printf("{\n"); +# printf("INSTANCE_TYPES = {\n"); # INSTANCE_TYPE_LIST(DUMP_TYPE) # printf("}\n"); # } @@ -386,13 +523,20 @@ INSTANCE_TYPES = { 66: "EXTERNAL_SYMBOL_TYPE", 74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE", 70: "EXTERNAL_ASCII_SYMBOL_TYPE", + 82: "SHORT_EXTERNAL_SYMBOL_TYPE", + 90: "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE", + 86: "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE", 0: "STRING_TYPE", 4: "ASCII_STRING_TYPE", 1: "CONS_STRING_TYPE", 5: "CONS_ASCII_STRING_TYPE", + 3: "SLICED_STRING_TYPE", 2: "EXTERNAL_STRING_TYPE", 10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE", 6: "EXTERNAL_ASCII_STRING_TYPE", + 18: "SHORT_EXTERNAL_STRING_TYPE", + 26: "SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE", + 22: "SHORT_EXTERNAL_ASCII_STRING_TYPE", 6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE", 128: "MAP_TYPE", 129: "CODE_TYPE", @@ -401,43 +545,46 @@ INSTANCE_TYPES = { 132: "HEAP_NUMBER_TYPE", 133: "FOREIGN_TYPE", 134: "BYTE_ARRAY_TYPE", - 135: "EXTERNAL_BYTE_ARRAY_TYPE", - 136: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", - 137: "EXTERNAL_SHORT_ARRAY_TYPE", - 138: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", - 139: "EXTERNAL_INT_ARRAY_TYPE", - 140: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", - 141: "EXTERNAL_FLOAT_ARRAY_TYPE", - 143: "EXTERNAL_PIXEL_ARRAY_TYPE", - 145: "FILLER_TYPE", - 146: "ACCESSOR_INFO_TYPE", - 147: "ACCESS_CHECK_INFO_TYPE", - 148: "INTERCEPTOR_INFO_TYPE", - 149: "CALL_HANDLER_INFO_TYPE", - 150: "FUNCTION_TEMPLATE_INFO_TYPE", - 151: "OBJECT_TEMPLATE_INFO_TYPE", - 152: "SIGNATURE_INFO_TYPE", - 153: "TYPE_SWITCH_INFO_TYPE", - 154: "SCRIPT_TYPE", - 155: "CODE_CACHE_TYPE", - 156: "POLYMORPHIC_CODE_CACHE_TYPE", - 159: "FIXED_ARRAY_TYPE", - 160: "SHARED_FUNCTION_INFO_TYPE", - 161: "JS_MESSAGE_OBJECT_TYPE", - 162: "JS_VALUE_TYPE", - 163: "JS_OBJECT_TYPE", - 164: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", - 165: "JS_GLOBAL_OBJECT_TYPE", - 166: "JS_BUILTINS_OBJECT_TYPE", - 167: "JS_GLOBAL_PROXY_TYPE", - 168: "JS_ARRAY_TYPE", - 169: "JS_PROXY_TYPE", - 170: "JS_WEAK_MAP_TYPE", - 171: "JS_REGEXP_TYPE", - 172: "JS_FUNCTION_TYPE", - 173: "JS_FUNCTION_PROXY_TYPE", - 157: "DEBUG_INFO_TYPE", - 158: "BREAK_POINT_INFO_TYPE", + 135: "FREE_SPACE_TYPE", + 136: "EXTERNAL_BYTE_ARRAY_TYPE", + 137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", + 138: "EXTERNAL_SHORT_ARRAY_TYPE", + 139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", + 140: "EXTERNAL_INT_ARRAY_TYPE", + 141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", + 142: "EXTERNAL_FLOAT_ARRAY_TYPE", + 144: "EXTERNAL_PIXEL_ARRAY_TYPE", + 146: "FILLER_TYPE", + 147: "ACCESSOR_INFO_TYPE", + 148: "ACCESSOR_PAIR_TYPE", + 149: "ACCESS_CHECK_INFO_TYPE", + 150: "INTERCEPTOR_INFO_TYPE", + 151: "CALL_HANDLER_INFO_TYPE", + 152: "FUNCTION_TEMPLATE_INFO_TYPE", + 153: "OBJECT_TEMPLATE_INFO_TYPE", + 154: "SIGNATURE_INFO_TYPE", + 155: "TYPE_SWITCH_INFO_TYPE", + 156: "SCRIPT_TYPE", + 157: "CODE_CACHE_TYPE", + 158: "POLYMORPHIC_CODE_CACHE_TYPE", + 161: "FIXED_ARRAY_TYPE", + 145: "FIXED_DOUBLE_ARRAY_TYPE", + 162: "SHARED_FUNCTION_INFO_TYPE", + 163: "JS_MESSAGE_OBJECT_TYPE", + 166: "JS_VALUE_TYPE", + 167: "JS_OBJECT_TYPE", + 168: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", + 169: "JS_GLOBAL_OBJECT_TYPE", + 170: "JS_BUILTINS_OBJECT_TYPE", + 171: "JS_GLOBAL_PROXY_TYPE", + 172: "JS_ARRAY_TYPE", + 165: "JS_PROXY_TYPE", + 175: "JS_WEAK_MAP_TYPE", + 176: "JS_REGEXP_TYPE", + 177: "JS_FUNCTION_TYPE", + 164: "JS_FUNCTION_PROXY_TYPE", + 159: "DEBUG_INFO_TYPE", + 160: "BREAK_POINT_INFO_TYPE", } @@ -501,34 +648,36 @@ class HeapObject(object): p.Print(str(self)) def __str__(self): - return "HeapObject(%08x, %s)" % (self.address, - INSTANCE_TYPES[self.map.instance_type]) + return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address), + INSTANCE_TYPES[self.map.instance_type]) def ObjectField(self, offset): - field_value = self.heap.reader.ReadU32(self.address + offset) + field_value = self.heap.reader.ReadUIntPtr(self.address + offset) return self.heap.FindObjectOrSmi(field_value) def SmiField(self, offset): - field_value = self.heap.reader.ReadU32(self.address + offset) + field_value = self.heap.reader.ReadUIntPtr(self.address + offset) assert (field_value & 1) == 0 return field_value / 2 class Map(HeapObject): - INSTANCE_TYPE_OFFSET = 8 + def InstanceTypeOffset(self): + return self.heap.PointerSize() + self.heap.IntSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) self.instance_type = \ - heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET) + heap.reader.ReadU8(self.address + self.InstanceTypeOffset()) class String(HeapObject): - LENGTH_OFFSET = 4 + def LengthOffset(self): + return self.heap.PointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.length = self.SmiField(String.LENGTH_OFFSET) + self.length = self.SmiField(self.LengthOffset()) def GetChars(self): return "?string?" @@ -541,11 +690,12 @@ class String(HeapObject): class SeqString(String): - CHARS_OFFSET = 12 + def CharsOffset(self): + return self.heap.PointerSize() * 3 def __init__(self, heap, map, address): String.__init__(self, heap, map, address) - self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET, + self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(), self.length) def GetChars(self): @@ -553,6 +703,7 @@ class SeqString(String): class ExternalString(String): + # TODO(vegorov) fix ExternalString for X64 architecture RESOURCE_OFFSET = 12 WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4 @@ -582,24 +733,28 @@ class ExternalString(String): class ConsString(String): - LEFT_OFFSET = 12 - RIGHT_OFFSET = 16 + def LeftOffset(self): + return self.heap.PointerSize() * 3 + + def RightOffset(self): + return self.heap.PointerSize() * 4 def __init__(self, heap, map, address): String.__init__(self, heap, map, address) - self.left = self.ObjectField(ConsString.LEFT_OFFSET) - self.right = self.ObjectField(ConsString.RIGHT_OFFSET) + self.left = self.ObjectField(self.LeftOffset()) + self.right = self.ObjectField(self.RightOffset()) def GetChars(self): return self.left.GetChars() + self.right.GetChars() class Oddball(HeapObject): - TO_STRING_OFFSET = 4 + def ToStringOffset(self): + return self.heap.PointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET) + self.to_string = self.ObjectField(self.ToStringOffset()) def Print(self, p): p.Print(str(self)) @@ -609,19 +764,23 @@ class Oddball(HeapObject): class FixedArray(HeapObject): - LENGTH_OFFSET = 4 - ELEMENTS_OFFSET = 8 + def LengthOffset(self): + return self.heap.PointerSize() + + def ElementsOffset(self): + return self.heap.PointerSize() * 2 def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.length = self.SmiField(FixedArray.LENGTH_OFFSET) + self.length = self.SmiField(self.LengthOffset()) def Print(self, p): - p.Print("FixedArray(%08x) {" % self.address) + p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address)) p.Indent() p.Print("length: %d" % self.length) + base_offset = self.ElementsOffset() for i in xrange(self.length): - offset = FixedArray.ELEMENTS_OFFSET + 4 * i + offset = base_offset + 4 * i p.Print("[%08d] = %s" % (i, self.ObjectField(offset))) p.Dedent() p.Print("}") @@ -631,19 +790,22 @@ class FixedArray(HeapObject): class JSFunction(HeapObject): - CODE_ENTRY_OFFSET = 12 - SHARED_OFFSET = 20 + def CodeEntryOffset(self): + return 3 * self.heap.PointerSize() + + def SharedOffset(self): + return 5 * self.heap.PointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) code_entry = \ - heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET) - self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1) - self.shared = self.ObjectField(JSFunction.SHARED_OFFSET) + heap.reader.ReadU32(self.address + self.CodeEntryOffset()) + self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1) + self.shared = self.ObjectField(self.SharedOffset()) def Print(self, p): source = "\n".join(" %s" % line for line in self._GetSource().split("\n")) - p.Print("JSFunction(%08x) {" % self.address) + p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address)) p.Indent() p.Print("inferred name: %s" % self.shared.inferred_name) if self.shared.script.Is(Script) and self.shared.script.name.Is(String): @@ -662,7 +824,8 @@ class JSFunction(HeapObject): inferred_name = "" if self.shared.Is(SharedFunctionInfo): inferred_name = self.shared.inferred_name - return "JSFunction(%08x, %s)" % (self.address, inferred_name) + return "JSFunction(%s, %s)" % \ + (self.heap.reader.FormatIntPtr(self.address), inferred_name) def _GetSource(self): source = "?source?" @@ -675,47 +838,75 @@ class JSFunction(HeapObject): class SharedFunctionInfo(HeapObject): - CODE_OFFSET = 2 * 4 - SCRIPT_OFFSET = 7 * 4 - INFERRED_NAME_OFFSET = 9 * 4 - START_POSITION_AND_TYPE_OFFSET = 17 * 4 - END_POSITION_OFFSET = 18 * 4 + def CodeOffset(self): + return 2 * self.heap.PointerSize() + + def ScriptOffset(self): + return 7 * self.heap.PointerSize() + + def InferredNameOffset(self): + return 9 * self.heap.PointerSize() + + def EndPositionOffset(self): + return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize() + + def StartPositionAndTypeOffset(self): + return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET) - self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET) - self.inferred_name = \ - self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET) - start_position_and_type = \ - self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET) - self.start_position = start_position_and_type >> 2 - self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET) + self.code = self.ObjectField(self.CodeOffset()) + self.script = self.ObjectField(self.ScriptOffset()) + self.inferred_name = self.ObjectField(self.InferredNameOffset()) + if heap.PointerSize() == 8: + start_position_and_type = \ + heap.reader.ReadU32(self.StartPositionAndTypeOffset()) + self.start_position = start_position_and_type >> 2 + pseudo_smi_end_position = \ + heap.reader.ReadU32(self.EndPositionOffset()) + self.end_position = pseudo_smi_end_position >> 2 + else: + start_position_and_type = \ + self.SmiField(self.StartPositionAndTypeOffset()) + self.start_position = start_position_and_type >> 2 + self.end_position = \ + self.SmiField(self.EndPositionOffset()) class Script(HeapObject): - SOURCE_OFFSET = 4 - NAME_OFFSET = 8 + def SourceOffset(self): + return self.heap.PointerSize() + + def NameOffset(self): + return self.SourceOffset() + self.heap.PointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.source = self.ObjectField(Script.SOURCE_OFFSET) - self.name = self.ObjectField(Script.NAME_OFFSET) + self.source = self.ObjectField(self.SourceOffset()) + self.name = self.ObjectField(self.NameOffset()) class Code(HeapObject): - INSTRUCTION_SIZE_OFFSET = 4 - ENTRY_OFFSET = 32 + CODE_ALIGNMENT_MASK = (1 << 5) - 1 + + def InstructionSizeOffset(self): + return self.heap.PointerSize() + + @staticmethod + def HeaderSize(heap): + return (heap.PointerSize() + heap.IntSize() + \ + 4 * heap.PointerSize() + 3 * heap.IntSize() + \ + Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) - self.entry = self.address + Code.ENTRY_OFFSET + self.entry = self.address + Code.HeaderSize(heap) self.instruction_size = \ - heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET) + heap.reader.ReadU32(self.address + self.InstructionSizeOffset()) def Print(self, p): lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size) - p.Print("Code(%08x) {" % self.address) + p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address)) p.Indent() p.Print("instruction_size: %d" % self.instruction_size) p.PrintLines(self._FormatLine(line) for line in lines) @@ -735,6 +926,9 @@ class V8Heap(object): "EXTERNAL_SYMBOL_TYPE": ExternalString, "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, + "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString, + "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString, + "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString, "STRING_TYPE": SeqString, "ASCII_STRING_TYPE": SeqString, "CONS_STRING_TYPE": ConsString, @@ -764,10 +958,10 @@ class V8Heap(object): def FindObject(self, tagged_address): if tagged_address in self.objects: return self.objects[tagged_address] - if (tagged_address & 1) != 1: return None + if (tagged_address & self.ObjectAlignmentMask()) != 1: return None address = tagged_address - 1 if not self.reader.IsValidAddress(address): return None - map_tagged_address = self.reader.ReadU32(address) + map_tagged_address = self.reader.ReadUIntPtr(address) if tagged_address == map_tagged_address: # Meta map? meta_map = Map(self, None, address) @@ -776,7 +970,7 @@ class V8Heap(object): meta_map.map = meta_map object = meta_map else: - map = self.FindObject(map_tagged_address) + map = self.FindMap(map_tagged_address) if map is None: return None instance_type_name = INSTANCE_TYPES.get(map.instance_type) if instance_type_name is None: return None @@ -785,9 +979,37 @@ class V8Heap(object): self.objects[tagged_address] = object return object + def FindMap(self, tagged_address): + if (tagged_address & self.MapAlignmentMask()) != 1: return None + address = tagged_address - 1 + if not self.reader.IsValidAddress(address): return None + object = Map(self, None, address) + return object + + def IntSize(self): + return 4 + + def PointerSize(self): + return self.reader.PointerSize() + + def ObjectAlignmentMask(self): + return self.PointerSize() - 1 + + def MapAlignmentMask(self): + if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64: + return (1 << 4) - 1 + elif self.reader.arch == MD_CPU_ARCHITECTURE_X86: + return (1 << 5) - 1 + EIP_PROXIMITY = 64 +CONTEXT_FOR_ARCH = { + MD_CPU_ARCHITECTURE_AMD64: + ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'], + MD_CPU_ARCHITECTURE_X86: + ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] +} def AnalyzeMinidump(options, minidump_name): reader = MinidumpReader(options, minidump_name) @@ -800,40 +1022,35 @@ def AnalyzeMinidump(options, minidump_name): print " thread id: %d" % exception_thread.id print " code: %08X" % reader.exception.exception.code print " context:" - print " eax: %08x" % reader.exception_context.eax - print " ebx: %08x" % reader.exception_context.ebx - print " ecx: %08x" % reader.exception_context.ecx - print " edx: %08x" % reader.exception_context.edx - print " edi: %08x" % reader.exception_context.edi - print " esi: %08x" % reader.exception_context.esi - print " ebp: %08x" % reader.exception_context.ebp - print " esp: %08x" % reader.exception_context.esp - print " eip: %08x" % reader.exception_context.eip + for r in CONTEXT_FOR_ARCH[reader.arch]: + print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) # TODO(vitalyr): decode eflags. print " eflags: %s" % bin(reader.exception_context.eflags)[2:] print + stack_top = reader.ExceptionSP() stack_bottom = exception_thread.stack.start + \ exception_thread.stack.memory.data_size - stack_map = {reader.exception_context.eip: -1} - for slot in xrange(reader.exception_context.esp, stack_bottom, 4): - maybe_address = reader.ReadU32(slot) + stack_map = {reader.ExceptionIP(): -1} + for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): + maybe_address = reader.ReadUIntPtr(slot) if not maybe_address in stack_map: stack_map[maybe_address] = slot heap = V8Heap(reader, stack_map) print "Disassembly around exception.eip:" - start = reader.exception_context.eip - EIP_PROXIMITY + start = reader.ExceptionIP() - EIP_PROXIMITY lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY) for line in lines: print FormatDisasmLine(start, heap, line) print print "Annotated stack (from exception.esp to bottom):" - for slot in xrange(reader.exception_context.esp, stack_bottom, 4): - maybe_address = reader.ReadU32(slot) + for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): + maybe_address = reader.ReadUIntPtr(slot) heap_object = heap.FindObject(maybe_address) - print "%08x: %08x" % (slot, maybe_address) + print "%s: %s" % (reader.FormatIntPtr(slot), + reader.FormatIntPtr(maybe_address)) if heap_object: heap_object.Print(Printer()) print diff --git a/deps/v8/tools/gyp/v8-node.gyp b/deps/v8/tools/gyp/v8-node.gyp deleted file mode 100644 index f2b3a337bd..0000000000 --- a/deps/v8/tools/gyp/v8-node.gyp +++ /dev/null @@ -1,13 +0,0 @@ -{ - 'includes': [ - '../../build/common.gypi', - 'v8.gyp' - ], - 'target_defaults': { - 'conditions': [ - [ 'OS=="solaris"', { - 'defines': [ '__C99FEATURES__=1' ], - }], - ], - }, -} diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index 92d1e5c96a..e60a23272a 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -1,4 +1,4 @@ -# Copyright 2011 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -32,6 +32,7 @@ 'targets': [ { 'target_name': 'v8', + 'dependencies_traverse': 1, 'conditions': [ ['want_separate_host_toolset==1', { 'toolsets': ['host', 'target'], @@ -72,11 +73,7 @@ }, }], ['soname_version!=""', { - # Ideally, we'd like to specify the full filename for the - # library and set it to "libv8.so.<(soname_version)", - # but currently the best we can do is use 'product_name' and - # get "libv8-<(soname_version).so". - 'product_name': 'v8-<(soname_version)', + 'product_extension': 'so.<(soname_version)', }], ], }, @@ -225,6 +222,9 @@ { 'target_name': 'v8_base', 'type': '<(library)', + 'variables': { + 'optimize': 'max', + }, 'include_dirs+': [ '../../src', ], @@ -240,7 +240,6 @@ '../../src/assembler.cc', '../../src/assembler.h', '../../src/ast.cc', - '../../src/ast-inl.h', '../../src/ast.h', '../../src/atomicops_internals_x86_gcc.cc', '../../src/bignum.cc', @@ -340,6 +339,8 @@ '../../src/ic-inl.h', '../../src/ic.cc', '../../src/ic.h', + '../../src/incremental-marking.cc', + '../../src/incremental-marking.h', '../../src/inspector.cc', '../../src/inspector.h', '../../src/interpreter-irregexp.cc', @@ -394,6 +395,7 @@ '../../src/prettyprinter.h', '../../src/property.cc', '../../src/property.h', + '../../src/property-details.h', '../../src/profile-generator-inl.h', '../../src/profile-generator.cc', '../../src/profile-generator.h', @@ -431,6 +433,9 @@ '../../src/spaces-inl.h', '../../src/spaces.cc', '../../src/spaces.h', + '../../src/store-buffer-inl.h', + '../../src/store-buffer.cc', + '../../src/store-buffer.h', '../../src/string-search.cc', '../../src/string-search.h', '../../src/string-stream.cc', @@ -549,6 +554,40 @@ '../../src/ia32/stub-cache-ia32.cc', ], }], + ['v8_target_arch=="mips"', { + 'sources': [ + '../../src/mips/assembler-mips.cc', + '../../src/mips/assembler-mips.h', + '../../src/mips/assembler-mips-inl.h', + '../../src/mips/builtins-mips.cc', + '../../src/mips/codegen-mips.cc', + '../../src/mips/codegen-mips.h', + '../../src/mips/code-stubs-mips.cc', + '../../src/mips/code-stubs-mips.h', + '../../src/mips/constants-mips.cc', + '../../src/mips/constants-mips.h', + '../../src/mips/cpu-mips.cc', + '../../src/mips/debug-mips.cc', + '../../src/mips/deoptimizer-mips.cc', + '../../src/mips/disasm-mips.cc', + '../../src/mips/frames-mips.cc', + '../../src/mips/frames-mips.h', + '../../src/mips/full-codegen-mips.cc', + '../../src/mips/ic-mips.cc', + '../../src/mips/lithium-codegen-mips.cc', + '../../src/mips/lithium-codegen-mips.h', + '../../src/mips/lithium-gap-resolver-mips.cc', + '../../src/mips/lithium-gap-resolver-mips.h', + '../../src/mips/lithium-mips.cc', + '../../src/mips/lithium-mips.h', + '../../src/mips/macro-assembler-mips.cc', + '../../src/mips/macro-assembler-mips.h', + '../../src/mips/regexp-macro-assembler-mips.cc', + '../../src/mips/regexp-macro-assembler-mips.h', + '../../src/mips/simulator-mips.cc', + '../../src/mips/stub-cache-mips.cc', + ], + }], ['v8_target_arch=="x64" or v8_target_arch=="mac" or OS=="mac"', { 'sources': [ '../../src/x64/assembler-x64-inl.h', @@ -586,7 +625,8 @@ ['v8_compress_startup_data=="bz2"', { 'libraries': [ '-lbz2', - ]}], + ] + }], ], }, 'sources': [ @@ -596,26 +636,30 @@ } ], ['OS=="android"', { + 'defines': [ + 'CAN_USE_VFP_INSTRUCTIONS', + ], 'sources': [ '../../src/platform-posix.cc', ], 'conditions': [ - ['host_os=="mac" and _toolset!="target"', { - 'sources': [ - '../../src/platform-macos.cc' - ] + ['host_os=="mac"', { + 'target_conditions': [ + ['_toolset=="host"', { + 'sources': [ + '../../src/platform-macos.cc' + ] + }, { + 'sources': [ + '../../src/platform-linux.cc' + ] + }], + ], }, { 'sources': [ '../../src/platform-linux.cc' ] }], - ['_toolset=="target"', { - 'link_settings': { - 'libraries': [ - '-llog', - ], - } - }], ], }, ], @@ -641,10 +685,25 @@ ], } ], + ['OS=="netbsd"', { + 'link_settings': { + 'libraries': [ + '-L/usr/pkg/lib -Wl,-R/usr/pkg/lib -lexecinfo', + ]}, + 'sources': [ + '../../src/platform-openbsd.cc', + '../../src/platform-posix.cc' + ], + } + ], ['OS=="solaris"', { + 'link_settings': { + 'libraries': [ + '-lsocket -lnsl', + ]}, 'sources': [ '../../src/platform-solaris.cc', - '../../src/platform-posix.cc' + '../../src/platform-posix.cc', ], } ], @@ -671,6 +730,11 @@ 'V8_SHARED', ], }], + ['v8_postmortem_support=="true"', { + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc', + ] + }], ], }, { @@ -704,7 +768,7 @@ 'experimental_library_files': [ '../../src/macros.py', '../../src/proxy.js', - '../../src/weakmap.js', + '../../src/collection.js', ], }, 'actions': [ @@ -747,9 +811,38 @@ ], }, { + 'target_name': 'postmortem-metadata', + 'type': 'none', + 'variables': { + 'heapobject_files': [ + '../../src/objects.h', + '../../src/objects-inl.h', + ], + }, + 'actions': [ + { + 'action_name': 'gen-postmortem-metadata', + 'inputs': [ + '../../tools/gen-postmortem-metadata.py', + '<@(heapobject_files)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc', + ], + 'action': [ + 'python', + '../../tools/gen-postmortem-metadata.py', + '<@(_outputs)', + '<@(heapobject_files)' + ] + } + ] + }, + { 'target_name': 'mksnapshot', 'type': 'executable', 'dependencies': [ + 'v8_base', 'v8_nosnapshot', ], 'include_dirs+': [ @@ -767,8 +860,8 @@ ['v8_compress_startup_data=="bz2"', { 'libraries': [ '-lbz2', - ]} - ], + ] + }], ], }, { @@ -793,7 +886,8 @@ ['v8_compress_startup_data=="bz2"', { 'libraries': [ '-lbz2', - ]}], + ] + }], ], }, { @@ -865,7 +959,7 @@ 'targets': [ { 'target_name': 'v8', - 'type': 'settings', + 'type': 'none', 'conditions': [ ['want_separate_host_toolset==1', { 'toolsets': ['host', 'target'], diff --git a/deps/v8/tools/js2c.py b/deps/v8/tools/js2c.py index a2ea8eacc7..fa559f362c 100644 --- a/deps/v8/tools/js2c.py +++ b/deps/v8/tools/js2c.py @@ -128,12 +128,13 @@ def ExpandMacros(lines, macros): end = pattern_match.end() assert lines[end - 1] == '(' last_match = end - arg_index = 0 + arg_index = [0] # Wrap state into array, to work around Python "scoping" mapping = { } def add_arg(str): # Remember to expand recursively in the arguments replacement = ExpandMacros(str.strip(), macros) - mapping[macro.args[arg_index]] = replacement + mapping[macro.args[arg_index[0]]] = replacement + arg_index[0] += 1 while end < len(lines) and height > 0: # We don't count commas at higher nesting levels. if lines[end] == ',' and height == 1: diff --git a/deps/v8/tools/linux-tick-processor b/deps/v8/tools/linux-tick-processor index 0b0a1fbd27..7070ce6fcc 100755 --- a/deps/v8/tools/linux-tick-processor +++ b/deps/v8/tools/linux-tick-processor @@ -1,5 +1,14 @@ #!/bin/sh +# find the name of the log file to process, it must not start with a dash. +log_file="v8.log" +for arg in "$@" +do + if ! expr "X${arg}" : "^X-" > /dev/null; then + log_file=${arg} + fi +done + tools_path=`cd $(dirname "$0");pwd` if [ ! "$D8_PATH" ]; then d8_public=`which d8` @@ -9,21 +18,19 @@ fi d8_exec=$D8_PATH/d8 if [ ! -x $d8_exec ]; then - echo "d8 shell not found in $D8_PATH" - echo "To build, execute 'scons <flags> d8' from the V8 directory" - exit 1 + D8_PATH=`pwd`/out/native + d8_exec=$D8_PATH/d8 fi +if [ ! -x $d8_exec ]; then + d8_exec=`grep -m 1 -o '".*/d8"' $log_file | sed 's/"//g'` +fi -# find the name of the log file to process, it must not start with a dash. -log_file="v8.log" -for arg in "$@" -do - if ! expr "X${arg}" : "^X-" > /dev/null; then - log_file=${arg} - fi -done - +if [ ! -x $d8_exec ]; then + echo "d8 shell not found in $D8_PATH" + echo "To build, execute 'make native' from the V8 directory" + exit 1 +fi # nm spits out 'no symbols found' messages to stderr. cat $log_file | $d8_exec $tools_path/splaytree.js $tools_path/codemap.js \ diff --git a/deps/v8/tools/ll_prof.py b/deps/v8/tools/ll_prof.py index 58cbb95851..51ba672aca 100755 --- a/deps/v8/tools/ll_prof.py +++ b/deps/v8/tools/ll_prof.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -334,6 +334,7 @@ class LogReader(object): _ARCH_TO_POINTER_TYPE_MAP = { "ia32": ctypes.c_uint32, "arm": ctypes.c_uint32, + "mips": ctypes.c_uint32, "x64": ctypes.c_uint64 } @@ -399,12 +400,16 @@ class LogReader(object): code = Code(name, start_address, end_address, origin, origin_offset) conficting_code = self.code_map.Find(start_address) if conficting_code: - LogReader._HandleCodeConflict(conficting_code, code) - # TODO(vitalyr): this warning is too noisy because of our - # attempts to reconstruct code log from the snapshot. - # print >>sys.stderr, \ - # "Warning: Skipping duplicate code log entry %s" % code - continue + if not (conficting_code.start_address == code.start_address and + conficting_code.end_address == code.end_address): + self.code_map.Remove(conficting_code) + else: + LogReader._HandleCodeConflict(conficting_code, code) + # TODO(vitalyr): this warning is too noisy because of our + # attempts to reconstruct code log from the snapshot. + # print >>sys.stderr, \ + # "Warning: Skipping duplicate code log entry %s" % code + continue self.code_map.Add(code) continue @@ -668,7 +673,9 @@ OBJDUMP_SECTION_HEADER_RE = re.compile( OBJDUMP_SYMBOL_LINE_RE = re.compile( r"^([a-f0-9]+)\s(.{7})\s(\S+)\s+([a-f0-9]+)\s+(?:\.hidden\s+)?(.*)$") OBJDUMP_DYNAMIC_SYMBOLS_START_RE = re.compile( - r"^DYNAMIC SYMBOL TABLE") + r"^DYNAMIC SYMBOL TABLE") +OBJDUMP_SKIP_RE = re.compile( + r"^.*ld\.so\.cache$") KERNEL_ALLSYMS_FILE = "/proc/kallsyms" PERF_KERNEL_ALLSYMS_RE = re.compile( r".*kallsyms.*") @@ -687,6 +694,8 @@ class LibraryRepo(object): # is 0. if mmap_info.tid == 0 and not options.kernel: return True + if OBJDUMP_SKIP_RE.match(mmap_info.filename): + return True if PERF_KERNEL_ALLSYMS_RE.match(mmap_info.filename): return self._LoadKernelSymbols(code_map) self.infos.append(mmap_info) diff --git a/deps/v8/tools/logreader.js b/deps/v8/tools/logreader.js index 315e721276..a8141da21b 100644 --- a/deps/v8/tools/logreader.js +++ b/deps/v8/tools/logreader.js @@ -1,4 +1,4 @@ -// Copyright 2009 the V8 project authors. All rights reserved. +// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -134,9 +134,8 @@ LogReader.prototype.skipDispatch = function(dispatch) { LogReader.prototype.dispatchLogRow_ = function(fields) { // Obtain the dispatch. var command = fields[0]; - if (!(command in this.dispatchTable_)) { - throw new Error('unknown command: ' + command); - } + if (!(command in this.dispatchTable_)) return; + var dispatch = this.dispatchTable_[command]; if (dispatch === null || this.skipDispatch(dispatch)) { diff --git a/deps/v8/tools/merge-to-branch.sh b/deps/v8/tools/merge-to-branch.sh new file mode 100644 index 0000000000..abe5fc24d4 --- /dev/null +++ b/deps/v8/tools/merge-to-branch.sh @@ -0,0 +1,361 @@ +#!/bin/bash +# Copyright 2012 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +########## Global variable definitions + +BRANCHNAME=prepare-merge +VERSION_FILE="src/version.cc" +PERSISTFILE_BASENAME=/tmp/v8-merge-to-branch-tempfile +ALREADY_MERGING_SENTINEL_FILE="$PERSISTFILE_BASENAME-already-merging" +CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry" +PATCH_FILE="$PERSISTFILE_BASENAME-patch" +COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg" +COMMITMSG_FILE_COPY="$PERSISTFILE_BASENAME-commitmsg-copy" +TOUCHED_FILES_FILE="$PERSISTFILE_BASENAME-touched-files" +TRUNK_REVISION_FILE="$PERSISTFILE_BASENAME-trunkrevision" +START_STEP=0 +CURRENT_STEP=0 + +usage() { +cat << EOF +usage: $0 [OPTIONS]... [BRANCH] [REVISION]... + +Performs the necessary steps to merge revisions from bleeding_edge +to other branches, including trunk. + +OPTIONS: + -h Show this message + -s Specify the step where to start work. Default: 0. +EOF +} + +########## Function definitions + +die() { + [[ -n "$1" ]] && echo "Error: $1" + echo "Exiting." + exit 1 +} + +confirm() { + echo -n "$1 [Y/n] " + read ANSWER + if [[ -z "$ANSWER" || "$ANSWER" == "Y" || "$ANSWER" == "y" ]] ; then + return 0 + else + return 1 + fi +} + +delete_branch() { + local MATCH=$(git branch | grep $1 | awk '{print $NF}' ) + if [ "$MATCH" == "$1" ] ; then + confirm "Branch $1 exists, do you want to delete it?" + if [ $? -eq 0 ] ; then + git branch -D $1 || die "Deleting branch '$1' failed." + echo "Branch $1 deleted." + else + die "Can't continue. Please delete branch $1 and try again." + fi + fi +} + +# Persist and restore variables to support canceling/resuming execution +# of this script. +persist() { + local VARNAME=$1 + local FILE="$PERSISTFILE_BASENAME-$VARNAME" + echo "${!VARNAME}" > $FILE +} + +restore() { + local VARNAME=$1 + local FILE="$PERSISTFILE_BASENAME-$VARNAME" + local VALUE="$(cat $FILE)" + eval "$VARNAME=\"$VALUE\"" +} + +restore_if_unset() { + local VARNAME=$1 + [[ -z "${!VARNAME}" ]] && restore "$VARNAME" + [[ -z "${!VARNAME}" ]] && die "Variable '$VARNAME' could not be restored." +} + +persist_patch_commit_hashes() { + local FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES" + echo "PATCH_COMMIT_HASHES=( ${PATCH_COMMIT_HASHES[@]} )" > $FILE +} + +restore_patch_commit_hashes() { + local FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES" + source $FILE +} + +restore_patch_commit_hashes_if_unset() { + [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && restore_patch_commit_hashes + [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && \ + die "Variable PATCH_COMMIT_HASHES could not be restored." +} + +########## Option parsing + +while getopts ":hs:f" OPTION ; do + case $OPTION in + h) usage + exit 0 + ;; + f) rm -f "$ALREADY_MERGING_SENTINEL_FILE" + ;; + s) START_STEP=$OPTARG + ;; + ?) echo "Illegal option: -$OPTARG" + usage + exit 1 + ;; + esac +done +let OPTION_COUNT=$OPTIND-1 +shift $OPTION_COUNT + +########## Regular workflow + +# If there is a merge in progress, abort. +[[ -e "$ALREADY_MERGING_SENTINEL_FILE" ]] && [[ -z "$START_STEP" ]] \ + && die "A merge is already in progress" +touch "$ALREADY_MERGING_SENTINEL_FILE" + +# Cancel if this is not a git checkout. +[[ -d .git ]] \ + || die "This is not a git checkout, this script won't work for you." + +# Cancel if EDITOR is unset or not executable. +[[ -n "$EDITOR" && -x "$(which $EDITOR)" ]] \ + || die "Please set your EDITOR environment variable, you'll need it." + +if [ $START_STEP -le $CURRENT_STEP ] ; then + MERGE_TO_BRANCH=$1 + [[ -n "$MERGE_TO_BRANCH" ]] \ + || die "Please specify a branch to merge to" + shift + persist "MERGE_TO_BRANCH" + + echo ">>> Step $CURRENT_STEP: Preparation" + # Check for a clean workdir. + [[ -z "$(git status -s -uno)" ]] \ + || die "Workspace is not clean. Please commit or undo your changes." + + # Persist current branch. + CURRENT_BRANCH=$(git status -s -b -uno | grep "^##" | awk '{print $2}') + persist "CURRENT_BRANCH" + delete_branch $BRANCHNAME +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Fetch unfetched revisions." + git svn fetch || die "'git svn fetch' failed." +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + restore_if_unset "MERGE_TO_BRANCH" + echo ">>> Step $CURRENT_STEP: Create a fresh branch for the patch." + git checkout -b $BRANCHNAME svn/$MERGE_TO_BRANCH \ + || die "Creating branch $BRANCHNAME failed." +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Find the git \ +revisions associated with the patches." + current=0 + for REVISION in "$@" ; do + NEXT_HASH=$(git svn find-rev "r$REVISION" svn/bleeding_edge) + [[ -n "$NEXT_HASH" ]] \ + || die "Cannot determine git hash for r$REVISION" + PATCH_COMMIT_HASHES[$current]="$NEXT_HASH" + [[ -n "$NEW_COMMIT_MSG" ]] && NEW_COMMIT_MSG="$NEW_COMMIT_MSG," + NEW_COMMIT_MSG="$NEW_COMMIT_MSG r$REVISION" + let current+=1 + done + NEW_COMMIT_MSG="Merged$NEW_COMMIT_MSG into $MERGE_TO_BRANCH branch." + + echo "$NEW_COMMIT_MSG" > $COMMITMSG_FILE + echo >> $COMMITMSG_FILE + for HASH in ${PATCH_COMMIT_HASHES[@]} ; do + PATCH_MERGE_DESCRIPTION=$(git log -1 --format=%s $HASH) + echo "$PATCH_MERGE_DESCRIPTION" >> $COMMITMSG_FILE + echo >> $COMMITMSG_FILE + done + for HASH in ${PATCH_COMMIT_HASHES[@]} ; do + BUG=$(git log -1 $HASH | grep "BUG=" | awk -F '=' '{print $NF}') + if [ $BUG ] ; then + if [ "$BUG_AGGREGATE" ] ; then + BUG_AGGREGATE="$BUG_AGGREGATE," + fi + BUG_AGGREGATE="$BUG_AGGREGATE$BUG" + fi + done + if [ "$BUG_AGGREGATE" ] ; then + echo "BUG=$BUG_AGGREGATE" >> $COMMITMSG_FILE + fi + persist "NEW_COMMIT_MSG" + persist_patch_commit_hashes +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + restore_if_unset "MERGE_TO_BRANCH" + restore_patch_commit_hashes_if_unset "PATCH_COMMIT_HASHES" + echo "${PATCH_COMMIT_HASHES[@]}" + echo ">>> Step $CURRENT_STEP: Apply patches for selected revisions." + rm -f "$TOUCHED_FILES_FILE" + for HASH in ${PATCH_COMMIT_HASHES[@]} ; do + git log -1 -p $HASH | patch -p1 \ + | tee >(awk '{print $NF}' >> "$TOUCHED_FILES_FILE") + [[ $? -eq 0 ]] \ + || die "Cannot apply the patch for $HASH to $MERGE_TO_BRANCH." + done + # Stage added and modified files. + TOUCHED_FILES=$(cat "$TOUCHED_FILES_FILE") + for FILE in $TOUCHED_FILES ; do + git add "$FILE" + done + # Stage deleted files. + DELETED_FILES=$(git status -s -uno --porcelain | grep "^ D" \ + | awk '{print $NF}') + for FILE in $DELETED_FILES ; do + git rm "$FILE" + done + rm -f "$TOUCHED_FILES_FILE" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Prepare version.cc" +# These version numbers are used again for creating the tag + PATCH=$(grep "#define PATCH_LEVEL" "$VERSION_FILE" | awk '{print $NF}') + persist "PATCH" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Increment version number." + restore_if_unset "PATCH" + NEWPATCH=$(($PATCH + 1)) + confirm "Automatically increment PATCH_LEVEL? (Saying 'n' will fire up \ +your EDITOR on $VERSION_FILE so you can make arbitrary changes. When \ +you're done, save the file and exit your EDITOR.)" + if [ $? -eq 0 ] ; then + sed -e "/#define PATCH_LEVEL/s/[0-9]*$/$NEWPATCH/" \ + -i "$VERSION_FILE" + else + $EDITOR "$VERSION_FILE" + fi + NEWMAJOR=$(grep "#define MAJOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') + persist "NEWMAJOR" + NEWMINOR=$(grep "#define MINOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') + persist "NEWMINOR" + NEWBUILD=$(grep "#define BUILD_NUMBER" "$VERSION_FILE" | awk '{print $NF}') + persist "NEWBUILD" + NEWPATCH=$(grep "#define PATCH_LEVEL" "$VERSION_FILE" | awk '{print $NF}') + persist "NEWPATCH" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Commit to local branch." + git commit -a -F "$COMMITMSG_FILE" \ + || die "'git commit -a' failed." +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Upload for code review." + echo -n "Please enter the email address of a V8 reviewer for your patch: " + read REVIEWER + git cl upload -r "$REVIEWER" --send-mail \ + || die "'git cl upload' failed, please try again." +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + restore_if_unset "MERGE_TO_BRANCH" + git checkout $BRANCHNAME \ + || die "cannot ensure that the current branch is $BRANCHNAME" + echo ">>> Step $CURRENT_STEP: Commit to the repository." + echo "Please wait for an LGTM, then type \"LGTM<Return>\" to commit your \ +change. (If you need to iterate on the patch or double check that it's \ +sane, do so in another shell, but remember to not change the headline of \ +the uploaded CL." + unset ANSWER + while [ "$ANSWER" != "LGTM" ] ; do + [[ -n "$ANSWER" ]] && echo "That was not 'LGTM'." + echo -n "> " + read ANSWER + done + git cl dcommit || die "failed to commit to $MERGE_TO_BRANCH" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + restore_if_unset "NEW_COMMIT_MSG" + restore_if_unset "MERGE_TO_BRANCH" + echo ">>> Step $CURRENT_STEP: Determine svn commit revision" + git svn fetch || die "'git svn fetch' failed." + COMMIT_HASH=$(git log -1 --format=%H --grep="$NEW_COMMIT_MSG" \ +svn/$MERGE_TO_BRANCH) + [[ -z "$COMMIT_HASH" ]] && die "Unable to map git commit to svn revision" + SVN_REVISION=$(git svn find-rev $COMMIT_HASH) + echo "subversion revision number is r$SVN_REVISION" + persist "SVN_REVISION" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + restore_if_unset "SVN_REVISION" + restore_if_unset "NEWMAJOR" + restore_if_unset "NEWMINOR" + restore_if_unset "NEWBUILD" + restore_if_unset "NEWPATCH" + echo ">>> Step $CURRENT_STEP: Create the tag." + echo "Creating tag svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH" + svn copy -r $SVN_REVISION \ +https://v8.googlecode.com/svn/branches/$MERGE_TO_BRANCH \ +https://v8.googlecode.com/svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH \ +-m "Tagging version $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH" +fi + +let CURRENT_STEP+=1 +if [ $START_STEP -le $CURRENT_STEP ] ; then + echo ">>> Step $CURRENT_STEP: Cleanup." + restore_if_unset "CURRENT_BRANCH" + git checkout -f $CURRENT_BRANCH + [[ "$BRANCHNAME" != "$CURRENT_BRANCH" ]] && git branch -D $BRANCHNAME + rm -f "$ALREADY_MERGING_SENTINEL_FILE" +fi diff --git a/deps/v8/tools/presubmit.py b/deps/v8/tools/presubmit.py index fda7ba96e5..a5f4c614d0 100755 --- a/deps/v8/tools/presubmit.py +++ b/deps/v8/tools/presubmit.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 the V8 project authors. All rights reserved. +# Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: @@ -42,6 +42,7 @@ import pickle import re import sys import subprocess +import multiprocessing from subprocess import PIPE # Disabled LINT rules and reason. @@ -101,6 +102,33 @@ whitespace/todo """.split() +LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]|^Done processing') + + +def CppLintWorker(command): + try: + process = subprocess.Popen(command, stderr=subprocess.PIPE) + process.wait() + out_lines = "" + error_count = -1 + while True: + out_line = process.stderr.readline() + if out_line == '' and process.poll() != None: + break + m = LINT_OUTPUT_PATTERN.match(out_line) + if m: + out_lines += out_line + error_count += 1 + sys.stderr.write(out_lines) + return error_count + except KeyboardInterrupt: + process.kill() + except: + print('Error running cpplint.py. Please make sure you have depot_tools' + + ' in your $PATH. Lint check skipped.') + process.kill() + + class FileContentsCache(object): def __init__(self, sums_file_name): @@ -206,24 +234,28 @@ class CppLintProcessor(SourceFileProcessor): return True filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES]) - command = ['cpplint.py', '--filter', filt] + join(files) + command = ['cpplint.py', '--filter', filt] local_cpplint = join(path, "tools", "cpplint.py") if exists(local_cpplint): - command = ['python', local_cpplint, '--filter', filt] + join(files) + command = ['python', local_cpplint, '--filter', filt] - process = subprocess.Popen(command, stderr=subprocess.PIPE) - LINT_ERROR_PATTERN = re.compile(r'^(.+)[:(]\d+[:)]') - while True: - out_line = process.stderr.readline() - if out_line == '' and process.poll() != None: - break - sys.stderr.write(out_line) - m = LINT_ERROR_PATTERN.match(out_line) - if m: - good_files_cache.RemoveFile(m.group(1)) + commands = join([command + [file] for file in files]) + count = multiprocessing.cpu_count() + pool = multiprocessing.Pool(count) + try: + results = pool.map_async(CppLintWorker, commands).get(999999) + except KeyboardInterrupt: + print "\nCaught KeyboardInterrupt, terminating workers." + sys.exit(1) + + for i in range(len(files)): + if results[i] > 0: + good_files_cache.RemoveFile(files[i]) + total_errors = sum(results) + print "Total errors found: %d" % total_errors good_files_cache.Save() - return process.returncode == 0 + return total_errors == 0 COPYRIGHT_HEADER_PATTERN = re.compile( diff --git a/deps/v8/tools/push-to-trunk.sh b/deps/v8/tools/push-to-trunk.sh index 761b733679..302c5f299b 100755 --- a/deps/v8/tools/push-to-trunk.sh +++ b/deps/v8/tools/push-to-trunk.sh @@ -38,6 +38,7 @@ CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry" PATCH_FILE="$PERSISTFILE_BASENAME-patch" COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg" TOUCHED_FILES_FILE="$PERSISTFILE_BASENAME-touched-files" +TRUNK_REVISION_FILE="$PERSISTFILE_BASENAME-trunkrevision" STEP=0 @@ -202,10 +203,14 @@ if [ $STEP -le 4 ] ; then for commit in $COMMITS ; do # Get the commit's title line. git log -1 $commit --format="%w(80,8,8)%s" >> "$CHANGELOG_ENTRY_FILE" - # Grep for "BUG=xxxx" lines in the commit message. - git log -1 $commit --format="%b" | grep BUG= | grep -v "BUG=$" \ - | sed -e 's/^/ /' \ - >> "$CHANGELOG_ENTRY_FILE" + # Grep for "BUG=xxxx" lines in the commit message and convert them to + # "(issue xxxx)". + git log -1 $commit --format="%B" \ + | grep "^BUG=" | grep -v "BUG=$" \ + | sed -e 's/^/ /' \ + | sed -e 's/BUG=v8:\(.*\)$/(issue \1)/' \ + | sed -e 's/BUG=\(.*\)$/(Chromium issue \1)/' \ + >> "$CHANGELOG_ENTRY_FILE" # Append the commit's author for reference. git log -1 $commit --format="%w(80,8,8)(%an)" >> "$CHANGELOG_ENTRY_FILE" echo "" >> "$CHANGELOG_ENTRY_FILE" @@ -221,7 +226,13 @@ exit your EDITOR. " $EDITOR "$CHANGELOG_ENTRY_FILE" NEWCHANGELOG=$(mktemp) # Eliminate any trailing newlines by going through a shell variable. - CHANGELOGENTRY=$(cat "$CHANGELOG_ENTRY_FILE") + # Also (1) eliminate tabs, (2) fix too little and (3) too much indentation, + # and (4) eliminate trailing whitespace. + CHANGELOGENTRY=$(cat "$CHANGELOG_ENTRY_FILE" \ + | sed -e 's/\t/ /g' \ + | sed -e 's/^ \{1,7\}\([^ ]\)/ \1/g' \ + | sed -e 's/^ \{9,80\}\([^ ]\)/ \1/g' \ + | sed -e 's/ \+$//') [[ -n "$CHANGELOGENTRY" ]] || die "Empty ChangeLog entry." echo "$CHANGELOGENTRY" > "$NEWCHANGELOG" echo "" >> "$NEWCHANGELOG" # Explicitly insert two empty lines. @@ -256,8 +267,10 @@ if [ $STEP -le 7 ] ; then restore_if_unset "NEWMAJOR" restore_if_unset "NEWMINOR" restore_if_unset "NEWBUILD" - git commit -a -m "Prepare push to trunk. \ -Now working on version $NEWMAJOR.$NEWMINOR.$NEWBUILD." \ + PREPARE_COMMIT_MSG="Prepare push to trunk. \ +Now working on version $NEWMAJOR.$NEWMINOR.$NEWBUILD." + persist "PREPARE_COMMIT_MSG" + git commit -a -m "$PREPARE_COMMIT_MSG" \ || die "'git commit -a' failed." fi @@ -272,7 +285,8 @@ fi if [ $STEP -le 9 ] ; then echo ">>> Step 9: Commit to the repository." echo "Please wait for an LGTM, then type \"LGTM<Return>\" to commit your \ -change. (If you need to iterate on the patch, do so in another shell.)" +change. (If you need to iterate on the patch, do so in another shell. Do not \ +modify the existing local commit's commit message.)" unset ANSWER while [ "$ANSWER" != "LGTM" ] ; do [[ -n "$ANSWER" ]] && echo "That was not 'LGTM'." @@ -294,15 +308,21 @@ change. (If you need to iterate on the patch, do so in another shell.)" fi if [ $STEP -le 10 ] ; then - echo ">>> Step 10: NOP" - # Present in the manual guide, not necessary (even harmful!) for this script. + echo ">>> Step 10: Fetch straggler commits that sneaked in between \ +steps 1 and 9." + git svn fetch || die "'git svn fetch' failed." + git checkout svn/bleeding_edge + restore_if_unset "PREPARE_COMMIT_MSG" + PREPARE_COMMIT_HASH=$(git log -1 --format=%H --grep="$PREPARE_COMMIT_MSG") + persist "PREPARE_COMMIT_HASH" fi if [ $STEP -le 11 ] ; then echo ">>> Step 11: Squash commits into one." # Instead of relying on "git rebase -i", we'll just create a diff, because # that's easier to automate. - git diff svn/trunk > "$PATCH_FILE" + restore_if_unset "PREPARE_COMMIT_HASH" + git diff svn/trunk $PREPARE_COMMIT_HASH > "$PATCH_FILE" # Convert the ChangeLog entry to commit message format: # - remove date # - remove indentation @@ -397,7 +417,10 @@ fi if [ $STEP -le 17 ] ; then echo ">>> Step 17. Commit to SVN." - git svn dcommit || die "'git svn dcommit' failed." + git svn dcommit | tee >(grep -E "^Committed r[0-9]+" \ + | sed -e 's/^Committed r\([0-9]\+\)/\1/' \ + > "$TRUNK_REVISION_FILE") \ + || die "'git svn dcommit' failed." fi if [ $STEP -le 18 ] ; then @@ -424,8 +447,10 @@ if [ $STEP -le 20 ] ; then restore_if_unset "MINOR" restore_if_unset "BUILD" echo "Congratulations, you have successfully created the trunk revision \ -$MAJOR.$MINOR.$BUILD. Please don't forget to update the v8rel spreadsheet, \ -and to roll this new version into Chromium." +$MAJOR.$MINOR.$BUILD. Please don't forget to roll this new version into \ +Chromium, and to update the v8rel spreadsheet:" + TRUNK_REVISION=$(cat "$TRUNK_REVISION_FILE") + echo -e "$MAJOR.$MINOR.$BUILD\ttrunk\t$TRUNK_REVISION" # Clean up all temporary files. rm -f "$PERSISTFILE_BASENAME"* fi diff --git a/deps/v8/tools/test-wrapper-gypbuild.py b/deps/v8/tools/test-wrapper-gypbuild.py index ad5449a404..e9984d76c1 100755 --- a/deps/v8/tools/test-wrapper-gypbuild.py +++ b/deps/v8/tools/test-wrapper-gypbuild.py @@ -131,18 +131,22 @@ def BuildOptions(): def ProcessOptions(options): - if options.arch_and_mode != None and options.arch_and_mode != "": - tokens = options.arch_and_mode.split(".") - options.arch = tokens[0] - options.mode = tokens[1] - options.mode = options.mode.split(',') + if options.arch_and_mode == ".": + options.arch = [] + options.mode = [] + else: + if options.arch_and_mode != None and options.arch_and_mode != "": + tokens = options.arch_and_mode.split(".") + options.arch = tokens[0] + options.mode = tokens[1] + options.mode = options.mode.split(',') + options.arch = options.arch.split(',') for mode in options.mode: if not mode in ['debug', 'release']: print "Unknown mode %s" % mode return False - options.arch = options.arch.split(',') for arch in options.arch: - if not arch in ['ia32', 'x64', 'arm']: + if not arch in ['ia32', 'x64', 'arm', 'mips']: print "Unknown architecture %s" % arch return False @@ -165,7 +169,7 @@ def PassOnOptions(options): if options.snapshot: result += ['--snapshot'] if options.special_command: - result += ['--special-command=' + options.special_command] + result += ['--special-command="%s"' % options.special_command] if options.valgrind: result += ['--valgrind'] if options.cat: @@ -232,6 +236,18 @@ def Main(): env=env) returncodes += child.wait() + if len(options.mode) == 0 and len(options.arch) == 0: + print ">>> running tests" + shellpath = workspace + '/' + options.outdir + env['LD_LIBRARY_PATH'] = shellpath + '/lib.target' + shell = shellpath + '/d8' + child = subprocess.Popen(' '.join(args_for_children + + ['--shell=' + shell]), + shell=True, + cwd=workspace, + env=env) + returncodes = child.wait() + return returncodes diff --git a/deps/v8/tools/test.py b/deps/v8/tools/test.py index ecc0062da5..c8f9da52ec 100755 --- a/deps/v8/tools/test.py +++ b/deps/v8/tools/test.py @@ -711,7 +711,7 @@ class Context(object): def GetTimeout(self, testcase, mode): result = self.timeout * TIMEOUT_SCALEFACTOR[mode] if '--stress-opt' in self.GetVmFlags(testcase, mode): - return result * 2 + return result * 4 else: return result @@ -1211,6 +1211,7 @@ def BuildOptions(): dest="suppress_dialogs", default=True, action="store_true") result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests", dest="suppress_dialogs", action="store_false") + result.add_option("--mips-arch-variant", help="mips architecture variant: mips32r1/mips32r2", default="mips32r2"); result.add_option("--shell", help="Path to V8 shell", default="d8") result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true") result.add_option("--store-unexpected-output", @@ -1272,6 +1273,9 @@ def ProcessOptions(options): if options.snapshot: options.scons_flags.append("snapshot=on") global VARIANT_FLAGS + if options.mips_arch_variant: + options.scons_flags.append("mips_arch_variant=" + options.mips_arch_variant) + if options.stress_only: VARIANT_FLAGS = [['--stress-opt', '--always-opt']] if options.nostress: diff --git a/deps/v8/tools/utils.py b/deps/v8/tools/utils.py index fb94d14186..232314cdee 100644 --- a/deps/v8/tools/utils.py +++ b/deps/v8/tools/utils.py @@ -61,6 +61,8 @@ def GuessOS(): return 'openbsd' elif id == 'SunOS': return 'solaris' + elif id == 'NetBSD': + return 'netbsd' else: return None diff --git a/doc/about/index.html b/doc/about/index.html index 04ba00aeb1..087cd48b80 100644 --- a/doc/about/index.html +++ b/doc/about/index.html @@ -21,7 +21,7 @@ <body class="alt int" id="about"> <div id="intro" class="interior"> <a href="/" title="Go back to the home page"> - <img id="logo" src="../logo-light.png" alt="node.js"> + <img id="logo" src="http://nodejs.org/images/logo-light.png" alt="node.js"> </a> </div> <div id="content" class="clearfix"> @@ -130,7 +130,7 @@ console.log('Server running at http://127.0.0.1:1337/');</pre> <li><a href="http://twitter.com/nodejs" class="twitter">@nodejs</a></li> </ul> - <p>Copyright 2010 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">license</a>.</p> + <p>Copyright 2012 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">license</a>.</p> </div> diff --git a/doc/api/child_processes.markdown b/doc/api/child_processes.markdown index d4c990e65e..57aa945209 100644 --- a/doc/api/child_processes.markdown +++ b/doc/api/child_processes.markdown @@ -24,6 +24,13 @@ of the signal, otherwise `null`. See `waitpid(2)`. +### Event: 'disconnect' + +This event is emitted after using the `.disconnect()` method in the parent or +in the child. After disconnecting it is no longer possible to send messages. +An alternative way to check if you can send messages is to see if the +`child.connected` property is `true`. + ### child.stdin A `Writable Stream` that represents the child process's `stdin`. @@ -198,7 +205,7 @@ subshell but rather the specified file directly. This makes it slightly leaner than `child_process.exec`. It has the same options. -### child_process.fork(modulePath, arguments, options) +### child_process.fork(modulePath, [arguments], [options]) This is a special case of the `spawn()` functionality for spawning Node processes. In addition to having all the methods in a normal ChildProcess @@ -229,8 +236,15 @@ And then the child script, `'sub.js'` might look like this: In the child the `process` object will have a `send()` method, and `process` will emit objects each time it receives a message on its channel. -By default the spawned Node process will have the stdin, stdout, stderr -associated with the parent's. +There is a special case when sending a `{cmd: 'NODE_foo'}` message. All messages +containing a `NODE_` prefix in its `cmd` property will not be emitted in +the `message` event, since they are internal messages used by node core. +Messages containing the prefix are emitted in the `internalMessage` event, you +should by all means avoid using this feature, it may change without warranty. + +By default the spawned Node process will have the stdout, stderr associated +with the parent's. To change this behavior set the `silent` property in the +`options` object to `true`. These child Nodes are still whole new instances of V8. Assume at least 30ms startup and 10mb memory for each new Node. That is, you cannot create many @@ -257,7 +271,12 @@ processes: } }); - +To close the IPC connection between parent and child use the +`child.disconnect()` method. This allows the child to exit gracefully since +there is no IPC channel keeping it alive. When calling this method the +`disconnect` event will be emitted in both parent and child, and the +`connected` flag will be set to `false`. Please note that you can also call +`process.disconnect()` in the child process. ### child.kill([signal]) diff --git a/doc/api/cluster.markdown b/doc/api/cluster.markdown index 7f1b19c69f..e16239e1eb 100644 --- a/doc/api/cluster.markdown +++ b/doc/api/cluster.markdown @@ -21,8 +21,9 @@ all share server ports. console.log('worker ' + worker.pid + ' died'); }); } else { - // Worker processes have a http server. - http.Server(function(req, res) { + // Workers can share any TCP connection + // In this case its a HTTP server + http.createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); @@ -34,64 +35,254 @@ Running node will now share port 8000 between the workers: Worker 2438 online Worker 2437 online -The difference between `cluster.fork()` and `child_process.fork()` is simply -that cluster allows TCP servers to be shared between workers. `cluster.fork` -is implemented on top of `child_process.fork`. The message passing API that -is available with `child_process.fork` is available with `cluster` as well. -As an example, here is a cluster which keeps count of the number of requests -in the master process via message passing: + +### cluster.isMaster + +This boolean flag is true if the process is a master. This is determined +by the `process.env.NODE_UNIQUE_ID`. If `process.env.NODE_UNIQUE_ID` is +undefined `isMaster` is `true`. + +### cluster.isWorker + +This boolean flag is true if the process is a worker forked from a master. +If the `process.env.NODE_UNIQUE_ID` is set to a value different efined +`isWorker` is `true`. + +### Event: 'fork' + +When a new worker is forked the cluster module will emit a 'fork' event. +This can be used to log worker activity, and create you own timeout. + + var timeouts = []; + var errorMsg = function () { + console.error("Something must be wrong with the connection ..."); + }); + + cluster.on('fork', function (worker) { + timeouts[worker.uniqueID] = setTimeout(errorMsg, 2000); + }); + cluster.on('listening', function (worker) { + clearTimeout(timeouts[worker.uniqueID]); + }); + cluster.on('death', function (worker) { + clearTimeout(timeouts[worker.uniqueID]); + errorMsg(); + }); + +### Event: 'online' + +After forking a new worker, the worker should respond with a online message. +When the master receives a online message it will emit such event. +The difference between 'fork' and 'online' is that fork is emitted when the +master tries to fork a worker, and 'online' is emitted when the worker is being +executed. + + cluster.on('online', function (worker) { + console.log("Yay, the worker responded after it was forked"); + }); + +### Event: 'listening' + +When calling `listen()` from a worker, a 'listening' event is automatically assigned +to the server instance. When the server is listening a message is send to the master +where the 'listening' event is emitted. + + cluster.on('listening', function (worker) { + console.log("We are now connected"); + }); + +### Event: 'death' + +When any of the workers die the cluster module will emit the 'death' event. +This can be used to restart the worker by calling `fork()` again. + + cluster.on('death', function(worker) { + console.log('worker ' + worker.pid + ' died. restart...'); + cluster.fork(); + }); + +### Event 'setup' + +When the `.setupMaster()` function has been executed this event emits. If `.setupMaster()` +was not executed before `fork()` this function will call `.setupMaster()` with no arguments. + +### cluster.setupMaster([options]) + +The `setupMaster` is used to change the default 'fork' behavior. It takes one option +object argument. + +Example: + + var cluster = require("cluster"); + cluster.setupMaster({ + exec : "worker.js", + args : ["--use", "https"], + silent : true + }); + cluster.autoFork(); + +The options argument can contain 3 different properties. + +- `exec` are the file path to the worker file, by default this is the same file as the master. +- `args` are a array of arguments send along with the worker, by default this is `process.argv.slice(2)`. +- `silent`, if this option is true the output of a worker won't propagate to the master, by default this is false. + +### cluster.settings + +All settings set by the `.setupMaster` is stored in this settings object. +This object is not supposed to be change or set manually, by you. + +All propertys are `undefined` if they are not yet set. + +### cluster.fork([env]) + +Spawn a new worker process. This can only be called from the master process. +The function takes an optional `env` object. The properties in this object +will be added to the process environment in the worker. + +### cluster.workers + +In the cluster all living worker objects are stored in this object by there +`uniqueID` as the key. This makes it easy to loop through all living workers. + + // Go through all workers + function eachWorker(callback) { + for (var uniqueID in cluster.workers) { + callback(cluster.workers[uniqueID]); + } + } + eachWorker(function (worker) { + worker.send('big announcement to all workers'); + }); + +Should you wish to reference a worker over a communication channel, using +the worker's uniqueID is the easiest way to find the worker. + + socket.on('data', function (uniqueID) { + var worker = cluster.workers[uniqueID]; + }); + +## Worker + +This object contains all public information and method about a worker. +In the master it can be obtained using `cluster.workers`. In a worker +it can be obtained using `cluster.worker`. + +### Worker.uniqueID + +Each new worker is given its own unique id, this id is stored in the `uniqueID`. + +### Worker.process + +All workers are created using `child_process.fork()`, the returned object from this +function is stored in process. + +### Worker.send(message, [sendHandle]) + +This function is equal to the send methods provided by `child_process.fork()`. +In the master you should use this function to send a message to a specific worker. +However in a worker you can also use `process.send(message)`, since this is the same +function. + +This example will echo back all messages from the master: + + if (cluster.isMaster) { + var worker = cluster.fork(); + worker.send('hi there'); + + } else if (cluster.isWorker) { + process.on('message', function (msg) { + process.send(msg); + }); + } + +### Worker.destroy() + +This function will kill the worker, and inform the master to not spawn a new worker. +To know the difference between suicide and accidentally death a suicide boolean is set to true. + + cluster.on('death', function (worker) { + if (worker.suicide === true) { + console.log('Oh, it was just suicide\' – no need to worry'). + } + }); + + // destroy worker + worker.destroy(); + +### Worker.suicide + +This property is a boolean. It is set when a worker dies, until then it is `undefined`. +It is true if the worker was killed using the `.destroy()` method, and false otherwise. + +### Event: message + +This event is the same as the one provided by `child_process.fork()`. +In the master you should use this event, however in a worker you can also use +`process.on('message')` + +As an example, here is a cluster that keeps count of the number of requests +in the master process using the message system: var cluster = require('cluster'); var http = require('http'); - var numReqs = 0; if (cluster.isMaster) { - // Fork workers. - for (var i = 0; i < 2; i++) { - var worker = cluster.fork(); - - worker.on('message', function(msg) { - if (msg.cmd && msg.cmd == 'notifyRequest') { - numReqs++; - } - }); - } + // Keep track of http requests + var numReqs = 0; setInterval(function() { console.log("numReqs =", numReqs); }, 1000); + + // Count requestes + var messageHandler = function (msg) { + if (msg.cmd && msg.cmd == 'notifyRequest') { + numReqs += 1; + } + }; + + // Start workers and listen for messages containing notifyRequest + cluster.autoFork(); + Object.keys(cluster.workers).forEach(function (uniqueID) { + cluster.workers[uniqueID].on('message', messageHandler); + }); + } else { + // Worker processes have a http server. http.Server(function(req, res) { res.writeHead(200); res.end("hello world\n"); - // Send message to master process + + // notify master about the request process.send({ cmd: 'notifyRequest' }); }).listen(8000); } +### Event: online +Same as the `cluster.on('online')` event, but emits only when the state change +on the specified worker. -### cluster.fork() + cluster.fork().on('online', function (worker) { + // Worker is online + }; -Spawn a new worker process. This can only be called from the master process. +### Event: listening -### cluster.isMaster -### cluster.isWorker +Same as the `cluster.on('listening')` event, but emits only when the state change +on the specified worker. -Boolean flags to determine if the current process is a master or a worker -process in a cluster. A process `isMaster` if `process.env.NODE_WORKER_ID` -is undefined. + cluster.fork().on('listening', function (worker) { + // Worker is listening + }; -### Event: 'death' +### Event: death -When any of the workers die the cluster module will emit the 'death' event. -This can be used to restart the worker by calling `fork()` again. +Same as the `cluster.on('death')` event, but emits only when the state change +on the specified worker. - cluster.on('death', function(worker) { - console.log('worker ' + worker.pid + ' died. restart...'); - cluster.fork(); - }); - -Different techniques can be used to restart the worker depending on the -application. + cluster.fork().on('death', function (worker) { + // Worker has died + }; diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index fd516acd6a..cbc3955f9a 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -123,6 +123,12 @@ Returns any remaining enciphered contents, with `output_encoding` being one of: Note: `cipher` object can not be used after `final()` method been called. +### cipher.setAutoPadding(auto_padding=true) + +You can disable automatic padding of the input data to block size. If `auto_padding` is false, +the length of the entire input data must be a multiple of the cipher's block size or `final` will fail. +Useful for non-standard padding, e.g. using `0x0` instead of PKCS padding. You must call this before `cipher.final`. + ### crypto.createDecipher(algorithm, password) @@ -150,6 +156,12 @@ Defaults to `'binary'`. Note: `decipher` object can not be used after `final()` method been called. +### decipher.setAutoPadding(auto_padding=true) + +You can disable auto padding if the data has been encrypted without standard block padding to prevent +`decipher.final` from checking and removing it. Can only work if the input data's length is a multiple of the +ciphers block size. You must call this before streaming data to `decipher.update`. + ### crypto.createSign(algorithm) diff --git a/doc/api/debugger.markdown b/doc/api/debugger.markdown index 5583f8685d..122435cbb3 100644 --- a/doc/api/debugger.markdown +++ b/doc/api/debugger.markdown @@ -93,10 +93,12 @@ prints the active watchers. To remove a watcher, type * `next`, `n` - Step next * `step`, `s` - Step in * `out`, `o` - Step out +* `pause` - Pause running code (like pause button in Developer TOols) #### Breakpoints * `setBreakpoint()`, `sb()` - Set breakpoint on current line +* `setBreakpoint(line)`, `sb(line)` - Set breakpoint on specific line * `setBreakpoint('fn()')`, `sb(...)` - Set breakpoint on a first statement in functions body * `setBreakpoint('script.js', 1)`, `sb(...)` - Set breakpoint on first line of diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 88d4c28294..16f8fbb35a 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -259,17 +259,29 @@ An exception occurs if the file does not exist. * `'w'` - Open file for writing. The file is created (if it does not exist) or truncated (if it exists). +* `'wx'` - Like `'w'` but opens the file in exclusive mode. + * `'w+'` - Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). +* `'wx+'` - Like `'w+'` but opens the file in exclusive mode. + * `'a'` - Open file for appending. The file is created if it does not exist. +* `'ax'` - Like `'a'` but opens the file in exclusive mode. + * `'a+'` - Open file for reading and appending. The file is created if it does not exist. +* `'ax+'` - Like `'a+'` but opens the file in exclusive mode. + `mode` defaults to `0666`. The callback gets two arguments `(err, fd)`. +Exclusive mode (`O_EXCL`) ensures that `path` is newly created. `fs.open()` +fails if a file by that name already exists. On POSIX systems, symlinks are +not followed. Exclusive mode may or may not work with network file systems. + ### fs.openSync(path, flags, [mode]) Synchronous open(2). @@ -387,6 +399,23 @@ Example: The synchronous version of `fs.writeFile`. +### fs.appendFile(filename, data, encoding='utf8', [callback]) + +Asynchronously append data to a file, creating the file if it not yet exists. +`data` can be a string or a buffer. The `encoding` argument is ignored if +`data` is a buffer. + +Example: + + fs.appendFile('message.txt', 'data to append', function (err) { + if (err) throw err; + console.log('The "data to append" was appended to file!'); + }); + +### fs.appendFileSync(filename, data, encoding='utf8') + +The synchronous version of `fs.appendFile`. + ### fs.watchFile(filename, [options], listener) Watch for changes on `filename`. The callback `listener` will be called each @@ -447,6 +476,20 @@ callback, and have some fallback logic if it is null. } }); +### fs.exists(p, [callback]) + +Test whether or not the given path exists by checking with the file system. +Then call the `callback` argument with either true or false. Example: + + fs.exists('/etc/passwd', function (exists) { + util.debug(exists ? "it's there" : "no passwd!"); + }); + + +### fs.existsSync(p) + +Synchronous version of `fs.exists`. + ## fs.Stats Objects returned from `fs.stat()`, `fs.lstat()` and `fs.fstat()` and their diff --git a/doc/api/http.markdown b/doc/api/http.markdown index e5cb91e482..70be739b2e 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -66,6 +66,24 @@ request body. Note that when this event is emitted and handled, the `request` event will not be emitted. +### Event: 'connect' + +`function (request, socket, head) { }` + +Emitted each time a client requests a http CONNECT method. If this event isn't +listened for, then clients requesting a CONNECT method will have their +connections closed. + +* `request` is the arguments for the http request, as it is in the request + event. +* `socket` is the network socket between the server and client. +* `head` is an instance of Buffer, the first packet of the tunneling stream, + this may be empty. + +After this event is emitted, the request's socket will not have a `data` +event listener, meaning you will need to bind to it in order to handle data +sent to the server on that socket. + ### Event: 'upgrade' `function (request, socket, head) { }` @@ -74,9 +92,11 @@ Emitted each time a client requests a http upgrade. If this event isn't listened for, then clients requesting an upgrade will have their connections closed. -* `request` is the arguments for the http request, as it is in the request event. +* `request` is the arguments for the http request, as it is in the request + event. * `socket` is the network socket between the server and client. -* `head` is an instance of Buffer, the first packet of the upgraded stream, this may be empty. +* `head` is an instance of Buffer, the first packet of the upgraded stream, + this may be empty. After this event is emitted, the request's socket will not have a `data` event listener, meaning you will need to bind to it in order to handle data @@ -123,6 +143,12 @@ Stops the server from accepting new connections. See [net.Server.close()](net.html#server.close). +### server.maxHeadersCount + +Limits maximum incoming headers count, equal to 1000 by default. If set to 0 - +no limit will be applied. + + ## http.ServerRequest This object is created internally by a HTTP server -- not by @@ -318,6 +344,13 @@ or response.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]); +### response.sendDate + +When true, the Date header will be automatically generated and sent in +the response if it is not already present in the headers. Defaults to true. + +This should only be disabled for testing; HTTP requires the Date header +in responses. ### response.getHeader(name) @@ -604,6 +637,69 @@ Options: Emitted after a socket is assigned to this request. +### Event: 'connect' + +`function (response, socket, head) { }` + +Emitted each time a server responds to a request with a CONNECT method. If this +event isn't being listened for, clients receiving a CONNECT method will have +their connections closed. + +A client server pair that show you how to listen for the `connect` event. + + var http = require('http'); + var net = require('net'); + var url = require('url'); + + // Create an HTTP tunneling proxy + var proxy = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + proxy.on('connect', function(req, cltSocket, head) { + // connect to an origin server + var srvUrl = url.parse('http://' + req.url); + var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, function() { + cltSocket.write('HTTP/1.1 200 Connection Established\r\n' + + 'Proxy-agent: Node-Proxy\r\n' + + '\r\n'); + srvSocket.write(head); + srvSocket.pipe(cltSocket); + cltSocket.pipe(srvSocket); + }); + }); + + // now that proxy is running + proxy.listen(1337, '127.0.0.1', function() { + + // make a request to a tunneling proxy + var options = { + port: 1337, + host: '127.0.0.1', + method: 'CONNECT', + path: 'www.google.com:80' + }; + + var req = http.request(options); + req.end(); + + req.on('connect', function(res, socket, head) { + console.log('got connected!'); + + // make a request over an HTTP tunnel + socket.write('GET / HTTP/1.1\r\n' + + 'Host: www.google.com:80\r\n' + + 'Connection: close\r\n' + + '\r\n'); + socket.on('data', function(chunk) { + console.log(chunk.toString()); + }); + socket.on('end', function() { + proxy.close(); + }); + }); + }); + ### Event: 'upgrade' `function (response, socket, head) { }` @@ -612,25 +708,22 @@ Emitted each time a server responds to a request with an upgrade. If this event isn't being listened for, clients receiving an upgrade header will have their connections closed. -A client server pair that show you how to listen for the `upgrade` event using `http.getAgent`: +A client server pair that show you how to listen for the `upgrade` event. var http = require('http'); - var net = require('net'); // Create an HTTP server var srv = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('okay'); }); - srv.on('upgrade', function(req, socket, upgradeHead) { + srv.on('upgrade', function(req, socket, head) { socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + - '\r\n\r\n'); + '\r\n'); - socket.ondata = function(data, start, end) { - socket.write(data.toString('utf8', start, end), 'utf8'); // echo back - }; + socket.pipe(socket); // echo back }); // now that server is running diff --git a/doc/api/https.markdown b/doc/api/https.markdown index 0c019349b4..5de5b4f41b 100644 --- a/doc/api/https.markdown +++ b/doc/api/https.markdown @@ -11,8 +11,8 @@ This class is a subclass of `tls.Server` and emits events same as ## https.createServer(options, [requestListener]) Returns a new HTTPS web server object. The `options` is similar to -`tls.createServer()`. The `requestListener` is a function which is -automatically added to the `'request'` event. +[tls.createServer()](tls.html#tls.createServer). The `requestListener` is +a function which is automatically added to the `'request'` event. Example: @@ -94,6 +94,10 @@ specified. However, a [globalAgent](#https.globalAgent) silently ignores these. - `cert`: Public x509 certificate to use. Default `null`. - `ca`: An authority certificate or array of authority certificates to check the remote host against. +- `rejectUnauthorized`: If `true`, the server certificate is verified against + the list of supplied CAs. An `'error'` event is emitted if verification + fails. Verification happens at the connection level, *before* the HTTP + request is sent. Default `false`. In order to specify these options, use a custom `Agent`. diff --git a/doc/api/modules.markdown b/doc/api/modules.markdown index 7d78610ed1..e73b6de7f5 100644 --- a/doc/api/modules.markdown +++ b/doc/api/modules.markdown @@ -115,6 +115,9 @@ That is, `circle.js` must be in the same directory as `foo.js` for Without a leading '/' or './' to indicate a file, the module is either a "core module" or is loaded from a `node_modules` folder. +If the given path does not exist, `require()` will throw an Error with its +`code` property set to `'MODULE_NOT_FOUND'`. + ### Loading from `node_modules` Folders If the module identifier passed to `require()` is not a native module, diff --git a/doc/api/net.markdown b/doc/api/net.markdown index 7b844b5bd1..28a0601e52 100644 --- a/doc/api/net.markdown +++ b/doc/api/net.markdown @@ -15,7 +15,7 @@ event. { allowHalfOpen: false } -If `allowHalfOpen` is `true`, then the socket won't automatically send FIN +If `allowHalfOpen` is `true`, then the socket won't automatically send a FIN packet when the other end of the socket sends a FIN packet. The socket becomes non-readable, but still writable. You should call the `end()` method explicitly. See ['end'](#event_end_) event for more information. @@ -49,25 +49,29 @@ Use `nc` to connect to a UNIX domain socket server: nc -U /tmp/echo.sock -### net.connect(arguments...) -### net.createConnection(arguments...) +### net.connect(options, [cnnectionListener]) +### net.createConnection(options, [cnnectionListener]) -Construct a new socket object and opens a socket to the given location. When -the socket is established the ['connect'](#event_connect_) event will be +Constructs a new socket object and opens the socket to the given location. +When the socket is established, the ['connect'](#event_connect_) event will be emitted. -The arguments for these methods change the type of connection: +For TCP sockets, `options` argument should be an object which specifies: -* `net.connect(port, [host], [connectListener])` -* `net.createConnection(port, [host], [connectListener])` + - `port`: Port the client should connect to (Required). - Creates a TCP connection to `port` on `host`. If `host` is omitted, - `'localhost'` will be assumed. + - `host`: Host the client should connect to. Defaults to `'localhost'`. -* `net.connect(path, [connectListener])` -* `net.createConnection(path, [connectListener])` +For UNIX domain sockets, `options` argument should be an object which specifies: - Creates unix socket connection to `path`. + - `path`: Path the client should connect to (Required). + +Common options are: + + - `allowHalfOpen`: if `true`, the socket won't automatically send + a FIN packet when the other end of the socket sends a FIN packet. + Defaults to `false`. + See ['end'](#event_end_) event for more information. The `connectListener` parameter will be added as an listener for the ['connect'](#event_connect_) event. @@ -75,7 +79,8 @@ The `connectListener` parameter will be added as an listener for the Here is an example of a client of echo server as described previously: var net = require('net'); - var client = net.connect(8124, function() { //'connect' listener + var client = net.connect({port: 8124}, + function() { //'connect' listener console.log('client connected'); client.write('world!\r\n'); }); @@ -90,7 +95,22 @@ Here is an example of a client of echo server as described previously: To connect on the socket `/tmp/echo.sock` the second line would just be changed to - var client = net.connect('/tmp/echo.sock', function() { //'connect' listener + var client = net.connect({path: '/tmp/echo.sock'}, + +### net.connect(port, [host], [connectListener]) +### net.createConnection(port, [host], [connectListener]) + +Creates a TCP connection to `port` on `host`. If `host` is omitted, +`'localhost'` will be assumed. +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. + +### net.connect(path, [connectListener]) +### net.createConnection(path, [connectListener]) + +Creates unix socket connection to `path`. +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. --- diff --git a/doc/api/path.markdown b/doc/api/path.markdown index ba48bd29c8..5c33adb4f0 100644 --- a/doc/api/path.markdown +++ b/doc/api/path.markdown @@ -4,9 +4,6 @@ This module contains utilities for handling and transforming file paths. Almost all these methods perform only string transformations. The file system is not consulted to check whether paths are valid. -`path.exists` and `path.existsSync` are the exceptions, and should -logically be found in the fs module as they do access the file system. - Use `require('path')` to use this module. The following methods are provided: ### path.normalize(p) @@ -140,16 +137,3 @@ an empty string. Examples: // returns '' -### path.exists(p, [callback]) - -Test whether or not the given path exists by checking with the file system. -Then call the `callback` argument with either true or false. Example: - - path.exists('/etc/passwd', function (exists) { - util.debug(exists ? "it's there" : "no passwd!"); - }); - - -### path.existsSync(p) - -Synchronous version of `path.exists`. diff --git a/doc/api/process.markdown b/doc/api/process.markdown index 57a139f298..8261ddbab1 100644 --- a/doc/api/process.markdown +++ b/doc/api/process.markdown @@ -145,6 +145,11 @@ Example: /usr/local/bin/node +### process.abort() + +This causes node to emit an abort. This will cause node to exit and +generate a core file. + ### process.chdir(directory) Changes the current working directory of the process or throws an exception if that fails. diff --git a/doc/api/querystring.markdown b/doc/api/querystring.markdown index 25741804dc..1dc9f89f3e 100644 --- a/doc/api/querystring.markdown +++ b/doc/api/querystring.markdown @@ -19,12 +19,15 @@ Example: // returns 'foo:bar;baz:qux' -### querystring.parse(str, [sep], [eq]) +### querystring.parse(str, [sep], [eq], [options]) Deserialize a query string to an object. Optionally override the default separator (`'&'`) and assignment (`'='`) characters. +Options object may contain `maxKeys` property (equal to 1000 by default), it'll +be used to limit processed keys. Set it to 0 to remove key count limitation. + Example: querystring.parse('foo=bar&baz=qux&baz=quux&corge') diff --git a/doc/api/readline.markdown b/doc/api/readline.markdown index 8310a9925d..1fb90cfb53 100644 --- a/doc/api/readline.markdown +++ b/doc/api/readline.markdown @@ -4,8 +4,8 @@ To use this module, do `require('readline')`. Readline allows reading of a stream (such as STDIN) on a line-by-line basis. Note that once you've invoked this module, your node program will not -terminate until you've closed the interface, and the STDIN stream. Here's how -to allow your program to gracefully terminate: +terminate until you've paused the interface. Here's how to allow your +program to gracefully pause: var rl = require('readline'); @@ -14,10 +14,7 @@ to allow your program to gracefully terminate: // TODO: Log the answer in a database console.log("Thank you for your valuable feedback."); - // These two lines together allow the program to terminate. Without - // them, it would run forever. - i.close(); - process.stdin.destroy(); + i.pause(); }); ### rl.createInterface(input, output, completer) @@ -48,6 +45,9 @@ Sets the prompt, for example when you run `node` on the command line, you see Readies readline for input from the user, putting the current `setPrompt` options on a new line, giving the user a new spot to write. +This will also resume the `in` stream used with `createInterface` if it has +been paused. + <!-- ### rl.getColumns() Not available? --> ### rl.question(query, callback) @@ -56,27 +56,29 @@ Prepends the prompt with `query` and invokes `callback` with the user's response. Displays the query to the user, and then invokes `callback` with the user's response after it has been typed. +This will also resume the `in` stream used with `createInterface` if it has +been paused. + Example usage: interface.question('What is your favorite food?', function(answer) { console.log('Oh, so your favorite food is ' + answer); }); -### rl.close() - - Closes tty. - ### rl.pause() - Pauses tty. +Pauses the readline `in` stream, allowing it to be resumed later if needed. ### rl.resume() - Resumes tty. +Resumes the readline `in` stream. ### rl.write() - Writes to tty. +Writes to tty. + +This will also resume the `in` stream used with `createInterface` if it has +been paused. ### Event: 'line' @@ -91,27 +93,98 @@ Example of listening for `line`: console.log('You just typed: '+cmd); }); -### Event: 'close' +### Event: 'pause' `function () {}` -Emitted whenever the `in` stream receives a `^C` or `^D`, respectively known -as `SIGINT` and `EOT`. This is a good way to know the user is finished using -your program. +Emitted whenever the `in` stream is paused or receives `^D`, respectively known +as `EOT`. This event is also called if there is no `SIGINT` event listener +present when the `in` stream receives a `^C`, respectively known as `SIGINT`. -Example of listening for `close`, and exiting the program afterward: +Also emitted whenever the `in` stream is not paused and receives the `SIGCONT` +event. (See events `SIGTSTP` and `SIGCONT`) - rl.on('close', function() { - console.log('goodbye!'); - process.exit(0); +Example of listening for `pause`: + + rl.on('pause', function() { + console.log('Readline paused.'); + }); + +### Event: 'resume' + +`function () {}` + +Emitted whenever the `in` stream is resumed. + +Example of listening for `resume`: + + rl.on('resume', function() { + console.log('Readline resumed.'); }); +### Event: 'SIGINT' + +`function () {}` + +Emitted whenever the `in` stream receives a `^C`, respectively known as +`SIGINT`. If there is no `SIGINT` event listener present when the `in` stream +receives a `SIGINT`, `pause` will be triggered. + +Example of listening for `SIGINT`: + + rl.on('SIGINT', function() { + rl.question('Are you sure you want to exit?', function(answer) { + if (answer.match(/^y(es)?$/i)) rl.pause(); + }); + }); + +### Event: 'SIGTSTP' + +`function () {}` + +Emitted whenever the `in` stream receives a `^Z`, respectively known as +`SIGTSTP`. If there is no `SIGTSTP` event listener present when the `in` stream +receives a `SIGTSTP`, the program will be sent to the background. + +When the program is resumed with `fg`, the `pause` and `SIGCONT` events will be +emitted. You can use either to resume the stream. + +The `pause` and `SIGCONT` events will not be triggered if the stream was paused +before the program was sent to the background. + +Example of listening for `SIGTSTP`: + + rl.on('SIGTSTP', function() { + // This will override SIGTSTP and prevent the program from going to the + // background. + console.log('Caught SIGTSTP.'); + }); + +### Event: 'SIGCONT' + +`function () {}` + +Emitted whenever the `in` stream is sent to the background with `^Z`, +respectively known as `SIGTSTP`, and then continued with `fg`. This event only +emits if the stream was not paused before sending the program to the +background. + +Example of listening for `SIGCONT`: + + rl.on('SIGCONT', function() { + // `prompt` will automatically resume the stream + rl.prompt(); + }); + + Here's an example of how to use all these together to craft a tiny command line interface: var readline = require('readline'), - rl = readline.createInterface(process.stdin, process.stdout), - prefix = 'OHAI> '; + rl = readline.createInterface(process.stdin, process.stdout); + + rl.setPrompt('OHAI> '); + rl.prompt(); rl.on('line', function(line) { switch(line.trim()) { @@ -122,18 +195,9 @@ line interface: console.log('Say what? I might have heard `' + line.trim() + '`'); break; } - rl.setPrompt(prefix, prefix.length); rl.prompt(); - }).on('close', function() { + }).on('pause', function() { console.log('Have a great day!'); process.exit(0); }); - console.log(prefix + 'Good to see you. Try typing stuff.'); - rl.setPrompt(prefix, prefix.length); - rl.prompt(); - -Take a look at this slightly more complicated -[example](https://gist.github.com/901104), and -[http-console](https://github.com/cloudhead/http-console) for a real-life use -case. diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 023c34be64..5a4f4632dc 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -138,10 +138,20 @@ You can test this server by connecting to it with `openssl s_client`: openssl s_client -connect 127.0.0.1:8000 -## tls.connect(port, [host], [options], [secureConnectListener]) +#### tls.connect(options, [secureConnectListener]) +#### tls.connect(port, [host], [options], [secureConnectListener]) -Creates a new client connection to the given `port` and `host`. (If `host` -defaults to `localhost`.) `options` should be an object which specifies +Creates a new client connection to the given `port` and `host` (old API) or +`options.port` and `options.host`. (If `host` is omitted, it defaults to +`localhost`.) `options` should be an object which specifies: + + - `host`: Host the client should connect to + + - `port`: Port the client should connect to + + - `socket`: Establish secure connection on a given socket rather than + creating a new socket. If this option is specified, `host` and `port` + are ignored. - `key`: A string or `Buffer` containing the private key of the client in PEM format. @@ -155,6 +165,10 @@ defaults to `localhost`.) `options` should be an object which specifies omitted several well known "root" CAs will be used, like VeriSign. These are used to authorize connections. + - `rejectUnauthorized`: If `true`, the server certificate is verified against + the list of supplied CAs. An `'error'` event is emitted if verification + fails. Default: `false`. + - `NPNProtocols`: An array of string or `Buffer` containing supported NPN protocols. `Buffer` should have following format: `0x05hello0x05world`, where first byte is next protocol name's length. (Passing array should diff --git a/doc/api/zlib.markdown b/doc/api/zlib.markdown index 475723e88b..6bd2c095dd 100644 --- a/doc/api/zlib.markdown +++ b/doc/api/zlib.markdown @@ -215,6 +215,7 @@ relevant when compressing, and are ignored by the decompression classes. * level (compression only) * memLevel (compression only) * strategy (compression only) +* dictionary (deflate/inflate only, empty dictionary by default) See the description of `deflateInit2` and `inflateInit2` at <http://zlib.net/manual.html#Advanced> for more information on these. diff --git a/doc/api_assets/anchor.png b/doc/api_assets/anchor.png Binary files differdeleted file mode 100644 index 1ed163ee1a..0000000000 --- a/doc/api_assets/anchor.png +++ /dev/null diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 0ade5b2231..a9bd886115 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -75,7 +75,7 @@ p { text-rendering: optimizeLegibility; } -.apidoc p { +.apidoc #apicontent p, .apidoc #apicontent li { font-size: 15px; line-height: 22px; color: #000; @@ -242,7 +242,7 @@ code.pre { color: #d2d8ba; /* preload platform-icons.png */ - background-image: url(platform-icons.png); + background-image: url(http://nodejs.org/images/platform-icons.png); background-repeat: no-repeat; background-position: -999em -999em; } @@ -341,15 +341,15 @@ p tt, p code { line-height: 23px; } -#column2.interior li a.home { background: url(icons-interior.png) no-repeat -156px 3px; } -#column2.interior li a.download { background: url(icons-interior.png) no-repeat -156px -21px; } -#column2.interior li a.about { background: url(icons-interior.png) no-repeat -156px -45px; } -#column2.interior li a.npm { background: url(icons-interior.png) no-repeat -156px -69px; } -#column2.interior li a.docs { background: url(icons-interior.png) no-repeat -156px -93px; } -#column2.interior li a.blog { background: url(icons-interior.png) no-repeat -156px -117px; } -#column2.interior li a.community { background: url(icons-interior.png) no-repeat -156px -141px; } -#column2.interior li a.logos { background: url(icons-interior.png) no-repeat -156px -165px; } -#column2.interior li a.jobs { background: url(icons-interior.png) no-repeat -156px -189px; } +#column2.interior li a.home { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px 3px; } +#column2.interior li a.download { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -21px; } +#column2.interior li a.about { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -45px; } +#column2.interior li a.npm { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -69px; } +#column2.interior li a.docs { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -93px; } +#column2.interior li a.blog { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -117px; } +#column2.interior li a.community { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -141px; } +#column2.interior li a.logos { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -165px; } +#column2.interior li a.jobs { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -189px; } #column2.interior li a.home.current { background-position: 2px 3px; } #column2.interior li a.download.current { background-position: 2px -21px; } @@ -381,7 +381,7 @@ p tt, p code { } #column2.interior p.twitter a { - background: url(twitter-bird.png) no-repeat 0 4px; + background: url(http://nodejs.org/images/twitter-bird.png) no-repeat 0 4px; padding-left: 37px; text-decoration: none; } @@ -394,7 +394,7 @@ a.totop { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif; font-weight: bold; text-indent: -9999999px; - background: url(anchor.png) no-repeat top left; + background: url(http://nodejs.org/images/anchor.png) no-repeat top left; margin-right: 7px; display: block; width: 13px; @@ -405,7 +405,7 @@ a.anchor { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif; font-weight: bold; text-indent: -9999999px; - background: url(anchor.png) no-repeat top right; + background: url(http://nodejs.org/images/anchor.png) no-repeat top right; display: block; width: 13px; border-bottom: 1px solid #cccccc; @@ -421,6 +421,9 @@ a.anchor { line-height:1em; padding: 0 0 0 195px; color: #666; +} + +#footer p, #footer li { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif; } @@ -439,7 +442,7 @@ a.anchor { } #footer ul { - background: url(footer-logo-alt.png) left 17px no-repeat; + background: url(http://nodejs.org/images/footer-logo-alt.png) left 17px no-repeat; padding: 23px 0 0 195px; height: 26px; margin-left: -1px; @@ -473,7 +476,7 @@ a.anchor { } #footer ul li a.twitter { - background: url(twitter-bird.png) no-repeat 5px 0px; + background: url(http://nodejs.org/images/twitter-bird.png) no-repeat 5px 0px; padding-left: 25px; } diff --git a/doc/community/index.html b/doc/community/index.html index 65c687b87b..1188d7f9b2 100644 --- a/doc/community/index.html +++ b/doc/community/index.html @@ -23,7 +23,7 @@ <body class="int" id="community"> <div id="intro" class="interior"> <a href="/" title="Go back to the home page"> - <img id="logo" src="../logo.png" alt="node.js"> + <img id="logo" src="http://nodejs.org/images/logo.png" alt="node.js"> </a> </div> <div id="content" class="clearfix"> @@ -163,7 +163,7 @@ </div> <p><a href="http://notinventedhe.re/on/2011-7-26"><img - src="not-invented-here.png" width="100%"></a></p> + src="http://nodejs.org/images/not-invented-here.png" width="100%"></a></p> </div> </div> <div id="footer"> @@ -180,7 +180,7 @@ <li><a href="http://twitter.com/nodejs" class="twitter">@nodejs</a></li> </ul> - <p>Copyright 2010 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">license</a>.</p> + <p>Copyright 2012 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">license</a>.</p> </div> <script> diff --git a/doc/footer-logo-alt.png b/doc/footer-logo-alt.png Binary files differdeleted file mode 100644 index e6d9c2390e..0000000000 --- a/doc/footer-logo-alt.png +++ /dev/null diff --git a/doc/icons-interior.png b/doc/icons-interior.png Binary files differdeleted file mode 100644 index 6b3d483dc0..0000000000 --- a/doc/icons-interior.png +++ /dev/null diff --git a/doc/anchor.png b/doc/images/anchor.png Binary files differindex 1ed163ee1a..1ed163ee1a 100644 --- a/doc/anchor.png +++ b/doc/images/anchor.png diff --git a/doc/close-downloads.png b/doc/images/close-downloads.png Binary files differindex ba72523f7b..ba72523f7b 100644 --- a/doc/close-downloads.png +++ b/doc/images/close-downloads.png diff --git a/doc/community-icons.png b/doc/images/community-icons.png Binary files differindex b3d58f0f1e..b3d58f0f1e 100644 --- a/doc/community-icons.png +++ b/doc/images/community-icons.png diff --git a/doc/download-logo.png b/doc/images/download-logo.png Binary files differindex 253235f4c3..253235f4c3 100644 --- a/doc/download-logo.png +++ b/doc/images/download-logo.png diff --git a/doc/ebay-logo.png b/doc/images/ebay-logo.png Binary files differindex 8a51935dd9..8a51935dd9 100644 --- a/doc/ebay-logo.png +++ b/doc/images/ebay-logo.png diff --git a/doc/api_assets/footer-logo-alt.png b/doc/images/footer-logo-alt.png Binary files differindex e6d9c2390e..e6d9c2390e 100644 --- a/doc/api_assets/footer-logo-alt.png +++ b/doc/images/footer-logo-alt.png diff --git a/doc/footer-logo.png b/doc/images/footer-logo.png Binary files differindex 19a4eaadcb..19a4eaadcb 100644 --- a/doc/footer-logo.png +++ b/doc/images/footer-logo.png diff --git a/doc/home-icons.png b/doc/images/home-icons.png Binary files differindex 421dfdaf11..421dfdaf11 100644 --- a/doc/home-icons.png +++ b/doc/images/home-icons.png diff --git a/doc/api_assets/icons-interior.png b/doc/images/icons-interior.png Binary files differindex 6b3d483dc0..6b3d483dc0 100644 --- a/doc/api_assets/icons-interior.png +++ b/doc/images/icons-interior.png diff --git a/doc/icons.png b/doc/images/icons.png Binary files differindex 421dfdaf11..421dfdaf11 100644 --- a/doc/icons.png +++ b/doc/images/icons.png diff --git a/doc/joyent-logo_orange_nodeorg-01.png b/doc/images/joyent-logo_orange_nodeorg-01.png Binary files differindex 1b977395b8..1b977395b8 100644 --- a/doc/joyent-logo_orange_nodeorg-01.png +++ b/doc/images/joyent-logo_orange_nodeorg-01.png diff --git a/doc/linkedin-logo.png b/doc/images/linkedin-logo.png Binary files differindex 2ad9fb2c0c..2ad9fb2c0c 100644 --- a/doc/linkedin-logo.png +++ b/doc/images/linkedin-logo.png diff --git a/doc/api_assets/logo-light.png b/doc/images/logo-light.png Binary files differindex 898fcecc96..898fcecc96 100644 --- a/doc/api_assets/logo-light.png +++ b/doc/images/logo-light.png diff --git a/doc/logo.png b/doc/images/logo.png Binary files differindex ef3494efa8..ef3494efa8 100644 --- a/doc/logo.png +++ b/doc/images/logo.png diff --git a/doc/logos/monitor.png b/doc/images/logos/monitor.png Binary files differindex 18c804e2df..18c804e2df 100644 --- a/doc/logos/monitor.png +++ b/doc/images/logos/monitor.png diff --git a/doc/logos/node-favicon.png b/doc/images/logos/node-favicon.png Binary files differindex 3802fced9d..3802fced9d 100644 --- a/doc/logos/node-favicon.png +++ b/doc/images/logos/node-favicon.png diff --git a/doc/logos/nodejs-1024x768.png b/doc/images/logos/nodejs-1024x768.png Binary files differindex e571c091d2..e571c091d2 100644 --- a/doc/logos/nodejs-1024x768.png +++ b/doc/images/logos/nodejs-1024x768.png diff --git a/doc/logos/nodejs-1280x1024.png b/doc/images/logos/nodejs-1280x1024.png Binary files differindex 00f65d0de5..00f65d0de5 100644 --- a/doc/logos/nodejs-1280x1024.png +++ b/doc/images/logos/nodejs-1280x1024.png diff --git a/doc/logos/nodejs-1440x900.png b/doc/images/logos/nodejs-1440x900.png Binary files differindex 4b818cc682..4b818cc682 100644 --- a/doc/logos/nodejs-1440x900.png +++ b/doc/images/logos/nodejs-1440x900.png diff --git a/doc/logos/nodejs-1920x1200.png b/doc/images/logos/nodejs-1920x1200.png Binary files differindex 6b31fecf49..6b31fecf49 100644 --- a/doc/logos/nodejs-1920x1200.png +++ b/doc/images/logos/nodejs-1920x1200.png diff --git a/doc/logos/nodejs-2560x1440.png b/doc/images/logos/nodejs-2560x1440.png Binary files differindex ba46b7704e..ba46b7704e 100644 --- a/doc/logos/nodejs-2560x1440.png +++ b/doc/images/logos/nodejs-2560x1440.png diff --git a/doc/logos/nodejs-black.eps b/doc/images/logos/nodejs-black.eps Binary files differindex e3186994f5..e3186994f5 100644 --- a/doc/logos/nodejs-black.eps +++ b/doc/images/logos/nodejs-black.eps diff --git a/doc/logos/nodejs-black.png b/doc/images/logos/nodejs-black.png Binary files differindex a7c6d63db4..a7c6d63db4 100644 --- a/doc/logos/nodejs-black.png +++ b/doc/images/logos/nodejs-black.png diff --git a/doc/logos/nodejs-dark.eps b/doc/images/logos/nodejs-dark.eps Binary files differindex 11c230db15..11c230db15 100644 --- a/doc/logos/nodejs-dark.eps +++ b/doc/images/logos/nodejs-dark.eps diff --git a/doc/logos/nodejs-dark.png b/doc/images/logos/nodejs-dark.png Binary files differindex 56094a9b41..56094a9b41 100644 --- a/doc/logos/nodejs-dark.png +++ b/doc/images/logos/nodejs-dark.png diff --git a/doc/logos/nodejs-green.eps b/doc/images/logos/nodejs-green.eps Binary files differindex 362ac37d89..362ac37d89 100644 --- a/doc/logos/nodejs-green.eps +++ b/doc/images/logos/nodejs-green.eps diff --git a/doc/logos/nodejs-green.png b/doc/images/logos/nodejs-green.png Binary files differindex ee8a2fb550..ee8a2fb550 100644 --- a/doc/logos/nodejs-green.png +++ b/doc/images/logos/nodejs-green.png diff --git a/doc/logos/nodejs-light.eps b/doc/images/logos/nodejs-light.eps Binary files differindex 2317313e4f..2317313e4f 100644 --- a/doc/logos/nodejs-light.eps +++ b/doc/images/logos/nodejs-light.eps diff --git a/doc/logos/nodejs.png b/doc/images/logos/nodejs.png Binary files differindex df49579bb5..df49579bb5 100644 --- a/doc/logos/nodejs.png +++ b/doc/images/logos/nodejs.png diff --git a/doc/microsoft-logo.png b/doc/images/microsoft-logo.png Binary files differindex 6dac0f39b6..6dac0f39b6 100644 --- a/doc/microsoft-logo.png +++ b/doc/images/microsoft-logo.png diff --git a/doc/community/not-invented-here.png b/doc/images/not-invented-here.png Binary files differindex 5d4efa0532..5d4efa0532 100644 --- a/doc/community/not-invented-here.png +++ b/doc/images/not-invented-here.png diff --git a/doc/api_assets/platform-icons.png b/doc/images/platform-icons.png Binary files differindex d6624a5a64..d6624a5a64 100644 --- a/doc/api_assets/platform-icons.png +++ b/doc/images/platform-icons.png diff --git a/doc/ryan-speaker.jpg b/doc/images/ryan-speaker.jpg Binary files differindex c0f6263441..c0f6263441 100644 --- a/doc/ryan-speaker.jpg +++ b/doc/images/ryan-speaker.jpg diff --git a/doc/api_assets/sh_javascript.min.js b/doc/images/sh_javascript.min.js index d12482ced2..d12482ced2 100644 --- a/doc/api_assets/sh_javascript.min.js +++ b/doc/images/sh_javascript.min.js diff --git a/doc/api_assets/sh_main.js b/doc/images/sh_main.js index 27905696c8..27905696c8 100644 --- a/doc/api_assets/sh_main.js +++ b/doc/images/sh_main.js diff --git a/doc/sponsored.png b/doc/images/sponsored.png Binary files differindex e7564e169d..e7564e169d 100644 --- a/doc/sponsored.png +++ b/doc/images/sponsored.png diff --git a/doc/api_assets/twitter-bird.png b/doc/images/twitter-bird.png Binary files differindex b619b2a0d3..b619b2a0d3 100644 --- a/doc/api_assets/twitter-bird.png +++ b/doc/images/twitter-bird.png diff --git a/doc/yahoo-logo.png b/doc/images/yahoo-logo.png Binary files differindex 05d75b3513..05d75b3513 100644 --- a/doc/yahoo-logo.png +++ b/doc/images/yahoo-logo.png diff --git a/doc/index.html b/doc/index.html index 06b04026a9..e4784729b7 100644 --- a/doc/index.html +++ b/doc/index.html @@ -20,7 +20,7 @@ </head> <body id="front"> <div id="intro"> - <img id="logo" src="logo.png" alt="node.js"> + <img id="logo" src="http://nodejs.org/images/logo.png" alt="node.js"> <p>Node.js is a platform built on <a href="http://code.google.com/p/v8/">Chrome's JavaScript runtime</a> @@ -31,12 +31,12 @@ <a href="#download" class="button" id="downloadbutton">Download</a> <a href="http://nodejs.org/docs/latest/api/index.html" class="button" id="docsbutton">Docs</a> - <p class="version">v0.6.11</p> + <p class="version">v0.7.4</p> </div> <div id="quotes" class="clearfix"> <h2>Node.js in the Industry</h2> <ul> - <li class="microsoft"><img src="microsoft-logo.png"> + <li class="microsoft"><img src="http://nodejs.org/images/microsoft-logo.png"> <p>Node gives Azure users the first end-to-end JavaScript experience for the development of a whole new class of real-time applications. @@ -45,7 +45,7 @@ <br> <span>Principal Program Manager, Interoperability Strategy</span></p></li> - <li class="ebay"><img src="ebay-logo.png"> + <li class="ebay"><img src="http://nodejs.org/images/ebay-logo.png"> <p>Node’s evented I/O model freed us from worrying about locking and concurrency issues that are common with multithreaded async I/O. @@ -54,7 +54,7 @@ <br> <span>Principal Member, Technical Staff</span></p></li> - <li class="linkedin"><img src="linkedin-logo.png"> + <li class="linkedin"><img src="http://nodejs.org/images/linkedin-logo.png"> <p>On the server side, our entire mobile software stack is completely built in Node. One reason was scale. The second is Node showed us huge performance gains. @@ -63,7 +63,7 @@ <br> <span>Director of Engineering, Mobile</span></p></li> - <li class="yahoo"><img src="yahoo-logo.png"> + <li class="yahoo"><img src="http://nodejs.org/images/yahoo-logo.png"> <p>Node.js is the execution core of Manhattan. Allowing developers to build one code base using one language – that is the nirvana for developers. @@ -76,17 +76,17 @@ <div id="download"> <a href="#" id="download-close">X</a> - <img id="download-logo" src="download-logo.png" alt="node.js"> + <img id="download-logo" src="http://nodejs.org/images/download-logo.png" alt="node.js"> <ul id="installers" class="clearfix"> - <li><a href="http://nodejs.org/dist/v0.6.11/node-v0.6.11.msi">Windows Installer</a><br>node-v0.6.11.msi</li> - <li><a href="http://nodejs.org/dist/v0.6.11/node-v0.6.11.pkg">Macintosh Installer</a><br>node-v0.6.11.pkg</li> - <li id="source"><a href="http://nodejs.org/dist/v0.6.11/node-v0.6.11.tar.gz">Source Code</a><br>node-v0.6.11.tar.gz</li> + <li><a href="http://nodejs.org/dist/v0.7.4/node-v0.7.4.msi">Windows Installer</a><br>node-v0.7.4.msi</li> + <li><a href="http://nodejs.org/dist/v0.7.4/node-v0.7.4.pkg">Macintosh Installer</a><br>node-v0.7.4.pkg</li> + <li id="source"><a href="http://nodejs.org/dist/v0.7.4/node-v0.7.4.tar.gz">Source Code</a><br>node-v0.7.4.tar.gz</li> </ul> <ul id="documentation"> - <li><a href="https://raw.github.com/joyent/node/v0.6.11/ChangeLog">Change Log</a></li> - <li><a href="http://nodejs.org/docs/v0.6.11/api/index.html">Documentation</a></li> - <li><a href="http://nodejs.org/dist/v0.6.11">Other release files</a></li> - <li><a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">License</a></li> + <li><a href="https://raw.github.com/joyent/node/v0.7.4/ChangeLog">Change Log</a></li> + <li><a href="http://nodejs.org/docs/v0.7.4/api/index.html">Documentation</a></li> + <li><a href="http://nodejs.org/dist/v0.7.4">Other release files</a></li> + <li><a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">License</a></li> <li><a href="https://github.com/joyent/node">Git Repository</a></li> <li><a href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">Installing with a Package Manager</a> @@ -181,7 +181,7 @@ server.listen(1337, '127.0.0.1');</pre> </div> <div id="column2"> <h2>Featured</h2> - <a href="http://www.youtube.com/watch?v=jo_B4LTHi3I"><img src="ryan-speaker.jpg"></a> + <a href="http://www.youtube.com/watch?v=jo_B4LTHi3I"><img src="http://nodejs.org/images/ryan-speaker.jpg"></a> A guided introduction to Node <h2>Explore Node.js</h2> @@ -211,7 +211,7 @@ server.listen(1337, '127.0.0.1');</pre> <!-- <li><a hrfe="http://twitter.com/nodejs" class="twitter">@nodejs</a></li> --> </ul> - <p>Copyright 2010 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">license</a>.</p> + <p>Copyright 2012 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">license</a>.</p> </div> diff --git a/doc/logo-light.png b/doc/logo-light.png Binary files differdeleted file mode 100644 index 898fcecc96..0000000000 --- a/doc/logo-light.png +++ /dev/null diff --git a/doc/logos/index.html b/doc/logos/index.html index 6f75a0eb61..1b36f3a955 100644 --- a/doc/logos/index.html +++ b/doc/logos/index.html @@ -21,7 +21,7 @@ <body class="int" id="logos"> <div id="intro" class="interior"> <a href="/" title="Go back to the home page"> - <img id="logo" src="../logo.png" alt="node.js"> + <img id="logo" src="http://nodejs.org/logo.png" alt="node.js"> </a> </div> <div id="content" class="clearfix"> @@ -45,26 +45,26 @@ <h2>Logo Downloads</h2> <table border="0" cellspacing="0" cellpadding="10"> <tr> - <td bgcolor="#FFFFFF"><a href="nodejs-light.eps"><img src="nodejs.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> - <td bgcolor="#46483E"><a href="nodejs-dark.eps"><img src="nodejs-dark.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> + <td bgcolor="#FFFFFF"><a href="http://nodejs.org/images/logos/nodejs-light.eps"><img src="http://nodejs.org/images/logos/nodejs.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> + <td bgcolor="#46483E"><a href="http://nodejs.org/images/logos/nodejs-dark.eps"><img src="http://nodejs.org/images/logos/nodejs-dark.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> </tr> <tr> - <td><a href="nodejs-light.eps">Node.js standard EPS</a></td> - <td><a href="nodejs-dark.eps">Node.js reversed EPS</a></td> + <td><a href="http://nodejs.org/images/logos/nodejs-light.eps">Node.js standard EPS</a></td> + <td><a href="http://nodejs.org/images/logos/nodejs-dark.eps">Node.js reversed EPS</a></td> </tr> <tr> - <td bgcolor="#8BC84B"><a href="nodejs-green.eps"><img src="nodejs-green.png" alt="Node.js dark" width="212" height="114" border="0"></a><a href="nodejs-dark.eps"></a></td> - <td bgcolor="#ffffff"><a href="nodejs-black.eps"><img src="nodejs-black.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> + <td bgcolor="#8BC84B"><a href="http://nodejs.org/images/logos/nodejs-green.eps"><img src="http://nodejs.org/images/logos/nodejs-green.png" alt="Node.js dark" width="212" height="114" border="0"></a><a href="http://nodejs.org/images/logos/nodejs-dark.eps"></a></td> + <td bgcolor="#ffffff"><a href="http://nodejs.org/images/logos/nodejs-black.eps"><img src="http://nodejs.org/images/logos/nodejs-black.png" alt="Node.js dark" width="212" height="114" border="0"></a></td> </tr> <tr> - <td><a href="nodejs-green.eps">Node.js bright EPS</a></td> - <td><a href="nodejs-black.eps">Node.js 1 color EPS</a></td> + <td><a href="http://nodejs.org/images/logos/nodejs-green.eps">Node.js bright EPS</a></td> + <td><a href="http://nodejs.org/images/logos/nodejs-black.eps">Node.js 1 color EPS</a></td> </tr> </table> <h2>Desktop Background</h2> - <p><img src="monitor.png" width="525" height="398" alt="Screensavers"></p> - <p>Select your screen resolution:<a href="nodejs-1024x768.png"><br> - <span class="desktops">1024 x 768</span></a><span class="desktops"> | <a href="nodejs-1280x1024.png">1280 x 1024</a> | <a href="nodejs-1440x900.png">1440 x 900</a> | <a href="nodejs-1920x1200.png">1920 x 1200</a> | <a href="nodejs-2560x1440.png">2560 x 1440</a></span></p> + <p><img src="http://nodejs.org/images/logos/monitor.png" width="525" height="398" alt="Screensavers"></p> + <p>Select your screen resolution:<a href="http://nodejs.org/images/logos/nodejs-1024x768.png"><br> + <span class="desktops">1024 x 768</span></a><span class="desktops"> | <a href="http://nodejs.org/images/logos/nodejs-1280x1024.png">1280 x 1024</a> | <a href="http://nodejs.org/images/logos/nodejs-1440x900.png">1440 x 900</a> | <a href="http://nodejs.org/images/logos/nodejs-1920x1200.png">1920 x 1200</a> | <a href="http://nodejs.org/images/logos/nodejs-2560x1440.png">2560 x 1440</a></span></p> </div> </div> @@ -82,7 +82,7 @@ <li><a href="http://twitter.com/nodejs" class="twitter">@nodejs</a></li> </ul> - <p>Copyright <a href="http://joyent.com">Joyent, Inc</a>., Node.js is a <a href="/trademark-policy.pdf">trademark of Joyent, Inc</a>., <a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">View License</a></p> + <p>Copyright <a href="http://joyent.com">Joyent, Inc</a>., Node.js is a <a href="/trademark-policy.pdf">trademark of Joyent, Inc</a>., <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">View License</a></p> </div> <script> diff --git a/doc/pipe.css b/doc/pipe.css index 999161b77b..fa8b44c23d 100644 --- a/doc/pipe.css +++ b/doc/pipe.css @@ -67,7 +67,7 @@ h1 a, h2 a, h3 a, h4 a color: #d2d8ba; /* preload platform-icons.png */ - background-image: url(platform-icons.png); + background-image: url(http://nodejs.org/images/platform-icons.png); background-repeat: no-repeat; background-position: -999em -999em; } @@ -240,15 +240,15 @@ h1 a, h2 a, h3 a, h4 a line-height: 23px; } -#column2.interior li a.home { background: url(icons-interior.png) no-repeat -156px 3px; } -#column2.interior li a.download { background: url(icons-interior.png) no-repeat -156px -21px; } -#column2.interior li a.about { background: url(icons-interior.png) no-repeat -156px -44px; } -#column2.interior li a.npm { background: url(icons-interior.png) no-repeat -156px -69px; } -#column2.interior li a.docs { background: url(icons-interior.png) no-repeat -156px -93px; } -#column2.interior li a.blog { background: url(icons-interior.png) no-repeat -156px -117px; } -#column2.interior li a.community { background: url(icons-interior.png) no-repeat -156px -141px; } -#column2.interior li a.logos { background: url(icons-interior.png) no-repeat -156px -165px; } -#column2.interior li a.jobs { background: url(icons-interior.png) no-repeat -156px -189px; } +#column2.interior li a.home { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px 3px; } +#column2.interior li a.download { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -21px; } +#column2.interior li a.about { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -44px; } +#column2.interior li a.npm { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -69px; } +#column2.interior li a.docs { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -93px; } +#column2.interior li a.blog { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -117px; } +#column2.interior li a.community { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -141px; } +#column2.interior li a.logos { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -165px; } +#column2.interior li a.jobs { background: url(http://nodejs.org/images/icons-interior.png) no-repeat -156px -189px; } #column2.interior li a.home.current { background-position: 2px 3px; } #column2.interior li a.download.current { background-position: 2px -21px; } @@ -285,7 +285,7 @@ h1 a, h2 a, h3 a, h4 a } #column2.interior p.twitter a { - background: url(twitter-bird.png) no-repeat 0 4px; + background: url(http://nodejs.org/images/twitter-bird.png) no-repeat 0 4px; padding-left: 37px; } @@ -303,7 +303,7 @@ h1 a, h2 a, h3 a, h4 a color: white; text-transform: none; font-family: Georgia, FreeSerif, Times, serif; - background: url(community-icons.png) no-repeat; + background: url(http://nodejs.org/images/community-icons.png) no-repeat; padding-left: 45px; padding-top: 6px; padding-bottom: 10px; @@ -333,7 +333,7 @@ h1 a, h2 a, h3 a, h4 a } #explore { - background: url(home-icons.png) no-repeat left 17px; + background: url(http://nodejs.org/images/home-icons.png) no-repeat left 17px; } #explore li { @@ -412,7 +412,7 @@ h1 a, h2 a, h3 a, h4 a } #footer ul { - background: url(footer-logo.png) left 17px no-repeat; + background: url(http://nodejs.org/images/footer-logo.png) left 17px no-repeat; border-top: 1px solid #626557; border-color:#90918b; padding-top:23px; @@ -421,7 +421,7 @@ h1 a, h2 a, h3 a, h4 a margin-right:137px; } .alt #footer ul { - background-image: url(footer-logo-alt.png); + background-image: url(http://nodejs.org/images/footer-logo-alt.png); } #footer ul li { @@ -450,7 +450,7 @@ h1 a, h2 a, h3 a, h4 a } #footer ul li a.twitter { - background: url(twitter-bird.png) no-repeat 5px 0px; + background: url(http://nodejs.org/images/twitter-bird.png) no-repeat 5px 0px; padding-left: 25px; } @@ -482,7 +482,7 @@ div#download:target { } #download-close { - background: url(close-downloads.png) no-repeat top right; + background: url(http://nodejs.org/images/close-downloads.png) no-repeat top right; width: 64px; height: 64px; position: absolute; @@ -496,7 +496,7 @@ div#download ul#installers { width: 550px; text-align: center; margin: 0 auto; - background: url(platform-icons.png) no-repeat top center; + background: url(http://nodejs.org/images/platform-icons.png) no-repeat top center; padding-top: 65px; padding-bottom: 50px; } diff --git a/doc/platform-icons.png b/doc/platform-icons.png Binary files differdeleted file mode 100644 index d6624a5a64..0000000000 --- a/doc/platform-icons.png +++ /dev/null diff --git a/doc/robots.txt b/doc/robots.txt new file mode 100644 index 0000000000..1f6fa9b165 --- /dev/null +++ b/doc/robots.txt @@ -0,0 +1,6 @@ +User-Agent: * +Disallow: /dist/ +Disallow: /docs/ +Allow: /dist/latest/ +Allow: /dist/latest/docs/api/ +Allow: /api/ diff --git a/doc/template.html b/doc/template.html index 6cdf913d78..6e170482f9 100644 --- a/doc/template.html +++ b/doc/template.html @@ -2,7 +2,7 @@ <html lang="en"> <head> <meta charset="utf-8"> - <title>{{section}}Node.js v0.6.11 Manual & Documentation</title> + <title>{{section}}Node.js v0.7.4 Manual & Documentation</title> <link rel="stylesheet" href="assets/style.css"> <link rel="stylesheet" href="assets/sh.css"> <link rel="canonical" href="http://nodejs.org/docs/latest/api/{{filename}}.html"> @@ -10,7 +10,7 @@ <body class="alt apidoc"> <div id="intro" class="interior"> <a href="/" title="Go back to the home page"> - <img id="logo" src="assets/logo-light.png" alt="node.js"> + <img id="logo" src="http://nodejs.org/images/logo-light.png" alt="node.js"> </a> </div> <div id="content" class="clearfix"> @@ -31,13 +31,15 @@ <div id="column1" class="interior"> <header> - <h1>Node.js v0.6.11 Manual & Documentation</h1> + <h1>Node.js v0.7.4 Manual & Documentation</h1> <div id="gtoc"> <p><a href="index.html" name="toc">Index</a> | <a href="all.html">View on single page</a></p> </div> <hr> </header> - {{content}} + <div id="apicontent"> + {{content}} + </div> </div> </div> <div id="footer"> @@ -54,7 +56,7 @@ <li><a href="http://twitter.com/nodejs" class="twitter">@nodejs</a></li> </ul> - <p>Copyright 2010 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.6.11/LICENSE">license</a>.</p> + <p>Copyright 2012 <a href="http://joyent.com">Joyent, Inc</a>, Node.js is a <a href="/trademark-policy.pdf">trademark</a> of Joyent, Inc. View <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">license</a>.</p> </div> <script src="assets/sh_main.js"></script> diff --git a/doc/twitter-bird.png b/doc/twitter-bird.png Binary files differdeleted file mode 100644 index b619b2a0d3..0000000000 --- a/doc/twitter-bird.png +++ /dev/null diff --git a/lib/_debugger.js b/lib/_debugger.js index 5a036a6a39..8bd356d744 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -236,6 +236,10 @@ Client.prototype._onResponse = function(res) { this.emit('break', res.body); handled = true; + } else if (res.body && res.body.event == 'exception') { + this.emit('exception', res.body); + handled = true; + } else if (res.body && res.body.event == 'afterCompile') { this._addHandle(res.body.body.script); handled = true; @@ -409,6 +413,16 @@ Client.prototype.reqBacktrace = function(cb) { }; +// reqSetExceptionBreak(type, cb) +// TODO: from, to, bottom +Client.prototype.reqSetExceptionBreak = function(type, cb) { + this.req({ + command: 'setexceptionbreak', + arguments: { type: type, enabled: true } + }, cb); +}; + + // Returns an array of objects like this: // // { handle: 11, @@ -495,7 +509,7 @@ Client.prototype.mirrorObject = function(handle, depth, cb) { var val; - if (handle.type == 'object') { + if (handle.type === 'object') { // The handle looks something like this: // { handle: 8, // type: 'object', @@ -695,7 +709,7 @@ function SourceUnderline(sourceText, position, tty) { function SourceInfo(body) { - var result = 'break in '; + var result = body.exception ? 'exception in ' : 'break in '; if (body.script) { if (body.script.name) { @@ -715,6 +729,8 @@ function SourceInfo(body) { result += ':'; result += body.sourceLine + 1; + if (body.exception) result += '\n' + body.exception.text; + return result; } @@ -762,7 +778,8 @@ function Interface(stdin, stdout, args) { 'out': 'o', 'backtrace': 'bt', 'setBreakpoint': 'sb', - 'clearBreakpoint': 'cb' + 'clearBreakpoint': 'cb', + 'pause_': 'pause' }; function defineProperty(key, protoKey) { @@ -867,7 +884,7 @@ Interface.prototype.childPrint = function(text) { }).map(function(chunk) { return '< ' + chunk; }).join('\n')); - this.repl.displayPrompt(); + this.repl.displayPrompt(true); }; // Errors formatting @@ -1304,6 +1321,12 @@ Interface.prototype.setBreakpoint = function(script, line, line = this.client.currentSourceLine + 1; } + // setBreakpoint(line-number) should insert breakpoint in current script + if (line === undefined && typeof script === 'number') { + line = script; + script = this.client.currentScript; + } + if (/\(\)$/.test(script)) { // setBreakpoint('functionname()'); var req = { @@ -1434,6 +1457,24 @@ Interface.prototype.breakpoints = function() { }; +// Pause child process +Interface.prototype.pause_ = function() { + if (!this.requireConnection()) return; + + var self = this, + cmd = 'process._debugPause();'; + + this.pause(); + this.client.reqFrameEval(cmd, NO_FRAME, function(err, res) { + if (err) { + self.error(err); + } else { + self.resume(); + } + }); +}; + + // Kill child process Interface.prototype.kill = function() { if (!this.child) return; @@ -1529,11 +1570,12 @@ Interface.prototype.trySpawn = function(cb) { this.killChild(); - // Connecting to remote debugger - // `node debug localhost:5858` if (this.args.length === 2) { var match = this.args[1].match(/^([^:]+):(\d+)$/); + if (match) { + // Connecting to remote debugger + // `node debug localhost:5858` host = match[1]; port = parseInt(match[2], 10); this.child = { @@ -1551,6 +1593,14 @@ Interface.prototype.trySpawn = function(cb) { } }; process._debugProcess(parseInt(this.args[2], 10)); + } else { + var match = this.args[1].match(/^--port=(\d+)$/); + if (match) { + // Start debugger on custom port + // `node debug --port=5858 app.js` + port = parseInt(match[1], 10); + this.args.splice(0, 2, '--debug-brk=' + port); + } } } @@ -1574,9 +1624,11 @@ Interface.prototype.trySpawn = function(cb) { self.setBreakpoint(bp.scriptId, bp.line, bp.condition, true); }); - if (cb) cb(); - - self.resume(); + // Break on exceptions + client.reqSetExceptionBreak('all', function(err, res) { + cb && cb(); + self.resume(); + }); client.on('close', function() { self.pause(); @@ -1597,6 +1649,10 @@ Interface.prototype.trySpawn = function(cb) { self.handleBreak(res.body); }); + client.on('exception', function(res) { + self.handleBreak(res.body); + }); + client.on('error', connectError); function connectError() { // If it's failed to connect 4 times then don't catch the next error diff --git a/lib/assert.js b/lib/assert.js index e44c3a1350..7c013eb7fe 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -170,12 +170,22 @@ function _deepEqual(actual, expected) { } else if (actual instanceof Date && expected instanceof Date) { return actual.getTime() === expected.getTime(); - // 7.3. Other pairs that do not both pass typeof value == 'object', + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (actual instanceof RegExp && expected instanceof RegExp) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. } else if (typeof actual != 'object' && typeof expected != 'object') { return actual == expected; - // 7.4. For all other Object pairs, including Array objects, equivalence is + // 7.5 For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified // with Object.prototype.hasOwnProperty.call), the same set of keys // (although not necessarily the same order), equivalent values for every diff --git a/lib/child_process.js b/lib/child_process.js index 145a34ff4b..4b5cbbe673 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -68,45 +68,45 @@ function mergeOptions(target, overrides) { function setupChannel(target, channel) { - var isWindows = process.platform === 'win32'; target._channel = channel; var jsonBuffer = ''; - - if (isWindows) { - var setSimultaneousAccepts = function(handle) { - var simultaneousAccepts = (process.env.NODE_MANY_ACCEPTS && - process.env.NODE_MANY_ACCEPTS != '0') ? true : false; - - if (handle._simultaneousAccepts != simultaneousAccepts) { - handle.setSimultaneousAccepts(simultaneousAccepts); - handle._simultaneousAccepts = simultaneousAccepts; - } - } - } - + channel.buffering = false; channel.onread = function(pool, offset, length, recvHandle) { - if (recvHandle && setSimultaneousAccepts) { - // Update simultaneous accepts on Windows - setSimultaneousAccepts(recvHandle); - } + // Update simultaneous accepts on Windows + net._setSimultaneousAccepts(recvHandle); if (pool) { jsonBuffer += pool.toString('ascii', offset, offset + length); var i, start = 0; + + //Linebreak is used as a message end sign while ((i = jsonBuffer.indexOf('\n', start)) >= 0) { var json = jsonBuffer.slice(start, i); var message = JSON.parse(json); - target.emit('message', message, recvHandle); + //Filter out internal messages + //if cmd property begin with "_NODE" + if (message !== null && + typeof message === 'object' && + typeof message.cmd === 'string' && + message.cmd.indexOf('NODE_') === 0) { + target.emit('internalMessage', message, recvHandle); + } + //Non-internal message + else { + target.emit('message', message, recvHandle); + } + start = i + 1; } jsonBuffer = jsonBuffer.slice(start); + this.buffering = jsonBuffer.length !== 0; } else { - channel.close(); - target._channel = null; + this.buffering = false; + target.disconnect(); } }; @@ -115,7 +115,7 @@ function setupChannel(target, channel) { throw new TypeError('message cannot be undefined'); } - if (!target._channel) throw new Error("channel closed"); + if (!this.connected) throw new Error("channel closed"); // For overflow protection don't write if channel queue is too deep. if (channel.writeQueueSize > 1024 * 1024) { @@ -124,10 +124,8 @@ function setupChannel(target, channel) { var buffer = Buffer(JSON.stringify(message) + '\n'); - if (sendHandle && setSimultaneousAccepts) { - // Update simultaneous accepts on Windows - setSimultaneousAccepts(sendHandle); - } + // Update simultaneous accepts on Windows + net._setSimultaneousAccepts(sendHandle); var writeReq = channel.write(buffer, 0, buffer.length, sendHandle); @@ -140,19 +138,60 @@ function setupChannel(target, channel) { return true; }; + target.connected = true; + target.disconnect = function() { + if (!this.connected) { + this.emit('error', new Error('IPC channel is already disconnected')); + return; + } + + // do not allow messages to be written + this.connected = false; + this._channel = null; + + var fired = false; + function finish() { + if (fired) return; + fired = true; + + channel.close(); + target.emit('disconnect'); + } + + // If a message is being read, then wait for it to complete. + if (channel.buffering) { + this.once('message', finish); + this.once('internalMessage', finish); + + return; + } + + finish(); + }; + channel.readStart(); } function nop() { } +exports.fork = function(modulePath /*, args, options*/) { -exports.fork = function(modulePath, args, options) { - if (!options) options = {}; + // Get options and args arguments. + var options, args; + if (Array.isArray(arguments[1])) { + args = arguments[1]; + options = arguments[2] || {}; + } else { + args = []; + options = arguments[1] || {}; + } - args = args ? args.slice(0) : []; + // Copy args and add modulePath + args = args.slice(0); args.unshift(modulePath); + // Don't allow stdinStream and customFds since a stdin channel will be used if (options.stdinStream) { throw new Error('stdinStream not allowed for fork()'); } @@ -162,8 +201,8 @@ exports.fork = function(modulePath, args, options) { } // Leave stdin open for the IPC channel. stdout and stderr should be the - // same as the parent's. - options.customFds = [-1, 1, 2]; + // same as the parent's if silent isn't set. + options.customFds = (options.silent ? [-1, -1, -1] : [-1, 1, 2]); // Just need to set this - child process won't actually use the fd. // For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6. @@ -177,12 +216,6 @@ exports.fork = function(modulePath, args, options) { setupChannel(child, options.stdinStream); - child.on('exit', function() { - if (child._channel) { - child._channel.close(); - } - }); - return child; }; diff --git a/lib/cluster.js b/lib/cluster.js index 1b565cac79..205df81466 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -23,9 +23,23 @@ var assert = require('assert'); var fork = require('child_process').fork; var net = require('net'); var EventEmitter = require('events').EventEmitter; +var util = require('util'); -var cluster = module.exports = new EventEmitter(); +function isObject(o) { + return (typeof o === 'object' && o !== null); +} + +function extendObject(origin, add) { + // Don't do anything if add isn't an object + if (!add) return origin; + var keys = Object.keys(add), + i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +} var debug; if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { @@ -38,195 +52,428 @@ if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { debug = function() { }; } +// cluster object: +function cluster() {} +util.inherits(cluster, EventEmitter); +var cluster = module.exports = new cluster(); // Used in the master: var masterStarted = false; var ids = 0; -var workers = []; -var servers = {}; -var workerFilename; -var workerArgs; +var serverHandlers = {}; // Used in the worker: -var workerId = 0; +var serverLisenters = {}; var queryIds = 0; var queryCallbacks = {}; -cluster.isWorker = 'NODE_WORKER_ID' in process.env; +// Define isWorker and isMaster +cluster.isWorker = 'NODE_UNIQUE_ID' in process.env; cluster.isMaster = ! cluster.isWorker; -// Call this from the master process. It will start child workers. -// -// options.workerFilename -// Specifies the script to execute for the child processes. Default is -// process.argv[1] -// -// options.args -// Specifies program arguments for the workers. The Default is -// process.argv.slice(2) -// -// options.workers -// The number of workers to start. Defaults to os.cpus().length. -function startMaster() { - // This can only be called from the master. - assert(cluster.isMaster); +// The worker object is only used in a worker +cluster.worker = cluster.isWorker ? {} : null; +// The workers array is oly used in the naster +cluster.workers = cluster.isMaster ? {} : null; + +// Settings object +var settings = cluster.settings = {}; + +// Simple function there call a function on each worker +function eachWorker(cb) { + // Go througe all workers + for (var id in cluster.workers) { + if (cluster.workers.hasOwnProperty(id)) { + cb(cluster.workers[id]); + } + } +} - if (masterStarted) return; - masterStarted = true; +cluster.setupMaster = function(options) { + // This can only be called from the master. + assert(cluster.isMaster); - workerFilename = process.argv[1]; - workerArgs = process.argv.slice(2); + // Don't allow this function to run more that once + if (masterStarted) return; + masterStarted = true; - process.on('uncaughtException', function(e) { + // Get filename and arguments + options = options || {}; + + // Set settings object + settings = cluster.settings = { + exec: options.exec || process.argv[1], + args: options.args || process.argv.slice(2), + silent: options.silent || false + }; + + // Kill workers when a uncaught exception is received + process.on('uncaughtException', function(err) { // Did the user install a listener? If so, it overrides this one. if (process.listeners('uncaughtException').length > 1) return; - // Quickly try to kill all the workers. - // TODO: be session leader - will cause auto SIGHUP to the children. - eachWorker(function(worker) { - debug('kill worker ' + worker.pid); - worker.kill(); - }); + // Output the error stack, and create on if non exist + if (!(err instanceof Error)) { + err = new Error(err); + } + console.error(err.stack); - console.error('Exception in cluster master process: ' + - e.message + '\n' + e.stack); + // quick destroy cluster + quickDestroyCluster(); + // when done exit process with error code: 1 process.exit(1); - }); + }); + + // emit setup event + cluster.emit('setup'); +}; + +// Check if a message is internal only +var INTERNAL_PREFIX = 'NODE_CLUSTER_'; +function isInternalMessage(message) { + return (isObject(message) && + typeof message.cmd === 'string' && + message.cmd.indexOf(INTERNAL_PREFIX) === 0); } +// Modyfi message object to be internal +function internalMessage(inMessage) { + var outMessage = extendObject({}, inMessage); -function handleWorkerMessage(worker, message) { - // This can only be called from the master. - assert(cluster.isMaster); + // Add internal prefix to cmd + outMessage.cmd = INTERNAL_PREFIX + (outMessage.cmd || ''); - debug('recv ' + JSON.stringify(message)); - - switch (message.cmd) { - case 'online': - debug('Worker ' + worker.pid + ' online'); - worker.online = true; - break; - - case 'queryServer': - var key = message.address + ':' + - message.port + ':' + - message.addressType; - var response = { _queryId: message._queryId }; - - if (!(key in servers)) { - // Create a new server. - debug('create new server ' + key); - servers[key] = net._createServerHandle(message.address, - message.port, - message.addressType); - } - worker.send(response, servers[key]); - break; - - default: - // Ignore. - break; - } + return outMessage; } +// Handle callback messges +function handleResponse(outMessage, outHandle, inMessage, inHandle, worker) { -function eachWorker(cb) { - // This can only be called from the master. - assert(cluster.isMaster); + // The message there will be send + var message = internalMessage(outMessage); - for (var id in workers) { - if (workers[id]) { - cb(workers[id]); - } + // callback id - will be undefined if not set + message._queryEcho = inMessage._requestEcho; + + // Call callback if a query echo is received + if (inMessage._queryEcho) { + queryCallbacks[inMessage._queryEcho](inMessage.content, inHandle); + delete queryCallbacks[inMessage._queryEcho]; + } + + // Send if outWrap do contain something useful + if (!(outMessage === undefined && message._queryEcho === undefined)) { + sendInternalMessage(worker, message, outHandle); } } +// Handle messages from both master and workers +var messageHandingObject = {}; +function handleMessage(worker, inMessage, inHandle) { -cluster.fork = function() { - // This can only be called from the master. - assert(cluster.isMaster); + //Remove internal prefix + var message = extendObject({}, inMessage); + message.cmd = inMessage.cmd.substr(INTERNAL_PREFIX.length); - // Lazily start the master process stuff. - startMaster(); + var respondUsed = false; + var respond = function(outMessage, outHandler) { + respondUsed = true; + handleResponse(outMessage, outHandler, inMessage, inHandle, worker); + }; - var id = ++ids; - var envCopy = {}; + // Run handler if it exist + if (messageHandingObject[message.cmd]) { + messageHandingObject[message.cmd](message, worker, respond); + } - for (var x in process.env) { - envCopy[x] = process.env[x]; + // Send respond if it wasn't done + if (respondUsed === false) { + respond(); } +} - envCopy['NODE_WORKER_ID'] = id; +// Messages to the master will be handled using this methods +if (cluster.isMaster) { - var worker = fork(workerFilename, workerArgs, { env: envCopy }); + // Handle online messages from workers + messageHandingObject.online = function(message, worker) { + worker.state = 'online'; + debug('Worker ' + worker.process.pid + ' online'); + worker.emit('online', worker); + cluster.emit('online', worker); + }; - workers[id] = worker; + // Handle queryServer messages form workers + messageHandingObject.queryServer = function(message, worker, send) { - worker.on('message', function(message) { - handleWorkerMessage(worker, message); - }); + // This sequence of infomation is unique to the connection but not + // to the worker + var args = [message.address, message.port, message.addressType]; + var key = args.join(':'); + var handler; - worker.on('exit', function() { - debug('worker id=' + id + ' died'); - delete workers[id]; - cluster.emit('death', worker); - }); + if (serverHandlers.hasOwnProperty(key)) { + handler = serverHandlers[key]; + } else { + handler = serverHandlers[key] = net._createServerHandle.apply(net, args); + } - return worker; -}; + // echo callback with the fd handler associated with it + send({}, handler); + }; + // Handle listening messages from workers + messageHandingObject.listening = function(message, worker) { -// Internal function. Called from src/node.js when worker process starts. -cluster._startWorker = function() { - assert(cluster.isWorker); - workerId = parseInt(process.env.NODE_WORKER_ID, 10); - - queryMaster({ cmd: 'online' }); - - // Make callbacks from queryMaster() - process.on('message', function(msg, handle) { - debug('recv ' + JSON.stringify(msg)); - if (msg._queryId && msg._queryId in queryCallbacks) { - var cb = queryCallbacks[msg._queryId]; - if (typeof cb == 'function') { - cb(msg, handle); - } - delete queryCallbacks[msg._queryId]; + worker.state = 'listening'; + + // Emit listining, now that we know the worker is listning + worker.emit('listening', worker, { + address: message.address, + port: message.port, + addressType: message.addressType + }); + cluster.emit('listening', worker, { + address: message.address, + port: message.port, + addressType: message.addressType + }); + }; + + // Handle suicide messages from workers + messageHandingObject.suicide = function(message, worker) { + worker.suicide = true; + }; + +} + +// Messages to a worker will be handled using this methods +else if (cluster.isWorker) { + + // TODO: the disconnect step will use this +} + +function toDecInt(value) { + value = parseInt(value, 10); + return isNaN(value) ? null : value; +} + +// Create a worker object, there works both for master and worker +function Worker(customEnv) { + if (!(this instanceof Worker)) return new Worker(); + + var self = this; + var env = process.env; + + // Assign uniqueID, default null + this.uniqueID = cluster.isMaster ? ++ids : toDecInt(env.NODE_UNIQUE_ID); + + // Assign state + this.state = 'none'; + + // Create or get process + if (cluster.isMaster) { + + // Create env object + // first: copy and add uniqueID + var envCopy = extendObject({}, env); + envCopy['NODE_UNIQUE_ID'] = this.uniqueID; + // second: extend envCopy with the env argument + if (isObject(customEnv)) { + envCopy = extendObject(envCopy, customEnv); } - }); + + // fork worker + this.process = fork(settings.exec, settings.args, { + 'env': envCopy, + 'silent': settings.silent + }); + } else { + this.process = process; + } + + if (cluster.isMaster) { + // Save worker in the cluster.workers array + cluster.workers[this.uniqueID] = this; + + // Emit a fork event, on next tick + // There is no worker.fork event since this has no real purpose + process.nextTick(function() { + cluster.emit('fork', self); + }); + } + + // handle internalMessage and exit event + this.process.on('internalMessage', handleMessage.bind(null, this)); + this.process.on('exit', prepareDeath.bind(null, this, 'dead', 'death')); + + // relay message and error + this.process.on('message', this.emit.bind(this, 'message')); + this.process.on('error', this.emit.bind(this, 'error')); + +} +util.inherits(Worker, EventEmitter); +cluster.Worker = Worker; + +function prepareDeath(worker, state, eventName) { + + // set state to disconnect + worker.state = state; + + // Make suicide a boolean + worker.suicide = !!worker.suicide; + + // Remove from workers in the master + if (cluster.isMaster) { + delete cluster.workers[worker.uniqueID]; + } + + // Emit events + worker.emit(eventName, worker); + cluster.emit(eventName, worker); +} + +// Send internal message +function sendInternalMessage(worker, message/*, handler, callback*/) { + + // Exist callback + var callback = arguments[arguments.length - 1]; + if (typeof callback !== 'function') { + callback = undefined; + } + + // exist handler + var handler = arguments[2] !== callback ? arguments[2] : undefined; + + if (!isInternalMessage(message)) { + message = internalMessage(message); + } + + // Store callback for later + if (callback) { + message._requestEcho = worker.uniqueID + ':' + (++queryIds); + queryCallbacks[message._requestEcho] = callback; + } + + + worker.send(message, handler); +} + +// Send message to worker or master +Worker.prototype.send = function() { + + //You could also just use process.send in a worker + this.process.send.apply(this.process, arguments); }; -function queryMaster(msg, cb) { - assert(cluster.isWorker); +function closeWorkerChannel(worker, callback) { + //Apparently the .close method is async, but do not have a callback + worker.process._channel.close(); + worker.process._channel = null; + process.nextTick(callback); +} - debug('send ' + JSON.stringify(msg)); +// Kill the worker without restarting +Worker.prototype.destroy = function() { + var self = this; - // Grab some random queryId - msg._queryId = (++queryIds); - msg._workerId = workerId; + this.suicide = true; + + if (cluster.isMaster) { + // Disconnect IPC channel + // this way the worker won't need to propagate suicide state to master + closeWorkerChannel(this, function() { + self.process.kill(); + }); - // Store callback for later. Callback called in _startWorker. - if (cb) { - queryCallbacks[msg._queryId] = cb; + } else { + // Channel is open + if (this.process.connected) { + + // Inform master that is is suicide and then kill + sendInternalMessage(this, {cmd: 'suicide'}, function() { + process.exit(0); + }); + + // When master do a quickDestroy the channel is not necesarily closed + // at the point this function runs. For that reason we need to keep + // checking that the channel is still open, until a actually callback + // from the master is resicved. Also we can't do a timeout and then + // just kill, since we don't know if the quickDestroy function was called. + setInterval(function() { + if (!self.process.connected) { + process.exit(0); + } + }, 200); + + } else { + process.exit(0); + } } +}; + +// Fork a new worker +cluster.fork = function(env) { + // This can only be called from the master. + assert(cluster.isMaster); + + // Make sure that the master has been initalized + cluster.setupMaster(); + + return (new cluster.Worker(env)); +}; - // Send message to master. - process.send(msg); +// Sync way to quickly kill all cluster workers +// However the workers may not die instantly +function quickDestroyCluster() { + eachWorker(function(worker) { + worker.process.disconnect(); + worker.process.kill(); + }); } +// Internal function. Called from src/node.js when worker process starts. +cluster._setupWorker = function() { + // Get worker class + var worker = cluster.worker = new Worker(); -// Internal function. Called by lib/net.js when attempting to bind a -// server. -cluster._getServer = function(address, port, addressType, cb) { + // Tell master that the worker is online + worker.state = 'online'; + sendInternalMessage(worker, { cmd: 'online' }); +}; + +// Internal function. Called by lib/net.js when attempting to bind a server. +cluster._getServer = function(tcpSelf, address, port, addressType, cb) { + // This can only be called from a worker. assert(cluster.isWorker); - queryMaster({ + // Store tcp instance for later use + var key = [address, port, addressType].join(':'); + serverLisenters[key] = tcpSelf; + + // Send a listening message to the master + tcpSelf.once('listening', function() { + cluster.worker.state = 'listening'; + sendInternalMessage(cluster.worker, { + cmd: 'listening', + address: address, + port: port, + addressType: addressType + }); + }); + + // Request the fd handler from the master process + var message = { cmd: 'queryServer', address: address, port: port, addressType: addressType - }, function(msg, handle) { + }; + + // The callback will be stored until the master has responed + sendInternalMessage(cluster.worker, message, function(msg, handle) { cb(handle); }); + }; @@ -37,6 +37,19 @@ var EventEmitter = require('events').EventEmitter; var kMinPoolSpace = 128; var kPoolSize = 40 * 1024; +var O_APPEND = constants.O_APPEND || 0; +var O_CREAT = constants.O_CREAT || 0; +var O_DIRECTORY = constants.O_DIRECTORY || 0; +var O_EXCL = constants.O_EXCL || 0; +var O_NOCTTY = constants.O_NOCTTY || 0; +var O_NOFOLLOW = constants.O_NOFOLLOW || 0; +var O_RDONLY = constants.O_RDONLY || 0; +var O_RDWR = constants.O_RDWR || 0; +var O_SYMLINK = constants.O_SYMLINK || 0; +var O_SYNC = constants.O_SYNC || 0; +var O_TRUNC = constants.O_TRUNC || 0; +var O_WRONLY = constants.O_WRONLY || 0; + fs.Stats = binding.Stats; fs.Stats.prototype._checkModeProperty = function(property) { @@ -71,6 +84,21 @@ fs.Stats.prototype.isSocket = function() { return this._checkModeProperty(constants.S_IFSOCK); }; +fs.exists = function(path, callback) { + binding.stat(path, function(err, stats) { + if (callback) callback(err ? false : true); + }); +}; + +fs.existsSync = function(path) { + try { + binding.stat(path); + return true; + } catch (e) { + return false; + } +}; + fs.readFile = function(path, encoding_) { var encoding = typeof(encoding_) === 'string' ? encoding_ : null; var callback = arguments[arguments.length - 1]; @@ -163,30 +191,43 @@ function stringToFlags(flag) { if (typeof flag !== 'string') { return flag; } - switch (flag) { - case 'r': - return constants.O_RDONLY; - case 'r+': - return constants.O_RDWR; + // O_EXCL is mandated by POSIX, Windows supports it too. + // Let's add a check anyway, just in case. + if (!O_EXCL && ~flag.indexOf('x')) { + throw errnoException('ENOSYS', 'fs.open(O_EXCL)'); + } - case 'w': - return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY; + switch (flag) { + case 'r' : return O_RDONLY; + case 'r+' : return O_RDWR; - case 'w+': - return constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR; + case 'w' : return O_TRUNC | O_CREAT | O_WRONLY; + case 'wx' : // fall through + case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; - case 'a': - return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY; + case 'w+' : return O_TRUNC | O_CREAT | O_RDWR; + case 'wx+': // fall through + case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL; - case 'a+': - return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR; + case 'a' : return O_APPEND | O_CREAT | O_WRONLY; + case 'ax' : // fall through + case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL; - default: - throw new Error('Unknown file open flag: ' + flag); + case 'a+' : return O_APPEND | O_CREAT | O_RDWR; + case 'ax+': // fall through + case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL; } + + throw new Error('Unknown file open flag: ' + flag); } +// exported but hidden, only used by test/simple/test-fs-open-flags.js +Object.defineProperty(exports, '_stringToFlags', { + enumerable: false, + value: stringToFlags +}); + function noop() {} // Yes, the follow could be easily DRYed up but I provide the explicit @@ -582,9 +623,12 @@ fs.futimesSync = function(fd, atime, mtime) { binding.futimes(fd, atime, mtime); }; -function writeAll(fd, buffer, offset, length, callback) { +function writeAll(fd, buffer, offset, length, position, callback) { + var callback_ = arguments[arguments.length - 1]; + callback = (typeof(callback_) == 'function' ? callback_ : null); + // write(fd, buffer, offset, length, position, callback) - fs.write(fd, buffer, offset, length, offset, function(writeErr, written) { + fs.write(fd, buffer, offset, length, position, function(writeErr, written) { if (writeErr) { fs.close(fd, function() { if (callback) callback(writeErr); @@ -593,7 +637,7 @@ function writeAll(fd, buffer, offset, length, callback) { if (written === length) { fs.close(fd, callback); } else { - writeAll(fd, buffer, offset + written, length - written, callback); + writeAll(fd, buffer, offset + written, length - written, position + written, callback); } } }); @@ -609,7 +653,7 @@ fs.writeFile = function(path, data, encoding_, callback) { } else { var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding); - writeAll(fd, buffer, 0, buffer.length, callback); + writeAll(fd, buffer, 0, buffer.length, 0, callback); } }); }; @@ -628,6 +672,42 @@ fs.writeFileSync = function(path, data, encoding) { fs.closeSync(fd); }; +fs.appendFile = function(path, data, encoding_, callback) { + var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8'); + var callback_ = arguments[arguments.length - 1]; + callback = (typeof(callback_) == 'function' ? callback_ : null); + + fs.open(path, 'a', 438 /*=0666*/, function(err, fd) { + if (err) return callback(err); + var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding); + writeAll(fd, buffer, 0, buffer.length, null, callback); + }); +}; + +fs.appendFileSync = function(path, data, encoding) { + var fd = fs.openSync(path, 'a'); + if (!Buffer.isBuffer(data)) { + data = new Buffer('' + data, encoding || 'utf8'); + } + var written = 0; + var position = null; + var length = data.length; + + while (written < length) { + try { + written += fs.writeSync(fd, data, written, length - written, position); + } catch (e) { + try { + fs.closeSync(fd); + } catch (e) { + // swallow exception + } + throw e; + } + position += written; + } + fs.closeSync(fd); +}; function errnoException(errorno, syscall) { // TODO make this more compatible with ErrnoException from src/node.cc diff --git a/lib/http.js b/lib/http.js index 4f7aa0a00a..d800895a5a 100644 --- a/lib/http.js +++ b/lib/http.js @@ -26,6 +26,7 @@ var EventEmitter = require('events').EventEmitter; var FreeList = require('freelist').FreeList; var HTTPParser = process.binding('http_parser').HTTPParser; var assert = require('assert').ok; +var END_OF_FILE = {}; var debug; @@ -48,7 +49,11 @@ var parsers = new FreeList('parsers', 1000, function() { // processed in a single run. This method is also // called to process trailing HTTP headers. parser.onHeaders = function(headers, url) { - parser._headers = parser._headers.concat(headers); + // Once we exceeded headers limit - stop collecting them + if (parser.maxHeaderPairs <= 0 || + parser._headers.length < parser.maxHeaderPairs) { + parser._headers = parser._headers.concat(headers); + } parser._url += url; }; @@ -77,7 +82,14 @@ var parsers = new FreeList('parsers', 1000, function() { parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor; parser.incoming.url = url; - for (var i = 0, n = headers.length; i < n; i += 2) { + var n = headers.length; + + // If parser.maxHeaderPairs <= 0 - assume that there're no limit + if (parser.maxHeaderPairs > 0) { + n = Math.min(n, parser.maxHeaderPairs); + } + + for (var i = 0; i < n; i += 2) { var k = headers[i]; var v = headers[i + 1]; parser.incoming._addHeaderLine(k.toLowerCase(), v); @@ -94,25 +106,25 @@ var parsers = new FreeList('parsers', 1000, function() { parser.incoming.upgrade = info.upgrade; - var isHeadResponse = false; + var skipBody = false; // response to HEAD or CONNECT if (!info.upgrade) { - // For upgraded connections, we'll emit this after parser.execute + // For upgraded connections and CONNECT method request, + // we'll emit this after parser.execute // so that we can capture the first part of the new protocol - isHeadResponse = parser.onIncoming(parser.incoming, info.shouldKeepAlive); + skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive); } - return isHeadResponse; + return skipBody; }; parser.onBody = function(b, start, len) { // TODO body encoding? var slice = b.slice(start, start + len); - if (parser.incoming._decoder) { - var string = parser.incoming._decoder.write(slice); - if (string.length) parser.incoming.emit('data', string); + if (parser.incoming._paused || parser.incoming._pendings.length) { + parser.incoming._pendings.push(slice); } else { - parser.incoming.emit('data', slice); + parser.incoming._emitData(slice); } }; @@ -133,8 +145,12 @@ var parsers = new FreeList('parsers', 1000, function() { if (!parser.incoming.upgrade) { // For upgraded connections, also emit this after parser.execute - parser.incoming.readable = false; - parser.incoming.emit('end'); + if (parser.incoming._paused || parser.incoming._pendings.length) { + parser.incoming._pendings.push(END_OF_FILE); + } else { + parser.incoming.readable = false; + parser.incoming.emit('end'); + } } if (parser.socket.readable) { @@ -210,9 +226,22 @@ var transferEncodingExpression = /Transfer-Encoding/i; var closeExpression = /close/i; var chunkExpression = /chunk/i; var contentLengthExpression = /Content-Length/i; +var dateExpression = /Date/i; var expectExpression = /Expect/i; var continueExpression = /100-continue/i; +var dateCache; +function utcDate() { + if (! dateCache) { + var d = new Date(); + dateCache = d.toUTCString(); + setTimeout(function () { + dateCache = undefined; + }, 1000 - d.getMilliseconds()); + } + return dateCache; +} + /* Abstract base class for ServerRequest and ClientResponse. */ function IncomingMessage(socket) { @@ -229,6 +258,9 @@ function IncomingMessage(socket) { this.readable = true; + this._paused = false; + this._pendings = []; + // request (server) only this.url = ''; @@ -256,12 +288,44 @@ IncomingMessage.prototype.setEncoding = function(encoding) { IncomingMessage.prototype.pause = function() { + this._paused = true; this.socket.pause(); }; IncomingMessage.prototype.resume = function() { - this.socket.resume(); + this._paused = false; + if (this.socket) { + this.socket.resume(); + } + if (this._pendings.length) { + var self = this; + process.nextTick(function() { + while (!self._paused && self._pendings.length) { + var chunk = self._pendings.shift(); + if (chunk !== END_OF_FILE) { + assert(Buffer.isBuffer(chunk)); + self._emitData(chunk); + } else { + assert(self._pendings.length === 0); + self.readable = false; + self.emit('end'); + } + } + }); + } +}; + + +IncomingMessage.prototype._emitData = function(d) { + if (this._decoder) { + var string = this._decoder.write(d); + if (string.length) { + this.emit('data', string); + } + } else { + this.emit('data', d); + } }; @@ -334,6 +398,7 @@ function OutgoingMessage() { this.chunkedEncoding = false; this.shouldKeepAlive = true; this.useChunkedEncodingByDefault = true; + this.sendDate = false; this._hasBody = true; this._trailer = ''; @@ -424,6 +489,7 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { var sentConnectionHeader = false; var sentContentLengthHeader = false; var sentTransferEncodingHeader = false; + var sentDateHeader = false; var sentExpect = false; // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n' @@ -449,7 +515,8 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { } else if (contentLengthExpression.test(field)) { sentContentLengthHeader = true; - + } else if (dateExpression.test(field)) { + sentDateHeader = true; } else if (expectExpression.test(field)) { sentExpect = true; } @@ -480,6 +547,11 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { } } + // Date header + if (this.sendDate == true && sentDateHeader == false) { + messageHeader += "Date: " + utcDate() + CRLF; + } + // keep-alive logic if (sentConnectionHeader === false) { if (this.shouldKeepAlive && @@ -767,6 +839,8 @@ function ServerResponse(req) { if (req.method === 'HEAD') this._hasBody = false; + this.sendDate = true; + if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) { this.useChunkedEncodingByDefault = false; this.shouldKeepAlive = false; @@ -1040,7 +1114,7 @@ function ClientRequest(options, cb) { new Buffer(options.auth).toString('base64')); } - if (method === 'GET' || method === 'HEAD') { + if (method === 'GET' || method === 'HEAD' || method === 'CONNECT') { self.useChunkedEncodingByDefault = false; } else { self.useChunkedEncodingByDefault = true; @@ -1121,6 +1195,14 @@ ClientRequest.prototype.onSocket = function(socket) { parser.incoming = null; req.parser = parser; + // Propagate headers limit from request object to parser + if (typeof req.maxHeadersCount === 'number') { + parser.maxHeaderPairs = req.maxHeadersCount << 1; + } else { + // Set default value because parser may be reused from FreeList + parser.maxHeaderPairs = 2000; + } + socket._httpMessage = req; // Setup "drain" propogation. httpSocketSetup(socket); @@ -1153,25 +1235,34 @@ ClientRequest.prototype.onSocket = function(socket) { freeParser(); socket.destroy(ret); } else if (parser.incoming && parser.incoming.upgrade) { + // Upgrade or CONNECT var bytesParsed = ret; - socket.ondata = null; - socket.onend = null; - var res = parser.incoming; req.res = res; + socket.ondata = null; + socket.onend = null; + parser.finish(); + freeParser(); + // This is start + byteParsed - var upgradeHead = d.slice(start + bytesParsed, end); - if (req.listeners('upgrade').length) { - // Emit 'upgrade' on the Agent. - req.upgraded = true; - req.emit('upgrade', res, socket, upgradeHead); + var bodyHead = d.slice(start + bytesParsed, end); + + var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade'; + if (req.listeners(eventName).length) { + req.upgradeOrConnect = true; + + // detach the socket socket.emit('agentRemove'); + socket.removeListener('close', closeListener); + socket.removeListener('error', errorListener); + + req.emit(eventName, res, socket, bodyHead); + req.emit('close'); } else { - // Got upgrade header, but have no handler. + // Got Upgrade header or CONNECT method, but have no handler. socket.destroy(); } - freeParser(); } else if (parser.incoming && parser.incoming.complete && // When the status code is 100 (Continue), the server will // send a final response after this client sends a request @@ -1223,6 +1314,12 @@ ClientRequest.prototype.onSocket = function(socket) { } req.res = res; + // Responses to CONNECT request is handled as Upgrade. + if (req.method === 'CONNECT') { + res.upgrade = true; + return true; // skip body + } + // Responses to HEAD requests are crazy. // HEAD responses aren't allowed to have an entity-body // but *can* have a content-length which actually corresponds @@ -1238,7 +1335,7 @@ ClientRequest.prototype.onSocket = function(socket) { return true; } - if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgraded) { + if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) { // Server MUST respond with Connection:keep-alive for us to enable it. // If we've been upgraded (via WebSockets) we also shouldn't try to // keep the connection open. @@ -1288,7 +1385,7 @@ ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) { } if (cb) { cb(); } } else { - self.socket.on('connect', function() { + self.socket.once('connect', function() { if (method) { self.socket[method].apply(self.socket, arguments_); } @@ -1338,13 +1435,15 @@ exports.get = function(options, cb) { return req; }; + +function ondrain() { + if (this._httpMessage) this._httpMessage.emit('drain'); +} + + function httpSocketSetup(socket) { - // NOTE: be sure not to use ondrain elsewhere in this file! - socket.ondrain = function() { - if (socket._httpMessage) { - socket._httpMessage.emit('drain'); - } - }; + socket.removeListener('drain', ondrain); + socket.on('drain', ondrain); } @@ -1388,6 +1487,14 @@ function connectionListener(socket) { // abort socket._httpMessage ? } + function serverSocketCloseListener() { + debug('server socket close'); + // unref the parser for easy gc + parsers.free(parser); + + abortIncoming(); + } + debug('SERVER new http connection'); httpSocketSetup(socket); @@ -1402,6 +1509,14 @@ function connectionListener(socket) { parser.socket = socket; parser.incoming = null; + // Propagate headers limit from server instance to parser + if (typeof this.maxHeadersCount === 'number') { + parser.maxHeaderPairs = this.maxHeadersCount << 1; + } else { + // Set default value because parser may be reused from FreeList + parser.maxHeaderPairs = 2000; + } + socket.addListener('error', function(e) { self.emit('clientError', e); }); @@ -1412,19 +1527,24 @@ function connectionListener(socket) { debug('parse error'); socket.destroy(ret); } else if (parser.incoming && parser.incoming.upgrade) { + // Upgrade or CONNECT var bytesParsed = ret; + var req = parser.incoming; + socket.ondata = null; socket.onend = null; - - var req = parser.incoming; + socket.removeListener('close', serverSocketCloseListener); + parser.finish(); + parsers.free(parser); // This is start + byteParsed - var upgradeHead = d.slice(start + bytesParsed, end); + var bodyHead = d.slice(start + bytesParsed, end); - if (self.listeners('upgrade').length) { - self.emit('upgrade', req, req.socket, upgradeHead); + var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade'; + if (self.listeners(eventName).length) { + self.emit(eventName, req, req.socket, bodyHead); } else { - // Got upgrade header, but have no handler. + // Got upgrade header or CONNECT method, but have no handler. socket.destroy(); } } @@ -1451,13 +1571,7 @@ function connectionListener(socket) { } }; - socket.addListener('close', function() { - debug('server socket close'); - // unref the parser for easy gc - parsers.free(parser); - - abortIncoming(); - }); + socket.addListener('close', serverSocketCloseListener); // The following callback is issued after the headers have been read on a // new message. In this callback we setup the response object and pass it @@ -1521,6 +1635,7 @@ exports._connectionListener = connectionListener; // Legacy Interface function Client(port, host) { + if (!(this instanceof Client)) return new Client(port, host); host = host || 'localhost'; port = port || 80; this.host = host; @@ -1558,6 +1673,11 @@ Client.prototype.request = function(method, path, headers) { }; exports.Client = Client; + +// TODO http.Client can be removed in v0.9. Until then leave this message. +module.deprecate('Client', 'It will be removed in the near future. Do not use it.'); + exports.createClient = function(port, host) { return new Client(port, host); }; +module.deprecate('createClient', 'Use `http.request` instead.'); diff --git a/lib/https.js b/lib/https.js index 1c3d61e100..991c63c298 100644 --- a/lib/https.js +++ b/lib/https.js @@ -52,7 +52,9 @@ exports.createServer = function(opts, requestListener) { // HTTPS agents. function createConnection(port, host, options) { - return tls.connect(port, host, options); + options.port = port; + options.host = host; + return tls.connect(options); }; function Agent(options) { diff --git a/lib/module.js b/lib/module.js index bf90c9692e..1cd54aef3e 100644 --- a/lib/module.js +++ b/lib/module.js @@ -329,7 +329,9 @@ Module._resolveFilename = function(request, parent) { var filename = Module._findPath(request, paths); if (!filename) { - throw new Error("Cannot find module '" + request + "'"); + var err = new Error("Cannot find module '" + request + "'"); + err.code = 'MODULE_NOT_FOUND'; + throw err; } return filename; }; diff --git a/lib/net.js b/lib/net.js index e41e96ae1d..4630de7cbf 100644 --- a/lib/net.js +++ b/lib/net.js @@ -65,17 +65,47 @@ exports.createServer = function() { }; -exports.connect = exports.createConnection = function(port /* [host], [cb] */) { - var s; +// Target API: +// +// var s = net.connect({port: 80, host: 'google.com'}, function() { +// ... +// }); +// +// There are various forms: +// +// connect(options, [cb]) +// connect(port, [host], [cb]) +// connect(path, [cb]); +// +exports.connect = exports.createConnection = function() { + var args = normalizeConnectArgs(arguments); + var s = new Socket(args[0]); + return Socket.prototype.connect.apply(s, args); +}; - if (isPipeName(port)) { - s = new Socket({ handle: createPipe() }); +// Returns an array [options] or [options, cb] +// It is the same as the argument of Socket.prototype.connect(). +function normalizeConnectArgs(args) { + var options = {}; + + if (typeof args[0] === 'object') { + // connect(options, [cb]) + options = args[0]; + } else if (isPipeName(args[0])) { + // connect(path, [cb]); + options.path = args[0]; } else { - s = new Socket(); + // connect(port, [host], [cb]) + options.port = args[0]; + if (typeof args[1] === 'string') { + options.host = args[1]; + } } - return s.connect(port, arguments[1], arguments[2]); -}; + var cb = args[args.length - 1]; + return (typeof cb === 'function') ? [options, cb] : [options]; +} + /* called when creating new Socket, or when re-using a closed Socket */ function initSocketHandle(self) { @@ -486,8 +516,6 @@ function afterWrite(status, handle, req, buffer) { self._pendingWriteReqs--; if (self._pendingWriteReqs == 0) { - // TODO remove all uses of ondrain - this is not a good hack. - if (self.ondrain) self.ondrain(); self.emit('drain'); } @@ -527,24 +555,25 @@ function connect(self, address, port, addressType) { } -Socket.prototype.connect = function(port /* [host], [cb] */) { - var self = this; +Socket.prototype.connect = function(options, cb) { + if (typeof options !== 'object') { + // Old API: + // connect(port, [host], [cb]) + // connect(path, [cb]); + var args = normalizeConnectArgs(arguments); + return Socket.prototype.connect.apply(this, args); + } - var pipe = isPipeName(port); + var self = this; + var pipe = !!options.path; if (this.destroyed || !this._handle) { this._handle = pipe ? createPipe() : createTCP(); initSocketHandle(this); } - var host; - if (typeof arguments[1] === 'function') { - self.on('connect', arguments[1]); - } else { - host = arguments[1]; - if (typeof arguments[2] === 'function') { - self.on('connect', arguments[2]); - } + if (typeof cb === 'function') { + self.on('connect', cb); } timers.active(this); @@ -553,9 +582,14 @@ Socket.prototype.connect = function(port /* [host], [cb] */) { self.writable = true; if (pipe) { - connect(self, /*pipe_name=*/port); + connect(self, options.path); - } else if (typeof host == 'string') { + } else if (!options.host) { + debug('connect: missing host'); + connect(self, '127.0.0.1', options.port, 4); + + } else { + var host = options.host; debug('connect: find host ' + host); require('dns').lookup(host, function(err, ip, addressType) { // It's possible we were destroyed while looking this up. @@ -581,13 +615,9 @@ Socket.prototype.connect = function(port /* [host], [cb] */) { // expects remoteAddress to have a meaningful value ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1'); - connect(self, ip, port, addressType); + connect(self, ip, options.port, addressType); } }); - - } else { - debug('connect: missing host'); - connect(self, '127.0.0.1', port, 4); } return self; }; @@ -756,8 +786,8 @@ Server.prototype._listen2 = function(address, port, addressType) { function listen(self, address, port, addressType) { - if (process.env.NODE_WORKER_ID) { - require('cluster')._getServer(address, port, addressType, function(handle) { + if (process.env.NODE_UNIQUE_ID) { + require('cluster')._getServer(self, address, port, addressType, function(handle) { self._handle = handle; self._listen2(address, port, addressType); }); @@ -868,9 +898,13 @@ Server.prototype.close = function() { }; Server.prototype._emitCloseIfDrained = function() { - if (!this._handle && !this.connections) { - this.emit('close'); - } + var self = this; + + if (self._handle || self.connections) return; + + process.nextTick(function() { + self.emit('close'); + }); }; @@ -912,3 +946,26 @@ exports.isIPv4 = function(input) { exports.isIPv6 = function(input) { return exports.isIP(input) === 6; }; + + +if (process.platform === 'win32') { + var simultaneousAccepts; + + exports._setSimultaneousAccepts = function(handle) { + if (typeof handle === 'undefined') { + return; + } + + if (typeof simultaneousAccepts === 'undefined') { + simultaneousAccepts = (process.env.NODE_MANY_ACCEPTS && + process.env.NODE_MANY_ACCEPTS != '0') ? true : false; + } + + if (handle._simultaneousAccepts != simultaneousAccepts) { + handle.setSimultaneousAccepts(simultaneousAccepts); + handle._simultaneousAccepts = simultaneousAccepts; + } + } +} else { + exports._setSimultaneousAccepts = function(handle) {} +} @@ -38,7 +38,6 @@ exports.platform = function() { }; exports.getNetworkInterfaces = function() { - require('util')._deprecationWarning('os', - 'os.getNetworkInterfaces() is deprecated - use os.networkInterfaces()'); return exports.networkInterfaces(); }; +module.deprecate('getNetworkInterfaces', 'It is now called `os.networkInterfaces`.'); diff --git a/lib/path.js b/lib/path.js index 5d69d2270f..0ca51e84f3 100644 --- a/lib/path.js +++ b/lib/path.js @@ -21,6 +21,7 @@ var isWindows = process.platform === 'win32'; +var _deprecationWarning = require('util')._deprecationWarning; // resolves . and .. elements in a path array with directory names there @@ -408,23 +409,18 @@ exports.extname = function(path) { exports.exists = function(path, callback) { - process.binding('fs').stat(_makeLong(path), function(err, stats) { - if (callback) callback(err ? false : true); - }); + require('fs').exists(path, callback); }; +module.deprecate('exists', 'It is now called `fs.exists`.'); exports.existsSync = function(path) { - try { - process.binding('fs').stat(_makeLong(path)); - return true; - } catch (e) { - return false; - } + return require('fs').existsSync(path); }; +module.deprecate('existsSync', 'It is now called `fs.existsSync`.'); -var _makeLong = exports._makeLong = isWindows ? +exports._makeLong = isWindows ? function(path) { path = "" + path; if (!path) { diff --git a/lib/punycode.js b/lib/punycode.js index 315f1e4dac..286136c8c5 100644 --- a/lib/punycode.js +++ b/lib/punycode.js @@ -40,8 +40,8 @@ /** Error messages */ errors = { 'overflow': 'Overflow: input needs wider integers to process.', - 'utf16decode': 'UTF-16(decode): illegal UTF-16 sequence', - 'utf16encode': 'UTF-16(encode): illegal UTF-16 value', + 'ucs2decode': 'UCS-2(decode): illegal sequence', + 'ucs2encode': 'UCS-2(encode): illegal value', 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', 'invalid-input': 'Invalid input' }, @@ -99,14 +99,13 @@ /** * Creates an array containing the decimal code points of each character in * the string. - * @see `punycode.utf16.encode` - * @see <http://tools.ietf.org/html/rfc2781> - * @memberOf punycode.utf16 + * @see `punycode.ucs2.encode` + * @memberOf punycode.ucs2 * @name decode * @param {String} string The Unicode input string. * @returns {Array} The new array. */ - function utf16decode(string) { + function ucs2decode(string) { var output = [], counter = 0, length = string.length, @@ -117,7 +116,7 @@ if ((value & 0xF800) == 0xD800) { extra = string.charCodeAt(counter++); if ((value & 0xFC00) != 0xD800 || (extra & 0xFC00) != 0xDC00) { - error('utf16decode'); + error('ucs2decode'); } value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; } @@ -128,18 +127,17 @@ /** * Creates a string based on an array of decimal code points. - * @see `punycode.utf16.decode` - * @see <http://tools.ietf.org/html/rfc2781> - * @memberOf punycode.utf16 + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 * @name encode * @param {Array} codePoints The array of decimal code points. * @returns {String} The new string. */ - function utf16encode(array) { + function ucs2encode(array) { return map(array, function(value) { var output = ''; if ((value & 0xF800) == 0xD800) { - error('utf16encode'); + error('ucs2encode'); } if (value > 0xFFFF) { value -= 0x10000; @@ -224,7 +222,7 @@ * @returns {String} The resulting string of Unicode code points. */ function decode(input) { - // Don't use UTF-16 + // Don't use UCS-2 var output = [], inputLength = input.length, out, @@ -315,7 +313,7 @@ } - return utf16encode(output); + return ucs2encode(output); } /** @@ -345,8 +343,8 @@ baseMinusT, qMinusT; - // Convert the input in UTF-16 to Unicode - input = utf16decode(input); + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); // Cache the length inputLength = input.length; @@ -475,16 +473,16 @@ * @memberOf punycode * @type String */ - 'version': '0.2.1', + 'version': '0.3.0', /** * An object of methods to convert from JavaScript's internal character - * representation to Unicode and back. + * representation (UCS-2) to Unicode and back. * @memberOf punycode * @type Object */ - 'utf16': { - 'decode': utf16decode, - 'encode': utf16encode + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode }, 'decode': decode, 'encode': encode, diff --git a/lib/querystring.js b/lib/querystring.js index d709c07fdf..467eabdea7 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -160,20 +160,33 @@ QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) { }; // Parse a key=val string. -QueryString.parse = QueryString.decode = function(qs, sep, eq) { +QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { sep = sep || '&'; eq = eq || '='; - var obj = {}; + var obj = {}, + maxKeys = 1000; + + // Handle maxKeys = 0 case + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } if (typeof qs !== 'string' || qs.length === 0) { return obj; } - qs.split(sep).forEach(function(kvp) { - var x = kvp.split(eq), k, v, useQS=false; + qs = qs.split(sep); + + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0) { + qs = qs.slice(0, maxKeys); + } + + qs.forEach(function(kvp) { + var x = kvp.split(eq), k, v, useQS = false; try { if (kvp.match(/\+/)) { // decodeURIComponent does not decode + to space - throw "has +"; + throw 'has +'; } k = decodeURIComponent(x[0]); v = decodeURIComponent(x.slice(1).join(eq) || ""); diff --git a/lib/readline.js b/lib/readline.js index a87ba1ea5d..b93a4d57dc 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -125,9 +125,10 @@ Interface.prototype.setPrompt = function(prompt, length) { }; -Interface.prototype.prompt = function() { +Interface.prototype.prompt = function(preserveCursor) { + if (this.paused) this.resume(); if (this.enabled) { - this.cursor = 0; + if (!preserveCursor) this.cursor = 0; this._refreshLine(); } else { this.output.write(this._prompt); @@ -136,16 +137,13 @@ Interface.prototype.prompt = function() { Interface.prototype.question = function(query, cb) { - if (cb) { - this.resume(); + if (typeof cb === 'function') { if (this._questionCallback) { - this.output.write('\n'); this.prompt(); } else { this._oldPrompt = this._prompt; this.setPrompt(query); this._questionCallback = cb; - this.output.write('\n'); this.prompt(); } } @@ -181,8 +179,6 @@ Interface.prototype._addHistory = function() { Interface.prototype._refreshLine = function() { - if (this._closed) return; - // Cursor to left edge. this.output.cursorTo(0); @@ -198,33 +194,29 @@ Interface.prototype._refreshLine = function() { }; -Interface.prototype.close = function(d) { - if (this._closing) return; - this._closing = true; - if (this.enabled) { - tty.setRawMode(false); - } - this.emit('close'); - this._closed = true; -}; - - Interface.prototype.pause = function() { + if (this.paused) return; if (this.enabled) { tty.setRawMode(false); } + this.input.pause(); + this.paused = true; + this.emit('pause'); }; Interface.prototype.resume = function() { + this.input.resume(); if (this.enabled) { tty.setRawMode(true); } + this.paused = false; + this.emit('resume'); }; Interface.prototype.write = function(d, key) { - if (this._closed) return; + if (this.paused) this.resume(); this.enabled ? this._ttyWrite(d, key) : this._normalWrite(d, key); }; @@ -454,16 +446,6 @@ Interface.prototype._historyPrev = function() { }; -Interface.prototype._attemptClose = function() { - if (this.listeners('attemptClose').length) { - // User is to call interface.close() manually. - this.emit('attemptClose'); - } else { - this.close(); - } -}; - - // handle a write from the tty Interface.prototype._ttyWrite = function(s, key) { var next_word, next_non_word, previous_word, previous_non_word; @@ -489,8 +471,8 @@ Interface.prototype._ttyWrite = function(s, key) { if (this.listeners('SIGINT').length) { this.emit('SIGINT'); } else { - // default behavior, end the readline - this._attemptClose(); + // Pause the stream + this.pause(); } break; @@ -500,7 +482,7 @@ Interface.prototype._ttyWrite = function(s, key) { case 'd': // delete right or EOF if (this.cursor === 0 && this.line.length === 0) { - this._attemptClose(); + this.pause(); } else if (this.cursor < this.line.length) { this._deleteRight(); } @@ -549,7 +531,22 @@ Interface.prototype._ttyWrite = function(s, key) { break; case 'z': - process.kill(process.pid, 'SIGTSTP'); + if (this.listeners('SIGTSTP').length) { + this.emit('SIGTSTP'); + } else { + process.once('SIGCONT', (function(self) { + return function() { + // Don't raise events if stream has already been abandoned. + if (!self.paused) { + // Stream must be paused and resumed after SIGCONT to catch + // SIGINT, SIGTSTP, and EOF. + self.pause(); + self.emit('SIGCONT'); + } + }; + })(this)); + process.kill(process.pid, 'SIGTSTP'); + } return; case 'w': // delete backwards to a word boundary diff --git a/lib/repl.js b/lib/repl.js index 20fa7aefc7..7bbc6d1448 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -130,7 +130,7 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) { var sawSIGINT = false; rli.on('SIGINT', function() { if (sawSIGINT) { - rli.close(); + rli.pause(); process.exit(); } @@ -185,7 +185,9 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) { function(e, ret) { if (e && !isSyntaxError(e)) return finish(e); - if (typeof ret === 'function' || e) { + if (typeof ret === 'function' && + /^[\r\n\s]*function/.test(evalCmd) || + e) { // Now as statement without parens. self.eval(evalCmd, self.context, 'repl', finish); } else { @@ -282,11 +284,11 @@ REPLServer.prototype.resetContext = function(force) { this.context = context; }; -REPLServer.prototype.displayPrompt = function() { +REPLServer.prototype.displayPrompt = function(preserveCursor) { this.rli.setPrompt(this.bufferedCommand.length ? '...' + new Array(this.lines.level.length).join('..') + ' ' : this.prompt); - this.rli.prompt(); + this.rli.prompt(preserveCursor); }; @@ -738,7 +740,7 @@ function defineDefaultCommands(repl) { repl.defineCommand('exit', { help: 'Exit the repl', action: function() { - this.rli.close(); + this.rli.pause(); } }); diff --git a/lib/stream.js b/lib/stream.js index fa45b8b1e1..d6da2525f9 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -52,12 +52,8 @@ Stream.prototype.pipe = function(dest, options) { dest.on('drain', ondrain); // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once, and - // only when all sources have ended. + // source gets the 'end' or 'close' events. Only dest.end() once. if (!dest._isStdio && (!options || options.end !== false)) { - dest._pipeCount = dest._pipeCount || 0; - dest._pipeCount++; - source.on('end', onend); source.on('close', onclose); } @@ -67,16 +63,9 @@ Stream.prototype.pipe = function(dest, options) { if (didOnEnd) return; didOnEnd = true; - dest._pipeCount--; - // remove the listeners cleanup(); - if (dest._pipeCount > 0) { - // waiting for other incoming streams to end. - return; - } - dest.end(); } @@ -85,16 +74,9 @@ Stream.prototype.pipe = function(dest, options) { if (didOnEnd) return; didOnEnd = true; - dest._pipeCount--; - // remove the listeners cleanup(); - if (dest._pipeCount > 0) { - // waiting for other incoming streams to end. - return; - } - dest.destroy(); } diff --git a/lib/sys.js b/lib/sys.js index 0039b89d20..276b0f189c 100644 --- a/lib/sys.js +++ b/lib/sys.js @@ -1,36 +1 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = require('util'); - -util._deprecationWarning('sys', - 'The "sys" module is now called "util". It should have a similar interface.'); - -exports.print = util.print; -exports.puts = util.puts; -exports.debug = util.debug; -exports.error = util.error; -exports.inspect = util.inspect; -exports.p = util.p; -exports.log = util.log; -exports.exec = util.exec; -exports.pump = util.pump; -exports.inherits = util.inherits; +throw new Error('The "sys" module is now called "util".'); diff --git a/lib/tls.js b/lib/tls.js index f9bb63c6a1..d951ef358b 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -469,9 +469,6 @@ CryptoStream.prototype._pull = function() { debug('drain ' + (this === this.pair.cleartext ? 'clear' : 'encrypted')); var self = this; process.nextTick(function() { - if (typeof self.ondrain === 'function') { - self.ondrain(); - } self.emit('drain'); }); this._needDrain = false; @@ -1050,7 +1047,7 @@ Server.prototype.SNICallback = function(servername) { // Target API: // -// var s = tls.connect(8000, "google.com", options, function() { +// var s = tls.connect({port: 8000, host: "google.com"}, function() { // if (!s.authorized) { // s.destroy(); // return; @@ -1062,35 +1059,43 @@ Server.prototype.SNICallback = function(servername) { // }); // // -// TODO: make port, host part of options! -exports.connect = function(port /* host, options, cb */) { - // parse args - var host, options = {}, cb; - for (var i = 1; i < arguments.length; i++) { - switch (typeof arguments[i]) { - case 'string': - host = arguments[i]; - break; - - case 'object': - options = arguments[i]; - break; - - case 'function': - cb = arguments[i]; - break; +exports.connect = function(/* [port, host], options, cb */) { + var options = {}, cb; + + if (typeof arguments[0] === 'object') { + options = arguments[0]; + } else if (typeof arguments[1] === 'object') { + options = arguments[1]; + options.port = arguments[0]; + } else if (typeof arguments[2] === 'object') { + options = arguments[2]; + options.port = arguments[0]; + options.host = arguments[1]; + } else { + // This is what happens when user passes no `options` argument, we can't + // throw `TypeError` here because it would be incompatible with old API + if (typeof arguments[0] === 'number') { + options.port = arguments[0]; + } + if (typeof arguments[1] === 'string') { + options.host = arguments[1]; } } + if (typeof arguments[arguments.length - 1] === 'function') { + cb = arguments[arguments.length - 1]; + } + var socket = options.socket ? options.socket : new net.Stream(); var sslcontext = crypto.createCredentials(options); convertNPNProtocols(options.NPNProtocols, this); - var pair = new SecurePair(sslcontext, false, true, false, + var pair = new SecurePair(sslcontext, false, true, + options.rejectUnauthorized === true ? true : false, { NPNProtocols: this.NPNProtocols, - servername: options.servername || host + servername: options.servername || options.host }); if (options.session) { @@ -1103,7 +1108,7 @@ exports.connect = function(port /* host, options, cb */) { } if (!options.socket) { - socket.connect(port, host); + socket.connect(options.port, options.host); } pair.on('secure', function() { @@ -1114,11 +1119,17 @@ exports.connect = function(port /* host, options, cb */) { if (verifyError) { cleartext.authorized = false; cleartext.authorizationError = verifyError; + + if (pair._rejectUnauthorized) { + cleartext.emit('error', verifyError); + pair.destroy(); + } else { + cleartext.emit('secureConnect'); + } } else { cleartext.authorized = true; + cleartext.emit('secureConnect'); } - - cleartext.emit('secureConnect'); }); pair.on('error', function(err) { cleartext.emit('error', err); diff --git a/lib/tty.js b/lib/tty.js index dccda5a481..c1bbba1b06 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -154,6 +154,8 @@ ReadStream.prototype._emitKey = function(s) { } } + key.sequence = s; + if (s === '\r' || s === '\n') { // enter key.name = 'enter'; @@ -210,6 +212,7 @@ ReadStream.prototype._emitKey = function(s) { key.ctrl = !!(modifier & 4); key.meta = !!(modifier & 10); key.shift = !!(modifier & 1); + key.code = code; // Parse the key itself switch (code) { @@ -305,6 +308,7 @@ ReadStream.prototype._emitKey = function(s) { /* misc. */ case '[Z': key.name = 'tab'; key.shift = true; break; + default: key.name = 'undefined'; break; } } else if (s.length > 1 && s[0] !== '\x1b') { diff --git a/lib/url.js b/lib/url.js index 7b4e1b397f..4f1c0e0e9c 100644 --- a/lib/url.js +++ b/lib/url.js @@ -35,7 +35,7 @@ var protocolPattern = /^([a-z0-9.+-]+:)/i, // RFC 2396: characters reserved for delimiting URLs. delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], // RFC 2396: characters not allowed for various reasons. - unwise = ['{', '}', '|', '\\', '^', '~', '[', ']', '`'].concat(delims), + unwise = ['{', '}', '|', '\\', '^', '~', '`'].concat(delims), // Allowed by RFCs, but cause of XSS attacks. Always escape these. autoEscape = ['\''], // Characters that are never ever allowed in a hostname. @@ -179,10 +179,15 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { // so even if it's empty, it has to be present. out.hostname = out.hostname || ''; + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = out.hostname[0] === '[' && + out.hostname[out.hostname.length - 1] === ']'; + // validate a little. if (out.hostname.length > hostnameMaxLen) { out.hostname = ''; - } else { + } else if (!ipv6Hostname) { var hostparts = out.hostname.split(/\./); for (var i = 0, l = hostparts.length; i < l; i++) { var part = hostparts[i]; @@ -221,22 +226,32 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { // hostnames are always lower case. out.hostname = out.hostname.toLowerCase(); - // IDNA Support: Returns a puny coded representation of "domain". - // It only converts the part of the domain name that - // has non ASCII characters. I.e. it dosent matter if - // you call it with a domain that already is in ASCII. - var domainArray = out.hostname.split('.'); - var newOut = []; - for (var i = 0; i < domainArray.length; ++i) { - var s = domainArray[i]; - newOut.push(s.match(/[^A-Za-z0-9_-]/) ? - 'xn--' + punycode.encode(s) : s); + if (!ipv6Hostname) { + // IDNA Support: Returns a puny coded representation of "domain". + // It only converts the part of the domain name that + // has non ASCII characters. I.e. it dosent matter if + // you call it with a domain that already is in ASCII. + var domainArray = out.hostname.split('.'); + var newOut = []; + for (var i = 0; i < domainArray.length; ++i) { + var s = domainArray[i]; + newOut.push(s.match(/[^A-Za-z0-9_-]/) ? + 'xn--' + punycode.encode(s) : s); + } + out.hostname = newOut.join('.'); } - out.hostname = newOut.join('.'); out.host = (out.hostname || '') + ((out.port) ? ':' + out.port : ''); out.href += out.host; + + // strip [ and ] from the hostname + if (ipv6Hostname) { + out.hostname = out.hostname.substr(1, out.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } } // now rest is set to the post-host stuff. @@ -323,20 +338,28 @@ function urlFormat(obj) { } var protocol = obj.protocol || '', - host = (obj.host !== undefined) ? auth + obj.host : - obj.hostname !== undefined ? ( - auth + obj.hostname + - (obj.port ? ':' + obj.port : '') - ) : - false, pathname = obj.pathname || '', - query = obj.query && - ((typeof obj.query === 'object' && - Object.keys(obj.query).length) ? - querystring.stringify(obj.query) : - '') || '', - search = obj.search || (query && ('?' + query)) || '', - hash = obj.hash || ''; + hash = obj.hash || '', + host = false, + query = ''; + + if (obj.host !== undefined) { + host = auth + obj.host; + } else if (obj.hostname !== undefined) { + host = auth + (obj.hostname.indexOf(':') === -1 ? + obj.hostname : + '[' + obj.hostname + ']'); + if (obj.port) { + host += ':' + obj.port; + } + } + + if (obj.query && typeof obj.query === 'object' && + Object.keys(obj.query).length) { + query = querystring.stringify(obj.query); + } + + var search = obj.search || (query && ('?' + query)) || ''; if (protocol && protocol.substr(-1) !== ':') protocol += ':'; diff --git a/lib/util.js b/lib/util.js index 6e410d98ae..5750159e82 100644 --- a/lib/util.js +++ b/lib/util.js @@ -181,7 +181,7 @@ function formatValue(ctx, value, recurseTimes) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } if (isDate(value)) { - return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + return ctx.stylize(Date.prototype.toString.call(value), 'date'); } if (isError(value)) { return formatError(value); @@ -407,18 +407,12 @@ function objectToString(o) { } -var pWarning; - exports.p = function() { - if (!pWarning) { - pWarning = 'util.p will be removed in future versions of Node. ' + - 'Use util.puts(util.inspect()) instead.\n'; - exports.error(pWarning); - } for (var i = 0, len = arguments.length; i < len; ++i) { error(exports.inspect(arguments[i])); } }; +module.deprecate('p', 'Use `util.puts(util.inspect())` instead.'); function pad(n) { @@ -444,15 +438,10 @@ exports.log = function(msg) { }; -var execWarning; exports.exec = function() { - if (!execWarning) { - execWarning = 'util.exec has moved to the "child_process" module.' + - ' Please update your source code.'; - error(execWarning); - } return require('child_process').exec.apply(this, arguments); }; +module.deprecate('exec', 'It is now called `child_process.exec`.'); exports.pump = function(readStream, writeStream, callback) { @@ -517,19 +506,3 @@ exports.inherits = function(ctor, superCtor) { } }); }; - -var deprecationWarnings; - -exports._deprecationWarning = function(moduleId, message) { - if (!deprecationWarnings) - deprecationWarnings = {}; - else if (message in deprecationWarnings) - return; - - deprecationWarnings[message] = true; - - if ((new RegExp('\\b' + moduleId + '\\b')).test(process.env.NODE_DEBUG)) - console.trace(message); - else - console.error(message); -}; diff --git a/lib/zlib.js b/lib/zlib.js index 155924f4eb..349c5c64d9 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -263,11 +263,18 @@ function Zlib(opts, Binding) { } } + if (opts.dictionary) { + if (!Buffer.isBuffer(opts.dictionary)) { + throw new Error('Invalid dictionary: it should be a Buffer instance'); + } + } + this._binding = new Binding(); this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, opts.level || exports.Z_DEFAULT_COMPRESSION, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, - opts.strategy || exports.Z_DEFAULT_STRATEGY); + opts.strategy || exports.Z_DEFAULT_STRATEGY, + opts.dictionary); this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; this._buffer = new Buffer(this._chunkSize); @@ -306,6 +313,10 @@ Zlib.prototype.write = function write(chunk, cb) { return empty; }; +Zlib.prototype.reset = function reset() { + return this._binding.reset(); +}; + Zlib.prototype.flush = function flush(cb) { this._flush = binding.Z_SYNC_FLUSH; return this.write(cb); @@ -54,7 +54,7 @@ 'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser', - 'deps/v8/tools/gyp/v8-node.gyp:v8', + 'deps/v8/tools/gyp/v8.gyp:v8', 'deps/uv/uv.gyp:uv', 'deps/zlib/zlib.gyp:zlib', 'node_js2c#host', @@ -106,7 +106,6 @@ 'src/node_string.h', 'src/node_version.h', 'src/pipe_wrap.h', - 'src/platform.h', 'src/req_wrap.h', 'src/stream_wrap.h', 'src/v8_typed_array.h', @@ -121,10 +120,9 @@ ], 'defines': [ + 'NODE_WANT_INTERNALS=1', 'ARCH="<(target_arch)"', 'PLATFORM="<(OS)"', - '_LARGEFILE_SOURCE', - '_FILE_OFFSET_BITS=64', ], 'conditions': [ @@ -151,9 +149,6 @@ [ 'OS=="win"', { 'sources': [ - 'src/platform_win32.cc', - # headers to make for a more pleasant IDE experience - 'src/platform_win32.h', 'tools/msvs/res/node.rc', ], 'defines': [ @@ -172,25 +167,28 @@ ] }], [ 'OS=="mac"', { - 'sources': [ 'src/platform_darwin.cc' ], 'libraries': [ '-framework Carbon' ], + 'defines!': [ + 'PLATFORM="mac"', + ], + 'defines': [ + # we need to use node's preferred "darwin" rather than gyp's preferred "mac" + 'PLATFORM="darwin"', + ], }], [ 'OS=="linux"', { - 'sources': [ 'src/platform_linux.cc' ], 'libraries': [ '-ldl', '-lutil' # needed for openpty ], }], [ 'OS=="freebsd"', { - 'sources': [ 'src/platform_freebsd.cc' ], 'libraries': [ '-lutil', '-lkvm', ], }], [ 'OS=="solaris"', { - 'sources': [ 'src/platform_sunos.cc' ], 'libraries': [ '-lkstat', ], @@ -207,9 +205,6 @@ 'target_name': 'node_js2c', 'type': 'none', 'toolsets': ['host'], - 'variables': { - }, - 'actions': [ { 'action_name': 'node_js2c', diff --git a/src/node.cc b/src/node.cc index d7c26ffcd1..42a2f8c0d7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -56,16 +56,11 @@ typedef int mode_t; #include <errno.h> #include <sys/types.h> -#if defined(__MINGW32__) || defined(_MSC_VER) -# include <platform_win32.h> /* winapi_perror() */ -#endif - #ifdef __POSIX__ # include <pwd.h> /* getpwnam() */ # include <grp.h> /* getgrnam() */ #endif -#include "platform.h" #include <node_buffer.h> #ifdef __POSIX__ # include <node_io_watcher.h> @@ -97,6 +92,7 @@ extern char **environ; namespace node { + static Persistent<Object> process; static Persistent<String> errno_symbol; @@ -158,6 +154,8 @@ static uv_idle_t gc_idle; static uv_timer_t gc_timer; bool need_gc; +// process-relative uptime base, initialized at start-up +static double prog_start_time; #define FAST_TICK 700. #define GC_WAIT_TIME 5000. @@ -873,6 +871,30 @@ Local<Value> UVException(int errorno, #ifdef _WIN32 +// Does about the same as strerror(), +// but supports all windows error messages +static const char *winapi_strerror(const int errorno) { + char *errmsg = NULL; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL); + + if (errmsg) { + // Remove trailing newlines + for (int i = strlen(errmsg) - 1; + i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) { + errmsg[i] = '\0'; + } + + return errmsg; + } else { + // FormatMessage failed + return "Unknown error"; + } +} + + Local<Value> WinapiErrnoException(int errorno, const char* syscall, const char* msg, @@ -1222,6 +1244,12 @@ Local<Value> ExecuteString(Handle<String> source, Handle<Value> filename) { } +static Handle<Value> Abort(const Arguments& args) { + abort(); + return Undefined(); +} + + static Handle<Value> Chdir(const Arguments& args) { HandleScope scope; @@ -1432,17 +1460,19 @@ static void CheckStatus(uv_timer_t* watcher, int status) { } } + static Handle<Value> Uptime(const Arguments& args) { HandleScope scope; assert(args.Length() == 0); + double uptime; - double uptime = Platform::GetUptime(true); + uv_err_t err = uv_uptime(&uptime); - if (uptime < 0) { + if (err.code != UV_OK) { return Undefined(); } - return scope.Close(Number::New(uptime)); + return scope.Close(Number::New(uptime - prog_start_time)); } @@ -1484,10 +1514,10 @@ v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) { size_t rss; - int r = Platform::GetMemory(&rss); + uv_err_t err = uv_resident_set_memory(&rss); - if (r != 0) { - return ThrowException(Exception::Error(String::New(strerror(errno)))); + if (err.code != UV_OK) { + return ThrowException(UVException(err.code, "uv_resident_set_memory")); } Local<Object> info = Object::New(); @@ -1697,24 +1727,7 @@ void FatalException(TryCatch &try_catch) { } -static uv_async_t debug_watcher; - -static void DebugMessageCallback(uv_async_t* watcher, int status) { - HandleScope scope; - assert(watcher == &debug_watcher); - Debug::ProcessDebugMessages(); -} - -static void DebugMessageDispatch(void) { - // This function is called from V8's debug thread when a debug TCP client - // has sent a message. - - // Send a signal to our main thread saying that it should enter V8 to - // handle the message. - uv_async_send(&debug_watcher); -} - -static void DebugBreakMessageHandler(const Debug::Message& message) { +static void DebugBreakMessageHandler(const v8::Debug::Message& message) { // do nothing with debug messages. // The message handler will get changed by DebuggerAgent::CreateSession in // debug-agent.cc of v8/src when a new session is created @@ -1782,9 +1795,9 @@ static Handle<Value> Binding(const Arguments& args) { static Handle<Value> ProcessTitleGetter(Local<String> property, const AccessorInfo& info) { HandleScope scope; - int len; - const char *s = Platform::GetProcessTitle(&len); - return scope.Close(s ? String::New(s, len) : String::Empty()); + char buffer[512]; + uv_get_process_title(buffer, sizeof(buffer)); + return scope.Close(String::New(buffer)); } @@ -1793,7 +1806,8 @@ static void ProcessTitleSetter(Local<String> property, const AccessorInfo& info) { HandleScope scope; String::Utf8Value title(value->ToString()); - Platform::SetProcessTitle(*title); + // TODO: protect with a lock + uv_set_process_title(*title); } @@ -1969,7 +1983,24 @@ static Handle<Object> GetFeatures() { } +static Handle<Value> DebugPortGetter(Local<String> property, + const AccessorInfo& info) { + HandleScope scope; + return scope.Close(Integer::NewFromUnsigned(debug_port)); +} + + +static void DebugPortSetter(Local<String> property, + Local<Value> value, + const AccessorInfo& info) { + HandleScope scope; + debug_port = value->NumberValue(); +} + + static Handle<Value> DebugProcess(const Arguments& args); +static Handle<Value> DebugPause(const Arguments& args); +static Handle<Value> DebugEnd(const Arguments& args); Handle<Object> SetupProcessObject(int argc, char *argv[]) { HandleScope scope; @@ -2071,10 +2102,15 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) { } delete [] execPath; + process->SetAccessor(String::New("debugPort"), + DebugPortGetter, + DebugPortSetter); + // define various internal methods NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback); NODE_SET_METHOD(process, "reallyExit", Exit); + NODE_SET_METHOD(process, "abort", Abort); NODE_SET_METHOD(process, "chdir", Chdir); NODE_SET_METHOD(process, "cwd", Cwd); @@ -2091,6 +2127,8 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "_kill", Kill); NODE_SET_METHOD(process, "_debugProcess", DebugProcess); + NODE_SET_METHOD(process, "_debugPause", DebugPause); + NODE_SET_METHOD(process, "_debugEnd", DebugEnd); NODE_SET_METHOD(process, "dlopen", DLOpen); @@ -2277,14 +2315,14 @@ static void EnableDebug(bool wait_connect) { node_isolate->Enter(); // Start the debug thread and it's associated TCP server on port 5858. - bool r = Debug::EnableAgent("node " NODE_VERSION, debug_port); + bool r = v8::Debug::EnableAgent("node " NODE_VERSION, debug_port); if (wait_connect) { // Set up an empty handler so v8 will not continue until a debugger // attaches. This is the same behavior as Debug::EnableAgent(_,_,true) // except we don't break at the beginning of the script. // see Debugger::StartAgent in debug.cc of v8/src - Debug::SetMessageHandler2(node::DebugBreakMessageHandler); + v8::Debug::SetMessageHandler2(node::DebugBreakMessageHandler); } // Crappy check that everything went well. FIXME @@ -2501,9 +2539,28 @@ static Handle<Value> DebugProcess(const Arguments& args) { #endif // _WIN32 +static Handle<Value> DebugPause(const Arguments& args) { + v8::Debug::DebugBreak(node_isolate); + return Undefined(); +} + + +static Handle<Value> DebugEnd(const Arguments& args) { + if (debugger_running) { + v8::Debug::DisableAgent(); + debugger_running = false; + } + + return Undefined(); +} + + char** Init(int argc, char *argv[]) { + // Initialize prog_start_time to get relative uptime. + uv_uptime(&prog_start_time); + // Hack aroung with the argv pointer. Used for process.title = "blah". - argv = node::Platform::SetupArgs(argc, argv); + argv = uv_setup_args(argc, argv); // Parse a few arguments which are specific to Node. node::ParseArgs(argc, argv); @@ -2568,20 +2625,6 @@ char** Init(int argc, char *argv[]) { V8::SetFatalErrorHandler(node::OnFatalError); - - // Set the callback DebugMessageDispatch which is called from the debug - // thread. - Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch); - - // Initialize the async watcher. DebugMessageCallback() is called from the - // main thread to execute a random bit of javascript - which will give V8 - // control so it can handle whatever new message had been received on the - // debug thread. - uv_async_init(uv_default_loop(), &node::debug_watcher, - node::DebugMessageCallback); - // unref it so that we exit the event loop despite it being active. - uv_unref(uv_default_loop()); - // Fetch a reference to the main isolate, so we have a reference to it // even when we need it to access it from another (debugger) thread. node_isolate = Isolate::GetCurrent(); @@ -2619,32 +2662,38 @@ int Start(int argc, char *argv[]) { // This needs to run *before* V8::Initialize() argv = Init(argc, argv); - v8::V8::Initialize(); - v8::HandleScope handle_scope; + V8::Initialize(); + Persistent<Context> context; + { + Locker locker; + HandleScope handle_scope; - // Create the one and only Context. - Persistent<v8::Context> context = v8::Context::New(); - v8::Context::Scope context_scope(context); + // Create the one and only Context. + Persistent<Context> context = Context::New(); + Context::Scope context_scope(context); - Handle<Object> process_l = SetupProcessObject(argc, argv); - v8_typed_array::AttachBindings(context->Global()); + Handle<Object> process_l = SetupProcessObject(argc, argv); + v8_typed_array::AttachBindings(context->Global()); - // Create all the objects, load modules, do everything. - // so your next reading stop should be node::Load()! - Load(process_l); + // Create all the objects, load modules, do everything. + // so your next reading stop should be node::Load()! + Load(process_l); - // All our arguments are loaded. We've evaluated all of the scripts. We - // might even have created TCP servers. Now we enter the main eventloop. If - // there are no watchers on the loop (except for the ones that were - // uv_unref'd) then this function exits. As long as there are active - // watchers, it blocks. - uv_run(uv_default_loop()); + // All our arguments are loaded. We've evaluated all of the scripts. We + // might even have created TCP servers. Now we enter the main eventloop. If + // there are no watchers on the loop (except for the ones that were + // uv_unref'd) then this function exits. As long as there are active + // watchers, it blocks. + uv_run(uv_default_loop()); - EmitExit(process_l); + EmitExit(process_l); +#ifndef NDEBUG + context.Dispose(); +#endif + } #ifndef NDEBUG // Clean up. - context.Dispose(); V8::Dispose(); #endif // NDEBUG diff --git a/src/node.h b/src/node.h index 05e3f7535f..703c74478e 100644 --- a/src/node.h +++ b/src/node.h @@ -66,24 +66,8 @@ #include <node_object_wrap.h> -#if defined(_MSC_VER) -#undef interface -#endif - -#ifndef offset_of -// g++ in strict mode complains loudly about the system offsetof() macro -// because it uses NULL as the base address. -#define offset_of(type, member) \ - ((intptr_t) ((char *) &(((type *) 8)->member) - 8)) -#endif - -#ifndef container_of -#define container_of(ptr, type, member) \ - ((type *) ((char *) (ptr) - offset_of(type, member))) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#if NODE_WANT_INTERNALS +# include "node_internals.h" #endif #ifndef NODE_STRINGIFY @@ -113,19 +97,25 @@ void EmitExit(v8::Handle<v8::Object> process); static_cast<v8::PropertyAttribute>( \ v8::ReadOnly|v8::DontDelete)) -#define NODE_SET_METHOD(obj, name, callback) \ - obj->Set(v8::String::NewSymbol(name), \ - v8::FunctionTemplate::New(callback)->GetFunction()) - -#define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \ -do { \ - v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ); \ - v8::Local<v8::FunctionTemplate> __callback##_TEM = \ - v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(), \ - __callback##_SIG); \ - templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name), \ - __callback##_TEM); \ -} while (0) +template <typename target_t> +void SetMethod(target_t obj, const char* name, + v8::InvocationCallback callback) +{ + obj->Set(v8::String::NewSymbol(name), + v8::FunctionTemplate::New(callback)->GetFunction()); +} + +template <typename target_t> +void SetPrototypeMethod(target_t target, + const char* name, v8::InvocationCallback callback) +{ + v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(callback); + target->PrototypeTemplate()->Set(v8::String::NewSymbol(name), templ); +} + +// for backwards compatibility +#define NODE_SET_METHOD node::SetMethod +#define NODE_SET_PROTOTYPE_METHOD node::SetPrototypeMethod enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX}; enum encoding ParseEncoding(v8::Handle<v8::Value> encoding_v, diff --git a/src/node.js b/src/node.js index 80f30a0109..e73eb18b45 100644 --- a/src/node.js +++ b/src/node.js @@ -46,8 +46,6 @@ startup.processChannel(); - startup.removedMethods(); - startup.resolveArgv0(); // There are various modes that Node can run in. The most common two @@ -86,9 +84,9 @@ // If this is a worker in cluster mode, start up the communiction // channel. - if (process.env.NODE_WORKER_ID) { + if (process.env.NODE_UNIQUE_ID) { var cluster = NativeModule.require('cluster'); - cluster._startWorker(); + cluster._setupWorker(); } var Module = NativeModule.require('module'); @@ -424,38 +422,13 @@ // Load tcp_wrap to avoid situation where we might immediately receive // a message. // FIXME is this really necessary? - process.binding('tcp_wrap') + process.binding('tcp_wrap'); cp._forkChild(); assert(process.send); } } - startup._removedProcessMethods = { - 'assert': 'process.assert() use require("assert").ok() instead', - 'debug': 'process.debug() use console.error() instead', - 'error': 'process.error() use console.error() instead', - 'watchFile': 'process.watchFile() has moved to fs.watchFile()', - 'unwatchFile': 'process.unwatchFile() has moved to fs.unwatchFile()', - 'mixin': 'process.mixin() has been removed.', - 'createChildProcess': 'childProcess API has changed. See doc/api.txt.', - 'inherits': 'process.inherits() has moved to util.inherits()', - '_byteLength': 'process._byteLength() has moved to Buffer.byteLength' - }; - - startup.removedMethods = function() { - for (var method in startup._removedProcessMethods) { - var reason = startup._removedProcessMethods[method]; - process[method] = startup._removedMethod(reason); - } - }; - - startup._removedMethod = function(reason) { - return function() { - throw new Error(reason); - }; - }; - startup.resolveArgv0 = function() { var cwd = process.cwd(); var isWindows = process.platform === 'win32'; @@ -548,5 +521,34 @@ NativeModule._cache[this.id] = this; }; + // Wrap a core module's method in a wrapper that will warn on first use + // and then return the result of invoking the original function. After + // first being called the original method is restored. + NativeModule.prototype.deprecate = function(method, message) { + var original = this.exports[method]; + var self = this; + var warned = false; + message = message || ''; + + Object.defineProperty(this.exports, method, { + enumerable: false, + value: function() { + if (!warned) { + warned = true; + message = self.id + '.' + method + ' is deprecated. ' + message; + + var moduleIdCheck = new RegExp('\\b' + self.id + '\\b'); + if (moduleIdCheck.test(process.env.NODE_DEBUG)) + console.trace(message); + else + console.error(message); + + self.exports[method] = original; + } + return original.apply(this, arguments); + } + }); + }; + startup(); }); diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 51e5423e5e..87b5bd4378 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -29,10 +29,6 @@ #include <stdlib.h> // malloc, free #include <string.h> // memcpy -#ifdef __MINGW32__ -# include "platform.h" -#endif - #ifdef __POSIX__ # include <arpa/inet.h> // htons, htonl #endif diff --git a/src/node_constants.cc b/src/node_constants.cc index e924f9ed14..25ad02f8f9 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -32,10 +32,6 @@ #include <sys/types.h> #include <sys/stat.h> -#if defined(__MINGW32__) || defined(_MSC_VER) -# include <platform_win32.h> -#endif - #if HAVE_OPENSSL # include <openssl/ssl.h> #endif diff --git a/src/node_crypto.cc b/src/node_crypto.cc index cf43cc6504..b011d52061 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -56,10 +56,8 @@ static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----"; static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1; - static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----"; static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1; - static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | XN_FLAG_SEP_MULTILINE @@ -86,67 +84,48 @@ static Persistent<String> ext_key_usage_symbol; static Persistent<FunctionTemplate> secure_context_constructor; -#ifdef _WIN32 - -static HANDLE* locks; - - -static void crypto_lock_init(void) { - int i, n; - - n = CRYPTO_num_locks(); - locks = new HANDLE[n]; - - for (i = 0; i < n; i++) - if (!(locks[i] = CreateMutex(NULL, FALSE, NULL))) - abort(); -} - - -static void crypto_lock_cb(int mode, int n, const char* file, int line) { - if (mode & CRYPTO_LOCK) - WaitForSingleObject(locks[n], INFINITE); - else - ReleaseMutex(locks[n]); -} +static uv_rwlock_t* locks; static unsigned long crypto_id_cb(void) { +#ifdef _WIN32 return (unsigned long) GetCurrentThreadId(); -} - #else /* !_WIN32 */ - -static pthread_rwlock_t* locks; + return (unsigned long) pthread_self(); +#endif /* !_WIN32 */ +} static void crypto_lock_init(void) { int i, n; n = CRYPTO_num_locks(); - locks = new pthread_rwlock_t[n]; + locks = new uv_rwlock_t[n]; for (i = 0; i < n; i++) - if (pthread_rwlock_init(locks + i, NULL)) + if (uv_rwlock_init(locks + i)) abort(); } static void crypto_lock_cb(int mode, int n, const char* file, int line) { + assert((mode & CRYPTO_LOCK) || (mode & CRYPTO_UNLOCK)); + assert((mode & CRYPTO_READ) || (mode & CRYPTO_WRITE)); + if (mode & CRYPTO_LOCK) { - if (mode & CRYPTO_READ) pthread_rwlock_rdlock(locks + n); - if (mode & CRYPTO_WRITE) pthread_rwlock_wrlock(locks + n); + if (mode & CRYPTO_READ) + uv_rwlock_rdlock(locks + n); + else + uv_rwlock_wrlock(locks + n); } else { - pthread_rwlock_unlock(locks + n); + if (mode & CRYPTO_READ) + uv_rwlock_rdunlock(locks + n); + else + uv_rwlock_wrunlock(locks + n); } } -static unsigned long crypto_id_cb(void) { - return (unsigned long) pthread_self(); -} - -#endif /* !_WIN32 */ void SecureContext::Initialize(Handle<Object> target) { @@ -1914,6 +1893,7 @@ class Cipher : public ObjectWrap { NODE_SET_PROTOTYPE_METHOD(t, "init", CipherInit); NODE_SET_PROTOTYPE_METHOD(t, "initiv", CipherInitIv); NODE_SET_PROTOTYPE_METHOD(t, "update", CipherUpdate); + NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding); NODE_SET_PROTOTYPE_METHOD(t, "final", CipherFinal); target->Set(String::NewSymbol("Cipher"), t->GetFunction()); @@ -1977,7 +1957,6 @@ class Cipher : public ObjectWrap { return true; } - int CipherUpdate(char* data, int len, unsigned char** out, int* out_len) { if (!initialised_) return 0; *out_len=len+EVP_CIPHER_CTX_block_size(&ctx); @@ -1987,19 +1966,24 @@ class Cipher : public ObjectWrap { return 1; } + int SetAutoPadding(bool auto_padding) { + if (!initialised_) return 0; + return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0); + } + int CipherFinal(unsigned char** out, int *out_len) { if (!initialised_) return 0; *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)]; - EVP_CipherFinal_ex(&ctx,*out,out_len); + int r = EVP_CipherFinal_ex(&ctx,*out, out_len); EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; - return 1; + return r; } protected: - static Handle<Value> New (const Arguments& args) { + static Handle<Value> New(const Arguments& args) { HandleScope scope; Cipher *cipher = new Cipher(); @@ -2187,21 +2171,41 @@ class Cipher : public ObjectWrap { return scope.Close(outString); } + static Handle<Value> SetAutoPadding(const Arguments& args) { + HandleScope scope; + Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This()); + + cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue()); + + return Undefined(); + } + static Handle<Value> CipherFinal(const Arguments& args) { Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This()); HandleScope scope; - unsigned char* out_value; - int out_len; + unsigned char* out_value = NULL; + int out_len = -1; char* out_hexdigest; int out_hex_len; Local<Value> outString ; int r = cipher->CipherFinal(&out_value, &out_len); + assert(out_value != NULL); + assert(out_len != -1 || r == 0); + if (out_len == 0 || r == 0) { - return scope.Close(String::New("")); + // out_value always get allocated. + delete[] out_value; + if (r == 0) { + Local<Value> exception = Exception::TypeError( + String::New("CipherFinal fail")); + return ThrowException(exception); + } else { + return scope.Close(String::New("")); + } } enum encoding enc = ParseEncoding(args[0], BINARY); @@ -2276,7 +2280,9 @@ class Decipher : public ObjectWrap { NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv); NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate); NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal<false>); + // This is completely undocumented: NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal<true>); + NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding); target->Set(String::NewSymbol("Decipher"), t->GetFunction()); } @@ -2359,9 +2365,16 @@ class Decipher : public ObjectWrap { return 1; } + int SetAutoPadding(bool auto_padding) { + if (!initialised_) return 0; + return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0); + } + // coverity[alloc_arg] template <bool TOLERATE_PADDING> int DecipherFinal(unsigned char** out, int *out_len) { + int r; + if (!initialised_) { *out_len = 0; *out = NULL; @@ -2370,13 +2383,13 @@ class Decipher : public ObjectWrap { *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)]; if (TOLERATE_PADDING) { - local_EVP_DecryptFinal_ex(&ctx,*out,out_len); + r = local_EVP_DecryptFinal_ex(&ctx,*out,out_len); } else { - EVP_CipherFinal_ex(&ctx,*out,out_len); + r = EVP_CipherFinal_ex(&ctx,*out,out_len); } EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; - return 1; + return r; } @@ -2609,21 +2622,39 @@ class Decipher : public ObjectWrap { } + static Handle<Value> SetAutoPadding(const Arguments& args) { + HandleScope scope; + Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This()); + + cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue()); + + return Undefined(); + } + template <bool TOLERATE_PADDING> static Handle<Value> DecipherFinal(const Arguments& args) { HandleScope scope; Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This()); - unsigned char* out_value; - int out_len; + unsigned char* out_value = NULL; + int out_len = -1; Local<Value> outString; int r = cipher->DecipherFinal<TOLERATE_PADDING>(&out_value, &out_len); + assert(out_value != NULL); + assert(out_len != -1); + if (out_len == 0 || r == 0) { - delete[] out_value; - return scope.Close(String::New("")); + delete [] out_value; // allocated even if out_len == 0 + if (r == 0) { + Local<Value> exception = Exception::TypeError( + String::New("DecipherFinal fail")); + return ThrowException(exception); + } else { + return scope.Close(String::New("")); + } } if (args.Length() == 0 || !args[0]->IsString()) { @@ -2820,14 +2851,17 @@ class Hmac : public ObjectWrap { HandleScope scope; - unsigned char* md_value; - unsigned int md_len; + unsigned char* md_value = NULL; + unsigned int md_len = -1; char* md_hexdigest; int md_hex_len; Local<Value> outString ; int r = hmac->HmacDigest(&md_value, &md_len); + assert(md_value != NULL); + assert(md_len != -1); + if (md_len == 0 || r == 0) { return scope.Close(String::New("")); } @@ -2887,10 +2921,7 @@ class Hash : public ObjectWrap { bool HashInit (const char* hashType) { md = EVP_get_digestbyname(hashType); - if(!md) { - fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType); - return false; - } + if(!md) return false; EVP_MD_CTX_init(&mdctx); EVP_DigestInit_ex(&mdctx, md, NULL); initialised_ = true; @@ -2914,13 +2945,16 @@ class Hash : public ObjectWrap { "Must give hashtype string as argument"))); } - Hash *hash = new Hash(); - hash->Wrap(args.This()); - String::Utf8Value hashType(args[0]->ToString()); - hash->HashInit(*hashType); + Hash *hash = new Hash(); + if (!hash->HashInit(*hashType)) { + delete hash; + return ThrowException(Exception::Error(String::New( + "Digest method not supported"))); + } + hash->Wrap(args.This()); return args.This(); } diff --git a/src/node_extensions.h b/src/node_extensions.h index 39ddf1748f..7a7702c9fb 100644 --- a/src/node_extensions.h +++ b/src/node_extensions.h @@ -22,6 +22,7 @@ NODE_EXT_LIST_START NODE_EXT_LIST_ITEM(node_buffer) +NODE_EXT_LIST_ITEM(node_typed_array) #if HAVE_OPENSSL NODE_EXT_LIST_ITEM(node_crypto) #endif diff --git a/src/node_file.cc b/src/node_file.cc index 5e0c0ad048..3042f2e2b2 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -37,7 +37,6 @@ #if defined(__MINGW32__) || defined(_MSC_VER) # include <io.h> -# include <platform_win32.h> #endif @@ -46,8 +45,11 @@ namespace node { using namespace v8; #define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define THROW_BAD_ARGS \ - ThrowException(Exception::TypeError(String::New("Bad argument"))) + +#define TYPE_ERROR(msg) \ + ThrowException(Exception::TypeError(String::New(msg))); + +#define THROW_BAD_ARGS TYPE_ERROR("Bad argument") typedef class ReqWrap<uv_fs_t> FSReqWrap; @@ -209,9 +211,15 @@ struct fs_req_wrap { FSReqWrap* req_wrap = new FSReqWrap(); \ int r = uv_fs_##func(uv_default_loop(), &req_wrap->req_, \ __VA_ARGS__, After); \ - assert(r == 0); \ req_wrap->object_->Set(oncomplete_sym, callback); \ req_wrap->Dispatched(); \ + if (r < 0) { \ + uv_fs_t* req = &req_wrap->req_; \ + req->result = r; \ + req->path = NULL; \ + req->errorno = uv_last_error(uv_default_loop()).code; \ + After(req); \ + } \ return scope.Close(req_wrap->object_); #define SYNC_CALL(func, path, ...) \ @@ -330,9 +338,8 @@ Local<Object> BuildStatsObject(NODE_STAT_STRUCT *s) { static Handle<Value> Stat(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -347,9 +354,8 @@ static Handle<Value> Stat(const Arguments& args) { static Handle<Value> LStat(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -381,9 +387,11 @@ static Handle<Value> FStat(const Arguments& args) { static Handle<Value> Symlink(const Arguments& args) { HandleScope scope; - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { - return THROW_BAD_ARGS; - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("dest path required"); + if (len < 2) return TYPE_ERROR("src path required"); + if (!args[0]->IsString()) return TYPE_ERROR("dest path must be a string"); + if (!args[1]->IsString()) return TYPE_ERROR("src path must be a string"); String::Utf8Value dest(args[0]->ToString()); String::Utf8Value path(args[1]->ToString()); @@ -407,9 +415,11 @@ static Handle<Value> Symlink(const Arguments& args) { static Handle<Value> Link(const Arguments& args) { HandleScope scope; - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { - return THROW_BAD_ARGS; - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("dest path required"); + if (len < 2) return TYPE_ERROR("src path required"); + if (!args[0]->IsString()) return TYPE_ERROR("dest path must be a string"); + if (!args[1]->IsString()) return TYPE_ERROR("src path must be a string"); String::Utf8Value orig_path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); @@ -425,9 +435,8 @@ static Handle<Value> Link(const Arguments& args) { static Handle<Value> ReadLink(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -442,10 +451,12 @@ static Handle<Value> ReadLink(const Arguments& args) { static Handle<Value> Rename(const Arguments& args) { HandleScope scope; - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { - return THROW_BAD_ARGS; - } - + int len = args.Length(); + if (len < 1) return TYPE_ERROR("old path required"); + if (len < 2) return TYPE_ERROR("new path required"); + if (!args[0]->IsString()) return TYPE_ERROR("old path must be a string"); + if (!args[1]->IsString()) return TYPE_ERROR("new path must be a string"); + String::Utf8Value old_path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); @@ -528,9 +539,8 @@ static Handle<Value> Fsync(const Arguments& args) { static Handle<Value> Unlink(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -545,9 +555,8 @@ static Handle<Value> Unlink(const Arguments& args) { static Handle<Value> RMDir(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -604,9 +613,8 @@ static Handle<Value> SendFile(const Arguments& args) { static Handle<Value> ReadDir(const Arguments& args) { HandleScope scope; - if (args.Length() < 1 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } + if (args.Length() < 1) return TYPE_ERROR("path required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); String::Utf8Value path(args[0]->ToString()); @@ -638,12 +646,13 @@ static Handle<Value> ReadDir(const Arguments& args) { static Handle<Value> Open(const Arguments& args) { HandleScope scope; - if (args.Length() < 3 || - !args[0]->IsString() || - !args[1]->IsInt32() || - !args[2]->IsInt32()) { - return THROW_BAD_ARGS; - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("path required"); + if (len < 2) return TYPE_ERROR("flags required"); + if (len < 3) return TYPE_ERROR("mode required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); + if (!args[1]->IsInt32()) return TYPE_ERROR("flags must be an int"); + if (!args[2]->IsInt32()) return TYPE_ERROR("mode must be an int"); String::Utf8Value path(args[0]->ToString()); int flags = args[1]->Int32Value(); @@ -838,13 +847,13 @@ static Handle<Value> FChmod(const Arguments& args) { static Handle<Value> Chown(const Arguments& args) { HandleScope scope; - if (args.Length() < 3 || !args[0]->IsString()) { - return THROW_BAD_ARGS; - } - - if (!args[1]->IsInt32() || !args[2]->IsInt32()) { - return ThrowException(Exception::Error(String::New("User and Group IDs must be an integer."))); - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("path required"); + if (len < 2) return TYPE_ERROR("uid required"); + if (len < 3) return TYPE_ERROR("gid required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); + if (!args[1]->IsInt32()) return TYPE_ERROR("uid must be an int"); + if (!args[2]->IsInt32()) return TYPE_ERROR("gid must be an int"); String::Utf8Value path(args[0]->ToString()); int uid = static_cast<int>(args[1]->Int32Value()); @@ -865,13 +874,13 @@ static Handle<Value> Chown(const Arguments& args) { static Handle<Value> FChown(const Arguments& args) { HandleScope scope; - if (args.Length() < 3 || !args[0]->IsInt32()) { - return THROW_BAD_ARGS; - } - - if (!args[1]->IsInt32() || !args[2]->IsInt32()) { - return ThrowException(Exception::Error(String::New("User and Group IDs must be an integer."))); - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("path required"); + if (len < 2) return TYPE_ERROR("uid required"); + if (len < 3) return TYPE_ERROR("gid required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); + if (!args[1]->IsInt32()) return TYPE_ERROR("uid must be an int"); + if (!args[2]->IsInt32()) return TYPE_ERROR("gid must be an int"); int fd = args[0]->Int32Value(); int uid = static_cast<int>(args[1]->Int32Value()); @@ -889,13 +898,13 @@ static Handle<Value> FChown(const Arguments& args) { static Handle<Value> UTimes(const Arguments& args) { HandleScope scope; - if (args.Length() < 3 - || !args[0]->IsString() - || !args[1]->IsNumber() - || !args[2]->IsNumber()) - { - return THROW_BAD_ARGS; - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("path required"); + if (len < 2) return TYPE_ERROR("atime required"); + if (len < 3) return TYPE_ERROR("mtime required"); + if (!args[0]->IsString()) return TYPE_ERROR("path must be a string"); + if (!args[1]->IsNumber()) return TYPE_ERROR("atime must be a number"); + if (!args[2]->IsNumber()) return TYPE_ERROR("mtime must be a number"); const String::Utf8Value path(args[0]->ToString()); const double atime = static_cast<double>(args[1]->NumberValue()); @@ -912,13 +921,13 @@ static Handle<Value> UTimes(const Arguments& args) { static Handle<Value> FUTimes(const Arguments& args) { HandleScope scope; - if (args.Length() < 3 - || !args[0]->IsInt32() - || !args[1]->IsNumber() - || !args[2]->IsNumber()) - { - return THROW_BAD_ARGS; - } + int len = args.Length(); + if (len < 1) return TYPE_ERROR("fd required"); + if (len < 2) return TYPE_ERROR("atime required"); + if (len < 3) return TYPE_ERROR("mtime required"); + if (!args[0]->IsInt32()) return TYPE_ERROR("fd must be an int"); + if (!args[1]->IsNumber()) return TYPE_ERROR("atime must be a number"); + if (!args[2]->IsNumber()) return TYPE_ERROR("mtime must be a number"); const int fd = args[0]->Int32Value(); const double atime = static_cast<double>(args[1]->NumberValue()); diff --git a/src/node_internals.h b/src/node_internals.h new file mode 100644 index 0000000000..89c949946e --- /dev/null +++ b/src/node_internals.h @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SRC_NODE_INTERNALS_H_ +#define SRC_NODE_INTERNALS_H_ + +namespace node { + +#ifndef offset_of +// g++ in strict mode complains loudly about the system offsetof() macro +// because it uses NULL as the base address. +#define offset_of(type, member) \ + ((intptr_t) ((char *) &(((type *) 8)->member) - 8)) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offset_of(type, member))) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#endif + +} // namespace node + +#endif // SRC_NODE_INTERNALS_H_ diff --git a/src/node_os.cc b/src/node_os.cc index 287467531d..b854961132 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -22,7 +22,6 @@ #include <node.h> #include <node_os.h> -#include "platform.h" #include <v8.h> @@ -31,8 +30,6 @@ #ifdef __MINGW32__ # include <io.h> - -# include <platform_win32.h> #endif #ifdef __POSIX__ @@ -105,13 +102,39 @@ static Handle<Value> GetOSRelease(const Arguments& args) { static Handle<Value> GetCPUInfo(const Arguments& args) { HandleScope scope; - Local<Array> cpus; - int r = Platform::GetCPUInfo(&cpus); + uv_cpu_info_t* cpu_infos; + int count, i; - if (r < 0) { + uv_err_t err = uv_cpu_info(&cpu_infos, &count); + + if (err.code != UV_OK) { return Undefined(); } + Local<Array> cpus = Array::New(); + + for (i = 0; i < count; i++) { + Local<Object> times_info = Object::New(); + times_info->Set(String::New("user"), + Integer::New(cpu_infos[i].cpu_times.user)); + times_info->Set(String::New("nice"), + Integer::New(cpu_infos[i].cpu_times.nice)); + times_info->Set(String::New("sys"), + Integer::New(cpu_infos[i].cpu_times.sys)); + times_info->Set(String::New("idle"), + Integer::New(cpu_infos[i].cpu_times.idle)); + times_info->Set(String::New("irq"), + Integer::New(cpu_infos[i].cpu_times.irq)); + + Local<Object> cpu_info = Object::New(); + cpu_info->Set(String::New("model"), String::New(cpu_infos[i].model)); + cpu_info->Set(String::New("speed"), Integer::New(cpu_infos[i].speed)); + cpu_info->Set(String::New("times"), times_info); + (*cpus)->Set(i,cpu_info); + } + + uv_free_cpu_info(cpu_infos, count); + return scope.Close(cpus); } @@ -139,9 +162,11 @@ static Handle<Value> GetTotalMemory(const Arguments& args) { static Handle<Value> GetUptime(const Arguments& args) { HandleScope scope; - double uptime = Platform::GetUptime(); + double uptime; - if (uptime < 0) { + uv_err_t err = uv_uptime(&uptime); + + if (err.code != UV_OK) { return Undefined(); } @@ -163,7 +188,54 @@ static Handle<Value> GetLoadAvg(const Arguments& args) { static Handle<Value> GetInterfaceAddresses(const Arguments& args) { - return Platform::GetInterfaceAddresses(); + HandleScope scope; + uv_interface_address_t* interfaces; + int count, i; + char ip[INET6_ADDRSTRLEN]; + Local<Object> ret, o; + Local<String> name, family; + Local<Array> ifarr; + + uv_err_t err = uv_interface_addresses(&interfaces, &count); + + if (err.code != UV_OK) { + return Undefined(); + } + + ret = Object::New(); + + for (i = 0; i < count; i++) { + name = String::New(interfaces[i].name); + if (ret->Has(name)) { + ifarr = Local<Array>::Cast(ret->Get(name)); + } else { + ifarr = Array::New(); + ret->Set(name, ifarr); + } + + if (interfaces[i].address.address4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].address.address4,ip, sizeof(ip)); + family = String::New("IPv4"); + } else if (interfaces[i].address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip)); + family = String::New("IPv6"); + } else { + strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN); + family = String::New("<unknown>"); + } + + o = Object::New(); + o->Set(String::New("address"), String::New(ip)); + o->Set(String::New("family"), family); + o->Set(String::New("internal"), interfaces[i].is_internal ? + True() : False()); + + ifarr->Set(ifarr->Length(), o); + } + + uv_free_interface_addresses(interfaces, count); + + return scope.Close(ret); } diff --git a/src/node_version.h b/src/node_version.h index 2b715b7aff..59a3313076 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -27,8 +27,8 @@ #define NODE_VERSION_H #define NODE_MAJOR_VERSION 0 -#define NODE_MINOR_VERSION 6 -#define NODE_PATCH_VERSION 12 +#define NODE_MINOR_VERSION 7 +#define NODE_PATCH_VERSION 5 #define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_STRINGIFY diff --git a/src/node_zlib.cc b/src/node_zlib.cc index fe7be7412f..a27fcef375 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -60,6 +60,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { public: ZCtx() : ObjectWrap() { + dictionary_ = NULL; } ~ZCtx() { @@ -68,6 +69,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) { (void)inflateEnd(&strm_); } + + if (dictionary_ != NULL) delete[] dictionary_; } // write(flush, in, in_off, in_len, out, out_off, out_len) @@ -155,6 +158,20 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { case GUNZIP: case INFLATERAW: err = inflate(&ctx->strm_, ctx->flush_); + + // If data was encoded with dictionary + if (err == Z_NEED_DICT) { + assert(ctx->dictionary_ != NULL && "Stream has no dictionary"); + + // Load it + err = inflateSetDictionary(&ctx->strm_, + ctx->dictionary_, + ctx->dictionary_len_); + assert(err == Z_OK && "Failed to set dictionary"); + + // And try to decode again + err = inflate(&ctx->strm_, ctx->flush_); + } break; default: assert(0 && "wtf?"); @@ -196,8 +213,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { static Handle<Value> Init(const Arguments& args) { HandleScope scope; - assert(args.Length() == 4 && - "init(windowBits, level, memLevel, strategy)"); + assert((args.Length() == 4 || args.Length() == 5) && + "init(windowBits, level, memLevel, strategy, [dictionary])"); ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This()); @@ -217,12 +234,35 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { strategy == Z_FIXED || strategy == Z_DEFAULT_STRATEGY) && "invalid strategy"); - Init(ctx, level, windowBits, memLevel, strategy); + char* dictionary = NULL; + size_t dictionary_len = 0; + if (args.Length() >= 5 && Buffer::HasInstance(args[4])) { + Local<Object> dictionary_ = args[4]->ToObject(); + + dictionary_len = Buffer::Length(dictionary_); + dictionary = new char[dictionary_len]; + + memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len); + } + + Init(ctx, level, windowBits, memLevel, strategy, + dictionary, dictionary_len); + SetDictionary(ctx); + return Undefined(); + } + + static Handle<Value> Reset(const Arguments &args) { + HandleScope scope; + + ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This()); + + Reset(ctx); + SetDictionary(ctx); return Undefined(); } static void Init(ZCtx *ctx, int level, int windowBits, int memLevel, - int strategy) { + int strategy, char* dictionary, size_t dictionary_len) { ctx->level_ = level; ctx->windowBits_ = windowBits; ctx->memLevel_ = memLevel; @@ -251,7 +291,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { case DEFLATE: case GZIP: case DEFLATERAW: - err = deflateInit2(&(ctx->strm_), + err = deflateInit2(&ctx->strm_, ctx->level_, Z_DEFLATED, ctx->windowBits_, @@ -262,15 +302,57 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { case GUNZIP: case INFLATERAW: case UNZIP: - err = inflateInit2(&(ctx->strm_), ctx->windowBits_); + err = inflateInit2(&ctx->strm_, ctx->windowBits_); break; default: assert(0 && "wtf?"); } + assert(err == Z_OK); + + ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary); + ctx->dictionary_len_ = dictionary_len; + ctx->write_in_progress_ = false; ctx->init_done_ = true; - assert(err == Z_OK); + } + + static void SetDictionary(ZCtx* ctx) { + if (ctx->dictionary_ == NULL) return; + + int err = Z_OK; + + switch (mode) { + case DEFLATE: + case DEFLATERAW: + err = deflateSetDictionary(&ctx->strm_, + ctx->dictionary_, + ctx->dictionary_len_); + break; + default: + break; + } + + assert(err == Z_OK && "Failed to set dictionary"); + } + + static void Reset(ZCtx* ctx) { + int err = Z_OK; + + switch (mode) { + case DEFLATE: + case DEFLATERAW: + err = deflateReset(&ctx->strm_); + break; + case INFLATE: + case INFLATERAW: + err = inflateReset(&ctx->strm_); + break; + default: + break; + } + + assert(err == Z_OK && "Failed to reset stream"); } private: @@ -283,6 +365,9 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { int memLevel_; int strategy_; + Bytef* dictionary_; + size_t dictionary_len_; + int flush_; int chunk_size_; @@ -299,6 +384,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { z->InstanceTemplate()->SetInternalFieldCount(1); \ NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \ NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \ + NODE_SET_PROTOTYPE_METHOD(z, "reset", ZCtx<mode>::Reset); \ z->SetClassName(String::NewSymbol(name)); \ target->Set(String::NewSymbol(name), z->GetFunction()); \ } diff --git a/src/platform.h b/src/platform.h deleted file mode 100644 index e89ebfaa6a..0000000000 --- a/src/platform.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef NODE_PLATFORM_H_ -#define NODE_PLATFORM_H_ - -#include <v8.h> - -namespace node { - -class Platform { - public: - static char** SetupArgs(int argc, char *argv[]); - static void SetProcessTitle(char *title); - static const char* GetProcessTitle(int *len); - - static int GetMemory(size_t *rss); - static int GetCPUInfo(v8::Local<v8::Array> *cpus); - static double GetUptime(bool adjusted = false) - { - return adjusted ? GetUptimeImpl() - prog_start_time : GetUptimeImpl(); - } - static v8::Handle<v8::Value> GetInterfaceAddresses(); - private: - static double GetUptimeImpl(); - static double prog_start_time; -}; - - -} // namespace node -#endif // NODE_PLATFORM_H_ diff --git a/src/platform_darwin.cc b/src/platform_darwin.cc deleted file mode 100644 index a196669eb7..0000000000 --- a/src/platform_darwin.cc +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - -#include <v8.h> - -#include <mach/task.h> -#include <mach/mach.h> -#include <mach/mach_host.h> -#include <limits.h> /* PATH_MAX */ - -#include <unistd.h> // sysconf -#include <sys/param.h> -#include <sys/sysctl.h> -#include <time.h> -#include <sys/socket.h> -#include <net/if.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <ifaddrs.h> - - - -namespace node { - -using namespace v8; - -static char *process_title; -double Platform::prog_start_time = Platform::GetUptime(); - -char** Platform::SetupArgs(int argc, char *argv[]) { - process_title = argc ? strdup(argv[0]) : NULL; - return argv; -} - - -// Platform::SetProcessTitle implemented in platform_darwin_proctitle.cc -} // namespace node -#include "platform_darwin_proctitle.cc" -namespace node { - - -const char* Platform::GetProcessTitle(int *len) { - if (process_title) { - *len = strlen(process_title); - return process_title; - } - *len = 0; - return NULL; -} - -// Researched by Tim Becker and Michael Knight -// http://blog.kuriositaet.de/?p=257 -int Platform::GetMemory(size_t *rss) { - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - int r = task_info(mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&t_info, - &t_info_count); - - if (r != KERN_SUCCESS) return -1; - - *rss = t_info.resident_size; - - return 0; -} - - -int Platform::GetCPUInfo(Local<Array> *cpus) { - Local<Object> cpuinfo; - Local<Object> cputimes; - unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), - multiplier = ((uint64_t)1000L / ticks); - char model[512]; - uint64_t cpuspeed; - size_t size; - - size = sizeof(model); - if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { - return -1; - } - size = sizeof(cpuspeed); - if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0) < 0) { - return -1; - } - - natural_t numcpus; - mach_msg_type_number_t count; - processor_cpu_load_info_data_t *info; - if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, - reinterpret_cast<processor_info_array_t*>(&info), - &count) != KERN_SUCCESS) { - return -1; - } - *cpus = Array::New(numcpus); - for (unsigned int i = 0; i < numcpus; i++) { - cpuinfo = Object::New(); - cputimes = Object::New(); - cputimes->Set(String::New("user"), - Number::New((uint64_t)(info[i].cpu_ticks[0]) * multiplier)); - cputimes->Set(String::New("nice"), - Number::New((uint64_t)(info[i].cpu_ticks[3]) * multiplier)); - cputimes->Set(String::New("sys"), - Number::New((uint64_t)(info[i].cpu_ticks[1]) * multiplier)); - cputimes->Set(String::New("idle"), - Number::New((uint64_t)(info[i].cpu_ticks[2]) * multiplier)); - cputimes->Set(String::New("irq"), Number::New(0)); - - cpuinfo->Set(String::New("model"), String::New(model)); - cpuinfo->Set(String::New("speed"), Number::New(cpuspeed/1000000)); - - cpuinfo->Set(String::New("times"), cputimes); - (*cpus)->Set(i, cpuinfo); - } - vm_deallocate(mach_task_self(), (vm_address_t)info, count); - - return 0; -} - -double Platform::GetUptimeImpl() { - time_t now; - struct timeval info; - size_t size = sizeof(info); - static int which[] = {CTL_KERN, KERN_BOOTTIME}; - - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { - return -1; - } - now = time(NULL); - - return static_cast<double>(now - info.tv_sec); -} - - -v8::Handle<v8::Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - struct ::ifaddrs *addrs, *ent; - struct ::sockaddr_in *in4; - struct ::sockaddr_in6 *in6; - char ip[INET6_ADDRSTRLEN]; - Local<Object> ret, o; - Local<String> name, ipaddr, family; - Local<Array> ifarr; - - if (getifaddrs(&addrs) != 0) { - return ThrowException(ErrnoException(errno, "getifaddrs")); - } - - ret = Object::New(); - - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - bzero(&ip, sizeof (ip)); - if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { - continue; - } - - if (ent->ifa_addr == NULL) { - continue; - } - - /* - * On Mac OS X getifaddrs returns information related to Mac Addresses for - * various devices, such as firewire, etc. These are not relevant here. - */ - if (ent->ifa_addr->sa_family == AF_LINK) - continue; - - name = String::New(ent->ifa_name); - if (ret->Has(name)) { - ifarr = Local<Array>::Cast(ret->Get(name)); - } else { - ifarr = Array::New(); - ret->Set(name, ifarr); - } - - if (ent->ifa_addr->sa_family == AF_INET6) { - in6 = (struct sockaddr_in6 *)ent->ifa_addr; - inet_ntop(AF_INET6, &(in6->sin6_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv6"); - } else if (ent->ifa_addr->sa_family == AF_INET) { - in4 = (struct sockaddr_in *)ent->ifa_addr; - inet_ntop(AF_INET, &(in4->sin_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv4"); - } else { - (void) strlcpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN); - family = String::New("<unknown>"); - } - - o = Object::New(); - o->Set(String::New("address"), String::New(ip)); - o->Set(String::New("family"), family); - o->Set(String::New("internal"), ent->ifa_flags & IFF_LOOPBACK ? - True() : False()); - - ifarr->Set(ifarr->Length(), o); - - } - - freeifaddrs(addrs); - - return scope.Close(ret); -} - -} // namespace node diff --git a/src/platform_darwin_proctitle.cc b/src/platform_darwin_proctitle.cc deleted file mode 100644 index e6a1ddd371..0000000000 --- a/src/platform_darwin_proctitle.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2010, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// This code emanates from the Chromium project: -// http://src.chromium.org/viewvc/chrome/trunk/src/base/mac_util.mm - -#include <CoreFoundation/CoreFoundation.h> -#include <Carbon/Carbon.h> // Oh noes! See discussion blow at GetCurrentProcess -#ifndef NDEBUG - #include <err.h> -#endif - -namespace node { - -void Platform::SetProcessTitle(char *title) { - static int symbol_lookup_status = 0; // 1=ok, 2=unavailable - if (symbol_lookup_status == 2) { - // feature is unavailable - return; - } - - if (process_title) free(process_title); - process_title = strdup(title); - - // Warning: here be dragons! This is SPI reverse-engineered from WebKit's - // plugin host, and could break at any time (although realistically it's only - // likely to break in a new major release). - // When 10.7 is available, check that this still works, and update this - // comment for 10.8. - - // Private CFType used in these LaunchServices calls. - typedef CFTypeRef PrivateLSASN; - typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); - typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, - CFStringRef, - CFStringRef, - CFDictionaryRef*); - - static LSGetCurrentApplicationASNType ls_get_current_application_asn_func = - NULL; - static LSSetApplicationInformationItemType - ls_set_application_information_item_func = NULL; - static CFStringRef ls_display_name_key = NULL; - if (!symbol_lookup_status) { - CFBundleRef launch_services_bundle = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); - if (!launch_services_bundle) { -#ifndef NDEBUG - warnx("failed to look up LaunchServices bundle"); -#endif - symbol_lookup_status = 2; - return; - } - - ls_get_current_application_asn_func = - reinterpret_cast<LSGetCurrentApplicationASNType>( - CFBundleGetFunctionPointerForName( - launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); - if (!ls_get_current_application_asn_func) { -#ifndef NDEBUG - warnx("could not find _LSGetCurrentApplicationASN"); -#endif - symbol_lookup_status = 2; - return; - } - - ls_set_application_information_item_func = - reinterpret_cast<LSSetApplicationInformationItemType>( - CFBundleGetFunctionPointerForName( - launch_services_bundle, - CFSTR("_LSSetApplicationInformationItem"))); - if (!ls_set_application_information_item_func) { -#ifndef NDEBUG - warnx("Could not find _LSSetApplicationInformationItem"); -#endif - symbol_lookup_status = 2; - return; - } - - const CFStringRef* key_pointer = reinterpret_cast<const CFStringRef*>( - CFBundleGetDataPointerForName(launch_services_bundle, - CFSTR("_kLSDisplayNameKey"))); - ls_display_name_key = key_pointer ? *key_pointer : NULL; - if (!ls_display_name_key) { -#ifndef NDEBUG - warnx("Could not find _kLSDisplayNameKey"); -#endif - symbol_lookup_status = 2; - return; - } - - // Internally, this call relies on the Mach ports that are started up by the - // Carbon Process Manager. In debug builds this usually happens due to how - // the logging layers are started up; but in release, it isn't started in as - // much of a defined order. So if the symbols had to be loaded, go ahead - // and force a call to make sure the manager has been initialized and hence - // the ports are opened. - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - symbol_lookup_status = 1; // 1=ok - } - - PrivateLSASN asn = ls_get_current_application_asn_func(); - // Constant used by WebKit; what exactly it means is unknown. - const int magic_session_constant = -2; - CFStringRef process_name = - CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); - OSErr err = - ls_set_application_information_item_func(magic_session_constant, asn, - ls_display_name_key, - process_name, - NULL /* optional out param */); -#ifndef NDEBUG - if (err) { - warnx("Call LSSetApplicationInformationItem failed"); - } -#endif -} - -} // namespace node diff --git a/src/platform_freebsd.cc b/src/platform_freebsd.cc deleted file mode 100644 index 5b0e01e88e..0000000000 --- a/src/platform_freebsd.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - -#include <v8.h> - -#include <stdlib.h> -#include <kvm.h> -#include <sys/param.h> -#include <sys/sysctl.h> -#include <sys/user.h> -#include <sys/dkstat.h> -#include <vm/vm_param.h> -#include <string.h> -#include <paths.h> -#include <fcntl.h> -#include <unistd.h> -#include <time.h> - - -namespace node { - -using namespace v8; - -static char *process_title; -double Platform::prog_start_time = Platform::GetUptime(); - -char** Platform::SetupArgs(int argc, char *argv[]) { - process_title = argc ? strdup(argv[0]) : NULL; - return argv; -} - - -void Platform::SetProcessTitle(char *title) { - if (process_title) free(process_title); - process_title = strdup(title); - setproctitle(title); -} - -const char* Platform::GetProcessTitle(int *len) { - if (process_title) { - *len = strlen(process_title); - return process_title; - } - *len = 0; - return NULL; -} - -int Platform::GetMemory(size_t *rss) { - kvm_t *kd = NULL; - struct kinfo_proc *kinfo = NULL; - pid_t pid; - int nprocs; - size_t page_size = getpagesize(); - - pid = getpid(); - - kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) goto error; - - kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs); - if (kinfo == NULL) goto error; - - *rss = kinfo->ki_rssize * page_size; - - kvm_close(kd); - - return 0; - -error: - if (kd) kvm_close(kd); - return -1; -} - - -int Platform::GetCPUInfo(Local<Array> *cpus) { - Local<Object> cpuinfo; - Local<Object> cputimes; - unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), - multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus, - cur = 0; - char model[512]; - int numcpus; - size_t size; - - size = sizeof(model); - if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { - return -1; - } - size = sizeof(numcpus); - if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) < 0) { - return -1; - } - - *cpus = Array::New(numcpus); - - size = sizeof(cpuspeed); - if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0) < 0) { - return -1; - } - // kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of ncpu - size = sizeof(maxcpus); - if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { - return -1; - } - size = maxcpus * CPUSTATES * sizeof(long); - long cp_times[size]; - if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) { - return -1; - } - for (int i = 0; i < numcpus; i++) { - cpuinfo = Object::New(); - cputimes = Object::New(); - cputimes->Set(String::New("user"), - Number::New((uint64_t)(cp_times[CP_USER+cur]) * multiplier)); - cputimes->Set(String::New("nice"), - Number::New((uint64_t)(cp_times[CP_NICE+cur]) * multiplier)); - cputimes->Set(String::New("sys"), - Number::New((uint64_t)(cp_times[CP_SYS+cur]) * multiplier)); - cputimes->Set(String::New("idle"), - Number::New((uint64_t)(cp_times[CP_IDLE+cur]) * multiplier)); - cputimes->Set(String::New("irq"), - Number::New((uint64_t)(cp_times[CP_INTR+cur]) * multiplier)); - - cpuinfo->Set(String::New("model"), String::New(model)); - cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); - - cpuinfo->Set(String::New("times"), cputimes); - (*cpus)->Set(i, cpuinfo); - cur+=CPUSTATES; - } - - return 0; -} - -double Platform::GetUptimeImpl() { - time_t now; - struct timeval info; - size_t size = sizeof(info); - static int which[] = {CTL_KERN, KERN_BOOTTIME}; - - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { - return -1; - } - now = time(NULL); - - return static_cast<double>(now - info.tv_sec); -} - - -Handle<Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - return scope.Close(Object::New()); -} - -} // namespace node diff --git a/src/platform_linux.cc b/src/platform_linux.cc deleted file mode 100644 index 287ed3e306..0000000000 --- a/src/platform_linux.cc +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - -#include <v8.h> - -#include <sys/param.h> // for MAXPATHLEN -#include <unistd.h> // getpagesize, sysconf -#include <stdio.h> // sscanf, snprintf - -/* SetProcessTitle */ -#include <sys/prctl.h> -#include <linux/prctl.h> -#include <stdlib.h> // free -#include <string.h> // strdup - -/* GetInterfaceAddresses */ -#include <arpa/inet.h> -#include <sys/types.h> -#include <ifaddrs.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <net/if.h> - -#include <time.h> - -#ifndef CLOCK_MONOTONIC -# include <sys/sysinfo.h> -#endif - -extern char **environ; - -namespace node { - -using namespace v8; - -static char buf[MAXPATHLEN + 1]; -double Platform::prog_start_time = Platform::GetUptime(); - -static struct { - char *str; - size_t len; -} process_title; - - -char** Platform::SetupArgs(int argc, char *argv[]) { - char **new_argv; - char **new_env; - size_t size; - int envc; - char *s; - int i; - - for (envc = 0; environ[envc]; envc++); - - s = envc ? environ[envc - 1] : argv[argc - 1]; - - process_title.str = argv[0]; - process_title.len = s + strlen(s) + 1 - argv[0]; - - size = process_title.len; - size += (argc + 1) * sizeof(char **); - size += (envc + 1) * sizeof(char **); - - if ((s = (char *) malloc(size)) == NULL) { - process_title.str = NULL; - process_title.len = 0; - return argv; - } - - new_argv = (char **) s; - new_env = new_argv + argc + 1; - s = (char *) (new_env + envc + 1); - memcpy(s, process_title.str, process_title.len); - - for (i = 0; i < argc; i++) - new_argv[i] = s + (argv[i] - argv[0]); - new_argv[argc] = NULL; - - s += environ[0] - argv[0]; - - for (i = 0; i < envc; i++) - new_env[i] = s + (environ[i] - environ[0]); - new_env[envc] = NULL; - - environ = new_env; - return new_argv; -} - - -void Platform::SetProcessTitle(char *title) { - /* No need to terminate, last char is always '\0'. */ - if (process_title.len) - strncpy(process_title.str, title, process_title.len - 1); -} - - -const char* Platform::GetProcessTitle(int *len) { - if (process_title.str) { - *len = strlen(process_title.str); - return process_title.str; - } - else { - *len = 0; - return NULL; - } -} - - -int Platform::GetMemory(size_t *rss) { - FILE *f = fopen("/proc/self/stat", "r"); - if (!f) return -1; - - int itmp; - char ctmp; - size_t page_size = getpagesize(); - char *cbuf; - bool foundExeEnd; - - /* PID */ - if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Exec file */ - cbuf = buf; - foundExeEnd = false; - if (fscanf (f, "%c", cbuf++) == 0) goto error; // ( - while (1) { - if (fscanf(f, "%c", cbuf) == 0) goto error; - if (*cbuf == ')') { - foundExeEnd = true; - } else if (foundExeEnd && *cbuf == ' ') { - *cbuf = 0; - break; - } - - cbuf++; - } - /* State */ - if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */ - /* Parent process */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Process group */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Session id */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* TTY */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* TTY owner process group */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Flags */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Minor faults (no memory page) */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Minor faults, children */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Major faults (memory page faults) */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Major faults, children */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* utime */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* stime */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* utime, children */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* stime, children */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies remaining in current time slice */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* 'nice' value */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies until next timeout */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies until next SIGALRM */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* start time (jiffies since system boot) */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - - /* Virtual memory size */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - - /* Resident set size */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - *rss = (size_t) itmp * page_size; - - /* rlim */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Start of text */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* End of text */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Start of stack */ - if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - - fclose (f); - - return 0; - -error: - fclose (f); - return -1; -} - - -int Platform::GetCPUInfo(Local<Array> *cpus) { - HandleScope scope; - Local<Object> cpuinfo; - Local<Object> cputimes; - unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), - multiplier = ((uint64_t)1000L / ticks), cpuspeed; - int numcpus = 0, i = 0; - unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr; - char line[512], speedPath[256], model[512]; - FILE *fpStat = fopen("/proc/stat", "r"); - FILE *fpModel = fopen("/proc/cpuinfo", "r"); - FILE *fpSpeed; - - if (fpModel) { - while (fgets(line, 511, fpModel) != NULL) { - if (strncmp(line, "model name", 10) == 0) { - numcpus++; - if (numcpus == 1) { - char *p = strchr(line, ':') + 2; - strcpy(model, p); - model[strlen(model)-1] = 0; - } - } else if (strncmp(line, "cpu MHz", 7) == 0) { - if (numcpus == 1) { - sscanf(line, "%*s %*s : %u", &cpuspeed); - } - } - } - fclose(fpModel); - } - - *cpus = Array::New(numcpus); - - if (fpStat) { - while (fgets(line, 511, fpStat) != NULL) { - if (strncmp(line, "cpu ", 4) == 0) { - continue; - } else if (strncmp(line, "cpu", 3) != 0) { - break; - } - - sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu", - &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr); - snprintf(speedPath, sizeof(speedPath), - "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i); - - fpSpeed = fopen(speedPath, "r"); - - if (fpSpeed) { - if (fgets(line, 511, fpSpeed) != NULL) { - sscanf(line, "%u", &cpuspeed); - cpuspeed /= 1000; - } - fclose(fpSpeed); - } - - cpuinfo = Object::New(); - cputimes = Object::New(); - cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier)); - cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier)); - cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier)); - cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier)); - cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier)); - - cpuinfo->Set(String::New("model"), String::New(model)); - cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); - - cpuinfo->Set(String::New("times"), cputimes); - (*cpus)->Set(i++, cpuinfo); - } - fclose(fpStat); - } - - return 0; -} - -double Platform::GetUptimeImpl() { -#ifdef CLOCK_MONOTONIC - struct timespec now; - if (0 == clock_gettime(CLOCK_MONOTONIC, &now)) { - double uptime = now.tv_sec; - uptime += (double)now.tv_nsec / 1000000000.0; - return uptime; - } - return -1; -#else - struct sysinfo info; - if (sysinfo(&info) < 0) { - return -1; - } - return static_cast<double>(info.uptime); -#endif -} - - -bool IsInternal(struct ifaddrs* addr) { - return addr->ifa_flags & IFF_UP && - addr->ifa_flags & IFF_RUNNING && - addr->ifa_flags & IFF_LOOPBACK; -} - - -Handle<Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - struct ::ifaddrs *addrs, *ent; - struct ::sockaddr_in *in4; - struct ::sockaddr_in6 *in6; - char ip[INET6_ADDRSTRLEN]; - Local<Object> ret, o; - Local<String> name, ipaddr, family; - Local<Array> ifarr; - - if (getifaddrs(&addrs) != 0) { - return ThrowException(ErrnoException(errno, "getifaddrs")); - } - - ret = Object::New(); - - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - bzero(&ip, sizeof (ip)); - if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { - continue; - } - - if (ent->ifa_addr == NULL) { - continue; - } - - /* - * On Linux getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information. - */ - if (ent->ifa_addr->sa_family == PF_PACKET) - continue; - - name = String::New(ent->ifa_name); - if (ret->Has(name)) { - ifarr = Local<Array>::Cast(ret->Get(name)); - } else { - ifarr = Array::New(); - ret->Set(name, ifarr); - } - - if (ent->ifa_addr->sa_family == AF_INET6) { - in6 = (struct sockaddr_in6 *)ent->ifa_addr; - inet_ntop(AF_INET6, &(in6->sin6_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv6"); - } else if (ent->ifa_addr->sa_family == AF_INET) { - in4 = (struct sockaddr_in *)ent->ifa_addr; - inet_ntop(AF_INET, &(in4->sin_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv4"); - } else { - (void) strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN); - family = String::New("<unknown>"); - } - - o = Object::New(); - o->Set(String::New("address"), String::New(ip)); - o->Set(String::New("family"), family); - o->Set(String::New("internal"), ent->ifa_flags & IFF_LOOPBACK ? - True() : False()); - - ifarr->Set(ifarr->Length(), o); - - } - - freeifaddrs(addrs); - - return scope.Close(ret); -} - - -} // namespace node diff --git a/src/platform_none.cc b/src/platform_none.cc deleted file mode 100644 index ea6125719b..0000000000 --- a/src/platform_none.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - - -namespace node { - - -char** OS::SetupArgs(int argc, char *argv[]) { - return argv; -} - - -void OS::SetProcessTitle(char *title) { - ; -} - - -const char* OS::GetProcessTitle(int *len) { - *len = 0; - return NULL; -} - - -int OS::GetMemory(size_t *rss) { - // Not implemented - *rss = 0; - return 0; -} - - -} // namespace node diff --git a/src/platform_openbsd.cc b/src/platform_openbsd.cc deleted file mode 100644 index 9399ad7acd..0000000000 --- a/src/platform_openbsd.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - -#include <v8.h> - -#include <stdlib.h> -#include <kvm.h> -#include <sys/param.h> -#include <sys/sysctl.h> -#include <sys/user.h> -#include <sys/dkstat.h> -#include <uvm/uvm_param.h> -#include <string.h> -#include <paths.h> -#include <fcntl.h> -#include <unistd.h> -#include <time.h> - -#include <stdio.h> -namespace node { - -using namespace v8; - -static char *process_title; -double Platform::prog_start_time = Platform::GetUptime(); - -char** Platform::SetupArgs(int argc, char *argv[]) { - process_title = argc ? strdup(argv[0]) : NULL; - return argv; -} - - -void Platform::SetProcessTitle(char *title) { - if (process_title) free(process_title); - process_title = strdup(title); - setproctitle(title); -} - -const char* Platform::GetProcessTitle(int *len) { - if (process_title) { - *len = strlen(process_title); - return process_title; - } - *len = 0; - return NULL; -} - -int Platform::GetMemory(size_t *rss) { - kvm_t *kd = NULL; - struct kinfo_proc2 *kinfo = NULL; - pid_t pid; - int nprocs, max_size = sizeof(struct kinfo_proc2); - size_t page_size = getpagesize(); - - pid = getpid(); - - kd = kvm_open(NULL, _PATH_MEM, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) goto error; - - kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); - if (kinfo == NULL) goto error; - - *rss = kinfo->p_vm_rssize * page_size; - - kvm_close(kd); - - return 0; - -error: - if (kd) kvm_close(kd); - return -1; -} - - -int Platform::GetCPUInfo(Local<Array> *cpus) { - Local<Object> cpuinfo; - Local<Object> cputimes; - unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), - multiplier = ((uint64_t)1000L / ticks), cpuspeed; - uint64_t info[CPUSTATES]; - char model[512]; - int numcpus = 1; - static int which[] = {CTL_HW, HW_MODEL, NULL}; - size_t size; - - size = sizeof(model); - if (sysctl(which, 2, &model, &size, NULL, 0) < 0) { - return -1; - } - which[1] = HW_NCPU; - size = sizeof(numcpus); - if (sysctl(which, 2, &numcpus, &size, NULL, 0) < 0) { - return -1; - } - - *cpus = Array::New(numcpus); - - which[1] = HW_CPUSPEED; - size = sizeof(cpuspeed); - if (sysctl(which, 2, &cpuspeed, &size, NULL, 0) < 0) { - return -1; - } - size = sizeof(info); - which[0] = CTL_KERN; - which[1] = KERN_CPTIME2; - for (int i = 0; i < numcpus; i++) { - which[2] = i; - size = sizeof(info); - if (sysctl(which, 3, &info, &size, NULL, 0) < 0) { - return -1; - } - cpuinfo = Object::New(); - cputimes = Object::New(); - cputimes->Set(String::New("user"), - Number::New((uint64_t)(info[CP_USER]) * multiplier)); - cputimes->Set(String::New("nice"), - Number::New((uint64_t)(info[CP_NICE]) * multiplier)); - cputimes->Set(String::New("sys"), - Number::New((uint64_t)(info[CP_SYS]) * multiplier)); - cputimes->Set(String::New("idle"), - Number::New((uint64_t)(info[CP_IDLE]) * multiplier)); - cputimes->Set(String::New("irq"), - Number::New((uint64_t)(info[CP_INTR]) * multiplier)); - - cpuinfo->Set(String::New("model"), String::New(model)); - cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); - - cpuinfo->Set(String::New("times"), cputimes); - (*cpus)->Set(i, cpuinfo); - } - return 0; -} - -double Platform::GetUptimeImpl() { - time_t now; - struct timeval info; - size_t size = sizeof(info); - static int which[] = {CTL_KERN, KERN_BOOTTIME}; - - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { - return -1; - } - now = time(NULL); - - return static_cast<double>(now - info.tv_sec); -} - - -Handle<Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - return scope.Close(Object::New()); -} - - -} // namespace node diff --git a/src/platform_sunos.cc b/src/platform_sunos.cc deleted file mode 100644 index 949e4e13f9..0000000000 --- a/src/platform_sunos.cc +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "node.h" -#include "platform.h" - - -#include <unistd.h> /* getpagesize() */ -#include <stdlib.h> /* getexecname() */ -#include <strings.h> /* strncpy() */ - -#include <kstat.h> -#include <errno.h> -#include <inttypes.h> -#include <sys/types.h> -#include <sys/loadavg.h> -#include <sys/socket.h> -#include <net/if.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#ifdef SUNOS_HAVE_IFADDRS -# include <ifaddrs.h> -#endif - - - -#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64) -#define PROCFS_FILE_OFFSET_BITS_HACK 1 -#undef _FILE_OFFSET_BITS -#else -#define PROCFS_FILE_OFFSET_BITS_HACK 0 -#endif - -#include <procfs.h> - -#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1) -#define _FILE_OFFSET_BITS 64 -#endif - - -namespace node { - -using namespace v8; - -double Platform::prog_start_time = Platform::GetUptime(); - -char** Platform::SetupArgs(int argc, char *argv[]) { - return argv; -} - - -void Platform::SetProcessTitle(char *title) { - ; -} - - -const char* Platform::GetProcessTitle(int *len) { - *len = 0; - return NULL; -} - - -int Platform::GetMemory(size_t *rss) { - pid_t pid = getpid(); - - char pidpath[1024]; - sprintf(pidpath, "/proc/%d/psinfo", pid); - - psinfo_t psinfo; - FILE *f = fopen(pidpath, "r"); - if (!f) return -1; - - if (fread(&psinfo, sizeof(psinfo_t), 1, f) != 1) { - fclose (f); - return -1; - } - - /* XXX correct? */ - - *rss = (size_t) psinfo.pr_rssize * 1024; - - fclose (f); - - return 0; -} - - -static Handle<Value> data_named(kstat_named_t *knp) { - Handle<Value> val; - - switch (knp->data_type) { - case KSTAT_DATA_CHAR: - val = Number::New(knp->value.c[0]); - break; - case KSTAT_DATA_INT32: - val = Number::New(knp->value.i32); - break; - case KSTAT_DATA_UINT32: - val = Number::New(knp->value.ui32); - break; - case KSTAT_DATA_INT64: - val = Number::New(knp->value.i64); - break; - case KSTAT_DATA_UINT64: - val = Number::New(knp->value.ui64); - break; - case KSTAT_DATA_STRING: - val = String::New(KSTAT_NAMED_STR_PTR(knp)); - break; - default: - val = String::New("unrecognized data type"); - } - - return (val); -} - - -int Platform::GetCPUInfo(Local<Array> *cpus) { - HandleScope scope; - Local<Object> cpuinfo; - Local<Object> cputimes; - - int lookup_instance; - kstat_ctl_t *kc; - kstat_t *ksp; - kstat_named_t *knp; - - if ((kc = kstat_open()) == NULL) - return -1; - - *cpus = Array::New(); - - lookup_instance = 0; - while (ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL)){ - cpuinfo = Object::New(); - - if (kstat_read(kc, ksp, NULL) == -1) { - /* - * It is deeply annoying, but some kstats can return errors - * under otherwise routine conditions. (ACPI is one - * offender; there are surely others.) To prevent these - * fouled kstats from completely ruining our day, we assign - * an "error" member to the return value that consists of - * the strerror(). - */ - cpuinfo->Set(String::New("error"), String::New(strerror(errno))); - (*cpus)->Set(lookup_instance, cpuinfo); - } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz"); - cpuinfo->Set(String::New("speed"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand"); - cpuinfo->Set(String::New("model"), data_named(knp)); - (*cpus)->Set(lookup_instance, cpuinfo); - } - - lookup_instance++; - } - - lookup_instance = 0; - while (ksp = kstat_lookup(kc, (char *)"cpu", lookup_instance, (char *)"sys")){ - cpuinfo = (*cpus)->Get(lookup_instance)->ToObject(); - cputimes = Object::New(); - - if (kstat_read(kc, ksp, NULL) == -1) { - cputimes->Set(String::New("error"), String::New(strerror(errno))); - cpuinfo->Set(String::New("times"), cpuinfo); - } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_kernel"); - cputimes->Set(String::New("system"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_user"); - cputimes->Set(String::New("user"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_idle"); - cputimes->Set(String::New("idle"), data_named(knp)); - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"intr"); - cputimes->Set(String::New("irq"), data_named(knp)); - - cpuinfo->Set(String::New("times"), cputimes); - } - - lookup_instance++; - } - - kstat_close(kc); - - return 0; -} - - -double Platform::GetUptimeImpl() { - kstat_ctl_t *kc; - kstat_t *ksp; - kstat_named_t *knp; - - long hz = sysconf(_SC_CLK_TCK); - double clk_intr; - - if ((kc = kstat_open()) == NULL) - return -1; - - ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); - - if (kstat_read(kc, ksp, NULL) == -1) { - clk_intr = -1; - } else { - knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clk_intr"); - clk_intr = knp->value.ul / hz; - } - - kstat_close(kc); - - return clk_intr; -} - - -Handle<Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - -#ifndef SUNOS_HAVE_IFADDRS - return ThrowException(Exception::Error(String::New( - "This version of sunos doesn't support getifaddrs"))); -#else - struct ::ifaddrs *addrs, *ent; - struct ::sockaddr_in *in4; - struct ::sockaddr_in6 *in6; - char ip[INET6_ADDRSTRLEN]; - Local<Object> ret, o; - Local<String> name, ipaddr, family; - Local<Array> ifarr; - - if (getifaddrs(&addrs) != 0) { - return ThrowException(ErrnoException(errno, "getifaddrs")); - } - - ret = Object::New(); - - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - bzero(&ip, sizeof (ip)); - if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { - continue; - } - - if (ent->ifa_addr == NULL) { - continue; - } - - name = String::New(ent->ifa_name); - if (ret->Has(name)) { - ifarr = Local<Array>::Cast(ret->Get(name)); - } else { - ifarr = Array::New(); - ret->Set(name, ifarr); - } - - if (ent->ifa_addr->sa_family == AF_INET6) { - in6 = (struct sockaddr_in6 *)ent->ifa_addr; - inet_ntop(AF_INET6, &(in6->sin6_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv6"); - } else if (ent->ifa_addr->sa_family == AF_INET) { - in4 = (struct sockaddr_in *)ent->ifa_addr; - inet_ntop(AF_INET, &(in4->sin_addr), ip, INET6_ADDRSTRLEN); - family = String::New("IPv4"); - } else { - (void) strlcpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN); - family = String::New("<unknown>"); - } - - o = Object::New(); - o->Set(String::New("address"), String::New(ip)); - o->Set(String::New("family"), family); - o->Set(String::New("internal"), ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags & - IFF_LOOPBACK ? True() : False()); - - ifarr->Set(ifarr->Length(), o); - - } - - freeifaddrs(addrs); - - return scope.Close(ret); - -#endif // SUNOS_HAVE_IFADDRS -} - - -} // namespace node - diff --git a/src/platform_win32.cc b/src/platform_win32.cc deleted file mode 100644 index c84131a701..0000000000 --- a/src/platform_win32.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include <node.h> -#include "platform.h" - -#include <v8.h> - -#include <errno.h> -#include <stdlib.h> -#if defined(__MINGW32__) -#include <sys/param.h> // for MAXPATHLEN -#include <unistd.h> // getpagesize -#endif - -#include <platform_win32.h> -#include <psapi.h> - -namespace node { - -using namespace v8; - -static char *process_title = NULL; -double Platform::prog_start_time = Platform::GetUptime(); - - -// Does the about the same as strerror(), -// but supports all windows errror messages -const char *winapi_strerror(const int errorno) { - char *errmsg = NULL; - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast<LPSTR>(&errmsg), 0, NULL); - - if (errmsg) { - // Remove trailing newlines - for (int i = strlen(errmsg) - 1; - i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) { - errmsg[i] = '\0'; - } - - return errmsg; - } else { - // FormatMessage failed - return "Unknown error"; - } -} - - -// Does the about the same as perror(), but for windows api functions -void winapi_perror(const char* prefix = NULL) { - DWORD errorno = GetLastError(); - const char *errmsg = NULL; - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast<LPSTR>(&errmsg), 0, NULL); - - if (!errmsg) { - errmsg = "Unknown error\n"; - } - - // FormatMessage messages include a newline character - - if (prefix) { - fprintf(stderr, "%s: %s", prefix, errmsg); - } else { - fputs(errmsg, stderr); - } -} - - -char** Platform::SetupArgs(int argc, char *argv[]) { - return argv; -} - - -// Max title length; the only thing MSDN tells us about the maximum length -// of the console title is that it is smaller than 64K. However in practice -// it is much smaller, and there is no way to figure out what the exact length -// of the title is or can be, at least not on XP. To make it even more -// annoying, GetConsoleTitle failes when the buffer to be read into is bigger -// than the actual maximum length. So we make a conservative guess here; -// just don't put the novel you're writing in the title, unless the plot -// survives truncation. -#define MAX_TITLE_LENGTH 8192 - -void Platform::SetProcessTitle(char *title) { - // We need to convert _title_ to UTF-16 first, because that's what windows uses internally. - // It would be more efficient to use the UTF-16 value that we can obtain from v8, - // but it's not accessible from here. - - int length; - WCHAR *title_w; - - // Find out how big the buffer for the wide-char title must be - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); - if (!length) { - winapi_perror("MultiByteToWideChar"); - return; - } - - // Convert to wide-char string - title_w = new WCHAR[length]; - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length); - if (!length) { - winapi_perror("MultiByteToWideChar"); - delete[] title_w; - return; - }; - - // If the title must be truncated insert a \0 terminator there - if (length > MAX_TITLE_LENGTH) { - title_w[MAX_TITLE_LENGTH - 1] = *L"\0"; - } - - if (!SetConsoleTitleW(title_w)) { - winapi_perror("SetConsoleTitleW"); - } - - free(process_title); - process_title = strdup(title); - - delete[] title_w; -} - - -static inline char* _getProcessTitle() { - WCHAR title_w[MAX_TITLE_LENGTH]; - char *title; - int result, length; - - result = GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR)); - - if (result == 0) { - winapi_perror("GetConsoleTitleW"); - return NULL; - } - - // Find out what the size of the buffer is that we need - length = WideCharToMultiByte(CP_UTF8, 0, title_w, -1, NULL, 0, NULL, NULL); - if (!length) { - winapi_perror("WideCharToMultiByte"); - return NULL; - } - - title = (char *) malloc(length); - if (!title) { - perror("malloc"); - return NULL; - } - - // Do utf16 -> utf8 conversion here - if (!WideCharToMultiByte(CP_UTF8, 0, title_w, -1, title, length, NULL, NULL)) { - winapi_perror("WideCharToMultiByte"); - free(title); - return NULL; - } - - return title; -} - - -const char* Platform::GetProcessTitle(int *len) { - // If the process_title was never read before nor explicitly set, - // we must query it with getConsoleTitleW - if (!process_title) { - process_title = _getProcessTitle(); - } - - if (process_title) { - *len = strlen(process_title); - return process_title; - } else { - *len = 0; - return NULL; - } -} - - -int Platform::GetMemory(size_t *rss) { - - HANDLE current_process = GetCurrentProcess(); - PROCESS_MEMORY_COUNTERS pmc; - - if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { - winapi_perror("GetProcessMemoryInfo"); - } - - *rss = pmc.WorkingSetSize; - - return 0; -} - -int Platform::GetCPUInfo(Local<Array> *cpus) { - - HandleScope scope; - *cpus = Array::New(); - - for (int i = 0; i < 32; i++) { - - wchar_t key[128] = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\"; - wchar_t processor_number[32]; - _itow(i, processor_number, 10); - wcsncat(key, processor_number, 2); - - HKEY processor_key = NULL; - - DWORD cpu_speed = 0; - DWORD cpu_speed_length = sizeof(cpu_speed); - - wchar_t cpu_brand[256]; - DWORD cpu_brand_length = sizeof(cpu_brand); - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, - key, - 0, - KEY_QUERY_VALUE, - &processor_key) != ERROR_SUCCESS) { - if (i == 0) { - winapi_perror("RegOpenKeyExW"); - return -1; - } - - continue; - } - - if (RegQueryValueExW(processor_key, - L"~MHz", - NULL, - NULL, - reinterpret_cast<LPBYTE>(&cpu_speed), - &cpu_speed_length) != ERROR_SUCCESS) { - winapi_perror("RegQueryValueExW"); - return -1; - } - - if (RegQueryValueExW(processor_key, - L"ProcessorNameString", - NULL, - NULL, - reinterpret_cast<LPBYTE>(&cpu_brand), - &cpu_brand_length) != ERROR_SUCCESS) { - winapi_perror("RegQueryValueExW"); - return -1; - } - - RegCloseKey(processor_key); - - Local<Object> times_info = Object::New(); // FIXME - find times on windows - times_info->Set(String::New("user"), Integer::New(0)); - times_info->Set(String::New("nice"), Integer::New(0)); - times_info->Set(String::New("sys"), Integer::New(0)); - times_info->Set(String::New("idle"), Integer::New(0)); - times_info->Set(String::New("irq"), Integer::New(0)); - - Local<Object> cpu_info = Object::New(); - cpu_info->Set(String::New("model"), - String::New(reinterpret_cast<uint16_t*>(cpu_brand))); - cpu_info->Set(String::New("speed"), Integer::New(cpu_speed)); - cpu_info->Set(String::New("times"), times_info); - (*cpus)->Set(i,cpu_info); - } - - return 0; -} - - -double Platform::GetUptimeImpl() { - return (double)GetTickCount()/1000.0; -} - -Handle<Value> Platform::GetInterfaceAddresses() { - HandleScope scope; - return scope.Close(Object::New()); -} - - -} // namespace node diff --git a/src/platform_win32.h b/src/platform_win32.h deleted file mode 100644 index 0a6a5d5bfe..0000000000 --- a/src/platform_win32.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-#ifndef NODE_PLATFORM_WIN32_H_
-#define NODE_PLATFORM_WIN32_H_
-
-// Require at least Windows XP SP1
-// (GetProcessId requires it)
-#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0501
-#endif
-
-#ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN // implies NOCRYPT and NOGDI.
-#endif
-
-#ifndef NOMINMAX
-# define NOMINMAX
-#endif
-
-#ifndef NOKERNEL
-# define NOKERNEL
-#endif
-
-#ifndef NOUSER
-# define NOUSER
-#endif
-
-#ifndef NOSERVICE
-# define NOSERVICE
-#endif
-
-#ifndef NOSOUND
-# define NOSOUND
-#endif
-
-#ifndef NOMCX
-# define NOMCX
-#endif
-
-#include <windows.h>
-#include <winsock2.h>
-
-#if defined(_MSC_VER)
-#define STDIN_FILENO 0
-#define STDOUT_FILENO 1
-#define STDERR_FILENO 2
-#endif
-
-namespace node {
-
-#define NO_IMPL_MSG(...) \
- fprintf(stderr, "Not implemented: %s\n", #__VA_ARGS__);
-
-const char *winapi_strerror(const int errorno);
-void winapi_perror(const char* prefix);
-
-}
-
-#endif // NODE_PLATFORM_WIN32_H_
-
diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 07d8d3143c..c99ecbafe9 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -245,14 +245,14 @@ Handle<Value> UDPWrap::SetMembership(const Arguments& args, assert(args.Length() == 2); String::Utf8Value address(args[0]->ToString()); - String::Utf8Value interface(args[1]->ToString()); + String::Utf8Value iface(args[1]->ToString()); - const char* interface_cstr = *interface; + const char* iface_cstr = *iface; if (args[1]->IsUndefined() || args[1]->IsNull()) { - interface_cstr = NULL; + iface_cstr = NULL; } - int r = uv_udp_set_membership(&wrap->handle_, *address, interface_cstr, + int r = uv_udp_set_membership(&wrap->handle_, *address, iface_cstr, membership); if (r) diff --git a/src/v8_typed_array.cc b/src/v8_typed_array.cc index 4b80da0c87..9941845fc5 100644 --- a/src/v8_typed_array.cc +++ b/src/v8_typed_array.cc @@ -25,6 +25,7 @@ #include <v8.h> #include "v8_typed_array.h" +#include "node_buffer.h" namespace { @@ -152,6 +153,7 @@ class TypedArray { v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache); static BatchedMethods methods[] = { + { "get", &TypedArray<TBytes, TEAType>::get }, { "set", &TypedArray<TBytes, TEAType>::set }, { "slice", &TypedArray<TBytes, TEAType>::subarray }, { "subarray", &TypedArray<TBytes, TEAType>::subarray }, @@ -184,14 +186,16 @@ class TypedArray { unsigned int length = 0; unsigned int byte_offset = 0; - if (ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor. + // [m1k3] added support for Buffer constructor + if (node::Buffer::HasInstance(args[0]) + || ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor. buffer = v8::Local<v8::Object>::Cast(args[0]); unsigned int buflen = buffer->GetIndexedPropertiesExternalArrayDataLength(); - if (args[1]->Int32Value() < 0) + if (!args[1]->IsUndefined() && args[1]->Int32Value() < 0) return ThrowRangeError("Byte offset out of range."); - byte_offset = args[1]->Uint32Value(); + byte_offset = args[1]->IsUndefined() ? 0 : args[1]->Uint32Value(); if (!checkAlignment(byte_offset, TBytes)) return ThrowRangeError("Byte offset is not aligned."); @@ -215,10 +219,11 @@ class TypedArray { } // TODO(deanm): Error check. - void* buf = buffer->GetPointerFromInternalField(0); + void* buf = buffer->GetIndexedPropertiesExternalArrayData(); args.This()->SetIndexedPropertiesToExternalArrayData( reinterpret_cast<char*>(buf) + byte_offset, TEAType, length); - } else if (args[0]->IsObject()) { // TypedArray / type[] constructor. + } + else if (args[0]->IsObject()) { // TypedArray / type[] constructor. v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(args[0]); length = obj->Get(v8::String::New("length"))->Uint32Value(); @@ -278,68 +283,121 @@ class TypedArray { return args.This(); } + static v8::Handle<v8::Value> get(const v8::Arguments& args) { + if (args.Length() < 1) + return ThrowError("Wrong number of arguments."); + + if (args[0]->IsNumber()) { + unsigned int index = args[0]->Uint32Value(); + void* ptr = args.This()->GetIndexedPropertiesExternalArrayData(); + + if (TEAType == v8::kExternalByteArray) + return v8::Integer::New(reinterpret_cast<char*>(ptr)[index]); + else if (TEAType == v8::kExternalUnsignedByteArray) + return v8::Integer::New(reinterpret_cast<unsigned char*>(ptr)[index]); + else if (TEAType == v8::kExternalShortArray) + return v8::Integer::New(reinterpret_cast<short*>(ptr)[index]); + else if (TEAType == v8::kExternalUnsignedShortArray) + return v8::Integer::New(reinterpret_cast<unsigned short*>(ptr)[index]); + else if (TEAType == v8::kExternalIntArray) + return v8::Integer::New(reinterpret_cast<int*>(ptr)[index]); + else if (TEAType == v8::kExternalUnsignedIntArray) + return v8::Integer::New(reinterpret_cast<unsigned int*>(ptr)[index]); + else if (TEAType == v8::kExternalFloatArray) + return v8::Number::New(reinterpret_cast<float*>(ptr)[index]); + else if (TEAType == v8::kExternalDoubleArray) + return v8::Number::New(reinterpret_cast<double*>(ptr)[index]); + } + return v8::Undefined(); + } + static v8::Handle<v8::Value> set(const v8::Arguments& args) { if (args.Length() < 1) return ThrowError("Wrong number of arguments."); - if (!args[0]->IsObject()) - return ThrowTypeError("Type error."); - - v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]); - - if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView. - v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast( - obj->Get(v8::String::New("buffer"))); - v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast( - args.This()->Get(v8::String::New("buffer"))); - - if (args[1]->Int32Value() < 0) - return ThrowRangeError("Offset may not be negative."); - - unsigned int offset = args[1]->Uint32Value(); - unsigned int src_length = - obj->Get(v8::String::New("length"))->Uint32Value(); - unsigned int dst_length = - args.This()->Get(v8::String::New("length"))->Uint32Value(); - if (offset > dst_length) - return ThrowRangeError("Offset out of range."); - - if (src_length > dst_length - offset) - return ThrowRangeError("Offset/length out of range."); - - // We don't want to get the buffer pointer, because that means we'll have - // to just do the calculations for byteOffset / byteLength again. - // Instead just use the pointer on the external array data. - void* src_ptr = obj->GetIndexedPropertiesExternalArrayData(); - void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData(); - - // From the spec: - // If the input array is a TypedArray, the two arrays may use the same - // underlying ArrayBuffer. In this situation, setting the values takes - // place as if all the data is first copied into a temporary buffer that - // does not overlap either of the arrays, and then the data from the - // temporary buffer is copied into the current array. - memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, - src_ptr, src_length * TBytes); - } else { // type[] - if (args[1]->Int32Value() < 0) - return ThrowRangeError("Offset may not be negative."); - - unsigned int src_length = - obj->Get(v8::String::New("length"))->Uint32Value(); - unsigned int dst_length = - args.This()->Get(v8::String::New("length"))->Uint32Value(); - unsigned int offset = args[1]->Uint32Value(); - - if (offset > dst_length) - return ThrowRangeError("Offset out of range."); - - if (src_length > dst_length - offset) - return ThrowRangeError("Offset/length out of range."); - - for (uint32_t i = 0; i < src_length; ++i) { - // Use the v8 setter to deal with typing. Maybe slow? - args.This()->Set(i + offset, obj->Get(i)); + //if (!args[0]->IsObject()) + // return ThrowTypeError("Type error."); + + if (args[0]->IsNumber()) { + // index, <type> value + unsigned int index = args[0]->Uint32Value(); + void* ptr = args.This()->GetIndexedPropertiesExternalArrayData(); + if (TEAType == v8::kExternalByteArray) + reinterpret_cast<char*>(ptr)[index] = (char) args[1]->Int32Value(); + else if (TEAType == v8::kExternalUnsignedByteArray) + reinterpret_cast<unsigned char*>(ptr)[index] = + (unsigned char) args[1]->Int32Value(); + else if (TEAType == v8::kExternalShortArray) + reinterpret_cast<short*>(ptr)[index] = (short) args[1]->Int32Value(); + else if (TEAType == v8::kExternalUnsignedShortArray) + reinterpret_cast<unsigned short*>(ptr)[index] = + (unsigned short) args[1]->Int32Value(); + else if (TEAType == v8::kExternalIntArray) + reinterpret_cast<int*>(ptr)[index] = (int) args[1]->Int32Value(); + else if (TEAType == v8::kExternalUnsignedIntArray) + reinterpret_cast<unsigned int*>(ptr)[index] = + (unsigned int) args[1]->Int32Value(); + else if (TEAType == v8::kExternalFloatArray) + reinterpret_cast<float*>(ptr)[index] = (float) args[1]->NumberValue(); + else if (TEAType == v8::kExternalDoubleArray) + reinterpret_cast<double*>(ptr)[index] = (double) args[1]->NumberValue(); + } else if (args[0]->IsObject()) { + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]); + + if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView. + v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast( + obj->Get(v8::String::New("buffer"))); + v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast( + args.This()->Get(v8::String::New("buffer"))); + + if (args[1]->Int32Value() < 0) + return ThrowRangeError("Offset may not be negative."); + + unsigned int offset = args[1]->Uint32Value(); + unsigned int src_length = + obj->Get(v8::String::New("length"))->Uint32Value(); + unsigned int dst_length = + args.This()->Get(v8::String::New("length"))->Uint32Value(); + if (offset > dst_length) + return ThrowRangeError("Offset out of range."); + + if (src_length > dst_length - offset) + return ThrowRangeError("Offset/length out of range."); + + // We don't want to get the buffer pointer, because that means we'll have + // to just do the calculations for byteOffset / byteLength again. + // Instead just use the pointer on the external array data. + void* src_ptr = obj->GetIndexedPropertiesExternalArrayData(); + void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData(); + + // From the spec: + // If the input array is a TypedArray, the two arrays may use the same + // underlying ArrayBuffer. In this situation, setting the values takes + // place as if all the data is first copied into a temporary buffer that + // does not overlap either of the arrays, and then the data from the + // temporary buffer is copied into the current array. + memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, src_ptr, + src_length * TBytes); + } else { // type[] + if (args[1]->Int32Value() < 0) + return ThrowRangeError("Offset may not be negative."); + + unsigned int src_length = + obj->Get(v8::String::New("length"))->Uint32Value(); + unsigned int dst_length = + args.This()->Get(v8::String::New("length"))->Uint32Value(); + unsigned int offset = args[1]->Uint32Value(); + + if (offset > dst_length) + return ThrowRangeError("Offset out of range."); + + if (src_length > dst_length - offset) + return ThrowRangeError("Offset/length out of range."); + + for (uint32_t i = 0; i < src_length; ++i) { + // Use the v8 setter to deal with typing. Maybe slow? + args.This()->Set(i + offset, obj->Get(i)); + } } } @@ -560,7 +618,8 @@ class DataView { unsigned int byte_length = buffer->GetIndexedPropertiesExternalArrayDataLength(); - unsigned int byte_offset = args[1]->Uint32Value(); + unsigned int byte_offset = + args[1]->IsUndefined() ? 0 : args[1]->Uint32Value(); if (args[1]->Int32Value() < 0 || byte_offset >= byte_length) return ThrowRangeError("byteOffset out of range."); @@ -739,6 +798,8 @@ class DataView { namespace v8_typed_array { void AttachBindings(v8::Handle<v8::Object> obj) { + v8::HandleScope scope; + obj->Set(v8::String::New("ArrayBuffer"), ArrayBuffer::GetTemplate()->GetFunction()); obj->Set(v8::String::New("Int8Array"), @@ -781,3 +842,5 @@ int SizeOfArrayElementForType(v8::ExternalArrayType type) { } } // namespace v8_typed_array + +NODE_MODULE(node_typed_array, v8_typed_array::AttachBindings) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 6c4d05e2b1..0000000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# -# tests -# - -if(${CMAKE_BUILD_TYPE} MATCHES Debug) - set(test_bin_dir debug) - get_target_property(node_bin node DEBUG_LOCATION) -else() - set(test_bin_dir default) - get_target_property(node_bin node LOCATION) -endif() - -file(GLOB_RECURSE node_tests ${CMAKE_SOURCE_DIR}/test/*) - -# add all tests with add_test -foreach(test ${node_tests}) - if(test MATCHES ".*/test-[^./\ ]*.\\.js" - AND NOT test MATCHES ".*disabled.*") - - # build a fancy name for each test - string(REPLACE ${CMAKE_SOURCE_DIR}/test/ "" test_name ${test}) - string(REPLACE test- "" test_name ${test_name}) - string(REPLACE ".js" "" test_name ${test_name}) - string(REPLACE "/" "-" test_name ${test_name}) - - add_test(${test_name} ${node_bin} ${test}) - endif() -endforeach() - -# the CTest custom config makes ctest recreate the tmp directory before and after -# each run -configure_file(${CMAKE_SOURCE_DIR}/cmake/CTestCustom.cmake ${CMAKE_BINARY_DIR}/CTestCustom.cmake COPYONLY) - -add_custom_command( - TARGET node POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${test_bin_dir} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${node_bin} ${PROJECT_BINARY_DIR}/${test_bin_dir} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) - -# this target gets overriden by ctest's test target -# add_custom_target( -# test -# COMMAND python tools/test.py --mode=release simple message -# DEPENDS node -# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -# ) - -add_custom_target(test-all - COMMAND python tools/test.py --mode=debug,release - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-release - COMMAND python tools/test.py --mode=release - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-debug - COMMAND python tools/test.py --mode=debug - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-message - COMMAND python tools/test.py message - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-simple - COMMAND python tools/test.py simple - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-pummel - COMMAND python tools/test.py pummel - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - -add_custom_target(test-internet - COMMAND python tools/test.py internet - DEPENDS node - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) diff --git a/test/addons/.gitignore b/test/addons/.gitignore new file mode 100644 index 0000000000..1aaecf8156 --- /dev/null +++ b/test/addons/.gitignore @@ -0,0 +1,5 @@ +Makefile +*.Makefile +*.mk +gyp-mac-tool +/*/out diff --git a/test/addons/hello-world/binding.cc b/test/addons/hello-world/binding.cc new file mode 100644 index 0000000000..82e8c5583d --- /dev/null +++ b/test/addons/hello-world/binding.cc @@ -0,0 +1,17 @@ +#include <node.h> +#include <v8.h> + +using namespace v8; + +extern "C" { + void init(Handle<Object> target); +} + +Handle<Value> Method(const Arguments& args) { + HandleScope scope; + return scope.Close(String::New("world")); +} + +void init(Handle<Object> target) { + NODE_SET_METHOD(target, "hello", Method); +} diff --git a/test/addons/hello-world/binding.gyp b/test/addons/hello-world/binding.gyp new file mode 100644 index 0000000000..3bfb84493f --- /dev/null +++ b/test/addons/hello-world/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/test/addons/hello-world/test.js b/test/addons/hello-world/test.js new file mode 100644 index 0000000000..27550a3864 --- /dev/null +++ b/test/addons/hello-world/test.js @@ -0,0 +1,4 @@ +var assert = require('assert'); +var binding = require('./out/Release/binding'); +assert.equal('world', binding.hello()); +console.log('binding.hello() =', binding.hello()); diff --git a/test/simple/test-assert.js b/test/simple/test-assert.js index df8d810204..84c333b574 100644 --- a/test/simple/test-assert.js +++ b/test/simple/test-assert.js @@ -82,13 +82,30 @@ assert.throws(makeBlock(a.deepEqual, new Date(), new Date(2000, 3, 14)), 'deepEqual date'); // 7.3 +assert.doesNotThrow(makeBlock(a.deepEqual, /a/, /a/)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/g, /a/g)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/i, /a/i)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/m, /a/m)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/igm, /a/igm)); +assert.throws(makeBlock(a.deepEqual, /ab/, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/g, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/i, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/m, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/igm, /a/im)); + +var re1 = /a/; +re1.lastIndex = 3; +assert.throws(makeBlock(a.deepEqual, re1, /a/)); + + +// 7.4 assert.doesNotThrow(makeBlock(a.deepEqual, 4, '4'), 'deepEqual == check'); assert.doesNotThrow(makeBlock(a.deepEqual, true, 1), 'deepEqual == check'); assert.throws(makeBlock(a.deepEqual, 4, '5'), a.AssertionError, 'deepEqual == check'); -// 7.4 +// 7.5 // having the same number of owned properties && the same set of keys assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4}, {a: 4})); assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4, b: '2'}, {a: 4, b: '2'})); diff --git a/test/simple/test-child-process-disconnect.js b/test/simple/test-child-process-disconnect.js new file mode 100644 index 0000000000..e490c0848d --- /dev/null +++ b/test/simple/test-child-process-disconnect.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var common = require('../common'); +var fork = require('child_process').fork; +var net = require('net'); + +// child +if (process.argv[2] === 'child') { + + var server = net.createServer(); + + server.on('connection', function (socket) { + + process.on('disconnect', function () { + socket.end((process.connected).toString()); + }); + + // when the socket is closed, we will close the server + // allowing the process to self terminate + socket.on('end', function () { + server.close(); + }); + + socket.write('ready'); + }); + + // when the server is ready tell parent + server.on('listening', function () { + process.send('ready'); + }); + + server.listen(common.PORT); + +} else { + // testcase + var child = fork(process.argv[1], ['child']); + + var childFlag = false; + var childSelfTerminate = false; + var parentEmit = false; + var parentFlag = false; + + // when calling .disconnect the event should emit + // and the disconnected flag should be true. + child.on('disconnect', function () { + parentEmit = true; + parentFlag = child.connected; + }); + + // the process should also self terminate without using signals + child.on('exit', function () { + childSelfTerminate = true; + }); + + // when child is listning + child.on('message', function (msg) { + if (msg === 'ready') { + + // connect to child using TCP to know if disconnect was emitted + var socket = net.connect(common.PORT); + + socket.on('data', function (data) { + data = data.toString(); + + // ready to be disconnected + if (data === 'ready') { + child.disconnect(); + assert.throws(child.disconnect.bind(child), Error); + return; + } + + // disconnect is emitted + childFlag = (data === 'true'); + }); + + } + }); + + process.on('exit', function () { + assert.equal(childFlag, false); + assert.equal(parentFlag, false); + + assert.ok(childSelfTerminate); + assert.ok(parentEmit); + }); +} diff --git a/test/simple/test-child-process-internal.js b/test/simple/test-child-process-internal.js new file mode 100644 index 0000000000..dd7ce556b5 --- /dev/null +++ b/test/simple/test-child-process-internal.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +//messages +var PREFIX = 'NODE_'; +var normal = {cmd: 'foo' + PREFIX}; +var internal = {cmd: PREFIX + 'bar'}; + +if (process.argv[2] === 'child') { + //send non-internal message containing PREFIX at a non prefix position + process.send(normal); + + //send inernal message + process.send(internal); + + process.exit(0); + +} else { + + var fork = require('child_process').fork; + var child = fork(process.argv[1], ['child']); + + var gotNormal; + child.once('message', function(data) { + gotNormal = data; + }); + + var gotInternal; + child.once('internalMessage', function(data) { + gotInternal = data; + }); + + process.on('exit', function() { + assert.deepEqual(gotNormal, normal); + assert.deepEqual(gotInternal, internal); + }); +} diff --git a/test/simple/test-child-process-silent.js b/test/simple/test-child-process-silent.js new file mode 100644 index 0000000000..f438bf47e9 --- /dev/null +++ b/test/simple/test-child-process-silent.js @@ -0,0 +1,105 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var childProcess = require('child_process'); + +// Child pipe test +if (process.argv[2] === 'pipetest') { + process.stdout.write('stdout message'); + process.stderr.write('stderr message'); + +} else if (process.argv[2] === 'ipctest') { + // Child IPC test + process.send('message from child'); + process.on('message', function() { + process.send('got message from master'); + }); + +} else if (process.argv[2] === 'parent') { + // Parent | start child pipe test + + var child = childProcess.fork(process.argv[1], ['pipetest'], {silent: true}); + + // Allow child process to self terminate + child._channel.close(); + child._channel = null; + + child.on('exit', function() { + process.exit(0); + }); + +} else { + // testcase | start parent && child IPC test + + // testing: is stderr and stdout piped to parent + var parent = childProcess.spawn(process.execPath, [process.argv[1], 'parent']); + + //got any stderr or std data + var stdoutData = false; + parent.stdout.on('data', function() { + stdoutData = true; + }); + var stderrData = false; + parent.stdout.on('data', function() { + stderrData = true; + }); + + // testing: do message system work when using silent + var child = childProcess.fork(process.argv[1], ['ipctest'], {silent: true}); + + // Manual pipe so we will get errors + child.stderr.pipe(process.stderr, {end: false}); + child.stdout.pipe(process.stdout, {end: false}); + + var childSending = false; + var childReciveing = false; + child.on('message', function(message) { + if (childSending === false) { + childSending = (message === 'message from child'); + } + + if (childReciveing === false) { + childReciveing = (message === 'got message from master'); + } + + if (childReciveing === true) { + child.kill(); + } + }); + child.send('message to child'); + + // Check all values + process.on('exit', function() { + // clean up + child.kill(); + parent.kill(); + + // Check std(out|err) pipes + assert.ok(!stdoutData, 'The stdout socket was piped to parent'); + assert.ok(!stderrData, 'The stderr socket was piped to parent'); + + // Check message system + assert.ok(childSending, 'The child was able to send a message'); + assert.ok(childReciveing, 'The child was able to receive a message'); + }); +} diff --git a/test/simple/test-cluster-basic.js b/test/simple/test-cluster-basic.js new file mode 100644 index 0000000000..adc7775206 --- /dev/null +++ b/test/simple/test-cluster-basic.js @@ -0,0 +1,161 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); + +function forEach(obj, fn) { + Object.keys(obj).forEach(function(name, index) { + fn(obj[name], name, index); + }); +} + + +if (cluster.isWorker) { + var http = require('http'); + http.Server(function() { + + }).listen(common.PORT, '127.0.0.1'); +} + +else if (cluster.isMaster) { + + assert.equal('NODE_UNIQUE_ID' in process.env, false, + 'cluster.isMaster should not be true when NODE_UNIQUE_ID is set'); + + var checks = { + cluster: { + events: { + fork: false, + online: false, + listening: false, + death: false + }, + equal: { + fork: false, + online: false, + listening: false, + death: false + } + }, + + worker: { + events: { + online: false, + listening: false, + death: false + }, + equal: { + online: false, + listening: false, + death: false + }, + states: { + none: false, + online: false, + listening: false, + dead: false + } + } + }; + + var worker; + var stateNames = Object.keys(checks.worker.states); + + //Check events, states, and emit arguments + forEach(checks.cluster.events, function(bool, name, index) { + + //Listen on event + cluster.on(name, function(/* worker */) { + + //Set event + checks.cluster.events[name] = true; + + //Check argument + checks.cluster.equal[name] = worker === arguments[0]; + + //Check state + var state = stateNames[index]; + checks.worker.states[state] = (state === worker.state); + }); + }); + + //Kill worker when listening + cluster.on('listening', function() { + worker.destroy(); + }); + + //Kill process when worker is killed + cluster.on('death', function() { + process.exit(0); + }); + + //Create worker + worker = cluster.fork(); + assert.ok(worker instanceof cluster.Worker, + 'the worker is not a instance of the Worker constructor'); + + //Check event + forEach(checks.worker.events, function(bool, name, index) { + worker.on(name, function() { + //Set event + checks.worker.events[name] = true; + + //Check argument + checks.worker.equal[name] = worker === arguments[0]; + }); + }); + + //Check all values + process.once('exit', function() { + //Check cluster events + forEach(checks.cluster.events, function(check, name) { + assert.ok(check, 'The cluster event "' + name + '" on the cluster ' + + 'object did not fire'); + }); + + //Check cluster event arguments + forEach(checks.cluster.equal, function(check, name) { + assert.ok(check, 'The cluster event "' + name + '" did not emit ' + + 'with corrent argument'); + }); + + //Check worker states + forEach(checks.worker.states, function(check, name) { + assert.ok(check, 'The worker state "' + name + '" was not set to true'); + }); + + //Check worker events + forEach(checks.worker.events, function(check, name) { + assert.ok(check, 'The worker event "' + name + '" on the worker object ' + + 'did not fire'); + }); + + //Check worker event arguments + forEach(checks.worker.equal, function(check, name) { + assert.ok(check, 'The worker event "' + name + '" did not emit with ' + + 'corrent argument'); + }); + }); + +} diff --git a/test/simple/test-cluster-fork-env.js b/test/simple/test-cluster-fork-env.js new file mode 100644 index 0000000000..a0b50aebdc --- /dev/null +++ b/test/simple/test-cluster-fork-env.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); + +if (cluster.isWorker) { + cluster.worker.send({ + prop: process.env['cluster_test_prop'], + overwrite: process.env['cluster_test_overwrite'] + }); + +} else if (cluster.isMaster) { + + var checks = { + using: false, + overwrite: false + }; + + // To check that the cluster extend on the process.env we will overwrite a + // property + process.env['cluster_test_overwrite'] = 'old'; + + // Fork worker + var worker = cluster.fork({ + 'cluster_test_prop': 'custom', + 'cluster_test_overwrite': 'new' + }); + + // Checks worker env + worker.on('message', function(data) { + checks.using = (data.prop === 'custom'); + checks.overwrite = (data.overwrite === 'new'); + process.exit(0); + }); + + process.once('exit', function() { + assert.ok(checks.using, 'The worker did not receive the correct env.'); + assert.ok(checks.overwrite, 'The custom environment did not overwrite ' + + 'the existing environment.'); + }); + +} diff --git a/test/simple/test-cluster-kill-workers.js b/test/simple/test-cluster-kill-workers.js deleted file mode 100644 index 39846fab03..0000000000 --- a/test/simple/test-cluster-kill-workers.js +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// This test checks that if we kill a cluster master immediately after fork, -// before the worker has time to register itself, that the master will still -// clean up the worker. -// https://github.com/joyent/node/issues/2047 - -var common = require('../common'); -var assert = require('assert'); -var cluster = require('cluster'); -var fork = require('child_process').fork; - -var isTestRunner = process.argv[2] != 'child'; - -if (isTestRunner) { - console.log('starting master...'); - var master = fork(__filename, ['child']); - - console.log('master pid =', master.pid); - - var workerPID; - - master.on('message', function(m) { - console.log('got message from master:', m); - if (m.workerPID) { - console.log('worker pid =', m.workerPID); - workerPID = m.workerPID; - } - }); - - var gotExit = false; - var gotKillException = false; - - master.on('exit', function(code) { - gotExit = true; - assert(code != 0); - assert(workerPID > 0); - try { - process.kill(workerPID, 0); - } catch (e) { - // workerPID is no longer running - console.log(e); - assert(e.code == 'ESRCH'); - gotKillException = true; - } - }); - - process.on('exit', function() { - assert(gotExit); - assert(gotKillException); - }); -} else { - // Cluster stuff. - if (cluster.isMaster) { - var worker = cluster.fork(); - process.send({ workerPID: worker.pid }); - // should kill the worker too - throw new Error('kill master'); - } else { - setTimeout(function() { - assert(false, 'worker should have been killed'); - }, 2500); - } -} - diff --git a/test/simple/test-cluster-master-error.js b/test/simple/test-cluster-master-error.js new file mode 100644 index 0000000000..4add0eba13 --- /dev/null +++ b/test/simple/test-cluster-master-error.js @@ -0,0 +1,132 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); + +// Cluster setup +if (cluster.isWorker) { + var http = require('http'); + http.Server(function() { + + }).listen(common.PORT, '127.0.0.1'); + +} else if (process.argv[2] === 'cluster') { + + var totalWorkers = 2; + + // Send PID to testcase process + var forkNum = 0; + cluster.on('fork', function forkEvent(worker) { + + // Send PID + process.send({ + cmd: 'worker', + workerPID: worker.process.pid + }); + + // Stop listening when done + if (++forkNum === totalWorkers) { + cluster.removeListener('fork', forkEvent); + } + }); + + // Throw accidently error when all workers are listening + var listeningNum = 0; + cluster.on('listening', function listeningEvent() { + + // When all workers are listening + if (++listeningNum === totalWorkers) { + // Stop listening + cluster.removeListener('listening', listeningEvent); + + // throw accidently error + process.nextTick(function() { + throw 'accidently error'; + }); + } + + }); + + // Startup a basic cluster + cluster.fork(); + cluster.fork(); + +} else { + // This is the testcase + + var fork = require('child_process').fork; + + var isAlive = function(pid) { + try { + //this will throw an error if the process is dead + process.kill(pid, 0); + + return true; + } catch (e) { + return false; + } + }; + + var existMaster = false; + var existWorker = false; + + // List all workers + var workers = []; + + // Spawn a cluster process + var master = fork(process.argv[1], ['cluster'], {silent: true}); + + // Handle messages from the cluster + master.on('message', function(data) { + + // Add worker pid to list and progress tracker + if (data.cmd === 'worker') { + workers.push(data.workerPID); + } + }); + + // When cluster is dead + master.on('exit', function(code) { + + // Check that the cluster died accidently + existMaster = (code === 1); + + // When master is dead all workers should be dead to + var alive = false; + workers.forEach(function(pid) { + if (isAlive(pid)) { + alive = true; + } + }); + + // If a worker was alive this did not act as expected + existWorker = !alive; + }); + + process.once('exit', function() { + assert.ok(existMaster, 'The master did not die after an error was throwed'); + assert.ok(existWorker, 'The workers did not die after an error in the master'); + }); + +} diff --git a/test/simple/test-cluster-message.js b/test/simple/test-cluster-message.js new file mode 100644 index 0000000000..6bab71d944 --- /dev/null +++ b/test/simple/test-cluster-message.js @@ -0,0 +1,130 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); +var net = require('net'); + +function forEach(obj, fn) { + Object.keys(obj).forEach(function(name, index) { + fn(obj[name], name, index); + }); +} + +if (cluster.isWorker) { + // Create a tcp server + // this will be used as cluster-shared-server + // and as an alternativ IPC channel + var server = net.Server(); + server.on('connection', function(socket) { + + // Tell master using TCP socket that a message is received + process.on('message', function(message) { + socket.write(JSON.stringify({ + code: 'received message', + echo: message + })); + }); + + process.send('message from worker'); + }); + + server.listen(common.PORT, '127.0.0.1'); +} + +else if (cluster.isMaster) { + + var checks = { + master: { + 'receive': false, + 'correct': false + }, + worker: { + 'receive': false, + 'correct': false + } + }; + + + var client; + var check = function(type, result) { + checks[type].receive = true; + checks[type].correct = result; + + var missing = false; + forEach(checks, function(type) { + if (type.receive === false) missing = true; + }); + + if (missing === false) { + client.end(); + } + }; + + // Spawn worker + var worker = cluster.fork(); + + // When a IPC message is resicved form the worker + worker.on('message', function(message) { + check('master', message === 'message from worker'); + }); + + // When a TCP connection is made with the worker connect to it + worker.on('listening', function() { + + client = net.connect(common.PORT, function() { + + //Send message to worker + worker.send('message from master'); + }); + + client.on('data', function(data) { + // All data is JSON + data = JSON.parse(data.toString()); + + if (data.code === 'received message') { + check('worker', data.echo === 'message from master'); + } else { + throw new Error('worng TCP message recived: ' + data); + } + }); + + // When the connection ends kill worker and shutdown process + client.on('end', function() { + worker.destroy(); + }); + + worker.on('death', function() { + process.exit(0); + }); + + }); + + process.once('exit', function() { + forEach(checks, function(check, type) { + assert.ok(check.receive, 'The ' + type + ' did not receive any message'); + assert.ok(check.correct, + 'The ' + type + ' did not get the correct message'); + }); + }); +} diff --git a/test/simple/test-cluster-setup-master.js b/test/simple/test-cluster-setup-master.js new file mode 100644 index 0000000000..7f542e4e68 --- /dev/null +++ b/test/simple/test-cluster-setup-master.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); + +if (cluster.isWorker) { + + // Just keep the worker alive + process.send(process.argv[2]); + +} else if (cluster.isMaster) { + + var checks = { + args: false, + setupEvent: false, + settingsObject: false + }; + + var totalWorkers = 2; + + cluster.once('setup', function() { + checks.setupEvent = true; + + var settings = cluster.settings; + if (settings && + settings.args && settings.args[0] === 'custom argument' && + settings.silent === true && + settings.exec === process.argv[1]) { + checks.settingsObject = true; + } + }); + + // Setup master + cluster.setupMaster({ + args: ['custom argument'], + silent: true + }); + + var correctIn = 0; + + cluster.on('online', function lisenter(worker) { + + worker.once('message', function(data) { + correctIn += (data === 'custom argument' ? 1 : 0); + if (correctIn === totalWorkers) { + checks.args = true; + } + worker.destroy(); + }); + + // All workers are online + if (cluster.onlineWorkers === totalWorkers) { + checks.workers = true; + } + }); + + // Start all workers + cluster.fork(); + cluster.fork(); + + // Check all values + process.once('exit', function() { + assert.ok(checks.args, 'The arguments was noy send to the worker'); + assert.ok(checks.setupEvent, 'The setup event was never emitted'); + assert.ok(checks.settingsObject, 'The settingsObject do not have correct properties'); + }); + +} diff --git a/test/simple/test-crypto-padding.js b/test/simple/test-crypto-padding.js new file mode 100644 index 0000000000..55bd94e00d --- /dev/null +++ b/test/simple/test-crypto-padding.js @@ -0,0 +1,119 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +try { + var crypto = require('crypto'); +} catch (e) { + console.log('Not compiled with OPENSSL support.'); + process.exit(); +} + + +/* + * Input data + */ + +var ODD_LENGTH_PLAIN = 'Hello node world!', + EVEN_LENGTH_PLAIN = 'Hello node world!AbC09876dDeFgHi'; + +var KEY_PLAIN = 'S3c.r.e.t.K.e.Y!', + IV_PLAIN = 'blahFizz2011Buzz'; + +var CIPHER_NAME = 'aes-128-cbc'; + + +/* + * Expected result data + */ + +// echo -n 'Hello node world!' | openssl enc -aes-128-cbc -e -K 5333632e722e652e742e4b2e652e5921 -iv 626c616846697a7a3230313142757a7a | xxd -p -c256 +var ODD_LENGTH_ENCRYPTED = '7f57859550d4d2fdb9806da2a750461a9fe77253cd1cbd4b07beee4e070d561f'; + +// echo -n 'Hello node world!AbC09876dDeFgHi' | openssl enc -aes-128-cbc -e -K 5333632e722e652e742e4b2e652e5921 -iv 626c616846697a7a3230313142757a7a | xxd -p -c256 +var EVEN_LENGTH_ENCRYPTED = '7f57859550d4d2fdb9806da2a750461ab46e71b3d78ebe2d9684dfc87f7575b9886119866912cb8c7bcaf76c5ebc2378'; + +// echo -n 'Hello node world!AbC09876dDeFgHi' | openssl enc -aes-128-cbc -e -K 5333632e722e652e742e4b2e652e5921 -iv 626c616846697a7a3230313142757a7a -nopad | xxd -p -c256 +var EVEN_LENGTH_ENCRYPTED_NOPAD = '7f57859550d4d2fdb9806da2a750461ab46e71b3d78ebe2d9684dfc87f7575b9'; + + +/* + * Helper wrappers + */ + +function enc(plain, pad) { + var encrypt = crypto.createCipheriv(CIPHER_NAME, KEY_PLAIN, IV_PLAIN); + encrypt.setAutoPadding(pad); + var hex = encrypt.update(plain, 'ascii', 'hex'); + hex += encrypt.final('hex'); + return hex; +} + +function dec(encd, pad) { + var decrypt = crypto.createDecipheriv(CIPHER_NAME, KEY_PLAIN, IV_PLAIN); + decrypt.setAutoPadding(pad); + var plain = decrypt.update(encd, 'hex'); + plain += decrypt.final('binary'); + return plain; +} + + +/* + * Test encryption + */ + +assert.equal(enc(ODD_LENGTH_PLAIN, true), ODD_LENGTH_ENCRYPTED); +assert.equal(enc(EVEN_LENGTH_PLAIN, true), EVEN_LENGTH_ENCRYPTED); + +assert.throws(function() { + // input must have block length % + enc(ODD_LENGTH_PLAIN, false); +}); + +assert.doesNotThrow(function() { + assert.equal(enc(EVEN_LENGTH_PLAIN, false), EVEN_LENGTH_ENCRYPTED_NOPAD); +}); + + +/* + * Test decryption + */ + +assert.equal(dec(ODD_LENGTH_ENCRYPTED, true), ODD_LENGTH_PLAIN); +assert.equal(dec(EVEN_LENGTH_ENCRYPTED, true), EVEN_LENGTH_PLAIN); + +assert.doesNotThrow(function() { + // returns including original padding + assert.equal(dec(ODD_LENGTH_ENCRYPTED, false).length, 32); + assert.equal(dec(EVEN_LENGTH_ENCRYPTED, false).length, 48); +}); + +assert.throws(function() { + // must have at least 1 byte of padding (PKCS): + assert.equal(dec(EVEN_LENGTH_ENCRYPTED_NOPAD, true), EVEN_LENGTH_PLAIN); +}); + +assert.doesNotThrow(function() { + // no-pad encrypted string should return the same: + assert.equal(dec(EVEN_LENGTH_ENCRYPTED_NOPAD, false), EVEN_LENGTH_PLAIN); +}); diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index 6bd1e02ca7..cbccc72dad 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -278,6 +278,11 @@ fileStream.on('close', function() { 'Test SHA1 of sample.png'); }); +// Issue #2227: unknown digest method should throw an error. +assert.throws(function() { + crypto.createHash('xyzzy'); +}); + // Test signing and verifying var s1 = crypto.createSign('RSA-SHA1') .update('Test123') diff --git a/test/simple/test-debugger-repl-utf8.js b/test/simple/test-debugger-repl-utf8.js index 820a539143..bee7994e91 100644 --- a/test/simple/test-debugger-repl-utf8.js +++ b/test/simple/test-debugger-repl-utf8.js @@ -27,7 +27,7 @@ var debug = require('_debugger'); var script = common.fixturesDir + '/breakpoints_utf8.js'; -var child = spawn(process.execPath, ['debug', script]); +var child = spawn(process.execPath, ['debug', '--port=' + common.PORT, script]); var buffer = ''; child.stdout.setEncoding('utf-8'); @@ -77,7 +77,7 @@ function addTest(input, output) { // Initial lines addTest(null, [ - /listening on port 5858/, + /listening on port \d+/, /connecting... ok/, /break in .*:1/, /1/, /2/, /3/ diff --git a/test/simple/test-debugger-repl.js b/test/simple/test-debugger-repl.js index df1b14298e..44a89b94e3 100644 --- a/test/simple/test-debugger-repl.js +++ b/test/simple/test-debugger-repl.js @@ -27,7 +27,7 @@ var debug = require('_debugger'); var script = common.fixturesDir + '/breakpoints.js'; -var child = spawn(process.execPath, ['debug', script]); +var child = spawn(process.execPath, ['debug', '--port=' + common.PORT, script]); var buffer = ''; child.stdout.setEncoding('utf-8'); @@ -82,7 +82,7 @@ function addTest(input, output) { // Initial lines addTest(null, [ - /listening on port 5858/, + /listening on port \d+/, /connecting... ok/, /break in .*:1/, /1/, /2/, /3/ diff --git a/test/simple/test-eio-limit.js b/test/simple/test-eio-limit.js new file mode 100644 index 0000000000..59cfbbfa65 --- /dev/null +++ b/test/simple/test-eio-limit.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'), + zlib = require('zlib'), + started = 0, + done = 0; + +function repeat(fn) { + if (started != 0) { + assert.ok(started - done < 100) + } + + process.nextTick(function() { + fn(); + repeat(fn); + }); +} + +repeat(function() { + if (started > 1000) return process.exit(0); + + for (var i = 0; i < 30; i++) { + started++; + var deflate = zlib.createDeflate(); + deflate.write('123'); + deflate.flush(function() { + done++; + }); + } +}); diff --git a/test/simple/test-fs-append-file-sync.js b/test/simple/test-fs-append-file-sync.js new file mode 100644 index 0000000000..c000946f95 --- /dev/null +++ b/test/simple/test-fs-append-file-sync.js @@ -0,0 +1,92 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var join = require('path').join; +var fs = require('fs'); + +var currentFileData = 'ABCD'; + +var num = 220; +var data = 'å—越国是å‰203年至å‰111å¹´å˜åœ¨äºŽå²å—地区的一个国家,国都ä½äºŽç•ªç¦ºï¼Œç–†åŸŸåŒ…括今天ä¸å›½çš„广东ã€' + + '广西两çœåŒºçš„大部份地区,ç¦å»ºçœã€æ¹–å—ã€è´µå·žã€äº‘å—的一å°éƒ¨ä»½åœ°åŒºå’Œè¶Šå—的北部。' + + 'å—越国是秦æœç亡åŽï¼Œç”±å—海郡尉赵佗于å‰203年起兵兼并桂林郡和象郡åŽå»ºç«‹ã€‚' + + 'å‰196å¹´å’Œå‰179年,å—越国曾先åŽä¸¤æ¬¡å义上臣属于西汉,æˆä¸ºè¥¿æ±‰çš„“外臣â€ã€‚å‰112年,' + + 'å—越国末代å›ä¸»èµµå»ºå¾·ä¸Žè¥¿æ±‰å‘生战争,被汉æ¦å¸äºŽå‰111年所ç。å—越国共å˜åœ¨93年,' + + '历ç»äº”代å›ä¸»ã€‚å—越国是å²å—地区的第一个有记载的政æƒå›½å®¶ï¼Œé‡‡ç”¨å°å»ºåˆ¶å’Œéƒ¡åŽ¿åˆ¶å¹¶å˜çš„制度,' + + '它的建立ä¿è¯äº†ç§¦æœ«ä¹±ä¸–å²å—地区社会秩åºçš„稳定,有效的改善了å²å—地区è½åŽçš„æ”¿æ²»ã€##济现状。\n'; + +// test that empty file will be created and have content added +var filename = join(common.fixturesDir, 'append-sync.txt'); + +common.error('appending to ' + filename); +fs.appendFileSync(filename, data); + +var fileData = fs.readFileSync(filename); + +assert.equal(Buffer.byteLength(data), fileData.length); + +// test that appends data to a non empty file +var filename2 = join(common.fixturesDir, 'append-sync2.txt'); +fs.writeFileSync(filename2, currentFileData); + +common.error('appending to ' + filename2); +fs.appendFileSync(filename2, data); + +var fileData2 = fs.readFileSync(filename2); + +assert.equal(Buffer.byteLength(data) + currentFileData.length, fileData2.length); + +// test that appendFileSync accepts buffers +var filename3 = join(common.fixturesDir, 'append-sync3.txt'); +fs.writeFileSync(filename3, currentFileData); + +common.error('appending to ' + filename3); + +var buf = new Buffer(data, 'utf8'); +fs.appendFileSync(filename3, buf); + +var fileData3 = fs.readFileSync(filename3); + +assert.equal(buf.length + currentFileData.length, fileData3.length); + +// test that appendFile accepts numbers. +var filename4 = join(common.fixturesDir, 'append-sync4.txt'); +fs.writeFileSync(filename4, currentFileData); + +common.error('appending to ' + filename4); +fs.appendFileSync(filename4, num); + +var fileData4 = fs.readFileSync(filename4); + +assert.equal(Buffer.byteLength('' + num) + currentFileData.length, fileData4.length); + +//exit logic for cleanup + +process.on('exit', function() { + common.error('done'); + + fs.unlinkSync(filename); + fs.unlinkSync(filename2); + fs.unlinkSync(filename3); + fs.unlinkSync(filename4); +}); diff --git a/test/simple/test-fs-append-file.js b/test/simple/test-fs-append-file.js new file mode 100644 index 0000000000..82a9d8f217 --- /dev/null +++ b/test/simple/test-fs-append-file.js @@ -0,0 +1,126 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var fs = require('fs'); +var join = require('path').join; + +var filename = join(common.fixturesDir, 'append.txt'); + +common.error('appending to ' + filename); + +var currentFileData = 'ABCD'; + +var n = 220; +var s = 'å—越国是å‰203年至å‰111å¹´å˜åœ¨äºŽå²å—地区的一个国家,国都ä½äºŽç•ªç¦ºï¼Œç–†åŸŸåŒ…括今天ä¸å›½çš„广东ã€' + + '广西两çœåŒºçš„大部份地区,ç¦å»ºçœã€æ¹–å—ã€è´µå·žã€äº‘å—的一å°éƒ¨ä»½åœ°åŒºå’Œè¶Šå—的北部。' + + 'å—越国是秦æœç亡åŽï¼Œç”±å—海郡尉赵佗于å‰203年起兵兼并桂林郡和象郡åŽå»ºç«‹ã€‚' + + 'å‰196å¹´å’Œå‰179年,å—越国曾先åŽä¸¤æ¬¡å义上臣属于西汉,æˆä¸ºè¥¿æ±‰çš„“外臣â€ã€‚å‰112年,' + + 'å—越国末代å›ä¸»èµµå»ºå¾·ä¸Žè¥¿æ±‰å‘生战争,被汉æ¦å¸äºŽå‰111年所ç。å—越国共å˜åœ¨93年,' + + '历ç»äº”代å›ä¸»ã€‚å—越国是å²å—地区的第一个有记载的政æƒå›½å®¶ï¼Œé‡‡ç”¨å°å»ºåˆ¶å’Œéƒ¡åŽ¿åˆ¶å¹¶å˜çš„制度,' + + '它的建立ä¿è¯äº†ç§¦æœ«ä¹±ä¸–å²å—地区社会秩åºçš„稳定,有效的改善了å²å—地区è½åŽçš„æ”¿æ²»ã€##济现状。\n'; + +var ncallbacks = 0; + +// test that empty file will be created and have content added +fs.appendFile(filename, s, function(e) { + if (e) throw e; + + ncallbacks++; + common.error('appended to file'); + + fs.readFile(filename, function(e, buffer) { + if (e) throw e; + common.error('file read'); + ncallbacks++; + assert.equal(Buffer.byteLength(s), buffer.length); + }); +}); + +// test that appends data to a non empty file +var filename2 = join(common.fixturesDir, 'append2.txt'); +fs.writeFileSync(filename2, currentFileData); + +fs.appendFile(filename2, s, function(e) { + if (e) throw e; + + ncallbacks++; + common.error('appended to file2'); + + fs.readFile(filename2, function(e, buffer) { + if (e) throw e; + common.error('file2 read'); + ncallbacks++; + assert.equal(Buffer.byteLength(s) + currentFileData.length, buffer.length); + }); +}); + +// test that appendFile accepts buffers +var filename3 = join(common.fixturesDir, 'append3.txt'); +fs.writeFileSync(filename3, currentFileData); + +var buf = new Buffer(s, 'utf8'); +common.error('appending to ' + filename3); + +fs.appendFile(filename3, buf, function(e) { + if (e) throw e; + + ncallbacks++; + common.error('appended to file3'); + + fs.readFile(filename3, function(e, buffer) { + if (e) throw e; + common.error('file3 read'); + ncallbacks++; + assert.equal(buf.length + currentFileData.length, buffer.length); + }); +}); + +// test that appendFile accepts numbers. +var filename4 = join(common.fixturesDir, 'append4.txt'); +fs.writeFileSync(filename4, currentFileData); + +common.error('appending to ' + filename4); + +fs.appendFile(filename4, n, function(e) { + if (e) throw e; + + ncallbacks++; + common.error('appended to file4'); + + fs.readFile(filename4, function(e, buffer) { + if (e) throw e; + common.error('file4 read'); + ncallbacks++; + assert.equal(Buffer.byteLength('' + n) + currentFileData.length, buffer.length); + }); +}); + +process.on('exit', function() { + common.error('done'); + assert.equal(8, ncallbacks); + + fs.unlinkSync(filename); + fs.unlinkSync(filename2); + fs.unlinkSync(filename3); + fs.unlinkSync(filename4); +}); diff --git a/test/simple/test-fs-exists.js b/test/simple/test-fs-exists.js new file mode 100644 index 0000000000..cf785ccdb1 --- /dev/null +++ b/test/simple/test-fs-exists.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var fs = require('fs'); +var f = __filename; +var exists; +var doesNotExist; + +fs.exists(f, function(y) { + exists = y; +}); + +fs.exists(f + '-NO', function (y) { + doesNotExist = y; +}); + +assert(fs.existsSync(f)); +assert(!fs.existsSync(f + '-NO')); + +process.on('exit', function () { + assert.strictEqual(exists, true); + assert.strictEqual(doesNotExist, false); +}); diff --git a/test/simple/test-fs-mkdir.js b/test/simple/test-fs-mkdir.js index 176cc4cf8d..d99cd371b3 100644 --- a/test/simple/test-fs-mkdir.js +++ b/test/simple/test-fs-mkdir.js @@ -21,7 +21,6 @@ var common = require('../common'); var assert = require('assert'); -var path = require('path'); var fs = require('fs'); function unlink(pathname) { @@ -39,7 +38,7 @@ function unlink(pathname) { fs.mkdir(pathname, function(err) { assert.equal(err, null); - assert.equal(path.existsSync(pathname), true); + assert.equal(fs.existsSync(pathname), true); ncalls++; }); @@ -57,7 +56,7 @@ function unlink(pathname) { fs.mkdir(pathname, 511 /*=0777*/, function(err) { assert.equal(err, null); - assert.equal(path.existsSync(pathname), true); + assert.equal(fs.existsSync(pathname), true); ncalls++; }); @@ -73,7 +72,7 @@ function unlink(pathname) { unlink(pathname); fs.mkdirSync(pathname); - var exists = path.existsSync(pathname); + var exists = fs.existsSync(pathname); unlink(pathname); assert.equal(exists, true); diff --git a/test/simple/test-fs-open-flags.js b/test/simple/test-fs-open-flags.js new file mode 100644 index 0000000000..7d51cc9a4c --- /dev/null +++ b/test/simple/test-fs-open-flags.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var constants = require('constants'); +var fs = require('fs'); + +var O_APPEND = constants.O_APPEND || 0; +var O_CREAT = constants.O_CREAT || 0; +var O_DIRECTORY = constants.O_DIRECTORY || 0; +var O_EXCL = constants.O_EXCL || 0; +var O_NOCTTY = constants.O_NOCTTY || 0; +var O_NOFOLLOW = constants.O_NOFOLLOW || 0; +var O_RDONLY = constants.O_RDONLY || 0; +var O_RDWR = constants.O_RDWR || 0; +var O_SYMLINK = constants.O_SYMLINK || 0; +var O_SYNC = constants.O_SYNC || 0; +var O_TRUNC = constants.O_TRUNC || 0; +var O_WRONLY = constants.O_WRONLY || 0; + +assert.equal(fs._stringToFlags('r'), O_RDONLY); +assert.equal(fs._stringToFlags('r+'), O_RDWR); +assert.equal(fs._stringToFlags('w'), O_TRUNC|O_CREAT|O_WRONLY); +assert.equal(fs._stringToFlags('w+'), O_TRUNC|O_CREAT|O_RDWR); +assert.equal(fs._stringToFlags('a'), O_APPEND|O_CREAT|O_WRONLY); +assert.equal(fs._stringToFlags('a+'), O_APPEND|O_CREAT|O_RDWR); + +assert.equal(fs._stringToFlags('wx'), O_TRUNC|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('xw'), O_TRUNC|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('wx+'), O_TRUNC|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('xw+'), O_TRUNC|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('ax'), O_APPEND|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('xa'), O_APPEND|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('ax+'), O_APPEND|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('xa+'), O_APPEND|O_CREAT|O_RDWR|O_EXCL); + +('+ +a +r +w rw wa war raw r++ a++ w++' + + 'x +x x+ rx rx+ wxx wax xwx xxx').split(' ').forEach(function(flags) { + assert.throws(function() { fs._stringToFlags(flags); }); +}); diff --git a/test/simple/test-http-1.0.js b/test/simple/test-http-1.0.js index fadaaf0f1e..f5f3aef0a6 100644 --- a/test/simple/test-http-1.0.js +++ b/test/simple/test-http-1.0.js @@ -104,6 +104,7 @@ function test(handler, request_generator, response_validator) { assert.equal('1.0', req.httpVersion); assert.equal(1, req.httpVersionMajor); assert.equal(0, req.httpVersionMinor); + res.sendDate = false; res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello, '); res._send(''); res.write('world!'); res._send(''); @@ -140,6 +141,7 @@ function test(handler, request_generator, response_validator) { assert.equal('1.1', req.httpVersion); assert.equal(1, req.httpVersionMajor); assert.equal(1, req.httpVersionMinor); + res.sendDate = false; res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello, '); res._send(''); res.write('world!'); res._send(''); diff --git a/test/simple/test-http-after-connect.js b/test/simple/test-http-after-connect.js new file mode 100644 index 0000000000..d6451bb85d --- /dev/null +++ b/test/simple/test-http-after-connect.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var serverConnected = false; +var serverRequests = 0; +var clientResponses = 0; + +var server = http.createServer(function(req, res) { + common.debug('Server got GET request'); + ++serverRequests; + res.writeHead(200); + res.write(''); + setTimeout(function() { + res.end(req.url); + }, 50); +}); +server.on('connect', function(req, socket, firstBodyChunk) { + common.debug('Server got CONNECT request'); + serverConnected = true; + socket.write('HTTP/1.1 200 Connection established\r\n\r\n'); + socket.on('end', function() { + socket.end(); + }); +}); +server.listen(common.PORT, function() { + var req = http.request({ + port: common.PORT, + method: 'CONNECT', + path: 'google.com:80' + }); + req.on('connect', function(res, socket, firstBodyChunk) { + common.debug('Client got CONNECT response'); + socket.end(); + socket.on('end', function() { + doRequest(0); + doRequest(1); + }); + }); + req.end(); +}); + +function doRequest(i) { + var req = http.get({ + port: common.PORT, + path: '/request' + i + }, function(res) { + common.debug('Client got GET response'); + var data = ''; + res.setEncoding('utf8'); + res.on('data', function(chunk) { + data += chunk; + }); + res.on('end', function() { + assert.equal(data, '/request' + i); + ++clientResponses; + if (clientResponses === 2) { + server.close(); + } + }); + }); +} + +process.on('exit', function() { + assert(serverConnected); + assert.equal(serverRequests, 2); + assert.equal(clientResponses, 2); +}); diff --git a/test/simple/test-http-connect.js b/test/simple/test-http-connect.js new file mode 100644 index 0000000000..668dda7963 --- /dev/null +++ b/test/simple/test-http-connect.js @@ -0,0 +1,105 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var serverGotConnect = false; +var clientGotConnect = false; + +var server = http.createServer(function(req, res) { + assert(false); +}); +server.on('connect', function(req, socket, firstBodyChunk) { + assert.equal(req.method, 'CONNECT'); + assert.equal(req.url, 'google.com:443'); + common.debug('Server got CONNECT request'); + serverGotConnect = true; + + socket.write('HTTP/1.1 200 Connection established\r\n\r\n'); + + var data = firstBodyChunk.toString(); + socket.on('data', function(buf) { + data += buf.toString(); + }); + socket.on('end', function() { + socket.end(data); + }); +}); +server.listen(common.PORT, function() { + var req = http.request({ + port: common.PORT, + method: 'CONNECT', + path: 'google.com:443' + }, function(res) { + assert(false); + }); + + var clientRequestClosed = false; + req.on('close', function() { + clientRequestClosed = true; + }); + + req.on('connect', function(res, socket, firstBodyChunk) { + common.debug('Client got CONNECT request'); + clientGotConnect = true; + + // Make sure this request got removed from the pool. + var name = 'localhost:' + common.PORT; + assert(!http.globalAgent.sockets.hasOwnProperty(name)); + assert(!http.globalAgent.requests.hasOwnProperty(name)); + + // Make sure this socket has detached. + assert(!socket.ondata); + assert(!socket.onend); + assert.equal(socket.listeners('connect').length, 0); + assert.equal(socket.listeners('data').length, 0); + assert.equal(socket.listeners('end').length, 0); + assert.equal(socket.listeners('free').length, 0); + assert.equal(socket.listeners('close').length, 0); + assert.equal(socket.listeners('error').length, 0); + assert.equal(socket.listeners('agentRemove').length, 0); + + var data = firstBodyChunk.toString(); + socket.on('data', function(buf) { + data += buf.toString(); + }); + socket.on('end', function() { + assert.equal(data, 'HeadBody'); + assert(clientRequestClosed); + server.close(); + }); + socket.write('Body'); + socket.end(); + }); + + // It is legal for the client to send some data intended for the server + // before the "200 Connection established" (or any other success or + // error code) is received. + req.write('Head'); + req.end(); +}); + +process.on('exit', function() { + assert.ok(serverGotConnect); + assert.ok(clientGotConnect); +}); diff --git a/test/simple/test-http-date-header.js b/test/simple/test-http-date-header.js new file mode 100644 index 0000000000..e416b37583 --- /dev/null +++ b/test/simple/test-http-date-header.js @@ -0,0 +1,35 @@ +var common = require("../common"); +var assert = require('assert'); +var http = require("http"); + +var testResBody = "other stuff!\n"; + +var server = http.createServer(function(req, res) { + assert.ok(! ("date" in req.headers), + "Request headers contained a Date." + ); + res.writeHead(200, { + 'Content-Type' : 'text/plain', + }); + res.end(testResBody); +}); +server.listen(common.PORT); + + +server.addListener("listening", function() { + var options = { + port: common.PORT, + path: "/", + method: "GET" + } + var req = http.request(options, function (res) { + assert.ok("date" in res.headers, + "Response headers didn't contain a Date." + ); + res.addListener('end', function () { + server.close(); + process.exit(); + }); + }); + req.end(); +}); diff --git a/test/simple/test-http-max-headers-count.js b/test/simple/test-http-max-headers-count.js new file mode 100644 index 0000000000..f34e7b5b79 --- /dev/null +++ b/test/simple/test-http-max-headers-count.js @@ -0,0 +1,87 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var requests = 0; +var responses = 0; + +var headers = {}; +var N = 2000; +for (var i = 0; i < N; ++i) { + headers['key' + i] = i; +} + +var maxAndExpected = [ // for server + [50, 50], + [1500, 1500], + [0, N + 2], // Host and Connection +]; +var max = maxAndExpected[requests][0]; +var expected = maxAndExpected[requests][1]; + +var server = http.createServer(function(req, res) { + assert.equal(Object.keys(req.headers).length, expected); + if (++requests < maxAndExpected.length) { + max = maxAndExpected[requests][0]; + expected = maxAndExpected[requests][1]; + server.maxHeadersCount = max; + } + res.writeHead(200, headers); + res.end(); +}); +server.maxHeadersCount = max; + +server.listen(common.PORT, function() { + var maxAndExpected = [ // for client + [20, 20], + [1200, 1200], + [0, N + 3], // Connection, Date and Transfer-Encoding + ]; + doRequest(); + + function doRequest() { + var max = maxAndExpected[responses][0]; + var expected = maxAndExpected[responses][1]; + var req = http.request({ + port: common.PORT, + headers: headers + }, function(res) { + assert.equal(Object.keys(res.headers).length, expected); + res.on('end', function() { + if (++responses < maxAndExpected.length) { + doRequest(); + } else { + server.close(); + } + }); + }); + req.maxHeadersCount = max; + req.end(); + } +}); + +process.on('exit', function() { + assert.equal(requests, maxAndExpected.length); + assert.equal(responses, maxAndExpected.length); +}); diff --git a/test/simple/test-http-parser.js b/test/simple/test-http-parser.js index 7dc15043c4..e93b846c70 100644 --- a/test/simple/test-http-parser.js +++ b/test/simple/test-http-parser.js @@ -150,6 +150,29 @@ function expectBody(expected) { // +// Response with no headers. +// +(function() { + var request = Buffer( + 'HTTP/1.0 200 Connection established' + CRLF + + CRLF + ); + + var parser = newParser(RESPONSE); + + parser.onHeadersComplete = mustCall(function(info) { + assert.equal(info.method, undefined); + assert.equal(info.versionMajor, 1); + assert.equal(info.versionMinor, 0); + assert.equal(info.statusCode, 200); + assert.deepEqual(info.headers || parser.headers, []); + }); + + parser.execute(request, 0, request.length); +})(); + + +// // Trailing headers. // (function() { @@ -483,7 +506,7 @@ function expectBody(expected) { // -// +// Test parser reinit sequence. // (function() { var req1 = Buffer( diff --git a/test/simple/test-http-pause.js b/test/simple/test-http-pause.js new file mode 100644 index 0000000000..0edf6d6221 --- /dev/null +++ b/test/simple/test-http-pause.js @@ -0,0 +1,75 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var expectedServer = 'Request Body from Client'; +var resultServer = ''; +var expectedClient = 'Response Body from Server'; +var resultClient = ''; + +var server = http.createServer(function(req, res) { + common.debug('pause server request'); + req.pause(); + setTimeout(function() { + common.debug('resume server request'); + req.resume(); + req.setEncoding('utf8'); + req.on('data', function(chunk) { + resultServer += chunk; + }); + req.on('end', function() { + common.debug(resultServer); + res.writeHead(200); + res.end(expectedClient); + }); + }, 100); +}); + +server.listen(common.PORT, function() { + var req = http.request({ + port: common.PORT, + path: '/', + method: 'POST' + }, function(res) { + common.debug('pause client response'); + res.pause(); + setTimeout(function() { + common.debug('resume client response'); + res.resume(); + res.on('data', function(chunk) { + resultClient += chunk; + }); + res.on('end', function() { + common.debug(resultClient); + server.close(); + }); + }, 100); + }); + req.end(expectedServer); +}); + +process.on('exit', function() { + assert.equal(expectedServer, resultServer); + assert.equal(expectedClient, resultClient); +}); diff --git a/test/simple/test-http-upgrade-agent.js b/test/simple/test-http-upgrade-agent.js index a87d5e864c..1077a983dc 100644 --- a/test/simple/test-http-upgrade-agent.js +++ b/test/simple/test-http-upgrade-agent.js @@ -74,11 +74,11 @@ srv.listen(common.PORT, '127.0.0.1', function() { 'connection': 'upgrade', 'upgrade': 'websocket' }; assert.deepEqual(expectedHeaders, res.headers); - assert.equal(http.globalAgent.sockets[name].length, 1); - process.nextTick(function() { - // Make sure this request got removed from the pool. - assert(!http.globalAgent.sockets.hasOwnProperty(name)); + // Make sure this request got removed from the pool. + assert(!http.globalAgent.sockets.hasOwnProperty(name)); + + req.on('close', function() { socket.end(); srv.close(); diff --git a/test/simple/test-https-client-reject.js b/test/simple/test-https-client-reject.js new file mode 100644 index 0000000000..77820533da --- /dev/null +++ b/test/simple/test-https-client-reject.js @@ -0,0 +1,95 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); +var https = require('https'); +var fs = require('fs'); +var path = require('path'); + +var options = { + key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), + cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) +}; + +var reqCount = 0; + +var server = https.createServer(options, function(req, res) { + ++reqCount; + res.writeHead(200); + res.end(); +}).listen(common.PORT, function() { + unauthorized(); +}); + +function unauthorized() { + var req = https.request({ + port: common.PORT + }, function(res) { + assert(!req.socket.authorized); + rejectUnauthorized(); + }); + req.on('error', function(err) { + assert(false); + }); + req.end(); +} + +function rejectUnauthorized() { + var options = { + port: common.PORT, + rejectUnauthorized: true + }; + options.agent = new https.Agent(options); + var req = https.request(options, function(res) { + assert(false); + }); + req.on('error', function(err) { + authorized(); + }); + req.end(); +} + +function authorized() { + var options = { + port: common.PORT, + rejectUnauthorized: true, + ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ] + }; + options.agent = new https.Agent(options); + var req = https.request(options, function(res) { + assert(req.socket.authorized); + server.close(); + }); + req.on('error', function(err) { + assert(false); + }); + req.end(); +} + +process.on('exit', function() { + assert.equal(reqCount, 2); +}); diff --git a/test/simple/test-net-connect-options.js b/test/simple/test-net-connect-options.js new file mode 100644 index 0000000000..8df692ef7e --- /dev/null +++ b/test/simple/test-net-connect-options.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); + +var serverGotEnd = false; +var clientGotEnd = false; + +var server = net.createServer({allowHalfOpen: true}, function(socket) { + socket.on('end', function() { + serverGotEnd = true; + }); + socket.end(); +}); + +server.listen(common.PORT, function() { + var client = net.connect({ + host: '127.0.0.1', + port: common.PORT, + allowHalfOpen: true + }, function() { + client.on('end', function() { + clientGotEnd = true; + setTimeout(function() { + assert(client.writable); + client.end(); + }, 10); + }); + client.on('close', function() { + server.close(); + }); + }); +}); + +process.on('exit', function() { + assert(serverGotEnd); + assert(clientGotEnd); +}); diff --git a/test/simple/test-net-server-listen-remove-callback.js b/test/simple/test-net-server-listen-remove-callback.js index a92a41859b..e10231de8c 100644 --- a/test/simple/test-net-server-listen-remove-callback.js +++ b/test/simple/test-net-server-listen-remove-callback.js @@ -34,6 +34,9 @@ server.on('close', function() { server.listen(common.PORT, function() { server.close(); +}); + +server.once('close', function() { server.listen(common.PORT + 1, function() { server.close(); }); diff --git a/test/simple/test-os.js b/test/simple/test-os.js index 4bed42eba8..92d1a264c1 100644 --- a/test/simple/test-os.js +++ b/test/simple/test-os.js @@ -72,4 +72,10 @@ switch (platform) { var expected = [{ address: '127.0.0.1', family: 'IPv4', internal: true }]; assert.deepEqual(actual, expected); break; + case 'win32': + var filter = function(e) { return e.address == '127.0.0.1'; }; + var actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter); + var expected = [{ address: '127.0.0.1', family: 'IPv4', internal: true }]; + assert.deepEqual(actual, expected); + break; } diff --git a/test/simple/test-path.js b/test/simple/test-path.js index 92c2647fd2..caf86aa465 100644 --- a/test/simple/test-path.js +++ b/test/simple/test-path.js @@ -78,9 +78,6 @@ if (isWindows) { '\\\\unc\\share\\foo\\bar'); } -path.exists(f, function(y) { assert.equal(y, true) }); - -assert.equal(path.existsSync(f), true); assert.equal(path.extname(''), ''); assert.equal(path.extname('/path/to/file'), ''); diff --git a/test/simple/test-querystring.js b/test/simple/test-querystring.js index de0d63102a..bfaf9ae18a 100644 --- a/test/simple/test-querystring.js +++ b/test/simple/test-querystring.js @@ -183,6 +183,28 @@ assert.equal(f, 'a:b;q:x%3Ay%3By%3Az'); assert.deepEqual({}, qs.parse()); +// Test limiting +assert.equal( + Object.keys(qs.parse('a=1&b=1&c=1', null, null, { maxKeys: 1 })).length, + 1 +); + +// Test removing limit +function testUnlimitedKeys() { + var query = {}, + url; + + for (var i = 0; i < 2000; i++) query[i] = i; + + url = qs.stringify(query); + + assert.equal( + Object.keys(qs.parse(url, null, null, { maxKeys: 0 })).length, + 2000 + ); +} +testUnlimitedKeys(); + var b = qs.unescapeBuffer('%d3%f2Ug%1f6v%24%5e%98%cb' + '%0d%ac%a2%2f%9d%eb%d8%a2%e6'); @@ -207,4 +229,3 @@ assert.equal(0xeb, b[16]); assert.equal(0xd8, b[17]); assert.equal(0xa2, b[18]); assert.equal(0xe6, b[19]); - diff --git a/test/simple/test-repl.js b/test/simple/test-repl.js index 4fc65f3631..3008210ec5 100644 --- a/test/simple/test-repl.js +++ b/test/simple/test-repl.js @@ -124,6 +124,9 @@ function error_test() { expect: prompt_unix }, { client: client_unix, send: 'blah()', expect: '1\n' + prompt_unix }, + // Functions should not evaluate twice (#2773) + { client: client_unix, send: 'var I = [1,2,3,function() {}]; I.pop()', + expect: '[Function]' }, // Multiline object { client: client_unix, send: '{ a: ', expect: prompt_multiline }, diff --git a/test/simple/test-require-exceptions.js b/test/simple/test-require-exceptions.js index 16280016db..41f130acc8 100644 --- a/test/simple/test-require-exceptions.js +++ b/test/simple/test-require-exceptions.js @@ -31,3 +31,12 @@ assert.throws(function() { assert.throws(function() { require(common.fixturesDir + '/throws_error'); }); + +// Requiring a module that does not exist should throw an +// error with its `code` set to MODULE_NOT_FOUND +assert.throws(function () { + require(common.fixturesDir + '/DOES_NOT_EXIST'); +}, function (e) { + assert.equal('MODULE_NOT_FOUND', e.code); + return true; +}); diff --git a/test/simple/test-stdin-resume-pause.js b/test/simple/test-stdin-resume-pause.js new file mode 100644 index 0000000000..2d2af2fc66 --- /dev/null +++ b/test/simple/test-stdin-resume-pause.js @@ -0,0 +1,23 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +process.stdin.resume(); +process.stdin.pause(); diff --git a/test/simple/test-stream-pipe-cleanup.js b/test/simple/test-stream-pipe-cleanup.js index c2df97019b..aa504c4164 100644 --- a/test/simple/test-stream-pipe-cleanup.js +++ b/test/simple/test-stream-pipe-cleanup.js @@ -77,16 +77,6 @@ assert.equal(limit, w.endCalls); w.endCalls = 0; -var r2; -r = new Readable(); -r2 = new Readable(); - -r.pipe(w); -r2.pipe(w); -r.emit('close'); -r2.emit('close'); -assert.equal(1, w.endCalls); - r = new Readable(); for (i = 0; i < limit; i++) { diff --git a/test/simple/test-sys.js b/test/simple/test-sys.js index cc34991789..6ea33161c5 100644 --- a/test/simple/test-sys.js +++ b/test/simple/test-sys.js @@ -31,7 +31,7 @@ assert.equal('[Function]', common.inspect(function() {})); assert.equal('undefined', common.inspect(undefined)); assert.equal('null', common.inspect(null)); assert.equal('/foo(bar\\n)?/gi', common.inspect(/foo(bar\n)?/gi)); -assert.equal('Sun, 14 Feb 2010 11:48:40 GMT', +assert.equal(new Date('2010-02-14T12:48:40+01:00').toString(), common.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT'))); assert.equal("'\\n\\u0001'", common.inspect('\n\u0001')); diff --git a/test/simple/test-tls-client-abort.js b/test/simple/test-tls-client-abort.js index 19b69f25da..8958ceb158 100644 --- a/test/simple/test-tls-client-abort.js +++ b/test/simple/test-tls-client-abort.js @@ -33,7 +33,7 @@ var path = require('path'); var cert = fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')); var key = fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')); -var conn = tls.connect(common.PORT, {cert: cert, key: key}, function() { +var conn = tls.connect({cert: cert, key: key, port: common.PORT}, function() { assert.ok(false); // callback should never be executed }); conn.on('error', function() { diff --git a/test/simple/test-tls-client-reject.js b/test/simple/test-tls-client-reject.js new file mode 100644 index 0000000000..e11d790e60 --- /dev/null +++ b/test/simple/test-tls-client-reject.js @@ -0,0 +1,92 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); +var path = require('path'); + +var options = { + key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), + cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) +}; + +var connectCount = 0; + +var server = tls.createServer(options, function(socket) { + ++connectCount; + socket.on('data', function(data) { + common.debug(data.toString()); + assert.equal(data, 'ok'); + }); +}).listen(common.PORT, function() { + unauthorized(); +}); + +function unauthorized() { + var socket = tls.connect(common.PORT, function() { + assert(!socket.authorized); + socket.end(); + rejectUnauthorized(); + }); + socket.on('error', function(err) { + assert(false); + }); + socket.write('ok'); +} + +function rejectUnauthorized() { + var socket = tls.connect(common.PORT, { + rejectUnauthorized: true + }, function() { + assert(false); + }); + socket.on('error', function(err) { + common.debug(err); + authorized(); + }); + socket.write('ng'); +} + +function authorized() { + var socket = tls.connect(common.PORT, { + rejectUnauthorized: true, + ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ] + }, function() { + assert(socket.authorized); + socket.end(); + server.close(); + }); + socket.on('error', function(err) { + assert(false); + }); + socket.write('ok'); +} + +process.on('exit', function() { + assert.equal(connectCount, 3); +}); diff --git a/test/simple/test-tls-client-resume.js b/test/simple/test-tls-client-resume.js index 2e165596dd..5469c529fa 100644 --- a/test/simple/test-tls-client-resume.js +++ b/test/simple/test-tls-client-resume.js @@ -50,7 +50,7 @@ var server = tls.Server(options, function(socket) { server.listen(common.PORT, function() { var session1 = null; - var client1 = tls.connect(common.PORT, function() { + var client1 = tls.connect({port: common.PORT}, function() { console.log('connect1'); assert.ok(!client1.isSessionReused(), 'Session *should not* be reused.'); session1 = client1.getSession(); @@ -59,7 +59,7 @@ server.listen(common.PORT, function() { client1.on('close', function() { console.log('close1'); - var client2 = tls.connect(common.PORT, {'session': session1}, function() { + var client2 = tls.connect({'session': session1, port: common.PORT}, function() { console.log('connect2'); assert.ok(client2.isSessionReused(), 'Session *should* be reused.'); }); diff --git a/test/simple/test-tls-client-verify.js b/test/simple/test-tls-client-verify.js index 4a1b82aaab..4e5c4abfbf 100644 --- a/test/simple/test-tls-client-verify.js +++ b/test/simple/test-tls-client-verify.js @@ -100,7 +100,7 @@ function testServers(index, servers, clientOptions, cb) { var b = ''; console.error('connecting...'); - var client = tls.connect(common.PORT, clientOptions, function() { + var client = tls.connect(clientOptions, function() { console.error('expected: ' + ok + ' authed: ' + client.authorized); @@ -128,6 +128,7 @@ function runTest(testIndex) { if (!tcase) return; var clientOptions = { + port: common.PORT, ca: tcase.ca.map(loadPEM), key: loadPEM(tcase.key), cert: loadPEM(tcase.cert) diff --git a/test/simple/test-tls-connect-given-socket.js b/test/simple/test-tls-connect-given-socket.js index 9a5f7f1c03..e341dfc82d 100644 --- a/test/simple/test-tls-connect-given-socket.js +++ b/test/simple/test-tls-connect-given-socket.js @@ -39,7 +39,7 @@ var server = tls.createServer(options, function(socket) { socket.end('Hello'); }).listen(common.PORT, function() { var socket = net.connect(common.PORT, function() { - var client = tls.connect(0, {socket: socket}, function() { + var client = tls.connect({socket: socket}, function() { clientConnected = true; var data = ''; client.on('data', function(chunk) { diff --git a/test/simple/test-tls-connect-simple.js b/test/simple/test-tls-connect-simple.js index 538efd6931..6c07f4cb02 100644 --- a/test/simple/test-tls-connect-simple.js +++ b/test/simple/test-tls-connect-simple.js @@ -39,12 +39,12 @@ var server = tls.Server(options, function(socket) { }); server.listen(common.PORT, function() { - var client1 = tls.connect(common.PORT, function() { + var client1 = tls.connect({port: common.PORT}, function() { ++clientConnected; client1.end(); }); - var client2 = tls.connect(common.PORT); + var client2 = tls.connect({port: common.PORT}); client2.on('secureConnect', function() { ++clientConnected; client2.end(); diff --git a/test/simple/test-tls-connect.js b/test/simple/test-tls-connect.js index 5e5f42df36..fe2d17ff2f 100644 --- a/test/simple/test-tls-connect.js +++ b/test/simple/test-tls-connect.js @@ -42,7 +42,7 @@ var path = require('path'); assert.ok(errorEmitted); }); - var conn = tls.connect(common.PORT, {cert: cert, key: key}, function() { + var conn = tls.connect({cert: cert, key: key, port: common.PORT}, function() { assert.ok(false); // callback should never be executed }); diff --git a/test/simple/test-tls-npn-server-client.js b/test/simple/test-tls-npn-server-client.js index 95eaf16721..cf8014a50b 100644 --- a/test/simple/test-tls-npn-server-client.js +++ b/test/simple/test-tls-npn-server-client.js @@ -45,25 +45,28 @@ var serverOptions = { NPNProtocols: ['a', 'b', 'c'] }; +var serverPort = common.PORT; + var clientsOptions = [{ + port: serverPort, key: serverOptions.key, cert: serverOptions.cert, crl: serverOptions.crl, NPNProtocols: ['a', 'b', 'c'] },{ + port: serverPort, key: serverOptions.key, cert: serverOptions.cert, crl: serverOptions.crl, NPNProtocols: ['c', 'b', 'e'] },{ + port: serverPort, key: serverOptions.key, cert: serverOptions.cert, crl: serverOptions.crl, NPNProtocols: ['first-priority-unsupported', 'x', 'y'] }]; -var serverPort = common.PORT; - var serverResults = [], clientsResults = []; @@ -74,7 +77,7 @@ server.listen(serverPort, startTest); function startTest() { function connectClient(options, callback) { - var client = tls.connect(serverPort, 'localhost', options, function() { + var client = tls.connect(options, function() { clientsResults.push(client.npnProtocol); client.destroy(); diff --git a/test/simple/test-tls-passphrase.js b/test/simple/test-tls-passphrase.js index bb458bcc25..e3c0f2a849 100644 --- a/test/simple/test-tls-passphrase.js +++ b/test/simple/test-tls-passphrase.js @@ -46,7 +46,8 @@ var server = tls.Server({ var connectCount = 0; server.listen(common.PORT, function() { - var c = tls.connect(common.PORT, { + var c = tls.connect({ + port: common.PORT, key: key, passphrase: 'passphrase', cert: cert @@ -59,7 +60,8 @@ server.listen(common.PORT, function() { }); assert.throws(function() { - tls.connect(common.PORT, { + tls.connect({ + port: common.PORT, key: key, passphrase: 'invalid', cert: cert diff --git a/test/simple/test-tls-pause-close.js b/test/simple/test-tls-pause-close.js index f7f17d1238..a53d017a13 100644 --- a/test/simple/test-tls-pause-close.js +++ b/test/simple/test-tls-pause-close.js @@ -66,7 +66,7 @@ var server = tls.createServer(options, function(s) { }); server.listen(common.PORT, function() { - var c = tls.connect(common.PORT, function() { + var c = tls.connect({port: common.PORT}, function() { console.log('client connected'); c.socket.on('end', function() { console.log('client socket ended'); diff --git a/test/simple/test-tls-pause.js b/test/simple/test-tls-pause.js index 3fc30018d2..9ca3dfb2bd 100644 --- a/test/simple/test-tls-pause.js +++ b/test/simple/test-tls-pause.js @@ -45,7 +45,7 @@ var server = tls.Server(options, function(socket) { server.listen(common.PORT, function() { var resumed = false; - var client = tls.connect(common.PORT, function() { + var client = tls.connect({port: common.PORT}, function() { client.pause(); common.debug('paused'); send(); diff --git a/test/simple/test-tls-peer-certificate.js b/test/simple/test-tls-peer-certificate.js index fe498422c0..ea3245a562 100644 --- a/test/simple/test-tls-peer-certificate.js +++ b/test/simple/test-tls-peer-certificate.js @@ -42,7 +42,7 @@ var server = tls.createServer(options, function(cleartext) { cleartext.end('World'); }); server.listen(common.PORT, function() { - var socket = tls.connect(common.PORT, function() { + var socket = tls.connect({port: common.PORT}, function() { var peerCert = socket.getPeerCertificate(); common.debug(util.inspect(peerCert)); assert.equal(peerCert.subject.subjectAltName, diff --git a/test/simple/test-tls-remote.js b/test/simple/test-tls-remote.js index 2b92f6191d..9aa51ab416 100644 --- a/test/simple/test-tls-remote.js +++ b/test/simple/test-tls-remote.js @@ -48,7 +48,7 @@ server.listen(common.PORT, '127.0.0.1', function() { assert.equal(server.address().address, '127.0.0.1'); assert.equal(server.address().port, common.PORT); - var c = tls.connect(common.PORT, '127.0.0.1', function() { + var c = tls.connect({port: common.PORT, host: '127.0.0.1'}, function() { assert.equal(c.address().address, c.socket.address().address); assert.equal(c.address().port, c.socket.address().port); diff --git a/test/simple/test-tls-request-timeout.js b/test/simple/test-tls-request-timeout.js index d7ed7016d1..c44ecef3fa 100644 --- a/test/simple/test-tls-request-timeout.js +++ b/test/simple/test-tls-request-timeout.js @@ -42,7 +42,7 @@ var server = tls.Server(options, function(socket) { }); server.listen(common.PORT, function() { - var socket = tls.connect(common.PORT); + var socket = tls.connect({port: common.PORT}); }); process.on('exit', function() { diff --git a/test/simple/test-tls-set-encoding.js b/test/simple/test-tls-set-encoding.js index 556a3b4e0c..8850a677e0 100644 --- a/test/simple/test-tls-set-encoding.js +++ b/test/simple/test-tls-set-encoding.js @@ -41,7 +41,7 @@ var server = tls.Server(options, function(socket) { server.listen(common.PORT, function() { - var client = tls.connect(common.PORT); + var client = tls.connect({port: common.PORT}); var buffer = ''; diff --git a/test/simple/test-tls-sni-server-client.js b/test/simple/test-tls-sni-server-client.js index f67fec7dce..721c2c0241 100644 --- a/test/simple/test-tls-sni-server-client.js +++ b/test/simple/test-tls-sni-server-client.js @@ -57,26 +57,28 @@ var SNIContexts = { } }; +var serverPort = common.PORT; var clientsOptions = [{ + port: serverPort, key: loadPEM('agent1-key'), cert: loadPEM('agent1-cert'), ca: [loadPEM('ca1-cert')], servername: 'a.example.com' },{ + port: serverPort, key: loadPEM('agent2-key'), cert: loadPEM('agent2-cert'), ca: [loadPEM('ca2-cert')], servername: 'b.test.com' },{ + port: serverPort, key: loadPEM('agent3-key'), cert: loadPEM('agent3-cert'), ca: [loadPEM('ca1-cert')], servername: 'c.wrong.com' }]; -var serverPort = common.PORT; - var serverResults = [], clientResults = []; @@ -91,7 +93,7 @@ server.listen(serverPort, startTest); function startTest() { function connectClient(options, callback) { - var client = tls.connect(serverPort, 'localhost', options, function() { + var client = tls.connect(options, function() { clientResults.push(client.authorized); client.destroy(); diff --git a/test/simple/test-typed-arrays.js b/test/simple/test-typed-arrays.js new file mode 100644 index 0000000000..d3be0ddef2 --- /dev/null +++ b/test/simple/test-typed-arrays.js @@ -0,0 +1,110 @@ +// Copyright Joyent, Inc. and other Node contributors. + +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: + +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* + * Test to verify we are using Typed Arrays + * (http://www.khronos.org/registry/typedarray/specs/latest/) correctly Test to + * verify Buffer can used in Typed Arrays + */ + +var assert = require('assert'); +var SlowBuffer = process.binding('buffer').SlowBuffer; +var ArrayBuffer = process.binding('typed_array').ArrayBuffer; +var Int32Array = process.binding('typed_array').Int32Array; +var Int16Array = process.binding('typed_array').Int16Array; +var Uint8Array = process.binding('typed_array').Uint8Array; + +function test(clazz) { + var size = clazz.length; + var b = clazz; + + // create a view v1 referring to b, of type Int32, starting at + // the default byte index (0) and extending until the end of the buffer + var v1 = new Int32Array(b); + assert(4, v1.BYTES_PER_ELEMENT); + + // create a view v2 referring to b, of type Uint8, starting at + // byte index 2 and extending until the end of the buffer + var v2 = new Uint8Array(b, 2); + assert(1, v1.BYTES_PER_ELEMENT); + + // create a view v3 referring to b, of type Int16, starting at + // byte index 2 and having a length of 2 + var v3 = new Int16Array(b, 2, 2); + assert(2, v1.BYTES_PER_ELEMENT); + + // The layout is now + // var index + // b = |0|1|2|3|4|5|6|7| bytes (not indexable) + // v1 = |0 |1 | indices (indexable) + // v2 = |0|1|2|3|4|5| + // v3 = |0 |1 | + + // testing values + v1[0] = 0x1234; + v1[1] = 0x5678; + + assert(0x1234, v1[0]); + assert(0x5678, v1[1]); + + assert(0x3, v2[0]); + assert(0x4, v2[1]); + assert(0x5, v2[2]); + assert(0x6, v2[3]); + assert(0x7, v2[4]); + assert(0x8, v2[5]); + + assert(0x34, v3[0]); + assert(0x56, v3[1]); + + // test get/set + v2.set(1, 0x8); + v2.set(2, 0xF); + assert(0x8, v2.get(1)); + assert(0xF, v2.get(2)); + assert(0x38, v3.get(0)); + assert(0xF6, v3.get(1)); + + // test subarray + var v4 = v1.subarray(1); + assert(Int32Array, typeof v4); + assert(0xF678, v4[0]); + + // test set with typed array and [] + v2.set([ 1, 2, 3, 4 ], 2); + assert(0x1234, v1[0]); + + var sub = new Int32Array(4); + sub[0] = 0xabcd; + v2.set(sub, 1); + assert(0x3a, v3[0]); + assert(0xbc, v3[1]); +} + +// basic Typed Arrays tests +var size = 8; +var ab = new ArrayBuffer(size); +assert.equal(size, ab.byteLength); +test(ab); + +// testing sharing Buffer object +var buffer = new Buffer(size); +test(buffer); diff --git a/test/simple/test-url.js b/test/simple/test-url.js index 6a847b246d..fbbdbbbf47 100644 --- a/test/simple/test-url.js +++ b/test/simple/test-url.js @@ -476,6 +476,55 @@ var parseTests = { 'href': 'www.example.com', 'pathname': 'www.example.com', 'path': 'www.example.com' + }, + // ipv6 support + '[fe80::1]': { + 'href': '[fe80::1]', + 'pathname': '[fe80::1]', + 'path': '[fe80::1]' + }, + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + 'hostname': 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + 'href': 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + 'pathname': '/', + 'path': '/' + }, + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[1080:0:0:0:8:800:200c:417a]:61616', + 'port': '61616', + 'hostname': '1080:0:0:0:8:800:200c:417a', + 'href': 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + 'pathname': '/', + 'path': '/' + }, + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:password', + 'host': '[3ffe:2a00:100:7031::1]:8080', + 'port': '8080', + 'hostname': '3ffe:2a00:100:7031::1', + 'href': 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + 'pathname': '/', + 'path': '/' + }, + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + 'protocol': 'coap:', + 'slashes': true, + 'auth': 'u:p', + 'host': '[::192.9.5.5]:61616', + 'port': '61616', + 'hostname': '::192.9.5.5', + 'href': 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + 'search': '?n=Temperature', + 'query': 'n=Temperature', + 'pathname': '/.well-known/r', + 'path': '/.well-known/r?n=Temperature' } }; @@ -650,6 +699,22 @@ var formatTests = { 'hostname': 'foo', 'protocol': 'dot.test:', 'pathname': '/bar' + }, + // ipv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + 'href': 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + 'protocol': 'coap:', + 'auth': 'u:p', + 'hostname': '::1', + 'port': '61616', + 'pathname': '/.well-known/r', + 'search': 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + 'href': 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + 'protocol': 'coap', + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + 'pathname': '/s/stopButton' } }; for (var u in formatTests) { diff --git a/test/simple/test-zlib-dictionary.js b/test/simple/test-zlib-dictionary.js new file mode 100644 index 0000000000..f3b1446071 --- /dev/null +++ b/test/simple/test-zlib-dictionary.js @@ -0,0 +1,95 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// test compression/decompresion with dictionary + +var common = require('../common.js'); +var assert = require('assert'); +var zlib = require('zlib'); +var path = require('path'); + +var spdyDict = new Buffer([ + 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', + 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', + 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', + '-agent10010120020120220320420520630030130230330430530630740040140240340440', + '5406407408409410411412413414415416417500501502503504505accept-rangesageeta', + 'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic', + 'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran', + 'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati', + 'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo', + 'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe', + 'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic', + 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', + '.1statusversionurl\0' +].join('')); + +var deflate = zlib.createDeflate({ dictionary: spdyDict }); + +var input = [ + 'HTTP/1.1 200 Ok', + 'Server: node.js', + 'Content-Length: 0', + '' +].join('\r\n'); + +var called = 0; + +// +// We'll use clean-new inflate stream each time +// and .reset() old dirty deflate one +// +function run(num) { + var inflate = zlib.createInflate({ dictionary: spdyDict }); + + if (num === 2) { + deflate.reset(); + deflate.removeAllListeners('data'); + } + + // Put data into deflate stream + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + // Get data from inflate stream + var output = []; + inflate.on('data', function(chunk) { + output.push(chunk); + }); + inflate.on('end', function() { + called++; + + assert.equal(output.join(''), input); + + if (num < 2) run(num + 1); + }); + + deflate.write(input); + deflate.flush(function() { + inflate.end(); + }); +} +run(1); + +process.on('exit', function() { + assert.equal(called, 2); +}); diff --git a/tools/addon.gypi b/tools/addon.gypi new file mode 100644 index 0000000000..a38e7c50d2 --- /dev/null +++ b/tools/addon.gypi @@ -0,0 +1,24 @@ +{ + 'target_defaults': { + 'type': 'loadable_module', + 'product_extension': 'node', + 'product_prefix': '', + 'include_dirs': [ + '../src', + '../deps/uv/include', + '../deps/v8/include' + ], + + 'conditions': [ + [ 'OS=="mac"', { + 'libraries': [ '-undefined dynamic_lookup' ], + }], + [ 'OS=="win"', { + 'libraries': [ '-l<(node_root_dir)/$(Configuration)/node.lib' ], + }], + [ 'OS=="freebsd" or OS=="openbsd" or OS=="solaris" or (OS=="linux" and target_arch!="ia32")', { + 'cflags': [ '-fPIC' ], + }] + ] + } +} diff --git a/tools/gyp/PRESUBMIT.py b/tools/gyp/PRESUBMIT.py index 737584bc7e..146327d784 100755..100644 --- a/tools/gyp/PRESUBMIT.py +++ b/tools/gyp/PRESUBMIT.py @@ -34,6 +34,16 @@ def CheckChangeOnCommit(input_api, output_api): input_api, output_api, 'http://gyp-status.appspot.com/status', 'http://gyp-status.appspot.com/current')) + + import sys + old_sys_path = sys.path + try: + sys.path = ['pylib', 'test/lib'] + sys.path + report.extend(input_api.canned_checks.RunPylint( + input_api, + output_api)) + finally: + sys.path = old_sys_path return report diff --git a/tools/gyp/buildbot/buildbot_run.py b/tools/gyp/buildbot/buildbot_run.py index adad7f9da0..a8531258b2 100755 --- a/tools/gyp/buildbot/buildbot_run.py +++ b/tools/gyp/buildbot/buildbot_run.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -13,7 +13,45 @@ import subprocess import sys -def GypTestFormat(title, format, msvs_version=None): +if sys.platform in ['win32', 'cygwin']: + EXE_SUFFIX = '.exe' +else: + EXE_SUFFIX = '' + + +BUILDBOT_DIR = os.path.dirname(os.path.abspath(__file__)) +TRUNK_DIR = os.path.dirname(BUILDBOT_DIR) +ROOT_DIR = os.path.dirname(TRUNK_DIR) +OUT_DIR = os.path.join(TRUNK_DIR, 'out') +NINJA_PATH = os.path.join(TRUNK_DIR, 'ninja' + EXE_SUFFIX) +NINJA_WORK_DIR = os.path.join(ROOT_DIR, 'ninja_work') + + +def InstallNinja(): + """Install + build ninja. + + Returns: + 0 for success, 1 for failure. + """ + print '@@@BUILD_STEP install ninja@@@' + # Delete old version if any. + try: + shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True) + except: + pass + # Sync new copy from git. + subprocess.check_call( + 'git clone https://github.com/martine/ninja.git ' + NINJA_WORK_DIR, + shell=True) + # Bootstrap. + subprocess.check_call('./bootstrap.sh', cwd=NINJA_WORK_DIR, shell=True) + # Copy out ninja. + shutil.copyfile(os.path.join(NINJA_WORK_DIR, 'ninja' + EXE_SUFFIX), + NINJA_PATH) + os.chmod(NINJA_PATH, 0777) + + +def GypTestFormat(title, format=None, msvs_version=None): """Run the gyp tests for a given format, emitting annotator tags. See annotator docs at: @@ -23,12 +61,27 @@ def GypTestFormat(title, format, msvs_version=None): Returns: 0 for sucesss, 1 for failure. """ + if not format: + format = title + + # Install ninja if needed. + # NOTE: as ninja gets installed each time, regressions to ninja can come + # either from changes to ninja itself, or changes to gyp. + if format == 'ninja': + try: + InstallNinja() + except Exception, e: + print '@@@STEP_FAILURE@@@' + print str(e) + return 1 + print '@@@BUILD_STEP ' + title + '@@@' sys.stdout.flush() - buildbot_dir = os.path.dirname(os.path.abspath(__file__)) - trunk_dir = os.path.dirname(buildbot_dir) - root_dir = os.path.dirname(trunk_dir) env = os.environ.copy() + # TODO(bradnelson): remove this when this issue is resolved: + # http://code.google.com/p/chromium/issues/detail?id=108251 + if format == 'ninja': + env['NOGOLD'] = '1' if msvs_version: env['GYP_MSVS_VERSION'] = msvs_version retcode = subprocess.call(' '.join( @@ -38,7 +91,7 @@ def GypTestFormat(title, format, msvs_version=None): '--format', format, '--chdir', 'trunk', '--path', '../scons']), - cwd=root_dir, env=env, shell=True) + cwd=ROOT_DIR, env=env, shell=True) if retcode: # Emit failure tag, and keep going. print '@@@STEP_FAILURE@@@' @@ -49,17 +102,23 @@ def GypTestFormat(title, format, msvs_version=None): def GypBuild(): # Dump out/ directory. print '@@@BUILD_STEP cleanup@@@' - print 'Removing out/ ...' - shutil.rmtree('out', ignore_errors=True) + print 'Removing %s...' % OUT_DIR + shutil.rmtree(OUT_DIR, ignore_errors=True) + print 'Removing %s...' % NINJA_WORK_DIR + shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True) + print 'Removing %s...' % NINJA_PATH + shutil.rmtree(NINJA_PATH, ignore_errors=True) print 'Done.' retcode = 0 if sys.platform.startswith('linux'): - retcode += GypTestFormat('scons', format='scons') - retcode += GypTestFormat('make', format='make') + retcode += GypTestFormat('ninja') + retcode += GypTestFormat('scons') + retcode += GypTestFormat('make') elif sys.platform == 'darwin': - retcode += GypTestFormat('xcode', format='xcode') - retcode += GypTestFormat('make', format='make') + retcode += GypTestFormat('ninja') + retcode += GypTestFormat('xcode') + retcode += GypTestFormat('make') elif sys.platform == 'win32': retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008') if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64': diff --git a/tools/gyp/gyptest.py b/tools/gyp/gyptest.py index 0cf38b15c5..e8bf482d99 100755 --- a/tools/gyp/gyptest.py +++ b/tools/gyp/gyptest.py @@ -212,7 +212,7 @@ def main(argv=None): 'win32': ['msvs'], 'linux2': ['make', 'ninja'], 'linux3': ['make', 'ninja'], - 'darwin': ['make', 'xcode'], + 'darwin': ['make', 'ninja', 'xcode'], }[sys.platform] for format in format_list: diff --git a/tools/gyp/pylib/gyp/MSVSNew.py b/tools/gyp/pylib/gyp/MSVSNew.py index ae8cbee688..6906c7bc77 100644 --- a/tools/gyp/pylib/gyp/MSVSNew.py +++ b/tools/gyp/pylib/gyp/MSVSNew.py @@ -1,6 +1,4 @@ -#!/usr/bin/python2.4 - -# Copyright (c) 2009 Google Inc. All rights reserved. +# Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -62,7 +60,7 @@ def MakeGuid(name, seed='msvs_new'): #------------------------------------------------------------------------------ -class MSVSFolder: +class MSVSFolder(object): """Folder in a Visual Studio project or solution.""" def __init__(self, path, name = None, entries = None, @@ -103,7 +101,7 @@ class MSVSFolder: #------------------------------------------------------------------------------ -class MSVSProject: +class MSVSProject(object): """Visual Studio project.""" def __init__(self, path, name = None, dependencies = None, guid = None, diff --git a/tools/gyp/pylib/gyp/MSVSProject.py b/tools/gyp/pylib/gyp/MSVSProject.py index fab11f9d0d..4713787cd4 100644 --- a/tools/gyp/pylib/gyp/MSVSProject.py +++ b/tools/gyp/pylib/gyp/MSVSProject.py @@ -1,6 +1,4 @@ -#!/usr/bin/python2.4 - -# Copyright (c) 2009 Google Inc. All rights reserved. +# Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -208,5 +206,3 @@ class Writer(object): ] easy_xml.WriteXmlIfChanged(content, self.project_path, encoding="Windows-1252") - -#------------------------------------------------------------------------------ diff --git a/tools/gyp/pylib/gyp/MSVSSettings.py b/tools/gyp/pylib/gyp/MSVSSettings.py index 6b364826a5..bf3cc7300f 100644 --- a/tools/gyp/pylib/gyp/MSVSSettings.py +++ b/tools/gyp/pylib/gyp/MSVSSettings.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # Copyright (c) 2011 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/MSVSSettings_test.py b/tools/gyp/pylib/gyp/MSVSSettings_test.py index 199f98b1b3..49e8a1d57f 100644..100755 --- a/tools/gyp/pylib/gyp/MSVSSettings_test.py +++ b/tools/gyp/pylib/gyp/MSVSSettings_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -1476,5 +1476,6 @@ class TestSequenceFunctions(unittest.TestCase): self.assertEqual(expected_msbuild_settings, actual_msbuild_settings) self._ExpectedWarnings([]) + if __name__ == '__main__': unittest.main() diff --git a/tools/gyp/pylib/gyp/MSVSToolFile.py b/tools/gyp/pylib/gyp/MSVSToolFile.py index fcad90ad95..25d97a1798 100644 --- a/tools/gyp/pylib/gyp/MSVSToolFile.py +++ b/tools/gyp/pylib/gyp/MSVSToolFile.py @@ -1,5 +1,3 @@ -#!/usr/bin/python2.4 - # Copyright (c) 2009 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/MSVSUserFile.py b/tools/gyp/pylib/gyp/MSVSUserFile.py index 423649f634..8cc5def568 100644 --- a/tools/gyp/pylib/gyp/MSVSUserFile.py +++ b/tools/gyp/pylib/gyp/MSVSUserFile.py @@ -1,5 +1,3 @@ -#!/usr/bin/python2.4 - # Copyright (c) 2009 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/MSVSVersion.py b/tools/gyp/pylib/gyp/MSVSVersion.py index fd3e191a49..4958ee0d3d 100755..100644 --- a/tools/gyp/pylib/gyp/MSVSVersion.py +++ b/tools/gyp/pylib/gyp/MSVSVersion.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # Copyright (c) 2011 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -12,7 +11,7 @@ import subprocess import sys -class VisualStudioVersion: +class VisualStudioVersion(object): """Information regarding a version of Visual Studio.""" def __init__(self, short_name, description, @@ -80,6 +79,7 @@ def _RegistryQueryBase(sysdir, key, value): return None return text + def _RegistryQuery(key, value=None): """Use reg.exe to read a particular key through _RegistryQueryBase. @@ -107,6 +107,7 @@ def _RegistryQuery(key, value=None): raise return text + def _RegistryGetValue(key, value): """Use reg.exe to obtain the value of a registry key. @@ -125,6 +126,7 @@ def _RegistryGetValue(key, value): return None return match.group(1) + def _RegistryKeyExists(key): """Use reg.exe to see if a key exists. diff --git a/tools/gyp/pylib/gyp/SCons.py b/tools/gyp/pylib/gyp/SCons.py index 9c57bcbfc4..568645daa6 100644 --- a/tools/gyp/pylib/gyp/SCons.py +++ b/tools/gyp/pylib/gyp/SCons.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -# Copyright (c) 2009 Google Inc. All rights reserved. +# Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -196,5 +194,6 @@ TargetMap = { 'loadable_module' : LoadableModuleTarget, } + def Target(spec): return TargetMap[spec.get('type')](spec) diff --git a/tools/gyp/pylib/gyp/__init__.py b/tools/gyp/pylib/gyp/__init__.py index 36d1b996ea..1402713889 100644..100755 --- a/tools/gyp/pylib/gyp/__init__.py +++ b/tools/gyp/pylib/gyp/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,6 +11,7 @@ import os.path import re import shlex import sys +import traceback # Default debug modes for GYP debug = {} @@ -20,9 +21,18 @@ DEBUG_GENERAL = 'general' DEBUG_VARIABLES = 'variables' DEBUG_INCLUDES = 'includes' + def DebugOutput(mode, message): - if mode in gyp.debug.keys(): - print "%s: %s" % (mode.upper(), message) + if 'all' in gyp.debug.keys() or mode in gyp.debug.keys(): + ctx = ('unknown', 0, 'unknown') + try: + f = traceback.extract_stack(limit=2) + if f: + ctx = f[0][:3] + except: + pass + print '%s:%s:%d:%s %s' % (mode.upper(), os.path.basename(ctx[0]), + ctx[1], ctx[2], message) def FindBuildFiles(): extension = '.gyp' @@ -266,8 +276,8 @@ def main(args): help='set DEPTH gyp variable to a relative path to PATH') parser.add_option('-d', '--debug', dest='debug', metavar='DEBUGMODE', action='append', default=[], help='turn on a debugging ' - 'mode for debugging GYP. Supported modes are "variables" ' - 'and "general"') + 'mode for debugging GYP. Supported modes are "variables", ' + '"includes" and "general" or "all" for all of them.') parser.add_option('-S', '--suffix', dest='suffix', default='', help='suffix to add to generated files') parser.add_option('-G', dest='generator_flags', action='append', default=[], @@ -327,16 +337,12 @@ def main(args): options.formats = generate_formats else: # Nothing in the variable, default based on platform. - options.formats = [ {'darwin': 'xcode', - 'win32': 'msvs', - 'cygwin': 'msvs', - 'freebsd7': 'make', - 'freebsd8': 'make', - 'linux2': 'make', - 'linux3': 'make', - 'openbsd4': 'make', - 'openbsd5': 'make', - 'sunos5': 'make',}[sys.platform] ] + if sys.platform == 'darwin': + options.formats = ['xcode'] + elif sys.platform in ('win32', 'cygwin'): + options.formats = ['msvs'] + else: + options.formats = ['make'] if not options.generator_output and options.use_environment: g_o = os.environ.get('GYP_GENERATOR_OUTPUT') diff --git a/tools/gyp/pylib/gyp/common.py b/tools/gyp/pylib/gyp/common.py index 880820e330..97594cd5d6 100644 --- a/tools/gyp/pylib/gyp/common.py +++ b/tools/gyp/pylib/gyp/common.py @@ -1,6 +1,4 @@ -#!/usr/bin/python - -# Copyright (c) 2009 Google Inc. All rights reserved. +# Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -78,6 +76,9 @@ def ResolveTarget(build_file, target, toolset): # and os.path.join will return it as-is. build_file = os.path.normpath(os.path.join(os.path.dirname(build_file), parsed_build_file)) + # Further (to handle cases like ../cwd), make it relative to cwd) + if not os.path.isabs(build_file): + build_file = RelativePath(build_file, '.') else: build_file = parsed_build_file @@ -343,6 +344,18 @@ def WriteOnDiff(filename): return Writer() +def GetFlavor(params): + """Returns |params.flavor| if it's set, the system's default flavor else.""" + flavors = { + 'darwin': 'mac', + 'sunos5': 'solaris', + 'freebsd7': 'freebsd', + 'freebsd8': 'freebsd', + } + flavor = flavors.get(sys.platform, 'linux') + return params.get('flavor', flavor) + + # From Alex Martelli, # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 # ASPN: Python Cookbook: Remove duplicates from a sequence diff --git a/tools/gyp/pylib/gyp/easy_xml.py b/tools/gyp/pylib/gyp/easy_xml.py index 66241758d0..db54aadbee 100644 --- a/tools/gyp/pylib/gyp/easy_xml.py +++ b/tools/gyp/pylib/gyp/easy_xml.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/easy_xml_test.py b/tools/gyp/pylib/gyp/easy_xml_test.py index 9e59559818..a2aa4f20c2 100644..100755 --- a/tools/gyp/pylib/gyp/easy_xml_test.py +++ b/tools/gyp/pylib/gyp/easy_xml_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py index ebb7ff97be..1e0900a231 100644 --- a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +++ b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -8,18 +6,18 @@ import collections import gyp import gyp.common import json +import sys generator_wants_static_library_dependencies_adjusted = False generator_default_variables = { - 'OS': 'linux', } for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']: # Some gyp steps fail if these are empty(!). generator_default_variables[dirname] = 'dir' for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', - 'RULE_INPUT_EXT', + 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', @@ -29,7 +27,8 @@ for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', def CalculateVariables(default_variables, params): generator_flags = params.get('generator_flags', {}) - default_variables['OS'] = generator_flags.get('os', 'linux') + default_variables['OS'] = generator_flags.get( + 'os', gyp.common.GetFlavor(params)) def CalculateGeneratorInputInfo(params): diff --git a/tools/gyp/pylib/gyp/generator/gypd.py b/tools/gyp/pylib/gyp/generator/gypd.py index eeb8d5756e..22ef57f847 100644 --- a/tools/gyp/pylib/gyp/generator/gypd.py +++ b/tools/gyp/pylib/gyp/generator/gypd.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/generator/gypsh.py b/tools/gyp/pylib/gyp/generator/gypsh.py index 75d20f5813..bd405f43a9 100644 --- a/tools/gyp/pylib/gyp/generator/gypsh.py +++ b/tools/gyp/pylib/gyp/generator/gypsh.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py index 1007672ad8..87ad79a675 100644 --- a/tools/gyp/pylib/gyp/generator/make.py +++ b/tools/gyp/pylib/gyp/generator/make.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -26,21 +24,18 @@ import gyp import gyp.common import gyp.system_test -import os.path import os +import re +import shlex import sys -# Debugging-related imports -- remove me once we're solid. -import code -import pprint - generator_default_variables = { 'EXECUTABLE_PREFIX': '', 'EXECUTABLE_SUFFIX': '', 'STATIC_LIB_PREFIX': 'lib', 'SHARED_LIB_PREFIX': 'lib', 'STATIC_LIB_SUFFIX': '.a', - 'INTERMEDIATE_DIR': '$(obj).$(TOOLSET)/geni', + 'INTERMEDIATE_DIR': '$(obj).$(TOOLSET)/$(TARGET)/geni', 'SHARED_INTERMEDIATE_DIR': '$(obj)/gen', 'PRODUCT_DIR': '$(builddir)', 'RULE_INPUT_ROOT': '%(INPUT_ROOT)s', # This gets expanded by Python. @@ -60,25 +55,13 @@ generator_supports_multiple_toolsets = True generator_wants_sorted_dependencies = False -def GetFlavor(params): - """Returns |params.flavor| if it's set, the system's default flavor else.""" - flavors = { - 'darwin': 'mac', - 'sunos5': 'solaris', - 'freebsd7': 'freebsd', - 'freebsd8': 'freebsd', - } - flavor = flavors.get(sys.platform, 'linux') - return params.get('flavor', flavor) - - def CalculateVariables(default_variables, params): """Calculate additional variables for use in the build (called by gyp).""" cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc')) default_variables['LINKER_SUPPORTS_ICF'] = \ gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target) - flavor = GetFlavor(params) + flavor = gyp.common.GetFlavor(params) if flavor == 'mac': default_variables.setdefault('OS', 'mac') default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') @@ -452,10 +435,10 @@ $(if $(or $(command_changed),$(prereq_changed)), ) endef -# Declare "all" target first so it is the default, even though we don't have the -# deps yet. -.PHONY: all -all: +# Declare the "%(default_target)s" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: %(default_target)s +%(default_target)s: # Use FORCE_DO_CMD to force a target to run. Should be coupled with # do_cmd. @@ -489,6 +472,9 @@ cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" """ SHARED_HEADER_SUN_COMMANDS = """ @@ -901,8 +887,6 @@ class XcodeSettings(object): self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS') self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS') self._WarnUnimplemented('GCC_ENABLE_OBJC_GC') - self._WarnUnimplemented('INFOPLIST_PREPROCESS') - self._WarnUnimplemented('INFOPLIST_PREPROCESSOR_DEFINITIONS') # TODO: This is exported correctly, but assigning to it is not supported. self._WarnUnimplemented('MACH_O_TYPE') @@ -917,7 +901,7 @@ class XcodeSettings(object): config = self.spec['configurations'][self.configname] framework_dirs = config.get('mac_framework_dirs', []) for directory in framework_dirs: - cflags.append('-F ' + os.path.join(sdk_root, directory)) + cflags.append('-F ' + directory.replace('$(SDKROOT)', sdk_root)) self.configname = None return cflags @@ -1499,6 +1483,12 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD cd_action = 'cd %s; ' % Sourceify(self.path or '.') + # command and cd_action get written to a toplevel variable called + # cmd_foo. Toplevel variables can't handle things that change per + # makefile like $(TARGET), so hardcode the target. + command = command.replace('$(TARGET)', self.target) + cd_action = cd_action.replace('$(TARGET)', self.target) + # Set LD_LIBRARY_PATH in case the action runs an executable from this # build which links to shared libs from this build. # actions run on the host, so they should in theory only use host @@ -1618,6 +1608,15 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD if len(dirs) > 0: mkdirs = 'mkdir -p %s; ' % ' '.join(dirs) cd_action = 'cd %s; ' % Sourceify(self.path or '.') + + # action, cd_action, and mkdirs get written to a toplevel variable + # called cmd_foo. Toplevel variables can't handle things that change + # per makefile like $(TARGET), so hardcode the target. + action = gyp.common.EncodePOSIXShellList(action) + action = action.replace('$(TARGET)', self.target) + cd_action = cd_action.replace('$(TARGET)', self.target) + mkdirs = mkdirs.replace('$(TARGET)', self.target) + # Set LD_LIBRARY_PATH in case the rule runs an executable from this # build which links to shared libs from this build. # rules run on the host, so they should in theory only use host @@ -1629,7 +1628,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD "$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; " "export LD_LIBRARY_PATH; " "%(cd_action)s%(mkdirs)s%(action)s" % { - 'action': gyp.common.EncodePOSIXShellList(action), + 'action': action, 'cd_action': cd_action, 'count': count, 'mkdirs': mkdirs, @@ -1666,6 +1665,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD outputs = [] for copy in copies: for path in copy['files']: + # Absolutify() calls normpath, stripping trailing slashes. path = Sourceify(self.Absolutify(path)) filename = os.path.split(path)[1] output = Sourceify(self.Absolutify(os.path.join(copy['destination'], @@ -1731,10 +1731,29 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD assert ' ' not in info_plist, ( "Spaces in resource filenames not supported (%s)" % info_plist) info_plist = self.Absolutify(info_plist) + settings = self.xcode_settings + + # If explicilty set to preprocess the plist, invoke the C preprocessor and + # specify any defines as -D flags. + if settings.GetPerTargetSetting('INFOPLIST_PREPROCESS', 'NO') == 'YES': + # Create an intermediate file based on the path. + intermediate_plist = ('$(obj).$(TOOLSET)/$(TARGET)/' + + os.path.basename(info_plist)) + defines = shlex.split(settings.GetPerTargetSetting( + 'INFOPLIST_PREPROCESSOR_DEFINITIONS', '')) + self.WriteList(defines, intermediate_plist + ': INFOPLIST_DEFINES', '-D', + quoter=EscapeCppDefine) + self.WriteMakeRule([intermediate_plist], [info_plist], + ['$(call do_cmd,infoplist)', + # "Convert" the plist so that any weird whitespace changes from the + # preprocessor do not affect the XML parser in mac_tool. + '@plutil -convert xml1 $@ $@']) + info_plist = intermediate_plist + path = generator_default_variables['PRODUCT_DIR'] - dest_plist = os.path.join(path, self.xcode_settings.GetBundlePlistPath()) + dest_plist = os.path.join(path, settings.GetBundlePlistPath()) dest_plist = QuoteSpaces(dest_plist) - extra_settings = self.xcode_settings.GetPerTargetSettings() + extra_settings = settings.GetPerTargetSettings() # plists can contain envvars and substitute them into the file.. self.WriteXcodeEnv(dest_plist, spec, additional_settings=extra_settings) self.WriteDoCmd([dest_plist], [info_plist], 'mac_tool,,,copy-info-plist', @@ -1984,6 +2003,27 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD order_only = True, multiple_output_trick = False) + if self.flavor == 'mac': + # Write an envvar for postbuilds. + # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack. + # TODO(thakis): It would be nice to have some general mechanism instead. + # This variable may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE), + # so we must output its definition first, since we declare variables + # using ":=". + # TODO(thakis): Write this only for targets that actually have + # postbuilds. + strip_save_file = self.xcode_settings.GetPerTargetSetting( + 'CHROMIUM_STRIP_SAVE_FILE') + if strip_save_file: + strip_save_file = self.Absolutify(strip_save_file) + else: + # Explicitly clear this out, else a postbuild might pick up an export + # from an earlier target. + strip_save_file = '' + self.WriteXcodeEnv( + self.output, spec, + additional_settings={'CHROMIUM_STRIP_SAVE_FILE': strip_save_file}) + has_target_postbuilds = False if self.type != 'none': for configname in sorted(configs.keys()): @@ -2007,6 +2047,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD # We want to get the literal string "$ORIGIN" into the link command, # so we need lots of escaping. ldflags.append(r'-Wl,-rpath=\$$ORIGIN/lib.%s/' % self.toolset) + ldflags.append(r'-Wl,-rpath-link=\$(builddir)/lib.%s/' % + self.toolset) self.WriteList(ldflags, 'LDFLAGS_%s' % configname) libraries = spec.get('libraries') if libraries: @@ -2045,23 +2087,6 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD postbuilds.append(gyp.common.EncodePOSIXShellList(shell_list)) if postbuilds: - # Write envvars for postbuilds. - extra_settings = {} - - # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack. - # TODO(thakis): It would be nice to have some general mechanism instead. - strip_save_file = self.xcode_settings.GetPerTargetSetting( - 'CHROMIUM_STRIP_SAVE_FILE') - if strip_save_file: - strip_save_file = self.Absolutify(strip_save_file) - else: - # Explicitly clear this out, else a postbuild might pick up an export - # from an earlier target. - strip_save_file = '' - extra_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file - - self.WriteXcodeEnv(self.output, spec, additional_settings=extra_settings) - for i in xrange(len(postbuilds)): if not postbuilds[i].startswith('$'): postbuilds[i] = EscapeShellArgument(postbuilds[i]) @@ -2409,6 +2434,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD # See /Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX\ Product\ Types.xcspec for FULL_PRODUCT_NAME 'FULL_PRODUCT_NAME' : product_name, 'SRCROOT' : srcroot, + 'SOURCE_ROOT': '$(SRCROOT)', # This is not true for static libraries, but currently the env is only # written for bundles: 'TARGET_BUILD_DIR' : built_products_dir, @@ -2453,14 +2479,66 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD # GetXcodeEnv() for the full rationale. keys_to_not_absolutify = ('PRODUCT_NAME', 'FULL_PRODUCT_NAME') - # Perform some transformations that are required to mimic Xcode behavior. - for k in env: - # Values that are not strings but are, for example, lists or tuples such - # as LDFLAGS or CFLAGS, should not be written out because they are - # not needed and it's undefined how multi-valued keys should be written. - if not isinstance(env[k], str): + # First sort the list of keys, removing any non-string values. + # Values that are not strings but are, for example, lists or tuples such + # as LDFLAGS or CFLAGS, should not be written out because they are + # not needed and it's undefined how multi-valued keys should be written. + key_list = env.keys() + key_list.sort() + key_list = [k for k in key_list if isinstance(env[k], str)] + + # Since environment variables can refer to other variables, the evaluation + # order is important. Below is the logic to compute the dependency graph + # and sort it. + regex = re.compile(r'\$\(([a-zA-Z0-9\-_]+)\)') + + # Phase 1: Create a set of edges of (DEPENDEE, DEPENDER) where in the graph, + # DEPENDEE -> DEPENDER. Also create sets of dependers and dependees. + edges = set() + dependees = set() + dependers = set() + for k in key_list: + matches = regex.findall(env[k]) + if not len(matches): continue + dependers.add(k) + for dependee in matches: + if dependee in env: + edges.add((dependee, k)) + dependees.add(dependee) + + # Phase 2: Create a list of graph nodes with no incoming edges. + sorted_nodes = [] + edgeless_nodes = dependees - dependers + + # Phase 3: Perform Kahn topological sort. + while len(edgeless_nodes): + # Find a node with no incoming edges, add it to the sorted list, and + # remove it from the list of nodes that aren't part of the graph. + node = edgeless_nodes.pop() + sorted_nodes.append(node) + key_list.remove(node) + + # Find all the edges between |node| and other nodes. + edges_to_node = [e for e in edges if e[0] == node] + for edge in edges_to_node: + edges.remove(edge) + # If the node connected to |node| by |edge| has no other incoming edges, + # add it to |edgeless_nodes|. + if not len([e for e in edges if e[1] == edge[1]]): + edgeless_nodes.add(edge[1]) + + # Any remaining edges indicate a cycle. + if len(edges): + raise Exception('Xcode environment variables are cyclically dependent: ' + + str(edges)) + + # Append the "nodes" not in the graph to those that were just sorted. + sorted_nodes.extend(key_list) + + # Perform some transformations that are required to mimic Xcode behavior. + for k in sorted_nodes: # For # foo := a\ b # the escaped space does the right thing. For @@ -2498,7 +2576,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD """Convert a subdirectory-relative path into a base-relative path. Skips over paths that contain variables.""" if '$(' in path: - return path + # path is no existing file in this case, but calling normpath is still + # important for trimming trailing slashes. + return os.path.normpath(path) return os.path.normpath(os.path.join(self.path, path)) @@ -2630,10 +2710,11 @@ def CopyTool(flavor, out_path): def GenerateOutput(target_list, target_dicts, data, params): options = params['options'] - flavor = GetFlavor(params) + flavor = gyp.common.GetFlavor(params) generator_flags = params.get('generator_flags', {}) builddir_name = generator_flags.get('output_dir', 'out') android_ndk_version = generator_flags.get('android_ndk_version', None) + default_target = generator_flags.get('default_target', 'all') def CalculateMakefilePath(build_file, base_name): """Determine where to write a Makefile for a given gyp file.""" @@ -2675,6 +2756,7 @@ def GenerateOutput(target_list, target_dicts, data, params): flock_command= 'flock' header_params = { + 'default_target': default_target, 'builddir': builddir_name, 'default_configuration': default_configuration, 'flock': flock_command, @@ -2714,7 +2796,8 @@ def GenerateOutput(target_list, target_dicts, data, params): if value[0] != '$': value = '$(abspath %s)' % value if key == 'LINK': - make_global_settings += '%s ?= %s %s\n' % (flock_command, key, value) + make_global_settings += ('%s ?= %s $(builddir)/linker.lock %s\n' % + (key, flock_command, value)) elif key in ['CC', 'CXX']: make_global_settings += ( 'ifneq (,$(filter $(origin %s), undefined default))\n' % key) diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py index 0efea70470..e9ead80d6a 100644 --- a/tools/gyp/pylib/gyp/generator/msvs.py +++ b/tools/gyp/pylib/gyp/generator/msvs.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/generator/msvs_test.py b/tools/gyp/pylib/gyp/generator/msvs_test.py index 60d25abe09..5a69c1c288 100644..100755 --- a/tools/gyp/pylib/gyp/generator/msvs_test.py +++ b/tools/gyp/pylib/gyp/generator/msvs_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py index f044f3746c..193f295a55 100644 --- a/tools/gyp/pylib/gyp/generator/ninja.py +++ b/tools/gyp/pylib/gyp/generator/ninja.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -15,18 +13,15 @@ import sys import gyp.ninja_syntax as ninja_syntax generator_default_variables = { - 'OS': 'linux', - 'EXECUTABLE_PREFIX': '', 'EXECUTABLE_SUFFIX': '', 'STATIC_LIB_PREFIX': '', 'STATIC_LIB_SUFFIX': '.a', 'SHARED_LIB_PREFIX': 'lib', - 'SHARED_LIB_SUFFIX': '.so', # Gyp expects the following variables to be expandable by the build # system to the appropriate locations. Ninja prefers paths to be - # known at compile time. To resolve this, introduce special + # known at gyp time. To resolve this, introduce special # variables starting with $! (which begin with a $ so gyp knows it # should be treated as a path, but is otherwise an invalid # ninja/shell variable) that are passed to gyp here but expanded @@ -65,12 +60,6 @@ def QuoteShellArgument(arg): return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'" -def MaybeQuoteShellArgument(arg): - if '"' in arg or ' ' in arg: - return QuoteShellArgument(arg) - return arg - - def InvertRelativePath(path): """Given a relative path like foo/bar, return the inverse relative path: the path from the relative path back to the origin dir. @@ -111,7 +100,7 @@ def InvertRelativePath(path): # to the input file name as well as the output target name. class NinjaWriter: - def __init__(self, target_outputs, base_dir, build_dir, output_file): + def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor): """ base_dir: path from source root to directory containing this gyp file, by gyp semantics, all input paths are relative to this @@ -122,6 +111,7 @@ class NinjaWriter: self.base_dir = base_dir self.build_dir = build_dir self.ninja = ninja_syntax.Writer(output_file) + self.flavor = flavor # Relative path from build output dir to base dir. self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir) @@ -154,6 +144,15 @@ class NinjaWriter: return path + def ExpandRuleVariables(self, path, root, dirname, source, ext, name): + path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root) + path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'], + dirname) + path = path.replace(generator_default_variables['RULE_INPUT_PATH'], source) + path = path.replace(generator_default_variables['RULE_INPUT_EXT'], ext) + path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name) + return path + def GypPathToNinja(self, path): """Translate a gyp path to a ninja path. @@ -211,7 +210,8 @@ class NinjaWriter: def WriteSpec(self, spec, config): """The main entry point for NinjaWriter: write the build rules for a spec. - Returns the path to the build output, or None.""" + Returns the path to the build output, or None, and a list of targets for + dependencies of its compile steps.""" self.name = spec['target_name'] self.toolset = spec['toolset'] @@ -226,43 +226,56 @@ class NinjaWriter: spec['type'] = 'none' # Compute predepends for all rules. - # prebuild is the dependencies this target depends on before - # running any of its internal steps. - prebuild = [] + # actions_depends is the dependencies this target depends on before running + # any of its action/rule/copy steps. + # compile_depends is the dependencies this target depends on before running + # any of its compile steps. + actions_depends = [] + compile_depends = [] if 'dependencies' in spec: for dep in spec['dependencies']: if dep in self.target_outputs: - prebuild.append(self.target_outputs[dep][0]) - prebuild = self.WriteCollapsedDependencies('predepends', prebuild) + input, precompile_input, linkable = self.target_outputs[dep] + actions_depends.append(input) + compile_depends.extend(precompile_input) + actions_depends = self.WriteCollapsedDependencies('actions_depends', + actions_depends) # Write out actions, rules, and copies. These must happen before we # compile any sources, so compute a list of predependencies for sources # while we do it. extra_sources = [] - sources_predepends = self.WriteActionsRulesCopies(spec, extra_sources, - prebuild) + sources_depends = self.WriteActionsRulesCopies(spec, extra_sources, + actions_depends) + + # If we have actions/rules/copies, we depend directly on those, but + # otherwise we depend on dependent target's actions/rules/copies etc. + # We never need to explicitly depend on previous target's link steps, + # because no compile ever depends on them. + compile_depends = self.WriteCollapsedDependencies('compile_depends', + sources_depends or compile_depends) # Write out the compilation steps, if any. link_deps = [] sources = spec.get('sources', []) + extra_sources if sources: - link_deps = self.WriteSources(config, sources, - sources_predepends or prebuild) + link_deps = self.WriteSources(config, sources, compile_depends) # Some actions/rules output 'sources' that are already object files. link_deps += [self.GypPathToNinja(f) for f in sources if f.endswith('.o')] # The final output of our target depends on the last output of the # above steps. output = None - final_deps = link_deps or sources_predepends or prebuild + final_deps = link_deps or sources_depends or actions_depends if final_deps: - output = self.WriteTarget(spec, config, final_deps) + output = self.WriteTarget(spec, config, final_deps, + order_only=actions_depends) if self.name != output and self.toolset == 'target': # Write a short name to build this target. This benefits both the # "build chrome" case as well as the gyp tests, which expect to be # able to run actions and build libraries by their short name. self.ninja.build(self.name, 'phony', output) - return output + return output, compile_depends def WriteActionsRulesCopies(self, spec, extra_sources, prebuild): """Write out the Actions, Rules, and Copies steps. Return any outputs @@ -351,9 +364,8 @@ class NinjaWriter: # Gather the list of outputs, expanding $vars if possible. outputs = [] for output in rule['outputs']: - outputs.append(output.replace( - generator_default_variables['RULE_INPUT_ROOT'], root).replace( - generator_default_variables['RULE_INPUT_DIRNAME'], dirname)) + outputs.append(self.ExpandRuleVariables(output, root, dirname, + source, ext, basename)) if int(rule.get('process_outputs_as_sources', False)): extra_sources += outputs @@ -408,15 +420,32 @@ class NinjaWriter: self.ninja.variable('cc', '$cc_host') self.ninja.variable('cxx', '$cxx_host') + if self.flavor == 'mac': + # TODO(jeremya/thakis): Extract these from XcodeSettings instead. + cflags = [] + cflags_c = [] + cflags_cc = [] + cflags_objc = [] + cflags_objcc = [] + else: + cflags = config.get('cflags', []) + cflags_c = config.get('cflags_c', []) + cflags_cc = config.get('cflags_cc', []) + self.WriteVariableList('defines', - ['-D' + MaybeQuoteShellArgument(ninja_syntax.escape(d)) + [QuoteShellArgument(ninja_syntax.escape('-D' + d)) for d in config.get('defines', [])]) self.WriteVariableList('includes', ['-I' + self.GypPathToNinja(i) for i in config.get('include_dirs', [])]) - self.WriteVariableList('cflags', config.get('cflags')) - self.WriteVariableList('cflags_c', config.get('cflags_c')) - self.WriteVariableList('cflags_cc', config.get('cflags_cc')) + self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags)) + self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c)) + self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc)) + if self.flavor == 'mac': + self.WriteVariableList('cflags_objc', map(self.ExpandSpecial, + cflags_objc)) + self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial, + cflags_objcc)) self.ninja.newline() outputs = [] for source in sources: @@ -426,6 +455,10 @@ class NinjaWriter: command = 'cxx' elif ext in ('c', 's', 'S'): command = 'cc' + elif self.flavor == 'mac' and ext == 'm': + command = 'objc' + elif self.flavor == 'mac' and ext == 'mm': + command = 'objcxx' else: # TODO: should we assert here on unexpected extensions? continue @@ -437,7 +470,7 @@ class NinjaWriter: self.ninja.newline() return outputs - def WriteTarget(self, spec, config, final_deps): + def WriteTarget(self, spec, config, final_deps, order_only): if spec['type'] == 'none': # This target doesn't have any explicit final output, but is instead # used for its effects before the final output (e.g. copies steps). @@ -460,7 +493,7 @@ class NinjaWriter: if output_uses_linker: extra_deps = set() for dep in spec['dependencies']: - input, linkable = self.target_outputs.get(dep, (None, False)) + input, _, linkable = self.target_outputs.get(dep, (None, [], False)) if not input: continue if linkable: @@ -481,9 +514,14 @@ class NinjaWriter: command = command_map[spec['type']] if output_uses_linker: + if self.flavor == 'mac': + # TODO(jeremya/thakis): Get this from XcodeSettings. + ldflags = [] + else: + ldflags = config.get('ldflags', []) self.WriteVariableList('ldflags', gyp.common.uniquer(map(self.ExpandSpecial, - config.get('ldflags', [])))) + ldflags))) self.WriteVariableList('libs', gyp.common.uniquer(map(self.ExpandSpecial, spec.get('libraries', [])))) @@ -494,6 +532,7 @@ class NinjaWriter: self.ninja.build(output, command, final_deps, implicit=list(implicit_deps), + order_only=order_only, variables=extra_bindings) return output @@ -516,6 +555,10 @@ class NinjaWriter: 'loadable_module': 'so', 'shared_library': 'so', } + # TODO(thakis/jeremya): Remove once the mac path name computation is done + # by XcodeSettings. + if self.flavor == 'mac': + DEFAULT_EXTENSION['shared_library'] = 'dylib' extension = spec.get('product_extension', DEFAULT_EXTENSION.get(spec['type'], '')) if extension: @@ -558,6 +601,10 @@ class NinjaWriter: if self.toolset != 'target': libdir = 'lib/%s' % self.toolset return os.path.join(libdir, filename) + # TODO(thakis/jeremya): Remove once the mac path name computation is done + # by XcodeSettings. + elif spec['type'] == 'static_library' and self.flavor == 'mac': + return filename else: return self.GypPathToUniqueOutput(filename, qualified=False) @@ -601,6 +648,32 @@ def CalculateVariables(default_variables, params): default_variables['LINKER_SUPPORTS_ICF'] = \ gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target) + flavor = gyp.common.GetFlavor(params) + if flavor == 'mac': + default_variables.setdefault('OS', 'mac') + default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') + + # TODO(jeremya/thakis): Set SHARED_LIB_DIR / LIB_DIR. + + # Copy additional generator configuration data from Xcode, which is shared + # by the Mac Ninja generator. + import gyp.generator.xcode as xcode_generator + global generator_additional_non_configuration_keys + generator_additional_non_configuration_keys = getattr(xcode_generator, + 'generator_additional_non_configuration_keys', []) + global generator_additional_path_sections + generator_additional_path_sections = getattr(xcode_generator, + 'generator_additional_path_sections', []) + global generator_extra_sources_for_rules + generator_extra_sources_for_rules = getattr(xcode_generator, + 'generator_extra_sources_for_rules', []) + else: + operating_system = flavor + if flavor == 'android': + operating_system = 'linux' # Keep this legacy behavior for now. + default_variables.setdefault('OS', operating_system) + default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') + def OpenOutput(path): """Open |path| for writing, creating directories if necessary.""" @@ -613,6 +686,7 @@ def OpenOutput(path): def GenerateOutput(target_list, target_dicts, data, params): options = params['options'] + flavor = gyp.common.GetFlavor(params) generator_flags = params.get('generator_flags', {}) if options.generator_output: @@ -635,7 +709,13 @@ def GenerateOutput(target_list, target_dicts, data, params): # TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests. master_ninja.variable('cc', os.environ.get('CC', 'gcc')) master_ninja.variable('cxx', os.environ.get('CXX', 'g++')) - master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4') + # TODO(bradnelson): remove NOGOLD when this is resolved: + # http://code.google.com/p/chromium/issues/detail?id=108251 + if flavor != 'mac' and not os.environ.get('NOGOLD'): + master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4') + else: + # TODO(jeremya/thakis): flock + master_ninja.variable('ld', '$cxx') master_ninja.variable('cc_host', '$cc') master_ninja.variable('cxx_host', '$cxx') master_ninja.newline() @@ -652,25 +732,60 @@ def GenerateOutput(target_list, target_dicts, data, params): command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' '-c $in -o $out'), depfile='$out.d') - master_ninja.rule( - 'alink', - description='AR $out', - command='rm -f $out && ar rcsT $out $in') - master_ninja.rule( - 'solink', - description='SOLINK $out', - command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' - '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs')) - master_ninja.rule( - 'solink_module', - description='SOLINK(module) $out', - command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' - '-Wl,--start-group $in -Wl,--end-group $libs')) - master_ninja.rule( - 'link', - description='LINK $out', - command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib ' - '-Wl,--start-group $in -Wl,--end-group $libs')) + if flavor != 'mac': + master_ninja.rule( + 'alink', + description='AR $out', + command='rm -f $out && ar rcsT $out $in') + master_ninja.rule( + 'solink', + description='SOLINK $out', + command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' + '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs')) + master_ninja.rule( + 'solink_module', + description='SOLINK(module) $out', + command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' + '-Wl,--start-group $in -Wl,--end-group $libs')) + master_ninja.rule( + 'link', + description='LINK $out', + command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib ' + '-Wl,--start-group $in -Wl,--end-group $libs')) + else: + master_ninja.rule( + 'objc', + description='OBJC $out', + command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c ' + '$cflags_objc -c $in -o $out'), + depfile='$out.d') + master_ninja.rule( + 'objcxx', + description='OBJCXX $out', + command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' + '$cflags_objcc -c $in -o $out'), + depfile='$out.d') + master_ninja.rule( + 'alink', + description='LIBTOOL-STATIC $out', + command='rm -f $out && libtool -static -o $out $in') + # TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass + # -bundle -single_module here (for osmesa.so). + master_ninja.rule( + 'solink', + description='SOLINK $out', + command=('$ld -shared $ldflags -o $out ' + '$in $libs')) + master_ninja.rule( + 'solink_module', + description='SOLINK(module) $out', + command=('$ld -shared $ldflags -o $out ' + '$in $libs')) + master_ninja.rule( + 'link', + description='LINK $out', + command=('$ld $ldflags -o $out ' + '$in $libs')) master_ninja.rule( 'stamp', description='STAMP $out', @@ -678,7 +793,7 @@ def GenerateOutput(target_list, target_dicts, data, params): master_ninja.rule( 'copy', description='COPY $in $out', - command='ln -f $in $out 2>/dev/null || cp -af $in $out') + command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)') master_ninja.newline() all_targets = set() @@ -708,13 +823,14 @@ def GenerateOutput(target_list, target_dicts, data, params): writer = NinjaWriter(target_outputs, base_path, builddir, OpenOutput(os.path.join(options.toplevel_dir, builddir, - output_file))) + output_file)), + flavor) master_ninja.subninja(output_file) - output = writer.WriteSpec(spec, config) + output, compile_depends = writer.WriteSpec(spec, config) if output: linkable = spec['type'] in ('static_library', 'shared_library') - target_outputs[qualified_target] = (output, linkable) + target_outputs[qualified_target] = (output, compile_depends, linkable) if qualified_target in all_targets: all_outputs.add(output) diff --git a/tools/gyp/pylib/gyp/generator/scons.py b/tools/gyp/pylib/gyp/generator/scons.py index 623c6d5a9f..90418875af 100644 --- a/tools/gyp/pylib/gyp/generator/scons.py +++ b/tools/gyp/pylib/gyp/generator/scons.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/generator/xcode.py b/tools/gyp/pylib/gyp/generator/xcode.py index 066bb9f02f..8b17517b76 100644 --- a/tools/gyp/pylib/gyp/generator/xcode.py +++ b/tools/gyp/pylib/gyp/generator/xcode.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylib/gyp/input.py b/tools/gyp/pylib/gyp/input.py index 4204f63276..19a16288cd 100644 --- a/tools/gyp/pylib/gyp/input.py +++ b/tools/gyp/pylib/gyp/input.py @@ -1,6 +1,4 @@ -#!/usr/bin/python - -# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -1360,6 +1358,13 @@ class DependencyGraphNode(object): # True) and this target won't be linked. return dependencies + # Don't traverse 'none' targets if explicitly excluded. + if (target_type == 'none' and + not targets[self.ref].get('dependencies_traverse', True)): + if self.ref not in dependencies: + dependencies.append(self.ref) + return dependencies + # Executables and loadable modules are already fully and finally linked. # Nothing else can be a link dependency of them, there can only be # dependencies in the sense that a dependent target might run an diff --git a/tools/gyp/pylib/gyp/mac_tool.py b/tools/gyp/pylib/gyp/mac_tool.py index b64ea986ae..64707762d3 100644..100755 --- a/tools/gyp/pylib/gyp/mac_tool.py +++ b/tools/gyp/pylib/gyp/mac_tool.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -16,10 +16,12 @@ import string import subprocess import sys + def main(args): executor = MacTool() executor.Dispatch(args) + class MacTool(object): """This class performs all the Mac tooling steps. The methods can either be executed directly, or dispatched from an argument list.""" @@ -185,5 +187,6 @@ class MacTool(object): else: return None + if __name__ == '__main__': sys.exit(main(sys.argv[1:])) diff --git a/tools/gyp/pylib/gyp/ninja_syntax.py b/tools/gyp/pylib/gyp/ninja_syntax.py index a8735225b1..35cda05707 100644 --- a/tools/gyp/pylib/gyp/ninja_syntax.py +++ b/tools/gyp/pylib/gyp/ninja_syntax.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # This file comes from # https://github.com/martine/ninja/blob/master/misc/ninja_syntax.py # Do not edit! Edit the upstream one instead. @@ -12,6 +10,10 @@ use Python. """ import textwrap +import re + +def escape_spaces(word): + return word.replace('$ ','$$ ').replace(' ','$ ') class Writer(object): def __init__(self, output, width=78): @@ -26,29 +28,40 @@ class Writer(object): self.output.write('# ' + line + '\n') def variable(self, key, value, indent=0): + if value is None: + return + if isinstance(value, list): + value = ' '.join(value) self._line('%s = %s' % (key, value), indent) - def rule(self, name, command, description=None, depfile=None): + def rule(self, name, command, description=None, depfile=None, + generator=False): self._line('rule %s' % name) self.variable('command', command, indent=1) if description: self.variable('description', description, indent=1) if depfile: self.variable('depfile', depfile, indent=1) + if generator: + self.variable('generator', '1', indent=1) def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, variables=None): outputs = self._as_list(outputs) all_inputs = self._as_list(inputs)[:] + out_outputs = map(escape_spaces, outputs) + all_inputs = map(escape_spaces, all_inputs) if implicit: + implicit = map(escape_spaces, self._as_list(implicit)) all_inputs.append('|') - all_inputs.extend(self._as_list(implicit)) + all_inputs.extend(implicit) if order_only: + order_only = map(escape_spaces, self._as_list(order_only)) all_inputs.append('||') - all_inputs.extend(self._as_list(order_only)) + all_inputs.extend(order_only) - self._line('build %s: %s %s' % (' '.join(outputs), + self._line('build %s: %s %s' % (' '.join(out_outputs), rule, ' '.join(all_inputs))) @@ -64,28 +77,52 @@ class Writer(object): def subninja(self, path): self._line('subninja %s' % path) + def default(self, paths): + self._line('default %s' % ' '.join(self._as_list(paths))) + def _line(self, text, indent=0): """Write 'text' word-wrapped at self.width characters.""" leading_space = ' ' * indent while len(text) > self.width: # The text is too wide; wrap if possible. - # Find the rightmost space that would obey our width constraint. + self.output.write(leading_space) + available_space = self.width - len(leading_space) - len(' $') - space = text.rfind(' ', 0, available_space) - if space < 0: - # No such space; just use the first space we can find. - space = text.find(' ', available_space) - if space < 0: - # Give up on breaking. - break - self.output.write(leading_space + text[0:space] + ' $\n') - text = text[space+1:] + # Write as much as we can into this line. + done = False + written_stuff = False + while available_space > 0: + space = re.search('((\$\$)+([^$]|^)|[^$]|^) ', text) + if space: + space_idx = space.start() + 1 + else: + # No spaces left. + done = True + break + + if space_idx > available_space: + # We're out of space. + if written_stuff: + # See if we can fit it on the next line. + break + # If we haven't written anything yet on this line, don't + # try to wrap. + self.output.write(text[0:space_idx] + ' ') + written_stuff = True + text = text[space_idx+1:] + available_space -= space_idx+1 + + self.output.write('$\n') # Subsequent lines are continuations, so indent them. leading_space = ' ' * (indent+2) + if done: + # No more spaces, so bail. + break + self.output.write(leading_space + text + '\n') def _as_list(self, input): diff --git a/tools/gyp/pylib/gyp/sun_tool.py b/tools/gyp/pylib/gyp/sun_tool.py index fe5d97dcc8..90d59c8240 100644..100755 --- a/tools/gyp/pylib/gyp/sun_tool.py +++ b/tools/gyp/pylib/gyp/sun_tool.py @@ -12,58 +12,12 @@ import struct import subprocess import sys -def main(args): - executor = SunTool() - executor.Dispatch(args) - -class SunTool(object): - """This class performs all the SunOS tooling steps. The methods can either be - executed directly, or dispatched from an argument list.""" - - def Dispatch(self, args): - """Dispatches a string command to a method.""" - if len(args) < 1: - raise Exception("Not enough arguments") - - method = "Exec%s" % self._CommandifyName(args[0]) - getattr(self, method)(*args[1:]) - - def _CommandifyName(self, name_string): - """Transforms a tool name like copy-info-plist to CopyInfoPlist""" - return name_string.title().replace('-', '') - - def ExecFlock(self, lockfile, *cmd_list): - """Emulates the most basic behavior of Linux's flock(1).""" - # Rely on exception handling to report errors. - # Note that the stock python on SunOS has a bug - # where fcntl.flock(fd, LOCK_EX) always fails - # with EBADF, that's why we use this F_SETLK - # hack instead. - fd = os.open(lockfile, os.O_WRONLY|os.O_NOCTTY|os.O_CREAT, 0666) - op = struct.pack('hhllhhl', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0) - fcntl.fcntl(fd, fcntl.F_SETLK, op) - return subprocess.call(cmd_list) - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) -#!/usr/bin/env python -# Copyright (c) 2011 Google Inc. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""These functions are executed via gyp-sun-tool when using the Makefile -generator.""" - -import fcntl -import os -import struct -import subprocess -import sys def main(args): executor = SunTool() executor.Dispatch(args) + class SunTool(object): """This class performs all the SunOS tooling steps. The methods can either be executed directly, or dispatched from an argument list.""" @@ -92,5 +46,6 @@ class SunTool(object): fcntl.fcntl(fd, fcntl.F_SETLK, op) return subprocess.call(cmd_list) + if __name__ == '__main__': sys.exit(main(sys.argv[1:])) diff --git a/tools/gyp/pylib/gyp/system_test.py b/tools/gyp/pylib/gyp/system_test.py index 887d57d4f7..4245f86e11 100644..100755 --- a/tools/gyp/pylib/gyp/system_test.py +++ b/tools/gyp/pylib/gyp/system_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -9,6 +9,7 @@ import tempfile import shutil import subprocess + def TestCommands(commands, files={}, env={}): """Run commands in a temporary directory, returning true if they all succeed. Return false on failures or if any commands produce output. @@ -64,7 +65,7 @@ def TestLinkerSupportsICF(cc_command='cc'): env={'cc': cc_command}) -if __name__ == '__main__': +def main(): # Run the various test functions and print the results. def RunTest(description, function, **kwargs): print "Testing " + description + ':', @@ -75,3 +76,8 @@ if __name__ == '__main__': RunTest("ar 'T' flag", TestArSupportsT) RunTest("ar 'T' flag with ccache", TestArSupportsT, cc_command='ccache cc') RunTest("ld --threads", TestLinkerSupportsThreads) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/gyp/pylib/gyp/xcodeproj_file.py b/tools/gyp/pylib/gyp/xcodeproj_file.py index fcf9abfe47..f6ee765d59 100644 --- a/tools/gyp/pylib/gyp/xcodeproj_file.py +++ b/tools/gyp/pylib/gyp/xcodeproj_file.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2009 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -13,8 +11,8 @@ Xcode.app and observing the resultant changes in the associated project files. XCODE PROJECT FILES -The generator targets the file format as written by Xcode 3.1 (specifically, -3.1.2), but past experience has taught that the format has not changed +The generator targets the file format as written by Xcode 3.2 (specifically, +3.2.6), but past experience has taught that the format has not changed significantly in the past several years, and future versions of Xcode are able to read older project files. @@ -2444,7 +2442,7 @@ class PBXProject(XCContainerPortal): 'attributes': [0, dict, 0, 0], 'buildConfigurationList': [0, XCConfigurationList, 1, 1, XCConfigurationList()], - 'compatibilityVersion': [0, str, 0, 1, 'Xcode 3.1'], + 'compatibilityVersion': [0, str, 0, 1, 'Xcode 3.2'], 'hasScannedForEncodings': [0, int, 0, 1, 1], 'mainGroup': [0, PBXGroup, 1, 1, PBXGroup()], 'projectDirPath': [0, str, 0, 1, ''], diff --git a/tools/gyp/pylib/gyp/xml_fix.py b/tools/gyp/pylib/gyp/xml_fix.py index 20f782d283..5de848158d 100644 --- a/tools/gyp/pylib/gyp/xml_fix.py +++ b/tools/gyp/pylib/gyp/xml_fix.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/tools/gyp/pylintrc b/tools/gyp/pylintrc new file mode 100644 index 0000000000..d7c23d2a21 --- /dev/null +++ b/tools/gyp/pylintrc @@ -0,0 +1,307 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +# C0103: Invalid name "NN" (should match [a-z_][a-z0-9_]{2,30}$) +# C0111: Missing docstring +# C0302: Too many lines in module (NN) +# R0902: Too many instance attributes (N/7) +# R0903: Too few public methods (N/2) +# R0904: Too many public methods (NN/20) +# R0912: Too many branches (NN/12) +# R0913: Too many arguments (N/5) +# R0914: Too many local variables (NN/15) +# R0915: Too many statements (NN/50) +# W0141: Used builtin function 'map' +# W0142: Used * or ** magic +# W0232: Class has no __init__ method +# W0511: TODO +# W0603: Using the global statement +# +# These should be enabled eventually: +# C0112: Empty docstring +# C0301: Line too long (NN/80) +# C0321: More than one statement on single line +# C0322: Operator not preceded by a space +# C0323: Operator not followed by a space +# C0324: Comma not followed by a space +# E0101: Explicit return in __init__ +# E0102: function already defined line NN +# E1002: Use of super on an old style class +# E1101: Instance of 'XX' has no 'YY' member +# E1103: Instance of 'XX' has no 'XX' member (but some types could not be inferred) +# E0602: Undefined variable 'XX' +# F0401: Unable to import 'XX' +# R0201: Method could be a function +# R0801: Similar lines in N files +# W0102: Dangerous default value {} as argument +# W0104: Statement seems to have no effect +# W0105: String statement has no effect +# W0108: Lambda may not be necessary +# W0201: Attribute 'XX' defined outside __init__ +# W0212: Access to a protected member XX of a client class +# W0221: Arguments number differs from overridden method +# W0223: Method 'XX' is abstract in class 'YY' but is not overridden +# W0231: __init__ method from base class 'XX' is not called +# W0301: Unnecessary semicolon +# W0311: Bad indentation. Found NN spaces, expected NN +# W0401: Wildcard import XX +# W0402: Uses of a deprecated module 'string' +# W0403: Relative import 'XX', should be 'YY.XX' +# W0404: Reimport 'XX' (imported line NN) +# W0601: Global variable 'XX' undefined at the module level +# W0602: Using global for 'XX' but no assignment is done +# W0611: Unused import pprint +# W0612: Unused variable 'XX' +# W0613: Unused argument 'XX' +# W0614: Unused import XX from wildcard import +# W0621: Redefining name 'XX' from outer scope (line NN) +# W0622: Redefining built-in 'NN' +# W0631: Using possibly undefined loop variable 'XX' +# W0701: Raising a string exception +# W0702: No exception type(s) specified +disable=C0103,C0111,C0302,R0902,R0903,R0904,R0912,R0913,R0914,R0915,W0141,W0142,W0232,W0511,W0603,C0112,C0301,C0321,C0322,C0323,C0324,E0101,E0102,E1002,E1101,E1103,E0602,F0401,R0201,R0801,W0102,W0104,W0105,W0108,W0201,W0212,W0221,W0223,W0231,W0301,W0311,W0401,W0402,W0403,W0404,W0601,W0602,W0611,W0612,W0613,W0614,W0621,W0622,W0631,W0701,W0702 + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=text + +# Include message's id in output +include-ids=yes + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=no + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/tools/gyp/test/actions-bare/gyptest-bare.py b/tools/gyp/test/actions-bare/gyptest-bare.py new file mode 100755 index 0000000000..b0c10938d1 --- /dev/null +++ b/tools/gyp/test/actions-bare/gyptest-bare.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies actions which are not depended on by other targets get executed. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('bare.gyp', chdir='src') +test.relocate('src', 'relocate/src') +test.build('bare.gyp', chdir='relocate/src') + +file_content = 'Hello from bare.py\n' + +test.built_file_must_match('out.txt', file_content, chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/actions-bare/src/bare.gyp b/tools/gyp/test/actions-bare/src/bare.gyp new file mode 100644 index 0000000000..3d28f099d4 --- /dev/null +++ b/tools/gyp/test/actions-bare/src/bare.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'bare', + 'type': 'none', + 'actions': [ + { + 'action_name': 'action1', + 'inputs': [ + 'bare.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/out.txt', + ], + 'action': ['python', 'bare.py', '<(PRODUCT_DIR)/out.txt'], + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/actions-bare/src/bare.py b/tools/gyp/test/actions-bare/src/bare.py new file mode 100755 index 0000000000..12307500f2 --- /dev/null +++ b/tools/gyp/test/actions-bare/src/bare.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +f = open(sys.argv[1], 'wb') +f.write('Hello from bare.py\n') +f.close() diff --git a/tools/gyp/test/actions-multiple/gyptest-all.py b/tools/gyp/test/actions-multiple/gyptest-all.py new file mode 100755 index 0000000000..7b94fef408 --- /dev/null +++ b/tools/gyp/test/actions-multiple/gyptest-all.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies two actions can be attached to the same input files. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +# Test that two actions can be attached to the same inputs. +test.build('actions.gyp', test.ALL, chdir='relocate/src') +test.must_contain('relocate/src/output1.txt', 'hello there') +test.must_contain('relocate/src/output2.txt', 'hello there') +test.must_contain('relocate/src/output3.txt', 'hello there') +test.must_contain('relocate/src/output4.txt', 'hello there') + +# Test that process_outputs_as_sources works in conjuction with merged +# actions. +test.run_built_executable( + 'multiple_action_source_filter', + chdir='relocate/src', + stdout=( + '{\n' + 'bar\n' + 'car\n' + 'dar\n' + 'ear\n' + '}\n' + ), +) + + +test.pass_test() diff --git a/tools/gyp/test/actions-multiple/src/actions.gyp b/tools/gyp/test/actions-multiple/src/actions.gyp new file mode 100644 index 0000000000..2d721ff61a --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/actions.gyp @@ -0,0 +1,165 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + # Have a long string so that actions will exceed xp 512 character + # command limit on xp. + 'long_string': + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + 'abcdefghijklmnopqrstuvwxyz0123456789' + }, + 'targets': [ + { + 'target_name': 'multiple_action_target', + 'type': 'none', + 'actions': [ + { + 'action_name': 'action1', + 'inputs': [ + 'copy.py', + 'input.txt', + ], + 'outputs': [ + 'output1.txt', + ], + 'action': [ + 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action2', + 'inputs': [ + 'copy.py', + 'input.txt', + ], + 'outputs': [ + 'output2.txt', + ], + 'action': [ + 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action3', + 'inputs': [ + 'copy.py', + 'input.txt', + ], + 'outputs': [ + 'output3.txt', + ], + 'action': [ + 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action4', + 'inputs': [ + 'copy.py', + 'input.txt', + ], + 'outputs': [ + 'output4.txt', + ], + 'action': [ + 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + { + 'target_name': 'multiple_action_source_filter', + 'type': 'executable', + 'sources': [ + 'main.c', + # TODO(bradnelson): add foo.c here once this issue is fixed: + # http://code.google.com/p/gyp/issues/detail?id=175 + ], + 'actions': [ + { + 'action_name': 'action1', + 'inputs': [ + 'foo.c', + 'filter.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/output1.c', + ], + 'process_outputs_as_sources': 1, + 'action': [ + 'python', 'filter.py', 'foo', 'bar', 'foo.c', '<@(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action2', + 'inputs': [ + 'foo.c', + 'filter.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/output2.c', + ], + 'process_outputs_as_sources': 1, + 'action': [ + 'python', 'filter.py', 'foo', 'car', 'foo.c', '<@(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action3', + 'inputs': [ + 'foo.c', + 'filter.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/output3.c', + ], + 'process_outputs_as_sources': 1, + 'action': [ + 'python', 'filter.py', 'foo', 'dar', 'foo.c', '<@(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'action4', + 'inputs': [ + 'foo.c', + 'filter.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/output4.c', + ], + 'process_outputs_as_sources': 1, + 'action': [ + 'python', 'filter.py', 'foo', 'ear', 'foo.c', '<@(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/actions-multiple/src/copy.py b/tools/gyp/test/actions-multiple/src/copy.py new file mode 100755 index 0000000000..0774679380 --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/copy.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import shutil +import sys + +shutil.copyfile(sys.argv[1], sys.argv[2]) diff --git a/tools/gyp/test/actions-multiple/src/filter.py b/tools/gyp/test/actions-multiple/src/filter.py new file mode 100755 index 0000000000..f61a5fa59a --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/filter.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +import sys + +data = open(sys.argv[3], 'r').read() +fh = open(sys.argv[4], 'w') +fh.write(data.replace(sys.argv[1], sys.argv[2])) +fh.close() diff --git a/tools/gyp/test/actions-multiple/src/foo.c b/tools/gyp/test/actions-multiple/src/foo.c new file mode 100644 index 0000000000..23c4ef7f26 --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/foo.c @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> + +void foo(void) { + printf("foo\n"); +} diff --git a/tools/gyp/test/actions-multiple/src/input.txt b/tools/gyp/test/actions-multiple/src/input.txt new file mode 100644 index 0000000000..c7c7da3c64 --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/input.txt @@ -0,0 +1 @@ +hello there diff --git a/tools/gyp/test/actions-multiple/src/main.c b/tools/gyp/test/actions-multiple/src/main.c new file mode 100644 index 0000000000..0a420b9034 --- /dev/null +++ b/tools/gyp/test/actions-multiple/src/main.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> + +void bar(void); +void car(void); +void dar(void); +void ear(void); + +int main() { + printf("{\n"); + bar(); + car(); + dar(); + ear(); + printf("}\n"); + return 0; +} diff --git a/tools/gyp/test/actions-subdir/gyptest-action.py b/tools/gyp/test/actions-subdir/gyptest-action.py new file mode 100755 index 0000000000..09cfef1893 --- /dev/null +++ b/tools/gyp/test/actions-subdir/gyptest-action.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test actions that output to PRODUCT_DIR. +""" + +import TestGyp + +# TODO fix this for xcode: http://code.google.com/p/gyp/issues/detail?id=88 +test = TestGyp.TestGyp(formats=['!xcode']) + +test.run_gyp('none.gyp', chdir='src') + +test.build('none.gyp', test.ALL, chdir='src') + +file_content = 'Hello from make-file.py\n' +subdir_file_content = 'Hello from make-subdir-file.py\n' + +test.built_file_must_match('file.out', file_content, chdir='src') +test.built_file_must_match('subdir_file.out', subdir_file_content, chdir='src') + +test.pass_test() diff --git a/tools/gyp/test/actions-subdir/src/make-file.py b/tools/gyp/test/actions-subdir/src/make-file.py new file mode 100755 index 0000000000..74e55811d2 --- /dev/null +++ b/tools/gyp/test/actions-subdir/src/make-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = 'Hello from make-file.py\n' + +open(sys.argv[1], 'wb').write(contents) diff --git a/tools/gyp/test/actions-subdir/src/none.gyp b/tools/gyp/test/actions-subdir/src/none.gyp new file mode 100644 index 0000000000..23f8d25a53 --- /dev/null +++ b/tools/gyp/test/actions-subdir/src/none.gyp @@ -0,0 +1,31 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'file', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'actions': [ + { + 'action_name': 'make-file', + 'inputs': [ + 'make-file.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/file.out', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + } + ], + 'dependencies': [ + 'subdir/subdir.gyp:subdir_file', + ], + }, + ], +} diff --git a/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py b/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py new file mode 100755 index 0000000000..80ce19ae0e --- /dev/null +++ b/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = 'Hello from make-subdir-file.py\n' + +open(sys.argv[1], 'wb').write(contents) diff --git a/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp b/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp new file mode 100644 index 0000000000..0315d4eb83 --- /dev/null +++ b/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp @@ -0,0 +1,28 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'subdir_file', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'actions': [ + { + 'action_name': 'make-subdir-file', + 'inputs': [ + 'make-subdir-file.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/subdir_file.out', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + } + ], + }, + ], +} diff --git a/tools/gyp/test/actions/gyptest-all.py b/tools/gyp/test/actions/gyptest-all.py new file mode 100755 index 0000000000..ad04f1f281 --- /dev/null +++ b/tools/gyp/test/actions/gyptest-all.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple actions when using an explicit build target of 'all'. +""" + +import glob +import os +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_all') + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +# Some gyp files use an action that mentions an output but never +# writes it as a means to making the action run on every build. That +# doesn't mesh well with ninja's semantics. TODO(evan): figure out +# how to work always-run actions in to ninja. +if test.format == 'ninja': + test.build('actions.gyp', test.ALL, chdir='relocate/src') +else: + # Test that an "always run" action increases a counter on multiple + # invocations, and that a dependent action updates in step. + test.build('actions.gyp', test.ALL, chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '1') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '1') + test.build('actions.gyp', test.ALL, chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2') + + # The "always run" action only counts to 2, but the dependent target + # will count forever if it's allowed to run. This verifies that the + # dependent target only runs when the "always run" action generates + # new output, not just because the "always run" ran. + test.build('actions.gyp', test.ALL, chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2') + +expect = """\ +Hello from program.c +Hello from make-prog1.py +Hello from make-prog2.py +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir1' +else: + chdir = 'relocate/src' +test.run_built_executable('program', chdir=chdir, stdout=expect) + + +test.must_match('relocate/src/subdir2/file.out', "Hello from make-file.py\n") + + +expect = "Hello from generate_main.py\n" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir3' +else: + chdir = 'relocate/src' +test.run_built_executable('null_input', chdir=chdir, stdout=expect) + + +# Clean out files which may have been created if test.ALL was run. +def clean_dep_files(): + for file in (glob.glob('relocate/src/dep_*.txt') + + glob.glob('relocate/src/deps_all_done_*.txt')): + if os.path.exists(file): + os.remove(file) + +# Confirm our clean. +clean_dep_files() +test.must_not_exist('relocate/src/dep_1.txt') +test.must_not_exist('relocate/src/deps_all_done_first_123.txt') + +# Make sure all deps finish before an action is run on a 'None' target. +# If using the Make builder, add -j to make things more difficult. +arguments = [] +if test.format == 'make': + arguments = ['-j'] +test.build('actions.gyp', 'action_with_dependencies_123', chdir='relocate/src', + arguments=arguments) +test.must_exist('relocate/src/deps_all_done_first_123.txt') + +# Try again with a target that has deps in reverse. Output files from +# previous tests deleted. Confirm this execution did NOT run the ALL +# target which would mess up our dep tests. +clean_dep_files() +test.build('actions.gyp', 'action_with_dependencies_321', chdir='relocate/src', + arguments=arguments) +test.must_exist('relocate/src/deps_all_done_first_321.txt') +test.must_not_exist('relocate/src/deps_all_done_first_123.txt') + + +test.pass_test() diff --git a/tools/gyp/test/actions/gyptest-default.py b/tools/gyp/test/actions/gyptest-default.py new file mode 100755 index 0000000000..b5bf7e99d9 --- /dev/null +++ b/tools/gyp/test/actions/gyptest-default.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple actions when using the default build target. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_default') + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +# Some gyp files use an action that mentions an output but never +# writes it as a means to making the action run on every build. That +# doesn't mesh well with ninja's semantics. TODO(evan): figure out +# how to work always-run actions in to ninja. +if test.format == 'ninja': + test.build('actions.gyp', test.ALL, chdir='relocate/src') +else: + # Test that an "always run" action increases a counter on multiple + # invocations, and that a dependent action updates in step. + test.build('actions.gyp', chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '1') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '1') + test.build('actions.gyp', chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2') + + # The "always run" action only counts to 2, but the dependent target + # will count forever if it's allowed to run. This verifies that the + # dependent target only runs when the "always run" action generates + # new output, not just because the "always run" ran. + test.build('actions.gyp', test.ALL, chdir='relocate/src') + test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2') + test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2') + +expect = """\ +Hello from program.c +Hello from make-prog1.py +Hello from make-prog2.py +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir1' +else: + chdir = 'relocate/src' +test.run_built_executable('program', chdir=chdir, stdout=expect) + + +test.must_match('relocate/src/subdir2/file.out', "Hello from make-file.py\n") + + +expect = "Hello from generate_main.py\n" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir3' +else: + chdir = 'relocate/src' +test.run_built_executable('null_input', chdir=chdir, stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/actions/gyptest-errors.py b/tools/gyp/test/actions/gyptest-errors.py new file mode 100755 index 0000000000..e1ef883e1e --- /dev/null +++ b/tools/gyp/test/actions/gyptest-errors.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies behavior for different action configuration errors: +exit status of 1, and the expected error message must be in stderr. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_errors') + + +test.run_gyp('action_missing_name.gyp', chdir='src', status=1, stderr=None) +expect = [ + "Anonymous action in target broken_actions2. An action must have an 'action_name' field.", +] +test.must_contain_all_lines(test.stderr(), expect) + + +test.pass_test() diff --git a/tools/gyp/test/actions/src/action_missing_name.gyp b/tools/gyp/test/actions/src/action_missing_name.gyp new file mode 100644 index 0000000000..00424c35a1 --- /dev/null +++ b/tools/gyp/test/actions/src/action_missing_name.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'broken_actions2', + 'type': 'none', + 'actions': [ + { + 'inputs': [ + 'no_name.input', + ], + 'action': [ + 'python', + '-c', + 'print \'missing name\'', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/actions/src/actions.gyp b/tools/gyp/test/actions/src/actions.gyp new file mode 100644 index 0000000000..5d2db1955e --- /dev/null +++ b/tools/gyp/test/actions/src/actions.gyp @@ -0,0 +1,114 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_all_actions', + 'type': 'none', + 'dependencies': [ + 'subdir1/executable.gyp:*', + 'subdir2/none.gyp:*', + 'subdir3/null_input.gyp:*', + ], + }, + { + 'target_name': 'depend_on_always_run_action', + 'type': 'none', + 'dependencies': [ 'subdir1/executable.gyp:counter' ], + 'actions': [ + { + 'action_name': 'use_always_run_output', + 'inputs': [ + 'subdir1/actions-out/action-counter.txt', + 'subdir1/counter.py', + ], + 'outputs': [ + 'subdir1/actions-out/action-counter_2.txt', + ], + 'action': [ + 'python', 'subdir1/counter.py', '<(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + + # Three deps which don't finish immediately. + # Each one has a small delay then creates a file. + # Delays are 1.0, 1.1, and 2.0 seconds. + { + 'target_name': 'dep_1', + 'type': 'none', + 'actions': [{ + 'inputs': [ 'actions.gyp' ], + 'outputs': [ 'dep_1.txt' ], + 'action_name': 'dep_1', + 'action': [ 'python', '-c', + 'import time; time.sleep(1); open(\'dep_1.txt\', \'w\')' ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + { + 'target_name': 'dep_2', + 'type': 'none', + 'actions': [{ + 'inputs': [ 'actions.gyp' ], + 'outputs': [ 'dep_2.txt' ], + 'action_name': 'dep_2', + 'action': [ 'python', '-c', + 'import time; time.sleep(1.1); open(\'dep_2.txt\', \'w\')' ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + { + 'target_name': 'dep_3', + 'type': 'none', + 'actions': [{ + 'inputs': [ 'actions.gyp' ], + 'outputs': [ 'dep_3.txt' ], + 'action_name': 'dep_3', + 'action': [ 'python', '-c', + 'import time; time.sleep(2.0); open(\'dep_3.txt\', \'w\')' ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + + # An action which assumes the deps have completed. + # Does NOT list the output files of it's deps as inputs. + # On success create the file deps_all_done_first.txt. + { + 'target_name': 'action_with_dependencies_123', + 'type': 'none', + 'dependencies': [ 'dep_1', 'dep_2', 'dep_3' ], + 'actions': [{ + 'inputs': [ 'actions.gyp' ], + 'outputs': [ 'deps_all_done_first_123.txt' ], + 'action_name': 'action_with_dependencies_123', + 'action': [ 'python', 'confirm-dep-files.py', '<(_outputs)' ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + # Same as above but with deps in reverse. + { + 'target_name': 'action_with_dependencies_321', + 'type': 'none', + 'dependencies': [ 'dep_3', 'dep_2', 'dep_1' ], + 'actions': [{ + 'inputs': [ 'actions.gyp' ], + 'outputs': [ 'deps_all_done_first_321.txt' ], + 'action_name': 'action_with_dependencies_321', + 'action': [ 'python', 'confirm-dep-files.py', '<(_outputs)' ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + + ], +} diff --git a/tools/gyp/test/actions/src/confirm-dep-files.py b/tools/gyp/test/actions/src/confirm-dep-files.py new file mode 100755 index 0000000000..3b8463057d --- /dev/null +++ b/tools/gyp/test/actions/src/confirm-dep-files.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Confirms presence of files generated by our targets we depend on. +If they exist, create a new file. + +Note target's input files are explicitly NOT defined in the gyp file +so they can't easily be passed to this script as args. +""" + +import os +import sys + +outfile = sys.argv[1] # Example value we expect: deps_all_done_first_123.txt +if (os.path.exists("dep_1.txt") and + os.path.exists("dep_2.txt") and + os.path.exists("dep_3.txt")): + open(outfile, "w") diff --git a/tools/gyp/test/actions/src/subdir1/counter.py b/tools/gyp/test/actions/src/subdir1/counter.py new file mode 100755 index 0000000000..3612d7d2bf --- /dev/null +++ b/tools/gyp/test/actions/src/subdir1/counter.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys +import time + +output = sys.argv[1] +persistoutput = "%s.persist" % sys.argv[1] + +count = 0 +try: + count = open(persistoutput, 'r').read() +except: + pass +count = int(count) + 1 + +if len(sys.argv) > 2: + max_count = int(sys.argv[2]) + if count > max_count: + count = max_count + +oldcount = 0 +try: + oldcount = open(output, 'r').read() +except: + pass + +# Save the count in a file that is undeclared, and thus hidden, to gyp. We need +# to do this because, prior to running commands, scons deletes any declared +# outputs, so we would lose our count if we just wrote to the given output file. +# (The other option is to use Precious() in the scons generator, but that seems +# too heavy-handed just to support this somewhat unrealistic test case, and +# might lead to unintended side-effects). +open(persistoutput, 'w').write('%d' % (count)) + +# Only write the given output file if the count has changed. +if int(oldcount) != count: + open(output, 'w').write('%d' % (count)) + # Sleep so the next run changes the file time sufficiently to make the build + # detect the file as changed. + time.sleep(1) + +sys.exit(0) diff --git a/tools/gyp/test/actions/src/subdir1/executable.gyp b/tools/gyp/test/actions/src/subdir1/executable.gyp new file mode 100644 index 0000000000..6a1ce4f91e --- /dev/null +++ b/tools/gyp/test/actions/src/subdir1/executable.gyp @@ -0,0 +1,74 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'program.c', + ], + 'actions': [ + { + 'action_name': 'make-prog1', + 'inputs': [ + 'make-prog1.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/prog1.c', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + { + 'action_name': 'make-prog2', + 'inputs': [ + 'make-prog2.py', + ], + 'outputs': [ + 'actions-out/prog2.c', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + { + 'target_name': 'counter', + 'type': 'none', + 'actions': [ + { + # This action should always run, regardless of whether or not it's + # inputs or the command-line change. We do this by creating a dummy + # first output, which is always missing, thus causing the build to + # always try to recreate it. Actual output files should be listed + # after the dummy one, and dependent targets should list the real + # output(s) in their inputs + # (see '../actions.gyp:depend_on_always_run_action'). + 'action_name': 'action_counter', + 'inputs': [ + 'counter.py', + ], + 'outputs': [ + 'actions-out/action-counter.txt.always', + 'actions-out/action-counter.txt', + ], + 'action': [ + 'python', '<(_inputs)', 'actions-out/action-counter.txt', '2', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/actions/src/subdir1/make-prog1.py b/tools/gyp/test/actions/src/subdir1/make-prog1.py new file mode 100755 index 0000000000..7ea1d8a2d4 --- /dev/null +++ b/tools/gyp/test/actions/src/subdir1/make-prog1.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = r""" +#include <stdio.h> + +void prog1(void) +{ + printf("Hello from make-prog1.py\n"); +} +""" + +open(sys.argv[1], 'w').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/actions/src/subdir1/make-prog2.py b/tools/gyp/test/actions/src/subdir1/make-prog2.py new file mode 100755 index 0000000000..0bfe4973c2 --- /dev/null +++ b/tools/gyp/test/actions/src/subdir1/make-prog2.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = r""" +#include <stdio.h> + +void prog2(void) +{ + printf("Hello from make-prog2.py\n"); +} +""" + +open(sys.argv[1], 'w').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/actions/src/subdir1/program.c b/tools/gyp/test/actions/src/subdir1/program.c new file mode 100644 index 0000000000..d5f661d905 --- /dev/null +++ b/tools/gyp/test/actions/src/subdir1/program.c @@ -0,0 +1,12 @@ +#include <stdio.h>
+
+extern void prog1(void);
+extern void prog2(void);
+
+int main(int argc, char *argv[])
+{
+ printf("Hello from program.c\n");
+ prog1();
+ prog2();
+ return 0;
+}
diff --git a/tools/gyp/test/actions/src/subdir2/make-file.py b/tools/gyp/test/actions/src/subdir2/make-file.py new file mode 100755 index 0000000000..fff0653144 --- /dev/null +++ b/tools/gyp/test/actions/src/subdir2/make-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = "Hello from make-file.py\n" + +open(sys.argv[1], 'wb').write(contents) diff --git a/tools/gyp/test/actions/src/subdir2/none.gyp b/tools/gyp/test/actions/src/subdir2/none.gyp new file mode 100644 index 0000000000..2caa97d55c --- /dev/null +++ b/tools/gyp/test/actions/src/subdir2/none.gyp @@ -0,0 +1,33 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'file', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'actions': [ + { + 'action_name': 'make-file', + 'inputs': [ + 'make-file.py', + ], + 'outputs': [ + 'file.out', + # TODO: enhance testing infrastructure to test this + # without having to hard-code the intermediate dir paths. + #'<(INTERMEDIATE_DIR)/file.out', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + } + ], + }, + ], +} diff --git a/tools/gyp/test/actions/src/subdir3/generate_main.py b/tools/gyp/test/actions/src/subdir3/generate_main.py new file mode 100755 index 0000000000..b90b3aa6d1 --- /dev/null +++ b/tools/gyp/test/actions/src/subdir3/generate_main.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = """ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from generate_main.py\\n"); + return 0; +} +""" + +open(sys.argv[1], 'w').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/actions/src/subdir3/null_input.gyp b/tools/gyp/test/actions/src/subdir3/null_input.gyp new file mode 100644 index 0000000000..9b0bea5fdb --- /dev/null +++ b/tools/gyp/test/actions/src/subdir3/null_input.gyp @@ -0,0 +1,29 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'null_input', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'actions': [ + { + 'action_name': 'generate_main', + 'process_outputs_as_sources': 1, + 'inputs': [], + 'outputs': [ + '<(INTERMEDIATE_DIR)/main.c', + ], + 'action': [ + # TODO: we can't just use <(_outputs) here?! + 'python', 'generate_main.py', '<(INTERMEDIATE_DIR)/main.c', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/additional-targets/gyptest-additional.py b/tools/gyp/test/additional-targets/gyptest-additional.py new file mode 100755 index 0000000000..af35b33de6 --- /dev/null +++ b/tools/gyp/test/additional-targets/gyptest-additional.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple actions when using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('all.gyp', chdir='src') +test.relocate('src', 'relocate/src') + +# Build all. +test.build('all.gyp', chdir='relocate/src') + +if test.format=='xcode': + chdir = 'relocate/src/dir1' +else: + chdir = 'relocate/src' + +# Output is as expected. +file_content = 'Hello from emit.py\n' +test.built_file_must_match('out2.txt', file_content, chdir=chdir) + +test.built_file_must_not_exist('out.txt', chdir='relocate/src') +test.built_file_must_not_exist('foolib1', + type=test.SHARED_LIB, + chdir=chdir) + +# TODO(mmoss) Make consistent with scons, with 'dir1' before 'out/Default'? +if test.format in ('make', 'ninja'): + chdir='relocate/src' +else: + chdir='relocate/src/dir1' + +# Build the action explicitly. +test.build('actions.gyp', 'action1_target', chdir=chdir) + +# Check that things got run. +file_content = 'Hello from emit.py\n' +test.built_file_must_exist('out.txt', chdir=chdir) + +# Build the shared library explicitly. +test.build('actions.gyp', 'foolib1', chdir=chdir) + +test.built_file_must_exist('foolib1', + type=test.SHARED_LIB, + chdir=chdir) + +test.pass_test() diff --git a/tools/gyp/test/additional-targets/src/all.gyp b/tools/gyp/test/additional-targets/src/all.gyp new file mode 100644 index 0000000000..21c83080aa --- /dev/null +++ b/tools/gyp/test/additional-targets/src/all.gyp @@ -0,0 +1,13 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'all_targets', + 'type': 'none', + 'dependencies': ['dir1/actions.gyp:*'], + }, + ], +} diff --git a/tools/gyp/test/additional-targets/src/dir1/actions.gyp b/tools/gyp/test/additional-targets/src/dir1/actions.gyp new file mode 100644 index 0000000000..5089c80913 --- /dev/null +++ b/tools/gyp/test/additional-targets/src/dir1/actions.gyp @@ -0,0 +1,56 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'action1_target', + 'type': 'none', + 'suppress_wildcard': 1, + 'actions': [ + { + 'action_name': 'action1', + 'inputs': [ + 'emit.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/out.txt', + ], + 'action': ['python', 'emit.py', '<(PRODUCT_DIR)/out.txt'], + 'msvs_cygwin_shell': 0, + }, + ], + }, + { + 'target_name': 'action2_target', + 'type': 'none', + 'actions': [ + { + 'action_name': 'action2', + 'inputs': [ + 'emit.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/out2.txt', + ], + 'action': ['python', 'emit.py', '<(PRODUCT_DIR)/out2.txt'], + 'msvs_cygwin_shell': 0, + }, + ], + }, + { + 'target_name': 'foolib1', + 'type': 'shared_library', + 'suppress_wildcard': 1, + 'sources': ['lib1.c'], + }, + ], + 'conditions': [ + ['OS=="linux"', { + 'target_defaults': { + 'cflags': ['-fPIC'], + }, + }], + ], +} diff --git a/tools/gyp/test/additional-targets/src/dir1/emit.py b/tools/gyp/test/additional-targets/src/dir1/emit.py new file mode 100755 index 0000000000..fd3138738e --- /dev/null +++ b/tools/gyp/test/additional-targets/src/dir1/emit.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +f = open(sys.argv[1], 'wb') +f.write('Hello from emit.py\n') +f.close() diff --git a/tools/gyp/test/additional-targets/src/dir1/lib1.c b/tools/gyp/test/additional-targets/src/dir1/lib1.c new file mode 100644 index 0000000000..df4cb10f79 --- /dev/null +++ b/tools/gyp/test/additional-targets/src/dir1/lib1.c @@ -0,0 +1,6 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif +int func1(void) { + return 42; +} diff --git a/tools/gyp/test/assembly/gyptest-assembly.py b/tools/gyp/test/assembly/gyptest-assembly.py new file mode 100755 index 0000000000..09d612b945 --- /dev/null +++ b/tools/gyp/test/assembly/gyptest-assembly.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that .hpp files are ignored when included in the source list on all +platforms. +""" + +import sys +import TestGyp + +# TODO(bradnelson): get this working for windows. +test = TestGyp.TestGyp(formats=['make', 'ninja', 'scons', 'xcode']) + +test.run_gyp('assembly.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('assembly.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from program.c +Got 42. +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/assembly/src/as.bat b/tools/gyp/test/assembly/src/as.bat new file mode 100644 index 0000000000..0a47382cb7 --- /dev/null +++ b/tools/gyp/test/assembly/src/as.bat @@ -0,0 +1,4 @@ +@echo off +:: Mock windows assembler. +cl /c %1 /Fo"%2" + diff --git a/tools/gyp/test/assembly/src/assembly.gyp b/tools/gyp/test/assembly/src/assembly.gyp new file mode 100644 index 0000000000..872dd5ec0c --- /dev/null +++ b/tools/gyp/test/assembly/src/assembly.gyp @@ -0,0 +1,59 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'conditions': [ + ['OS=="win"', { + 'defines': ['PLATFORM_WIN'], + }], + ['OS=="mac"', { + 'defines': ['PLATFORM_MAC'], + }], + ['OS=="linux"', { + 'defines': ['PLATFORM_LINUX'], + }], + ], + }, + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'dependencies': ['lib1'], + 'sources': [ + 'program.c', + ], + }, + { + 'target_name': 'lib1', + 'type': 'static_library', + 'sources': [ + 'lib1.S', + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'target_defaults': { + 'rules': [ + { + 'rule_name': 'assembler', + 'msvs_cygwin_shell': 0, + 'extension': 'S', + 'inputs': [ + 'as.bat', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', + ], + 'action': + ['as.bat', 'lib1.c', '<(_outputs)'], + 'message': 'Building assembly file <(RULE_INPUT_PATH)', + 'process_outputs_as_sources': 1, + }, + ], + }, + },], + ], +} diff --git a/tools/gyp/test/assembly/src/lib1.S b/tools/gyp/test/assembly/src/lib1.S new file mode 100644 index 0000000000..e7102bf249 --- /dev/null +++ b/tools/gyp/test/assembly/src/lib1.S @@ -0,0 +1,10 @@ +#if PLATFORM_WINDOWS || PLATFORM_MAC +# define IDENTIFIER(n) _##n +#else /* Linux */ +# define IDENTIFIER(n) n +#endif + +.globl IDENTIFIER(lib1_function) +IDENTIFIER(lib1_function): + movl $42, %eax + ret diff --git a/tools/gyp/test/assembly/src/lib1.c b/tools/gyp/test/assembly/src/lib1.c new file mode 100644 index 0000000000..be21ecd5f6 --- /dev/null +++ b/tools/gyp/test/assembly/src/lib1.c @@ -0,0 +1,3 @@ +int lib1_function(void) { + return 42; +} diff --git a/tools/gyp/test/assembly/src/program.c b/tools/gyp/test/assembly/src/program.c new file mode 100644 index 0000000000..ecce3b0bbb --- /dev/null +++ b/tools/gyp/test/assembly/src/program.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +extern int lib1_function(void); + +int main(int argc, char *argv[]) +{ + fprintf(stdout, "Hello from program.c\n"); + fflush(stdout); + fprintf(stdout, "Got %d.\n", lib1_function()); + fflush(stdout); + return 0; +} diff --git a/tools/gyp/test/builddir/gyptest-all.py b/tools/gyp/test/builddir/gyptest-all.py new file mode 100755 index 0000000000..885d680bd0 --- /dev/null +++ b/tools/gyp/test/builddir/gyptest-all.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify the settings that cause a set of programs to be created in +a specific build directory, and that no intermediate built files +get created outside of that build directory hierarchy even when +referred to with deeply-nested ../../.. paths. +""" + +import TestGyp + +# TODO(mmoss): Make only supports (theoretically) a single, global build +# directory (through GYP_GENERATOR_FLAGS 'output_dir'), rather than +# gyp-file-specific settings (e.g. the stuff in builddir.gypi) that the other +# generators support, so this doesn't work yet for make. +# TODO(mmoss) Make also has the issue that the top-level Makefile is written to +# the "--depth" location, which is one level above 'src', but then this test +# moves 'src' somewhere else, leaving the Makefile behind, so make can't find +# its sources. I'm not sure if make is wrong for writing outside the current +# directory, or if the test is wrong for assuming everything generated is under +# the current directory. +test = TestGyp.TestGyp(formats=['!make', '!ninja']) + +test.run_gyp('prog1.gyp', '--depth=..', chdir='src') + +test.relocate('src', 'relocate/src') + +test.subdir('relocate/builddir') + +# Make sure that all the built ../../etc. files only get put under builddir, +# by making all of relocate read-only and then making only builddir writable. +test.writable('relocate', False) +test.writable('relocate/builddir', True) + +# Suppress the test infrastructure's setting SYMROOT on the command line. +test.build('prog1.gyp', test.ALL, SYMROOT=None, chdir='relocate/src') + +expect1 = """\ +Hello from prog1.c +Hello from func1.c +""" + +expect2 = """\ +Hello from subdir2/prog2.c +Hello from func2.c +""" + +expect3 = """\ +Hello from subdir2/subdir3/prog3.c +Hello from func3.c +""" + +expect4 = """\ +Hello from subdir2/subdir3/subdir4/prog4.c +Hello from func4.c +""" + +expect5 = """\ +Hello from subdir2/subdir3/subdir4/subdir5/prog5.c +Hello from func5.c +""" + +def run_builddir(prog, expect): + dir = 'relocate/builddir/Default/' + test.run(program=test.workpath(dir + prog), stdout=expect) + +run_builddir('prog1', expect1) +run_builddir('prog2', expect2) +run_builddir('prog3', expect3) +run_builddir('prog4', expect4) +run_builddir('prog5', expect5) + +test.pass_test() diff --git a/tools/gyp/test/builddir/gyptest-default.py b/tools/gyp/test/builddir/gyptest-default.py new file mode 100755 index 0000000000..8c6302618f --- /dev/null +++ b/tools/gyp/test/builddir/gyptest-default.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify the settings that cause a set of programs to be created in +a specific build directory, and that no intermediate built files +get created outside of that build directory hierarchy even when +referred to with deeply-nested ../../.. paths. +""" + +import TestGyp + +# TODO(mmoss): Make only supports (theoretically) a single, global build +# directory (through GYP_GENERATOR_FLAGS 'output_dir'), rather than +# gyp-file-specific settings (e.g. the stuff in builddir.gypi) that the other +# generators support, so this doesn't work yet for make. +# TODO(mmoss) Make also has the issue that the top-level Makefile is written to +# the "--depth" location, which is one level above 'src', but then this test +# moves 'src' somewhere else, leaving the Makefile behind, so make can't find +# its sources. I'm not sure if make is wrong for writing outside the current +# directory, or if the test is wrong for assuming everything generated is under +# the current directory. +test = TestGyp.TestGyp(formats=['!make', '!ninja']) + +test.run_gyp('prog1.gyp', '--depth=..', chdir='src') + +test.relocate('src', 'relocate/src') + +test.subdir('relocate/builddir') + +# Make sure that all the built ../../etc. files only get put under builddir, +# by making all of relocate read-only and then making only builddir writable. +test.writable('relocate', False) +test.writable('relocate/builddir', True) + +# Suppress the test infrastructure's setting SYMROOT on the command line. +test.build('prog1.gyp', SYMROOT=None, chdir='relocate/src') + +expect1 = """\ +Hello from prog1.c +Hello from func1.c +""" + +expect2 = """\ +Hello from subdir2/prog2.c +Hello from func2.c +""" + +expect3 = """\ +Hello from subdir2/subdir3/prog3.c +Hello from func3.c +""" + +expect4 = """\ +Hello from subdir2/subdir3/subdir4/prog4.c +Hello from func4.c +""" + +expect5 = """\ +Hello from subdir2/subdir3/subdir4/subdir5/prog5.c +Hello from func5.c +""" + +def run_builddir(prog, expect): + dir = 'relocate/builddir/Default/' + test.run(program=test.workpath(dir + prog), stdout=expect) + +run_builddir('prog1', expect1) +run_builddir('prog2', expect2) +run_builddir('prog3', expect3) +run_builddir('prog4', expect4) +run_builddir('prog5', expect5) + +test.pass_test() diff --git a/tools/gyp/test/builddir/src/builddir.gypi b/tools/gyp/test/builddir/src/builddir.gypi new file mode 100644 index 0000000000..e3c61475b5 --- /dev/null +++ b/tools/gyp/test/builddir/src/builddir.gypi @@ -0,0 +1,21 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'configurations': { + 'Default': { + 'msvs_configuration_attributes': { + 'OutputDirectory': '<(DEPTH)\\builddir\Default', + }, + }, + }, + }, + 'scons_settings': { + 'sconsbuild_dir': '<(DEPTH)/builddir', + }, + 'xcode_settings': { + 'SYMROOT': '<(DEPTH)/builddir', + }, +} diff --git a/tools/gyp/test/builddir/src/func1.c b/tools/gyp/test/builddir/src/func1.c new file mode 100644 index 0000000000..b8e6a06951 --- /dev/null +++ b/tools/gyp/test/builddir/src/func1.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func1(void) +{ + printf("Hello from func1.c\n"); +} diff --git a/tools/gyp/test/builddir/src/func2.c b/tools/gyp/test/builddir/src/func2.c new file mode 100644 index 0000000000..14aabac475 --- /dev/null +++ b/tools/gyp/test/builddir/src/func2.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func2(void) +{ + printf("Hello from func2.c\n"); +} diff --git a/tools/gyp/test/builddir/src/func3.c b/tools/gyp/test/builddir/src/func3.c new file mode 100644 index 0000000000..3b4edeae6d --- /dev/null +++ b/tools/gyp/test/builddir/src/func3.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func3(void) +{ + printf("Hello from func3.c\n"); +} diff --git a/tools/gyp/test/builddir/src/func4.c b/tools/gyp/test/builddir/src/func4.c new file mode 100644 index 0000000000..732891b79a --- /dev/null +++ b/tools/gyp/test/builddir/src/func4.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func4(void) +{ + printf("Hello from func4.c\n"); +} diff --git a/tools/gyp/test/builddir/src/func5.c b/tools/gyp/test/builddir/src/func5.c new file mode 100644 index 0000000000..18fdfabbbe --- /dev/null +++ b/tools/gyp/test/builddir/src/func5.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func5(void) +{ + printf("Hello from func5.c\n"); +} diff --git a/tools/gyp/test/builddir/src/prog1.c b/tools/gyp/test/builddir/src/prog1.c new file mode 100644 index 0000000000..674ca747b7 --- /dev/null +++ b/tools/gyp/test/builddir/src/prog1.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void func1(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + func1(); + return 0; +} diff --git a/tools/gyp/test/builddir/src/prog1.gyp b/tools/gyp/test/builddir/src/prog1.gyp new file mode 100644 index 0000000000..5b96f035ec --- /dev/null +++ b/tools/gyp/test/builddir/src/prog1.gyp @@ -0,0 +1,30 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + 'builddir.gypi', + ], + 'targets': [ + { + 'target_name': 'pull_in_all', + 'type': 'none', + 'dependencies': [ + 'prog1', + 'subdir2/prog2.gyp:prog2', + 'subdir2/subdir3/prog3.gyp:prog3', + 'subdir2/subdir3/subdir4/prog4.gyp:prog4', + 'subdir2/subdir3/subdir4/subdir5/prog5.gyp:prog5', + ], + }, + { + 'target_name': 'prog1', + 'type': 'executable', + 'sources': [ + 'prog1.c', + 'func1.c', + ], + }, + ], +} diff --git a/tools/gyp/test/builddir/src/subdir2/prog2.c b/tools/gyp/test/builddir/src/subdir2/prog2.c new file mode 100644 index 0000000000..bbdf4f0603 --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/prog2.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void func2(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from subdir2/prog2.c\n"); + func2(); + return 0; +} diff --git a/tools/gyp/test/builddir/src/subdir2/prog2.gyp b/tools/gyp/test/builddir/src/subdir2/prog2.gyp new file mode 100644 index 0000000000..96299b646d --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/prog2.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../builddir.gypi', + ], + 'targets': [ + { + 'target_name': 'prog2', + 'type': 'executable', + 'sources': [ + 'prog2.c', + '../func2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c new file mode 100644 index 0000000000..10c530b23f --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void func3(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from subdir2/subdir3/prog3.c\n"); + func3(); + return 0; +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp new file mode 100644 index 0000000000..d7df43c7bd --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../builddir.gypi', + ], + 'targets': [ + { + 'target_name': 'prog3', + 'type': 'executable', + 'sources': [ + 'prog3.c', + '../../func3.c', + ], + }, + ], +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c new file mode 100644 index 0000000000..dcba9a9d4a --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void func4(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from subdir2/subdir3/subdir4/prog4.c\n"); + func4(); + return 0; +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp new file mode 100644 index 0000000000..862a8a18cd --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../../builddir.gypi', + ], + 'targets': [ + { + 'target_name': 'prog4', + 'type': 'executable', + 'sources': [ + 'prog4.c', + '../../../func4.c', + ], + }, + ], +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c new file mode 100644 index 0000000000..69132e5763 --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void func5(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from subdir2/subdir3/subdir4/subdir5/prog5.c\n"); + func5(); + return 0; +} diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp new file mode 100644 index 0000000000..fe1c9cbf50 --- /dev/null +++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../../../builddir.gypi', + ], + 'targets': [ + { + 'target_name': 'prog5', + 'type': 'executable', + 'sources': [ + 'prog5.c', + '../../../../func5.c', + ], + }, + ], +} diff --git a/tools/gyp/test/cflags/cflags.c b/tools/gyp/test/cflags/cflags.c new file mode 100644 index 0000000000..c1e2452070 --- /dev/null +++ b/tools/gyp/test/cflags/cflags.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2010 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef __OPTIMIZE__ + printf("Using an optimization flag\n"); +#else + printf("Using no optimization flag\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/cflags/cflags.gyp b/tools/gyp/test/cflags/cflags.gyp new file mode 100644 index 0000000000..9003fb1679 --- /dev/null +++ b/tools/gyp/test/cflags/cflags.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'cflags', + 'type': 'executable', + 'opt': '-Os', + 'sources': [ + 'cflags.c', + ], + }, + ], +} diff --git a/tools/gyp/test/cflags/gyptest-cflags.py b/tools/gyp/test/cflags/gyptest-cflags.py new file mode 100755 index 0000000000..acc424a2c4 --- /dev/null +++ b/tools/gyp/test/cflags/gyptest-cflags.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ define specified by a gyp define, and +the use of the environment during regeneration when the gyp file changes. +""" + +import os +import TestGyp + +env_stack = [] + + +def PushEnv(): + env_copy = os.environ.copy() + env_stack.append(env_copy) + +def PopEnv(): + os.eniron=env_stack.pop() + +# Regenerating build files when a gyp file changes is currently only supported +# by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +try: + PushEnv() + os.environ['CFLAGS'] = '-O0' + test.run_gyp('cflags.gyp') +finally: + # We clear the environ after calling gyp. When the auto-regeneration happens, + # the same define should be reused anyway. Reset to empty string first in + # case the platform doesn't support unsetenv. + PopEnv() + +test.build('cflags.gyp') + +expect = """\ +Using no optimization flag +""" +test.run_built_executable('cflags', stdout=expect) + +test.sleep() + +try: + PushEnv() + os.environ['CFLAGS'] = '-O2' + test.run_gyp('cflags.gyp') +finally: + # We clear the environ after calling gyp. When the auto-regeneration happens, + # the same define should be reused anyway. Reset to empty string first in + # case the platform doesn't support unsetenv. + PopEnv() + +test.build('cflags.gyp') + +expect = """\ +Using an optimization flag +""" +test.run_built_executable('cflags', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/compilable/gyptest-headers.py b/tools/gyp/test/compilable/gyptest-headers.py new file mode 100755 index 0000000000..91760216fb --- /dev/null +++ b/tools/gyp/test/compilable/gyptest-headers.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that .hpp files are ignored when included in the source list on all +platforms. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('headers.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('headers.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from program.c +Hello from lib1.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/compilable/src/headers.gyp b/tools/gyp/test/compilable/src/headers.gyp new file mode 100644 index 0000000000..b6c2a8857b --- /dev/null +++ b/tools/gyp/test/compilable/src/headers.gyp @@ -0,0 +1,26 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'dependencies': [ + 'lib1' + ], + 'sources': [ + 'program.cpp', + ], + }, + { + 'target_name': 'lib1', + 'type': 'static_library', + 'sources': [ + 'lib1.hpp', + 'lib1.cpp', + ], + }, + ], +} diff --git a/tools/gyp/test/compilable/src/lib1.cpp b/tools/gyp/test/compilable/src/lib1.cpp new file mode 100644 index 0000000000..51bc31a40b --- /dev/null +++ b/tools/gyp/test/compilable/src/lib1.cpp @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "lib1.hpp" + +void lib1_function(void) { + fprintf(stdout, "Hello from lib1.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/compilable/src/lib1.hpp b/tools/gyp/test/compilable/src/lib1.hpp new file mode 100644 index 0000000000..72e63e8acd --- /dev/null +++ b/tools/gyp/test/compilable/src/lib1.hpp @@ -0,0 +1,6 @@ +#ifndef _lib1_hpp +#define _lib1_hpp + +extern void lib1_function(void); + +#endif diff --git a/tools/gyp/test/compilable/src/program.cpp b/tools/gyp/test/compilable/src/program.cpp new file mode 100644 index 0000000000..81420bad43 --- /dev/null +++ b/tools/gyp/test/compilable/src/program.cpp @@ -0,0 +1,9 @@ +#include <stdio.h> +#include "lib1.hpp" + +int main(int argc, char *argv[]) { + fprintf(stdout, "Hello from program.c\n"); + fflush(stdout); + lib1_function(); + return 0; +} diff --git a/tools/gyp/test/configurations/basics/configurations.c b/tools/gyp/test/configurations/basics/configurations.c new file mode 100644 index 0000000000..6c1f900169 --- /dev/null +++ b/tools/gyp/test/configurations/basics/configurations.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef FOO + printf("Foo configuration\n"); +#endif +#ifdef DEBUG + printf("Debug configuration\n"); +#endif +#ifdef RELEASE + printf("Release configuration\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/configurations/basics/configurations.gyp b/tools/gyp/test/configurations/basics/configurations.gyp new file mode 100644 index 0000000000..93f1d8d5c7 --- /dev/null +++ b/tools/gyp/test/configurations/basics/configurations.gyp @@ -0,0 +1,32 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'executable', + 'sources': [ + 'configurations.c', + ], + 'configurations': { + 'Debug': { + 'defines': [ + 'DEBUG', + ], + }, + 'Release': { + 'defines': [ + 'RELEASE', + ], + }, + 'Foo': { + 'defines': [ + 'FOO', + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/basics/gyptest-configurations.py b/tools/gyp/test/configurations/basics/gyptest-configurations.py new file mode 100755 index 0000000000..27cd2e87d2 --- /dev/null +++ b/tools/gyp/test/configurations/basics/gyptest-configurations.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable in three different configurations. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('configurations.gyp') + +test.set_configuration('Release') +test.build('configurations.gyp') +test.run_built_executable('configurations', stdout="Release configuration\n") + +test.set_configuration('Debug') +test.build('configurations.gyp') +test.run_built_executable('configurations', stdout="Debug configuration\n") + +test.set_configuration('Foo') +test.build('configurations.gyp') +test.run_built_executable('configurations', stdout="Foo configuration\n") + +test.pass_test() diff --git a/tools/gyp/test/configurations/inheritance/configurations.c b/tools/gyp/test/configurations/inheritance/configurations.c new file mode 100644 index 0000000000..2d5565eeb5 --- /dev/null +++ b/tools/gyp/test/configurations/inheritance/configurations.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef BASE + printf("Base configuration\n"); +#endif +#ifdef COMMON + printf("Common configuration\n"); +#endif +#ifdef COMMON2 + printf("Common2 configuration\n"); +#endif +#ifdef DEBUG + printf("Debug configuration\n"); +#endif +#ifdef RELEASE + printf("Release configuration\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/configurations/inheritance/configurations.gyp b/tools/gyp/test/configurations/inheritance/configurations.gyp new file mode 100644 index 0000000000..9441376b4d --- /dev/null +++ b/tools/gyp/test/configurations/inheritance/configurations.gyp @@ -0,0 +1,40 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'configurations': { + 'Base': { + 'abstract': 1, + 'defines': ['BASE'], + }, + 'Common': { + 'abstract': 1, + 'inherit_from': ['Base'], + 'defines': ['COMMON'], + }, + 'Common2': { + 'abstract': 1, + 'defines': ['COMMON2'], + }, + 'Debug': { + 'inherit_from': ['Common', 'Common2'], + 'defines': ['DEBUG'], + }, + 'Release': { + 'inherit_from': ['Common', 'Common2'], + 'defines': ['RELEASE'], + }, + }, + }, + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'executable', + 'sources': [ + 'configurations.c', + ], + }, + ], +} diff --git a/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py b/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py new file mode 100755 index 0000000000..22c73a3754 --- /dev/null +++ b/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable in three different configurations. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('configurations.gyp') + +test.set_configuration('Release') +test.build('configurations.gyp') +test.run_built_executable('configurations', + stdout=('Base configuration\n' + 'Common configuration\n' + 'Common2 configuration\n' + 'Release configuration\n')) + +test.set_configuration('Debug') +test.build('configurations.gyp') +test.run_built_executable('configurations', + stdout=('Base configuration\n' + 'Common configuration\n' + 'Common2 configuration\n' + 'Debug configuration\n')) + +test.pass_test() diff --git a/tools/gyp/test/configurations/invalid/actions.gyp b/tools/gyp/test/configurations/invalid/actions.gyp new file mode 100644 index 0000000000..a6e42089eb --- /dev/null +++ b/tools/gyp/test/configurations/invalid/actions.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'actions': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp b/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp new file mode 100644 index 0000000000..b16a245df5 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'all_dependent_settings': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/configurations.gyp b/tools/gyp/test/configurations/invalid/configurations.gyp new file mode 100644 index 0000000000..2cfc960049 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/configurations.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'configurations': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/dependencies.gyp b/tools/gyp/test/configurations/invalid/dependencies.gyp new file mode 100644 index 0000000000..74633f3f11 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/dependencies.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'dependencies': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp b/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp new file mode 100644 index 0000000000..8a0f2e95ea --- /dev/null +++ b/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'direct_dependent_settings': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/gyptest-configurations.py b/tools/gyp/test/configurations/invalid/gyptest-configurations.py new file mode 100755 index 0000000000..d76cdedb0c --- /dev/null +++ b/tools/gyp/test/configurations/invalid/gyptest-configurations.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable in three different configurations. +""" + +import TestGyp + +# Keys that do not belong inside a configuration dictionary. +invalid_configuration_keys = [ + 'actions', + 'all_dependent_settings', + 'configurations', + 'dependencies', + 'direct_dependent_settings', + 'libraries', + 'link_settings', + 'sources', + 'target_name', + 'type', +] + +test = TestGyp.TestGyp() + +if test.format == 'scons': + test.skip_test('TODO: http://code.google.com/p/gyp/issues/detail?id=176\n') + +for test_key in invalid_configuration_keys: + test.run_gyp('%s.gyp' % test_key, status=1, stderr=None) + expect = ['%s not allowed in the Debug configuration, found in target ' + '%s.gyp:configurations#target' % (test_key, test_key)] + test.must_contain_all_lines(test.stderr(), expect) + +test.pass_test() diff --git a/tools/gyp/test/configurations/invalid/libraries.gyp b/tools/gyp/test/configurations/invalid/libraries.gyp new file mode 100644 index 0000000000..c4014ed406 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/libraries.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'libraries': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/link_settings.gyp b/tools/gyp/test/configurations/invalid/link_settings.gyp new file mode 100644 index 0000000000..2f0e1c46f5 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/link_settings.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'link_settings': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/sources.gyp b/tools/gyp/test/configurations/invalid/sources.gyp new file mode 100644 index 0000000000..b38cca0381 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/sources.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'sources': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/target_name.gyp b/tools/gyp/test/configurations/invalid/target_name.gyp new file mode 100644 index 0000000000..83baad95d6 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/target_name.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'target_name': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/invalid/type.gyp b/tools/gyp/test/configurations/invalid/type.gyp new file mode 100644 index 0000000000..bc55898b89 --- /dev/null +++ b/tools/gyp/test/configurations/invalid/type.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'none', + 'configurations': { + 'Debug': { + 'type': [ + ], + }, + } + }, + ], +} diff --git a/tools/gyp/test/configurations/target_platform/configurations.gyp b/tools/gyp/test/configurations/target_platform/configurations.gyp new file mode 100644 index 0000000000..d15429f4e5 --- /dev/null +++ b/tools/gyp/test/configurations/target_platform/configurations.gyp @@ -0,0 +1,58 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'configurations': { + 'Debug_Win32': { + 'msvs_configuration_platform': 'Win32', + }, + 'Debug_x64': { + 'msvs_configuration_platform': 'x64', + }, + }, + }, + 'targets': [ + { + 'target_name': 'left', + 'type': 'static_library', + 'sources': [ + 'left.c', + ], + 'configurations': { + 'Debug_Win32': { + 'msvs_target_platform': 'x64', + }, + }, + }, + { + 'target_name': 'right', + 'type': 'static_library', + 'sources': [ + 'right.c', + ], + }, + { + 'target_name': 'front_left', + 'type': 'executable', + 'dependencies': ['left'], + 'sources': [ + 'front.c', + ], + 'configurations': { + 'Debug_Win32': { + 'msvs_target_platform': 'x64', + }, + }, + }, + { + 'target_name': 'front_right', + 'type': 'executable', + 'dependencies': ['right'], + 'sources': [ + 'front.c', + ], + }, + ], +} diff --git a/tools/gyp/test/configurations/target_platform/front.c b/tools/gyp/test/configurations/target_platform/front.c new file mode 100644 index 0000000000..12b1d0aa3b --- /dev/null +++ b/tools/gyp/test/configurations/target_platform/front.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +const char *message(void); + +int main(int argc, char *argv[]) { + printf("%s\n", message()); + return 0; +} diff --git a/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py b/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py new file mode 100755 index 0000000000..ae4e9e5a2d --- /dev/null +++ b/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Tests the msvs specific msvs_target_platform option. +""" + +import TestGyp +import TestCommon + + +def RunX64(exe, stdout): + try: + test.run_built_executable(exe, stdout=stdout) + except WindowsError, e: + # Assume the exe is 64-bit if it can't load on 32-bit systems. + # Both versions of the error are required because different versions + # of python seem to return different errors for invalid exe type. + if e.errno != 193 and '[Error 193]' not in str(e): + raise + + +test = TestGyp.TestGyp(formats=['msvs']) + +test.run_gyp('configurations.gyp') + +test.set_configuration('Debug|x64') +test.build('configurations.gyp', rebuild=True) +RunX64('front_left', stdout=('left\n')) +RunX64('front_right', stdout=('right\n')) + +test.set_configuration('Debug|Win32') +test.build('configurations.gyp', rebuild=True) +RunX64('front_left', stdout=('left\n')) +test.run_built_executable('front_right', stdout=('right\n')) + +test.pass_test() diff --git a/tools/gyp/test/configurations/target_platform/left.c b/tools/gyp/test/configurations/target_platform/left.c new file mode 100644 index 0000000000..1ce2ea1227 --- /dev/null +++ b/tools/gyp/test/configurations/target_platform/left.c @@ -0,0 +1,3 @@ +const char *message(void) { + return "left"; +} diff --git a/tools/gyp/test/configurations/target_platform/right.c b/tools/gyp/test/configurations/target_platform/right.c new file mode 100644 index 0000000000..b1578492fe --- /dev/null +++ b/tools/gyp/test/configurations/target_platform/right.c @@ -0,0 +1,3 @@ +const char *message(void) { + return "right"; +} diff --git a/tools/gyp/test/configurations/x64/configurations.c b/tools/gyp/test/configurations/x64/configurations.c new file mode 100644 index 0000000000..72c97e31da --- /dev/null +++ b/tools/gyp/test/configurations/x64/configurations.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) { + if (sizeof(void*) == 4) { + printf("Running Win32\n"); + } else if (sizeof(void*) == 8) { + printf("Running x64\n"); + } else { + printf("Unexpected platform\n"); + } + return 0; +} diff --git a/tools/gyp/test/configurations/x64/configurations.gyp b/tools/gyp/test/configurations/x64/configurations.gyp new file mode 100644 index 0000000000..06ffa3758b --- /dev/null +++ b/tools/gyp/test/configurations/x64/configurations.gyp @@ -0,0 +1,26 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'configurations': { + 'Debug': { + 'msvs_configuration_platform': 'Win32', + }, + 'Debug_x64': { + 'inherit_from': ['Debug'], + 'msvs_configuration_platform': 'x64', + }, + }, + }, + 'targets': [ + { + 'target_name': 'configurations', + 'type': 'executable', + 'sources': [ + 'configurations.c', + ], + }, + ], +} diff --git a/tools/gyp/test/configurations/x64/gyptest-x86.py b/tools/gyp/test/configurations/x64/gyptest-x86.py new file mode 100755 index 0000000000..254ea6fbc0 --- /dev/null +++ b/tools/gyp/test/configurations/x64/gyptest-x86.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable in three different configurations. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['msvs']) + +test.run_gyp('configurations.gyp') + +for platform in ['Win32', 'x64']: + test.set_configuration('Debug|%s' % platform) + test.build('configurations.gyp', rebuild=True) + try: + test.run_built_executable('configurations', + stdout=('Running %s\n' % platform)) + except WindowsError, e: + # Assume the exe is 64-bit if it can't load on 32-bit systems. + if platform == 'x64' and (e.errno == 193 or '[Error 193]' in str(e)): + continue + raise + +test.pass_test() diff --git a/tools/gyp/test/copies/gyptest-all.py b/tools/gyp/test/copies/gyptest-all.py new file mode 100755 index 0000000000..8542ab7b92 --- /dev/null +++ b/tools/gyp/test/copies/gyptest-all.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies file copies using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('copies.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('copies.gyp', test.ALL, chdir='relocate/src') + +test.must_match(['relocate', 'src', 'copies-out', 'file1'], 'file1 contents\n') + +test.built_file_must_match('copies-out/file2', + 'file2 contents\n', + chdir='relocate/src') + +test.built_file_must_match('copies-out/directory/file3', + 'file3 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/directory/file4', + 'file4 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/directory/subdir/file5', + 'file5 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/subdir/file6', + 'file6 contents\n', + chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/copies/gyptest-default.py b/tools/gyp/test/copies/gyptest-default.py new file mode 100755 index 0000000000..a5d1bf9c3c --- /dev/null +++ b/tools/gyp/test/copies/gyptest-default.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies file copies using the build tool default. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('copies.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('copies.gyp', chdir='relocate/src') + +test.must_match(['relocate', 'src', 'copies-out', 'file1'], 'file1 contents\n') + +test.built_file_must_match('copies-out/file2', + 'file2 contents\n', + chdir='relocate/src') + +test.built_file_must_match('copies-out/directory/file3', + 'file3 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/directory/file4', + 'file4 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/directory/subdir/file5', + 'file5 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out/subdir/file6', + 'file6 contents\n', + chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/copies/gyptest-slash.py b/tools/gyp/test/copies/gyptest-slash.py new file mode 100755 index 0000000000..81a4f42a32 --- /dev/null +++ b/tools/gyp/test/copies/gyptest-slash.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies file copies with a trailing slash in the destination directory. +""" + +import TestGyp + +test = TestGyp.TestGyp() +test.run_gyp('copies-slash.gyp', chdir='src') +test.relocate('src', 'relocate/src') +test.build('copies-slash.gyp', chdir='relocate/src') + +test.built_file_must_match('copies-out-slash/directory/file3', + 'file3 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out-slash/directory/file4', + 'file4 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out-slash/directory/subdir/file5', + 'file5 contents\n', + chdir='relocate/src') + +test.built_file_must_match('copies-out-slash-2/directory/file3', + 'file3 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out-slash-2/directory/file4', + 'file4 contents\n', + chdir='relocate/src') +test.built_file_must_match('copies-out-slash-2/directory/subdir/file5', + 'file5 contents\n', + chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/copies/src/copies-slash.gyp b/tools/gyp/test/copies/src/copies-slash.gyp new file mode 100644 index 0000000000..9bf54bd181 --- /dev/null +++ b/tools/gyp/test/copies/src/copies-slash.gyp @@ -0,0 +1,36 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + # A trailing slash on the destination directory should be ignored. + { + 'target_name': 'copies_recursive_trailing_slash', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out-slash/', + 'files': [ + 'directory/', + ], + }, + ], + }, + # Even if the source directory is below <(PRODUCT_DIR). + { + 'target_name': 'copies_recursive_trailing_slash_in_product_dir', + 'type': 'none', + 'dependencies': [ ':copies_recursive_trailing_slash' ], + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out-slash-2/', + 'files': [ + '<(PRODUCT_DIR)/copies-out-slash/directory/', + ], + }, + ], + }, + ], +} + diff --git a/tools/gyp/test/copies/src/copies.gyp b/tools/gyp/test/copies/src/copies.gyp new file mode 100644 index 0000000000..ce2e0cabca --- /dev/null +++ b/tools/gyp/test/copies/src/copies.gyp @@ -0,0 +1,70 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'copies1', + 'type': 'none', + 'copies': [ + { + 'destination': 'copies-out', + 'files': [ + 'file1', + ], + }, + ], + }, + { + 'target_name': 'copies2', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out', + 'files': [ + 'file2', + ], + }, + ], + }, + # Copy a directory tree. + { + 'target_name': 'copies_recursive', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out', + 'files': [ + 'directory/', + ], + }, + ], + }, + # Copy a directory from deeper in the tree (this should not reproduce the + # entire directory path in the destination, only the final directory). + { + 'target_name': 'copies_recursive_depth', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out', + 'files': [ + 'parentdir/subdir/', + ], + }, + ], + }, + # Verify that a null 'files' list doesn't gag the generators. + { + 'target_name': 'copies_null', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-null', + 'files': [], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/copies/src/directory/file3 b/tools/gyp/test/copies/src/directory/file3 new file mode 100644 index 0000000000..43f16f3522 --- /dev/null +++ b/tools/gyp/test/copies/src/directory/file3 @@ -0,0 +1 @@ +file3 contents diff --git a/tools/gyp/test/copies/src/directory/file4 b/tools/gyp/test/copies/src/directory/file4 new file mode 100644 index 0000000000..5f7270a084 --- /dev/null +++ b/tools/gyp/test/copies/src/directory/file4 @@ -0,0 +1 @@ +file4 contents diff --git a/tools/gyp/test/copies/src/directory/subdir/file5 b/tools/gyp/test/copies/src/directory/subdir/file5 new file mode 100644 index 0000000000..41f47186bd --- /dev/null +++ b/tools/gyp/test/copies/src/directory/subdir/file5 @@ -0,0 +1 @@ +file5 contents diff --git a/tools/gyp/test/copies/src/file1 b/tools/gyp/test/copies/src/file1 new file mode 100644 index 0000000000..84d55c5759 --- /dev/null +++ b/tools/gyp/test/copies/src/file1 @@ -0,0 +1 @@ +file1 contents diff --git a/tools/gyp/test/copies/src/file2 b/tools/gyp/test/copies/src/file2 new file mode 100644 index 0000000000..af1b8ae35d --- /dev/null +++ b/tools/gyp/test/copies/src/file2 @@ -0,0 +1 @@ +file2 contents diff --git a/tools/gyp/test/copies/src/parentdir/subdir/file6 b/tools/gyp/test/copies/src/parentdir/subdir/file6 new file mode 100644 index 0000000000..f5d5757348 --- /dev/null +++ b/tools/gyp/test/copies/src/parentdir/subdir/file6 @@ -0,0 +1 @@ +file6 contents diff --git a/tools/gyp/test/cxxflags/cxxflags.cc b/tools/gyp/test/cxxflags/cxxflags.cc new file mode 100644 index 0000000000..c1e2452070 --- /dev/null +++ b/tools/gyp/test/cxxflags/cxxflags.cc @@ -0,0 +1,15 @@ +/* Copyright (c) 2010 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef __OPTIMIZE__ + printf("Using an optimization flag\n"); +#else + printf("Using no optimization flag\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/cxxflags/cxxflags.gyp b/tools/gyp/test/cxxflags/cxxflags.gyp new file mode 100644 index 0000000000..24d883aaed --- /dev/null +++ b/tools/gyp/test/cxxflags/cxxflags.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'cxxflags', + 'type': 'executable', + 'opt': '-Os', + 'sources': [ + 'cxxflags.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/cxxflags/gyptest-cxxflags.py b/tools/gyp/test/cxxflags/gyptest-cxxflags.py new file mode 100755 index 0000000000..2e5a6d9474 --- /dev/null +++ b/tools/gyp/test/cxxflags/gyptest-cxxflags.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ define specified by a gyp define, and +the use of the environment during regeneration when the gyp file changes. +""" + +import os +import TestGyp + +env_stack = [] + + +def PushEnv(): + env_copy = os.environ.copy() + env_stack.append(env_copy) + +def PopEnv(): + os.eniron=env_stack.pop() + +# Regenerating build files when a gyp file changes is currently only supported +# by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +try: + PushEnv() + os.environ['CXXFLAGS'] = '-O0' + test.run_gyp('cxxflags.gyp') +finally: + # We clear the environ after calling gyp. When the auto-regeneration happens, + # the same define should be reused anyway. Reset to empty string first in + # case the platform doesn't support unsetenv. + PopEnv() + +test.build('cxxflags.gyp') + +expect = """\ +Using no optimization flag +""" +test.run_built_executable('cxxflags', stdout=expect) + +test.sleep() + +try: + PushEnv() + os.environ['CXXFLAGS'] = '-O2' + test.run_gyp('cxxflags.gyp') +finally: + # We clear the environ after calling gyp. When the auto-regeneration happens, + # the same define should be reused anyway. Reset to empty string first in + # case the platform doesn't support unsetenv. + PopEnv() + +test.build('cxxflags.gyp') + +expect = """\ +Using an optimization flag +""" +test.run_built_executable('cxxflags', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/defines-escaping/defines-escaping.c b/tools/gyp/test/defines-escaping/defines-escaping.c new file mode 100644 index 0000000000..440757222e --- /dev/null +++ b/tools/gyp/test/defines-escaping/defines-escaping.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2010 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf(TEST_FORMAT, TEST_ARGS); + return 0; +} diff --git a/tools/gyp/test/defines-escaping/defines-escaping.gyp b/tools/gyp/test/defines-escaping/defines-escaping.gyp new file mode 100644 index 0000000000..6f0f3fde41 --- /dev/null +++ b/tools/gyp/test/defines-escaping/defines-escaping.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'defines_escaping', + 'type': 'executable', + 'sources': [ + 'defines-escaping.c', + ], + 'defines': [ + 'TEST_FORMAT="<(test_format)"', + 'TEST_ARGS=<(test_args)', + ], + }, + ], +} diff --git a/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py b/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py new file mode 100755 index 0000000000..eb18a3d369 --- /dev/null +++ b/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ define specified by a gyp define using +various special characters such as quotes, commas, etc. +""" + +import os +import TestGyp + +test = TestGyp.TestGyp() + +# Tests string literals, percents, and backslash escapes. +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s\n' """ + r"""test_args='"Simple test of %s with a literal"'""") + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.build('defines-escaping.gyp') + +expect = """ +Simple test of %s with a literal +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# Test multiple comma-and-space-separated string literals. +try: + os.environ['GYP_DEFINES'] = \ + r"""test_format='\n%s and %s\n' test_args='"foo", "bar"'""" + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = """ +foo and bar +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# Test string literals containing quotes. +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s %s %s %s %s\n' """ + r"""test_args='"\"These,\"",""" + r""" "\"words,\"",""" + r""" "\"are,\"",""" + r""" "\"in,\"",""" + r""" "\"quotes.\""'""") + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = """ +"These," "words," "are," "in," "quotes." +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# Test string literals containing single quotes. +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s %s %s %s %s\n' """ + r"""test_args="\"'These,'\",""" + r""" \"'words,'\",""" + r""" \"'are,'\",""" + r""" \"'in,'\",""" + r""" \"'quotes.'\"" """) + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = """ +'These,' 'words,' 'are,' 'in,' 'quotes.' +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# Test string literals containing different numbers of backslashes before quotes +# (to exercise Windows' quoting behaviour). +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s\n%s\n%s\n' """ + r"""test_args='"\\\"1 visible slash\\\"",""" + r""" "\\\\\"2 visible slashes\\\\\"",""" + r""" "\\\\\\\"3 visible slashes\\\\\\\""'""") + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = r""" +\"1 visible slash\" +\\"2 visible slashes\\" +\\\"3 visible slashes\\\" +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# Test that various scary sequences are passed unfettered. +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s\n' """ + r"""test_args='"$foo, " `foo`;"'""") + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = """ +$foo, " `foo`; +""" +test.run_built_executable('defines_escaping', stdout=expect) + + +# VisualStudio 2010 can't handle passing %PATH% +if not (test.format == 'msvs' and test.uses_msbuild): + try: + os.environ['GYP_DEFINES'] = ( + """test_format='%s' """ + """test_args='"%PATH%"'""") + test.run_gyp('defines-escaping.gyp') + finally: + del os.environ['GYP_DEFINES'] + + test.sleep() + test.touch('defines-escaping.c') + test.build('defines-escaping.gyp') + + expect = "%PATH%" + test.run_built_executable('defines_escaping', stdout=expect) + + +# Test commas and semi-colons preceded by backslashes (to exercise Windows' +# quoting behaviour). +try: + os.environ['GYP_DEFINES'] = ( + r"""test_format='\n%s\n%s\n' """ + r"""test_args='"\\, \\\\;",""" + # Same thing again, but enclosed in visible quotes. + r""" "\"\\, \\\\;\""'""") + test.run_gyp('defines-escaping.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines-escaping.c') +test.build('defines-escaping.gyp') + +expect = r""" +\, \\; +"\, \\;" +""" +test.run_built_executable('defines_escaping', stdout=expect) + +# We deliberately do not test having an odd number of quotes in a string +# literal because that isn't feasible in MSVS. + +test.pass_test() diff --git a/tools/gyp/test/defines/defines-env.gyp b/tools/gyp/test/defines/defines-env.gyp new file mode 100644 index 0000000000..1781546ae0 --- /dev/null +++ b/tools/gyp/test/defines/defines-env.gyp @@ -0,0 +1,22 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'value%': '5', + }, + 'targets': [ + { + 'target_name': 'defines', + 'type': 'executable', + 'sources': [ + 'defines.c', + ], + 'defines': [ + 'VALUE=<(value)', + ], + }, + ], +} + diff --git a/tools/gyp/test/defines/defines.c b/tools/gyp/test/defines/defines.c new file mode 100644 index 0000000000..47215490b1 --- /dev/null +++ b/tools/gyp/test/defines/defines.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef FOO + printf("FOO is defined\n"); +#endif + printf("VALUE is %d\n", VALUE); + +#ifdef PAREN_VALUE + printf("2*PAREN_VALUE is %d\n", 2*PAREN_VALUE); +#endif + return 0; +} diff --git a/tools/gyp/test/defines/defines.gyp b/tools/gyp/test/defines/defines.gyp new file mode 100644 index 0000000000..25d8c41f9a --- /dev/null +++ b/tools/gyp/test/defines/defines.gyp @@ -0,0 +1,37 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'defines', + 'type': 'executable', + 'sources': [ + 'defines.c', + ], + 'defines': [ + 'FOO', + 'VALUE=1', + 'PAREN_VALUE=(1+2+3)', + ], + }, + ], + 'conditions': [ + ['OS=="fakeos"', { + 'targets': [ + { + 'target_name': 'fakeosprogram', + 'type': 'executable', + 'sources': [ + 'defines.c', + ], + 'defines': [ + 'FOO', + 'VALUE=1', + ], + }, + ], + }], + ], +} diff --git a/tools/gyp/test/defines/gyptest-define-override.py b/tools/gyp/test/defines/gyptest-define-override.py new file mode 100755 index 0000000000..82e325af2c --- /dev/null +++ b/tools/gyp/test/defines/gyptest-define-override.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a default gyp define can be overridden. +""" + +import os +import TestGyp + +test = TestGyp.TestGyp() + +# Command-line define +test.run_gyp('defines.gyp', '-D', 'OS=fakeos') +test.build('defines.gyp') +test.built_file_must_exist('fakeosprogram', type=test.EXECUTABLE) +# Clean up the exe so subsequent tests don't find an old exe. +os.remove(test.built_file_path('fakeosprogram', type=test.EXECUTABLE)) + +# Without "OS" override, fokeosprogram shouldn't be built. +test.run_gyp('defines.gyp') +test.build('defines.gyp') +test.built_file_must_not_exist('fakeosprogram', type=test.EXECUTABLE) + +# Environment define +os.environ['GYP_DEFINES'] = 'OS=fakeos' +test.run_gyp('defines.gyp') +test.build('defines.gyp') +test.built_file_must_exist('fakeosprogram', type=test.EXECUTABLE) + +test.pass_test() diff --git a/tools/gyp/test/defines/gyptest-defines-env-regyp.py b/tools/gyp/test/defines/gyptest-defines-env-regyp.py new file mode 100755 index 0000000000..57ca42b2fc --- /dev/null +++ b/tools/gyp/test/defines/gyptest-defines-env-regyp.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ define specified by a gyp define, and +the use of the environment during regeneration when the gyp file changes. +""" + +import os +import TestGyp + +# Regenerating build files when a gyp file changes is currently only supported +# by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +try: + os.environ['GYP_DEFINES'] = 'value=50' + test.run_gyp('defines.gyp') +finally: + # We clear the environ after calling gyp. When the auto-regeneration happens, + # the same define should be reused anyway. Reset to empty string first in + # case the platform doesn't support unsetenv. + os.environ['GYP_DEFINES'] = '' + del os.environ['GYP_DEFINES'] + +test.build('defines.gyp') + +expect = """\ +FOO is defined +VALUE is 1 +2*PAREN_VALUE is 12 +""" +test.run_built_executable('defines', stdout=expect) + +# Sleep so that the changed gyp file will have a newer timestamp than the +# previously generated build files. +test.sleep() +test.write('defines.gyp', test.read('defines-env.gyp')) + +test.build('defines.gyp', test.ALL) + +expect = """\ +VALUE is 50 +""" +test.run_built_executable('defines', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/defines/gyptest-defines-env.py b/tools/gyp/test/defines/gyptest-defines-env.py new file mode 100755 index 0000000000..6b4e7175a6 --- /dev/null +++ b/tools/gyp/test/defines/gyptest-defines-env.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ define specified by a gyp define. +""" + +import os +import TestGyp + +test = TestGyp.TestGyp() + +# With the value only given in environment, it should be used. +try: + os.environ['GYP_DEFINES'] = 'value=10' + test.run_gyp('defines-env.gyp') +finally: + del os.environ['GYP_DEFINES'] + +test.build('defines-env.gyp') + +expect = """\ +VALUE is 10 +""" +test.run_built_executable('defines', stdout=expect) + + +# With the value given in both command line and environment, +# command line should take precedence. +try: + os.environ['GYP_DEFINES'] = 'value=20' + test.run_gyp('defines-env.gyp', '-Dvalue=25') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines.c') +test.build('defines-env.gyp') + +expect = """\ +VALUE is 25 +""" +test.run_built_executable('defines', stdout=expect) + + +# With the value only given in environment, it should be ignored if +# --ignore-environment is specified. +try: + os.environ['GYP_DEFINES'] = 'value=30' + test.run_gyp('defines-env.gyp', '--ignore-environment') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines.c') +test.build('defines-env.gyp') + +expect = """\ +VALUE is 5 +""" +test.run_built_executable('defines', stdout=expect) + + +# With the value given in both command line and environment, and +# --ignore-environment also specified, command line should still be used. +try: + os.environ['GYP_DEFINES'] = 'value=40' + test.run_gyp('defines-env.gyp', '--ignore-environment', '-Dvalue=45') +finally: + del os.environ['GYP_DEFINES'] + +test.sleep() +test.touch('defines.c') +test.build('defines-env.gyp') + +expect = """\ +VALUE is 45 +""" +test.run_built_executable('defines', stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/defines/gyptest-defines.py b/tools/gyp/test/defines/gyptest-defines.py new file mode 100755 index 0000000000..0b6d64b855 --- /dev/null +++ b/tools/gyp/test/defines/gyptest-defines.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies build of an executable with C++ defines. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('defines.gyp') + +test.build('defines.gyp') + +expect = """\ +FOO is defined +VALUE is 1 +2*PAREN_VALUE is 12 +""" +test.run_built_executable('defines', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/dependencies/a.c b/tools/gyp/test/dependencies/a.c new file mode 100755 index 0000000000..3bba111d24 --- /dev/null +++ b/tools/gyp/test/dependencies/a.c @@ -0,0 +1,9 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +extern int funcB(); + +int funcA() { + return funcB(); +} diff --git a/tools/gyp/test/dependencies/b/b.c b/tools/gyp/test/dependencies/b/b.c new file mode 100755 index 0000000000..b5e771bcc7 --- /dev/null +++ b/tools/gyp/test/dependencies/b/b.c @@ -0,0 +1,3 @@ +int funcB() { + return 2; +} diff --git a/tools/gyp/test/dependencies/b/b.gyp b/tools/gyp/test/dependencies/b/b.gyp new file mode 100755 index 0000000000..893dc64d65 --- /dev/null +++ b/tools/gyp/test/dependencies/b/b.gyp @@ -0,0 +1,22 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'b', + 'type': 'static_library', + 'sources': [ + 'b.c', + ], + }, + { + 'target_name': 'b3', + 'type': 'static_library', + 'sources': [ + 'b3.c', + ], + }, + ], +} diff --git a/tools/gyp/test/dependencies/b/b3.c b/tools/gyp/test/dependencies/b/b3.c new file mode 100755 index 0000000000..287f67ff31 --- /dev/null +++ b/tools/gyp/test/dependencies/b/b3.c @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +int funcB() { + return 3; +} diff --git a/tools/gyp/test/dependencies/c/c.c b/tools/gyp/test/dependencies/c/c.c new file mode 100644 index 0000000000..4949daf3ee --- /dev/null +++ b/tools/gyp/test/dependencies/c/c.c @@ -0,0 +1,4 @@ +int funcC() { + return 3 + // Intentional syntax error. This file should never be compiled, so this + // shouldn't be a problem. diff --git a/tools/gyp/test/dependencies/c/c.gyp b/tools/gyp/test/dependencies/c/c.gyp new file mode 100644 index 0000000000..eabebea9ef --- /dev/null +++ b/tools/gyp/test/dependencies/c/c.gyp @@ -0,0 +1,22 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'c_unused', + 'type': 'static_library', + 'sources': [ + 'c.c', + ], + }, + { + 'target_name': 'd', + 'type': 'static_library', + 'sources': [ + 'd.c', + ], + }, + ], +} diff --git a/tools/gyp/test/dependencies/c/d.c b/tools/gyp/test/dependencies/c/d.c new file mode 100644 index 0000000000..05465fc1af --- /dev/null +++ b/tools/gyp/test/dependencies/c/d.c @@ -0,0 +1,3 @@ +int funcD() { + return 4; +} diff --git a/tools/gyp/test/dependencies/extra_targets.gyp b/tools/gyp/test/dependencies/extra_targets.gyp new file mode 100644 index 0000000000..c1a26de422 --- /dev/null +++ b/tools/gyp/test/dependencies/extra_targets.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'static_library', + 'sources': [ + 'a.c', + ], + # This only depends on the "d" target; other targets in c.gyp + # should not become part of the build (unlike with 'c/c.gyp:*'). + 'dependencies': ['c/c.gyp:d'], + }, + ], +} diff --git a/tools/gyp/test/dependencies/gyptest-extra-targets.py b/tools/gyp/test/dependencies/gyptest-extra-targets.py new file mode 100755 index 0000000000..3752f7445d --- /dev/null +++ b/tools/gyp/test/dependencies/gyptest-extra-targets.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that dependencies don't pull unused targets into the build. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('extra_targets.gyp') + +# This should fail if it tries to build 'c_unused' since 'c/c.c' has a syntax +# error and won't compile. +test.build('extra_targets.gyp', test.ALL) + +test.pass_test() diff --git a/tools/gyp/test/dependencies/gyptest-lib-only.py b/tools/gyp/test/dependencies/gyptest-lib-only.py new file mode 100755 index 0000000000..02159f5f15 --- /dev/null +++ b/tools/gyp/test/dependencies/gyptest-lib-only.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that a link time only dependency will get pulled into the set of built +targets, even if no executable uses it. +""" + +import TestGyp + +import sys + +test = TestGyp.TestGyp() + +test.run_gyp('lib_only.gyp') + +test.build('lib_only.gyp', test.ALL) + +test.built_file_must_exist('a', type=test.STATIC_LIB) + +# TODO(bradnelson/mark): +# On linux and windows a library target will at least pull its link dependencies +# into the generated sln/_main.scons, since not doing so confuses users. +# This is not currently implemented on mac, which has the opposite behavior. +if sys.platform == 'darwin': + if test.format == 'xcode': + test.built_file_must_not_exist('b', type=test.STATIC_LIB) + else: + assert test.format in ('make', 'ninja') + test.built_file_must_exist('b', type=test.STATIC_LIB) +else: + # Make puts the resulting library in a directory matching the input gyp file; + # for the 'b' library, that is in the 'b' subdirectory. + test.built_file_must_exist('b', type=test.STATIC_LIB, subdir='b') + +test.pass_test() diff --git a/tools/gyp/test/dependencies/gyptest-none-traversal.py b/tools/gyp/test/dependencies/gyptest-none-traversal.py new file mode 100755 index 0000000000..c09063dad3 --- /dev/null +++ b/tools/gyp/test/dependencies/gyptest-none-traversal.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that static library dependencies don't traverse none targets, unless +explicitly specified. +""" + +import TestGyp + +import sys + +test = TestGyp.TestGyp() + +test.run_gyp('none_traversal.gyp') + +test.build('none_traversal.gyp', test.ALL) + +test.run_built_executable('needs_chain', stdout="2\n") +test.run_built_executable('doesnt_need_chain', stdout="3\n") + +test.pass_test() diff --git a/tools/gyp/test/dependencies/lib_only.gyp b/tools/gyp/test/dependencies/lib_only.gyp new file mode 100755 index 0000000000..f6c84dea64 --- /dev/null +++ b/tools/gyp/test/dependencies/lib_only.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'static_library', + 'sources': [ + 'a.c', + ], + 'dependencies': ['b/b.gyp:b'], + }, + ], +} diff --git a/tools/gyp/test/dependencies/main.c b/tools/gyp/test/dependencies/main.c new file mode 100644 index 0000000000..185bd482f2 --- /dev/null +++ b/tools/gyp/test/dependencies/main.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> + +extern int funcA(); + +int main() { + printf("%d\n", funcA()); + return 0; +} diff --git a/tools/gyp/test/dependencies/none_traversal.gyp b/tools/gyp/test/dependencies/none_traversal.gyp new file mode 100755 index 0000000000..3d8ab30aff --- /dev/null +++ b/tools/gyp/test/dependencies/none_traversal.gyp @@ -0,0 +1,46 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'needs_chain', + 'type': 'executable', + 'sources': [ + 'a.c', + 'main.c', + ], + 'dependencies': ['chain'], + }, + { + 'target_name': 'chain', + 'type': 'none', + 'dependencies': ['b/b.gyp:b'], + }, + { + 'target_name': 'doesnt_need_chain', + 'type': 'executable', + 'sources': [ + 'main.c', + ], + 'dependencies': ['no_chain', 'other_chain'], + }, + { + 'target_name': 'no_chain', + 'type': 'none', + 'sources': [ + ], + 'dependencies': ['b/b.gyp:b'], + 'dependencies_traverse': 0, + }, + { + 'target_name': 'other_chain', + 'type': 'static_library', + 'sources': [ + 'a.c', + ], + 'dependencies': ['b/b.gyp:b3'], + }, + ], +} diff --git a/tools/gyp/test/dependency-copy/gyptest-copy.py b/tools/gyp/test/dependency-copy/gyptest-copy.py new file mode 100755 index 0000000000..5ba7c73d41 --- /dev/null +++ b/tools/gyp/test/dependency-copy/gyptest-copy.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies dependencies do the copy step. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('copies.gyp', chdir='src') + +test.build('copies.gyp', 'proj2', chdir='src') + +test.run_built_executable('proj1', + chdir='src', + stdout="Hello from file1.c\n") +test.run_built_executable('proj2', + chdir='src', + stdout="Hello from file2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/dependency-copy/src/copies.gyp b/tools/gyp/test/dependency-copy/src/copies.gyp new file mode 100644 index 0000000000..4176b18787 --- /dev/null +++ b/tools/gyp/test/dependency-copy/src/copies.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'proj1', + 'type': 'executable', + 'sources': [ + 'file1.c', + ], + }, + { + 'target_name': 'proj2', + 'type': 'executable', + 'sources': [ + 'file2.c', + ], + 'dependencies': [ + 'proj1', + ] + }, + ], +} diff --git a/tools/gyp/test/dependency-copy/src/file1.c b/tools/gyp/test/dependency-copy/src/file1.c new file mode 100644 index 0000000000..3caf5d6345 --- /dev/null +++ b/tools/gyp/test/dependency-copy/src/file1.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from file1.c\n"); + return 0; +} diff --git a/tools/gyp/test/dependency-copy/src/file2.c b/tools/gyp/test/dependency-copy/src/file2.c new file mode 100644 index 0000000000..ed45cc012d --- /dev/null +++ b/tools/gyp/test/dependency-copy/src/file2.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from file2.c\n"); + return 0; +} diff --git a/tools/gyp/test/exclusion/exclusion.gyp b/tools/gyp/test/exclusion/exclusion.gyp new file mode 100644 index 0000000000..1232dabaef --- /dev/null +++ b/tools/gyp/test/exclusion/exclusion.gyp @@ -0,0 +1,23 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'hello', + 'type': 'executable', + 'sources': [ + 'hello.c', + 'bogus.c', + 'also/not/real.c', + 'also/not/real2.c', + ], + 'sources!': [ + 'bogus.c', + 'also/not/real.c', + 'also/not/real2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/exclusion/gyptest-exclusion.py b/tools/gyp/test/exclusion/gyptest-exclusion.py new file mode 100755 index 0000000000..1fc32bf871 --- /dev/null +++ b/tools/gyp/test/exclusion/gyptest-exclusion.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that exclusions (e.g. sources!) are respected. Excluded sources +that do not exist should not prevent the build from succeeding. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('exclusion.gyp') +test.build('exclusion.gyp') + +# executables +test.built_file_must_exist('hello' + test._exe, test.EXECUTABLE, bare=True) + +test.pass_test() diff --git a/tools/gyp/test/exclusion/hello.c b/tools/gyp/test/exclusion/hello.c new file mode 100644 index 0000000000..30e8d5416d --- /dev/null +++ b/tools/gyp/test/exclusion/hello.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2010 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int func1(void) { + return 42; +} + +int main(int argc, char *argv[]) { + printf("Hello, world!\n"); + printf("%d\n", func1()); + return 0; +} diff --git a/tools/gyp/test/generator-output/actions/actions.gyp b/tools/gyp/test/generator-output/actions/actions.gyp new file mode 100644 index 0000000000..dded59aff3 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/actions.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_all_actions', + 'type': 'none', + 'dependencies': [ + 'subdir1/executable.gyp:*', + 'subdir2/none.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/actions/build/README.txt b/tools/gyp/test/generator-output/actions/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt b/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/build/README.txt b/tools/gyp/test/generator-output/actions/subdir1/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/executable.gyp b/tools/gyp/test/generator-output/actions/subdir1/executable.gyp new file mode 100644 index 0000000000..6bdd60a1fb --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/executable.gyp @@ -0,0 +1,44 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'program.c', + ], + 'actions': [ + { + 'action_name': 'make-prog1', + 'inputs': [ + 'make-prog1.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/prog1.c', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + { + 'action_name': 'make-prog2', + 'inputs': [ + 'make-prog2.py', + ], + 'outputs': [ + 'actions-out/prog2.c', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py b/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py new file mode 100755 index 0000000000..7ea1d8a2d4 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = r""" +#include <stdio.h> + +void prog1(void) +{ + printf("Hello from make-prog1.py\n"); +} +""" + +open(sys.argv[1], 'w').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py b/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py new file mode 100755 index 0000000000..0bfe4973c2 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = r""" +#include <stdio.h> + +void prog2(void) +{ + printf("Hello from make-prog2.py\n"); +} +""" + +open(sys.argv[1], 'w').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/generator-output/actions/subdir1/program.c b/tools/gyp/test/generator-output/actions/subdir1/program.c new file mode 100644 index 0000000000..d5f661d905 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir1/program.c @@ -0,0 +1,12 @@ +#include <stdio.h>
+
+extern void prog1(void);
+extern void prog2(void);
+
+int main(int argc, char *argv[])
+{
+ printf("Hello from program.c\n");
+ prog1();
+ prog2();
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt b/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir2/build/README.txt b/tools/gyp/test/generator-output/actions/subdir2/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir2/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir2/make-file.py b/tools/gyp/test/generator-output/actions/subdir2/make-file.py new file mode 100755 index 0000000000..fff0653144 --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir2/make-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = "Hello from make-file.py\n" + +open(sys.argv[1], 'wb').write(contents) diff --git a/tools/gyp/test/generator-output/actions/subdir2/none.gyp b/tools/gyp/test/generator-output/actions/subdir2/none.gyp new file mode 100644 index 0000000000..f98f52753d --- /dev/null +++ b/tools/gyp/test/generator-output/actions/subdir2/none.gyp @@ -0,0 +1,31 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'file', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'actions': [ + { + 'action_name': 'make-file', + 'inputs': [ + 'make-file.py', + ], + 'outputs': [ + 'actions-out/file.out', + # TODO: enhance testing infrastructure to test this + # without having to hard-code the intermediate dir paths. + #'<(INTERMEDIATE_DIR)/file.out', + ], + 'action': [ + 'python', '<(_inputs)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + } + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/copies/build/README.txt b/tools/gyp/test/generator-output/copies/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/copies/copies-out/README.txt b/tools/gyp/test/generator-output/copies/copies-out/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/copies-out/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/copies/copies.gyp b/tools/gyp/test/generator-output/copies/copies.gyp new file mode 100644 index 0000000000..479a3d9b6e --- /dev/null +++ b/tools/gyp/test/generator-output/copies/copies.gyp @@ -0,0 +1,50 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_subdir', + 'type': 'none', + 'dependencies': [ + 'subdir/subdir.gyp:*', + ], + }, + { + 'target_name': 'copies1', + 'type': 'none', + 'copies': [ + { + 'destination': 'copies-out', + 'files': [ + 'file1', + ], + }, + ], + }, + { + 'target_name': 'copies2', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out', + 'files': [ + 'file2', + ], + }, + ], + }, + # Verify that a null 'files' list doesn't gag the generators. + { + 'target_name': 'copies_null', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-null', + 'files': [], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/copies/file1 b/tools/gyp/test/generator-output/copies/file1 new file mode 100644 index 0000000000..84d55c5759 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/file1 @@ -0,0 +1 @@ +file1 contents diff --git a/tools/gyp/test/generator-output/copies/file2 b/tools/gyp/test/generator-output/copies/file2 new file mode 100644 index 0000000000..af1b8ae35d --- /dev/null +++ b/tools/gyp/test/generator-output/copies/file2 @@ -0,0 +1 @@ +file2 contents diff --git a/tools/gyp/test/generator-output/copies/subdir/build/README.txt b/tools/gyp/test/generator-output/copies/subdir/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/subdir/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt b/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/copies/subdir/file3 b/tools/gyp/test/generator-output/copies/subdir/file3 new file mode 100644 index 0000000000..43f16f3522 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/subdir/file3 @@ -0,0 +1 @@ +file3 contents diff --git a/tools/gyp/test/generator-output/copies/subdir/file4 b/tools/gyp/test/generator-output/copies/subdir/file4 new file mode 100644 index 0000000000..5f7270a084 --- /dev/null +++ b/tools/gyp/test/generator-output/copies/subdir/file4 @@ -0,0 +1 @@ +file4 contents diff --git a/tools/gyp/test/generator-output/copies/subdir/subdir.gyp b/tools/gyp/test/generator-output/copies/subdir/subdir.gyp new file mode 100644 index 0000000000..af031d283a --- /dev/null +++ b/tools/gyp/test/generator-output/copies/subdir/subdir.gyp @@ -0,0 +1,32 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'copies3', + 'type': 'none', + 'copies': [ + { + 'destination': 'copies-out', + 'files': [ + 'file3', + ], + }, + ], + }, + { + 'target_name': 'copies4', + 'type': 'none', + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/copies-out', + 'files': [ + 'file4', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/gyptest-actions.py b/tools/gyp/test/generator-output/gyptest-actions.py new file mode 100755 index 0000000000..6e62720c2b --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-actions.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies --generator-output= behavior when using actions. +""" + +import TestGyp + +# Ninja doesn't support --generator-output. +test = TestGyp.TestGyp(formats=['!ninja']) + +# All the generated files should go under 'gypfiles'. The source directory +# ('actions') should be untouched. +test.writable(test.workpath('actions'), False) +test.run_gyp('actions.gyp', + '--generator-output=' + test.workpath('gypfiles'), + chdir='actions') + +test.writable(test.workpath('actions'), True) + +test.relocate('actions', 'relocate/actions') +test.relocate('gypfiles', 'relocate/gypfiles') + +test.writable(test.workpath('relocate/actions'), False) + +# Some of the action outputs use "pure" relative paths (i.e. without prefixes +# like <(INTERMEDIATE_DIR) or <(PROGRAM_DIR)). Even though we are building under +# 'gypfiles', such outputs will still be created relative to the original .gyp +# sources. Projects probably wouldn't normally do this, since it kind of defeats +# the purpose of '--generator-output', but it is supported behaviour. +test.writable(test.workpath('relocate/actions/build'), True) +test.writable(test.workpath('relocate/actions/subdir1/build'), True) +test.writable(test.workpath('relocate/actions/subdir1/actions-out'), True) +test.writable(test.workpath('relocate/actions/subdir2/build'), True) +test.writable(test.workpath('relocate/actions/subdir2/actions-out'), True) + +test.build('actions.gyp', test.ALL, chdir='relocate/gypfiles') + +expect = """\ +Hello from program.c +Hello from make-prog1.py +Hello from make-prog2.py +""" + +if test.format == 'xcode': + chdir = 'relocate/actions/subdir1' +else: + chdir = 'relocate/gypfiles' +test.run_built_executable('program', chdir=chdir, stdout=expect) + +test.must_match('relocate/actions/subdir2/actions-out/file.out', + "Hello from make-file.py\n") + +test.pass_test() diff --git a/tools/gyp/test/generator-output/gyptest-copies.py b/tools/gyp/test/generator-output/gyptest-copies.py new file mode 100755 index 0000000000..83ba013f0f --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-copies.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies file copies with --generator-output using an explicit build +target of 'all'. +""" + +import TestGyp + +# Ninja doesn't support --generator-output. +test = TestGyp.TestGyp(formats=['!ninja']) + +test.writable(test.workpath('copies'), False) + +test.run_gyp('copies.gyp', + '--generator-output=' + test.workpath('gypfiles'), + chdir='copies') + +test.writable(test.workpath('copies'), True) + +test.relocate('copies', 'relocate/copies') +test.relocate('gypfiles', 'relocate/gypfiles') + +test.writable(test.workpath('relocate/copies'), False) + +test.writable(test.workpath('relocate/copies/build'), True) +test.writable(test.workpath('relocate/copies/copies-out'), True) +test.writable(test.workpath('relocate/copies/subdir/build'), True) +test.writable(test.workpath('relocate/copies/subdir/copies-out'), True) + +test.build('copies.gyp', test.ALL, chdir='relocate/gypfiles') + +test.must_match(['relocate', 'copies', 'copies-out', 'file1'], + "file1 contents\n") + +if test.format == 'xcode': + chdir = 'relocate/copies/build' +elif test.format == 'make': + chdir = 'relocate/gypfiles/out' +else: + chdir = 'relocate/gypfiles' +test.must_match([chdir, 'Default', 'copies-out', 'file2'], "file2 contents\n") + +test.must_match(['relocate', 'copies', 'subdir', 'copies-out', 'file3'], + "file3 contents\n") + +if test.format == 'xcode': + chdir = 'relocate/copies/subdir/build' +elif test.format == 'make': + chdir = 'relocate/gypfiles/out' +else: + chdir = 'relocate/gypfiles' +test.must_match([chdir, 'Default', 'copies-out', 'file4'], "file4 contents\n") + +test.pass_test() diff --git a/tools/gyp/test/generator-output/gyptest-relocate.py b/tools/gyp/test/generator-output/gyptest-relocate.py new file mode 100755 index 0000000000..31a98ea1c2 --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-relocate.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a project hierarchy created with the --generator-output= +option can be built even when it's relocated to a different path. +""" + +import TestGyp + +# Ninja doesn't support --generator-output. +test = TestGyp.TestGyp(formats=['!ninja']) + +test.writable(test.workpath('src'), False) + +test.run_gyp('prog1.gyp', + '-Dset_symroot=1', + '--generator-output=' + test.workpath('gypfiles'), + chdir='src') + +test.writable(test.workpath('src'), True) + +test.relocate('src', 'relocate/src') +test.relocate('gypfiles', 'relocate/gypfiles') + +test.writable(test.workpath('relocate/src'), False) + +test.writable(test.workpath('relocate/src/build'), True) +test.writable(test.workpath('relocate/src/subdir2/build'), True) +test.writable(test.workpath('relocate/src/subdir3/build'), True) + +test.build('prog1.gyp', test.ALL, chdir='relocate/gypfiles') + +chdir = 'relocate/gypfiles' + +expect = """\ +Hello from %s +Hello from inc.h +Hello from inc1/include1.h +Hello from inc2/include2.h +Hello from inc3/include3.h +Hello from subdir2/deeper/deeper.h +""" + +if test.format == 'xcode': + chdir = 'relocate/src' +test.run_built_executable('prog1', chdir=chdir, stdout=expect % 'prog1.c') + +if test.format == 'xcode': + chdir = 'relocate/src/subdir2' +test.run_built_executable('prog2', chdir=chdir, stdout=expect % 'prog2.c') + +if test.format == 'xcode': + chdir = 'relocate/src/subdir3' +test.run_built_executable('prog3', chdir=chdir, stdout=expect % 'prog3.c') + +test.pass_test() diff --git a/tools/gyp/test/generator-output/gyptest-rules.py b/tools/gyp/test/generator-output/gyptest-rules.py new file mode 100755 index 0000000000..678416a2fc --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-rules.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies --generator-output= behavior when using rules. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['!ninja']) + +test.writable(test.workpath('rules'), False) + +test.run_gyp('rules.gyp', + '--generator-output=' + test.workpath('gypfiles'), + chdir='rules') + +test.writable(test.workpath('rules'), True) + +test.relocate('rules', 'relocate/rules') +test.relocate('gypfiles', 'relocate/gypfiles') + +test.writable(test.workpath('relocate/rules'), False) + +test.writable(test.workpath('relocate/rules/build'), True) +test.writable(test.workpath('relocate/rules/subdir1/build'), True) +test.writable(test.workpath('relocate/rules/subdir2/build'), True) +test.writable(test.workpath('relocate/rules/subdir2/rules-out'), True) + +test.build('rules.gyp', test.ALL, chdir='relocate/gypfiles') + +expect = """\ +Hello from program.c +Hello from function1.in1 +Hello from function2.in1 +Hello from define3.in0 +Hello from define4.in0 +""" + +if test.format == 'xcode': + chdir = 'relocate/rules/subdir1' +else: + chdir = 'relocate/gypfiles' +test.run_built_executable('program', chdir=chdir, stdout=expect) + +test.must_match('relocate/rules/subdir2/rules-out/file1.out', + "Hello from file1.in0\n") +test.must_match('relocate/rules/subdir2/rules-out/file2.out', + "Hello from file2.in0\n") +test.must_match('relocate/rules/subdir2/rules-out/file3.out', + "Hello from file3.in1\n") +test.must_match('relocate/rules/subdir2/rules-out/file4.out', + "Hello from file4.in1\n") + +test.pass_test() diff --git a/tools/gyp/test/generator-output/gyptest-subdir2-deep.py b/tools/gyp/test/generator-output/gyptest-subdir2-deep.py new file mode 100755 index 0000000000..cb5ea15bf1 --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-subdir2-deep.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a target from a .gyp file a few subdirectories +deep when the --generator-output= option is used to put the build +configuration files in a separate directory tree. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['!ninja']) + +test.writable(test.workpath('src'), False) + +test.writable(test.workpath('src/subdir2/deeper/build'), True) + +test.run_gyp('deeper.gyp', + '-Dset_symroot=1', + '--generator-output=' + test.workpath('gypfiles'), + chdir='src/subdir2/deeper') + +test.build('deeper.gyp', test.ALL, chdir='gypfiles') + +chdir = 'gypfiles' + +if test.format == 'xcode': + chdir = 'src/subdir2/deeper' +test.run_built_executable('deeper', + chdir=chdir, + stdout="Hello from deeper.c\n") + +test.pass_test() diff --git a/tools/gyp/test/generator-output/gyptest-top-all.py b/tools/gyp/test/generator-output/gyptest-top-all.py new file mode 100755 index 0000000000..4841f9b6b0 --- /dev/null +++ b/tools/gyp/test/generator-output/gyptest-top-all.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a project hierarchy created when the --generator-output= +option is used to put the build configuration files in a separate +directory tree. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['!ninja']) + +test.writable(test.workpath('src'), False) + +test.run_gyp('prog1.gyp', + '-Dset_symroot=1', + '--generator-output=' + test.workpath('gypfiles'), + chdir='src') + +test.writable(test.workpath('src/build'), True) +test.writable(test.workpath('src/subdir2/build'), True) +test.writable(test.workpath('src/subdir3/build'), True) + +test.build('prog1.gyp', test.ALL, chdir='gypfiles') + +chdir = 'gypfiles' + +expect = """\ +Hello from %s +Hello from inc.h +Hello from inc1/include1.h +Hello from inc2/include2.h +Hello from inc3/include3.h +Hello from subdir2/deeper/deeper.h +""" + +if test.format == 'xcode': + chdir = 'src' +test.run_built_executable('prog1', chdir=chdir, stdout=expect % 'prog1.c') + +if test.format == 'xcode': + chdir = 'src/subdir2' +test.run_built_executable('prog2', chdir=chdir, stdout=expect % 'prog2.c') + +if test.format == 'xcode': + chdir = 'src/subdir3' +test.run_built_executable('prog3', chdir=chdir, stdout=expect % 'prog3.c') + +test.pass_test() diff --git a/tools/gyp/test/generator-output/rules/build/README.txt b/tools/gyp/test/generator-output/rules/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/copy-file.py b/tools/gyp/test/generator-output/rules/copy-file.py new file mode 100755 index 0000000000..938c336adb --- /dev/null +++ b/tools/gyp/test/generator-output/rules/copy-file.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +contents = open(sys.argv[1], 'r').read() +open(sys.argv[2], 'wb').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/generator-output/rules/rules.gyp b/tools/gyp/test/generator-output/rules/rules.gyp new file mode 100644 index 0000000000..dded59aff3 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/rules.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_all_actions', + 'type': 'none', + 'dependencies': [ + 'subdir1/executable.gyp:*', + 'subdir2/none.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/rules/subdir1/build/README.txt b/tools/gyp/test/generator-output/rules/subdir1/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/subdir1/define3.in0 b/tools/gyp/test/generator-output/rules/subdir1/define3.in0 new file mode 100644 index 0000000000..cc29c643f3 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/define3.in0 @@ -0,0 +1 @@ +#define STRING3 "Hello from define3.in0\n" diff --git a/tools/gyp/test/generator-output/rules/subdir1/define4.in0 b/tools/gyp/test/generator-output/rules/subdir1/define4.in0 new file mode 100644 index 0000000000..c9b0467b32 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/define4.in0 @@ -0,0 +1 @@ +#define STRING4 "Hello from define4.in0\n" diff --git a/tools/gyp/test/generator-output/rules/subdir1/executable.gyp b/tools/gyp/test/generator-output/rules/subdir1/executable.gyp new file mode 100644 index 0000000000..2fd89a0d52 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/executable.gyp @@ -0,0 +1,59 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'program.c', + 'function1.in1', + 'function2.in1', + 'define3.in0', + 'define4.in0', + ], + 'include_dirs': [ + '<(INTERMEDIATE_DIR)', + ], + 'rules': [ + { + 'rule_name': 'copy_file_0', + 'extension': 'in0', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + # TODO: fix SCons and Make to support generated files not + # in a variable-named path like <(INTERMEDIATE_DIR) + #'<(RULE_INPUT_ROOT).c', + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).h', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 0, + }, + { + 'rule_name': 'copy_file_1', + 'extension': 'in1', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + # TODO: fix SCons and Make to support generated files not + # in a variable-named path like <(INTERMEDIATE_DIR) + #'<(RULE_INPUT_ROOT).c', + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).c', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/rules/subdir1/function1.in1 b/tools/gyp/test/generator-output/rules/subdir1/function1.in1 new file mode 100644 index 0000000000..545e7ca16b --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/function1.in1 @@ -0,0 +1,6 @@ +#include <stdio.h> + +void function1(void) +{ + printf("Hello from function1.in1\n"); +} diff --git a/tools/gyp/test/generator-output/rules/subdir1/function2.in1 b/tools/gyp/test/generator-output/rules/subdir1/function2.in1 new file mode 100644 index 0000000000..6bad43f9cf --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/function2.in1 @@ -0,0 +1,6 @@ +#include <stdio.h> + +void function2(void) +{ + printf("Hello from function2.in1\n"); +} diff --git a/tools/gyp/test/generator-output/rules/subdir1/program.c b/tools/gyp/test/generator-output/rules/subdir1/program.c new file mode 100644 index 0000000000..27fd31ed4e --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir1/program.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include "define3.h" +#include "define4.h" + +extern void function1(void); +extern void function2(void); +extern void function3(void); +extern void function4(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from program.c\n"); + function1(); + function2(); + printf("%s", STRING3); + printf("%s", STRING4); + return 0; +} diff --git a/tools/gyp/test/generator-output/rules/subdir2/build/README.txt b/tools/gyp/test/generator-output/rules/subdir2/build/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/subdir2/file1.in0 b/tools/gyp/test/generator-output/rules/subdir2/file1.in0 new file mode 100644 index 0000000000..7aca64f4ce --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/file1.in0 @@ -0,0 +1 @@ +Hello from file1.in0 diff --git a/tools/gyp/test/generator-output/rules/subdir2/file2.in0 b/tools/gyp/test/generator-output/rules/subdir2/file2.in0 new file mode 100644 index 0000000000..80a281a2a9 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/file2.in0 @@ -0,0 +1 @@ +Hello from file2.in0 diff --git a/tools/gyp/test/generator-output/rules/subdir2/file3.in1 b/tools/gyp/test/generator-output/rules/subdir2/file3.in1 new file mode 100644 index 0000000000..60ae2e7931 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/file3.in1 @@ -0,0 +1 @@ +Hello from file3.in1 diff --git a/tools/gyp/test/generator-output/rules/subdir2/file4.in1 b/tools/gyp/test/generator-output/rules/subdir2/file4.in1 new file mode 100644 index 0000000000..5a3c30720e --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/file4.in1 @@ -0,0 +1 @@ +Hello from file4.in1 diff --git a/tools/gyp/test/generator-output/rules/subdir2/none.gyp b/tools/gyp/test/generator-output/rules/subdir2/none.gyp new file mode 100644 index 0000000000..664cbd9cb7 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/none.gyp @@ -0,0 +1,49 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'files', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'file1.in0', + 'file2.in0', + 'file3.in1', + 'file4.in1', + ], + 'rules': [ + { + 'rule_name': 'copy_file_0', + 'extension': 'in0', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + 'rules-out/<(RULE_INPUT_ROOT).out', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 0, + }, + { + 'rule_name': 'copy_file_1', + 'extension': 'in1', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + 'rules-out/<(RULE_INPUT_ROOT).out', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt b/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt new file mode 100644 index 0000000000..1b052c9a24 --- /dev/null +++ b/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/build/README.txt b/tools/gyp/test/generator-output/src/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/src/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/src/inc.h b/tools/gyp/test/generator-output/src/inc.h new file mode 100644 index 0000000000..57aa1a5a74 --- /dev/null +++ b/tools/gyp/test/generator-output/src/inc.h @@ -0,0 +1 @@ +#define INC_STRING "inc.h" diff --git a/tools/gyp/test/generator-output/src/inc1/include1.h b/tools/gyp/test/generator-output/src/inc1/include1.h new file mode 100644 index 0000000000..1d59065fc9 --- /dev/null +++ b/tools/gyp/test/generator-output/src/inc1/include1.h @@ -0,0 +1 @@ +#define INCLUDE1_STRING "inc1/include1.h" diff --git a/tools/gyp/test/generator-output/src/prog1.c b/tools/gyp/test/generator-output/src/prog1.c new file mode 100644 index 0000000000..656f81d5fe --- /dev/null +++ b/tools/gyp/test/generator-output/src/prog1.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +#include "inc.h" +#include "include1.h" +#include "include2.h" +#include "include3.h" +#include "deeper.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + printf("Hello from %s\n", INC_STRING); + printf("Hello from %s\n", INCLUDE1_STRING); + printf("Hello from %s\n", INCLUDE2_STRING); + printf("Hello from %s\n", INCLUDE3_STRING); + printf("Hello from %s\n", DEEPER_STRING); + return 0; +} diff --git a/tools/gyp/test/generator-output/src/prog1.gyp b/tools/gyp/test/generator-output/src/prog1.gyp new file mode 100644 index 0000000000..d50e6fb0a7 --- /dev/null +++ b/tools/gyp/test/generator-output/src/prog1.gyp @@ -0,0 +1,28 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + 'symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'dependencies': [ + 'subdir2/prog2.gyp:prog2', + ], + 'include_dirs': [ + '.', + 'inc1', + 'subdir2/inc2', + 'subdir3/inc3', + 'subdir2/deeper', + ], + 'sources': [ + 'prog1.c', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/src/subdir2/build/README.txt b/tools/gyp/test/generator-output/src/subdir2/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt b/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c new file mode 100644 index 0000000000..56c49d1f78 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from deeper.c\n"); + return 0; +} diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp new file mode 100644 index 0000000000..8648770872 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'deeper', + 'type': 'executable', + 'sources': [ + 'deeper.c', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h new file mode 100644 index 0000000000..f6484a0fe5 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h @@ -0,0 +1 @@ +#define DEEPER_STRING "subdir2/deeper/deeper.h" diff --git a/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h b/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h new file mode 100644 index 0000000000..1ccfa5dea7 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h @@ -0,0 +1 @@ +#define INCLUDE2_STRING "inc2/include2.h" diff --git a/tools/gyp/test/generator-output/src/subdir2/prog2.c b/tools/gyp/test/generator-output/src/subdir2/prog2.c new file mode 100644 index 0000000000..38d6c84d11 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/prog2.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +#include "inc.h" +#include "include1.h" +#include "include2.h" +#include "include3.h" +#include "deeper.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from prog2.c\n"); + printf("Hello from %s\n", INC_STRING); + printf("Hello from %s\n", INCLUDE1_STRING); + printf("Hello from %s\n", INCLUDE2_STRING); + printf("Hello from %s\n", INCLUDE3_STRING); + printf("Hello from %s\n", DEEPER_STRING); + return 0; +} diff --git a/tools/gyp/test/generator-output/src/subdir2/prog2.gyp b/tools/gyp/test/generator-output/src/subdir2/prog2.gyp new file mode 100644 index 0000000000..7176ed8be7 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir2/prog2.gyp @@ -0,0 +1,28 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog2', + 'type': 'executable', + 'include_dirs': [ + '..', + '../inc1', + 'inc2', + '../subdir3/inc3', + 'deeper', + ], + 'dependencies': [ + '../subdir3/prog3.gyp:prog3', + ], + 'sources': [ + 'prog2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/src/subdir3/build/README.txt b/tools/gyp/test/generator-output/src/subdir3/build/README.txt new file mode 100644 index 0000000000..90ef886193 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir3/build/README.txt @@ -0,0 +1,4 @@ +A place-holder for this Xcode build output directory, so that the +test script can verify that .xcodeproj files are not created in +their normal location by making the src/ read-only, and then +selectively making this build directory writable. diff --git a/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h b/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h new file mode 100644 index 0000000000..bf53bf1f00 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h @@ -0,0 +1 @@ +#define INCLUDE3_STRING "inc3/include3.h" diff --git a/tools/gyp/test/generator-output/src/subdir3/prog3.c b/tools/gyp/test/generator-output/src/subdir3/prog3.c new file mode 100644 index 0000000000..7848b45abd --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir3/prog3.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +#include "inc.h" +#include "include1.h" +#include "include2.h" +#include "include3.h" +#include "deeper.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from prog3.c\n"); + printf("Hello from %s\n", INC_STRING); + printf("Hello from %s\n", INCLUDE1_STRING); + printf("Hello from %s\n", INCLUDE2_STRING); + printf("Hello from %s\n", INCLUDE3_STRING); + printf("Hello from %s\n", DEEPER_STRING); + return 0; +} diff --git a/tools/gyp/test/generator-output/src/subdir3/prog3.gyp b/tools/gyp/test/generator-output/src/subdir3/prog3.gyp new file mode 100644 index 0000000000..46c5e000a2 --- /dev/null +++ b/tools/gyp/test/generator-output/src/subdir3/prog3.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog3', + 'type': 'executable', + 'include_dirs': [ + '..', + '../inc1', + '../subdir2/inc2', + 'inc3', + '../subdir2/deeper', + ], + 'sources': [ + 'prog3.c', + ], + }, + ], +} diff --git a/tools/gyp/test/generator-output/src/symroot.gypi b/tools/gyp/test/generator-output/src/symroot.gypi new file mode 100644 index 0000000000..519916427c --- /dev/null +++ b/tools/gyp/test/generator-output/src/symroot.gypi @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'set_symroot%': 0, + }, + 'conditions': [ + ['set_symroot == 1', { + 'xcode_settings': { + 'SYMROOT': '<(DEPTH)/build', + }, + }], + ], +} diff --git a/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py b/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py new file mode 100755 index 0000000000..ba51528800 --- /dev/null +++ b/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that a hard_dependency that is exported is pulled in as a dependency +for a target if the target is a static library and if the generator will +remove dependencies between static libraries. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +if test.format == 'dump_dependency_json': + test.skip_test('Skipping test; dependency JSON does not adjust ' \ + 'static libraries.\n') + +test.run_gyp('hard_dependency.gyp', chdir='src') + +chdir = 'relocate/src' +test.relocate('src', chdir) + +test.build('hard_dependency.gyp', 'c', chdir=chdir) + +# The 'a' static library should be built, as it has actions with side-effects +# that are necessary to compile 'c'. Even though 'c' does not directly depend +# on 'a', because 'a' is a hard_dependency that 'b' exports, 'c' should import +# it as a hard_dependency and ensure it is built before building 'c'. +test.built_file_must_exist('a', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_not_exist('b', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_exist('c', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_not_exist('d', type=test.STATIC_LIB, chdir=chdir) + +test.pass_test() diff --git a/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py b/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py new file mode 100755 index 0000000000..10774ca2a0 --- /dev/null +++ b/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that a hard_dependency that is not exported is not pulled in as a +dependency for a target if the target does not explicitly specify a dependency +and none of its dependencies export the hard_dependency. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +if test.format == 'dump_dependency_json': + test.skip_test('Skipping test; dependency JSON does not adjust ' \ + 'static libaries.\n') + +test.run_gyp('hard_dependency.gyp', chdir='src') + +chdir = 'relocate/src' +test.relocate('src', chdir) + +test.build('hard_dependency.gyp', 'd', chdir=chdir) + +# Because 'c' does not export a hard_dependency, only the target 'd' should +# be built. This is because the 'd' target does not need the generated headers +# in order to be compiled. +test.built_file_must_not_exist('a', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_not_exist('b', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_not_exist('c', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_exist('d', type=test.STATIC_LIB, chdir=chdir) + +test.pass_test() diff --git a/tools/gyp/test/hard_dependency/src/a.c b/tools/gyp/test/hard_dependency/src/a.c new file mode 100644 index 0000000000..0fa0223c97 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/a.c @@ -0,0 +1,9 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "a.h" + +int funcA() { + return 42; +} diff --git a/tools/gyp/test/hard_dependency/src/a.h b/tools/gyp/test/hard_dependency/src/a.h new file mode 100644 index 0000000000..854a06504a --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/a.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef A_H_ +#define A_H_ + +#include "generated.h" + +int funcA(); + +#endif // A_H_ diff --git a/tools/gyp/test/hard_dependency/src/b.c b/tools/gyp/test/hard_dependency/src/b.c new file mode 100644 index 0000000000..0baace929e --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/b.c @@ -0,0 +1,9 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "a.h" + +int funcB() { + return funcA(); +} diff --git a/tools/gyp/test/hard_dependency/src/b.h b/tools/gyp/test/hard_dependency/src/b.h new file mode 100644 index 0000000000..22b48cefe2 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/b.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef B_H_ +#define B_H_ + +#include "a.h" + +int funcB(); + +#endif // B_H_ diff --git a/tools/gyp/test/hard_dependency/src/c.c b/tools/gyp/test/hard_dependency/src/c.c new file mode 100644 index 0000000000..b0e0fb17d6 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/c.c @@ -0,0 +1,9 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "c.h" + +int funcC() { + return funcB(); +} diff --git a/tools/gyp/test/hard_dependency/src/c.h b/tools/gyp/test/hard_dependency/src/c.h new file mode 100644 index 0000000000..f4ea7fefa2 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/c.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef C_H_ +#define C_H_ + +int funcC(); + +#endif // C_H_ diff --git a/tools/gyp/test/hard_dependency/src/d.c b/tools/gyp/test/hard_dependency/src/d.c new file mode 100644 index 0000000000..d016c3ce71 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/d.c @@ -0,0 +1,9 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "c.h" + +int funcD() { + return funcC(); +} diff --git a/tools/gyp/test/hard_dependency/src/emit.py b/tools/gyp/test/hard_dependency/src/emit.py new file mode 100755 index 0000000000..2df74b79a1 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/emit.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +f = open(sys.argv[1], 'wb') +f.write('/* Hello World */\n') +f.close() diff --git a/tools/gyp/test/hard_dependency/src/hard_dependency.gyp b/tools/gyp/test/hard_dependency/src/hard_dependency.gyp new file mode 100644 index 0000000000..4479c5f045 --- /dev/null +++ b/tools/gyp/test/hard_dependency/src/hard_dependency.gyp @@ -0,0 +1,78 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'static_library', + 'sources': [ + 'a.c', + 'a.h', + ], + 'hard_dependency': 1, + 'actions': [ + { + 'action_name': 'generate_headers', + 'inputs': [ + 'emit.py' + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/generated.h' + ], + 'action': [ + 'python', + 'emit.py', + '<(SHARED_INTERMEDIATE_DIR)/generated.h', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + }, + { + 'target_name': 'b', + 'type': 'static_library', + 'sources': [ + 'b.c', + 'b.h', + ], + 'dependencies': [ + 'a', + ], + 'export_dependent_settings': [ + 'a', + ], + }, + { + 'target_name': 'c', + 'type': 'static_library', + 'sources': [ + 'c.c', + 'c.h', + ], + 'dependencies': [ + 'b', + ], + }, + { + 'target_name': 'd', + 'type': 'static_library', + 'sources': [ + 'd.c', + ], + 'dependencies': [ + 'c', + ], + } + ], +} diff --git a/tools/gyp/test/hello/gyptest-all.py b/tools/gyp/test/hello/gyptest-all.py new file mode 100755 index 0000000000..1739b6886e --- /dev/null +++ b/tools/gyp/test/hello/gyptest-all.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simplest-possible build of a "Hello, world!" program +using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_all') + +test.run_gyp('hello.gyp') + +test.build('hello.gyp', test.ALL) + +test.run_built_executable('hello', stdout="Hello, world!\n") + +test.up_to_date('hello.gyp', test.ALL) + +test.pass_test() diff --git a/tools/gyp/test/hello/gyptest-default.py b/tools/gyp/test/hello/gyptest-default.py new file mode 100755 index 0000000000..22377e7ac5 --- /dev/null +++ b/tools/gyp/test/hello/gyptest-default.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simplest-possible build of a "Hello, world!" program +using the default build target. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_default') + +test.run_gyp('hello.gyp') + +test.build('hello.gyp') + +test.run_built_executable('hello', stdout="Hello, world!\n") + +test.up_to_date('hello.gyp', test.DEFAULT) + +test.pass_test() diff --git a/tools/gyp/test/hello/gyptest-disable-regyp.py b/tools/gyp/test/hello/gyptest-disable-regyp.py new file mode 100755 index 0000000000..1e4b306674 --- /dev/null +++ b/tools/gyp/test/hello/gyptest-disable-regyp.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that Makefiles don't get rebuilt when a source gyp file changes and +the disable_regeneration generator flag is set. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('hello.gyp', '-Gauto_regeneration=0') + +test.build('hello.gyp', test.ALL) + +test.run_built_executable('hello', stdout="Hello, world!\n") + +# Sleep so that the changed gyp file will have a newer timestamp than the +# previously generated build files. +test.sleep() +test.write('hello.gyp', test.read('hello2.gyp')) + +test.build('hello.gyp', test.ALL) + +# Should still be the old executable, as regeneration was disabled. +test.run_built_executable('hello', stdout="Hello, world!\n") + +test.pass_test() diff --git a/tools/gyp/test/hello/gyptest-regyp.py b/tools/gyp/test/hello/gyptest-regyp.py new file mode 100755 index 0000000000..827c7235ce --- /dev/null +++ b/tools/gyp/test/hello/gyptest-regyp.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that Makefiles get rebuilt when a source gyp file changes. +""" + +import TestGyp + +# Regenerating build files when a gyp file changes is currently only supported +# by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +test.run_gyp('hello.gyp') + +test.build('hello.gyp', test.ALL) + +test.run_built_executable('hello', stdout="Hello, world!\n") + +# Sleep so that the changed gyp file will have a newer timestamp than the +# previously generated build files. +test.sleep() +test.write('hello.gyp', test.read('hello2.gyp')) + +test.build('hello.gyp', test.ALL) + +test.run_built_executable('hello', stdout="Hello, two!\n") + +test.pass_test() diff --git a/tools/gyp/test/hello/gyptest-target.py b/tools/gyp/test/hello/gyptest-target.py new file mode 100755 index 0000000000..1abaf7057b --- /dev/null +++ b/tools/gyp/test/hello/gyptest-target.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simplest-possible build of a "Hello, world!" program +using an explicit build target of 'hello'. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_target') + +test.run_gyp('hello.gyp') + +test.build('hello.gyp', 'hello') + +test.run_built_executable('hello', stdout="Hello, world!\n") + +test.up_to_date('hello.gyp', 'hello') + +test.pass_test() diff --git a/tools/gyp/test/hello/hello.c b/tools/gyp/test/hello/hello.c new file mode 100644 index 0000000000..8dbecc0492 --- /dev/null +++ b/tools/gyp/test/hello/hello.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello, world!\n"); + return 0; +} diff --git a/tools/gyp/test/hello/hello.gyp b/tools/gyp/test/hello/hello.gyp new file mode 100644 index 0000000000..1974d51ccd --- /dev/null +++ b/tools/gyp/test/hello/hello.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'hello', + 'type': 'executable', + 'sources': [ + 'hello.c', + ], + }, + ], +} diff --git a/tools/gyp/test/hello/hello2.c b/tools/gyp/test/hello/hello2.c new file mode 100644 index 0000000000..19ef3fbd5c --- /dev/null +++ b/tools/gyp/test/hello/hello2.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello, two!\n"); + return 0; +} diff --git a/tools/gyp/test/hello/hello2.gyp b/tools/gyp/test/hello/hello2.gyp new file mode 100644 index 0000000000..25b08caf3c --- /dev/null +++ b/tools/gyp/test/hello/hello2.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'hello', + 'type': 'executable', + 'sources': [ + 'hello2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py new file mode 100755 index 0000000000..974db7fee5 --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies inclusion of $HOME/.gyp/includes.gypi works properly with relocation +and with regeneration. +""" + +import os +import TestGyp + +# Regenerating build files when a gyp file changes is currently only supported +# by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +os.environ['HOME'] = os.path.abspath('home') + +test.run_gyp('all.gyp', chdir='src') + +# After relocating, we should still be able to build (build file shouldn't +# contain relative reference to ~/.gyp/includes.gypi) +test.relocate('src', 'relocate/src') + +test.build('all.gyp', test.ALL, chdir='relocate/src') + +test.run_built_executable('printfoo', + chdir='relocate/src', + stdout='FOO is fromhome\n') + +# Building should notice any changes to ~/.gyp/includes.gypi and regyp. +test.sleep() + +test.write('home/.gyp/include.gypi', test.read('home2/.gyp/include.gypi')) + +test.build('all.gyp', test.ALL, chdir='relocate/src') + +test.run_built_executable('printfoo', + chdir='relocate/src', + stdout='FOO is fromhome2\n') + +test.pass_test() diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py new file mode 100755 index 0000000000..3131e2d248 --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies inclusion of $HOME/.gyp/includes.gypi works. +""" + +import os +import TestGyp + +test = TestGyp.TestGyp() + +os.environ['HOME'] = os.path.abspath('home') + +test.run_gyp('all.gyp', chdir='src') + +# After relocating, we should still be able to build (build file shouldn't +# contain relative reference to ~/.gyp/includes.gypi) +test.relocate('src', 'relocate/src') + +test.build('all.gyp', test.ALL, chdir='relocate/src') + +test.run_built_executable('printfoo', + chdir='relocate/src', + stdout='FOO is fromhome\n') + +test.pass_test() diff --git a/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi b/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi new file mode 100644 index 0000000000..fcfb39befd --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi @@ -0,0 +1,5 @@ +{ + 'variables': { + 'foo': '"fromhome"', + }, +} diff --git a/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi b/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi new file mode 100644 index 0000000000..f0d84b31ad --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi @@ -0,0 +1,5 @@ +{ + 'variables': { + 'foo': '"fromhome2"', + }, +} diff --git a/tools/gyp/test/home_dot_gyp/src/all.gyp b/tools/gyp/test/home_dot_gyp/src/all.gyp new file mode 100644 index 0000000000..14b6aea285 --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/src/all.gyp @@ -0,0 +1,22 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'foo%': '"fromdefault"', + }, + 'targets': [ + { + 'target_name': 'printfoo', + 'type': 'executable', + 'sources': [ + 'printfoo.c', + ], + 'defines': [ + 'FOO=<(foo)', + ], + }, + ], +} + diff --git a/tools/gyp/test/home_dot_gyp/src/printfoo.c b/tools/gyp/test/home_dot_gyp/src/printfoo.c new file mode 100644 index 0000000000..92d2cbacb7 --- /dev/null +++ b/tools/gyp/test/home_dot_gyp/src/printfoo.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("FOO is %s\n", FOO); + return 0; +} diff --git a/tools/gyp/test/include_dirs/gyptest-all.py b/tools/gyp/test/include_dirs/gyptest-all.py new file mode 100755 index 0000000000..94a1338d49 --- /dev/null +++ b/tools/gyp/test/include_dirs/gyptest-all.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies use of include_dirs when using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +if test.format == 'scons': + test.skip_test('TODO: http://code.google.com/p/gyp/issues/detail?id=176\n') + +test.run_gyp('includes.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('includes.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from includes.c +Hello from inc.h +Hello from include1.h +Hello from subdir/inc2/include2.h +Hello from shadow2/shadow.h +""" +test.run_built_executable('includes', stdout=expect, chdir='relocate/src') + +if test.format == 'xcode': + chdir='relocate/src/subdir' +else: + chdir='relocate/src' + +expect = """\ +Hello from subdir/subdir_includes.c +Hello from subdir/inc.h +Hello from include1.h +Hello from subdir/inc2/include2.h +""" +test.run_built_executable('subdir_includes', stdout=expect, chdir=chdir) + +test.pass_test() diff --git a/tools/gyp/test/include_dirs/gyptest-default.py b/tools/gyp/test/include_dirs/gyptest-default.py new file mode 100755 index 0000000000..42acd1f962 --- /dev/null +++ b/tools/gyp/test/include_dirs/gyptest-default.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies use of include_dirs when using the default build target. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +if test.format == 'scons': + test.skip_test('TODO: http://code.google.com/p/gyp/issues/detail?id=176\n') + +test.run_gyp('includes.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('includes.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from includes.c +Hello from inc.h +Hello from include1.h +Hello from subdir/inc2/include2.h +Hello from shadow2/shadow.h +""" +test.run_built_executable('includes', stdout=expect, chdir='relocate/src') + +if test.format == 'xcode': + chdir='relocate/src/subdir' +else: + chdir='relocate/src' + +expect = """\ +Hello from subdir/subdir_includes.c +Hello from subdir/inc.h +Hello from include1.h +Hello from subdir/inc2/include2.h +""" +test.run_built_executable('subdir_includes', stdout=expect, chdir=chdir) + +test.pass_test() diff --git a/tools/gyp/test/include_dirs/src/inc.h b/tools/gyp/test/include_dirs/src/inc.h new file mode 100644 index 0000000000..0398d6915f --- /dev/null +++ b/tools/gyp/test/include_dirs/src/inc.h @@ -0,0 +1 @@ +#define INC_STRING "inc.h" diff --git a/tools/gyp/test/include_dirs/src/inc1/include1.h b/tools/gyp/test/include_dirs/src/inc1/include1.h new file mode 100644 index 0000000000..43356b5f47 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/inc1/include1.h @@ -0,0 +1 @@ +#define INCLUDE1_STRING "include1.h" diff --git a/tools/gyp/test/include_dirs/src/includes.c b/tools/gyp/test/include_dirs/src/includes.c new file mode 100644 index 0000000000..e2afbd3ed8 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/includes.c @@ -0,0 +1,19 @@ +#include <stdio.h> + +#include "inc.h" +#include "include1.h" +#include "include2.h" +#include "shadow.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from includes.c\n"); + printf("Hello from %s\n", INC_STRING); + printf("Hello from %s\n", INCLUDE1_STRING); + printf("Hello from %s\n", INCLUDE2_STRING); + /* Test that include_dirs happen first: The gyp file has a -Ishadow1 + cflag and an include_dir of shadow2. Including shadow.h should get + the shadow.h from the include_dir. */ + printf("Hello from %s\n", SHADOW_STRING); + return 0; +} diff --git a/tools/gyp/test/include_dirs/src/includes.gyp b/tools/gyp/test/include_dirs/src/includes.gyp new file mode 100644 index 0000000000..3592690208 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/includes.gyp @@ -0,0 +1,27 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'includes', + 'type': 'executable', + 'dependencies': [ + 'subdir/subdir_includes.gyp:subdir_includes', + ], + 'cflags': [ + '-Ishadow1', + ], + 'include_dirs': [ + '.', + 'inc1', + 'shadow2', + 'subdir/inc2', + ], + 'sources': [ + 'includes.c', + ], + }, + ], +} diff --git a/tools/gyp/test/include_dirs/src/shadow1/shadow.h b/tools/gyp/test/include_dirs/src/shadow1/shadow.h new file mode 100644 index 0000000000..80f6de20b8 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/shadow1/shadow.h @@ -0,0 +1 @@ +#define SHADOW_STRING "shadow1/shadow.h" diff --git a/tools/gyp/test/include_dirs/src/shadow2/shadow.h b/tools/gyp/test/include_dirs/src/shadow2/shadow.h new file mode 100644 index 0000000000..fad5ccd085 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/shadow2/shadow.h @@ -0,0 +1 @@ +#define SHADOW_STRING "shadow2/shadow.h" diff --git a/tools/gyp/test/include_dirs/src/subdir/inc.h b/tools/gyp/test/include_dirs/src/subdir/inc.h new file mode 100644 index 0000000000..0a68d7b36a --- /dev/null +++ b/tools/gyp/test/include_dirs/src/subdir/inc.h @@ -0,0 +1 @@ +#define INC_STRING "subdir/inc.h" diff --git a/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h b/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h new file mode 100644 index 0000000000..721577effb --- /dev/null +++ b/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h @@ -0,0 +1 @@ +#define INCLUDE2_STRING "subdir/inc2/include2.h" diff --git a/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c new file mode 100644 index 0000000000..727f682205 --- /dev/null +++ b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c @@ -0,0 +1,14 @@ +#include <stdio.h> + +#include "inc.h" +#include "include1.h" +#include "include2.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from subdir/subdir_includes.c\n"); + printf("Hello from %s\n", INC_STRING); + printf("Hello from %s\n", INCLUDE1_STRING); + printf("Hello from %s\n", INCLUDE2_STRING); + return 0; +} diff --git a/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp new file mode 100644 index 0000000000..257d052c3c --- /dev/null +++ b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp @@ -0,0 +1,20 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'subdir_includes', + 'type': 'executable', + 'include_dirs': [ + '.', + '../inc1', + 'inc2', + ], + 'sources': [ + 'subdir_includes.c', + ], + }, + ], +} diff --git a/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py b/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py new file mode 100755 index 0000000000..b4fd16f468 --- /dev/null +++ b/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that targets have independent INTERMEDIATE_DIRs. +""" + +import TestGyp + +import os +import sys + +test = TestGyp.TestGyp() + +test.run_gyp('test.gyp', chdir='src') +test.build('test.gyp', 'target1', chdir='src') +# Check stuff exists. +intermediate_file1 = test.read('src/outfile.txt') +test.must_contain(intermediate_file1, 'target1') + +shared_intermediate_file1 = test.read('src/shared_outfile.txt') +test.must_contain(shared_intermediate_file1, 'shared_target1') + +test.run_gyp('test2.gyp', chdir='src') +test.build('test2.gyp', 'target2', chdir='src') +# Check INTERMEDIATE_DIR file didn't get overwritten but SHARED_INTERMEDIAT_DIR +# file did. +intermediate_file2 = test.read('src/outfile.txt') +test.must_contain(intermediate_file1, 'target1') +test.must_contain(intermediate_file2, 'target2') + +shared_intermediate_file2 = test.read('src/shared_outfile.txt') +if shared_intermediate_file1 != shared_intermediate_file2: + test.fail_test(shared_intermediate_file1 + ' != ' + shared_intermediate_file2) + +# These sometimes fail flakily with the xcode generator due to the shared file +# still containing 'target1'. Maybe the xcode build runs actions asynchronously? +# Asserting that the intermediate file names are identical is good enough. +#test.must_contain(shared_intermediate_file1, 'shared_target2') +#test.must_contain(shared_intermediate_file2, 'shared_target2') + +test.pass_test() diff --git a/tools/gyp/test/intermediate_dir/src/script.py b/tools/gyp/test/intermediate_dir/src/script.py new file mode 100755 index 0000000000..fa828a06b9 --- /dev/null +++ b/tools/gyp/test/intermediate_dir/src/script.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Takes 3 arguments. Writes the 1st argument to the file in the 2nd argument, +# and writes the absolute path to the file in the 2nd argument to the file in +# the 3rd argument. + +import os +import shlex +import sys + +if len(sys.argv) == 3 and ' ' in sys.argv[2]: + sys.argv[2], fourth = shlex.split(sys.argv[2]) + sys.argv.append(fourth) + +#print >>sys.stderr, sys.argv + +with open(sys.argv[2], 'w') as f: + f.write(sys.argv[1]) + +with open(sys.argv[3], 'w') as f: + f.write(os.path.abspath(sys.argv[2])) diff --git a/tools/gyp/test/intermediate_dir/src/test.gyp b/tools/gyp/test/intermediate_dir/src/test.gyp new file mode 100644 index 0000000000..9cd07c9e34 --- /dev/null +++ b/tools/gyp/test/intermediate_dir/src/test.gyp @@ -0,0 +1,40 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'target1', + 'type': 'none', + 'actions': [ + { + 'action_name': 'intermediate', + 'inputs': [], + 'outputs': [ + '<(INTERMEDIATE_DIR)/intermediate_out.txt', + 'outfile.txt', + ], + 'action': [ + 'python', 'script.py', 'target1', '<(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'shared_intermediate', + 'inputs': [], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/intermediate_out.txt', + 'shared_outfile.txt', + ], + 'action': [ + 'python', 'script.py', 'shared_target1', '<(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/intermediate_dir/src/test2.gyp b/tools/gyp/test/intermediate_dir/src/test2.gyp new file mode 100644 index 0000000000..07ed9a03e6 --- /dev/null +++ b/tools/gyp/test/intermediate_dir/src/test2.gyp @@ -0,0 +1,40 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'target2', + 'type': 'none', + 'actions': [ + { + 'action_name': 'intermediate', + 'inputs': [], + 'outputs': [ + '<(INTERMEDIATE_DIR)/intermediate_out.txt', + 'outfile.txt', + ], + 'action': [ + 'python', 'script.py', 'target2', '<(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + { + 'action_name': 'shared_intermediate', + 'inputs': [], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/intermediate_out.txt', + 'shared_outfile.txt', + ], + 'action': [ + 'python', 'script.py', 'shared_target2', '<(_outputs)', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/lib/README.txt b/tools/gyp/test/lib/README.txt new file mode 100644 index 0000000000..b3d724574e --- /dev/null +++ b/tools/gyp/test/lib/README.txt @@ -0,0 +1,17 @@ +Supporting modules for GYP testing. + + TestCmd.py + TestCommon.py + + Modules for generic testing of command-line utilities, + specifically including the ability to copy a test configuration + to temporary directories (with default cleanup on exit) as part + of running test scripts that invoke commands, compare actual + against expected output, etc. + + Our copies of these come from the SCons project, + http://www.scons.org/. + + TestGyp.py + + Modules for GYP-specific tests, of course. diff --git a/tools/gyp/test/lib/TestCmd.py b/tools/gyp/test/lib/TestCmd.py new file mode 100644 index 0000000000..45d901ca29 --- /dev/null +++ b/tools/gyp/test/lib/TestCmd.py @@ -0,0 +1,1594 @@ +""" +TestCmd.py: a testing framework for commands and scripts. + +The TestCmd module provides a framework for portable automated testing +of executable commands and scripts (in any language, not just Python), +especially commands and scripts that require file system interaction. + +In addition to running tests and evaluating conditions, the TestCmd +module manages and cleans up one or more temporary workspace +directories, and provides methods for creating files and directories in +those workspace directories from in-line data, here-documents), allowing +tests to be completely self-contained. + +A TestCmd environment object is created via the usual invocation: + + import TestCmd + test = TestCmd.TestCmd() + +There are a bunch of keyword arguments available at instantiation: + + test = TestCmd.TestCmd(description = 'string', + program = 'program_or_script_to_test', + interpreter = 'script_interpreter', + workdir = 'prefix', + subdir = 'subdir', + verbose = Boolean, + match = default_match_function, + diff = default_diff_function, + combine = Boolean) + +There are a bunch of methods that let you do different things: + + test.verbose_set(1) + + test.description_set('string') + + test.program_set('program_or_script_to_test') + + test.interpreter_set('script_interpreter') + test.interpreter_set(['script_interpreter', 'arg']) + + test.workdir_set('prefix') + test.workdir_set('') + + test.workpath('file') + test.workpath('subdir', 'file') + + test.subdir('subdir', ...) + + test.rmdir('subdir', ...) + + test.write('file', "contents\n") + test.write(['subdir', 'file'], "contents\n") + + test.read('file') + test.read(['subdir', 'file']) + test.read('file', mode) + test.read(['subdir', 'file'], mode) + + test.writable('dir', 1) + test.writable('dir', None) + + test.preserve(condition, ...) + + test.cleanup(condition) + + test.command_args(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program') + + test.run(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program', + chdir = 'directory_to_chdir_to', + stdin = 'input to feed to the program\n') + universal_newlines = True) + + p = test.start(program = 'program_or_script_to_run', + interpreter = 'script_interpreter', + arguments = 'arguments to pass to program', + universal_newlines = None) + + test.finish(self, p) + + test.pass_test() + test.pass_test(condition) + test.pass_test(condition, function) + + test.fail_test() + test.fail_test(condition) + test.fail_test(condition, function) + test.fail_test(condition, function, skip) + + test.no_result() + test.no_result(condition) + test.no_result(condition, function) + test.no_result(condition, function, skip) + + test.stdout() + test.stdout(run) + + test.stderr() + test.stderr(run) + + test.symlink(target, link) + + test.banner(string) + test.banner(string, width) + + test.diff(actual, expected) + + test.match(actual, expected) + + test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") + test.match_exact(["actual 1\n", "actual 2\n"], + ["expected 1\n", "expected 2\n"]) + + test.match_re("actual 1\nactual 2\n", regex_string) + test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes) + + test.match_re_dotall("actual 1\nactual 2\n", regex_string) + test.match_re_dotall(["actual 1\n", "actual 2\n"], list_of_regexes) + + test.tempdir() + test.tempdir('temporary-directory') + + test.sleep() + test.sleep(seconds) + + test.where_is('foo') + test.where_is('foo', 'PATH1:PATH2') + test.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') + + test.unlink('file') + test.unlink('subdir', 'file') + +The TestCmd module provides pass_test(), fail_test(), and no_result() +unbound functions that report test results for use with the Aegis change +management system. These methods terminate the test immediately, +reporting PASSED, FAILED, or NO RESULT respectively, and exiting with +status 0 (success), 1 or 2 respectively. This allows for a distinction +between an actual failed test and a test that could not be properly +evaluated because of an external condition (such as a full file system +or incorrect permissions). + + import TestCmd + + TestCmd.pass_test() + TestCmd.pass_test(condition) + TestCmd.pass_test(condition, function) + + TestCmd.fail_test() + TestCmd.fail_test(condition) + TestCmd.fail_test(condition, function) + TestCmd.fail_test(condition, function, skip) + + TestCmd.no_result() + TestCmd.no_result(condition) + TestCmd.no_result(condition, function) + TestCmd.no_result(condition, function, skip) + +The TestCmd module also provides unbound functions that handle matching +in the same way as the match_*() methods described above. + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.match_exact) + + test = TestCmd.TestCmd(match = TestCmd.match_re) + + test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) + +The TestCmd module provides unbound functions that can be used for the +"diff" argument to TestCmd.TestCmd instantiation: + + import TestCmd + + test = TestCmd.TestCmd(match = TestCmd.match_re, + diff = TestCmd.diff_re) + + test = TestCmd.TestCmd(diff = TestCmd.simple_diff) + +The "diff" argument can also be used with standard difflib functions: + + import difflib + + test = TestCmd.TestCmd(diff = difflib.context_diff) + + test = TestCmd.TestCmd(diff = difflib.unified_diff) + +Lastly, the where_is() method also exists in an unbound function +version. + + import TestCmd + + TestCmd.where_is('foo') + TestCmd.where_is('foo', 'PATH1:PATH2') + TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') +""" + +# Copyright 2000-2010 Steven Knight +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +__author__ = "Steven Knight <knight at baldmt dot com>" +__revision__ = "TestCmd.py 0.37.D001 2010/01/11 16:55:50 knight" +__version__ = "0.37" + +import errno +import os +import os.path +import re +import shutil +import stat +import string +import sys +import tempfile +import time +import traceback +import types +import UserList + +__all__ = [ + 'diff_re', + 'fail_test', + 'no_result', + 'pass_test', + 'match_exact', + 'match_re', + 'match_re_dotall', + 'python_executable', + 'TestCmd' +] + +try: + import difflib +except ImportError: + __all__.append('simple_diff') + +def is_List(e): + return type(e) is types.ListType \ + or isinstance(e, UserList.UserList) + +try: + from UserString import UserString +except ImportError: + class UserString: + pass + +if hasattr(types, 'UnicodeType'): + def is_String(e): + return type(e) is types.StringType \ + or type(e) is types.UnicodeType \ + or isinstance(e, UserString) +else: + def is_String(e): + return type(e) is types.StringType or isinstance(e, UserString) + +tempfile.template = 'testcmd.' +if os.name in ('posix', 'nt'): + tempfile.template = 'testcmd.' + str(os.getpid()) + '.' +else: + tempfile.template = 'testcmd.' + +re_space = re.compile('\s') + +_Cleanup = [] + +_chain_to_exitfunc = None + +def _clean(): + global _Cleanup + cleanlist = filter(None, _Cleanup) + del _Cleanup[:] + cleanlist.reverse() + for test in cleanlist: + test.cleanup() + if _chain_to_exitfunc: + _chain_to_exitfunc() + +try: + import atexit +except ImportError: + # TODO(1.5): atexit requires python 2.0, so chain sys.exitfunc + try: + _chain_to_exitfunc = sys.exitfunc + except AttributeError: + pass + sys.exitfunc = _clean +else: + atexit.register(_clean) + +try: + zip +except NameError: + def zip(*lists): + result = [] + for i in xrange(min(map(len, lists))): + result.append(tuple(map(lambda l, i=i: l[i], lists))) + return result + +class Collector: + def __init__(self, top): + self.entries = [top] + def __call__(self, arg, dirname, names): + pathjoin = lambda n, d=dirname: os.path.join(d, n) + self.entries.extend(map(pathjoin, names)) + +def _caller(tblist, skip): + string = "" + arr = [] + for file, line, name, text in tblist: + if file[-10:] == "TestCmd.py": + break + arr = [(file, line, name, text)] + arr + atfrom = "at" + for file, line, name, text in arr[skip:]: + if name in ("?", "<module>"): + name = "" + else: + name = " (" + name + ")" + string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name)) + atfrom = "\tfrom" + return string + +def fail_test(self = None, condition = 1, function = None, skip = 0): + """Cause the test to fail. + + By default, the fail_test() method reports that the test FAILED + and exits with a status of 1. If a condition argument is supplied, + the test fails only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + of = "" + desc = "" + sep = " " + if not self is None: + if self.program: + of = " of " + self.program + sep = "\n\t" + if self.description: + desc = " [" + self.description + "]" + sep = "\n\t" + + at = _caller(traceback.extract_stack(), skip) + sys.stderr.write("FAILED test" + of + desc + sep + at) + + sys.exit(1) + +def no_result(self = None, condition = 1, function = None, skip = 0): + """Causes a test to exit with no valid result. + + By default, the no_result() method reports NO RESULT for the test + and exits with a status of 2. If a condition argument is supplied, + the test fails only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + of = "" + desc = "" + sep = " " + if not self is None: + if self.program: + of = " of " + self.program + sep = "\n\t" + if self.description: + desc = " [" + self.description + "]" + sep = "\n\t" + + at = _caller(traceback.extract_stack(), skip) + sys.stderr.write("NO RESULT for test" + of + desc + sep + at) + + sys.exit(2) + +def pass_test(self = None, condition = 1, function = None): + """Causes a test to pass. + + By default, the pass_test() method reports PASSED for the test + and exits with a status of 0. If a condition argument is supplied, + the test passes only if the condition is true. + """ + if not condition: + return + if not function is None: + function() + sys.stderr.write("PASSED\n") + sys.exit(0) + +def match_exact(lines = None, matches = None): + """ + """ + if not is_List(lines): + lines = string.split(lines, "\n") + if not is_List(matches): + matches = string.split(matches, "\n") + if len(lines) != len(matches): + return + for i in range(len(lines)): + if lines[i] != matches[i]: + return + return 1 + +def match_re(lines = None, res = None): + """ + """ + if not is_List(lines): + lines = string.split(lines, "\n") + if not is_List(res): + res = string.split(res, "\n") + if len(lines) != len(res): + return + for i in range(len(lines)): + s = "^" + res[i] + "$" + try: + expr = re.compile(s) + except re.error, e: + msg = "Regular expression error in %s: %s" + raise re.error, msg % (repr(s), e[0]) + if not expr.search(lines[i]): + return + return 1 + +def match_re_dotall(lines = None, res = None): + """ + """ + if not type(lines) is type(""): + lines = string.join(lines, "\n") + if not type(res) is type(""): + res = string.join(res, "\n") + s = "^" + res + "$" + try: + expr = re.compile(s, re.DOTALL) + except re.error, e: + msg = "Regular expression error in %s: %s" + raise re.error, msg % (repr(s), e[0]) + if expr.match(lines): + return 1 + +try: + import difflib +except ImportError: + pass +else: + def simple_diff(a, b, fromfile='', tofile='', + fromfiledate='', tofiledate='', n=3, lineterm='\n'): + """ + A function with the same calling signature as difflib.context_diff + (diff -c) and difflib.unified_diff (diff -u) but which prints + output like the simple, unadorned 'diff" command. + """ + sm = difflib.SequenceMatcher(None, a, b) + def comma(x1, x2): + return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2) + result = [] + for op, a1, a2, b1, b2 in sm.get_opcodes(): + if op == 'delete': + result.append("%sd%d" % (comma(a1, a2), b1)) + result.extend(map(lambda l: '< ' + l, a[a1:a2])) + elif op == 'insert': + result.append("%da%s" % (a1, comma(b1, b2))) + result.extend(map(lambda l: '> ' + l, b[b1:b2])) + elif op == 'replace': + result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) + result.extend(map(lambda l: '< ' + l, a[a1:a2])) + result.append('---') + result.extend(map(lambda l: '> ' + l, b[b1:b2])) + return result + +def diff_re(a, b, fromfile='', tofile='', + fromfiledate='', tofiledate='', n=3, lineterm='\n'): + """ + A simple "diff" of two sets of lines when the expected lines + are regular expressions. This is a really dumb thing that + just compares each line in turn, so it doesn't look for + chunks of matching lines and the like--but at least it lets + you know exactly which line first didn't compare correctl... + """ + result = [] + diff = len(a) - len(b) + if diff < 0: + a = a + ['']*(-diff) + elif diff > 0: + b = b + ['']*diff + i = 0 + for aline, bline in zip(a, b): + s = "^" + aline + "$" + try: + expr = re.compile(s) + except re.error, e: + msg = "Regular expression error in %s: %s" + raise re.error, msg % (repr(s), e[0]) + if not expr.search(bline): + result.append("%sc%s" % (i+1, i+1)) + result.append('< ' + repr(a[i])) + result.append('---') + result.append('> ' + repr(b[i])) + i = i+1 + return result + +if os.name == 'java': + + python_executable = os.path.join(sys.prefix, 'jython') + +else: + + python_executable = sys.executable + +if sys.platform == 'win32': + + default_sleep_seconds = 2 + + def where_is(file, path=None, pathext=None): + if path is None: + path = os.environ['PATH'] + if is_String(path): + path = string.split(path, os.pathsep) + if pathext is None: + pathext = os.environ['PATHEXT'] + if is_String(pathext): + pathext = string.split(pathext, os.pathsep) + for ext in pathext: + if string.lower(ext) == string.lower(file[-len(ext):]): + pathext = [''] + break + for dir in path: + f = os.path.join(dir, file) + for ext in pathext: + fext = f + ext + if os.path.isfile(fext): + return fext + return None + +else: + + def where_is(file, path=None, pathext=None): + if path is None: + path = os.environ['PATH'] + if is_String(path): + path = string.split(path, os.pathsep) + for dir in path: + f = os.path.join(dir, file) + if os.path.isfile(f): + try: + st = os.stat(f) + except OSError: + continue + if stat.S_IMODE(st[stat.ST_MODE]) & 0111: + return f + return None + + default_sleep_seconds = 1 + + + +try: + import subprocess +except ImportError: + # The subprocess module doesn't exist in this version of Python, + # so we're going to cobble up something that looks just enough + # like its API for our purposes below. + import new + + subprocess = new.module('subprocess') + + subprocess.PIPE = 'PIPE' + subprocess.STDOUT = 'STDOUT' + subprocess.mswindows = (sys.platform == 'win32') + + try: + import popen2 + popen2.Popen3 + except AttributeError: + class Popen3: + universal_newlines = 1 + def __init__(self, command, **kw): + if sys.platform == 'win32' and command[0] == '"': + command = '"' + command + '"' + (stdin, stdout, stderr) = os.popen3(' ' + command) + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + def close_output(self): + self.stdout.close() + self.resultcode = self.stderr.close() + def wait(self): + resultcode = self.resultcode + if os.WIFEXITED(resultcode): + return os.WEXITSTATUS(resultcode) + elif os.WIFSIGNALED(resultcode): + return os.WTERMSIG(resultcode) + else: + return None + + else: + try: + popen2.Popen4 + except AttributeError: + # A cribbed Popen4 class, with some retrofitted code from + # the Python 1.5 Popen3 class methods to do certain things + # by hand. + class Popen4(popen2.Popen3): + childerr = None + + def __init__(self, cmd, bufsize=-1): + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + os.dup2(c2pwrite, 2) + for i in range(3, popen2.MAXFD): + try: + os.close(i) + except: pass + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + # Shouldn't come here, I guess + os._exit(1) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + popen2._active.append(self) + + popen2.Popen4 = Popen4 + + class Popen3(popen2.Popen3, popen2.Popen4): + universal_newlines = 1 + def __init__(self, command, **kw): + if kw.get('stderr') == 'STDOUT': + apply(popen2.Popen4.__init__, (self, command, 1)) + else: + apply(popen2.Popen3.__init__, (self, command, 1)) + self.stdin = self.tochild + self.stdout = self.fromchild + self.stderr = self.childerr + def wait(self, *args, **kw): + resultcode = apply(popen2.Popen3.wait, (self,)+args, kw) + if os.WIFEXITED(resultcode): + return os.WEXITSTATUS(resultcode) + elif os.WIFSIGNALED(resultcode): + return os.WTERMSIG(resultcode) + else: + return None + + subprocess.Popen = Popen3 + + + +# From Josiah Carlson, +# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 + +PIPE = subprocess.PIPE + +if subprocess.mswindows: + from win32file import ReadFile, WriteFile + from win32pipe import PeekNamedPipe + import msvcrt +else: + import select + import fcntl + + try: fcntl.F_GETFL + except AttributeError: fcntl.F_GETFL = 3 + + try: fcntl.F_SETFL + except AttributeError: fcntl.F_SETFL = 4 + +class Popen(subprocess.Popen): + def recv(self, maxsize=None): + return self._recv('stdout', maxsize) + + def recv_err(self, maxsize=None): + return self._recv('stderr', maxsize) + + def send_recv(self, input='', maxsize=None): + return self.send(input), self.recv(maxsize), self.recv_err(maxsize) + + def get_conn_maxsize(self, which, maxsize): + if maxsize is None: + maxsize = 1024 + elif maxsize < 1: + maxsize = 1 + return getattr(self, which), maxsize + + def _close(self, which): + getattr(self, which).close() + setattr(self, which, None) + + if subprocess.mswindows: + def send(self, input): + if not self.stdin: + return None + + try: + x = msvcrt.get_osfhandle(self.stdin.fileno()) + (errCode, written) = WriteFile(x, input) + except ValueError: + return self._close('stdin') + except (subprocess.pywintypes.error, Exception), why: + if why[0] in (109, errno.ESHUTDOWN): + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + x = msvcrt.get_osfhandle(conn.fileno()) + (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + if maxsize < nAvail: + nAvail = maxsize + if nAvail > 0: + (errCode, read) = ReadFile(x, nAvail, None) + except ValueError: + return self._close(which) + except (subprocess.pywintypes.error, Exception), why: + if why[0] in (109, errno.ESHUTDOWN): + return self._close(which) + raise + + #if self.universal_newlines: + # read = self._translate_newlines(read) + return read + + else: + def send(self, input): + if not self.stdin: + return None + + if not select.select([], [self.stdin], [], 0)[1]: + return 0 + + try: + written = os.write(self.stdin.fileno(), input) + except OSError, why: + if why[0] == errno.EPIPE: #broken pipe + return self._close('stdin') + raise + + return written + + def _recv(self, which, maxsize): + conn, maxsize = self.get_conn_maxsize(which, maxsize) + if conn is None: + return None + + try: + flags = fcntl.fcntl(conn, fcntl.F_GETFL) + except TypeError: + flags = None + else: + if not conn.closed: + fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK) + + try: + if not select.select([conn], [], [], 0)[0]: + return '' + + r = conn.read(maxsize) + if not r: + return self._close(which) + + #if self.universal_newlines: + # r = self._translate_newlines(r) + return r + finally: + if not conn.closed and not flags is None: + fcntl.fcntl(conn, fcntl.F_SETFL, flags) + +disconnect_message = "Other end disconnected!" + +def recv_some(p, t=.1, e=1, tr=5, stderr=0): + if tr < 1: + tr = 1 + x = time.time()+t + y = [] + r = '' + pr = p.recv + if stderr: + pr = p.recv_err + while time.time() < x or r: + r = pr() + if r is None: + if e: + raise Exception(disconnect_message) + else: + break + elif r: + y.append(r) + else: + time.sleep(max((x-time.time())/tr, 0)) + return ''.join(y) + +# TODO(3.0: rewrite to use memoryview() +def send_all(p, data): + while len(data): + sent = p.send(data) + if sent is None: + raise Exception(disconnect_message) + data = buffer(data, sent) + + + +try: + object +except NameError: + class object: + pass + + + +class TestCmd(object): + """Class TestCmd + """ + + def __init__(self, description = None, + program = None, + interpreter = None, + workdir = None, + subdir = None, + verbose = None, + match = None, + diff = None, + combine = 0, + universal_newlines = 1): + self._cwd = os.getcwd() + self.description_set(description) + self.program_set(program) + self.interpreter_set(interpreter) + if verbose is None: + try: + verbose = max( 0, int(os.environ.get('TESTCMD_VERBOSE', 0)) ) + except ValueError: + verbose = 0 + self.verbose_set(verbose) + self.combine = combine + self.universal_newlines = universal_newlines + if match is not None: + self.match_function = match + else: + self.match_function = match_re + if diff is not None: + self.diff_function = diff + else: + try: + difflib + except NameError: + pass + else: + self.diff_function = simple_diff + #self.diff_function = difflib.context_diff + #self.diff_function = difflib.unified_diff + self._dirlist = [] + self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} + if os.environ.has_key('PRESERVE') and not os.environ['PRESERVE'] is '': + self._preserve['pass_test'] = os.environ['PRESERVE'] + self._preserve['fail_test'] = os.environ['PRESERVE'] + self._preserve['no_result'] = os.environ['PRESERVE'] + else: + try: + self._preserve['pass_test'] = os.environ['PRESERVE_PASS'] + except KeyError: + pass + try: + self._preserve['fail_test'] = os.environ['PRESERVE_FAIL'] + except KeyError: + pass + try: + self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT'] + except KeyError: + pass + self._stdout = [] + self._stderr = [] + self.status = None + self.condition = 'no_result' + self.workdir_set(workdir) + self.subdir(subdir) + + def __del__(self): + self.cleanup() + + def __repr__(self): + return "%x" % id(self) + + banner_char = '=' + banner_width = 80 + + def banner(self, s, width=None): + if width is None: + width = self.banner_width + return s + self.banner_char * (width - len(s)) + + if os.name == 'posix': + + def escape(self, arg): + "escape shell special characters" + slash = '\\' + special = '"$' + + arg = string.replace(arg, slash, slash+slash) + for c in special: + arg = string.replace(arg, c, slash+c) + + if re_space.search(arg): + arg = '"' + arg + '"' + return arg + + else: + + # Windows does not allow special characters in file names + # anyway, so no need for an escape function, we will just quote + # the arg. + def escape(self, arg): + if re_space.search(arg): + arg = '"' + arg + '"' + return arg + + def canonicalize(self, path): + if is_List(path): + path = apply(os.path.join, tuple(path)) + if not os.path.isabs(path): + path = os.path.join(self.workdir, path) + return path + + def chmod(self, path, mode): + """Changes permissions on the specified file or directory + path name.""" + path = self.canonicalize(path) + os.chmod(path, mode) + + def cleanup(self, condition = None): + """Removes any temporary working directories for the specified + TestCmd environment. If the environment variable PRESERVE was + set when the TestCmd environment was created, temporary working + directories are not removed. If any of the environment variables + PRESERVE_PASS, PRESERVE_FAIL, or PRESERVE_NO_RESULT were set + when the TestCmd environment was created, then temporary working + directories are not removed if the test passed, failed, or had + no result, respectively. Temporary working directories are also + preserved for conditions specified via the preserve method. + + Typically, this method is not called directly, but is used when + the script exits to clean up temporary working directories as + appropriate for the exit status. + """ + if not self._dirlist: + return + os.chdir(self._cwd) + self.workdir = None + if condition is None: + condition = self.condition + if self._preserve[condition]: + for dir in self._dirlist: + print "Preserved directory", dir + else: + list = self._dirlist[:] + list.reverse() + for dir in list: + self.writable(dir, 1) + shutil.rmtree(dir, ignore_errors = 1) + self._dirlist = [] + + try: + global _Cleanup + _Cleanup.remove(self) + except (AttributeError, ValueError): + pass + + def command_args(self, program = None, + interpreter = None, + arguments = None): + if program: + if type(program) == type('') and not os.path.isabs(program): + program = os.path.join(self._cwd, program) + else: + program = self.program + if not interpreter: + interpreter = self.interpreter + if not type(program) in [type([]), type(())]: + program = [program] + cmd = list(program) + if interpreter: + if not type(interpreter) in [type([]), type(())]: + interpreter = [interpreter] + cmd = list(interpreter) + cmd + if arguments: + if type(arguments) == type(''): + arguments = string.split(arguments) + cmd.extend(arguments) + return cmd + + def description_set(self, description): + """Set the description of the functionality being tested. + """ + self.description = description + + try: + difflib + except NameError: + def diff(self, a, b, name, *args, **kw): + print self.banner('Expected %s' % name) + print a + print self.banner('Actual %s' % name) + print b + else: + def diff(self, a, b, name, *args, **kw): + print self.banner(name) + args = (a.splitlines(), b.splitlines()) + args + lines = apply(self.diff_function, args, kw) + for l in lines: + print l + + def fail_test(self, condition = 1, function = None, skip = 0): + """Cause the test to fail. + """ + if not condition: + return + self.condition = 'fail_test' + fail_test(self = self, + condition = condition, + function = function, + skip = skip) + + def interpreter_set(self, interpreter): + """Set the program to be used to interpret the program + under test as a script. + """ + self.interpreter = interpreter + + def match(self, lines, matches): + """Compare actual and expected file contents. + """ + return self.match_function(lines, matches) + + def match_exact(self, lines, matches): + """Compare actual and expected file contents. + """ + return match_exact(lines, matches) + + def match_re(self, lines, res): + """Compare actual and expected file contents. + """ + return match_re(lines, res) + + def match_re_dotall(self, lines, res): + """Compare actual and expected file contents. + """ + return match_re_dotall(lines, res) + + def no_result(self, condition = 1, function = None, skip = 0): + """Report that the test could not be run. + """ + if not condition: + return + self.condition = 'no_result' + no_result(self = self, + condition = condition, + function = function, + skip = skip) + + def pass_test(self, condition = 1, function = None): + """Cause the test to pass. + """ + if not condition: + return + self.condition = 'pass_test' + pass_test(self = self, condition = condition, function = function) + + def preserve(self, *conditions): + """Arrange for the temporary working directories for the + specified TestCmd environment to be preserved for one or more + conditions. If no conditions are specified, arranges for + the temporary working directories to be preserved for all + conditions. + """ + if conditions is (): + conditions = ('pass_test', 'fail_test', 'no_result') + for cond in conditions: + self._preserve[cond] = 1 + + def program_set(self, program): + """Set the executable program or script to be tested. + """ + if program and not os.path.isabs(program): + program = os.path.join(self._cwd, program) + self.program = program + + def read(self, file, mode = 'rb'): + """Reads and returns the contents of the specified file name. + The file name may be a list, in which case the elements are + concatenated with the os.path.join() method. The file is + assumed to be under the temporary working directory unless it + is an absolute path name. The I/O mode for the file may + be specified; it must begin with an 'r'. The default is + 'rb' (binary read). + """ + file = self.canonicalize(file) + if mode[0] != 'r': + raise ValueError, "mode must begin with 'r'" + with open(file, mode) as f: + result = f.read() + return result + + def rmdir(self, dir): + """Removes the specified dir name. + The dir name may be a list, in which case the elements are + concatenated with the os.path.join() method. The dir is + assumed to be under the temporary working directory unless it + is an absolute path name. + The dir must be empty. + """ + dir = self.canonicalize(dir) + os.rmdir(dir) + + def start(self, program = None, + interpreter = None, + arguments = None, + universal_newlines = None, + **kw): + """ + Starts a program or script for the test environment. + + The specified program will have the original directory + prepended unless it is enclosed in a [list]. + """ + cmd = self.command_args(program, interpreter, arguments) + cmd_string = string.join(map(self.escape, cmd), ' ') + if self.verbose: + sys.stderr.write(cmd_string + "\n") + if universal_newlines is None: + universal_newlines = self.universal_newlines + + # On Windows, if we make stdin a pipe when we plan to send + # no input, and the test program exits before + # Popen calls msvcrt.open_osfhandle, that call will fail. + # So don't use a pipe for stdin if we don't need one. + stdin = kw.get('stdin', None) + if stdin is not None: + stdin = subprocess.PIPE + + combine = kw.get('combine', self.combine) + if combine: + stderr_value = subprocess.STDOUT + else: + stderr_value = subprocess.PIPE + + return Popen(cmd, + stdin=stdin, + stdout=subprocess.PIPE, + stderr=stderr_value, + universal_newlines=universal_newlines) + + def finish(self, popen, **kw): + """ + Finishes and waits for the process being run under control of + the specified popen argument, recording the exit status, + standard output and error output. + """ + popen.stdin.close() + self.status = popen.wait() + if not self.status: + self.status = 0 + self._stdout.append(popen.stdout.read()) + if popen.stderr: + stderr = popen.stderr.read() + else: + stderr = '' + self._stderr.append(stderr) + + def run(self, program = None, + interpreter = None, + arguments = None, + chdir = None, + stdin = None, + universal_newlines = None): + """Runs a test of the program or script for the test + environment. Standard output and error output are saved for + future retrieval via the stdout() and stderr() methods. + + The specified program will have the original directory + prepended unless it is enclosed in a [list]. + """ + if chdir: + oldcwd = os.getcwd() + if not os.path.isabs(chdir): + chdir = os.path.join(self.workpath(chdir)) + if self.verbose: + sys.stderr.write("chdir(" + chdir + ")\n") + os.chdir(chdir) + p = self.start(program, + interpreter, + arguments, + universal_newlines, + stdin=stdin) + if stdin: + if is_List(stdin): + for line in stdin: + p.stdin.write(line) + else: + p.stdin.write(stdin) + p.stdin.close() + + out = p.stdout.read() + if p.stderr is None: + err = '' + else: + err = p.stderr.read() + try: + close_output = p.close_output + except AttributeError: + p.stdout.close() + if not p.stderr is None: + p.stderr.close() + else: + close_output() + + self._stdout.append(out) + self._stderr.append(err) + + self.status = p.wait() + if not self.status: + self.status = 0 + + if chdir: + os.chdir(oldcwd) + if self.verbose >= 2: + write = sys.stdout.write + write('============ STATUS: %d\n' % self.status) + out = self.stdout() + if out or self.verbose >= 3: + write('============ BEGIN STDOUT (len=%d):\n' % len(out)) + write(out) + write('============ END STDOUT\n') + err = self.stderr() + if err or self.verbose >= 3: + write('============ BEGIN STDERR (len=%d)\n' % len(err)) + write(err) + write('============ END STDERR\n') + + def sleep(self, seconds = default_sleep_seconds): + """Sleeps at least the specified number of seconds. If no + number is specified, sleeps at least the minimum number of + seconds necessary to advance file time stamps on the current + system. Sleeping more seconds is all right. + """ + time.sleep(seconds) + + def stderr(self, run = None): + """Returns the error output from the specified run number. + If there is no specified run number, then returns the error + output of the last run. If the run number is less than zero, + then returns the error output from that many runs back from the + current run. + """ + if not run: + run = len(self._stderr) + elif run < 0: + run = len(self._stderr) + run + run = run - 1 + return self._stderr[run] + + def stdout(self, run = None): + """Returns the standard output from the specified run number. + If there is no specified run number, then returns the standard + output of the last run. If the run number is less than zero, + then returns the standard output from that many runs back from + the current run. + """ + if not run: + run = len(self._stdout) + elif run < 0: + run = len(self._stdout) + run + run = run - 1 + return self._stdout[run] + + def subdir(self, *subdirs): + """Create new subdirectories under the temporary working + directory, one for each argument. An argument may be a list, + in which case the list elements are concatenated using the + os.path.join() method. Subdirectories multiple levels deep + must be created using a separate argument for each level: + + test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory']) + + Returns the number of subdirectories actually created. + """ + count = 0 + for sub in subdirs: + if sub is None: + continue + if is_List(sub): + sub = apply(os.path.join, tuple(sub)) + new = os.path.join(self.workdir, sub) + try: + os.mkdir(new) + except OSError: + pass + else: + count = count + 1 + return count + + def symlink(self, target, link): + """Creates a symlink to the specified target. + The link name may be a list, in which case the elements are + concatenated with the os.path.join() method. The link is + assumed to be under the temporary working directory unless it + is an absolute path name. The target is *not* assumed to be + under the temporary working directory. + """ + link = self.canonicalize(link) + os.symlink(target, link) + + def tempdir(self, path=None): + """Creates a temporary directory. + A unique directory name is generated if no path name is specified. + The directory is created, and will be removed when the TestCmd + object is destroyed. + """ + if path is None: + try: + path = tempfile.mktemp(prefix=tempfile.template) + except TypeError: + path = tempfile.mktemp() + os.mkdir(path) + + # Symlinks in the path will report things + # differently from os.getcwd(), so chdir there + # and back to fetch the canonical path. + cwd = os.getcwd() + try: + os.chdir(path) + path = os.getcwd() + finally: + os.chdir(cwd) + + # Uppercase the drive letter since the case of drive + # letters is pretty much random on win32: + drive,rest = os.path.splitdrive(path) + if drive: + path = string.upper(drive) + rest + + # + self._dirlist.append(path) + global _Cleanup + try: + _Cleanup.index(self) + except ValueError: + _Cleanup.append(self) + + return path + + def touch(self, path, mtime=None): + """Updates the modification time on the specified file or + directory path name. The default is to update to the + current time if no explicit modification time is specified. + """ + path = self.canonicalize(path) + atime = os.path.getatime(path) + if mtime is None: + mtime = time.time() + os.utime(path, (atime, mtime)) + + def unlink(self, file): + """Unlinks the specified file name. + The file name may be a list, in which case the elements are + concatenated with the os.path.join() method. The file is + assumed to be under the temporary working directory unless it + is an absolute path name. + """ + file = self.canonicalize(file) + os.unlink(file) + + def verbose_set(self, verbose): + """Set the verbose level. + """ + self.verbose = verbose + + def where_is(self, file, path=None, pathext=None): + """Find an executable file. + """ + if is_List(file): + file = apply(os.path.join, tuple(file)) + if not os.path.isabs(file): + file = where_is(file, path, pathext) + return file + + def workdir_set(self, path): + """Creates a temporary working directory with the specified + path name. If the path is a null string (''), a unique + directory name is created. + """ + if (path != None): + if path == '': + path = None + path = self.tempdir(path) + self.workdir = path + + def workpath(self, *args): + """Returns the absolute path name to a subdirectory or file + within the current temporary working directory. Concatenates + the temporary working directory name with the specified + arguments using the os.path.join() method. + """ + return apply(os.path.join, (self.workdir,) + tuple(args)) + + def readable(self, top, read=1): + """Make the specified directory tree readable (read == 1) + or not (read == None). + + This method has no effect on Windows systems, which use a + completely different mechanism to control file readability. + """ + + if sys.platform == 'win32': + return + + if read: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IREAD)) + else: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IREAD)) + + if os.path.isfile(top): + # If it's a file, that's easy, just chmod it. + do_chmod(top) + elif read: + # It's a directory and we're trying to turn on read + # permission, so it's also pretty easy, just chmod the + # directory and then chmod every entry on our walk down the + # tree. Because os.path.walk() is top-down, we'll enable + # read permission on any directories that have it disabled + # before os.path.walk() tries to list their contents. + do_chmod(top) + + def chmod_entries(arg, dirname, names, do_chmod=do_chmod): + for n in names: + do_chmod(os.path.join(dirname, n)) + + os.path.walk(top, chmod_entries, None) + else: + # It's a directory and we're trying to turn off read + # permission, which means we have to chmod the directoreis + # in the tree bottom-up, lest disabling read permission from + # the top down get in the way of being able to get at lower + # parts of the tree. But os.path.walk() visits things top + # down, so we just use an object to collect a list of all + # of the entries in the tree, reverse the list, and then + # chmod the reversed (bottom-up) list. + col = Collector(top) + os.path.walk(top, col, None) + col.entries.reverse() + for d in col.entries: do_chmod(d) + + def writable(self, top, write=1): + """Make the specified directory tree writable (write == 1) + or not (write == None). + """ + + if sys.platform == 'win32': + + if write: + def do_chmod(fname): + try: os.chmod(fname, stat.S_IWRITE) + except OSError: pass + else: + def do_chmod(fname): + try: os.chmod(fname, stat.S_IREAD) + except OSError: pass + + else: + + if write: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200)) + else: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200)) + + if os.path.isfile(top): + do_chmod(top) + else: + col = Collector(top) + os.path.walk(top, col, None) + for d in col.entries: do_chmod(d) + + def executable(self, top, execute=1): + """Make the specified directory tree executable (execute == 1) + or not (execute == None). + + This method has no effect on Windows systems, which use a + completely different mechanism to control file executability. + """ + + if sys.platform == 'win32': + return + + if execute: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IEXEC)) + else: + def do_chmod(fname): + try: st = os.stat(fname) + except OSError: pass + else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IEXEC)) + + if os.path.isfile(top): + # If it's a file, that's easy, just chmod it. + do_chmod(top) + elif execute: + # It's a directory and we're trying to turn on execute + # permission, so it's also pretty easy, just chmod the + # directory and then chmod every entry on our walk down the + # tree. Because os.path.walk() is top-down, we'll enable + # execute permission on any directories that have it disabled + # before os.path.walk() tries to list their contents. + do_chmod(top) + + def chmod_entries(arg, dirname, names, do_chmod=do_chmod): + for n in names: + do_chmod(os.path.join(dirname, n)) + + os.path.walk(top, chmod_entries, None) + else: + # It's a directory and we're trying to turn off execute + # permission, which means we have to chmod the directories + # in the tree bottom-up, lest disabling execute permission from + # the top down get in the way of being able to get at lower + # parts of the tree. But os.path.walk() visits things top + # down, so we just use an object to collect a list of all + # of the entries in the tree, reverse the list, and then + # chmod the reversed (bottom-up) list. + col = Collector(top) + os.path.walk(top, col, None) + col.entries.reverse() + for d in col.entries: do_chmod(d) + + def write(self, file, content, mode = 'wb'): + """Writes the specified content text (second argument) to the + specified file name (first argument). The file name may be + a list, in which case the elements are concatenated with the + os.path.join() method. The file is created under the temporary + working directory. Any subdirectories in the path must already + exist. The I/O mode for the file may be specified; it must + begin with a 'w'. The default is 'wb' (binary write). + """ + file = self.canonicalize(file) + if mode[0] != 'w': + raise ValueError, "mode must begin with 'w'" + with open(file, mode) as f: + f.write(content) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/tools/gyp/test/lib/TestCommon.py b/tools/gyp/test/lib/TestCommon.py new file mode 100644 index 0000000000..4aa7185a2f --- /dev/null +++ b/tools/gyp/test/lib/TestCommon.py @@ -0,0 +1,581 @@ +""" +TestCommon.py: a testing framework for commands and scripts + with commonly useful error handling + +The TestCommon module provides a simple, high-level interface for writing +tests of executable commands and scripts, especially commands and scripts +that interact with the file system. All methods throw exceptions and +exit on failure, with useful error messages. This makes a number of +explicit checks unnecessary, making the test scripts themselves simpler +to write and easier to read. + +The TestCommon class is a subclass of the TestCmd class. In essence, +TestCommon is a wrapper that handles common TestCmd error conditions in +useful ways. You can use TestCommon directly, or subclass it for your +program and add additional (or override) methods to tailor it to your +program's specific needs. Alternatively, the TestCommon class serves +as a useful example of how to define your own TestCmd subclass. + +As a subclass of TestCmd, TestCommon provides access to all of the +variables and methods from the TestCmd module. Consequently, you can +use any variable or method documented in the TestCmd module without +having to explicitly import TestCmd. + +A TestCommon environment object is created via the usual invocation: + + import TestCommon + test = TestCommon.TestCommon() + +You can use all of the TestCmd keyword arguments when instantiating a +TestCommon object; see the TestCmd documentation for details. + +Here is an overview of the methods and keyword arguments that are +provided by the TestCommon class: + + test.must_be_writable('file1', ['file2', ...]) + + test.must_contain('file', 'required text\n') + + test.must_contain_all_lines(output, lines, ['title', find]) + + test.must_contain_any_line(output, lines, ['title', find]) + + test.must_exist('file1', ['file2', ...]) + + test.must_match('file', "expected contents\n") + + test.must_not_be_writable('file1', ['file2', ...]) + + test.must_not_contain('file', 'banned text\n') + + test.must_not_contain_any_line(output, lines, ['title', find]) + + test.must_not_exist('file1', ['file2', ...]) + + test.run(options = "options to be prepended to arguments", + stdout = "expected standard output from the program", + stderr = "expected error output from the program", + status = expected_status, + match = match_function) + +The TestCommon module also provides the following variables + + TestCommon.python_executable + TestCommon.exe_suffix + TestCommon.obj_suffix + TestCommon.shobj_prefix + TestCommon.shobj_suffix + TestCommon.lib_prefix + TestCommon.lib_suffix + TestCommon.dll_prefix + TestCommon.dll_suffix + +""" + +# Copyright 2000-2010 Steven Knight +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +__author__ = "Steven Knight <knight at baldmt dot com>" +__revision__ = "TestCommon.py 0.37.D001 2010/01/11 16:55:50 knight" +__version__ = "0.37" + +import copy +import os +import os.path +import stat +import string +import sys +import types +import UserList + +from TestCmd import * +from TestCmd import __all__ + +__all__.extend([ 'TestCommon', + 'exe_suffix', + 'obj_suffix', + 'shobj_prefix', + 'shobj_suffix', + 'lib_prefix', + 'lib_suffix', + 'dll_prefix', + 'dll_suffix', + ]) + +# Variables that describe the prefixes and suffixes on this system. +if sys.platform == 'win32': + exe_suffix = '.exe' + obj_suffix = '.obj' + shobj_suffix = '.obj' + shobj_prefix = '' + lib_prefix = '' + lib_suffix = '.lib' + dll_prefix = '' + dll_suffix = '.dll' +elif sys.platform == 'cygwin': + exe_suffix = '.exe' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = '' + dll_suffix = '.dll' +elif string.find(sys.platform, 'irix') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.o' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.so' +elif string.find(sys.platform, 'darwin') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.dylib' +elif string.find(sys.platform, 'sunos') != -1: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = 'so_' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.dylib' +else: + exe_suffix = '' + obj_suffix = '.o' + shobj_suffix = '.os' + shobj_prefix = '' + lib_prefix = 'lib' + lib_suffix = '.a' + dll_prefix = 'lib' + dll_suffix = '.so' + +def is_List(e): + return type(e) is types.ListType \ + or isinstance(e, UserList.UserList) + +def is_writable(f): + mode = os.stat(f)[stat.ST_MODE] + return mode & stat.S_IWUSR + +def separate_files(flist): + existing = [] + missing = [] + for f in flist: + if os.path.exists(f): + existing.append(f) + else: + missing.append(f) + return existing, missing + +if os.name == 'posix': + def _failed(self, status = 0): + if self.status is None or status is None: + return None + return _status(self) != status + def _status(self): + return self.status +elif os.name == 'nt': + def _failed(self, status = 0): + return not (self.status is None or status is None) and \ + self.status != status + def _status(self): + return self.status + +class TestCommon(TestCmd): + + # Additional methods from the Perl Test::Cmd::Common module + # that we may wish to add in the future: + # + # $test->subdir('subdir', ...); + # + # $test->copy('src_file', 'dst_file'); + + def __init__(self, **kw): + """Initialize a new TestCommon instance. This involves just + calling the base class initialization, and then changing directory + to the workdir. + """ + apply(TestCmd.__init__, [self], kw) + os.chdir(self.workdir) + + def must_be_writable(self, *files): + """Ensures that the specified file(s) exist and are writable. + An individual file can be specified as a list of directory names, + in which case the pathname will be constructed by concatenating + them. Exits FAILED if any of the files does not exist or is + not writable. + """ + files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) + existing, missing = separate_files(files) + unwritable = filter(lambda x, iw=is_writable: not iw(x), existing) + if missing: + print "Missing files: `%s'" % string.join(missing, "', `") + if unwritable: + print "Unwritable files: `%s'" % string.join(unwritable, "', `") + self.fail_test(missing + unwritable) + + def must_contain(self, file, required, mode = 'rb'): + """Ensures that the specified file contains the required text. + """ + file_contents = self.read(file, mode) + contains = (string.find(file_contents, required) != -1) + if not contains: + print "File `%s' does not contain required string." % file + print self.banner('Required string ') + print required + print self.banner('%s contents ' % file) + print file_contents + self.fail_test(not contains) + + def must_contain_all_lines(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + contains all of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + find = lambda o, l: string.find(o, l) != -1 + missing = [] + for line in lines: + if not find(output, line): + missing.append(line) + + if missing: + if title is None: + title = 'output' + sys.stdout.write("Missing expected lines from %s:\n" % title) + for line in missing: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ')) + sys.stdout.write(output) + self.fail_test() + + def must_contain_any_line(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + contains at least one of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + find = lambda o, l: string.find(o, l) != -1 + for line in lines: + if find(output, line): + return + + if title is None: + title = 'output' + sys.stdout.write("Missing any expected line from %s:\n" % title) + for line in lines: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ')) + sys.stdout.write(output) + self.fail_test() + + def must_contain_lines(self, lines, output, title=None): + # Deprecated; retain for backwards compatibility. + return self.must_contain_all_lines(output, lines, title) + + def must_exist(self, *files): + """Ensures that the specified file(s) must exist. An individual + file be specified as a list of directory names, in which case the + pathname will be constructed by concatenating them. Exits FAILED + if any of the files does not exist. + """ + files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) + missing = filter(lambda x: not os.path.exists(x), files) + if missing: + print "Missing files: `%s'" % string.join(missing, "', `") + self.fail_test(missing) + + def must_match(self, file, expect, mode = 'rb'): + """Matches the contents of the specified file (first argument) + against the expected contents (second argument). The expected + contents are a list of lines or a string which will be split + on newlines. + """ + file_contents = self.read(file, mode) + try: + self.fail_test(not self.match(file_contents, expect)) + except KeyboardInterrupt: + raise + except: + print "Unexpected contents of `%s'" % file + self.diff(expect, file_contents, 'contents ') + raise + + def must_not_contain(self, file, banned, mode = 'rb'): + """Ensures that the specified file doesn't contain the banned text. + """ + file_contents = self.read(file, mode) + contains = (string.find(file_contents, banned) != -1) + if contains: + print "File `%s' contains banned string." % file + print self.banner('Banned string ') + print banned + print self.banner('%s contents ' % file) + print file_contents + self.fail_test(contains) + + def must_not_contain_any_line(self, output, lines, title=None, find=None): + """Ensures that the specified output string (first argument) + does not contain any of the specified lines (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + find = lambda o, l: string.find(o, l) != -1 + unexpected = [] + for line in lines: + if find(output, line): + unexpected.append(line) + + if unexpected: + if title is None: + title = 'output' + sys.stdout.write("Unexpected lines in %s:\n" % title) + for line in unexpected: + sys.stdout.write(' ' + repr(line) + '\n') + sys.stdout.write(self.banner(title + ' ')) + sys.stdout.write(output) + self.fail_test() + + def must_not_contain_lines(self, lines, output, title=None): + return self.must_not_contain_any_line(output, lines, title) + + def must_not_exist(self, *files): + """Ensures that the specified file(s) must not exist. + An individual file be specified as a list of directory names, in + which case the pathname will be constructed by concatenating them. + Exits FAILED if any of the files exists. + """ + files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) + existing = filter(os.path.exists, files) + if existing: + print "Unexpected files exist: `%s'" % string.join(existing, "', `") + self.fail_test(existing) + + + def must_not_be_writable(self, *files): + """Ensures that the specified file(s) exist and are not writable. + An individual file can be specified as a list of directory names, + in which case the pathname will be constructed by concatenating + them. Exits FAILED if any of the files does not exist or is + writable. + """ + files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) + existing, missing = separate_files(files) + writable = filter(is_writable, existing) + if missing: + print "Missing files: `%s'" % string.join(missing, "', `") + if writable: + print "Writable files: `%s'" % string.join(writable, "', `") + self.fail_test(missing + writable) + + def _complete(self, actual_stdout, expected_stdout, + actual_stderr, expected_stderr, status, match): + """ + Post-processes running a subcommand, checking for failure + status and displaying output appropriately. + """ + if _failed(self, status): + expect = '' + if status != 0: + expect = " (expected %s)" % str(status) + print "%s returned %s%s" % (self.program, str(_status(self)), expect) + print self.banner('STDOUT ') + print actual_stdout + print self.banner('STDERR ') + print actual_stderr + self.fail_test() + if not expected_stdout is None and not match(actual_stdout, expected_stdout): + self.diff(expected_stdout, actual_stdout, 'STDOUT ') + if actual_stderr: + print self.banner('STDERR ') + print actual_stderr + self.fail_test() + if not expected_stderr is None and not match(actual_stderr, expected_stderr): + print self.banner('STDOUT ') + print actual_stdout + self.diff(expected_stderr, actual_stderr, 'STDERR ') + self.fail_test() + + def start(self, program = None, + interpreter = None, + arguments = None, + universal_newlines = None, + **kw): + """ + Starts a program or script for the test environment. + + This handles the "options" keyword argument and exceptions. + """ + try: + options = kw['options'] + del kw['options'] + except KeyError: + pass + else: + if options: + if arguments is None: + arguments = options + else: + arguments = options + " " + arguments + try: + return apply(TestCmd.start, + (self, program, interpreter, arguments, universal_newlines), + kw) + except KeyboardInterrupt: + raise + except Exception, e: + print self.banner('STDOUT ') + try: + print self.stdout() + except IndexError: + pass + print self.banner('STDERR ') + try: + print self.stderr() + except IndexError: + pass + cmd_args = self.command_args(program, interpreter, arguments) + sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) + raise e + + def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): + """ + Finishes and waits for the process being run under control of + the specified popen argument. Additional arguments are similar + to those of the run() method: + + stdout The expected standard output from + the command. A value of None means + don't test standard output. + + stderr The expected error output from + the command. A value of None means + don't test error output. + + status The expected exit status from the + command. A value of None means don't + test exit status. + """ + apply(TestCmd.finish, (self, popen,), kw) + match = kw.get('match', self.match) + self._complete(self.stdout(), stdout, + self.stderr(), stderr, status, match) + + def run(self, options = None, arguments = None, + stdout = None, stderr = '', status = 0, **kw): + """Runs the program under test, checking that the test succeeded. + + The arguments are the same as the base TestCmd.run() method, + with the addition of: + + options Extra options that get appended to the beginning + of the arguments. + + stdout The expected standard output from + the command. A value of None means + don't test standard output. + + stderr The expected error output from + the command. A value of None means + don't test error output. + + status The expected exit status from the + command. A value of None means don't + test exit status. + + By default, this expects a successful exit (status = 0), does + not test standard output (stdout = None), and expects that error + output is empty (stderr = ""). + """ + if options: + if arguments is None: + arguments = options + else: + arguments = options + " " + arguments + kw['arguments'] = arguments + try: + match = kw['match'] + del kw['match'] + except KeyError: + match = self.match + apply(TestCmd.run, [self], kw) + self._complete(self.stdout(), stdout, + self.stderr(), stderr, status, match) + + def skip_test(self, message="Skipping test.\n"): + """Skips a test. + + Proper test-skipping behavior is dependent on the external + TESTCOMMON_PASS_SKIPS environment variable. If set, we treat + the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. + In either case, we print the specified message as an indication + that the substance of the test was skipped. + + (This was originally added to support development under Aegis. + Technically, skipping a test is a NO RESULT, but Aegis would + treat that as a test failure and prevent the change from going to + the next step. Since we ddn't want to force anyone using Aegis + to have to install absolutely every tool used by the tests, we + would actually report to Aegis that a skipped test has PASSED + so that the workflow isn't held up.) + """ + if message: + sys.stdout.write(message) + sys.stdout.flush() + pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS') + if pass_skips in [None, 0, '0']: + # skip=1 means skip this function when showing where this + # result came from. They only care about the line where the + # script called test.skip_test(), not the line number where + # we call test.no_result(). + self.no_result(skip=1) + else: + # We're under the development directory for this change, + # so this is an Aegis invocation; pass the test (exit 0). + self.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/tools/gyp/test/lib/TestGyp.py b/tools/gyp/test/lib/TestGyp.py new file mode 100644 index 0000000000..2b4b0db967 --- /dev/null +++ b/tools/gyp/test/lib/TestGyp.py @@ -0,0 +1,817 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +TestGyp.py: a testing framework for GYP integration tests. +""" + +import os +import re +import shutil +import stat +import sys +import tempfile + +import TestCommon +from TestCommon import __all__ + +__all__.extend([ + 'TestGyp', +]) + + +class TestGypBase(TestCommon.TestCommon): + """ + Class for controlling end-to-end tests of gyp generators. + + Instantiating this class will create a temporary directory and + arrange for its destruction (via the TestCmd superclass) and + copy all of the non-gyptest files in the directory hierarchy of the + executing script. + + The default behavior is to test the 'gyp' or 'gyp.bat' file in the + current directory. An alternative may be specified explicitly on + instantiation, or by setting the TESTGYP_GYP environment variable. + + This class should be subclassed for each supported gyp generator + (format). Various abstract methods below define calling signatures + used by the test scripts to invoke builds on the generated build + configuration and to run executables generated by those builds. + """ + + build_tool = None + build_tool_list = [] + + _exe = TestCommon.exe_suffix + _obj = TestCommon.obj_suffix + shobj_ = TestCommon.shobj_prefix + _shobj = TestCommon.shobj_suffix + lib_ = TestCommon.lib_prefix + _lib = TestCommon.lib_suffix + dll_ = TestCommon.dll_prefix + _dll = TestCommon.dll_suffix + + # Constants to represent different targets. + ALL = '__all__' + DEFAULT = '__default__' + + # Constants for different target types. + EXECUTABLE = '__executable__' + STATIC_LIB = '__static_lib__' + SHARED_LIB = '__shared_lib__' + + def __init__(self, gyp=None, *args, **kw): + self.origin_cwd = os.path.abspath(os.path.dirname(sys.argv[0])) + + if not gyp: + gyp = os.environ.get('TESTGYP_GYP') + if not gyp: + if sys.platform == 'win32': + gyp = 'gyp.bat' + else: + gyp = 'gyp' + self.gyp = os.path.abspath(gyp) + + self.initialize_build_tool() + + if not kw.has_key('match'): + kw['match'] = TestCommon.match_exact + + # Put test output in out/testworkarea by default. + # Use temporary names so there are no collisions. + workdir = os.path.join('out', kw.get('workdir', 'testworkarea')) + # Create work area if it doesn't already exist. + try: + os.makedirs(workdir) + except OSError: + pass + kw['workdir'] = tempfile.mktemp(prefix='testgyp.', dir=workdir) + + formats = kw.get('formats', []) + if kw.has_key('formats'): + del kw['formats'] + + super(TestGypBase, self).__init__(*args, **kw) + + excluded_formats = set([f for f in formats if f[0] == '!']) + included_formats = set(formats) - excluded_formats + if ('!'+self.format in excluded_formats or + included_formats and self.format not in included_formats): + msg = 'Invalid test for %r format; skipping test.\n' + self.skip_test(msg % self.format) + + self.copy_test_configuration(self.origin_cwd, self.workdir) + self.set_configuration(None) + + # Set $HOME so that gyp doesn't read the user's actual + # ~/.gyp/include.gypi file, which may contain variables + # and other settings that would change the output. + os.environ['HOME'] = self.workpath() + # Clear $GYP_DEFINES for the same reason. + if 'GYP_DEFINES' in os.environ: + del os.environ['GYP_DEFINES'] + + def built_file_must_exist(self, name, type=None, **kw): + """ + Fails the test if the specified built file name does not exist. + """ + return self.must_exist(self.built_file_path(name, type, **kw)) + + def built_file_must_not_exist(self, name, type=None, **kw): + """ + Fails the test if the specified built file name exists. + """ + return self.must_not_exist(self.built_file_path(name, type, **kw)) + + def built_file_must_match(self, name, contents, **kw): + """ + Fails the test if the contents of the specified built file name + do not match the specified contents. + """ + return self.must_match(self.built_file_path(name, **kw), contents) + + def built_file_must_not_match(self, name, contents, **kw): + """ + Fails the test if the contents of the specified built file name + match the specified contents. + """ + return self.must_not_match(self.built_file_path(name, **kw), contents) + + def copy_test_configuration(self, source_dir, dest_dir): + """ + Copies the test configuration from the specified source_dir + (the directory in which the test script lives) to the + specified dest_dir (a temporary working directory). + + This ignores all files and directories that begin with + the string 'gyptest', and all '.svn' subdirectories. + """ + for root, dirs, files in os.walk(source_dir): + if '.svn' in dirs: + dirs.remove('.svn') + dirs = [ d for d in dirs if not d.startswith('gyptest') ] + files = [ f for f in files if not f.startswith('gyptest') ] + for dirname in dirs: + source = os.path.join(root, dirname) + destination = source.replace(source_dir, dest_dir) + os.mkdir(destination) + if sys.platform != 'win32': + shutil.copystat(source, destination) + for filename in files: + source = os.path.join(root, filename) + destination = source.replace(source_dir, dest_dir) + shutil.copy2(source, destination) + + def initialize_build_tool(self): + """ + Initializes the .build_tool attribute. + + Searches the .build_tool_list for an executable name on the user's + $PATH. The first tool on the list is used as-is if nothing is found + on the current $PATH. + """ + for build_tool in self.build_tool_list: + if not build_tool: + continue + if os.path.isabs(build_tool): + self.build_tool = build_tool + return + build_tool = self.where_is(build_tool) + if build_tool: + self.build_tool = build_tool + return + + if self.build_tool_list: + self.build_tool = self.build_tool_list[0] + + def relocate(self, source, destination): + """ + Renames (relocates) the specified source (usually a directory) + to the specified destination, creating the destination directory + first if necessary. + + Note: Don't use this as a generic "rename" operation. In the + future, "relocating" parts of a GYP tree may affect the state of + the test to modify the behavior of later method calls. + """ + destination_dir = os.path.dirname(destination) + if not os.path.exists(destination_dir): + self.subdir(destination_dir) + os.rename(source, destination) + + def report_not_up_to_date(self): + """ + Reports that a build is not up-to-date. + + This provides common reporting for formats that have complicated + conditions for checking whether a build is up-to-date. Formats + that expect exact output from the command (make, scons) can + just set stdout= when they call the run_build() method. + """ + print "Build is not up-to-date:" + print self.banner('STDOUT ') + print self.stdout() + stderr = self.stderr() + if stderr: + print self.banner('STDERR ') + print stderr + + def run_gyp(self, gyp_file, *args, **kw): + """ + Runs gyp against the specified gyp_file with the specified args. + """ + # TODO: --depth=. works around Chromium-specific tree climbing. + args = ('--depth=.', '--format='+self.format, gyp_file) + args + return self.run(program=self.gyp, arguments=args, **kw) + + def run(self, *args, **kw): + """ + Executes a program by calling the superclass .run() method. + + This exists to provide a common place to filter out keyword + arguments implemented in this layer, without having to update + the tool-specific subclasses or clutter the tests themselves + with platform-specific code. + """ + if kw.has_key('SYMROOT'): + del kw['SYMROOT'] + super(TestGypBase, self).run(*args, **kw) + + def set_configuration(self, configuration): + """ + Sets the configuration, to be used for invoking the build + tool and testing potential built output. + """ + self.configuration = configuration + + def configuration_dirname(self): + if self.configuration: + return self.configuration.split('|')[0] + else: + return 'Default' + + def configuration_buildname(self): + if self.configuration: + return self.configuration + else: + return 'Default' + + # + # Abstract methods to be defined by format-specific subclasses. + # + + def build(self, gyp_file, target=None, **kw): + """ + Runs a build of the specified target against the configuration + generated from the specified gyp_file. + + A 'target' argument of None or the special value TestGyp.DEFAULT + specifies the default argument for the underlying build tool. + A 'target' argument of TestGyp.ALL specifies the 'all' target + (if any) of the underlying build tool. + """ + raise NotImplementedError + + def built_file_path(self, name, type=None, **kw): + """ + Returns a path to the specified file name, of the specified type. + """ + raise NotImplementedError + + def built_file_basename(self, name, type=None, **kw): + """ + Returns the base name of the specified file name, of the specified type. + + A bare=True keyword argument specifies that prefixes and suffixes shouldn't + be applied. + """ + if not kw.get('bare'): + if type == self.EXECUTABLE: + name = name + self._exe + elif type == self.STATIC_LIB: + name = self.lib_ + name + self._lib + elif type == self.SHARED_LIB: + name = self.dll_ + name + self._dll + return name + + def run_built_executable(self, name, *args, **kw): + """ + Runs an executable program built from a gyp-generated configuration. + + The specified name should be independent of any particular generator. + Subclasses should find the output executable in the appropriate + output build directory, tack on any necessary executable suffix, etc. + """ + raise NotImplementedError + + def up_to_date(self, gyp_file, target=None, **kw): + """ + Verifies that a build of the specified target is up to date. + + The subclass should implement this by calling build() + (or a reasonable equivalent), checking whatever conditions + will tell it the build was an "up to date" null build, and + failing if it isn't. + """ + raise NotImplementedError + + +class TestGypGypd(TestGypBase): + """ + Subclass for testing the GYP 'gypd' generator (spit out the + internal data structure as pretty-printed Python). + """ + format = 'gypd' + + +class TestGypMake(TestGypBase): + """ + Subclass for testing the GYP Make generator. + """ + format = 'make' + build_tool_list = ['make'] + ALL = 'all' + def build(self, gyp_file, target=None, **kw): + """ + Runs a Make build using the Makefiles generated from the specified + gyp_file. + """ + arguments = kw.get('arguments', [])[:] + if self.configuration: + arguments.append('BUILDTYPE=' + self.configuration) + if target not in (None, self.DEFAULT): + arguments.append(target) + # Sub-directory builds provide per-gyp Makefiles (i.e. + # Makefile.gyp_filename), so use that if there is no Makefile. + chdir = kw.get('chdir', '') + if not os.path.exists(os.path.join(chdir, 'Makefile')): + print "NO Makefile in " + os.path.join(chdir, 'Makefile') + arguments.insert(0, '-f') + arguments.insert(1, os.path.splitext(gyp_file)[0] + '.Makefile') + kw['arguments'] = arguments + return self.run(program=self.build_tool, **kw) + def up_to_date(self, gyp_file, target=None, **kw): + """ + Verifies that a build of the specified Make target is up to date. + """ + if target in (None, self.DEFAULT): + message_target = 'all' + else: + message_target = target + kw['stdout'] = "make: Nothing to be done for `%s'.\n" % message_target + return self.build(gyp_file, target, **kw) + def run_built_executable(self, name, *args, **kw): + """ + Runs an executable built by Make. + """ + configuration = self.configuration_dirname() + libdir = os.path.join('out', configuration, 'lib') + # TODO(piman): when everything is cross-compile safe, remove lib.target + os.environ['LD_LIBRARY_PATH'] = libdir + '.host:' + libdir + '.target' + # Enclosing the name in a list avoids prepending the original dir. + program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] + return self.run(program=program, *args, **kw) + def built_file_path(self, name, type=None, **kw): + """ + Returns a path to the specified file name, of the specified type, + as built by Make. + + Built files are in the subdirectory 'out/{configuration}'. + The default is 'out/Default'. + + A chdir= keyword argument specifies the source directory + relative to which the output subdirectory can be found. + + "type" values of STATIC_LIB or SHARED_LIB append the necessary + prefixes and suffixes to a platform-independent library base name. + + A subdir= keyword argument specifies a library subdirectory within + the default 'obj.target'. + """ + result = [] + chdir = kw.get('chdir') + if chdir: + result.append(chdir) + configuration = self.configuration_dirname() + result.extend(['out', configuration]) + if type == self.STATIC_LIB and sys.platform != 'darwin': + result.append('obj.target') + elif type == self.SHARED_LIB and sys.platform != 'darwin': + result.append('lib.target') + subdir = kw.get('subdir') + if subdir: + result.append(subdir) + result.append(self.built_file_basename(name, type, **kw)) + return self.workpath(*result) + + +class TestGypNinja(TestGypBase): + """ + Subclass for testing the GYP Ninja generator. + """ + format = 'ninja' + build_tool_list = ['ninja'] + ALL = 'all' + DEFAULT = 'all' + + # The default library prefix is computed from TestCommon.lib_prefix, + # but ninja uses no prefix for static libraries. + lib_ = '' + + def run_gyp(self, gyp_file, *args, **kw): + # We must pass the desired configuration as a parameter. + if self.configuration: + args = list(args) + ['-Gconfig=' + self.configuration] + # Stash the gyp configuration we used to run gyp, so we can + # know whether we need to rerun it later. + self.last_gyp_configuration = self.configuration + TestGypBase.run_gyp(self, gyp_file, *args, **kw) + + def build(self, gyp_file, target=None, **kw): + if self.last_gyp_configuration != self.configuration: + # Rerun gyp if necessary. + self.run_gyp(gyp_file) + + arguments = kw.get('arguments', [])[:] + + # Add a -C output/path to the command line. + arguments.append('-C') + arguments.append(os.path.join('out', self.configuration_dirname())) + + if target is None: + target = 'all' + arguments.append(target) + + kw['arguments'] = arguments + return self.run(program=self.build_tool, **kw) + + def run_built_executable(self, name, *args, **kw): + # Enclosing the name in a list avoids prepending the original dir. + program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] + if sys.platform == 'darwin': + libdir = os.path.join('out', 'Default', 'lib') + if self.configuration: + libdir = os.path.join('out', self.configuration, 'lib') + os.environ['DYLD_LIBRARY_PATH'] = libdir + return self.run(program=program, *args, **kw) + + def built_file_path(self, name, type=None, **kw): + result = [] + chdir = kw.get('chdir') + if chdir: + result.append(chdir) + result.append('out') + result.append(self.configuration_dirname()) + if type == self.STATIC_LIB: + if sys.platform != 'darwin': + result.append('obj') + elif type == self.SHARED_LIB: + result.append('lib') + subdir = kw.get('subdir') + if subdir: + result.append(subdir) + result.append(self.built_file_basename(name, type, **kw)) + return self.workpath(*result) + + def up_to_date(self, gyp_file, target=None, **kw): + result = self.build(gyp_file, target, **kw) + if not result: + stdout = self.stdout() + if 'ninja: no work to do' not in stdout: + self.report_not_up_to_date() + self.fail_test() + return result + + +class TestGypMSVS(TestGypBase): + """ + Subclass for testing the GYP Visual Studio generator. + """ + format = 'msvs' + + u = r'=== Build: 0 succeeded, 0 failed, (\d+) up-to-date, 0 skipped ===' + up_to_date_re = re.compile(u, re.M) + + # Initial None element will indicate to our .initialize_build_tool() + # method below that 'devenv' was not found on %PATH%. + # + # Note: we must use devenv.com to be able to capture build output. + # Directly executing devenv.exe only sends output to BuildLog.htm. + build_tool_list = [None, 'devenv.com'] + + def initialize_build_tool(self): + """ Initializes the Visual Studio .build_tool and .uses_msbuild parameters. + + We use the value specified by GYP_MSVS_VERSION. If not specified, we + search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable. + Failing that, we search for likely deployment paths. + """ + super(TestGypMSVS, self).initialize_build_tool() + possible_roots = ['C:\\Program Files (x86)', 'C:\\Program Files', + 'E:\\Program Files (x86)', 'E:\\Program Files'] + possible_paths = { + '2010': r'Microsoft Visual Studio 10.0\Common7\IDE\devenv.com', + '2008': r'Microsoft Visual Studio 9.0\Common7\IDE\devenv.com', + '2005': r'Microsoft Visual Studio 8\Common7\IDE\devenv.com'} + msvs_version = os.environ.get('GYP_MSVS_VERSION', 'auto') + if msvs_version in possible_paths: + # Check that the path to the specified GYP_MSVS_VERSION exists. + path = possible_paths[msvs_version] + for r in possible_roots: + bt = os.path.join(r, path) + if os.path.exists(bt): + self.build_tool = bt + self.uses_msbuild = msvs_version >= '2010' + return + else: + print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" ' + 'but corresponding "%s" was not found.' % (msvs_version, path)) + if self.build_tool: + # We found 'devenv' on the path, use that and try to guess the version. + for version, path in possible_paths.iteritems(): + if self.build_tool.find(path) >= 0: + self.uses_msbuild = version >= '2010' + return + else: + # If not, assume not MSBuild. + self.uses_msbuild = False + return + # Neither GYP_MSVS_VERSION nor the path help us out. Iterate through + # the choices looking for a match. + for version, path in possible_paths.iteritems(): + for r in possible_roots: + bt = os.path.join(r, path) + if os.path.exists(bt): + self.build_tool = bt + self.uses_msbuild = msvs_version >= '2010' + return + print 'Error: could not find devenv' + sys.exit(1) + def build(self, gyp_file, target=None, rebuild=False, **kw): + """ + Runs a Visual Studio build using the configuration generated + from the specified gyp_file. + """ + configuration = self.configuration_buildname() + if rebuild: + build = '/Rebuild' + else: + build = '/Build' + arguments = kw.get('arguments', [])[:] + arguments.extend([gyp_file.replace('.gyp', '.sln'), + build, configuration]) + # Note: the Visual Studio generator doesn't add an explicit 'all' + # target, so we just treat it the same as the default. + if target not in (None, self.ALL, self.DEFAULT): + arguments.extend(['/Project', target]) + if self.configuration: + arguments.extend(['/ProjectConfig', self.configuration]) + kw['arguments'] = arguments + return self.run(program=self.build_tool, **kw) + def up_to_date(self, gyp_file, target=None, **kw): + """ + Verifies that a build of the specified Visual Studio target is up to date. + + Beware that VS2010 will behave strangely if you build under + C:\USERS\yourname\AppData\Local. It will cause needless work. The ouptut + will be "1 succeeded and 0 up to date". MSBuild tracing reveals that: + "Project 'C:\Users\...\AppData\Local\...vcxproj' not up to date because + 'C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\VC\BIN\1033\CLUI.DLL' + was modified at 02/21/2011 17:03:30, which is newer than '' which was + modified at 01/01/0001 00:00:00. + + The workaround is to specify a workdir when instantiating the test, e.g. + test = TestGyp.TestGyp(workdir='workarea') + """ + result = self.build(gyp_file, target, **kw) + if not result: + stdout = self.stdout() + + m = self.up_to_date_re.search(stdout) + up_to_date = m and m.group(1) == '1' + if not up_to_date: + self.report_not_up_to_date() + self.fail_test() + return result + def run_built_executable(self, name, *args, **kw): + """ + Runs an executable built by Visual Studio. + """ + configuration = self.configuration_dirname() + # Enclosing the name in a list avoids prepending the original dir. + program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] + return self.run(program=program, *args, **kw) + def built_file_path(self, name, type=None, **kw): + """ + Returns a path to the specified file name, of the specified type, + as built by Visual Studio. + + Built files are in a subdirectory that matches the configuration + name. The default is 'Default'. + + A chdir= keyword argument specifies the source directory + relative to which the output subdirectory can be found. + + "type" values of STATIC_LIB or SHARED_LIB append the necessary + prefixes and suffixes to a platform-independent library base name. + """ + result = [] + chdir = kw.get('chdir') + if chdir: + result.append(chdir) + result.append(self.configuration_dirname()) + if type == self.STATIC_LIB: + result.append('lib') + result.append(self.built_file_basename(name, type, **kw)) + return self.workpath(*result) + + +class TestGypSCons(TestGypBase): + """ + Subclass for testing the GYP SCons generator. + """ + format = 'scons' + build_tool_list = ['scons', 'scons.py'] + ALL = 'all' + def build(self, gyp_file, target=None, **kw): + """ + Runs a scons build using the SCons configuration generated from the + specified gyp_file. + """ + arguments = kw.get('arguments', [])[:] + dirname = os.path.dirname(gyp_file) + if dirname: + arguments.extend(['-C', dirname]) + if self.configuration: + arguments.append('--mode=' + self.configuration) + if target not in (None, self.DEFAULT): + arguments.append(target) + kw['arguments'] = arguments + return self.run(program=self.build_tool, **kw) + def up_to_date(self, gyp_file, target=None, **kw): + """ + Verifies that a build of the specified SCons target is up to date. + """ + if target in (None, self.DEFAULT): + up_to_date_targets = 'all' + else: + up_to_date_targets = target + up_to_date_lines = [] + for arg in up_to_date_targets.split(): + up_to_date_lines.append("scons: `%s' is up to date.\n" % arg) + kw['stdout'] = ''.join(up_to_date_lines) + arguments = kw.get('arguments', [])[:] + arguments.append('-Q') + kw['arguments'] = arguments + return self.build(gyp_file, target, **kw) + def run_built_executable(self, name, *args, **kw): + """ + Runs an executable built by scons. + """ + configuration = self.configuration_dirname() + os.environ['LD_LIBRARY_PATH'] = os.path.join(configuration, 'lib') + # Enclosing the name in a list avoids prepending the original dir. + program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] + return self.run(program=program, *args, **kw) + def built_file_path(self, name, type=None, **kw): + """ + Returns a path to the specified file name, of the specified type, + as built by Scons. + + Built files are in a subdirectory that matches the configuration + name. The default is 'Default'. + + A chdir= keyword argument specifies the source directory + relative to which the output subdirectory can be found. + + "type" values of STATIC_LIB or SHARED_LIB append the necessary + prefixes and suffixes to a platform-independent library base name. + """ + result = [] + chdir = kw.get('chdir') + if chdir: + result.append(chdir) + result.append(self.configuration_dirname()) + if type in (self.STATIC_LIB, self.SHARED_LIB): + result.append('lib') + result.append(self.built_file_basename(name, type, **kw)) + return self.workpath(*result) + + +class TestGypXcode(TestGypBase): + """ + Subclass for testing the GYP Xcode generator. + """ + format = 'xcode' + build_tool_list = ['xcodebuild'] + + phase_script_execution = ("\n" + "PhaseScriptExecution /\\S+/Script-[0-9A-F]+\\.sh\n" + " cd /\\S+\n" + " /bin/sh -c /\\S+/Script-[0-9A-F]+\\.sh\n" + "(make: Nothing to be done for `all'\\.\n)?") + + strip_up_to_date_expressions = [ + # Various actions or rules can run even when the overall build target + # is up to date. Strip those phases' GYP-generated output. + re.compile(phase_script_execution, re.S), + + # The message from distcc_pump can trail the "BUILD SUCCEEDED" + # message, so strip that, too. + re.compile('__________Shutting down distcc-pump include server\n', re.S), + ] + + up_to_date_endings = ( + 'Checking Dependencies...\n** BUILD SUCCEEDED **\n', # Xcode 3.0/3.1 + 'Check dependencies\n** BUILD SUCCEEDED **\n\n', # Xcode 3.2 + ) + + def build(self, gyp_file, target=None, **kw): + """ + Runs an xcodebuild using the .xcodeproj generated from the specified + gyp_file. + """ + # Be sure we're working with a copy of 'arguments' since we modify it. + # The caller may not be expecting it to be modified. + arguments = kw.get('arguments', [])[:] + arguments.extend(['-project', gyp_file.replace('.gyp', '.xcodeproj')]) + if target == self.ALL: + arguments.append('-alltargets',) + elif target not in (None, self.DEFAULT): + arguments.extend(['-target', target]) + if self.configuration: + arguments.extend(['-configuration', self.configuration]) + symroot = kw.get('SYMROOT', '$SRCROOT/build') + if symroot: + arguments.append('SYMROOT='+symroot) + kw['arguments'] = arguments + return self.run(program=self.build_tool, **kw) + def up_to_date(self, gyp_file, target=None, **kw): + """ + Verifies that a build of the specified Xcode target is up to date. + """ + result = self.build(gyp_file, target, **kw) + if not result: + output = self.stdout() + for expression in self.strip_up_to_date_expressions: + output = expression.sub('', output) + if not output.endswith(self.up_to_date_endings): + self.report_not_up_to_date() + self.fail_test() + return result + def run_built_executable(self, name, *args, **kw): + """ + Runs an executable built by xcodebuild. + """ + configuration = self.configuration_dirname() + os.environ['DYLD_LIBRARY_PATH'] = os.path.join('build', configuration) + # Enclosing the name in a list avoids prepending the original dir. + program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] + return self.run(program=program, *args, **kw) + def built_file_path(self, name, type=None, **kw): + """ + Returns a path to the specified file name, of the specified type, + as built by Xcode. + + Built files are in the subdirectory 'build/{configuration}'. + The default is 'build/Default'. + + A chdir= keyword argument specifies the source directory + relative to which the output subdirectory can be found. + + "type" values of STATIC_LIB or SHARED_LIB append the necessary + prefixes and suffixes to a platform-independent library base name. + """ + result = [] + chdir = kw.get('chdir') + if chdir: + result.append(chdir) + configuration = self.configuration_dirname() + result.extend(['build', configuration]) + result.append(self.built_file_basename(name, type, **kw)) + return self.workpath(*result) + + +format_class_list = [ + TestGypGypd, + TestGypMake, + TestGypMSVS, + TestGypNinja, + TestGypSCons, + TestGypXcode, +] + +def TestGyp(*args, **kw): + """ + Returns an appropriate TestGyp* instance for a specified GYP format. + """ + format = kw.get('format') + if format: + del kw['format'] + else: + format = os.environ.get('TESTGYP_FORMAT') + for format_class in format_class_list: + if format == format_class.format: + return format_class(*args, **kw) + raise Exception, "unknown format %r" % format diff --git a/tools/gyp/test/library/gyptest-shared-obj-install-path.py b/tools/gyp/test/library/gyptest-shared-obj-install-path.py new file mode 100755 index 0000000000..2cf1a28458 --- /dev/null +++ b/tools/gyp/test/library/gyptest-shared-obj-install-path.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that .so files that are order only dependencies are specified by +their install location rather than by their alias. +""" + +# Python 2.5 needs this for the with statement. +from __future__ import with_statement + +import os +import TestGyp + +test = TestGyp.TestGyp(formats=['make']) + +test.run_gyp('shared_dependency.gyp', + chdir='src') +test.relocate('src', 'relocate/src') + +test.build('shared_dependency.gyp', test.ALL, chdir='relocate/src') + +with open('relocate/src/Makefile') as makefile: + make_contents = makefile.read() + +# If we remove the code to generate lib1, Make should still be able +# to build lib2 since lib1.so already exists. +make_contents = make_contents.replace('include lib1.target.mk', '') +with open('relocate/src/Makefile', 'w') as makefile: + makefile.write(make_contents) + +test.build('shared_dependency.gyp', test.ALL, chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/library/gyptest-shared.py b/tools/gyp/test/library/gyptest-shared.py new file mode 100755 index 0000000000..a1d2985d91 --- /dev/null +++ b/tools/gyp/test/library/gyptest-shared.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple build of a "Hello, world!" program with shared libraries, +including verifying that libraries are rebuilt correctly when functions +move between libraries. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('library.gyp', + '-Dlibrary=shared_library', + '-Dmoveable_function=lib1', + chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib1_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.run_gyp('library.gyp', + '-Dlibrary=shared_library', + '-Dmoveable_function=lib2', + chdir='relocate/src') + +# Update program.c to force a rebuild. +test.sleep() +contents = test.read('relocate/src/program.c') +contents = contents.replace('Hello', 'Hello again') +test.write('relocate/src/program.c', contents) + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello again from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib2_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.run_gyp('library.gyp', + '-Dlibrary=shared_library', + '-Dmoveable_function=lib1', + chdir='relocate/src') + +# Update program.c to force a rebuild. +test.sleep() +contents = test.read('relocate/src/program.c') +contents = contents.replace('again', 'again again') +test.write('relocate/src/program.c', contents) + +# TODO(sgk): we have to force a rebuild of lib2 so that it weeds out +# the "moved" module. This should be done in gyp by adding a dependency +# on the generated .vcproj file itself. +test.touch('relocate/src/lib2.c') + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello again again from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib1_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/library/gyptest-static.py b/tools/gyp/test/library/gyptest-static.py new file mode 100755 index 0000000000..4bc71c4962 --- /dev/null +++ b/tools/gyp/test/library/gyptest-static.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple build of a "Hello, world!" program with static libraries, +including verifying that libraries are rebuilt correctly when functions +move between libraries. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('library.gyp', + '-Dlibrary=static_library', + '-Dmoveable_function=lib1', + chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib1_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.run_gyp('library.gyp', + '-Dlibrary=static_library', + '-Dmoveable_function=lib2', + chdir='relocate/src') + +# Update program.c to force a rebuild. +test.sleep() +contents = test.read('relocate/src/program.c') +contents = contents.replace('Hello', 'Hello again') +test.write('relocate/src/program.c', contents) + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello again from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib2_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.run_gyp('library.gyp', + '-Dlibrary=static_library', + '-Dmoveable_function=lib1', + chdir='relocate/src') + +# Update program.c and lib2.c to force a rebuild. +test.sleep() +contents = test.read('relocate/src/program.c') +contents = contents.replace('again', 'again again') +test.write('relocate/src/program.c', contents) + +# TODO(sgk): we have to force a rebuild of lib2 so that it weeds out +# the "moved" module. This should be done in gyp by adding a dependency +# on the generated .vcproj file itself. +test.touch('relocate/src/lib2.c') + +test.build('library.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello again again from program.c +Hello from lib1.c +Hello from lib2.c +Hello from lib1_moveable.c +""" +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + + +test.pass_test() diff --git a/tools/gyp/test/library/src/lib1.c b/tools/gyp/test/library/src/lib1.c new file mode 100644 index 0000000000..3866b1b845 --- /dev/null +++ b/tools/gyp/test/library/src/lib1.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void lib1_function(void) +{ + fprintf(stdout, "Hello from lib1.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/library/src/lib1_moveable.c b/tools/gyp/test/library/src/lib1_moveable.c new file mode 100644 index 0000000000..5d3cc1d9aa --- /dev/null +++ b/tools/gyp/test/library/src/lib1_moveable.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void moveable_function(void) +{ + fprintf(stdout, "Hello from lib1_moveable.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/library/src/lib2.c b/tools/gyp/test/library/src/lib2.c new file mode 100644 index 0000000000..21dda72653 --- /dev/null +++ b/tools/gyp/test/library/src/lib2.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void lib2_function(void) +{ + fprintf(stdout, "Hello from lib2.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/library/src/lib2_moveable.c b/tools/gyp/test/library/src/lib2_moveable.c new file mode 100644 index 0000000000..f645071d1e --- /dev/null +++ b/tools/gyp/test/library/src/lib2_moveable.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void moveable_function(void) +{ + fprintf(stdout, "Hello from lib2_moveable.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/library/src/library.gyp b/tools/gyp/test/library/src/library.gyp new file mode 100644 index 0000000000..bc35516426 --- /dev/null +++ b/tools/gyp/test/library/src/library.gyp @@ -0,0 +1,58 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'moveable_function%': 0, + }, + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'dependencies': [ + 'lib1', + 'lib2', + ], + 'sources': [ + 'program.c', + ], + }, + { + 'target_name': 'lib1', + 'type': '<(library)', + 'sources': [ + 'lib1.c', + ], + 'conditions': [ + ['moveable_function=="lib1"', { + 'sources': [ + 'lib1_moveable.c', + ], + }], + ], + }, + { + 'target_name': 'lib2', + 'type': '<(library)', + 'sources': [ + 'lib2.c', + ], + 'conditions': [ + ['moveable_function=="lib2"', { + 'sources': [ + 'lib2_moveable.c', + ], + }], + ], + }, + ], + 'conditions': [ + ['OS=="linux"', { + 'target_defaults': { + # Support 64-bit shared libs (also works fine for 32-bit). + 'cflags': ['-fPIC'], + }, + }], + ], +} diff --git a/tools/gyp/test/library/src/program.c b/tools/gyp/test/library/src/program.c new file mode 100644 index 0000000000..d7712cced4 --- /dev/null +++ b/tools/gyp/test/library/src/program.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +extern void lib1_function(void); +extern void lib2_function(void); +extern void moveable_function(void); + +int main(int argc, char *argv[]) +{ + fprintf(stdout, "Hello from program.c\n"); + fflush(stdout); + lib1_function(); + lib2_function(); + moveable_function(); + return 0; +} diff --git a/tools/gyp/test/library/src/shared_dependency.gyp b/tools/gyp/test/library/src/shared_dependency.gyp new file mode 100644 index 0000000000..7d29f5de59 --- /dev/null +++ b/tools/gyp/test/library/src/shared_dependency.gyp @@ -0,0 +1,33 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'lib1', + 'type': 'shared_library', + 'sources': [ + 'lib1.c', + ], + }, + { + 'target_name': 'lib2', + 'type': 'shared_library', + 'sources': [ + 'lib2.c', + ], + 'dependencies': [ + 'lib1', + ], + }, + ], + 'conditions': [ + ['OS=="linux"', { + 'target_defaults': { + # Support 64-bit shared libs (also works fine for 32-bit). + 'cflags': ['-fPIC'], + }, + }], + ], +} diff --git a/tools/gyp/test/link-objects/base.c b/tools/gyp/test/link-objects/base.c new file mode 100644 index 0000000000..2bc29a1b18 --- /dev/null +++ b/tools/gyp/test/link-objects/base.c @@ -0,0 +1,6 @@ +void extra(); + +int main(int argc, char** argv) { + extra(); + return 0; +} diff --git a/tools/gyp/test/link-objects/extra.c b/tools/gyp/test/link-objects/extra.c new file mode 100644 index 0000000000..1d7ee09b10 --- /dev/null +++ b/tools/gyp/test/link-objects/extra.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +void extra() { + printf("PASS\n"); +} diff --git a/tools/gyp/test/link-objects/gyptest-all.py b/tools/gyp/test/link-objects/gyptest-all.py new file mode 100755 index 0000000000..45bd6e1891 --- /dev/null +++ b/tools/gyp/test/link-objects/gyptest-all.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Put an object file on the sources list. +Expect the result to link ok. +""" + +import TestGyp + +import sys + +if sys.platform != 'darwin': + # Currently only works under the linux make build. + test = TestGyp.TestGyp(formats=['make']) + + test.run_gyp('link-objects.gyp') + + test.build('link-objects.gyp', test.ALL) + + test.run_built_executable('link-objects', stdout="PASS\n") + + test.up_to_date('link-objects.gyp', test.ALL) + + test.pass_test() diff --git a/tools/gyp/test/link-objects/link-objects.gyp b/tools/gyp/test/link-objects/link-objects.gyp new file mode 100644 index 0000000000..ab72855531 --- /dev/null +++ b/tools/gyp/test/link-objects/link-objects.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'link-objects', + 'type': 'executable', + 'actions': [ + { + 'action_name': 'build extra object', + 'inputs': ['extra.c'], + 'outputs': ['extra.o'], + 'action': ['gcc', '-o', 'extra.o', '-c', 'extra.c'], + 'process_outputs_as_sources': 1, + }, + ], + 'sources': [ + 'base.c', + ], + }, + ], +} diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000..452e7fabf9 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +/* Localized versions of Info.plist keys */ + +NSHumanReadableCopyright = "Copyright ©2011 Google Inc." diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib new file mode 100644 index 0000000000..4524596787 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib @@ -0,0 +1,4119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> + <data> + <int key="IBDocument.SystemTarget">1060</int> + <string key="IBDocument.SystemVersion">10A324</string> + <string key="IBDocument.InterfaceBuilderVersion">719</string> + <string key="IBDocument.AppKitVersion">1015</string> + <string key="IBDocument.HIToolboxVersion">418.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="NS.object.0">719</string> + </object> + <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> + <bool key="EncodedWithXMLCoder">YES</bool> + <integer value="371"/> + <integer value="29"/> + </object> + <object class="NSArray" key="IBDocument.PluginDependencies"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys" id="0"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSCustomObject" id="1021"> + <string key="NSClassName">NSApplication</string> + </object> + <object class="NSCustomObject" id="1014"> + <string key="NSClassName">FirstResponder</string> + </object> + <object class="NSCustomObject" id="1050"> + <string key="NSClassName">NSApplication</string> + </object> + <object class="NSMenu" id="649796088"> + <string key="NSTitle">AMainMenu</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="694149608"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">TestApp</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <object class="NSCustomResource" key="NSOnImage" id="35465992"> + <string key="NSClassName">NSImage</string> + <string key="NSResourceName">NSMenuCheckmark</string> + </object> + <object class="NSCustomResource" key="NSMixedImage" id="502551668"> + <string key="NSClassName">NSImage</string> + <string key="NSResourceName">NSMenuMixedState</string> + </object> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="110575045"> + <string key="NSTitle">TestApp</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="238522557"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">About TestApp</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="304266470"> + <reference key="NSMenu" ref="110575045"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="609285721"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Preferences…</string> + <string key="NSKeyEquiv">,</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="481834944"> + <reference key="NSMenu" ref="110575045"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1046388886"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Services</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="752062318"> + <string key="NSTitle">Services</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <string key="NSName">_NSServicesMenu</string> + </object> + </object> + <object class="NSMenuItem" id="646227648"> + <reference key="NSMenu" ref="110575045"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="755159360"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Hide TestApp</string> + <string key="NSKeyEquiv">h</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="342932134"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Hide Others</string> + <string key="NSKeyEquiv">h</string> + <int key="NSKeyEquivModMask">1572864</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="908899353"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Show All</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1056857174"> + <reference key="NSMenu" ref="110575045"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="632727374"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Quit TestApp</string> + <string key="NSKeyEquiv">q</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + <string key="NSName">_NSAppleMenu</string> + </object> + </object> + <object class="NSMenuItem" id="379814623"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">File</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="720053764"> + <string key="NSTitle">File</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="705341025"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">New</string> + <string key="NSKeyEquiv">n</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="722745758"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Open…</string> + <string key="NSKeyEquiv">o</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1025936716"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Open Recent</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="1065607017"> + <string key="NSTitle">Open Recent</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="759406840"> + <reference key="NSMenu" ref="1065607017"/> + <string key="NSTitle">Clear Menu</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + <string key="NSName">_NSRecentDocumentsMenu</string> + </object> + </object> + <object class="NSMenuItem" id="425164168"> + <reference key="NSMenu" ref="720053764"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="776162233"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Close</string> + <string key="NSKeyEquiv">w</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1023925487"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Save</string> + <string key="NSKeyEquiv">s</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="117038363"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Save As…</string> + <string key="NSKeyEquiv">S</string> + <int key="NSKeyEquivModMask">1179648</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="579971712"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Revert to Saved</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1010469920"> + <reference key="NSMenu" ref="720053764"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="294629803"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Page Setup...</string> + <string key="NSKeyEquiv">P</string> + <int key="NSKeyEquivModMask">1179648</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSToolTip"/> + </object> + <object class="NSMenuItem" id="49223823"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Print…</string> + <string key="NSKeyEquiv">p</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="952259628"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">Edit</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="789758025"> + <string key="NSTitle">Edit</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="1058277027"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Undo</string> + <string key="NSKeyEquiv">z</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="790794224"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Redo</string> + <string key="NSKeyEquiv">Z</string> + <int key="NSKeyEquivModMask">1179648</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1040322652"> + <reference key="NSMenu" ref="789758025"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="296257095"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Cut</string> + <string key="NSKeyEquiv">x</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="860595796"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Copy</string> + <string key="NSKeyEquiv">c</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="29853731"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Paste</string> + <string key="NSKeyEquiv">v</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="82994268"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Paste and Match Style</string> + <string key="NSKeyEquiv">V</string> + <int key="NSKeyEquivModMask">1572864</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="437104165"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Delete</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="583158037"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Select All</string> + <string key="NSKeyEquiv">a</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="212016141"> + <reference key="NSMenu" ref="789758025"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="892235320"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Find</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="963351320"> + <string key="NSTitle">Find</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="447796847"> + <reference key="NSMenu" ref="963351320"/> + <string key="NSTitle">Find…</string> + <string key="NSKeyEquiv">f</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">1</int> + </object> + <object class="NSMenuItem" id="326711663"> + <reference key="NSMenu" ref="963351320"/> + <string key="NSTitle">Find Next</string> + <string key="NSKeyEquiv">g</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">2</int> + </object> + <object class="NSMenuItem" id="270902937"> + <reference key="NSMenu" ref="963351320"/> + <string key="NSTitle">Find Previous</string> + <string key="NSKeyEquiv">G</string> + <int key="NSKeyEquivModMask">1179648</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">3</int> + </object> + <object class="NSMenuItem" id="159080638"> + <reference key="NSMenu" ref="963351320"/> + <string key="NSTitle">Use Selection for Find</string> + <string key="NSKeyEquiv">e</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">7</int> + </object> + <object class="NSMenuItem" id="88285865"> + <reference key="NSMenu" ref="963351320"/> + <string key="NSTitle">Jump to Selection</string> + <string key="NSKeyEquiv">j</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="972420730"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Spelling and Grammar</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="769623530"> + <string key="NSTitle">Spelling and Grammar</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="679648819"> + <reference key="NSMenu" ref="769623530"/> + <string key="NSTitle">Show Spelling and Grammar</string> + <string key="NSKeyEquiv">:</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="96193923"> + <reference key="NSMenu" ref="769623530"/> + <string key="NSTitle">Check Document Now</string> + <string key="NSKeyEquiv">;</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="859480356"> + <reference key="NSMenu" ref="769623530"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="948374510"> + <reference key="NSMenu" ref="769623530"/> + <string key="NSTitle">Check Spelling While Typing</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="967646866"> + <reference key="NSMenu" ref="769623530"/> + <string key="NSTitle">Check Grammar With Spelling</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="795346622"> + <reference key="NSMenu" ref="769623530"/> + <string key="NSTitle">Correct Spelling Automatically</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="507821607"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Substitutions</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="698887838"> + <string key="NSTitle">Substitutions</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="65139061"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Show Substitutions</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="19036812"> + <reference key="NSMenu" ref="698887838"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="605118523"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Smart Copy/Paste</string> + <string key="NSKeyEquiv">f</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">1</int> + </object> + <object class="NSMenuItem" id="197661976"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Smart Quotes</string> + <string key="NSKeyEquiv">g</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">2</int> + </object> + <object class="NSMenuItem" id="672708820"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Smart Dashes</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="708854459"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Smart Links</string> + <string key="NSKeyEquiv">G</string> + <int key="NSKeyEquivModMask">1179648</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">3</int> + </object> + <object class="NSMenuItem" id="537092702"> + <reference key="NSMenu" ref="698887838"/> + <string key="NSTitle">Text Replacement</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="288088188"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Transformations</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="579392910"> + <string key="NSTitle">Transformations</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="1060694897"> + <reference key="NSMenu" ref="579392910"/> + <string key="NSTitle">Make Upper Case</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="879586729"> + <reference key="NSMenu" ref="579392910"/> + <string key="NSTitle">Make Lower Case</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="56570060"> + <reference key="NSMenu" ref="579392910"/> + <string key="NSTitle">Capitalize</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="676164635"> + <reference key="NSMenu" ref="789758025"/> + <string key="NSTitle">Speech</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="785027613"> + <string key="NSTitle">Speech</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="731782645"> + <reference key="NSMenu" ref="785027613"/> + <string key="NSTitle">Start Speaking</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="680220178"> + <reference key="NSMenu" ref="785027613"/> + <string key="NSTitle">Stop Speaking</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="302598603"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">Format</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="941447902"> + <string key="NSTitle">Format</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="792887677"> + <reference key="NSMenu" ref="941447902"/> + <string key="NSTitle">Font</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="786677654"> + <string key="NSTitle">Font</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="159677712"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Show Fonts</string> + <string key="NSKeyEquiv">t</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="305399458"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Bold</string> + <string key="NSKeyEquiv">b</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">2</int> + </object> + <object class="NSMenuItem" id="814362025"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Italic</string> + <string key="NSKeyEquiv">i</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">1</int> + </object> + <object class="NSMenuItem" id="330926929"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Underline</string> + <string key="NSKeyEquiv">u</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="533507878"> + <reference key="NSMenu" ref="786677654"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="158063935"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Bigger</string> + <string key="NSKeyEquiv">+</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">3</int> + </object> + <object class="NSMenuItem" id="885547335"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Smaller</string> + <string key="NSKeyEquiv">-</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <int key="NSTag">4</int> + </object> + <object class="NSMenuItem" id="901062459"> + <reference key="NSMenu" ref="786677654"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="767671776"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Kern</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="175441468"> + <string key="NSTitle">Kern</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="252969304"> + <reference key="NSMenu" ref="175441468"/> + <string key="NSTitle">Use Default</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="766922938"> + <reference key="NSMenu" ref="175441468"/> + <string key="NSTitle">Use None</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="677519740"> + <reference key="NSMenu" ref="175441468"/> + <string key="NSTitle">Tighten</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="238351151"> + <reference key="NSMenu" ref="175441468"/> + <string key="NSTitle">Loosen</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="691570813"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Ligature</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="1058217995"> + <string key="NSTitle">Ligature</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="706297211"> + <reference key="NSMenu" ref="1058217995"/> + <string key="NSTitle">Use Default</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="568384683"> + <reference key="NSMenu" ref="1058217995"/> + <string key="NSTitle">Use None</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="663508465"> + <reference key="NSMenu" ref="1058217995"/> + <string key="NSTitle">Use All</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="769124883"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Baseline</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="18263474"> + <string key="NSTitle">Baseline</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="257962622"> + <reference key="NSMenu" ref="18263474"/> + <string key="NSTitle">Use Default</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="644725453"> + <reference key="NSMenu" ref="18263474"/> + <string key="NSTitle">Superscript</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1037576581"> + <reference key="NSMenu" ref="18263474"/> + <string key="NSTitle">Subscript</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="941806246"> + <reference key="NSMenu" ref="18263474"/> + <string key="NSTitle">Raise</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1045724900"> + <reference key="NSMenu" ref="18263474"/> + <string key="NSTitle">Lower</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="739652853"> + <reference key="NSMenu" ref="786677654"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="1012600125"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Show Colors</string> + <string key="NSKeyEquiv">C</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="214559597"> + <reference key="NSMenu" ref="786677654"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="596732606"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Copy Style</string> + <string key="NSKeyEquiv">c</string> + <int key="NSKeyEquivModMask">1572864</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="393423671"> + <reference key="NSMenu" ref="786677654"/> + <string key="NSTitle">Paste Style</string> + <string key="NSKeyEquiv">v</string> + <int key="NSKeyEquivModMask">1572864</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + <string key="NSName">_NSFontMenu</string> + </object> + </object> + <object class="NSMenuItem" id="215659978"> + <reference key="NSMenu" ref="941447902"/> + <string key="NSTitle">Text</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="446991534"> + <string key="NSTitle">Text</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="875092757"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Align Left</string> + <string key="NSKeyEquiv">{</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="630155264"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Center</string> + <string key="NSKeyEquiv">|</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="945678886"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Justify</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="512868991"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Align Right</string> + <string key="NSKeyEquiv">}</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="163117631"> + <reference key="NSMenu" ref="446991534"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="31516759"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Writing Direction</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="956096989"> + <string key="NSTitle">Writing Direction</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="257099033"> + <reference key="NSMenu" ref="956096989"/> + <bool key="NSIsDisabled">YES</bool> + <string key="NSTitle">Paragraph</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="551969625"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="249532473"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="607364498"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="508151438"> + <reference key="NSMenu" ref="956096989"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="981751889"> + <reference key="NSMenu" ref="956096989"/> + <bool key="NSIsDisabled">YES</bool> + <string key="NSTitle">Selection</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="380031999"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="825984362"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="560145579"> + <reference key="NSMenu" ref="956096989"/> + <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="908105787"> + <reference key="NSMenu" ref="446991534"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="644046920"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Show Ruler</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="231811626"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Copy Ruler</string> + <string key="NSKeyEquiv">c</string> + <int key="NSKeyEquivModMask">1310720</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="883618387"> + <reference key="NSMenu" ref="446991534"/> + <string key="NSTitle">Paste Ruler</string> + <string key="NSKeyEquiv">v</string> + <int key="NSKeyEquivModMask">1310720</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="586577488"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">View</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="466310130"> + <string key="NSTitle">View</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="102151532"> + <reference key="NSMenu" ref="466310130"/> + <string key="NSTitle">Show Toolbar</string> + <string key="NSKeyEquiv">t</string> + <int key="NSKeyEquivModMask">1572864</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="237841660"> + <reference key="NSMenu" ref="466310130"/> + <string key="NSTitle">Customize Toolbar…</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + </object> + </object> + <object class="NSMenuItem" id="713487014"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">Window</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="835318025"> + <string key="NSTitle">Window</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="1011231497"> + <reference key="NSMenu" ref="835318025"/> + <string key="NSTitle">Minimize</string> + <string key="NSKeyEquiv">m</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="575023229"> + <reference key="NSMenu" ref="835318025"/> + <string key="NSTitle">Zoom</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="299356726"> + <reference key="NSMenu" ref="835318025"/> + <bool key="NSIsDisabled">YES</bool> + <bool key="NSIsSeparator">YES</bool> + <string key="NSTitle"/> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + <object class="NSMenuItem" id="625202149"> + <reference key="NSMenu" ref="835318025"/> + <string key="NSTitle">Bring All to Front</string> + <string key="NSKeyEquiv"/> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + <string key="NSName">_NSWindowsMenu</string> + </object> + </object> + <object class="NSMenuItem" id="448692316"> + <reference key="NSMenu" ref="649796088"/> + <string key="NSTitle">Help</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + <string key="NSAction">submenuAction:</string> + <object class="NSMenu" key="NSSubmenu" id="992780483"> + <string key="NSTitle">Help</string> + <object class="NSMutableArray" key="NSMenuItems"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSMenuItem" id="105068016"> + <reference key="NSMenu" ref="992780483"/> + <string key="NSTitle">TestApp Help</string> + <string key="NSKeyEquiv">?</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> + </object> + <string key="NSName">_NSHelpMenu</string> + </object> + </object> + </object> + <string key="NSName">_NSMainMenu</string> + </object> + <object class="NSWindowTemplate" id="972006081"> + <int key="NSWindowStyleMask">15</int> + <int key="NSWindowBacking">2</int> + <string key="NSWindowRect">{{335, 390}, {480, 360}}</string> + <int key="NSWTFlags">1954021376</int> + <string key="NSWindowTitle">TestApp</string> + <string key="NSWindowClass">NSWindow</string> + <nil key="NSViewClass"/> + <string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string> + <object class="NSView" key="NSWindowView" id="439893737"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">256</int> + <string key="NSFrameSize">{480, 360}</string> + <reference key="NSSuperview"/> + </object> + <string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string> + <string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string> + </object> + <object class="NSCustomObject" id="976324537"> + <string key="NSClassName">TestAppAppDelegate</string> + </object> + <object class="NSCustomObject" id="755631768"> + <string key="NSClassName">NSFontManager</string> + </object> + </object> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <object class="NSMutableArray" key="connectionRecords"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performMiniaturize:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1011231497"/> + </object> + <int key="connectionID">37</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">arrangeInFront:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="625202149"/> + </object> + <int key="connectionID">39</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">print:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="49223823"/> + </object> + <int key="connectionID">86</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">runPageLayout:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="294629803"/> + </object> + <int key="connectionID">87</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">clearRecentDocuments:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="759406840"/> + </object> + <int key="connectionID">127</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">orderFrontStandardAboutPanel:</string> + <reference key="source" ref="1021"/> + <reference key="destination" ref="238522557"/> + </object> + <int key="connectionID">142</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performClose:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="776162233"/> + </object> + <int key="connectionID">193</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleContinuousSpellChecking:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="948374510"/> + </object> + <int key="connectionID">222</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">undo:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1058277027"/> + </object> + <int key="connectionID">223</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">copy:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="860595796"/> + </object> + <int key="connectionID">224</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">checkSpelling:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="96193923"/> + </object> + <int key="connectionID">225</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">paste:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="29853731"/> + </object> + <int key="connectionID">226</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">stopSpeaking:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="680220178"/> + </object> + <int key="connectionID">227</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">cut:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="296257095"/> + </object> + <int key="connectionID">228</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">showGuessPanel:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="679648819"/> + </object> + <int key="connectionID">230</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">redo:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="790794224"/> + </object> + <int key="connectionID">231</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">selectAll:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="583158037"/> + </object> + <int key="connectionID">232</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">startSpeaking:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="731782645"/> + </object> + <int key="connectionID">233</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">delete:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="437104165"/> + </object> + <int key="connectionID">235</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performZoom:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="575023229"/> + </object> + <int key="connectionID">240</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performFindPanelAction:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="447796847"/> + </object> + <int key="connectionID">241</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">centerSelectionInVisibleArea:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="88285865"/> + </object> + <int key="connectionID">245</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleGrammarChecking:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="967646866"/> + </object> + <int key="connectionID">347</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleSmartInsertDelete:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="605118523"/> + </object> + <int key="connectionID">355</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleAutomaticQuoteSubstitution:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="197661976"/> + </object> + <int key="connectionID">356</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleAutomaticLinkDetection:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="708854459"/> + </object> + <int key="connectionID">357</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">saveDocument:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1023925487"/> + </object> + <int key="connectionID">362</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">saveDocumentAs:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="117038363"/> + </object> + <int key="connectionID">363</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">revertDocumentToSaved:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="579971712"/> + </object> + <int key="connectionID">364</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">runToolbarCustomizationPalette:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="237841660"/> + </object> + <int key="connectionID">365</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleToolbarShown:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="102151532"/> + </object> + <int key="connectionID">366</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">hide:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="755159360"/> + </object> + <int key="connectionID">367</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">hideOtherApplications:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="342932134"/> + </object> + <int key="connectionID">368</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">unhideAllApplications:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="908899353"/> + </object> + <int key="connectionID">370</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">newDocument:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="705341025"/> + </object> + <int key="connectionID">373</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">openDocument:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="722745758"/> + </object> + <int key="connectionID">374</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">addFontTrait:</string> + <reference key="source" ref="755631768"/> + <reference key="destination" ref="305399458"/> + </object> + <int key="connectionID">421</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">addFontTrait:</string> + <reference key="source" ref="755631768"/> + <reference key="destination" ref="814362025"/> + </object> + <int key="connectionID">422</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">modifyFont:</string> + <reference key="source" ref="755631768"/> + <reference key="destination" ref="885547335"/> + </object> + <int key="connectionID">423</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">orderFrontFontPanel:</string> + <reference key="source" ref="755631768"/> + <reference key="destination" ref="159677712"/> + </object> + <int key="connectionID">424</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">modifyFont:</string> + <reference key="source" ref="755631768"/> + <reference key="destination" ref="158063935"/> + </object> + <int key="connectionID">425</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">raiseBaseline:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="941806246"/> + </object> + <int key="connectionID">426</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">lowerBaseline:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1045724900"/> + </object> + <int key="connectionID">427</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">copyFont:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="596732606"/> + </object> + <int key="connectionID">428</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">subscript:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1037576581"/> + </object> + <int key="connectionID">429</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">superscript:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="644725453"/> + </object> + <int key="connectionID">430</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">tightenKerning:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="677519740"/> + </object> + <int key="connectionID">431</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">underline:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="330926929"/> + </object> + <int key="connectionID">432</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">orderFrontColorPanel:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1012600125"/> + </object> + <int key="connectionID">433</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">useAllLigatures:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="663508465"/> + </object> + <int key="connectionID">434</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">loosenKerning:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="238351151"/> + </object> + <int key="connectionID">435</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">pasteFont:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="393423671"/> + </object> + <int key="connectionID">436</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">unscript:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="257962622"/> + </object> + <int key="connectionID">437</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">useStandardKerning:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="252969304"/> + </object> + <int key="connectionID">438</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">useStandardLigatures:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="706297211"/> + </object> + <int key="connectionID">439</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">turnOffLigatures:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="568384683"/> + </object> + <int key="connectionID">440</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">turnOffKerning:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="766922938"/> + </object> + <int key="connectionID">441</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">terminate:</string> + <reference key="source" ref="1050"/> + <reference key="destination" ref="632727374"/> + </object> + <int key="connectionID">449</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleAutomaticSpellingCorrection:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="795346622"/> + </object> + <int key="connectionID">456</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">orderFrontSubstitutionsPanel:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="65139061"/> + </object> + <int key="connectionID">458</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleAutomaticDashSubstitution:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="672708820"/> + </object> + <int key="connectionID">461</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleAutomaticTextReplacement:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="537092702"/> + </object> + <int key="connectionID">463</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">uppercaseWord:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="1060694897"/> + </object> + <int key="connectionID">464</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">capitalizeWord:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="56570060"/> + </object> + <int key="connectionID">467</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">lowercaseWord:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="879586729"/> + </object> + <int key="connectionID">468</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">pasteAsPlainText:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="82994268"/> + </object> + <int key="connectionID">486</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performFindPanelAction:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="326711663"/> + </object> + <int key="connectionID">487</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performFindPanelAction:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="270902937"/> + </object> + <int key="connectionID">488</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">performFindPanelAction:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="159080638"/> + </object> + <int key="connectionID">489</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">showHelp:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="105068016"/> + </object> + <int key="connectionID">493</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">delegate</string> + <reference key="source" ref="1021"/> + <reference key="destination" ref="976324537"/> + </object> + <int key="connectionID">495</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">alignCenter:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="630155264"/> + </object> + <int key="connectionID">518</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">pasteRuler:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="883618387"/> + </object> + <int key="connectionID">519</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleRuler:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="644046920"/> + </object> + <int key="connectionID">520</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">alignRight:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="512868991"/> + </object> + <int key="connectionID">521</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">copyRuler:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="231811626"/> + </object> + <int key="connectionID">522</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">alignJustified:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="945678886"/> + </object> + <int key="connectionID">523</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">alignLeft:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="875092757"/> + </object> + <int key="connectionID">524</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeBaseWritingDirectionNatural:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="551969625"/> + </object> + <int key="connectionID">525</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeBaseWritingDirectionLeftToRight:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="249532473"/> + </object> + <int key="connectionID">526</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeBaseWritingDirectionRightToLeft:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="607364498"/> + </object> + <int key="connectionID">527</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeTextWritingDirectionNatural:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="380031999"/> + </object> + <int key="connectionID">528</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeTextWritingDirectionLeftToRight:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="825984362"/> + </object> + <int key="connectionID">529</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">makeTextWritingDirectionRightToLeft:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="560145579"/> + </object> + <int key="connectionID">530</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">window</string> + <reference key="source" ref="976324537"/> + <reference key="destination" ref="972006081"/> + </object> + <int key="connectionID">532</int> + </object> + </object> + <object class="IBMutableOrderedSet" key="objectRecords"> + <object class="NSArray" key="orderedObjects"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <reference key="object" ref="0"/> + <reference key="children" ref="1048"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="1021"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="1014"/> + <reference key="parent" ref="0"/> + <string key="objectName">First Responder</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-3</int> + <reference key="object" ref="1050"/> + <reference key="parent" ref="0"/> + <string key="objectName">Application</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">29</int> + <reference key="object" ref="649796088"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="713487014"/> + <reference ref="694149608"/> + <reference ref="952259628"/> + <reference ref="379814623"/> + <reference ref="586577488"/> + <reference ref="302598603"/> + <reference ref="448692316"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">19</int> + <reference key="object" ref="713487014"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="835318025"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">56</int> + <reference key="object" ref="694149608"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="110575045"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">217</int> + <reference key="object" ref="952259628"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="789758025"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">83</int> + <reference key="object" ref="379814623"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="720053764"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">81</int> + <reference key="object" ref="720053764"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1023925487"/> + <reference ref="117038363"/> + <reference ref="49223823"/> + <reference ref="722745758"/> + <reference ref="705341025"/> + <reference ref="1025936716"/> + <reference ref="294629803"/> + <reference ref="776162233"/> + <reference ref="425164168"/> + <reference ref="579971712"/> + <reference ref="1010469920"/> + </object> + <reference key="parent" ref="379814623"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">75</int> + <reference key="object" ref="1023925487"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">80</int> + <reference key="object" ref="117038363"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">78</int> + <reference key="object" ref="49223823"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">72</int> + <reference key="object" ref="722745758"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">82</int> + <reference key="object" ref="705341025"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">124</int> + <reference key="object" ref="1025936716"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1065607017"/> + </object> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">77</int> + <reference key="object" ref="294629803"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">73</int> + <reference key="object" ref="776162233"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">79</int> + <reference key="object" ref="425164168"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">112</int> + <reference key="object" ref="579971712"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">74</int> + <reference key="object" ref="1010469920"/> + <reference key="parent" ref="720053764"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">125</int> + <reference key="object" ref="1065607017"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="759406840"/> + </object> + <reference key="parent" ref="1025936716"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">126</int> + <reference key="object" ref="759406840"/> + <reference key="parent" ref="1065607017"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">205</int> + <reference key="object" ref="789758025"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="437104165"/> + <reference ref="583158037"/> + <reference ref="1058277027"/> + <reference ref="212016141"/> + <reference ref="296257095"/> + <reference ref="29853731"/> + <reference ref="860595796"/> + <reference ref="1040322652"/> + <reference ref="790794224"/> + <reference ref="892235320"/> + <reference ref="972420730"/> + <reference ref="676164635"/> + <reference ref="507821607"/> + <reference ref="288088188"/> + <reference ref="82994268"/> + </object> + <reference key="parent" ref="952259628"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">202</int> + <reference key="object" ref="437104165"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">198</int> + <reference key="object" ref="583158037"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">207</int> + <reference key="object" ref="1058277027"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">214</int> + <reference key="object" ref="212016141"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">199</int> + <reference key="object" ref="296257095"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">203</int> + <reference key="object" ref="29853731"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">197</int> + <reference key="object" ref="860595796"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">206</int> + <reference key="object" ref="1040322652"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">215</int> + <reference key="object" ref="790794224"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">218</int> + <reference key="object" ref="892235320"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="963351320"/> + </object> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">216</int> + <reference key="object" ref="972420730"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="769623530"/> + </object> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">200</int> + <reference key="object" ref="769623530"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="948374510"/> + <reference ref="96193923"/> + <reference ref="679648819"/> + <reference ref="967646866"/> + <reference ref="859480356"/> + <reference ref="795346622"/> + </object> + <reference key="parent" ref="972420730"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">219</int> + <reference key="object" ref="948374510"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">201</int> + <reference key="object" ref="96193923"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">204</int> + <reference key="object" ref="679648819"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">220</int> + <reference key="object" ref="963351320"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="270902937"/> + <reference ref="88285865"/> + <reference ref="159080638"/> + <reference ref="326711663"/> + <reference ref="447796847"/> + </object> + <reference key="parent" ref="892235320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">213</int> + <reference key="object" ref="270902937"/> + <reference key="parent" ref="963351320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">210</int> + <reference key="object" ref="88285865"/> + <reference key="parent" ref="963351320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">221</int> + <reference key="object" ref="159080638"/> + <reference key="parent" ref="963351320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">208</int> + <reference key="object" ref="326711663"/> + <reference key="parent" ref="963351320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">209</int> + <reference key="object" ref="447796847"/> + <reference key="parent" ref="963351320"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">57</int> + <reference key="object" ref="110575045"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="238522557"/> + <reference ref="755159360"/> + <reference ref="908899353"/> + <reference ref="632727374"/> + <reference ref="646227648"/> + <reference ref="609285721"/> + <reference ref="481834944"/> + <reference ref="304266470"/> + <reference ref="1046388886"/> + <reference ref="1056857174"/> + <reference ref="342932134"/> + </object> + <reference key="parent" ref="694149608"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">58</int> + <reference key="object" ref="238522557"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">134</int> + <reference key="object" ref="755159360"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">150</int> + <reference key="object" ref="908899353"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">136</int> + <reference key="object" ref="632727374"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">144</int> + <reference key="object" ref="646227648"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">129</int> + <reference key="object" ref="609285721"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">143</int> + <reference key="object" ref="481834944"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">236</int> + <reference key="object" ref="304266470"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">131</int> + <reference key="object" ref="1046388886"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="752062318"/> + </object> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">149</int> + <reference key="object" ref="1056857174"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">145</int> + <reference key="object" ref="342932134"/> + <reference key="parent" ref="110575045"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">130</int> + <reference key="object" ref="752062318"/> + <reference key="parent" ref="1046388886"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">24</int> + <reference key="object" ref="835318025"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="299356726"/> + <reference ref="625202149"/> + <reference ref="575023229"/> + <reference ref="1011231497"/> + </object> + <reference key="parent" ref="713487014"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">92</int> + <reference key="object" ref="299356726"/> + <reference key="parent" ref="835318025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5</int> + <reference key="object" ref="625202149"/> + <reference key="parent" ref="835318025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">239</int> + <reference key="object" ref="575023229"/> + <reference key="parent" ref="835318025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">23</int> + <reference key="object" ref="1011231497"/> + <reference key="parent" ref="835318025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">295</int> + <reference key="object" ref="586577488"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="466310130"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">296</int> + <reference key="object" ref="466310130"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="102151532"/> + <reference ref="237841660"/> + </object> + <reference key="parent" ref="586577488"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">297</int> + <reference key="object" ref="102151532"/> + <reference key="parent" ref="466310130"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">298</int> + <reference key="object" ref="237841660"/> + <reference key="parent" ref="466310130"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">211</int> + <reference key="object" ref="676164635"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="785027613"/> + </object> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">212</int> + <reference key="object" ref="785027613"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="680220178"/> + <reference ref="731782645"/> + </object> + <reference key="parent" ref="676164635"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">195</int> + <reference key="object" ref="680220178"/> + <reference key="parent" ref="785027613"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">196</int> + <reference key="object" ref="731782645"/> + <reference key="parent" ref="785027613"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">346</int> + <reference key="object" ref="967646866"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">348</int> + <reference key="object" ref="507821607"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="698887838"/> + </object> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">349</int> + <reference key="object" ref="698887838"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="605118523"/> + <reference ref="197661976"/> + <reference ref="708854459"/> + <reference ref="65139061"/> + <reference ref="19036812"/> + <reference ref="672708820"/> + <reference ref="537092702"/> + </object> + <reference key="parent" ref="507821607"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">350</int> + <reference key="object" ref="605118523"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">351</int> + <reference key="object" ref="197661976"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">354</int> + <reference key="object" ref="708854459"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">371</int> + <reference key="object" ref="972006081"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="439893737"/> + </object> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">372</int> + <reference key="object" ref="439893737"/> + <reference key="parent" ref="972006081"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">375</int> + <reference key="object" ref="302598603"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="941447902"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">376</int> + <reference key="object" ref="941447902"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="792887677"/> + <reference ref="215659978"/> + </object> + <reference key="parent" ref="302598603"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">377</int> + <reference key="object" ref="792887677"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="786677654"/> + </object> + <reference key="parent" ref="941447902"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">388</int> + <reference key="object" ref="786677654"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="159677712"/> + <reference ref="305399458"/> + <reference ref="814362025"/> + <reference ref="330926929"/> + <reference ref="533507878"/> + <reference ref="158063935"/> + <reference ref="885547335"/> + <reference ref="901062459"/> + <reference ref="767671776"/> + <reference ref="691570813"/> + <reference ref="769124883"/> + <reference ref="739652853"/> + <reference ref="1012600125"/> + <reference ref="214559597"/> + <reference ref="596732606"/> + <reference ref="393423671"/> + </object> + <reference key="parent" ref="792887677"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">389</int> + <reference key="object" ref="159677712"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">390</int> + <reference key="object" ref="305399458"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">391</int> + <reference key="object" ref="814362025"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">392</int> + <reference key="object" ref="330926929"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">393</int> + <reference key="object" ref="533507878"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">394</int> + <reference key="object" ref="158063935"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">395</int> + <reference key="object" ref="885547335"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">396</int> + <reference key="object" ref="901062459"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">397</int> + <reference key="object" ref="767671776"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="175441468"/> + </object> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">398</int> + <reference key="object" ref="691570813"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1058217995"/> + </object> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">399</int> + <reference key="object" ref="769124883"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="18263474"/> + </object> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">400</int> + <reference key="object" ref="739652853"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">401</int> + <reference key="object" ref="1012600125"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">402</int> + <reference key="object" ref="214559597"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">403</int> + <reference key="object" ref="596732606"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">404</int> + <reference key="object" ref="393423671"/> + <reference key="parent" ref="786677654"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">405</int> + <reference key="object" ref="18263474"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="257962622"/> + <reference ref="644725453"/> + <reference ref="1037576581"/> + <reference ref="941806246"/> + <reference ref="1045724900"/> + </object> + <reference key="parent" ref="769124883"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">406</int> + <reference key="object" ref="257962622"/> + <reference key="parent" ref="18263474"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">407</int> + <reference key="object" ref="644725453"/> + <reference key="parent" ref="18263474"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">408</int> + <reference key="object" ref="1037576581"/> + <reference key="parent" ref="18263474"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">409</int> + <reference key="object" ref="941806246"/> + <reference key="parent" ref="18263474"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">410</int> + <reference key="object" ref="1045724900"/> + <reference key="parent" ref="18263474"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">411</int> + <reference key="object" ref="1058217995"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="706297211"/> + <reference ref="568384683"/> + <reference ref="663508465"/> + </object> + <reference key="parent" ref="691570813"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">412</int> + <reference key="object" ref="706297211"/> + <reference key="parent" ref="1058217995"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">413</int> + <reference key="object" ref="568384683"/> + <reference key="parent" ref="1058217995"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">414</int> + <reference key="object" ref="663508465"/> + <reference key="parent" ref="1058217995"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">415</int> + <reference key="object" ref="175441468"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="252969304"/> + <reference ref="766922938"/> + <reference ref="677519740"/> + <reference ref="238351151"/> + </object> + <reference key="parent" ref="767671776"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">416</int> + <reference key="object" ref="252969304"/> + <reference key="parent" ref="175441468"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">417</int> + <reference key="object" ref="766922938"/> + <reference key="parent" ref="175441468"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">418</int> + <reference key="object" ref="677519740"/> + <reference key="parent" ref="175441468"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">419</int> + <reference key="object" ref="238351151"/> + <reference key="parent" ref="175441468"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">420</int> + <reference key="object" ref="755631768"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">450</int> + <reference key="object" ref="288088188"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="579392910"/> + </object> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">451</int> + <reference key="object" ref="579392910"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="1060694897"/> + <reference ref="879586729"/> + <reference ref="56570060"/> + </object> + <reference key="parent" ref="288088188"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">452</int> + <reference key="object" ref="1060694897"/> + <reference key="parent" ref="579392910"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">453</int> + <reference key="object" ref="859480356"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">454</int> + <reference key="object" ref="795346622"/> + <reference key="parent" ref="769623530"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">457</int> + <reference key="object" ref="65139061"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">459</int> + <reference key="object" ref="19036812"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">460</int> + <reference key="object" ref="672708820"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">462</int> + <reference key="object" ref="537092702"/> + <reference key="parent" ref="698887838"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">465</int> + <reference key="object" ref="879586729"/> + <reference key="parent" ref="579392910"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">466</int> + <reference key="object" ref="56570060"/> + <reference key="parent" ref="579392910"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">485</int> + <reference key="object" ref="82994268"/> + <reference key="parent" ref="789758025"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">490</int> + <reference key="object" ref="448692316"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="992780483"/> + </object> + <reference key="parent" ref="649796088"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">491</int> + <reference key="object" ref="992780483"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="105068016"/> + </object> + <reference key="parent" ref="448692316"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">492</int> + <reference key="object" ref="105068016"/> + <reference key="parent" ref="992780483"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">494</int> + <reference key="object" ref="976324537"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">496</int> + <reference key="object" ref="215659978"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="446991534"/> + </object> + <reference key="parent" ref="941447902"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">497</int> + <reference key="object" ref="446991534"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="875092757"/> + <reference ref="630155264"/> + <reference ref="945678886"/> + <reference ref="512868991"/> + <reference ref="163117631"/> + <reference ref="31516759"/> + <reference ref="908105787"/> + <reference ref="644046920"/> + <reference ref="231811626"/> + <reference ref="883618387"/> + </object> + <reference key="parent" ref="215659978"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">498</int> + <reference key="object" ref="875092757"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">499</int> + <reference key="object" ref="630155264"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">500</int> + <reference key="object" ref="945678886"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">501</int> + <reference key="object" ref="512868991"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">502</int> + <reference key="object" ref="163117631"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">503</int> + <reference key="object" ref="31516759"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="956096989"/> + </object> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">504</int> + <reference key="object" ref="908105787"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">505</int> + <reference key="object" ref="644046920"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">506</int> + <reference key="object" ref="231811626"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">507</int> + <reference key="object" ref="883618387"/> + <reference key="parent" ref="446991534"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">508</int> + <reference key="object" ref="956096989"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="257099033"/> + <reference ref="551969625"/> + <reference ref="249532473"/> + <reference ref="607364498"/> + <reference ref="508151438"/> + <reference ref="981751889"/> + <reference ref="380031999"/> + <reference ref="825984362"/> + <reference ref="560145579"/> + </object> + <reference key="parent" ref="31516759"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">509</int> + <reference key="object" ref="257099033"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">510</int> + <reference key="object" ref="551969625"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">511</int> + <reference key="object" ref="249532473"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">512</int> + <reference key="object" ref="607364498"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">513</int> + <reference key="object" ref="508151438"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">514</int> + <reference key="object" ref="981751889"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">515</int> + <reference key="object" ref="380031999"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">516</int> + <reference key="object" ref="825984362"/> + <reference key="parent" ref="956096989"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">517</int> + <reference key="object" ref="560145579"/> + <reference key="parent" ref="956096989"/> + </object> + </object> + </object> + <object class="NSMutableDictionary" key="flattenedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>-3.IBPluginDependency</string> + <string>112.IBPluginDependency</string> + <string>112.ImportedFromIB2</string> + <string>124.IBPluginDependency</string> + <string>124.ImportedFromIB2</string> + <string>125.IBPluginDependency</string> + <string>125.ImportedFromIB2</string> + <string>125.editorWindowContentRectSynchronizationRect</string> + <string>126.IBPluginDependency</string> + <string>126.ImportedFromIB2</string> + <string>129.IBPluginDependency</string> + <string>129.ImportedFromIB2</string> + <string>130.IBPluginDependency</string> + <string>130.ImportedFromIB2</string> + <string>130.editorWindowContentRectSynchronizationRect</string> + <string>131.IBPluginDependency</string> + <string>131.ImportedFromIB2</string> + <string>134.IBPluginDependency</string> + <string>134.ImportedFromIB2</string> + <string>136.IBPluginDependency</string> + <string>136.ImportedFromIB2</string> + <string>143.IBPluginDependency</string> + <string>143.ImportedFromIB2</string> + <string>144.IBPluginDependency</string> + <string>144.ImportedFromIB2</string> + <string>145.IBPluginDependency</string> + <string>145.ImportedFromIB2</string> + <string>149.IBPluginDependency</string> + <string>149.ImportedFromIB2</string> + <string>150.IBPluginDependency</string> + <string>150.ImportedFromIB2</string> + <string>19.IBPluginDependency</string> + <string>19.ImportedFromIB2</string> + <string>195.IBPluginDependency</string> + <string>195.ImportedFromIB2</string> + <string>196.IBPluginDependency</string> + <string>196.ImportedFromIB2</string> + <string>197.IBPluginDependency</string> + <string>197.ImportedFromIB2</string> + <string>198.IBPluginDependency</string> + <string>198.ImportedFromIB2</string> + <string>199.IBPluginDependency</string> + <string>199.ImportedFromIB2</string> + <string>200.IBEditorWindowLastContentRect</string> + <string>200.IBPluginDependency</string> + <string>200.ImportedFromIB2</string> + <string>200.editorWindowContentRectSynchronizationRect</string> + <string>201.IBPluginDependency</string> + <string>201.ImportedFromIB2</string> + <string>202.IBPluginDependency</string> + <string>202.ImportedFromIB2</string> + <string>203.IBPluginDependency</string> + <string>203.ImportedFromIB2</string> + <string>204.IBPluginDependency</string> + <string>204.ImportedFromIB2</string> + <string>205.IBEditorWindowLastContentRect</string> + <string>205.IBPluginDependency</string> + <string>205.ImportedFromIB2</string> + <string>205.editorWindowContentRectSynchronizationRect</string> + <string>206.IBPluginDependency</string> + <string>206.ImportedFromIB2</string> + <string>207.IBPluginDependency</string> + <string>207.ImportedFromIB2</string> + <string>208.IBPluginDependency</string> + <string>208.ImportedFromIB2</string> + <string>209.IBPluginDependency</string> + <string>209.ImportedFromIB2</string> + <string>210.IBPluginDependency</string> + <string>210.ImportedFromIB2</string> + <string>211.IBPluginDependency</string> + <string>211.ImportedFromIB2</string> + <string>212.IBPluginDependency</string> + <string>212.ImportedFromIB2</string> + <string>212.editorWindowContentRectSynchronizationRect</string> + <string>213.IBPluginDependency</string> + <string>213.ImportedFromIB2</string> + <string>214.IBPluginDependency</string> + <string>214.ImportedFromIB2</string> + <string>215.IBPluginDependency</string> + <string>215.ImportedFromIB2</string> + <string>216.IBPluginDependency</string> + <string>216.ImportedFromIB2</string> + <string>217.IBPluginDependency</string> + <string>217.ImportedFromIB2</string> + <string>218.IBPluginDependency</string> + <string>218.ImportedFromIB2</string> + <string>219.IBPluginDependency</string> + <string>219.ImportedFromIB2</string> + <string>220.IBEditorWindowLastContentRect</string> + <string>220.IBPluginDependency</string> + <string>220.ImportedFromIB2</string> + <string>220.editorWindowContentRectSynchronizationRect</string> + <string>221.IBPluginDependency</string> + <string>221.ImportedFromIB2</string> + <string>23.IBPluginDependency</string> + <string>23.ImportedFromIB2</string> + <string>236.IBPluginDependency</string> + <string>236.ImportedFromIB2</string> + <string>239.IBPluginDependency</string> + <string>239.ImportedFromIB2</string> + <string>24.IBEditorWindowLastContentRect</string> + <string>24.IBPluginDependency</string> + <string>24.ImportedFromIB2</string> + <string>24.editorWindowContentRectSynchronizationRect</string> + <string>29.IBEditorWindowLastContentRect</string> + <string>29.IBPluginDependency</string> + <string>29.ImportedFromIB2</string> + <string>29.WindowOrigin</string> + <string>29.editorWindowContentRectSynchronizationRect</string> + <string>295.IBPluginDependency</string> + <string>296.IBEditorWindowLastContentRect</string> + <string>296.IBPluginDependency</string> + <string>296.editorWindowContentRectSynchronizationRect</string> + <string>297.IBPluginDependency</string> + <string>298.IBPluginDependency</string> + <string>346.IBPluginDependency</string> + <string>346.ImportedFromIB2</string> + <string>348.IBPluginDependency</string> + <string>348.ImportedFromIB2</string> + <string>349.IBEditorWindowLastContentRect</string> + <string>349.IBPluginDependency</string> + <string>349.ImportedFromIB2</string> + <string>349.editorWindowContentRectSynchronizationRect</string> + <string>350.IBPluginDependency</string> + <string>350.ImportedFromIB2</string> + <string>351.IBPluginDependency</string> + <string>351.ImportedFromIB2</string> + <string>354.IBPluginDependency</string> + <string>354.ImportedFromIB2</string> + <string>371.IBEditorWindowLastContentRect</string> + <string>371.IBPluginDependency</string> + <string>371.IBWindowTemplateEditedContentRect</string> + <string>371.NSWindowTemplate.visibleAtLaunch</string> + <string>371.editorWindowContentRectSynchronizationRect</string> + <string>371.windowTemplate.maxSize</string> + <string>372.IBPluginDependency</string> + <string>375.IBPluginDependency</string> + <string>376.IBEditorWindowLastContentRect</string> + <string>376.IBPluginDependency</string> + <string>377.IBPluginDependency</string> + <string>388.IBEditorWindowLastContentRect</string> + <string>388.IBPluginDependency</string> + <string>389.IBPluginDependency</string> + <string>390.IBPluginDependency</string> + <string>391.IBPluginDependency</string> + <string>392.IBPluginDependency</string> + <string>393.IBPluginDependency</string> + <string>394.IBPluginDependency</string> + <string>395.IBPluginDependency</string> + <string>396.IBPluginDependency</string> + <string>397.IBPluginDependency</string> + <string>398.IBPluginDependency</string> + <string>399.IBPluginDependency</string> + <string>400.IBPluginDependency</string> + <string>401.IBPluginDependency</string> + <string>402.IBPluginDependency</string> + <string>403.IBPluginDependency</string> + <string>404.IBPluginDependency</string> + <string>405.IBPluginDependency</string> + <string>406.IBPluginDependency</string> + <string>407.IBPluginDependency</string> + <string>408.IBPluginDependency</string> + <string>409.IBPluginDependency</string> + <string>410.IBPluginDependency</string> + <string>411.IBPluginDependency</string> + <string>412.IBPluginDependency</string> + <string>413.IBPluginDependency</string> + <string>414.IBPluginDependency</string> + <string>415.IBPluginDependency</string> + <string>416.IBPluginDependency</string> + <string>417.IBPluginDependency</string> + <string>418.IBPluginDependency</string> + <string>419.IBPluginDependency</string> + <string>450.IBPluginDependency</string> + <string>451.IBEditorWindowLastContentRect</string> + <string>451.IBPluginDependency</string> + <string>452.IBPluginDependency</string> + <string>453.IBPluginDependency</string> + <string>454.IBPluginDependency</string> + <string>457.IBPluginDependency</string> + <string>459.IBPluginDependency</string> + <string>460.IBPluginDependency</string> + <string>462.IBPluginDependency</string> + <string>465.IBPluginDependency</string> + <string>466.IBPluginDependency</string> + <string>485.IBPluginDependency</string> + <string>490.IBPluginDependency</string> + <string>491.IBEditorWindowLastContentRect</string> + <string>491.IBPluginDependency</string> + <string>492.IBPluginDependency</string> + <string>496.IBPluginDependency</string> + <string>497.IBEditorWindowLastContentRect</string> + <string>497.IBPluginDependency</string> + <string>498.IBPluginDependency</string> + <string>499.IBPluginDependency</string> + <string>5.IBPluginDependency</string> + <string>5.ImportedFromIB2</string> + <string>500.IBPluginDependency</string> + <string>501.IBPluginDependency</string> + <string>502.IBPluginDependency</string> + <string>503.IBPluginDependency</string> + <string>504.IBPluginDependency</string> + <string>505.IBPluginDependency</string> + <string>506.IBPluginDependency</string> + <string>507.IBPluginDependency</string> + <string>508.IBEditorWindowLastContentRect</string> + <string>508.IBPluginDependency</string> + <string>509.IBPluginDependency</string> + <string>510.IBPluginDependency</string> + <string>511.IBPluginDependency</string> + <string>512.IBPluginDependency</string> + <string>513.IBPluginDependency</string> + <string>514.IBPluginDependency</string> + <string>515.IBPluginDependency</string> + <string>516.IBPluginDependency</string> + <string>517.IBPluginDependency</string> + <string>56.IBPluginDependency</string> + <string>56.ImportedFromIB2</string> + <string>57.IBEditorWindowLastContentRect</string> + <string>57.IBPluginDependency</string> + <string>57.ImportedFromIB2</string> + <string>57.editorWindowContentRectSynchronizationRect</string> + <string>58.IBPluginDependency</string> + <string>58.ImportedFromIB2</string> + <string>72.IBPluginDependency</string> + <string>72.ImportedFromIB2</string> + <string>73.IBPluginDependency</string> + <string>73.ImportedFromIB2</string> + <string>74.IBPluginDependency</string> + <string>74.ImportedFromIB2</string> + <string>75.IBPluginDependency</string> + <string>75.ImportedFromIB2</string> + <string>77.IBPluginDependency</string> + <string>77.ImportedFromIB2</string> + <string>78.IBPluginDependency</string> + <string>78.ImportedFromIB2</string> + <string>79.IBPluginDependency</string> + <string>79.ImportedFromIB2</string> + <string>80.IBPluginDependency</string> + <string>80.ImportedFromIB2</string> + <string>81.IBEditorWindowLastContentRect</string> + <string>81.IBPluginDependency</string> + <string>81.ImportedFromIB2</string> + <string>81.editorWindowContentRectSynchronizationRect</string> + <string>82.IBPluginDependency</string> + <string>82.ImportedFromIB2</string> + <string>83.IBPluginDependency</string> + <string>83.ImportedFromIB2</string> + <string>92.IBPluginDependency</string> + <string>92.ImportedFromIB2</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{522, 812}, {146, 23}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{436, 809}, {64, 6}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{753, 187}, {275, 113}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{608, 612}, {275, 83}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{547, 180}, {254, 283}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{187, 434}, {243, 243}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{608, 612}, {167, 43}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{753, 217}, {238, 103}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{608, 612}, {241, 103}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{654, 239}, {194, 73}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{525, 802}, {197, 73}}</string> + <string>{{380, 836}, {512, 20}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{74, 862}</string> + <string>{{6, 978}, {478, 20}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{604, 269}, {231, 43}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{475, 832}, {234, 43}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{746, 287}, {220, 133}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{608, 612}, {215, 63}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{380, 496}, {480, 360}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{380, 496}, {480, 360}}</string> + <integer value="1"/> + <string>{{33, 99}, {480, 360}}</string> + <string>{3.40282e+38, 3.40282e+38}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{591, 420}, {83, 43}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{523, 2}, {178, 283}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{753, 197}, {170, 63}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{725, 289}, {246, 23}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{674, 260}, {204, 183}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>{{878, 180}, {164, 173}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{286, 129}, {275, 183}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{23, 794}, {245, 183}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{452, 109}, {196, 203}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>{{145, 474}, {199, 203}}</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <integer value="1"/> + </object> + </object> + <object class="NSMutableDictionary" key="unlocalizedProperties"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="activeLocalization"/> + <object class="NSMutableDictionary" key="localizations"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference key="dict.sortedKeys" ref="0"/> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> + <nil key="sourceID"/> + <int key="maxID">532</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <object class="NSMutableArray" key="referencedPartialClassDescriptions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">TestAppAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="outlets"> + <string key="NS.key.0">window</string> + <string key="NS.object.0">NSWindow</string> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">TestAppAppDelegate.h</string> + </object> + </object> + </object> + <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <string key="superclassName">NSResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="822405504"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSApplication.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="850738725"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSApplicationScripting.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="624831158"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSColorPanel.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSHelpManager.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSPageLayout.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSApplication</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSUserInterfaceItemSearching.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSBrowser</string> + <string key="superclassName">NSControl</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSBrowser.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSControl</string> + <string key="superclassName">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="310914472"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSControl.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSDocument</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="actions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>printDocument:</string> + <string>revertDocumentToSaved:</string> + <string>runPageLayout:</string> + <string>saveDocument:</string> + <string>saveDocumentAs:</string> + <string>saveDocumentTo:</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>id</string> + <string>id</string> + <string>id</string> + <string>id</string> + <string>id</string> + <string>id</string> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDocument.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSDocument</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDocumentScripting.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSDocumentController</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="actions"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>clearRecentDocuments:</string> + <string>newDocument:</string> + <string>openDocument:</string> + <string>saveAllDocuments:</string> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + <string>id</string> + <string>id</string> + <string>id</string> + <string>id</string> + </object> + </object> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDocumentController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSFontManager</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="946436764"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSFontManager.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSFormatter</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSMatrix</string> + <string key="superclassName">NSControl</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSMatrix.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSMenu</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="1056362899"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSMenu.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSMenuItem</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="472958451"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSMovieView</string> + <string key="superclassName">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSMovieView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSAccessibility.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="822405504"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="850738725"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="624831158"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="310914472"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDictionaryController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDragging.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="946436764"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSFontPanel.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSKeyValueBinding.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <reference key="sourceIdentifier" ref="1056362899"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSNibLoading.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSOutlineView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSPasteboard.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSSavePanel.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="809545482"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSTableView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSToolbarItem.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier" id="260078765"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSError.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSObject.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSThread.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURL.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSInterfaceStyle.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSResponder</string> + <string key="superclassName">NSObject</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSResponder.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSTableView</string> + <string key="superclassName">NSControl</string> + <reference key="sourceIdentifier" ref="809545482"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSText</string> + <string key="superclassName">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSText.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSTextView</string> + <string key="superclassName">NSText</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSTextView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSClipView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSView</string> + <reference key="sourceIdentifier" ref="472958451"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSView</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSRulerView.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSView</string> + <string key="superclassName">NSResponder</string> + <reference key="sourceIdentifier" ref="260078765"/> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSWindow</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSDrawer.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSWindow</string> + <string key="superclassName">NSResponder</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSWindow.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> + <string key="className">NSWindow</string> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBFrameworkSource</string> + <string key="minorKey">AppKit.framework/Headers/NSWindowScripting.h</string> + </object> + </object> + </object> + </object> + <int key="IBDocument.localizationMode">0</int> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string> + <integer value="1060" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string> + <integer value="3000" key="NS.object.0"/> + </object> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <string key="IBDocument.LastKnownRelativeProjectPath">../TestApp.xcodeproj</string> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + </data> +</archive> diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist b/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist new file mode 100644 index 0000000000..98fd515200 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.google.${PRODUCT_NAME}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSMinimumSystemVersion</key> + <string>${MACOSX_DEPLOYMENT_TARGET}</string> + <key>NSMainNibFile</key> + <string>MainMenu</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> +</dict> +</plist> diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h new file mode 100644 index 0000000000..518645eae9 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h @@ -0,0 +1,13 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Cocoa/Cocoa.h> + +@interface TestAppAppDelegate : NSObject <NSApplicationDelegate> { + NSWindow *window; +} + +@property (assign) IBOutlet NSWindow *window; + +@end diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m new file mode 100644 index 0000000000..9aafa42000 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m @@ -0,0 +1,15 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "TestAppAppDelegate.h" + +@implementation TestAppAppDelegate + +@synthesize window; + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +@end diff --git a/tools/gyp/test/mac/app-bundle/TestApp/main.m b/tools/gyp/test/mac/app-bundle/TestApp/main.m new file mode 100644 index 0000000000..df6a12d065 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/TestApp/main.m @@ -0,0 +1,10 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Cocoa/Cocoa.h> + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/tools/gyp/test/mac/app-bundle/empty.c b/tools/gyp/test/mac/app-bundle/empty.c new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/empty.c diff --git a/tools/gyp/test/mac/app-bundle/test.gyp b/tools/gyp/test/mac/app-bundle/test.gyp new file mode 100644 index 0000000000..f51c7b4b67 --- /dev/null +++ b/tools/gyp/test/mac/app-bundle/test.gyp @@ -0,0 +1,39 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'dep_framework', + 'product_name': 'Dependency Framework', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'empty.c', ], + }, + { + 'target_name': 'test_app', + 'product_name': 'Test App Gyp', + 'type': 'executable', + 'mac_bundle': 1, + 'dependencies': [ 'dep_framework', ], + 'sources': [ + 'TestApp/main.m', + 'TestApp/TestApp_Prefix.pch', + 'TestApp/TestAppAppDelegate.h', + 'TestApp/TestAppAppDelegate.m', + ], + 'mac_bundle_resources': [ + 'TestApp/English.lproj/InfoPlist.strings', + 'TestApp/English.lproj/MainMenu.xib', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework', + ], + }, + 'xcode_settings': { + 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/debuginfo/file.c b/tools/gyp/test/mac/debuginfo/file.c new file mode 100644 index 0000000000..9cddaf1b0b --- /dev/null +++ b/tools/gyp/test/mac/debuginfo/file.c @@ -0,0 +1,6 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void f() {} +int main() {} diff --git a/tools/gyp/test/mac/debuginfo/test.gyp b/tools/gyp/test/mac/debuginfo/test.gyp new file mode 100644 index 0000000000..3faf6b5c76 --- /dev/null +++ b/tools/gyp/test/mac/debuginfo/test.gyp @@ -0,0 +1,82 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'nonbundle_static_library', + 'type': 'static_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'nonbundle_shared_library', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'nonbundle_loadable_module', + 'type': 'loadable_module', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'nonbundle_executable', + 'type': 'executable', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + + { + 'target_name': 'bundle_shared_library', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'bundle_loadable_module', + 'type': 'loadable_module', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'my_app', + 'product_name': 'My App', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym', + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings b/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000..88f65cf6ea --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/tools/gyp/test/mac/framework/TestFramework/Info.plist b/tools/gyp/test/mac/framework/TestFramework/Info.plist new file mode 100644 index 0000000000..5e05a5190c --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/Info.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.yourcompany.${PRODUCT_NAME}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h new file mode 100644 index 0000000000..c2450960cd --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Cocoa/Cocoa.h> + +#ifdef __cplusplus +struct ObjCVectorImp; +#else +typedef struct _ObjCVectorImpT ObjCVectorImp; +#endif + +@interface ObjCVector : NSObject { + @private + ObjCVectorImp* imp_; +} + +- (id)init; + +- (void)addObject:(id)obj; +- (void)addObject:(id)obj atIndex:(NSUInteger)index; + +- (void)removeObject:(id)obj; +- (void)removeObjectAtIndex:(NSUInteger)index; + +- (id)objectAtIndex:(NSUInteger)index; + +@end diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm new file mode 100644 index 0000000000..cbf431f28d --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm @@ -0,0 +1,63 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ObjCVectorInternal.h" +#import "ObjCVector.h" + +#include <vector> + +@interface ObjCVector (Private) +- (std::vector<id>::iterator)makeIterator:(NSUInteger)index; +@end + +@implementation ObjCVector + +- (id)init { + if ((self = [super init])) { + imp_ = new ObjCVectorImp(); + } + return self; +} + +- (void)dealloc { + delete imp_; + [super dealloc]; +} + +- (void)addObject:(id)obj { + imp_->v.push_back([obj retain]); +} + +- (void)addObject:(id)obj atIndex:(NSUInteger)index { + imp_->v.insert([self makeIterator:index], [obj retain]); +} + +- (void)removeObject:(id)obj { + for (std::vector<id>::iterator it = imp_->v.begin(); + it != imp_->v.end(); + ++it) { + if ([*it isEqual:obj]) { + [*it autorelease]; + imp_->v.erase(it); + return; + } + } +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + [imp_->v[index] autorelease]; + imp_->v.erase([self makeIterator:index]); +} + +- (id)objectAtIndex:(NSUInteger)index { + return imp_->v[index]; +} + +- (std::vector<id>::iterator)makeIterator:(NSUInteger)index { + std::vector<id>::iterator it = imp_->v.begin(); + it += index; + return it; +} + +@end diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h b/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h new file mode 100644 index 0000000000..fb6c98258b --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <vector> + +struct ObjCVectorImp { + std::vector<id> v; +}; diff --git a/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch b/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch new file mode 100644 index 0000000000..394f41d957 --- /dev/null +++ b/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'TestFramework' target in the 'TestFramework' project. +// + +#ifdef __OBJC__ + #import <Cocoa/Cocoa.h> +#endif diff --git a/tools/gyp/test/mac/framework/empty.c b/tools/gyp/test/mac/framework/empty.c new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/gyp/test/mac/framework/empty.c diff --git a/tools/gyp/test/mac/framework/framework.gyp b/tools/gyp/test/mac/framework/framework.gyp new file mode 100644 index 0000000000..7480e526c6 --- /dev/null +++ b/tools/gyp/test/mac/framework/framework.gyp @@ -0,0 +1,74 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'dep_framework', + 'product_name': 'Dependency Bundle', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'empty.c', ], + }, + { + 'target_name': 'test_framework', + 'product_name': 'Test Framework', + 'type': 'shared_library', + 'mac_bundle': 1, + 'dependencies': [ 'dep_framework', ], + 'sources': [ + 'TestFramework/ObjCVector.h', + 'TestFramework/ObjCVectorInternal.h', + 'TestFramework/ObjCVector.mm', + ], + 'mac_framework_headers': [ + 'TestFramework/ObjCVector.h', + ], + 'mac_bundle_resources': [ + 'TestFramework/English.lproj/InfoPlist.strings', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework', + ], + }, + 'xcode_settings': { + 'INFOPLIST_FILE': 'TestFramework/Info.plist', + 'GCC_DYNAMIC_NO_PIC': 'NO', + }, + 'copies': [ + # Test copying to a file that has envvars in its dest path. + # Needs to be in a mac_bundle target, else CONTENTS_FOLDER_PATH isn't + # set. + { + 'destination': '<(PRODUCT_DIR)/$(CONTENTS_FOLDER_PATH)/Libraries', + 'files': [ + 'empty.c', + ], + }, + ], + }, + { + 'target_name': 'copy_target', + 'type': 'none', + 'dependencies': [ 'test_framework', 'dep_framework', ], + 'copies': [ + # Test copying directories with spaces in src and dest paths. + { + 'destination': '<(PRODUCT_DIR)/Test Framework.framework/foo', + 'files': [ + '<(PRODUCT_DIR)/Dependency Bundle.framework', + ], + }, + ], + 'actions': [ + { + 'action_name': 'aektschn', + 'inputs': [], + 'outputs': ['<(PRODUCT_DIR)/touched_file'], + 'action': ['touch', '${BUILT_PRODUCTS_DIR}/action_file'], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/mac/gyptest-app.py b/tools/gyp/test/mac/gyptest-app.py new file mode 100755 index 0000000000..5e772f8144 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-app.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that app bundles are built correctly. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='app-bundle') + + test.build('test.gyp', test.ALL, chdir='app-bundle') + + # Binary + test.built_file_must_exist('Test App Gyp.app/Contents/MacOS/Test App Gyp', + chdir='app-bundle') + + # Info.plist + info_plist = test.built_file_path('Test App Gyp.app/Contents/Info.plist', + chdir='app-bundle') + test.must_exist(info_plist) + test.must_contain(info_plist, 'com.google.Test App Gyp') # Variable expansion + + # Resources + test.built_file_must_exist( + 'Test App Gyp.app/Contents/Resources/English.lproj/InfoPlist.strings', + chdir='app-bundle') + test.built_file_must_exist( + 'Test App Gyp.app/Contents/Resources/English.lproj/MainMenu.nib', + chdir='app-bundle') + + # Packaging + test.built_file_must_exist('Test App Gyp.app/Contents/PkgInfo', + chdir='app-bundle') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-copies.py b/tools/gyp/test/mac/gyptest-copies.py new file mode 100755 index 0000000000..091b1dec5b --- /dev/null +++ b/tools/gyp/test/mac/gyptest-copies.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that 'copies' with app bundles are handled correctly. +""" + +import TestGyp + +import os +import sys +import time + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('framework.gyp', chdir='framework') + + test.build('framework.gyp', 'copy_target', chdir='framework') + + # Check that the copy succeeded. + test.built_file_must_exist( + 'Test Framework.framework/foo/Dependency Bundle.framework', + chdir='framework') + test.built_file_must_exist( + 'Test Framework.framework/foo/Dependency Bundle.framework/Versions/A', + chdir='framework') + test.built_file_must_exist( + 'Test Framework.framework/Versions/A/Libraries/empty.c', + chdir='framework') + + + # Check that rebuilding the target a few times works. + dep_bundle = test.built_file_path('Dependency Bundle.framework', + chdir='framework') + mtime = os.path.getmtime(dep_bundle) + atime = os.path.getatime(dep_bundle) + for i in range(3): + os.utime(dep_bundle, (atime + i * 1000, mtime + i * 1000)) + test.build('framework.gyp', 'copy_target', chdir='framework') + + + # Check that actions ran. + test.built_file_must_exist('action_file', chdir='framework') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-debuginfo.py b/tools/gyp/test/mac/gyptest-debuginfo.py new file mode 100755 index 0000000000..1ec2d1da10 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-debuginfo.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Tests things related to debug information generation. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='debuginfo') + + test.build('test.gyp', test.ALL, chdir='debuginfo') + + test.built_file_must_exist('libnonbundle_shared_library.dylib.dSYM', + chdir='debuginfo') + test.built_file_must_exist('nonbundle_loadable_module.so.dSYM', + chdir='debuginfo') + test.built_file_must_exist('nonbundle_executable.dSYM', + chdir='debuginfo') + + test.built_file_must_exist('bundle_shared_library.framework.dSYM', + chdir='debuginfo') + test.built_file_must_exist('bundle_loadable_module.bundle.dSYM', + chdir='debuginfo') + test.built_file_must_exist('My App.app.dSYM', + chdir='debuginfo') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-framework.py b/tools/gyp/test/mac/gyptest-framework.py new file mode 100755 index 0000000000..6c19e40b33 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-framework.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that app bundles are built correctly. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('framework.gyp', chdir='framework') + + test.build('framework.gyp', 'test_framework', chdir='framework') + + # Binary + test.built_file_must_exist( + 'Test Framework.framework/Versions/A/Test Framework', + chdir='framework') + + # Info.plist + test.built_file_must_exist( + 'Test Framework.framework/Versions/A/Resources/Info.plist', + chdir='framework') + + # Resources + test.built_file_must_exist( + 'Test Framework.framework/Versions/A/Resources/English.lproj/' + 'InfoPlist.strings', + chdir='framework') + + # Symlinks created by packaging process + test.built_file_must_exist('Test Framework.framework/Versions/Current', + chdir='framework') + test.built_file_must_exist('Test Framework.framework/Resources', + chdir='framework') + test.built_file_must_exist('Test Framework.framework/Test Framework', + chdir='framework') + # PkgInfo. + test.built_file_must_not_exist( + 'Test Framework.framework/Versions/A/Resources/PkgInfo', + chdir='framework') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-infoplist-process.py b/tools/gyp/test/mac/gyptest-infoplist-process.py new file mode 100755 index 0000000000..9a2b140d31 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-infoplist-process.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies the Info.plist preprocessor functionality. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + CHDIR = 'infoplist-process' + INFO_PLIST_PATH = 'Test.app/Contents/Info.plist' + + # First process both keys. + test.set_configuration('One') + test.run_gyp('test1.gyp', chdir=CHDIR) + test.build('test1.gyp', test.ALL, chdir=CHDIR) + info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR) + test.must_exist(info_plist) + test.must_contain(info_plist, 'Foo') + test.must_contain(info_plist, 'Bar') + + # Then process a single key. + test.set_configuration('Two') + test.run_gyp('test2.gyp', chdir=CHDIR) + test.build('test2.gyp', chdir=CHDIR) + info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR) + test.must_exist(info_plist) + test.must_contain(info_plist, 'com.google.Test') # Normal expansion works. + test.must_contain(info_plist, 'Foo (Bar)') + test.must_contain(info_plist, 'PROCESSED_KEY2') + + # Then turn off the processor. + test.set_configuration('Three') + test.run_gyp('test3.gyp', chdir=CHDIR) + test.build('test3.gyp', chdir=CHDIR) + info_plist = test.built_file_path('Test App.app/Contents/Info.plist', + chdir=CHDIR) + test.must_exist(info_plist) + test.must_contain(info_plist, 'com.google.Test') # Normal expansion works. + test.must_contain(info_plist, 'PROCESSED_KEY1') + test.must_contain(info_plist, 'PROCESSED_KEY2') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-loadable-module.py b/tools/gyp/test/mac/gyptest-loadable-module.py new file mode 100755 index 0000000000..39e87f0605 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-loadable-module.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Tests that a loadable_module target is built correctly. +""" + +import TestGyp + +import os +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='loadable-module') + test.build('test.gyp', test.ALL, chdir='loadable-module') + + # Binary. + test.built_file_must_exist( + 'test_loadable_module.plugin/Contents/MacOS/test_loadable_module', + chdir='loadable-module') + + # Info.plist. + info_plist = test.built_file_path( + 'test_loadable_module.plugin/Contents/Info.plist', + chdir='loadable-module') + test.must_exist(info_plist) + test.must_contain(info_plist, """ + <key>CFBundleExecutable</key> + <string>test_loadable_module</string> +""") + + # PkgInfo. + test.built_file_must_not_exist( + 'test_loadable_module.plugin/Contents/PkgInfo', + chdir='loadable-module') + test.built_file_must_not_exist( + 'test_loadable_module.plugin/Contents/Resources', + chdir='loadable-module') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-postbuild-fail.py b/tools/gyp/test/mac/gyptest-postbuild-fail.py new file mode 100755 index 0000000000..28b1a8061f --- /dev/null +++ b/tools/gyp/test/mac/gyptest-postbuild-fail.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a failing postbuild step lets the build fail. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + # set |match| to ignore build stderr output. + test = TestGyp.TestGyp(formats=['make', 'xcode'], match = lambda a, b: True) + + test.run_gyp('test.gyp', chdir='postbuild-fail') + + build_error_code = { + 'xcode': 1, + 'make': 2, + }[test.format] + + + # If a postbuild fails, all postbuilds should be re-run on the next build. + # However, even if the first postbuild fails the other postbuilds are still + # executed. + + + # Non-bundles + test.build('test.gyp', 'nonbundle', chdir='postbuild-fail', + status=build_error_code) + test.built_file_must_exist('static_touch', + chdir='postbuild-fail') + # Check for non-up-to-date-ness by checking if building again produces an + # error. + test.build('test.gyp', 'nonbundle', chdir='postbuild-fail', + status=build_error_code) + + + # Bundles + test.build('test.gyp', 'bundle', chdir='postbuild-fail', + status=build_error_code) + test.built_file_must_exist('dynamic_touch', + chdir='postbuild-fail') + # Check for non-up-to-date-ness by checking if building again produces an + # error. + test.build('test.gyp', 'bundle', chdir='postbuild-fail', + status=build_error_code) + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-postbuild.py b/tools/gyp/test/mac/gyptest-postbuild.py new file mode 100755 index 0000000000..b69a0bdfea --- /dev/null +++ b/tools/gyp/test/mac/gyptest-postbuild.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that postbuild steps work. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='postbuilds') + + test.build('test.gyp', test.ALL, chdir='postbuilds') + + # See comment in test/subdirectory/gyptest-subdir-default.py + if test.format == 'xcode': + chdir = 'postbuilds/subdirectory' + else: + chdir = 'postbuilds' + + # Created by the postbuild scripts + test.built_file_must_exist('el.a_touch', + type=test.STATIC_LIB, + chdir='postbuilds') + test.built_file_must_exist('el.a_gyp_touch', + type=test.STATIC_LIB, + chdir='postbuilds') + test.built_file_must_exist('nest_el.a_touch', + type=test.STATIC_LIB, + chdir=chdir) + test.built_file_must_exist( + 'dyna.framework/Versions/A/dyna_touch', + chdir='postbuilds') + test.built_file_must_exist( + 'dyna.framework/Versions/A/dyna_gyp_touch', + chdir='postbuilds') + test.built_file_must_exist( + 'nest_dyna.framework/Versions/A/nest_dyna_touch', + chdir=chdir) + test.built_file_must_exist('dyna_standalone.dylib_gyp_touch', + type=test.SHARED_LIB, + chdir='postbuilds') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-prefixheader.py b/tools/gyp/test/mac/gyptest-prefixheader.py new file mode 100755 index 0000000000..04b00f6b13 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-prefixheader.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that GCC_PREFIX_HEADER works. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + test.run_gyp('test.gyp', chdir='prefixheader') + test.build('test.gyp', test.ALL, chdir='prefixheader') + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-rebuild.py b/tools/gyp/test/mac/gyptest-rebuild.py new file mode 100755 index 0000000000..f88dcb94b2 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-rebuild.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that app bundles are rebuilt correctly. +""" + +import TestGyp + +import os +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='app-bundle') + + test.build('test.gyp', test.ALL, chdir='app-bundle') + + # Touch a source file, rebuild, and check that the app target is up-to-date. + os.utime('app-bundle/TestApp/main.m', None) + test.build('test.gyp', test.ALL, chdir='app-bundle') + + test.up_to_date('test.gyp', test.ALL, chdir='app-bundle') + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-strip.py b/tools/gyp/test/mac/gyptest-strip.py new file mode 100755 index 0000000000..d031d4b3d8 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-strip.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that stripping works. +""" + +import TestGyp + +import os +import re +import subprocess +import sys +import time + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='strip') + + test.build('test.gyp', test.ALL, chdir='strip') + + # Lightweight check if stripping was done. + def OutPath(s): + return test.built_file_path(s, type=test.SHARED_LIB, chdir='strip') + + def CheckNsyms(p, n_expected): + r = re.compile(r'nsyms\s+(\d+)') + proc = subprocess.Popen(['otool', '-l', p], stdout=subprocess.PIPE) + o = proc.communicate()[0] + assert not proc.returncode + m = r.search(o, re.MULTILINE) + n = int(m.group(1)) + if n != n_expected: + print 'Stripping: Expected %d symbols, got %d' % (n_expected, n) + test.fail_test() + + # The actual numbers here are not interesting, they just need to be the same + # in both the xcode and the make build. + CheckNsyms(OutPath('no_postprocess'), 11) + CheckNsyms(OutPath('no_strip'), 11) + CheckNsyms(OutPath('strip_all'), 0) + CheckNsyms(OutPath('strip_nonglobal'), 2) + CheckNsyms(OutPath('strip_debugging'), 3) + CheckNsyms(OutPath('strip_all_custom_flags'), 0) + CheckNsyms(test.built_file_path( + 'strip_all_bundle.framework/Versions/A/strip_all_bundle', chdir='strip'), + 0) + CheckNsyms(OutPath('strip_save'), 3) + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-type-envvars.py b/tools/gyp/test/mac/gyptest-type-envvars.py new file mode 100755 index 0000000000..1ef677319f --- /dev/null +++ b/tools/gyp/test/mac/gyptest-type-envvars.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test that MACH_O_TYPE etc are set correctly. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + test.run_gyp('test.gyp', chdir='type_envvars') + + test.build('test.gyp', test.ALL, chdir='type_envvars') + + # The actual test is done by postbuild scripts during |test.build()|. + + test.pass_test() diff --git a/tools/gyp/test/mac/gyptest-xcode-env-order.py b/tools/gyp/test/mac/gyptest-xcode-env-order.py new file mode 100755 index 0000000000..6459373247 --- /dev/null +++ b/tools/gyp/test/mac/gyptest-xcode-env-order.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that dependent Xcode settings are processed correctly. +""" + +import TestGyp + +import sys + +if sys.platform == 'darwin': + test = TestGyp.TestGyp(formats=['make', 'xcode']) + + CHDIR = 'xcode-env-order' + INFO_PLIST_PATH = 'Test.app/Contents/Info.plist' + + test.run_gyp('test.gyp', chdir=CHDIR) + test.build('test.gyp', test.ALL, chdir=CHDIR) + info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR) + test.must_exist(info_plist) + test.must_contain(info_plist, '>/Source/Project/Test') + test.must_contain(info_plist, '>DEP:/Source/Project/Test') + test.must_contain(info_plist, + '>com.apple.product-type.application:DEP:/Source/Project/Test') + + test.pass_test() diff --git a/tools/gyp/test/mac/infoplist-process/Info.plist b/tools/gyp/test/mac/infoplist-process/Info.plist new file mode 100644 index 0000000000..cb65721f43 --- /dev/null +++ b/tools/gyp/test/mac/infoplist-process/Info.plist @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.google.${PRODUCT_NAME}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSMinimumSystemVersion</key> + <string>${MACOSX_DEPLOYMENT_TARGET}</string> + <key>NSMainNibFile</key> + <string>MainMenu</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>ProcessedKey1</key> + <string>PROCESSED_KEY1</string> + <key>ProcessedKey2</key> + <string>PROCESSED_KEY2</string> +</dict> +</plist> diff --git a/tools/gyp/test/mac/infoplist-process/main.c b/tools/gyp/test/mac/infoplist-process/main.c new file mode 100644 index 0000000000..1bf4b2a11a --- /dev/null +++ b/tools/gyp/test/mac/infoplist-process/main.c @@ -0,0 +1,7 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +int main() { + return 0; +} diff --git a/tools/gyp/test/mac/infoplist-process/test1.gyp b/tools/gyp/test/mac/infoplist-process/test1.gyp new file mode 100644 index 0000000000..bc625a968b --- /dev/null +++ b/tools/gyp/test/mac/infoplist-process/test1.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'test_app', + 'product_name': 'Test', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ + 'main.c', + ], + 'configurations': { + 'One': { + }, + }, + 'xcode_settings': { + 'INFOPLIST_FILE': 'Info.plist', + 'INFOPLIST_PREPROCESS': 'YES', + 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1=Foo PROCESSED_KEY2=Bar', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/infoplist-process/test2.gyp b/tools/gyp/test/mac/infoplist-process/test2.gyp new file mode 100644 index 0000000000..ecfbc9f64c --- /dev/null +++ b/tools/gyp/test/mac/infoplist-process/test2.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'test_app', + 'product_name': 'Test', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ + 'main.c', + ], + 'configurations': { + 'Two': { + }, + }, + 'xcode_settings': { + 'INFOPLIST_FILE': 'Info.plist', + 'INFOPLIST_PREPROCESS': 'YES', + 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1="Foo (Bar)"', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/infoplist-process/test3.gyp b/tools/gyp/test/mac/infoplist-process/test3.gyp new file mode 100644 index 0000000000..be8fe75a53 --- /dev/null +++ b/tools/gyp/test/mac/infoplist-process/test3.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'test_app', + 'product_name': 'Test App', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ + 'main.c', + ], + 'configurations': { + 'Three': { + }, + }, + 'xcode_settings': { + 'INFOPLIST_FILE': 'Info.plist', + 'INFOPLIST_PREPROCESS': 'NO', + 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1=Foo', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/loadable-module/Info.plist b/tools/gyp/test/mac/loadable-module/Info.plist new file mode 100644 index 0000000000..f6607aebd9 --- /dev/null +++ b/tools/gyp/test/mac/loadable-module/Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>com.google.test_loadable_module</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>BRPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>CFPlugInDynamicRegisterFunction</key> + <string></string> + <key>CFPlugInDynamicRegistration</key> + <string>NO</string> +</dict> +</plist> diff --git a/tools/gyp/test/mac/loadable-module/module.c b/tools/gyp/test/mac/loadable-module/module.c new file mode 100644 index 0000000000..9584538347 --- /dev/null +++ b/tools/gyp/test/mac/loadable-module/module.c @@ -0,0 +1,11 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +int SuperFly() { + return 42; +} + +const char* SuperFoo() { + return "Hello World"; +} diff --git a/tools/gyp/test/mac/loadable-module/test.gyp b/tools/gyp/test/mac/loadable-module/test.gyp new file mode 100644 index 0000000000..3c8a5309d2 --- /dev/null +++ b/tools/gyp/test/mac/loadable-module/test.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'test_loadable_module', + 'type': 'loadable_module', + 'mac_bundle': 1, + 'sources': [ 'module.c' ], + 'product_extension': 'plugin', + 'xcode_settings': { + 'INFOPLIST_FILE': 'Info.plist', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/postbuild-fail/file.c b/tools/gyp/test/mac/postbuild-fail/file.c new file mode 100644 index 0000000000..91695b10c6 --- /dev/null +++ b/tools/gyp/test/mac/postbuild-fail/file.c @@ -0,0 +1,6 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// That's right, this is copyrighted. +void f() {} diff --git a/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh new file mode 100755 index 0000000000..d4113d059e --- /dev/null +++ b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh @@ -0,0 +1,6 @@ +#!/usr/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +exit 1 diff --git a/tools/gyp/test/mac/postbuild-fail/test.gyp b/tools/gyp/test/mac/postbuild-fail/test.gyp new file mode 100644 index 0000000000..e63283db03 --- /dev/null +++ b/tools/gyp/test/mac/postbuild-fail/test.gyp @@ -0,0 +1,38 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'nonbundle', + 'type': 'static_library', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Postbuild Fail', + 'action': [ './postbuild-fail.sh', ], + }, + { + 'postbuild_name': 'Runs after failing postbuild', + 'action': [ './touch-static.sh', ], + }, + ], + }, + { + 'target_name': 'bundle', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Postbuild Fail', + 'action': [ './postbuild-fail.sh', ], + }, + { + 'postbuild_name': 'Runs after failing postbuild', + 'action': [ './touch-dynamic.sh', ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh b/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh new file mode 100755 index 0000000000..a388a64102 --- /dev/null +++ b/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e +touch "${BUILT_PRODUCTS_DIR}/dynamic_touch" diff --git a/tools/gyp/test/mac/postbuild-fail/touch-static.sh b/tools/gyp/test/mac/postbuild-fail/touch-static.sh new file mode 100755 index 0000000000..97ecaa6868 --- /dev/null +++ b/tools/gyp/test/mac/postbuild-fail/touch-static.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e +touch "${BUILT_PRODUCTS_DIR}/static_touch" diff --git a/tools/gyp/test/mac/postbuilds/file.c b/tools/gyp/test/mac/postbuilds/file.c new file mode 100644 index 0000000000..653e71ff7e --- /dev/null +++ b/tools/gyp/test/mac/postbuilds/file.c @@ -0,0 +1,4 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +void f() {} diff --git a/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh b/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh new file mode 100755 index 0000000000..c623c8bf21 --- /dev/null +++ b/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +lib="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" +nm ${lib} > /dev/null # Just make sure this works. + +pattern="${1}" + +if [ $pattern != "a|b" ]; then + echo "Parameter quoting is broken" + exit 1 +fi + +if [ "${2}" != "arg with spaces" ]; then + echo "Parameter space escaping is broken" + exit 1 +fi + +touch "${lib}"_touch diff --git a/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh b/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh new file mode 100755 index 0000000000..2bf09b34e1 --- /dev/null +++ b/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +lib="${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" +nm ${lib} > /dev/null # Just make sure this works. + +pattern="${1}" + +if [ $pattern != "a|b" ]; then + echo "Parameter quote escaping is broken" + exit 1 +fi + +if [ "${2}" != "arg with spaces" ]; then + echo "Parameter space escaping is broken" + exit 1 +fi + +touch "${lib}"_touch.a diff --git a/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp b/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp new file mode 100644 index 0000000000..f7ca9a6bf0 --- /dev/null +++ b/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp @@ -0,0 +1,45 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'nest_el', + 'type': 'static_library', + 'sources': [ '../file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Static library postbuild', + 'variables': { + 'some_regex': 'a|b', + }, + 'action': [ + '../script/static_library_postbuild.sh', + '<(some_regex)', + 'arg with spaces', + ], + }, + ], + }, + { + 'target_name': 'nest_dyna', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ '../file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Dynamic library postbuild', + 'variables': { + 'some_regex': 'a|b', + }, + 'action': [ + '../script/shared_library_postbuild.sh', + '<(some_regex)', + 'arg with spaces', + ], + }, + ], + }, + ], +} + diff --git a/tools/gyp/test/mac/postbuilds/test.gyp b/tools/gyp/test/mac/postbuilds/test.gyp new file mode 100644 index 0000000000..a9a05cb2d6 --- /dev/null +++ b/tools/gyp/test/mac/postbuilds/test.gyp @@ -0,0 +1,79 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'el', + 'type': 'static_library', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Static library postbuild', + 'variables': { + 'some_regex': 'a|b', + }, + 'action': [ + 'script/static_library_postbuild.sh', + '<(some_regex)', + 'arg with spaces', + ], + }, + { + 'postbuild_name': 'Test variable in gyp file', + 'action': [ + 'cp', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch.a', + ], + }, + ], + }, + { + 'target_name': 'dyna', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'dependencies': [ + 'subdirectory/nested_target.gyp:nest_dyna', + 'subdirectory/nested_target.gyp:nest_el', + ], + 'postbuilds': [ + { + 'postbuild_name': 'Dynamic library postbuild', + 'variables': { + 'some_regex': 'a|b', + }, + 'action': [ + 'script/shared_library_postbuild.sh', + '<(some_regex)', + 'arg with spaces', + ], + }, + { + 'postbuild_name': 'Test variable in gyp file', + 'action': [ + 'cp', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch', + ], + }, + ], + }, + { + 'target_name': 'dyna_standalone', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'Test variable in gyp file', + 'action': [ + 'cp', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}', + '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch.dylib', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/mac/prefixheader/file.c b/tools/gyp/test/mac/prefixheader/file.c new file mode 100644 index 0000000000..d0b39d1f6d --- /dev/null +++ b/tools/gyp/test/mac/prefixheader/file.c @@ -0,0 +1 @@ +MyInt f() { return 0; } diff --git a/tools/gyp/test/mac/prefixheader/header.h b/tools/gyp/test/mac/prefixheader/header.h new file mode 100644 index 0000000000..0716e500c5 --- /dev/null +++ b/tools/gyp/test/mac/prefixheader/header.h @@ -0,0 +1 @@ +typedef int MyInt; diff --git a/tools/gyp/test/mac/prefixheader/test.gyp b/tools/gyp/test/mac/prefixheader/test.gyp new file mode 100644 index 0000000000..ce318dfc71 --- /dev/null +++ b/tools/gyp/test/mac/prefixheader/test.gyp @@ -0,0 +1,25 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'prefix_header', + 'type': 'static_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'GCC_PREFIX_HEADER': 'header.h', + }, + }, + { + 'target_name': 'precompiled_prefix_header', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'GCC_PREFIX_HEADER': 'header.h', + 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/strip/file.c b/tools/gyp/test/mac/strip/file.c new file mode 100644 index 0000000000..421f0405f5 --- /dev/null +++ b/tools/gyp/test/mac/strip/file.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +static void the_static_function() {} + +void the_function() { + the_static_function(); +} diff --git a/tools/gyp/test/mac/strip/strip.saves b/tools/gyp/test/mac/strip/strip.saves new file mode 100644 index 0000000000..b60ca62857 --- /dev/null +++ b/tools/gyp/test/mac/strip/strip.saves @@ -0,0 +1,5 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file would list symbols that should not be stripped. diff --git a/tools/gyp/test/mac/strip/test.gyp b/tools/gyp/test/mac/strip/test.gyp new file mode 100644 index 0000000000..08c8c526ab --- /dev/null +++ b/tools/gyp/test/mac/strip/test.gyp @@ -0,0 +1,115 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# These xcode_settings affect stripping: +# "Deployment postprocessing involves stripping the binary, and setting +# its file mode, owner, and group." +#'DEPLOYMENT_POSTPROCESSING': 'YES', + +# "Specifies whether to strip symbol information from the binary. +# Prerequisite: $DEPLOYMENT_POSTPROCESSING = YES" "Default Value: 'NO'" +#'STRIP_INSTALLED_PRODUCT': 'YES', + +# "Values: +# * all: Strips the binary completely, removing the symbol table and +# relocation information +# * non-global: Strips nonglobal symbols but saves external symbols. +# * debugging: Strips debugging symbols but saves local and global +# symbols." +# (maps to no flag, -x, -S in that order) +#'STRIP_STYLE': 'non-global', + +# "Additional strip flags" +#'STRIPFLAGS': '-c', + +# "YES: Copied binaries are stripped of debugging symbols. This does +# not cause the binary produced by the linker to be stripped. Use +# 'STRIP_INSTALLED_PRODUCT (Strip Linked Product)' to have the linker +# strip the binary." +#'COPY_PHASE_STRIP': 'NO', +{ + 'targets': [ + { + 'target_name': 'no_postprocess', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'NO', + 'STRIP_INSTALLED_PRODUCT': 'YES', + }, + }, + { + 'target_name': 'no_strip', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'NO', + }, + }, + { + 'target_name': 'strip_all', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIP_STYLE': 'all', + }, + }, + { + 'target_name': 'strip_nonglobal', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIP_STYLE': 'non-global', + }, + }, + { + 'target_name': 'strip_debugging', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIP_STYLE': 'debugging', + }, + }, + { + 'target_name': 'strip_all_custom_flags', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIP_STYLE': 'all', + 'STRIPFLAGS': '-c', + }, + }, + { + 'target_name': 'strip_all_bundle', + 'type': 'shared_library', + 'mac_bundle': '1', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIP_STYLE': 'all', + }, + }, + { + 'target_name': 'strip_save', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'xcode_settings': { + 'DEPLOYMENT_POSTPROCESSING': 'YES', + 'STRIP_INSTALLED_PRODUCT': 'YES', + 'STRIPFLAGS': '-s $(CHROMIUM_STRIP_SAVE_FILE)', + 'CHROMIUM_STRIP_SAVE_FILE': 'strip.saves', + }, + }, + ], +} diff --git a/tools/gyp/test/mac/type_envvars/file.c b/tools/gyp/test/mac/type_envvars/file.c new file mode 100644 index 0000000000..9cddaf1b0b --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/file.c @@ -0,0 +1,6 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void f() {} +int main() {} diff --git a/tools/gyp/test/mac/type_envvars/test.gyp b/tools/gyp/test/mac/type_envvars/test.gyp new file mode 100644 index 0000000000..4827957f67 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test.gyp @@ -0,0 +1,89 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'my_app', + 'product_name': 'My App', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_bundle_executable.sh', ], + }, + ], + }, + { + 'target_name': 'bundle_loadable_module', + 'type': 'loadable_module', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_bundle_loadable_module.sh', ], + }, + ], + }, + { + 'target_name': 'bundle_shared_library', + 'type': 'shared_library', + 'mac_bundle': 1, + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_bundle_shared_library.sh', ], + }, + ], + }, + + { + 'target_name': 'nonbundle_executable', + 'type': 'executable', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_nonbundle_executable.sh', ], + }, + ], + }, + { + 'target_name': 'nonbundle_loadable_module', + 'type': 'loadable_module', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_nonbundle_loadable_module.sh', ], + }, + ], + }, + { + 'target_name': 'nonbundle_shared_library', + 'type': 'shared_library', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_nonbundle_shared_library.sh', ], + }, + ], + }, + { + 'target_name': 'nonbundle_static_library', + 'type': 'static_library', + 'sources': [ 'file.c', ], + 'postbuilds': [ + { + 'postbuild_name': 'envtest', + 'action': [ './test_nonbundle_static_library.sh', ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh b/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh new file mode 100755 index 0000000000..4d3a084f07 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = mh_execute +test $PRODUCT_TYPE = com.apple.product-type.application diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh b/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh new file mode 100755 index 0000000000..c74c8e435e --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = mh_bundle +test $PRODUCT_TYPE = com.apple.product-type.bundle diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh b/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh new file mode 100755 index 0000000000..08ad4fb7f6 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = mh_dylib +test $PRODUCT_TYPE = com.apple.product-type.framework diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh new file mode 100755 index 0000000000..02d373de86 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e +# For some reason, Xcode doesn't set MACH_O_TYPE for non-bundle executables. +# Check for "not set", not just "empty": +[[ ! $MACH_O_TYPE && ${MACH_O_TYPE-_} ]] +test $PRODUCT_TYPE = com.apple.product-type.tool diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh new file mode 100755 index 0000000000..aa67df3531 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = mh_bundle +test $PRODUCT_TYPE = com.apple.product-type.library.dynamic diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh new file mode 100755 index 0000000000..937a50c4aa --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = mh_dylib +test $PRODUCT_TYPE = com.apple.product-type.library.dynamic diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh new file mode 100755 index 0000000000..892f0d9f11 --- /dev/null +++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +test $MACH_O_TYPE = staticlib +test $PRODUCT_TYPE = com.apple.product-type.library.static diff --git a/tools/gyp/test/mac/xcode-env-order/Info.plist b/tools/gyp/test/mac/xcode-env-order/Info.plist new file mode 100644 index 0000000000..55db30dba6 --- /dev/null +++ b/tools/gyp/test/mac/xcode-env-order/Info.plist @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.google.${PRODUCT_NAME}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSMinimumSystemVersion</key> + <string>${MACOSX_DEPLOYMENT_TARGET}</string> + <key>NSMainNibFile</key> + <string>MainMenu</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>ProcessedKey1</key> + <string>${DEPENDENT_KEY1}</string> + <key>ProcessedKey2</key> + <string>${DEPENDENT_KEY2}</string> + <key>ProcessedKey3</key> + <string>${DEPENDENT_KEY3}</string> +</dict> +</plist> diff --git a/tools/gyp/test/mac/xcode-env-order/main.c b/tools/gyp/test/mac/xcode-env-order/main.c new file mode 100644 index 0000000000..1bf4b2a11a --- /dev/null +++ b/tools/gyp/test/mac/xcode-env-order/main.c @@ -0,0 +1,7 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +int main() { + return 0; +} diff --git a/tools/gyp/test/mac/xcode-env-order/test.gyp b/tools/gyp/test/mac/xcode-env-order/test.gyp new file mode 100644 index 0000000000..d9191aab5e --- /dev/null +++ b/tools/gyp/test/mac/xcode-env-order/test.gyp @@ -0,0 +1,23 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'test_app', + 'product_name': 'Test', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ + 'main.c', + ], + 'xcode_settings': { + 'INFOPLIST_FILE': 'Info.plist', + 'STRING_KEY': '/Source/Project', + 'DEPENDENT_KEY2': '$(STRING_KEY)/$(PRODUCT_NAME)', + 'DEPENDENT_KEY1': 'DEP:$(DEPENDENT_KEY2)', + 'DEPENDENT_KEY3': '$(PRODUCT_TYPE):$(DEPENDENT_KEY1)', + }, + }, + ], +} diff --git a/tools/gyp/test/make/dependencies.gyp b/tools/gyp/test/make/dependencies.gyp new file mode 100644 index 0000000000..e2bee24fce --- /dev/null +++ b/tools/gyp/test/make/dependencies.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'main', + 'type': 'executable', + 'sources': [ + 'main.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/make/gyptest-dependencies.py b/tools/gyp/test/make/gyptest-dependencies.py new file mode 100755 index 0000000000..76cfd0e819 --- /dev/null +++ b/tools/gyp/test/make/gyptest-dependencies.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that .d files and all.deps are properly generated. +""" + +import os +import TestGyp + +# .d files are only used by the make build. +test = TestGyp.TestGyp(formats=['make']) + +test.run_gyp('dependencies.gyp') + +test.build('dependencies.gyp', test.ALL) + +deps_file = test.built_file_path(".deps/out/Default/obj.target/main/main.o.d") +test.must_contain(deps_file, "main.h") + +# Build a second time to make sure we generate all.deps. +test.build('dependencies.gyp', test.ALL) + +all_deps_file = test.built_file_path(".deps/all.deps") +test.must_contain(all_deps_file, "main.h") +test.must_contain(all_deps_file, "cmd_") + +test.pass_test() diff --git a/tools/gyp/test/make/gyptest-noload.py b/tools/gyp/test/make/gyptest-noload.py new file mode 100755 index 0000000000..1f5103315c --- /dev/null +++ b/tools/gyp/test/make/gyptest-noload.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Tests the use of the NO_LOAD flag which makes loading sub .mk files +optional. +""" + +# Python 2.5 needs this for the with statement. +from __future__ import with_statement + +import os +import TestGyp + +test = TestGyp.TestGyp(formats=['make']) + +test.run_gyp('all.gyp', chdir='noload') + +test.relocate('noload', 'relocate/noload') + +test.build('build/all.gyp', test.ALL, chdir='relocate/noload') +test.run_built_executable('exe', chdir='relocate/noload', + stdout='Hello from shared.c.\n') + +# Just sanity test that NO_LOAD=lib doesn't break anything. +test.build('build/all.gyp', test.ALL, chdir='relocate/noload', + arguments=['NO_LOAD=lib']) +test.run_built_executable('exe', chdir='relocate/noload', + stdout='Hello from shared.c.\n') +test.build('build/all.gyp', test.ALL, chdir='relocate/noload', + arguments=['NO_LOAD=z']) +test.run_built_executable('exe', chdir='relocate/noload', + stdout='Hello from shared.c.\n') + +# Make sure we can rebuild without reloading the sub .mk file. +with open('relocate/noload/main.c', 'a') as src_file: + src_file.write("\n") +test.build('build/all.gyp', test.ALL, chdir='relocate/noload', + arguments=['NO_LOAD=lib']) +test.run_built_executable('exe', chdir='relocate/noload', + stdout='Hello from shared.c.\n') + +# Change shared.c, but verify that it doesn't get rebuild if we don't load it. +with open('relocate/noload/lib/shared.c', 'w') as shared_file: + shared_file.write( + '#include "shared.h"\n' + 'const char kSharedStr[] = "modified";\n' + ) +test.build('build/all.gyp', test.ALL, chdir='relocate/noload', + arguments=['NO_LOAD=lib']) +test.run_built_executable('exe', chdir='relocate/noload', + stdout='Hello from shared.c.\n') + +test.pass_test() diff --git a/tools/gyp/test/make/main.cc b/tools/gyp/test/make/main.cc new file mode 100644 index 0000000000..70ac6e46ae --- /dev/null +++ b/tools/gyp/test/make/main.cc @@ -0,0 +1,12 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +#include "main.h" + +int main(int argc, char *argv[]) { + printf("hello world\n"); + return 0; +} diff --git a/tools/gyp/test/make/main.h b/tools/gyp/test/make/main.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/gyp/test/make/main.h diff --git a/tools/gyp/test/make/noload/all.gyp b/tools/gyp/test/make/noload/all.gyp new file mode 100644 index 0000000000..1617a9e97c --- /dev/null +++ b/tools/gyp/test/make/noload/all.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'exe', + 'type': 'executable', + 'sources': [ + 'main.c', + ], + 'dependencies': [ + 'lib/shared.gyp:shared', + ], + }, + ], +} diff --git a/tools/gyp/test/make/noload/lib/shared.c b/tools/gyp/test/make/noload/lib/shared.c new file mode 100644 index 0000000000..51776c5acf --- /dev/null +++ b/tools/gyp/test/make/noload/lib/shared.c @@ -0,0 +1,3 @@ +#include "shared.h" + +const char kSharedStr[] = "shared.c"; diff --git a/tools/gyp/test/make/noload/lib/shared.gyp b/tools/gyp/test/make/noload/lib/shared.gyp new file mode 100644 index 0000000000..8a8841b3a0 --- /dev/null +++ b/tools/gyp/test/make/noload/lib/shared.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'shared', + 'type': 'shared_library', + 'sources': [ + 'shared.c', + 'shared.h', + ], + }, + ], +} diff --git a/tools/gyp/test/make/noload/lib/shared.h b/tools/gyp/test/make/noload/lib/shared.h new file mode 100644 index 0000000000..a21da7538b --- /dev/null +++ b/tools/gyp/test/make/noload/lib/shared.h @@ -0,0 +1 @@ +extern const char kSharedStr[]; diff --git a/tools/gyp/test/make/noload/main.c b/tools/gyp/test/make/noload/main.c new file mode 100644 index 0000000000..46d3c52c2d --- /dev/null +++ b/tools/gyp/test/make/noload/main.c @@ -0,0 +1,9 @@ +#include <stdio.h> + +#include "lib/shared.h" + +int main(int argc, char *argv[]) +{ + printf("Hello from %s.\n", kSharedStr); + return 0; +} diff --git a/tools/gyp/test/module/gyptest-default.py b/tools/gyp/test/module/gyptest-default.py new file mode 100755 index 0000000000..6b1c9b6a85 --- /dev/null +++ b/tools/gyp/test/module/gyptest-default.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple build of a "Hello, world!" program with loadable modules. The +default for all platforms should be to output the loadable modules to the same +path as the executable. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('module.gyp', chdir='src') + +test.build('module.gyp', test.ALL, chdir='src') + +expect = """\ +Hello from program.c +Hello from lib1.c +Hello from lib2.c +""" +test.run_built_executable('program', chdir='src', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/module/src/lib1.c b/tools/gyp/test/module/src/lib1.c new file mode 100644 index 0000000000..8de0e94bee --- /dev/null +++ b/tools/gyp/test/module/src/lib1.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void module_main(void) +{ + fprintf(stdout, "Hello from lib1.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/module/src/lib2.c b/tools/gyp/test/module/src/lib2.c new file mode 100644 index 0000000000..266396dc91 --- /dev/null +++ b/tools/gyp/test/module/src/lib2.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#ifdef _WIN32 +__declspec(dllexport) +#endif +void module_main(void) +{ + fprintf(stdout, "Hello from lib2.c\n"); + fflush(stdout); +} diff --git a/tools/gyp/test/module/src/module.gyp b/tools/gyp/test/module/src/module.gyp new file mode 100644 index 0000000000..bb43c30230 --- /dev/null +++ b/tools/gyp/test/module/src/module.gyp @@ -0,0 +1,55 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'conditions': [ + ['OS=="win"', { + 'defines': ['PLATFORM_WIN'], + }], + ['OS=="mac"', { + 'defines': ['PLATFORM_MAC'], + }], + ['OS=="linux"', { + 'defines': ['PLATFORM_LINUX'], + # Support 64-bit shared libs (also works fine for 32-bit). + 'cflags': ['-fPIC'], + 'ldflags': ['-ldl'], + }], + ], + }, + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'dependencies': [ + 'lib1', + 'lib2', + ], + 'sources': [ + 'program.c', + ], + }, + { + 'target_name': 'lib1', + 'type': 'loadable_module', + 'product_name': 'lib1', + 'product_prefix': '', + 'xcode_settings': {'OTHER_LDFLAGS': ['-dynamiclib'], 'MACH_O_TYPE': ''}, + 'sources': [ + 'lib1.c', + ], + }, + { + 'target_name': 'lib2', + 'product_name': 'lib2', + 'product_prefix': '', + 'type': 'loadable_module', + 'xcode_settings': {'OTHER_LDFLAGS': ['-dynamiclib'], 'MACH_O_TYPE': ''}, + 'sources': [ + 'lib2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/module/src/program.c b/tools/gyp/test/module/src/program.c new file mode 100644 index 0000000000..b2f3320917 --- /dev/null +++ b/tools/gyp/test/module/src/program.c @@ -0,0 +1,111 @@ +#include <stdio.h> +#include <stdlib.h> + +#if defined(PLATFORM_WIN) +#include <windows.h> +#elif defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#include <dlfcn.h> +#include <libgen.h> +#include <string.h> +#include <sys/param.h> +#define MAX_PATH PATH_MAX +#endif + +#if defined(PLATFORM_WIN) +#define MODULE_SUFFIX ".dll" +#elif defined(PLATFORM_MAC) +#define MODULE_SUFFIX ".so" +#elif defined(PLATFORM_LINUX) +#define MODULE_SUFFIX ".so" +#endif + +typedef void (*module_symbol)(void); +char bin_path[MAX_PATH + 1]; + + +void CallModule(const char* module) { + char module_path[MAX_PATH + 1]; + const char* module_function = "module_main"; + module_symbol funcptr; +#if defined(PLATFORM_WIN) + HMODULE dl; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + + if (_splitpath_s(bin_path, drive, _MAX_DRIVE, dir, _MAX_DIR, + NULL, 0, NULL, 0)) { + fprintf(stderr, "Failed to split executable path.\n"); + return; + } + if (_makepath_s(module_path, MAX_PATH, drive, dir, module, MODULE_SUFFIX)) { + fprintf(stderr, "Failed to calculate module path.\n"); + return; + } + + dl = LoadLibrary(module_path); + if (!dl) { + fprintf(stderr, "Failed to open module: %s\n", module_path); + return; + } + + funcptr = (module_symbol) GetProcAddress(dl, module_function); + if (!funcptr) { + fprintf(stderr, "Failed to find symbol: %s\n", module_function); + return; + } + funcptr(); + + FreeLibrary(dl); +#elif defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + void* dl; + char* path_copy = strdup(bin_path); + char* bin_dir = dirname(path_copy); + int path_size = snprintf(module_path, MAX_PATH, "%s/%s%s", bin_dir, module, + MODULE_SUFFIX); + free(path_copy); + if (path_size < 0 || path_size > MAX_PATH) { + fprintf(stderr, "Failed to calculate module path.\n"); + return; + } + module_path[path_size] = 0; + + dl = dlopen(module_path, RTLD_LAZY); + if (!dl) { + fprintf(stderr, "Failed to open module: %s\n", module_path); + return; + } + + funcptr = dlsym(dl, module_function); + if (!funcptr) { + fprintf(stderr, "Failed to find symbol: %s\n", module_function); + return; + } + funcptr(); + + dlclose(dl); +#endif +} + +int main(int argc, char *argv[]) +{ + fprintf(stdout, "Hello from program.c\n"); + fflush(stdout); + +#if defined(PLATFORM_WIN) + if (!GetModuleFileName(NULL, bin_path, MAX_PATH)) { + fprintf(stderr, "Failed to determine executable path.\n"); + return; + } +#elif defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + // Using argv[0] should be OK here since we control how the tests run, and + // can avoid exec and such issues that make it unreliable. + if (!realpath(argv[0], bin_path)) { + fprintf(stderr, "Failed to determine executable path (%s).\n", argv[0]); + return; + } +#endif + + CallModule("lib1"); + CallModule("lib2"); + return 0; +} diff --git a/tools/gyp/test/msvs/express/base/base.gyp b/tools/gyp/test/msvs/express/base/base.gyp new file mode 100644 index 0000000000..b7c9fc6d81 --- /dev/null +++ b/tools/gyp/test/msvs/express/base/base.gyp @@ -0,0 +1,22 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'static_library', + 'sources': [ + 'a.c', + ], + }, + { + 'target_name': 'b', + 'type': 'static_library', + 'sources': [ + 'b.c', + ], + }, + ], +} diff --git a/tools/gyp/test/msvs/express/express.gyp b/tools/gyp/test/msvs/express/express.gyp new file mode 100644 index 0000000000..917abe2cc0 --- /dev/null +++ b/tools/gyp/test/msvs/express/express.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'express', + 'type': 'executable', + 'dependencies': [ + 'base/base.gyp:a', + 'base/base.gyp:b', + ], + 'sources': [ + 'main.c', + ], + }, + ], +} diff --git a/tools/gyp/test/msvs/express/gyptest-express.py b/tools/gyp/test/msvs/express/gyptest-express.py new file mode 100755 index 0000000000..54c06f664a --- /dev/null +++ b/tools/gyp/test/msvs/express/gyptest-express.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that flat solutions get generated for Express versions of +Visual Studio. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['msvs']) + +test.run_gyp('express.gyp', '-G', 'msvs_version=2005') +test.must_contain('express.sln', '(base)') + +test.run_gyp('express.gyp', '-G', 'msvs_version=2008') +test.must_contain('express.sln', '(base)') + +test.run_gyp('express.gyp', '-G', 'msvs_version=2005e') +test.must_not_contain('express.sln', '(base)') + +test.run_gyp('express.gyp', '-G', 'msvs_version=2008e') +test.must_not_contain('express.sln', '(base)') + + +test.pass_test() diff --git a/tools/gyp/test/msvs/precompiled/gyptest-all.py b/tools/gyp/test/msvs/precompiled/gyptest-all.py new file mode 100755 index 0000000000..327ca6d6e1 --- /dev/null +++ b/tools/gyp/test/msvs/precompiled/gyptest-all.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that precompiled headers can be specified. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['msvs'], workdir='workarea_all') + +test.run_gyp('hello.gyp') + +test.build('hello.gyp', 'hello') + +test.run_built_executable('hello', stdout="Hello, world!\nHello, two!\n") + +test.up_to_date('hello.gyp', test.ALL) + +test.pass_test() diff --git a/tools/gyp/test/msvs/precompiled/hello.c b/tools/gyp/test/msvs/precompiled/hello.c new file mode 100644 index 0000000000..d1abbb9e51 --- /dev/null +++ b/tools/gyp/test/msvs/precompiled/hello.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +// Note the abscence of a stdio.h include. This will be inserted because of the +// precompiled header. + +extern int hello2(); + +int main(int argc, char *argv[]) { + printf("Hello, world!\n"); + hello2(); + return 0; +} diff --git a/tools/gyp/test/msvs/precompiled/hello.gyp b/tools/gyp/test/msvs/precompiled/hello.gyp new file mode 100644 index 0000000000..b9533efd85 --- /dev/null +++ b/tools/gyp/test/msvs/precompiled/hello.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'hello', + 'type': 'executable', + 'sources': [ + 'hello.c', + 'hello2.c', + 'precomp.c', + ], + 'msvs_precompiled_header': 'stdio.h', + 'msvs_precompiled_source': 'precomp.c', + }, + ], +} diff --git a/tools/gyp/test/msvs/precompiled/hello2.c b/tools/gyp/test/msvs/precompiled/hello2.c new file mode 100644 index 0000000000..d6d53111fb --- /dev/null +++ b/tools/gyp/test/msvs/precompiled/hello2.c @@ -0,0 +1,13 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +// Unlike hello.c, this file specifies the headers. + +#include <windows.h> +#include <stdio.h> + +int hello2() { + printf("Hello, two!\n"); + return 0; +} diff --git a/tools/gyp/test/msvs/precompiled/precomp.c b/tools/gyp/test/msvs/precompiled/precomp.c new file mode 100644 index 0000000000..517c61a36b --- /dev/null +++ b/tools/gyp/test/msvs/precompiled/precomp.c @@ -0,0 +1,8 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +// The precompiled header does not have to be the first one in the file. + +#include <windows.h> +#include <stdio.h> diff --git a/tools/gyp/test/multiple-targets/gyptest-all.py b/tools/gyp/test/multiple-targets/gyptest-all.py new file mode 100755 index 0000000000..9f157c4f82 --- /dev/null +++ b/tools/gyp/test/multiple-targets/gyptest-all.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('multiple.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +# TODO(sgk): remove stderr=None when the --generator-output= support +# gets rid of the scons warning +test.build('multiple.gyp', test.ALL, chdir='relocate/src', stderr=None) + +expect1 = """\ +hello from prog1.c +hello from common.c +""" + +expect2 = """\ +hello from prog2.c +hello from common.c +""" + +test.run_built_executable('prog1', stdout=expect1, chdir='relocate/src') +test.run_built_executable('prog2', stdout=expect2, chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/multiple-targets/gyptest-default.py b/tools/gyp/test/multiple-targets/gyptest-default.py new file mode 100755 index 0000000000..8d5072d230 --- /dev/null +++ b/tools/gyp/test/multiple-targets/gyptest-default.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('multiple.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +# TODO(sgk): remove stderr=None when the --generator-output= support +# gets rid of the scons warning +test.build('multiple.gyp', chdir='relocate/src', stderr=None) + +expect1 = """\ +hello from prog1.c +hello from common.c +""" + +expect2 = """\ +hello from prog2.c +hello from common.c +""" + +test.run_built_executable('prog1', stdout=expect1, chdir='relocate/src') +test.run_built_executable('prog2', stdout=expect2, chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/multiple-targets/src/common.c b/tools/gyp/test/multiple-targets/src/common.c new file mode 100644 index 0000000000..f1df7c1431 --- /dev/null +++ b/tools/gyp/test/multiple-targets/src/common.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +void common(void) +{ + printf("hello from common.c\n"); + return; +} diff --git a/tools/gyp/test/multiple-targets/src/multiple.gyp b/tools/gyp/test/multiple-targets/src/multiple.gyp new file mode 100644 index 0000000000..3db4ea30cd --- /dev/null +++ b/tools/gyp/test/multiple-targets/src/multiple.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'sources': [ + 'prog1.c', + 'common.c', + ], + }, + { + 'target_name': 'prog2', + 'type': 'executable', + 'sources': [ + 'prog2.c', + 'common.c', + ], + }, + ], +} diff --git a/tools/gyp/test/multiple-targets/src/prog1.c b/tools/gyp/test/multiple-targets/src/prog1.c new file mode 100644 index 0000000000..d55f8af1d0 --- /dev/null +++ b/tools/gyp/test/multiple-targets/src/prog1.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void common(void); + +int main(int argc, char *argv[]) +{ + printf("hello from prog1.c\n"); + common(); + return 0; +} diff --git a/tools/gyp/test/multiple-targets/src/prog2.c b/tools/gyp/test/multiple-targets/src/prog2.c new file mode 100644 index 0000000000..760590eb68 --- /dev/null +++ b/tools/gyp/test/multiple-targets/src/prog2.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void common(void); + +int main(int argc, char *argv[]) +{ + printf("hello from prog2.c\n"); + common(); + return 0; +} diff --git a/tools/gyp/test/ninja/action_dependencies/gyptest-action-dependencies.py b/tools/gyp/test/ninja/action_dependencies/gyptest-action-dependencies.py new file mode 100755 index 0000000000..f84af24ec5 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/gyptest-action-dependencies.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify that building an object file correctly depends on running actions in +dependent targets, but not the targets themselves. +""" + +import TestGyp + +# NOTE(piman): This test will not work with other generators because: +# - it explicitly tests the optimization, which is not implemented (yet?) on +# other generators +# - it relies on the exact path to output object files, which is generator +# dependent, and actually, relies on the ability to build only that object file, +# which I don't think is available on all generators. +# TODO(piman): Extend to other generators when possible. +test = TestGyp.TestGyp(formats=['ninja']) + +test.run_gyp('action_dependencies.gyp', chdir='src') + +chdir = 'relocate/src' +test.relocate('src', chdir) + +test.build('action_dependencies.gyp', 'obj/b.b.o', chdir=chdir) + +# The 'a' actions should be run (letting b.c compile), but the a static library +# should not be built. +test.built_file_must_not_exist('a', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_not_exist('b', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_exist('obj/b.b.o', chdir=chdir) + +test.build('action_dependencies.gyp', 'obj/c.c.o', chdir=chdir) + +# 'a' and 'b' should be built, so that the 'c' action succeeds, letting c.c +# compile +test.built_file_must_exist('a', type=test.STATIC_LIB, chdir=chdir) +test.built_file_must_exist('b', type=test.EXECUTABLE, chdir=chdir) +test.built_file_must_exist('obj/c.c.o', chdir=chdir) + + +test.pass_test() diff --git a/tools/gyp/test/ninja/action_dependencies/src/a.c b/tools/gyp/test/ninja/action_dependencies/src/a.c new file mode 100644 index 0000000000..4d7af9b26c --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/a.c @@ -0,0 +1,10 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "a.h" + +int funcA() { + return 42; +} diff --git a/tools/gyp/test/ninja/action_dependencies/src/a.h b/tools/gyp/test/ninja/action_dependencies/src/a.h new file mode 100644 index 0000000000..335db56739 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/a.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef A_H_ +#define A_H_ + +#include "a/generated.h" + +int funcA(); + +#endif // A_H_ diff --git a/tools/gyp/test/ninja/action_dependencies/src/action_dependencies.gyp b/tools/gyp/test/ninja/action_dependencies/src/action_dependencies.gyp new file mode 100644 index 0000000000..5baa7a7d47 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/action_dependencies.gyp @@ -0,0 +1,88 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'static_library', + 'sources': [ + 'a.c', + 'a.h', + ], + 'actions': [ + { + 'action_name': 'generate_headers', + 'inputs': [ + 'emit.py' + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/a/generated.h' + ], + 'action': [ + 'python', + 'emit.py', + '<(SHARED_INTERMEDIATE_DIR)/a/generated.h', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + }, + { + 'target_name': 'b', + 'type': 'executable', + 'sources': [ + 'b.c', + 'b.h', + ], + 'dependencies': [ + 'a', + ], + }, + { + 'target_name': 'c', + 'type': 'static_library', + 'sources': [ + 'c.c', + 'c.h', + ], + 'dependencies': [ + 'b', + ], + 'actions': [ + { + 'action_name': 'generate_headers', + 'inputs': [ + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/c/generated.h' + ], + 'action': [ + '<(PRODUCT_DIR)/b', + '<(SHARED_INTERMEDIATE_DIR)/c/generated.h', + ], + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }, + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + }, + ], +} diff --git a/tools/gyp/test/ninja/action_dependencies/src/b.c b/tools/gyp/test/ninja/action_dependencies/src/b.c new file mode 100644 index 0000000000..7d70d01ca0 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/b.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> + +#include "b.h" + +int main(int argc, char** argv) { + if (argc < 2) + return 1; + FILE* f = fopen(argv[1], "wt"); + fprintf(f, "#define VALUE %d\n", funcA()); + fclose(f); + return 0; +} diff --git a/tools/gyp/test/ninja/action_dependencies/src/b.h b/tools/gyp/test/ninja/action_dependencies/src/b.h new file mode 100644 index 0000000000..91362cd899 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/b.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef B_H_ +#define B_H_ + +#include "a.h" + +int funcB(); + +#endif // B_H_ diff --git a/tools/gyp/test/ninja/action_dependencies/src/c.c b/tools/gyp/test/ninja/action_dependencies/src/c.c new file mode 100644 index 0000000000..b412087ec8 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/c.c @@ -0,0 +1,10 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "c.h" + +int funcC() { + return VALUE; +} diff --git a/tools/gyp/test/ninja/action_dependencies/src/c.h b/tools/gyp/test/ninja/action_dependencies/src/c.h new file mode 100644 index 0000000000..c81a45bbe7 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/c.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef C_H_ +#define C_H_ + +#include "c/generated.h" + +int funcC(); + +#endif // C_H_ diff --git a/tools/gyp/test/ninja/action_dependencies/src/emit.py b/tools/gyp/test/ninja/action_dependencies/src/emit.py new file mode 100755 index 0000000000..2df74b79a1 --- /dev/null +++ b/tools/gyp/test/ninja/action_dependencies/src/emit.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +f = open(sys.argv[1], 'wb') +f.write('/* Hello World */\n') +f.close() diff --git a/tools/gyp/test/no-output/gyptest-no-output.py b/tools/gyp/test/no-output/gyptest-no-output.py new file mode 100755 index 0000000000..bf9a0b5aaa --- /dev/null +++ b/tools/gyp/test/no-output/gyptest-no-output.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verified things don't explode when there are targets without outputs. +""" + +import TestGyp + +# TODO(evan): in ninja when there are no targets, there is no 'all' +# target either. Disabling this test for now. +test = TestGyp.TestGyp(formats=['!ninja']) + +test.run_gyp('nooutput.gyp', chdir='src') +test.relocate('src', 'relocate/src') +test.build('nooutput.gyp', chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/no-output/src/nooutput.gyp b/tools/gyp/test/no-output/src/nooutput.gyp new file mode 100644 index 0000000000..c40124efc1 --- /dev/null +++ b/tools/gyp/test/no-output/src/nooutput.gyp @@ -0,0 +1,17 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'no_output', + 'type': 'none', + 'direct_dependent_settings': { + 'defines': [ + 'NADA', + ], + }, + }, + ], +} diff --git a/tools/gyp/test/product/gyptest-product.py b/tools/gyp/test/product/gyptest-product.py new file mode 100755 index 0000000000..e9790f30da --- /dev/null +++ b/tools/gyp/test/product/gyptest-product.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simplest-possible build of a "Hello, world!" program +using the default build target. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('product.gyp') +test.build('product.gyp') + +# executables +test.built_file_must_exist('alt1' + test._exe, test.EXECUTABLE, bare=True) +test.built_file_must_exist('hello2.stuff', test.EXECUTABLE, bare=True) +test.built_file_must_exist('yoalt3.stuff', test.EXECUTABLE, bare=True) + +# shared libraries +test.built_file_must_exist(test.dll_ + 'alt4' + test._dll, + test.SHARED_LIB, bare=True) +test.built_file_must_exist(test.dll_ + 'hello5.stuff', + test.SHARED_LIB, bare=True) +test.built_file_must_exist('yoalt6.stuff', test.SHARED_LIB, bare=True) + +# static libraries +test.built_file_must_exist(test.lib_ + 'alt7' + test._lib, + test.STATIC_LIB, bare=True) +test.built_file_must_exist(test.lib_ + 'hello8.stuff', + test.STATIC_LIB, bare=True) +test.built_file_must_exist('yoalt9.stuff', test.STATIC_LIB, bare=True) + +# alternate product_dir +test.built_file_must_exist('bob/yoalt10.stuff', test.EXECUTABLE, bare=True) +test.built_file_must_exist('bob/yoalt11.stuff', test.EXECUTABLE, bare=True) +test.built_file_must_exist('bob/yoalt12.stuff', test.EXECUTABLE, bare=True) + +test.pass_test() diff --git a/tools/gyp/test/product/hello.c b/tools/gyp/test/product/hello.c new file mode 100644 index 0000000000..94798f3e75 --- /dev/null +++ b/tools/gyp/test/product/hello.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int func1(void) { + return 42; +} + +int main(int argc, char *argv[]) { + printf("Hello, world!\n"); + printf("%d\n", func1()); + return 0; +} diff --git a/tools/gyp/test/product/product.gyp b/tools/gyp/test/product/product.gyp new file mode 100644 index 0000000000..c25eaaacb5 --- /dev/null +++ b/tools/gyp/test/product/product.gyp @@ -0,0 +1,128 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'hello1', + 'product_name': 'alt1', + 'type': 'executable', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello2', + 'product_extension': 'stuff', + 'type': 'executable', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello3', + 'product_name': 'alt3', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'type': 'executable', + 'sources': [ + 'hello.c', + ], + }, + + { + 'target_name': 'hello4', + 'product_name': 'alt4', + 'type': 'shared_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello5', + 'product_extension': 'stuff', + 'type': 'shared_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello6', + 'product_name': 'alt6', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'type': 'shared_library', + 'sources': [ + 'hello.c', + ], + }, + + { + 'target_name': 'hello7', + 'product_name': 'alt7', + 'type': 'static_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello8', + 'product_extension': 'stuff', + 'type': 'static_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello9', + 'product_name': 'alt9', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'type': 'static_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello10', + 'product_name': 'alt10', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'product_dir': '<(PRODUCT_DIR)/bob', + 'type': 'executable', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello11', + 'product_name': 'alt11', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'product_dir': '<(PRODUCT_DIR)/bob', + 'type': 'shared_library', + 'sources': [ + 'hello.c', + ], + }, + { + 'target_name': 'hello12', + 'product_name': 'alt12', + 'product_extension': 'stuff', + 'product_prefix': 'yo', + 'product_dir': '<(PRODUCT_DIR)/bob', + 'type': 'static_library', + 'sources': [ + 'hello.c', + ], + }, + ], + 'conditions': [ + ['OS=="linux"', { + 'target_defaults': { + 'cflags': ['-fPIC'], + }, + }], + ], +} diff --git a/tools/gyp/test/relative/foo/a/a.cc b/tools/gyp/test/relative/foo/a/a.cc new file mode 100644 index 0000000000..7d1c953448 --- /dev/null +++ b/tools/gyp/test/relative/foo/a/a.cc @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +int main() { + return 0; +} diff --git a/tools/gyp/test/relative/foo/a/a.gyp b/tools/gyp/test/relative/foo/a/a.gyp new file mode 100644 index 0000000000..66316ac681 --- /dev/null +++ b/tools/gyp/test/relative/foo/a/a.gyp @@ -0,0 +1,13 @@ +{ + 'targets': [ + { + 'target_name': 'a', + 'type': 'executable', + 'sources': ['a.cc'], + 'dependencies': [ + '../../foo/b/b.gyp:b', + 'c/c.gyp:c', + ], + }, + ], +} diff --git a/tools/gyp/test/relative/foo/a/c/c.cc b/tools/gyp/test/relative/foo/a/c/c.cc new file mode 100644 index 0000000000..9d22471684 --- /dev/null +++ b/tools/gyp/test/relative/foo/a/c/c.cc @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +int func() { + return 0; +} diff --git a/tools/gyp/test/relative/foo/a/c/c.gyp b/tools/gyp/test/relative/foo/a/c/c.gyp new file mode 100644 index 0000000000..c1f087db99 --- /dev/null +++ b/tools/gyp/test/relative/foo/a/c/c.gyp @@ -0,0 +1,12 @@ +{ + 'targets': [ + { + 'target_name': 'c', + 'type': 'static_library', + 'sources': ['c.cc'], + 'dependencies': [ + '../../b/b.gyp:b', + ], + }, + ], +} diff --git a/tools/gyp/test/relative/foo/b/b.cc b/tools/gyp/test/relative/foo/b/b.cc new file mode 100644 index 0000000000..011d59cebb --- /dev/null +++ b/tools/gyp/test/relative/foo/b/b.cc @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +int func2() { + return 0; +} diff --git a/tools/gyp/test/relative/foo/b/b.gyp b/tools/gyp/test/relative/foo/b/b.gyp new file mode 100644 index 0000000000..0ebe4533d3 --- /dev/null +++ b/tools/gyp/test/relative/foo/b/b.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'b', + 'type': 'static_library', + 'sources': ['b.cc'], + }, + ], +} diff --git a/tools/gyp/test/relative/gyptest-default.py b/tools/gyp/test/relative/gyptest-default.py new file mode 100755 index 0000000000..2d657aa675 --- /dev/null +++ b/tools/gyp/test/relative/gyptest-default.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simplest-possible build of a "Hello, world!" program +using the default build target. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_default', formats=['msvs']) + +# Run from down in foo. +test.run_gyp('a.gyp', chdir='foo/a') +sln = test.workpath('foo/a/a.sln') +sln_data = open(sln, 'rb').read() +vcproj = sln_data.count('b.vcproj') +vcxproj = sln_data.count('b.vcxproj') +if (vcproj, vcxproj) not in [(1, 0), (0, 1)]: + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/rules-dirname/gyptest-dirname.py b/tools/gyp/test/rules-dirname/gyptest-dirname.py new file mode 100755 index 0000000000..6e684a4c42 --- /dev/null +++ b/tools/gyp/test/rules-dirname/gyptest-dirname.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple rules when using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['make', 'ninja', 'xcode']) + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('actions.gyp', chdir='relocate/src') + +expect = """\ +hi c +hello baz +""" +if test.format == 'xcode': + chdir = 'relocate/src/subdir' +else: + chdir = 'relocate/src' +test.run_built_executable('gencc_int_output', chdir=chdir, stdout=expect) + +if test.format == 'msvs': + test.must_exist('relocate/src/subdir/foo/bar/baz.printed') + test.must_exist('relocate/src/subdir/a/b/c.printed') +else: + test.must_match('relocate/src/subdir/foo/bar/baz.printed', 'foo/bar') + test.must_match('relocate/src/subdir/a/b/c.printed', 'a/b') + +test.pass_test() diff --git a/tools/gyp/test/rules-dirname/src/actions.gyp b/tools/gyp/test/rules-dirname/src/actions.gyp new file mode 100644 index 0000000000..c5693c6c9e --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/actions.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_all_actions', + 'type': 'none', + 'dependencies': [ + 'subdir/input-rule-dirname.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/rules-dirname/src/copy-file.py b/tools/gyp/test/rules-dirname/src/copy-file.py new file mode 100755 index 0000000000..9774ccc960 --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/copy-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +import sys + +contents = open(sys.argv[1], 'r').read() +open(sys.argv[2], 'wb').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/rules-dirname/src/subdir/a/b/c.gencc b/tools/gyp/test/rules-dirname/src/subdir/a/b/c.gencc new file mode 100644 index 0000000000..a4c8eea95f --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/a/b/c.gencc @@ -0,0 +1,11 @@ +// -*- mode: c++ -*- +#include <iostream> + +using std::cout; +using std::endl; + +namespace gen { + void c() { + cout << "hi c" << endl; + } +} diff --git a/tools/gyp/test/rules-dirname/src/subdir/a/b/c.printvars b/tools/gyp/test/rules-dirname/src/subdir/a/b/c.printvars new file mode 100644 index 0000000000..cc4561dc41 --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/a/b/c.printvars @@ -0,0 +1 @@ +# Empty file for testing build rules diff --git a/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.gencc b/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.gencc new file mode 100644 index 0000000000..ff01c2ee50 --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.gencc @@ -0,0 +1,11 @@ +// -*- mode: c++ -*- +#include <iostream> + +using std::cout; +using std::endl; + +namespace gen { + void baz() { + cout << "hello baz" << endl; + } +} diff --git a/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.printvars b/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.printvars new file mode 100644 index 0000000000..cc4561dc41 --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/foo/bar/baz.printvars @@ -0,0 +1 @@ +# Empty file for testing build rules diff --git a/tools/gyp/test/rules-dirname/src/subdir/input-rule-dirname.gyp b/tools/gyp/test/rules-dirname/src/subdir/input-rule-dirname.gyp new file mode 100644 index 0000000000..96e32efa7a --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/input-rule-dirname.gyp @@ -0,0 +1,92 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'print_rule_input_path', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'foo/bar/baz.printvars', + 'a/b/c.printvars', + ], + 'rules': [ + { + 'rule_name': 'printvars', + 'extension': 'printvars', + 'inputs': [ + 'printvars.py', + ], + 'outputs': [ + '<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).printed', + ], + 'action': [ + 'python', '<@(_inputs)', '<(RULE_INPUT_DIRNAME)', '<@(_outputs)', + ], + }, + ], + }, + { + 'target_name': 'gencc_int_output', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'msvs_cygwin_dirs': ['../../../../../../<(DEPTH)/third_party/cygwin'], + 'sources': [ + 'foo/bar/baz.gencc', + 'a/b/c.gencc', + 'main.cc', + ], + 'conditions': [ + ['OS=="win"', { + 'dependencies': [ + 'cygwin', + ], + }], + ], + 'rules': [ + { + 'rule_name': 'gencc', + 'extension': 'gencc', + 'msvs_external_rule': 1, + 'inputs': [ + '<(DEPTH)/copy-file.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).cc', + ], + 'action': [ + 'python', '<@(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'cygwin', + 'type': 'none', + 'actions': [ + { + 'action_name': 'setup_mount', + 'msvs_cygwin_shell': 0, + 'inputs': [ + '../../../../../../<(DEPTH)/third_party/cygwin/setup_mount.bat', + ], + # Visual Studio requires an output file, or else the + # custom build step won't run. + 'outputs': [ + '<(INTERMEDIATE_DIR)/_always_run_setup_mount.marker', + ], + 'action': ['', '<@(_inputs)'], + }, + ], + }, + ], + }], + ], +} diff --git a/tools/gyp/test/rules-dirname/src/subdir/main.cc b/tools/gyp/test/rules-dirname/src/subdir/main.cc new file mode 100644 index 0000000000..bacc568ad2 --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/main.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +namespace gen { + extern void c(); + extern void baz(); +} + +int main() { + gen::c(); + gen::baz(); +} diff --git a/tools/gyp/test/rules-dirname/src/subdir/printvars.py b/tools/gyp/test/rules-dirname/src/subdir/printvars.py new file mode 100755 index 0000000000..ef3d92e8cf --- /dev/null +++ b/tools/gyp/test/rules-dirname/src/subdir/printvars.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Prints interesting vars +""" + +import sys; + +out = open(sys.argv[2], 'w') +out.write(sys.argv[1]); diff --git a/tools/gyp/test/rules-rebuild/gyptest-all.py b/tools/gyp/test/rules-rebuild/gyptest-all.py new file mode 100755 index 0000000000..aaaa2a6e6f --- /dev/null +++ b/tools/gyp/test/rules-rebuild/gyptest-all.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a rule that generates multiple outputs rebuilds +correctly when the inputs change. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_all') + +test.run_gyp('same_target.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + + +test.build('same_target.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in! +Hello from prog2.in! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.sleep() +contents = test.read(['relocate', 'src', 'prog1.in']) +contents = contents.replace('!', ' AGAIN!') +test.write(['relocate', 'src', 'prog1.in'], contents) + +test.build('same_target.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in AGAIN! +Hello from prog2.in! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.sleep() +contents = test.read(['relocate', 'src', 'prog2.in']) +contents = contents.replace('!', ' AGAIN!') +test.write(['relocate', 'src', 'prog2.in'], contents) + +test.build('same_target.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in AGAIN! +Hello from prog2.in AGAIN! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.pass_test() diff --git a/tools/gyp/test/rules-rebuild/gyptest-default.py b/tools/gyp/test/rules-rebuild/gyptest-default.py new file mode 100755 index 0000000000..89694ef08b --- /dev/null +++ b/tools/gyp/test/rules-rebuild/gyptest-default.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a rule that generates multiple outputs rebuilds +correctly when the inputs change. +""" + +import TestGyp + +test = TestGyp.TestGyp(workdir='workarea_default') + +test.run_gyp('same_target.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + + +test.build('same_target.gyp', chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in! +Hello from prog2.in! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.sleep() +contents = test.read(['relocate', 'src', 'prog1.in']) +contents = contents.replace('!', ' AGAIN!') +test.write(['relocate', 'src', 'prog1.in'], contents) + +test.build('same_target.gyp', chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in AGAIN! +Hello from prog2.in! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.sleep() +contents = test.read(['relocate', 'src', 'prog2.in']) +contents = contents.replace('!', ' AGAIN!') +test.write(['relocate', 'src', 'prog2.in'], contents) + +test.build('same_target.gyp', chdir='relocate/src') + +expect = """\ +Hello from main.c +Hello from prog1.in AGAIN! +Hello from prog2.in AGAIN! +""" + +test.run_built_executable('program', chdir='relocate/src', stdout=expect) + +test.up_to_date('same_target.gyp', 'program', chdir='relocate/src') + + +test.pass_test() diff --git a/tools/gyp/test/rules-rebuild/src/main.c b/tools/gyp/test/rules-rebuild/src/main.c new file mode 100644 index 0000000000..bdc5ec875e --- /dev/null +++ b/tools/gyp/test/rules-rebuild/src/main.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +extern void prog1(void); +extern void prog2(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from main.c\n"); + prog1(); + prog2(); + return 0; +} diff --git a/tools/gyp/test/rules-rebuild/src/make-sources.py b/tools/gyp/test/rules-rebuild/src/make-sources.py new file mode 100755 index 0000000000..7ec022780c --- /dev/null +++ b/tools/gyp/test/rules-rebuild/src/make-sources.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +assert len(sys.argv) == 4, sys.argv + +(in_file, c_file, h_file) = sys.argv[1:] + +def write_file(filename, contents): + open(filename, 'wb').write(contents) + +write_file(c_file, open(in_file, 'rb').read()) + +write_file(h_file, '#define NAME "%s"\n' % in_file) + +sys.exit(0) diff --git a/tools/gyp/test/rules-rebuild/src/prog1.in b/tools/gyp/test/rules-rebuild/src/prog1.in new file mode 100644 index 0000000000..191b00ef1e --- /dev/null +++ b/tools/gyp/test/rules-rebuild/src/prog1.in @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "prog1.h" + +void prog1(void) +{ + printf("Hello from %s!\n", NAME); +} diff --git a/tools/gyp/test/rules-rebuild/src/prog2.in b/tools/gyp/test/rules-rebuild/src/prog2.in new file mode 100644 index 0000000000..7bfac5104c --- /dev/null +++ b/tools/gyp/test/rules-rebuild/src/prog2.in @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "prog2.h" + +void prog2(void) +{ + printf("Hello from %s!\n", NAME); +} diff --git a/tools/gyp/test/rules-rebuild/src/same_target.gyp b/tools/gyp/test/rules-rebuild/src/same_target.gyp new file mode 100644 index 0000000000..22ba56056d --- /dev/null +++ b/tools/gyp/test/rules-rebuild/src/same_target.gyp @@ -0,0 +1,31 @@ +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'main.c', + 'prog1.in', + 'prog2.in', + ], + 'rules': [ + { + 'rule_name': 'make_sources', + 'extension': 'in', + 'inputs': [ + 'make-sources.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).c', + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).h', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_NAME)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules-variables/gyptest-rules-variables.py b/tools/gyp/test/rules-variables/gyptest-rules-variables.py new file mode 100755 index 0000000000..06ee5ca838 --- /dev/null +++ b/tools/gyp/test/rules-variables/gyptest-rules-variables.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies rules related variables are expanded. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['ninja']) + +test.relocate('src', 'relocate/src') + +test.run_gyp('variables.gyp', chdir='relocate/src') + +test.build('variables.gyp', chdir='relocate/src') + +test.run_built_executable('all_rule_variables', + chdir='relocate/src', + stdout="input_root\ninput_dirname\ninput_path\n" + + "input_ext\ninput_name\n") + +test.pass_test() diff --git a/tools/gyp/test/rules-variables/src/input_ext.c b/tools/gyp/test/rules-variables/src/input_ext.c new file mode 100644 index 0000000000..f41e73ef8a --- /dev/null +++ b/tools/gyp/test/rules-variables/src/input_ext.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +void input_ext() { + printf("input_ext\n"); +} diff --git a/tools/gyp/test/rules-variables/src/input_name/test.c b/tools/gyp/test/rules-variables/src/input_name/test.c new file mode 100644 index 0000000000..e28b74d115 --- /dev/null +++ b/tools/gyp/test/rules-variables/src/input_name/test.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +void input_name() { + printf("input_name\n"); +} diff --git a/tools/gyp/test/rules-variables/src/input_path/subdir/test.c b/tools/gyp/test/rules-variables/src/input_path/subdir/test.c new file mode 100644 index 0000000000..403dbbda4c --- /dev/null +++ b/tools/gyp/test/rules-variables/src/input_path/subdir/test.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +void input_path() { + printf("input_path\n"); +} diff --git a/tools/gyp/test/rules-variables/src/subdir/input_dirname.c b/tools/gyp/test/rules-variables/src/subdir/input_dirname.c new file mode 100644 index 0000000000..40cecd87d9 --- /dev/null +++ b/tools/gyp/test/rules-variables/src/subdir/input_dirname.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +void input_dirname() { + printf("input_dirname\n"); +} diff --git a/tools/gyp/test/rules-variables/src/subdir/test.c b/tools/gyp/test/rules-variables/src/subdir/test.c new file mode 100644 index 0000000000..6c0280b8ad --- /dev/null +++ b/tools/gyp/test/rules-variables/src/subdir/test.c @@ -0,0 +1,18 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +extern void input_root(); +extern void input_dirname(); +extern void input_path(); +extern void input_ext(); +extern void input_name(); + +int main() { + input_root(); + input_dirname(); + input_path(); + input_ext(); + input_name(); + return 0; +} diff --git a/tools/gyp/test/rules-variables/src/test.input_root.c b/tools/gyp/test/rules-variables/src/test.input_root.c new file mode 100644 index 0000000000..33a7740a5c --- /dev/null +++ b/tools/gyp/test/rules-variables/src/test.input_root.c @@ -0,0 +1,9 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +void input_root() { + printf("input_root\n"); +} diff --git a/tools/gyp/test/rules-variables/src/variables.gyp b/tools/gyp/test/rules-variables/src/variables.gyp new file mode 100644 index 0000000000..e40f3a85f5 --- /dev/null +++ b/tools/gyp/test/rules-variables/src/variables.gyp @@ -0,0 +1,30 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'all_rule_variables', + 'type': 'executable', + 'sources': [ + 'subdir/test.c', + ], + 'rules': [ + { + 'rule_name': 'rule_variable', + 'extension': 'c', + 'outputs': [ + '<(RULE_INPUT_ROOT).input_root.c', + '<(RULE_INPUT_DIRNAME)/input_dirname.c', + 'input_path/<(RULE_INPUT_PATH)', + 'input_ext<(RULE_INPUT_EXT)', + 'input_name/<(RULE_INPUT_NAME)', + ], + 'action': [], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/gyptest-all.py b/tools/gyp/test/rules/gyptest-all.py new file mode 100755 index 0000000000..63c3a10ba5 --- /dev/null +++ b/tools/gyp/test/rules/gyptest-all.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple rules when using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('actions.gyp', test.ALL, chdir='relocate/src') + +expect = """\ +Hello from program.c +Hello from function1.in +Hello from function2.in +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir1' +else: + chdir = 'relocate/src' +test.run_built_executable('program', chdir=chdir, stdout=expect) + +expect = """\ +Hello from program.c +Hello from function3.in +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir3' +else: + chdir = 'relocate/src' +test.run_built_executable('program2', chdir=chdir, stdout=expect) + +test.must_match('relocate/src/subdir2/file1.out', 'Hello from file1.in\n') +test.must_match('relocate/src/subdir2/file2.out', 'Hello from file2.in\n') + +test.must_match('relocate/src/subdir2/file1.out2', 'Hello from file1.in\n') +test.must_match('relocate/src/subdir2/file2.out2', 'Hello from file2.in\n') + +test.must_match('relocate/src/external/file1.external_rules.out', + 'Hello from file1.in\n') +test.must_match('relocate/src/external/file2.external_rules.out', + 'Hello from file2.in\n') + +expect = """\ +Hello from program.c +Got 41. +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir4' +else: + chdir = 'relocate/src' +test.run_built_executable('program4', chdir=chdir, stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/rules/gyptest-default.py b/tools/gyp/test/rules/gyptest-default.py new file mode 100755 index 0000000000..117c53db03 --- /dev/null +++ b/tools/gyp/test/rules/gyptest-default.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies simple rules when using an explicit build target of 'all'. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('actions.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('actions.gyp', chdir='relocate/src') + +expect = """\ +Hello from program.c +Hello from function1.in +Hello from function2.in +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir1' +else: + chdir = 'relocate/src' +test.run_built_executable('program', chdir=chdir, stdout=expect) + +expect = """\ +Hello from program.c +Hello from function3.in +""" + +if test.format == 'xcode': + chdir = 'relocate/src/subdir3' +else: + chdir = 'relocate/src' +test.run_built_executable('program2', chdir=chdir, stdout=expect) + +test.must_match('relocate/src/subdir2/file1.out', 'Hello from file1.in\n') +test.must_match('relocate/src/subdir2/file2.out', 'Hello from file2.in\n') + +test.must_match('relocate/src/subdir2/file1.out2', 'Hello from file1.in\n') +test.must_match('relocate/src/subdir2/file2.out2', 'Hello from file2.in\n') + +test.must_match('relocate/src/external/file1.external_rules.out', + 'Hello from file1.in\n') +test.must_match('relocate/src/external/file2.external_rules.out', + 'Hello from file2.in\n') + +test.pass_test() diff --git a/tools/gyp/test/rules/gyptest-input-root.py b/tools/gyp/test/rules/gyptest-input-root.py new file mode 100755 index 0000000000..92bade6d48 --- /dev/null +++ b/tools/gyp/test/rules/gyptest-input-root.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that RULE_INPUT_ROOT isn't turned into a path in rule actions +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('input-root.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('input-root.gyp', target='test', chdir='relocate/src') + +expect = """\ +Hello somefile +""" + +test.run_built_executable('test', chdir='relocate/src', stdout=expect) +test.pass_test() diff --git a/tools/gyp/test/rules/src/actions.gyp b/tools/gyp/test/rules/src/actions.gyp new file mode 100644 index 0000000000..23e00cec61 --- /dev/null +++ b/tools/gyp/test/rules/src/actions.gyp @@ -0,0 +1,21 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'pull_in_all_actions', + 'type': 'none', + 'dependencies': [ + 'subdir1/executable.gyp:*', + 'subdir2/never_used.gyp:*', + 'subdir2/no_inputs.gyp:*', + 'subdir2/none.gyp:*', + 'subdir3/executable2.gyp:*', + 'subdir4/build-asm.gyp:*', + 'external/external.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/copy-file.py b/tools/gyp/test/rules/src/copy-file.py new file mode 100755 index 0000000000..5a5feae1f2 --- /dev/null +++ b/tools/gyp/test/rules/src/copy-file.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +import sys + +contents = open(sys.argv[1], 'r').read() +open(sys.argv[2], 'wb').write(contents) + +sys.exit(0) diff --git a/tools/gyp/test/rules/src/external/external.gyp b/tools/gyp/test/rules/src/external/external.gyp new file mode 100644 index 0000000000..004ec63599 --- /dev/null +++ b/tools/gyp/test/rules/src/external/external.gyp @@ -0,0 +1,66 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Test that the case where there are no inputs (other than the +# file the rule applies to). +{ + 'target_defaults': { + 'msvs_cygwin_dirs': ['../../../../../../<(DEPTH)/third_party/cygwin'], + }, + 'targets': [ + { + 'target_name': 'external_rules', + 'type': 'none', + 'sources': [ + 'file1.in', + 'file2.in', + ], + 'conditions': [ + ['OS=="win"', { + 'dependencies': [ + 'cygwin', + ], + }], + ], + 'rules': [ + { + 'rule_name': 'copy_file', + 'extension': 'in', + 'msvs_external_rule': 1, + 'outputs': [ + '<(RULE_INPUT_ROOT).external_rules.out', + ], + 'action': [ + 'python', '../copy-file.py', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + }, + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'cygwin', + 'type': 'none', + 'actions': [ + { + 'action_name': 'setup_mount', + 'msvs_cygwin_shell': 0, + 'inputs': [ + '../../../../../../<(DEPTH)/third_party/cygwin/setup_mount.bat', + ], + # Visual Studio requires an output file, or else the + # custom build step won't run. + 'outputs': [ + '<(INTERMEDIATE_DIR)/_always_run_setup_mount.marker', + ], + 'action': ['', '<@(_inputs)'], + }, + ], + }, + ], + }], + ], +} diff --git a/tools/gyp/test/rules/src/external/file1.in b/tools/gyp/test/rules/src/external/file1.in new file mode 100644 index 0000000000..86ac3ad389 --- /dev/null +++ b/tools/gyp/test/rules/src/external/file1.in @@ -0,0 +1 @@ +Hello from file1.in diff --git a/tools/gyp/test/rules/src/external/file2.in b/tools/gyp/test/rules/src/external/file2.in new file mode 100644 index 0000000000..bf83d8ecec --- /dev/null +++ b/tools/gyp/test/rules/src/external/file2.in @@ -0,0 +1 @@ +Hello from file2.in diff --git a/tools/gyp/test/rules/src/input-root.gyp b/tools/gyp/test/rules/src/input-root.gyp new file mode 100644 index 0000000000..b6600e767c --- /dev/null +++ b/tools/gyp/test/rules/src/input-root.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'test', + 'type': 'executable', + 'sources': [ 'somefile.ext', ], + 'rules': [{ + 'rule_name': 'rule', + 'extension': 'ext', + 'inputs': [ 'rule.py', ], + 'outputs': [ '<(RULE_INPUT_ROOT).cc', ], + 'action': [ 'python', 'rule.py', '<(RULE_INPUT_ROOT)', ], + 'message': 'Processing <(RULE_INPUT_PATH)', + 'process_outputs_as_sources': 1, + # Allows the test to run without hermetic cygwin on windows. + 'msvs_cygwin_shell': 0, + }], + }, + ], +} diff --git a/tools/gyp/test/rules/src/rule.py b/tools/gyp/test/rules/src/rule.py new file mode 100755 index 0000000000..8a1f36dedb --- /dev/null +++ b/tools/gyp/test/rules/src/rule.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +f = open(sys.argv[1] + ".cc", "w") +f.write("""\ +#include <stdio.h> + +int main() { + puts("Hello %s"); + return 0; +} +""" % sys.argv[1]) +f.close() diff --git a/tools/gyp/test/rules/src/somefile.ext b/tools/gyp/test/rules/src/somefile.ext new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/gyp/test/rules/src/somefile.ext diff --git a/tools/gyp/test/rules/src/subdir1/executable.gyp b/tools/gyp/test/rules/src/subdir1/executable.gyp new file mode 100644 index 0000000000..302857789d --- /dev/null +++ b/tools/gyp/test/rules/src/subdir1/executable.gyp @@ -0,0 +1,37 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'program.c', + 'function1.in', + 'function2.in', + ], + 'rules': [ + { + 'rule_name': 'copy_file', + 'extension': 'in', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + # TODO: fix SCons and Make to support generated files not + # in a variable-named path like <(INTERMEDIATE_DIR) + #'<(RULE_INPUT_ROOT).c', + '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).c', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir1/function1.in b/tools/gyp/test/rules/src/subdir1/function1.in new file mode 100644 index 0000000000..60ff28949b --- /dev/null +++ b/tools/gyp/test/rules/src/subdir1/function1.in @@ -0,0 +1,6 @@ +#include <stdio.h> + +void function1(void) +{ + printf("Hello from function1.in\n"); +} diff --git a/tools/gyp/test/rules/src/subdir1/function2.in b/tools/gyp/test/rules/src/subdir1/function2.in new file mode 100644 index 0000000000..0fcfc03fdb --- /dev/null +++ b/tools/gyp/test/rules/src/subdir1/function2.in @@ -0,0 +1,6 @@ +#include <stdio.h> + +void function2(void) +{ + printf("Hello from function2.in\n"); +} diff --git a/tools/gyp/test/rules/src/subdir1/program.c b/tools/gyp/test/rules/src/subdir1/program.c new file mode 100644 index 0000000000..258d7f99ef --- /dev/null +++ b/tools/gyp/test/rules/src/subdir1/program.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +extern void function1(void); +extern void function2(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from program.c\n"); + function1(); + function2(); + return 0; +} diff --git a/tools/gyp/test/rules/src/subdir2/file1.in b/tools/gyp/test/rules/src/subdir2/file1.in new file mode 100644 index 0000000000..86ac3ad389 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir2/file1.in @@ -0,0 +1 @@ +Hello from file1.in diff --git a/tools/gyp/test/rules/src/subdir2/file2.in b/tools/gyp/test/rules/src/subdir2/file2.in new file mode 100644 index 0000000000..bf83d8ecec --- /dev/null +++ b/tools/gyp/test/rules/src/subdir2/file2.in @@ -0,0 +1 @@ +Hello from file2.in diff --git a/tools/gyp/test/rules/src/subdir2/never_used.gyp b/tools/gyp/test/rules/src/subdir2/never_used.gyp new file mode 100644 index 0000000000..17f6f55371 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir2/never_used.gyp @@ -0,0 +1,31 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Test that the case where there is a rule that doesn't apply to anything. +{ + 'targets': [ + { + 'target_name': 'files_no_input2', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'file1.in', + 'file2.in', + ], + 'rules': [ + { + 'rule_name': 'copy_file3', + 'extension': 'in2', + 'outputs': [ + '<(RULE_INPUT_ROOT).out3', + ], + 'action': [ + 'python', '../copy-file.py', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir2/no_inputs.gyp b/tools/gyp/test/rules/src/subdir2/no_inputs.gyp new file mode 100644 index 0000000000..e61a1a3ff6 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir2/no_inputs.gyp @@ -0,0 +1,32 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Test that the case where there are no inputs (other than the +# file the rule applies to). +{ + 'targets': [ + { + 'target_name': 'files_no_input', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'file1.in', + 'file2.in', + ], + 'rules': [ + { + 'rule_name': 'copy_file2', + 'extension': 'in', + 'outputs': [ + '<(RULE_INPUT_ROOT).out2', + ], + 'action': [ + 'python', '../copy-file.py', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir2/none.gyp b/tools/gyp/test/rules/src/subdir2/none.gyp new file mode 100644 index 0000000000..38bcdabdf6 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir2/none.gyp @@ -0,0 +1,33 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'files', + 'type': 'none', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'file1.in', + 'file2.in', + ], + 'rules': [ + { + 'rule_name': 'copy_file', + 'extension': 'in', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + '<(RULE_INPUT_ROOT).out', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir3/executable2.gyp b/tools/gyp/test/rules/src/subdir3/executable2.gyp new file mode 100644 index 0000000000..a2a528fc7b --- /dev/null +++ b/tools/gyp/test/rules/src/subdir3/executable2.gyp @@ -0,0 +1,37 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This one tests that rules are properly written if extensions are different +# between the target's sources (program.c) and the generated files +# (function3.cc) + +{ + 'targets': [ + { + 'target_name': 'program2', + 'type': 'executable', + 'msvs_cygwin_shell': 0, + 'sources': [ + 'program.c', + 'function3.in', + ], + 'rules': [ + { + 'rule_name': 'copy_file', + 'extension': 'in', + 'inputs': [ + '../copy-file.py', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).cc', + ], + 'action': [ + 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir3/function3.in b/tools/gyp/test/rules/src/subdir3/function3.in new file mode 100644 index 0000000000..99f46ab05e --- /dev/null +++ b/tools/gyp/test/rules/src/subdir3/function3.in @@ -0,0 +1,6 @@ +#include <stdio.h> + +extern "C" void function3(void) +{ + printf("Hello from function3.in\n"); +} diff --git a/tools/gyp/test/rules/src/subdir3/program.c b/tools/gyp/test/rules/src/subdir3/program.c new file mode 100644 index 0000000000..94f6c50912 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir3/program.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern void function3(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from program.c\n"); + function3(); + return 0; +} diff --git a/tools/gyp/test/rules/src/subdir4/asm-function.asm b/tools/gyp/test/rules/src/subdir4/asm-function.asm new file mode 100644 index 0000000000..ed47cade95 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir4/asm-function.asm @@ -0,0 +1,10 @@ +#if PLATFORM_WINDOWS || PLATFORM_MAC +# define IDENTIFIER(n) _##n +#else /* Linux */ +# define IDENTIFIER(n) n +#endif + +.globl IDENTIFIER(asm_function) +IDENTIFIER(asm_function): + movl $41, %eax + ret diff --git a/tools/gyp/test/rules/src/subdir4/build-asm.gyp b/tools/gyp/test/rules/src/subdir4/build-asm.gyp new file mode 100644 index 0000000000..be4a612d18 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir4/build-asm.gyp @@ -0,0 +1,49 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This one tests that assembly files ended as .s and .S are compiled. + +{ + 'target_defaults': { + 'conditions': [ + ['OS=="win"', { + 'defines': ['PLATFORM_WIN'], + }], + ['OS=="mac"', { + 'defines': ['PLATFORM_MAC'], + }], + ['OS=="linux"', { + 'defines': ['PLATFORM_LINUX'], + }], + ], + }, + 'targets': [ + { + 'target_name': 'program4', + 'type': 'executable', + 'sources': [ + 'asm-function.asm', + 'program.c', + ], + 'conditions': [ + ['OS=="linux" or OS=="mac"', { + 'rules': [ + { + 'rule_name': 'convert_asm', + 'extension': 'asm', + 'inputs': [], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).S', + ], + 'action': [ + 'bash', '-c', 'mv <(RULE_INPUT_PATH) <@(_outputs)', + ], + 'process_outputs_as_sources': 1, + }, + ], + }], + ], + }, + ], +} diff --git a/tools/gyp/test/rules/src/subdir4/program.c b/tools/gyp/test/rules/src/subdir4/program.c new file mode 100644 index 0000000000..4247590624 --- /dev/null +++ b/tools/gyp/test/rules/src/subdir4/program.c @@ -0,0 +1,19 @@ +#include <stdio.h> + +// Use the assembly function in linux and mac where it is built. +#if PLATFORM_LINUX || PLATFORM_MAC +extern int asm_function(void); +#else +int asm_function() { + return 41; +} +#endif + +int main(int argc, char *argv[]) +{ + fprintf(stdout, "Hello from program.c\n"); + fflush(stdout); + fprintf(stdout, "Got %d.\n", asm_function()); + fflush(stdout); + return 0; +} diff --git a/tools/gyp/test/same-gyp-name/gyptest-all.py b/tools/gyp/test/same-gyp-name/gyptest-all.py new file mode 100755 index 0000000000..76456885ed --- /dev/null +++ b/tools/gyp/test/same-gyp-name/gyptest-all.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Build a .gyp that depends on 2 gyp files with the same name. +""" + +import TestGyp + +# This causes a problem on XCode (duplicate ID). +# See http://code.google.com/p/gyp/issues/detail?id=114 +test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make']) + +test.run_gyp('all.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('all.gyp', test.ALL, chdir='relocate/src') + +expect1 = """\ +Hello from main1.cc +""" + +expect2 = """\ +Hello from main2.cc +""" + +test.run_built_executable('program1', chdir='relocate/src', stdout=expect1) +test.run_built_executable('program2', chdir='relocate/src', stdout=expect2) + +test.pass_test() diff --git a/tools/gyp/test/same-gyp-name/gyptest-default.py b/tools/gyp/test/same-gyp-name/gyptest-default.py new file mode 100755 index 0000000000..c1031f86bd --- /dev/null +++ b/tools/gyp/test/same-gyp-name/gyptest-default.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Build a .gyp that depends on 2 gyp files with the same name. +""" + +import TestGyp + +# This causes a problem on XCode (duplicate ID). +# See http://code.google.com/p/gyp/issues/detail?id=114 +test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make']) + +test.run_gyp('all.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('all.gyp', chdir='relocate/src') + +expect1 = """\ +Hello from main1.cc +""" + +expect2 = """\ +Hello from main2.cc +""" + +test.run_built_executable('program1', chdir='relocate/src', stdout=expect1) +test.run_built_executable('program2', chdir='relocate/src', stdout=expect2) + +test.pass_test() diff --git a/tools/gyp/test/same-gyp-name/src/all.gyp b/tools/gyp/test/same-gyp-name/src/all.gyp new file mode 100644 index 0000000000..229f02ea84 --- /dev/null +++ b/tools/gyp/test/same-gyp-name/src/all.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'all_exes', + 'type': 'none', + 'dependencies': [ + 'subdir1/executable.gyp:*', + 'subdir2/executable.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/same-gyp-name/src/subdir1/executable.gyp b/tools/gyp/test/same-gyp-name/src/subdir1/executable.gyp new file mode 100644 index 0000000000..82483b4c69 --- /dev/null +++ b/tools/gyp/test/same-gyp-name/src/subdir1/executable.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program1', + 'type': 'executable', + 'sources': [ + 'main1.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/same-gyp-name/src/subdir1/main1.cc b/tools/gyp/test/same-gyp-name/src/subdir1/main1.cc new file mode 100644 index 0000000000..3645558324 --- /dev/null +++ b/tools/gyp/test/same-gyp-name/src/subdir1/main1.cc @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main() { + printf("Hello from main1.cc\n"); + return 0; +} diff --git a/tools/gyp/test/same-gyp-name/src/subdir2/executable.gyp b/tools/gyp/test/same-gyp-name/src/subdir2/executable.gyp new file mode 100644 index 0000000000..e3537013eb --- /dev/null +++ b/tools/gyp/test/same-gyp-name/src/subdir2/executable.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program2', + 'type': 'executable', + 'sources': [ + 'main2.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/same-gyp-name/src/subdir2/main2.cc b/tools/gyp/test/same-gyp-name/src/subdir2/main2.cc new file mode 100644 index 0000000000..0c724dee35 --- /dev/null +++ b/tools/gyp/test/same-gyp-name/src/subdir2/main2.cc @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main() { + printf("Hello from main2.cc\n"); + return 0; +} diff --git a/tools/gyp/test/same-name/gyptest-all.py b/tools/gyp/test/same-name/gyptest-all.py new file mode 100755 index 0000000000..4c215027c2 --- /dev/null +++ b/tools/gyp/test/same-name/gyptest-all.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Build a .gyp with two targets that share a common .c source file. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('all.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('all.gyp', test.ALL, chdir='relocate/src') + +expect1 = """\ +Hello from prog1.c +Hello prog1 from func.c +""" + +expect2 = """\ +Hello from prog2.c +Hello prog2 from func.c +""" + +test.run_built_executable('prog1', chdir='relocate/src', stdout=expect1) +test.run_built_executable('prog2', chdir='relocate/src', stdout=expect2) + +test.pass_test() diff --git a/tools/gyp/test/same-name/gyptest-default.py b/tools/gyp/test/same-name/gyptest-default.py new file mode 100755 index 0000000000..98757c2697 --- /dev/null +++ b/tools/gyp/test/same-name/gyptest-default.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Build a .gyp with two targets that share a common .c source file. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('all.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('all.gyp', chdir='relocate/src') + +expect1 = """\ +Hello from prog1.c +Hello prog1 from func.c +""" + +expect2 = """\ +Hello from prog2.c +Hello prog2 from func.c +""" + +test.run_built_executable('prog1', chdir='relocate/src', stdout=expect1) +test.run_built_executable('prog2', chdir='relocate/src', stdout=expect2) + +test.pass_test() diff --git a/tools/gyp/test/same-name/src/all.gyp b/tools/gyp/test/same-name/src/all.gyp new file mode 100644 index 0000000000..44e1049821 --- /dev/null +++ b/tools/gyp/test/same-name/src/all.gyp @@ -0,0 +1,38 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'defines': [ + 'PROG="prog1"', + ], + 'sources': [ + 'prog1.c', + 'func.c', + # Uncomment to test same-named files in different directories, + # which Visual Studio doesn't support. + #'subdir1/func.c', + #'subdir2/func.c', + ], + }, + { + 'target_name': 'prog2', + 'type': 'executable', + 'defines': [ + 'PROG="prog2"', + ], + 'sources': [ + 'prog2.c', + 'func.c', + # Uncomment to test same-named files in different directories, + # which Visual Studio doesn't support. + #'subdir1/func.c', + #'subdir2/func.c', + ], + }, + ], +} diff --git a/tools/gyp/test/same-name/src/func.c b/tools/gyp/test/same-name/src/func.c new file mode 100644 index 0000000000..e069c692a6 --- /dev/null +++ b/tools/gyp/test/same-name/src/func.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void func(void) +{ + printf("Hello %s from func.c\n", PROG); +} diff --git a/tools/gyp/test/same-name/src/prog1.c b/tools/gyp/test/same-name/src/prog1.c new file mode 100644 index 0000000000..c8940fedc3 --- /dev/null +++ b/tools/gyp/test/same-name/src/prog1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void func(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + func(); + /* + * Uncomment to test same-named files in different directories, + * which Visual Studio doesn't support. + subdir1_func(); + subdir2_func(); + */ + return 0; +} diff --git a/tools/gyp/test/same-name/src/prog2.c b/tools/gyp/test/same-name/src/prog2.c new file mode 100644 index 0000000000..e6605c2bd9 --- /dev/null +++ b/tools/gyp/test/same-name/src/prog2.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void func(void); + +int main(int argc, char *argv[]) +{ + printf("Hello from prog2.c\n"); + func(); + /* + * Uncomment to test same-named files in different directories, + * which Visual Studio doesn't support. + subdir1_func(); + subdir2_func(); + */ + return 0; +} diff --git a/tools/gyp/test/same-name/src/subdir1/func.c b/tools/gyp/test/same-name/src/subdir1/func.c new file mode 100644 index 0000000000..b73450d105 --- /dev/null +++ b/tools/gyp/test/same-name/src/subdir1/func.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void subdir1_func(void) +{ + printf("Hello %s from subdir1/func.c\n", PROG); +} diff --git a/tools/gyp/test/same-name/src/subdir2/func.c b/tools/gyp/test/same-name/src/subdir2/func.c new file mode 100644 index 0000000000..0248b5720e --- /dev/null +++ b/tools/gyp/test/same-name/src/subdir2/func.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void subdir2_func(void) +{ + printf("Hello %s from subdir2/func.c\n", PROG); +} diff --git a/tools/gyp/test/same-target-name/gyptest-same-target-name.py b/tools/gyp/test/same-target-name/gyptest-same-target-name.py new file mode 100755 index 0000000000..bfe5540f31 --- /dev/null +++ b/tools/gyp/test/same-target-name/gyptest-same-target-name.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Check that duplicate targets in a directory gives an error. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +# Require that gyp files with duplicate targets spit out an error. +test.run_gyp('all.gyp', chdir='src', status=1, stderr=None) + +test.pass_test() diff --git a/tools/gyp/test/same-target-name/src/all.gyp b/tools/gyp/test/same-target-name/src/all.gyp new file mode 100644 index 0000000000..ac16976da6 --- /dev/null +++ b/tools/gyp/test/same-target-name/src/all.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'all_exes', + 'type': 'none', + 'dependencies': [ + 'executable1.gyp:*', + 'executable2.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/same-target-name/src/executable1.gyp b/tools/gyp/test/same-target-name/src/executable1.gyp new file mode 100644 index 0000000000..3c492c1b37 --- /dev/null +++ b/tools/gyp/test/same-target-name/src/executable1.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'sources': [ + 'main1.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/same-target-name/src/executable2.gyp b/tools/gyp/test/same-target-name/src/executable2.gyp new file mode 100644 index 0000000000..41e84a61c6 --- /dev/null +++ b/tools/gyp/test/same-target-name/src/executable2.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'sources': [ + 'main2.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/scons_tools/gyptest-tools.py b/tools/gyp/test/scons_tools/gyptest-tools.py new file mode 100755 index 0000000000..e97f5e6318 --- /dev/null +++ b/tools/gyp/test/scons_tools/gyptest-tools.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that a scons build picks up tools modules specified +via 'scons_tools' in the 'scons_settings' dictionary. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('tools.gyp') + +test.build('tools.gyp', test.ALL) + +if test.format == 'scons': + expect = "Hello, world!\n" +else: + expect = "" +test.run_built_executable('tools', stdout=expect) + +test.pass_test() diff --git a/tools/gyp/test/scons_tools/site_scons/site_tools/this_tool.py b/tools/gyp/test/scons_tools/site_scons/site_tools/this_tool.py new file mode 100644 index 0000000000..10c89476d7 --- /dev/null +++ b/tools/gyp/test/scons_tools/site_scons/site_tools/this_tool.py @@ -0,0 +1,10 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# SCons "tool" module that simply sets a -D value. +def generate(env): + env['CPPDEFINES'] = ['THIS_TOOL'] + +def exists(env): + pass diff --git a/tools/gyp/test/scons_tools/tools.c b/tools/gyp/test/scons_tools/tools.c new file mode 100644 index 0000000000..78dc0e31ef --- /dev/null +++ b/tools/gyp/test/scons_tools/tools.c @@ -0,0 +1,13 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#ifdef THIS_TOOL + printf("Hello, world!\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/scons_tools/tools.gyp b/tools/gyp/test/scons_tools/tools.gyp new file mode 100644 index 0000000000..736ba3f224 --- /dev/null +++ b/tools/gyp/test/scons_tools/tools.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'tools', + 'type': 'executable', + 'sources': [ + 'tools.c', + ], + }, + ], + 'scons_settings': { + 'tools': ['default', 'this_tool'], + }, +} diff --git a/tools/gyp/test/settings/gyptest-settings.py b/tools/gyp/test/settings/gyptest-settings.py new file mode 100755 index 0000000000..0ed81edbf2 --- /dev/null +++ b/tools/gyp/test/settings/gyptest-settings.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Smoke-tests 'settings' blocks. +""" + +import TestGyp + +# 'settings' is only supported for make and scons (and will be removed there as +# well eventually). +test = TestGyp.TestGyp(formats=['make', 'scons']) +test.run_gyp('settings.gyp') +test.build('test.gyp', test.ALL) +test.pass_test() diff --git a/tools/gyp/test/settings/settings.gyp b/tools/gyp/test/settings/settings.gyp new file mode 100644 index 0000000000..5bde13ef27 --- /dev/null +++ b/tools/gyp/test/settings/settings.gyp @@ -0,0 +1,20 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'settings_target', + 'type': 'settings', + # In real life, this would set 'cflags' etc and other targets + # would depend on it. + }, + { + # This is needed so scons will actually generate a SConstruct + # (which it doesn't do for settings targets alone). + 'target_name': 'junk1', + 'type': 'none', + }, + ], +} diff --git a/tools/gyp/test/sibling/gyptest-all.py b/tools/gyp/test/sibling/gyptest-all.py new file mode 100755 index 0000000000..7e80cf8236 --- /dev/null +++ b/tools/gyp/test/sibling/gyptest-all.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('build/all.gyp', chdir='src') + +test.build('build/all.gyp', test.ALL, chdir='src') + +chdir = 'src/build' + +# The top-level Makefile is in the directory where gyp was run. +# TODO(mmoss) Should the Makefile go in the directory of the passed in .gyp +# file? What about when passing in multiple .gyp files? Would sub-project +# Makefiles (see http://codereview.chromium.org/340008 comments) solve this? +if test.format in ('make', 'ninja'): + chdir = 'src' + +if test.format == 'xcode': + chdir = 'src/prog1' +test.run_built_executable('prog1', + chdir=chdir, + stdout="Hello from prog1.c\n") + +if test.format == 'xcode': + chdir = 'src/prog2' +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/sibling/gyptest-relocate.py b/tools/gyp/test/sibling/gyptest-relocate.py new file mode 100755 index 0000000000..7c86548186 --- /dev/null +++ b/tools/gyp/test/sibling/gyptest-relocate.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('build/all.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('build/all.gyp', test.ALL, chdir='relocate/src') + +chdir = 'relocate/src/build' + +# The top-level Makefile is in the directory where gyp was run. +# TODO(mmoss) Should the Makefile go in the directory of the passed in .gyp +# file? What about when passing in multiple .gyp files? Would sub-project +# Makefiles (see http://codereview.chromium.org/340008 comments) solve this? +if test.format in ('make', 'ninja'): + chdir = 'relocate/src' + +if test.format == 'xcode': + chdir = 'relocate/src/prog1' +test.run_built_executable('prog1', + chdir=chdir, + stdout="Hello from prog1.c\n") + +if test.format == 'xcode': + chdir = 'relocate/src/prog2' +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/sibling/src/build/all.gyp b/tools/gyp/test/sibling/src/build/all.gyp new file mode 100644 index 0000000000..6eafdf99b1 --- /dev/null +++ b/tools/gyp/test/sibling/src/build/all.gyp @@ -0,0 +1,17 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + # TODO(sgk): a target name of 'all' leads to a scons dependency cycle + 'target_name': 'All', + 'type': 'none', + 'dependencies': [ + '../prog1/prog1.gyp:*', + '../prog2/prog2.gyp:*', + ], + }, + ], +} diff --git a/tools/gyp/test/sibling/src/prog1/prog1.c b/tools/gyp/test/sibling/src/prog1/prog1.c new file mode 100644 index 0000000000..161ae8a38e --- /dev/null +++ b/tools/gyp/test/sibling/src/prog1/prog1.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + return 0; +} diff --git a/tools/gyp/test/sibling/src/prog1/prog1.gyp b/tools/gyp/test/sibling/src/prog1/prog1.gyp new file mode 100644 index 0000000000..fbe38b97ad --- /dev/null +++ b/tools/gyp/test/sibling/src/prog1/prog1.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'sources': [ + 'prog1.c', + ], + }, + ], +} diff --git a/tools/gyp/test/sibling/src/prog2/prog2.c b/tools/gyp/test/sibling/src/prog2/prog2.c new file mode 100644 index 0000000000..7635ae8c1c --- /dev/null +++ b/tools/gyp/test/sibling/src/prog2/prog2.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog2.c\n"); + return 0; +} diff --git a/tools/gyp/test/sibling/src/prog2/prog2.gyp b/tools/gyp/test/sibling/src/prog2/prog2.gyp new file mode 100644 index 0000000000..5934548369 --- /dev/null +++ b/tools/gyp/test/sibling/src/prog2/prog2.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog2', + 'type': 'executable', + 'sources': [ + 'prog2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/small/gyptest-small.py b/tools/gyp/test/small/gyptest-small.py new file mode 100755 index 0000000000..a484cb3667 --- /dev/null +++ b/tools/gyp/test/small/gyptest-small.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Runs small tests. +""" + +import imp +import os +import sys +import unittest + +import TestGyp + + +test = TestGyp.TestGyp() + +# Add pylib to the import path (so tests can import their dependencies). +# This is consistant with the path.append done in the top file "gyp". +sys.path.append(os.path.join(test._cwd, 'pylib')) + +# Add new test suites here. +files_to_test = [ + 'pylib/gyp/MSVSSettings_test.py', + 'pylib/gyp/easy_xml_test.py', + 'pylib/gyp/generator/msvs_test.py', +] + +# Collect all the suites from the above files. +suites = [] +for filename in files_to_test: + # Carve the module name out of the path. + name = os.path.splitext(os.path.split(filename)[1])[0] + # Find the complete module path. + full_filename = os.path.join(test._cwd, filename) + # Load the module. + module = imp.load_source(name, full_filename) + # Add it to the list of test suites. + suites.append(unittest.defaultTestLoader.loadTestsFromModule(module)) +# Create combined suite. +all_tests = unittest.TestSuite(suites) + +# Run all the tests. +result = unittest.TextTestRunner(verbosity=2).run(all_tests) +if result.failures or result.errors: + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-SYMROOT-all.py b/tools/gyp/test/subdirectory/gyptest-SYMROOT-all.py new file mode 100755 index 0000000000..b7509041ae --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-SYMROOT-all.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a target and a subsidiary dependent target from a +.gyp file in a subdirectory, without specifying an explicit output build +directory, and using the generated solution or project file at the top +of the tree as the entry point. + +The configuration sets the Xcode SYMROOT variable and uses --depth= +to make Xcode behave like the other build tools--that is, put all +built targets in a single output build directory at the top of the tree. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('prog1.gyp', '-Dset_symroot=1', '--depth=.', chdir='src') + +test.relocate('src', 'relocate/src') + +# Suppress the test infrastructure's setting SYMROOT on the command line. +test.build('prog1.gyp', test.ALL, SYMROOT=None, chdir='relocate/src') + +test.run_built_executable('prog1', + stdout="Hello from prog1.c\n", + chdir='relocate/src') +test.run_built_executable('prog2', + stdout="Hello from prog2.c\n", + chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-SYMROOT-default.py b/tools/gyp/test/subdirectory/gyptest-SYMROOT-default.py new file mode 100755 index 0000000000..c64ae7da39 --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-SYMROOT-default.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a target and a subsidiary dependent target from a +.gyp file in a subdirectory, without specifying an explicit output build +directory, and using the generated solution or project file at the top +of the tree as the entry point. + +The configuration sets the Xcode SYMROOT variable and uses --depth= +to make Xcode behave like the other build tools--that is, put all +built targets in a single output build directory at the top of the tree. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('prog1.gyp', '-Dset_symroot=1', '--depth=.', chdir='src') + +test.relocate('src', 'relocate/src') + +# Suppress the test infrastructure's setting SYMROOT on the command line. +test.build('prog1.gyp', SYMROOT=None, chdir='relocate/src') + +test.run_built_executable('prog1', + stdout="Hello from prog1.c\n", + chdir='relocate/src') + +test.run_built_executable('prog2', + stdout="Hello from prog2.c\n", + chdir='relocate/src') + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-subdir-all.py b/tools/gyp/test/subdirectory/gyptest-subdir-all.py new file mode 100755 index 0000000000..93344a075c --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-subdir-all.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a subsidiary dependent target from a .gyp file in a +subdirectory, without specifying an explicit output build directory, +and using the subdirectory's solution or project file as the entry point. +""" + +import TestGyp +import errno + +# Ninja doesn't support running from subdirectories. +test = TestGyp.TestGyp(formats=['!ninja']) + +test.run_gyp('prog1.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +chdir = 'relocate/src/subdir' +target = test.ALL + +test.build('prog2.gyp', target, chdir=chdir) + +test.built_file_must_not_exist('prog1', type=test.EXECUTABLE, chdir=chdir) + +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-subdir-default.py b/tools/gyp/test/subdirectory/gyptest-subdir-default.py new file mode 100755 index 0000000000..92edcd217a --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-subdir-default.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a subsidiary dependent target from a .gyp file in a +subdirectory, without specifying an explicit output build directory, +and using the subdirectory's solution or project file as the entry point. +""" + +import TestGyp +import errno + +# Ninja doesn't support running from subdirectories. +test = TestGyp.TestGyp(formats=['!ninja']) + +test.run_gyp('prog1.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +chdir = 'relocate/src/subdir' + +test.build('prog2.gyp', chdir=chdir) + +test.built_file_must_not_exist('prog1', type=test.EXECUTABLE, chdir=chdir) + +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-subdir2-deep.py b/tools/gyp/test/subdirectory/gyptest-subdir2-deep.py new file mode 100755 index 0000000000..48548982f8 --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-subdir2-deep.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a project rooted several layers under src_dir works. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('prog3.gyp', chdir='src/subdir/subdir2') + +test.relocate('src', 'relocate/src') + +test.build('prog3.gyp', test.ALL, chdir='relocate/src/subdir/subdir2') + +test.run_built_executable('prog3', + chdir='relocate/src/subdir/subdir2', + stdout="Hello from prog3.c\n") + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-top-all.py b/tools/gyp/test/subdirectory/gyptest-top-all.py new file mode 100755 index 0000000000..a29a41b4d1 --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-top-all.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a target and a subsidiary dependent target from a +.gyp file in a subdirectory, without specifying an explicit output build +directory, and using the generated solution or project file at the top +of the tree as the entry point. + +There is a difference here in the default behavior of the underlying +build tools. Specifically, when building the entire "solution", Xcode +puts the output of each project relative to the .xcodeproj directory, +while Visual Studio (and our implementations of SCons and Make) put it +in a build directory relative to the "solution"--that is, the entry-point +from which you built the entire tree. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('prog1.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('prog1.gyp', test.ALL, chdir='relocate/src') + +test.run_built_executable('prog1', + stdout="Hello from prog1.c\n", + chdir='relocate/src') + +if test.format == 'xcode': + chdir = 'relocate/src/subdir' +else: + chdir = 'relocate/src' +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/gyptest-top-default.py b/tools/gyp/test/subdirectory/gyptest-top-default.py new file mode 100755 index 0000000000..ac5f60dbcc --- /dev/null +++ b/tools/gyp/test/subdirectory/gyptest-top-default.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a target and a subsidiary dependent target from a +.gyp file in a subdirectory, without specifying an explicit output build +directory, and using the generated solution or project file at the top +of the tree as the entry point. + +There is a difference here in the default behavior of the underlying +build tools. Specifically, when building the entire "solution", Xcode +puts the output of each project relative to the .xcodeproj directory, +while Visual Studio (and our implementations of SCons and Make) put it +in a build directory relative to the "solution"--that is, the entry-point +from which you built the entire tree. +""" + +import TestGyp + +test = TestGyp.TestGyp() + +test.run_gyp('prog1.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('prog1.gyp', chdir='relocate/src') + +test.run_built_executable('prog1', + stdout="Hello from prog1.c\n", + chdir='relocate/src') + +if test.format == 'xcode': + chdir = 'relocate/src/subdir' +else: + chdir = 'relocate/src' +test.run_built_executable('prog2', + chdir=chdir, + stdout="Hello from prog2.c\n") + +test.pass_test() diff --git a/tools/gyp/test/subdirectory/src/prog1.c b/tools/gyp/test/subdirectory/src/prog1.c new file mode 100644 index 0000000000..161ae8a38e --- /dev/null +++ b/tools/gyp/test/subdirectory/src/prog1.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + return 0; +} diff --git a/tools/gyp/test/subdirectory/src/prog1.gyp b/tools/gyp/test/subdirectory/src/prog1.gyp new file mode 100644 index 0000000000..2aa66ce7d7 --- /dev/null +++ b/tools/gyp/test/subdirectory/src/prog1.gyp @@ -0,0 +1,21 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + 'symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'dependencies': [ + 'subdir/prog2.gyp:prog2', + ], + 'sources': [ + 'prog1.c', + ], + }, + ], +} diff --git a/tools/gyp/test/subdirectory/src/subdir/prog2.c b/tools/gyp/test/subdirectory/src/subdir/prog2.c new file mode 100644 index 0000000000..7635ae8c1c --- /dev/null +++ b/tools/gyp/test/subdirectory/src/subdir/prog2.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog2.c\n"); + return 0; +} diff --git a/tools/gyp/test/subdirectory/src/subdir/prog2.gyp b/tools/gyp/test/subdirectory/src/subdir/prog2.gyp new file mode 100644 index 0000000000..c6cd35f7f8 --- /dev/null +++ b/tools/gyp/test/subdirectory/src/subdir/prog2.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog2', + 'type': 'executable', + 'sources': [ + 'prog2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.c b/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.c new file mode 100644 index 0000000000..7cfb0fa948 --- /dev/null +++ b/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog3.c\n"); + return 0; +} diff --git a/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.gyp b/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.gyp new file mode 100644 index 0000000000..b49fb59113 --- /dev/null +++ b/tools/gyp/test/subdirectory/src/subdir/subdir2/prog3.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../symroot.gypi', + ], + 'targets': [ + { + 'target_name': 'prog3', + 'type': 'executable', + 'sources': [ + 'prog3.c', + ], + }, + ], +} diff --git a/tools/gyp/test/subdirectory/src/symroot.gypi b/tools/gyp/test/subdirectory/src/symroot.gypi new file mode 100644 index 0000000000..519916427c --- /dev/null +++ b/tools/gyp/test/subdirectory/src/symroot.gypi @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'set_symroot%': 0, + }, + 'conditions': [ + ['set_symroot == 1', { + 'xcode_settings': { + 'SYMROOT': '<(DEPTH)/build', + }, + }], + ], +} diff --git a/tools/gyp/test/toolsets/gyptest-toolsets.py b/tools/gyp/test/toolsets/gyptest-toolsets.py new file mode 100755 index 0000000000..19737f83d0 --- /dev/null +++ b/tools/gyp/test/toolsets/gyptest-toolsets.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies that toolsets are correctly applied +""" + +import TestGyp + +# Multiple toolsets are currently only supported by the make generator. +test = TestGyp.TestGyp(formats=['make']) + +test.run_gyp('toolsets.gyp') + +test.build('toolsets.gyp', test.ALL) + +test.run_built_executable('host-main', stdout="Host\n") +test.run_built_executable('target-main', stdout="Target\n") + +test.pass_test() diff --git a/tools/gyp/test/toolsets/main.cc b/tools/gyp/test/toolsets/main.cc new file mode 100644 index 0000000000..0f353ae54f --- /dev/null +++ b/tools/gyp/test/toolsets/main.cc @@ -0,0 +1,11 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <stdio.h> + +const char *GetToolset(); + +int main(int argc, char *argv[]) { + printf("%s\n", GetToolset()); +} diff --git a/tools/gyp/test/toolsets/toolsets.cc b/tools/gyp/test/toolsets/toolsets.cc new file mode 100644 index 0000000000..a45fa029cb --- /dev/null +++ b/tools/gyp/test/toolsets/toolsets.cc @@ -0,0 +1,11 @@ +/* Copyright (c) 2009 Google Inc. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +const char *GetToolset() { +#ifdef TARGET + return "Target"; +#else + return "Host"; +#endif +} diff --git a/tools/gyp/test/toolsets/toolsets.gyp b/tools/gyp/test/toolsets/toolsets.gyp new file mode 100644 index 0000000000..e41b928de6 --- /dev/null +++ b/tools/gyp/test/toolsets/toolsets.gyp @@ -0,0 +1,38 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'target_conditions': [ + ['_toolset=="target"', {'defines': ['TARGET']}] + ] + }, + 'targets': [ + { + 'target_name': 'toolsets', + 'type': 'static_library', + 'toolsets': ['target', 'host'], + 'sources': [ + 'toolsets.cc', + ], + }, + { + 'target_name': 'host-main', + 'type': 'executable', + 'toolsets': ['host'], + 'dependencies': ['toolsets'], + 'sources': [ + 'main.cc', + ], + }, + { + 'target_name': 'target-main', + 'type': 'executable', + 'dependencies': ['toolsets'], + 'sources': [ + 'main.cc', + ], + }, + ], +} diff --git a/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py b/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py new file mode 100755 index 0000000000..61986cdaa5 --- /dev/null +++ b/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verifies building a subsidiary dependent target from a .gyp file in a +subdirectory, without specifying an explicit output build directory, +and using the subdirectory's solution or project file as the entry point. +""" + +import TestGyp +import errno + +test = TestGyp.TestGyp(formats=['make']) + +# We want our Makefile to be one dir up from main.gyp. +test.run_gyp('main.gyp', '--toplevel-dir=..', chdir='src/sub1') + +toplevel_dir = 'src' + +test.build('all', chdir=toplevel_dir) + +test.built_file_must_exist('prog1', type=test.EXECUTABLE, chdir=toplevel_dir) + +test.run_built_executable('prog1', + chdir=toplevel_dir, + stdout="Hello from prog1.c\n") + +test.pass_test() diff --git a/tools/gyp/test/toplevel-dir/src/sub1/main.gyp b/tools/gyp/test/toplevel-dir/src/sub1/main.gyp new file mode 100644 index 0000000000..33219010e4 --- /dev/null +++ b/tools/gyp/test/toplevel-dir/src/sub1/main.gyp @@ -0,0 +1,18 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog1', + 'type': 'executable', + 'dependencies': [ + '<(DEPTH)/../sub2/prog2.gyp:prog2', + ], + 'sources': [ + 'prog1.c', + ], + }, + ], +} diff --git a/tools/gyp/test/toplevel-dir/src/sub1/prog1.c b/tools/gyp/test/toplevel-dir/src/sub1/prog1.c new file mode 100644 index 0000000000..161ae8a38e --- /dev/null +++ b/tools/gyp/test/toplevel-dir/src/sub1/prog1.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog1.c\n"); + return 0; +} diff --git a/tools/gyp/test/toplevel-dir/src/sub2/prog2.c b/tools/gyp/test/toplevel-dir/src/sub2/prog2.c new file mode 100644 index 0000000000..7635ae8c1c --- /dev/null +++ b/tools/gyp/test/toplevel-dir/src/sub2/prog2.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + printf("Hello from prog2.c\n"); + return 0; +} diff --git a/tools/gyp/test/toplevel-dir/src/sub2/prog2.gyp b/tools/gyp/test/toplevel-dir/src/sub2/prog2.gyp new file mode 100644 index 0000000000..5934548369 --- /dev/null +++ b/tools/gyp/test/toplevel-dir/src/sub2/prog2.gyp @@ -0,0 +1,15 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'prog2', + 'type': 'executable', + 'sources': [ + 'prog2.c', + ], + }, + ], +} diff --git a/tools/gyp/test/variables/commands/commands-repeated.gyp b/tools/gyp/test/variables/commands/commands-repeated.gyp new file mode 100644 index 0000000000..822ae4f05d --- /dev/null +++ b/tools/gyp/test/variables/commands/commands-repeated.gyp @@ -0,0 +1,128 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a simple test file to make sure that variable substitution +# happens correctly. Run "run_tests.py" using python to generate the +# output from this gyp file. + +{ + 'variables': { + 'pi': 'import math; print math.pi', + 'third_letters': "<(other_letters)HIJK", + 'letters_list': 'ABCD', + 'other_letters': '<(letters_list)EFG', + 'check_included': '<(included_variable)', + 'check_lists': [ + '<(included_variable)', + '<(third_letters)', + ], + 'check_int': 5, + 'check_str_int': '6', + 'check_list_int': [ + 7, + '8', + 9, + ], + 'not_int_1': ' 10', + 'not_int_2': '11 ', + 'not_int_3': '012', + 'not_int_4': '13.0', + 'not_int_5': '+14', + 'negative_int': '-15', + 'zero_int': '0', + }, + 'includes': [ + 'commands.gypi', + ], + 'targets': [ + { + 'target_name': 'foo', + 'type': 'none', + 'variables': { + 'var1': '<!(["python", "-c", "<(pi)"])', + 'var2': '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")', + 'var3': '<!(python -c "print \'<(letters_list)\'")', + 'var4': '<(<!(python -c "print \'letters_list\'"))', + 'var5': 'letters_', + 'var6': 'list', + 'var7': '<(check_int)', + 'var8': '<(check_int)blah', + 'var9': '<(check_str_int)', + 'var10': '<(check_list_int)', + 'var11': ['<@(check_list_int)'], + 'var12': '<(not_int_1)', + 'var13': '<(not_int_2)', + 'var14': '<(not_int_3)', + 'var15': '<(not_int_4)', + 'var16': '<(not_int_5)', + 'var17': '<(negative_int)', + 'var18': '<(zero_int)', + # A second set with different names to make sure they only execute the + # commands once. + 'var1prime': '<!(["python", "-c", "<(pi)"])', + 'var2prime': '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")', + 'var3prime': '<!(python -c "print \'<(letters_list)\'")', + 'var4prime': '<(<!(python -c "print \'letters_list\'"))', + }, + 'actions': [ + { + 'action_name': 'test_action', + 'variables': { + 'var7': '<!(echo <(var5)<(var6))', + }, + 'inputs' : [ + '<(var2)', + ], + 'outputs': [ + '<(var4)', + '<(var7)', + ], + 'action': [ + 'echo', + '<(_inputs)', + '<(_outputs)', + ], + }, + # Again with the same vars to make sure the right things happened. + { + 'action_name': 'test_action_prime', + 'variables': { + 'var7': '<!(echo <(var5)<(var6))', + }, + 'inputs' : [ + '<(var2)', + ], + 'outputs': [ + '<(var4)', + '<(var7)', + ], + 'action': [ + 'echo', + '<(_inputs)', + '<(_outputs)', + ], + }, + # And one more time with the other vars... + { + 'action_name': 'test_action_prime_prime', + 'variables': { + 'var7': '<!(echo <(var5)<(var6))', + }, + 'inputs' : [ + '<(var2prime)', + ], + 'outputs': [ + '<(var4prime)', + '<(var7)', + ], + 'action': [ + 'echo', + '<(_inputs)', + '<(_outputs)', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/variables/commands/commands-repeated.gyp.stdout b/tools/gyp/test/variables/commands/commands-repeated.gyp.stdout new file mode 100644 index 0000000000..fa140991e1 --- /dev/null +++ b/tools/gyp/test/variables/commands/commands-repeated.gyp.stdout @@ -0,0 +1,405 @@ +GENERAL:__init__.py:357:main running with these options: +GENERAL:__init__.py:364:main check: None +GENERAL:__init__.py:364:main circular_check: True +GENERAL:__init__.py:364:main debug: ['variables', 'general'] +GENERAL:__init__.py:364:main defines: None +GENERAL:__init__.py:362:main depth: '.' +GENERAL:__init__.py:364:main formats: ['gypd'] +GENERAL:__init__.py:364:main generator_flags: [] +GENERAL:__init__.py:364:main generator_output: None +GENERAL:__init__.py:364:main includes: None +GENERAL:__init__.py:364:main msvs_version: None +GENERAL:__init__.py:362:main suffix: '' +GENERAL:__init__.py:364:main toplevel_dir: None +GENERAL:__init__.py:364:main use_environment: True +GENERAL:__init__.py:418:main cmdline_default_variables: {} +GENERAL:__init__.py:444:main generator_flags: {} +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFG', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding 'import math; print math.pi' to 'import math; print math.pi' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'third_letters', 'is_array': '', 'replace': '<(third_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'third_letters' to 'third_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(other_letters)HIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(third_letters)' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '8' to 8 +VARIABLES:input.py:783:ExpandVariables Expanding '.' to '.' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<!(python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "print \'<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "import math; print math.pi"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "import math; print math.pi"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "<(pi)"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "import math; print math.pi"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'3.14159265359 ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'3.14159265359 ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<!(python -c "<(pi)") ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print '3.14159265359 ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '"python", "-c", "<(pi', 'is_array': '[', 'replace': '<!(["python", "-c", "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output '["python", "-c", "import math; print math.pi"]', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "import math; print math.pi"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "<(pi)"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:658:ExpandVariables Executing command '['python', '-c', 'import math; print math.pi']' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(["python", "-c", "<(pi)"])' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '<!(python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<(<!(python -c "print \'letters_list\'")', 'type': '<', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<!(python -c "print \'letters_list\'")', 'type': '<!', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'letters_list\'"' to 'python -c "print \'letters_list\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'letters_list'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'letters_list\'")' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(<!(python -c "print \'letters_list\'"))' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '"python", "-c", "<(pi', 'is_array': '[', 'replace': '<!(["python", "-c", "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output '["python", "-c", "import math; print math.pi"]', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "import math; print math.pi"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "<(pi)"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:703:ExpandVariables Had cache value for command '['python', '-c', 'import math; print math.pi']' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(["python", "-c", "<(pi)"])' to '3.14159265359' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<(letters_list', 'is_array': '', 'replace': '<!(python -c "print \'<(letters_list)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'ABCD\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<(letters_list)\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<(letters_list)\'")' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<!(python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "print \'<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "import math; print math.pi"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "import math; print math.pi"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "<(pi)"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python -c "import math; print math.pi"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'3.14159265359 ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'3.14159265359 ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<!(python -c "<(pi)") ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python -c "print '3.14159265359 ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_str_int', 'is_array': '', 'replace': '<(check_str_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_str_int' to 'check_str_int' +VARIABLES:input.py:765:ExpandVariables Found output '6', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_str_int)' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5blah', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)blah' to '5blah' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '<!(python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<(<!(python -c "print \'letters_list\'")', 'type': '<', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<!(python -c "print \'letters_list\'")', 'type': '<!', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'letters_list\'"' to 'python -c "print \'letters_list\'"' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python -c "print 'letters_list'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'letters_list\'")' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(<!(python -c "print \'letters_list\'"))' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<(letters_list', 'is_array': '', 'replace': '<!(python -c "print \'<(letters_list)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'ABCD\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<(letters_list)\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python -c "print 'ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<(letters_list)\'")' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_4', 'is_array': '', 'replace': '<(not_int_4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_4' to 'not_int_4' +VARIABLES:input.py:765:ExpandVariables Found output '13.0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_4)' to '13.0' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_3', 'is_array': '', 'replace': '<(not_int_3)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_3' to 'not_int_3' +VARIABLES:input.py:765:ExpandVariables Found output '012', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_3)' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'negative_int', 'is_array': '', 'replace': '<(negative_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'negative_int' to 'negative_int' +VARIABLES:input.py:765:ExpandVariables Found output '-15', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding '<(negative_int)' to -15 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_5', 'is_array': '', 'replace': '<(not_int_5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_5' to 'not_int_5' +VARIABLES:input.py:765:ExpandVariables Found output '+14', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_5)' to '+14' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<(check_list_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output '7 8 9', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_list_int)' to '7 8 9' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_2', 'is_array': '', 'replace': '<(not_int_2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_2' to 'not_int_2' +VARIABLES:input.py:765:ExpandVariables Found output '11 ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_2)' to '11 ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_1', 'is_array': '', 'replace': '<(not_int_1)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_1' to 'not_int_1' +VARIABLES:input.py:765:ExpandVariables Found output ' 10', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_1)' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'zero_int', 'is_array': '', 'replace': '<(zero_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'zero_int' to 'zero_int' +VARIABLES:input.py:765:ExpandVariables Found output '0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '<(zero_int)' to 0 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<@(check_list_int)', 'type': '<@', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output [7, 8, 9], recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 7 to 7 +VARIABLES:input.py:783:ExpandVariables Expanding 8 to 8 +VARIABLES:input.py:783:ExpandVariables Expanding 9 to 9 +VARIABLES:input.py:783:ExpandVariables Expanding '<@(check_list_int)' to [7, 8, 9] +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var6', 'is_array': '', 'replace': '<(var6)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var6' to 'var6' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'echo <(var5', 'is_array': '', 'replace': '<!(echo <(var5)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var5', 'is_array': '', 'replace': '<(var5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var5' to 'var5' +VARIABLES:input.py:765:ExpandVariables Found output 'echo letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'echo letters_list' to 'echo letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo <(var5)list' to 'echo letters_list' +VARIABLES:input.py:658:ExpandVariables Executing command 'echo letters_list' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(echo <(var5)<(var6))' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_inputs', 'is_array': '', 'replace': '<(_inputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_inputs' to '_inputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var2', 'is_array': '', 'replace': '<(var2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var2' to 'var2' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var2)' to '3.14159265359 ABCD' +VARIABLES:input.py:765:ExpandVariables Found output '"3.14159265359 ABCD"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_inputs)' to '"3.14159265359 ABCD"' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_outputs', 'is_array': '', 'replace': '<(_outputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_outputs' to '_outputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var4', 'is_array': '', 'replace': '<(var4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var4' to 'var4' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var4)' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var7', 'is_array': '', 'replace': '<(var7)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var7' to 'var7' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var7)' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_outputs)' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var6', 'is_array': '', 'replace': '<(var6)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var6' to 'var6' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'echo <(var5', 'is_array': '', 'replace': '<!(echo <(var5)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var5', 'is_array': '', 'replace': '<(var5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var5' to 'var5' +VARIABLES:input.py:765:ExpandVariables Found output 'echo letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'echo letters_list' to 'echo letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo <(var5)list' to 'echo letters_list' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'echo letters_list' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(echo <(var5)<(var6))' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action_prime' to 'test_action_prime' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_inputs', 'is_array': '', 'replace': '<(_inputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_inputs' to '_inputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var2', 'is_array': '', 'replace': '<(var2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var2' to 'var2' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var2)' to '3.14159265359 ABCD' +VARIABLES:input.py:765:ExpandVariables Found output '"3.14159265359 ABCD"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_inputs)' to '"3.14159265359 ABCD"' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_outputs', 'is_array': '', 'replace': '<(_outputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_outputs' to '_outputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var4', 'is_array': '', 'replace': '<(var4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var4' to 'var4' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var4)' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var7', 'is_array': '', 'replace': '<(var7)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var7' to 'var7' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var7)' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_outputs)' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var6', 'is_array': '', 'replace': '<(var6)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var6' to 'var6' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'echo <(var5', 'is_array': '', 'replace': '<!(echo <(var5)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var5', 'is_array': '', 'replace': '<(var5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var5' to 'var5' +VARIABLES:input.py:765:ExpandVariables Found output 'echo letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'echo letters_list' to 'echo letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo <(var5)list' to 'echo letters_list' +VARIABLES:input.py:703:ExpandVariables Had cache value for command 'echo letters_list' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(echo <(var5)<(var6))' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action_prime_prime' to 'test_action_prime_prime' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_inputs', 'is_array': '', 'replace': '<(_inputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_inputs' to '_inputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var2prime', 'is_array': '', 'replace': '<(var2prime)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var2prime' to 'var2prime' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var2prime)' to '3.14159265359 ABCD' +VARIABLES:input.py:765:ExpandVariables Found output '"3.14159265359 ABCD"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_inputs)' to '"3.14159265359 ABCD"' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_outputs', 'is_array': '', 'replace': '<(_outputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_outputs' to '_outputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var4prime', 'is_array': '', 'replace': '<(var4prime)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var4prime' to 'var4prime' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var4prime)' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var7', 'is_array': '', 'replace': '<(var7)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var7' to 'var7' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var7)' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_outputs)' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands-repeated.gyp' to 'commands-repeated.gyp' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands.gypi' to 'commands.gypi' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action_prime' to 'test_action_prime' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action_prime_prime' to 'test_action_prime_prime' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' diff --git a/tools/gyp/test/variables/commands/commands-repeated.gypd.golden b/tools/gyp/test/variables/commands/commands-repeated.gypd.golden new file mode 100644 index 0000000000..96615b6631 --- /dev/null +++ b/tools/gyp/test/variables/commands/commands-repeated.gypd.golden @@ -0,0 +1,72 @@ +{'_DEPTH': '.', + 'included_files': ['commands-repeated.gyp', 'commands.gypi'], + 'targets': [{'actions': [{'action': ['echo', + '"3.14159265359 ABCD"', + 'ABCD letters_list'], + 'action_name': 'test_action', + 'inputs': ['3.14159265359 ABCD'], + 'outputs': ['ABCD', 'letters_list'], + 'variables': {'var7': 'letters_list'}}, + {'action': ['echo', + '"3.14159265359 ABCD"', + 'ABCD letters_list'], + 'action_name': 'test_action_prime', + 'inputs': ['3.14159265359 ABCD'], + 'outputs': ['ABCD', 'letters_list'], + 'variables': {'var7': 'letters_list'}}, + {'action': ['echo', + '"3.14159265359 ABCD"', + 'ABCD letters_list'], + 'action_name': 'test_action_prime_prime', + 'inputs': ['3.14159265359 ABCD'], + 'outputs': ['ABCD', 'letters_list'], + 'variables': {'var7': 'letters_list'}}], + 'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'target_name': 'foo', + 'toolset': 'target', + 'type': 'none', + 'variables': {'var1': '3.14159265359', + 'var10': '7 8 9', + 'var11': ['7', '8', '9'], + 'var12': ' 10', + 'var13': '11 ', + 'var14': '012', + 'var15': '13.0', + 'var16': '+14', + 'var17': '-15', + 'var18': '0', + 'var1prime': '3.14159265359', + 'var2': '3.14159265359 ABCD', + 'var2prime': '3.14159265359 ABCD', + 'var3': 'ABCD', + 'var3prime': 'ABCD', + 'var4': 'ABCD', + 'var4prime': 'ABCD', + 'var5': 'letters_', + 'var6': 'list', + 'var7': '5', + 'var8': '5blah', + 'var9': '6'}}, + {'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'target_name': 'dummy', + 'toolset': 'target', + 'type': 'none'}], + 'variables': {'check_included': 'XYZ', + 'check_int': '5', + 'check_list_int': ['7', '8', '9'], + 'check_lists': ['XYZ', 'ABCDEFGHIJK'], + 'check_str_int': '6', + 'included_variable': 'XYZ', + 'letters_list': 'ABCD', + 'negative_int': '-15', + 'not_int_1': ' 10', + 'not_int_2': '11 ', + 'not_int_3': '012', + 'not_int_4': '13.0', + 'not_int_5': '+14', + 'other_letters': 'ABCDEFG', + 'pi': 'import math; print math.pi', + 'third_letters': 'ABCDEFGHIJK', + 'zero_int': '0'}} diff --git a/tools/gyp/test/variables/commands/commands.gyp b/tools/gyp/test/variables/commands/commands.gyp new file mode 100644 index 0000000000..113e4a279f --- /dev/null +++ b/tools/gyp/test/variables/commands/commands.gyp @@ -0,0 +1,84 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a simple test file to make sure that variable substitution +# happens correctly. Run "run_tests.py" using python to generate the +# output from this gyp file. + +{ + 'variables': { + 'pi': 'import math; print math.pi', + 'third_letters': "<(other_letters)HIJK", + 'letters_list': 'ABCD', + 'other_letters': '<(letters_list)EFG', + 'check_included': '<(included_variable)', + 'check_lists': [ + '<(included_variable)', + '<(third_letters)', + ], + 'check_int': 5, + 'check_str_int': '6', + 'check_list_int': [ + 7, + '8', + 9, + ], + 'not_int_1': ' 10', + 'not_int_2': '11 ', + 'not_int_3': '012', + 'not_int_4': '13.0', + 'not_int_5': '+14', + 'negative_int': '-15', + 'zero_int': '0', + }, + 'includes': [ + 'commands.gypi', + ], + 'targets': [ + { + 'target_name': 'foo', + 'type': 'none', + 'variables': { + 'var1': '<!(["python", "-c", "<(pi)"])', + 'var2': '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")', + 'var3': '<!(python -c "print \'<(letters_list)\'")', + 'var4': '<(<!(python -c "print \'letters_list\'"))', + 'var5': 'letters_', + 'var6': 'list', + 'var7': '<(check_int)', + 'var8': '<(check_int)blah', + 'var9': '<(check_str_int)', + 'var10': '<(check_list_int)', + 'var11': ['<@(check_list_int)'], + 'var12': '<(not_int_1)', + 'var13': '<(not_int_2)', + 'var14': '<(not_int_3)', + 'var15': '<(not_int_4)', + 'var16': '<(not_int_5)', + 'var17': '<(negative_int)', + 'var18': '<(zero_int)', + }, + 'actions': [ + { + 'action_name': 'test_action', + 'variables': { + 'var7': '<!(echo <(var5)<(var6))', + }, + 'inputs' : [ + '<(var2)', + ], + 'outputs': [ + '<(var4)', + '<(var7)', + ], + 'action': [ + 'echo', + '<(_inputs)', + '<(_outputs)', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout b/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout new file mode 100644 index 0000000000..b016419fff --- /dev/null +++ b/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout @@ -0,0 +1,254 @@ +GENERAL:__init__.py:357:main running with these options: +GENERAL:__init__.py:364:main check: None +GENERAL:__init__.py:364:main circular_check: True +GENERAL:__init__.py:364:main debug: ['variables', 'general'] +GENERAL:__init__.py:364:main defines: None +GENERAL:__init__.py:362:main depth: '.' +GENERAL:__init__.py:364:main formats: ['gypd'] +GENERAL:__init__.py:364:main generator_flags: [] +GENERAL:__init__.py:364:main generator_output: None +GENERAL:__init__.py:364:main includes: None +GENERAL:__init__.py:364:main msvs_version: None +GENERAL:__init__.py:362:main suffix: '' +GENERAL:__init__.py:364:main toplevel_dir: None +GENERAL:__init__.py:364:main use_environment: False +GENERAL:__init__.py:418:main cmdline_default_variables: {} +GENERAL:__init__.py:444:main generator_flags: {} +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFG', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding 'import math; print math.pi' to 'import math; print math.pi' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'third_letters', 'is_array': '', 'replace': '<(third_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'third_letters' to 'third_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(other_letters)HIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(third_letters)' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '8' to 8 +VARIABLES:input.py:783:ExpandVariables Expanding '.' to '.' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '<!(python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<(<!(python -c "print \'letters_list\'")', 'type': '<', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<!(python -c "print \'letters_list\'")', 'type': '<!', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'letters_list\'"' to 'python -c "print \'letters_list\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'letters_list'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'letters_list\'")' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(<!(python -c "print \'letters_list\'"))' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '"python", "-c", "<(pi', 'is_array': '[', 'replace': '<!(["python", "-c", "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output '["python", "-c", "import math; print math.pi"]', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "import math; print math.pi"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "<(pi)"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:658:ExpandVariables Executing command '['python', '-c', 'import math; print math.pi']' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(["python", "-c", "<(pi)"])' to '3.14159265359' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<(letters_list', 'is_array': '', 'replace': '<!(python -c "print \'<(letters_list)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'ABCD\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<(letters_list)\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<(letters_list)\'")' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<!(python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "print \'<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "import math; print math.pi"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "import math; print math.pi"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "<(pi)"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "import math; print math.pi"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'3.14159265359 ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'3.14159265359 ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<!(python -c "<(pi)") ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print '3.14159265359 ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_str_int', 'is_array': '', 'replace': '<(check_str_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_str_int' to 'check_str_int' +VARIABLES:input.py:765:ExpandVariables Found output '6', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_str_int)' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5blah', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)blah' to '5blah' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_4', 'is_array': '', 'replace': '<(not_int_4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_4' to 'not_int_4' +VARIABLES:input.py:765:ExpandVariables Found output '13.0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_4)' to '13.0' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_3', 'is_array': '', 'replace': '<(not_int_3)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_3' to 'not_int_3' +VARIABLES:input.py:765:ExpandVariables Found output '012', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_3)' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'negative_int', 'is_array': '', 'replace': '<(negative_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'negative_int' to 'negative_int' +VARIABLES:input.py:765:ExpandVariables Found output '-15', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding '<(negative_int)' to -15 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_5', 'is_array': '', 'replace': '<(not_int_5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_5' to 'not_int_5' +VARIABLES:input.py:765:ExpandVariables Found output '+14', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_5)' to '+14' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<(check_list_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output '7 8 9', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_list_int)' to '7 8 9' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_2', 'is_array': '', 'replace': '<(not_int_2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_2' to 'not_int_2' +VARIABLES:input.py:765:ExpandVariables Found output '11 ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_2)' to '11 ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_1', 'is_array': '', 'replace': '<(not_int_1)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_1' to 'not_int_1' +VARIABLES:input.py:765:ExpandVariables Found output ' 10', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_1)' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'zero_int', 'is_array': '', 'replace': '<(zero_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'zero_int' to 'zero_int' +VARIABLES:input.py:765:ExpandVariables Found output '0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '<(zero_int)' to 0 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<@(check_list_int)', 'type': '<@', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output [7, 8, 9], recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 7 to 7 +VARIABLES:input.py:783:ExpandVariables Expanding 8 to 8 +VARIABLES:input.py:783:ExpandVariables Expanding 9 to 9 +VARIABLES:input.py:783:ExpandVariables Expanding '<@(check_list_int)' to [7, 8, 9] +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var6', 'is_array': '', 'replace': '<(var6)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var6' to 'var6' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'echo <(var5', 'is_array': '', 'replace': '<!(echo <(var5)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var5', 'is_array': '', 'replace': '<(var5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var5' to 'var5' +VARIABLES:input.py:765:ExpandVariables Found output 'echo letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'echo letters_list' to 'echo letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo <(var5)list' to 'echo letters_list' +VARIABLES:input.py:658:ExpandVariables Executing command 'echo letters_list' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(echo <(var5)<(var6))' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_inputs', 'is_array': '', 'replace': '<(_inputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_inputs' to '_inputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var2', 'is_array': '', 'replace': '<(var2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var2' to 'var2' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var2)' to '3.14159265359 ABCD' +VARIABLES:input.py:765:ExpandVariables Found output '"3.14159265359 ABCD"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_inputs)' to '"3.14159265359 ABCD"' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_outputs', 'is_array': '', 'replace': '<(_outputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_outputs' to '_outputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var4', 'is_array': '', 'replace': '<(var4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var4' to 'var4' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var4)' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var7', 'is_array': '', 'replace': '<(var7)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var7' to 'var7' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var7)' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_outputs)' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands.gyp' to 'commands.gyp' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands.gypi' to 'commands.gypi' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' diff --git a/tools/gyp/test/variables/commands/commands.gyp.stdout b/tools/gyp/test/variables/commands/commands.gyp.stdout new file mode 100644 index 0000000000..2debd33fad --- /dev/null +++ b/tools/gyp/test/variables/commands/commands.gyp.stdout @@ -0,0 +1,254 @@ +GENERAL:__init__.py:357:main running with these options: +GENERAL:__init__.py:364:main check: None +GENERAL:__init__.py:364:main circular_check: True +GENERAL:__init__.py:364:main debug: ['variables', 'general'] +GENERAL:__init__.py:364:main defines: None +GENERAL:__init__.py:362:main depth: '.' +GENERAL:__init__.py:364:main formats: ['gypd'] +GENERAL:__init__.py:364:main generator_flags: [] +GENERAL:__init__.py:364:main generator_output: None +GENERAL:__init__.py:364:main includes: None +GENERAL:__init__.py:364:main msvs_version: None +GENERAL:__init__.py:362:main suffix: '' +GENERAL:__init__.py:364:main toplevel_dir: None +GENERAL:__init__.py:364:main use_environment: True +GENERAL:__init__.py:418:main cmdline_default_variables: {} +GENERAL:__init__.py:444:main generator_flags: {} +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFG', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFG' to 'ABCDEFG' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding 'import math; print math.pi' to 'import math; print math.pi' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'included_variable', 'is_array': '', 'replace': '<(included_variable)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'included_variable' to 'included_variable' +VARIABLES:input.py:765:ExpandVariables Found output 'XYZ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'XYZ' to 'XYZ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(included_variable)' to 'XYZ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'third_letters', 'is_array': '', 'replace': '<(third_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'third_letters' to 'third_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(other_letters)HIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'other_letters', 'is_array': '', 'replace': '<(other_letters)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'other_letters' to 'other_letters' +VARIABLES:input.py:765:ExpandVariables Found output '<(letters_list)EFGHIJK', recursing. +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCDEFGHIJK', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCDEFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(letters_list)EFGHIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(other_letters)HIJK' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '<(third_letters)' to 'ABCDEFGHIJK' +VARIABLES:input.py:783:ExpandVariables Expanding '8' to 8 +VARIABLES:input.py:783:ExpandVariables Expanding '.' to '.' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '<!(python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<(<!(python -c "print \'letters_list\'")', 'type': '<', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'letters_list\'"', 'is_array': '', 'replace': '<!(python -c "print \'letters_list\'")', 'type': '<!', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'letters_list\'"' to 'python -c "print \'letters_list\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'letters_list'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'letters_list\'")' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(<!(python -c "print \'letters_list\'"))' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)' to 5 +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '"python", "-c", "<(pi', 'is_array': '[', 'replace': '<!(["python", "-c", "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output '["python", "-c", "import math; print math.pi"]', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "import math; print math.pi"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:783:ExpandVariables Expanding '["python", "-c", "<(pi)"]' to '["python", "-c", "import math; print math.pi"]' +VARIABLES:input.py:658:ExpandVariables Executing command '['python', '-c', 'import math; print math.pi']' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(["python", "-c", "<(pi)"])' to '3.14159265359' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<(letters_list', 'is_array': '', 'replace': '<!(python -c "print \'<(letters_list)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'ABCD\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<(letters_list)\'"' to 'python -c "print \'ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print 'ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<(letters_list)\'")' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'letters_list', 'is_array': '', 'replace': '<(letters_list)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "print \'<!(python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "print \'<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python -c "<(pi', 'is_array': '', 'replace': '<!(python -c "<(pi)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'pi', 'is_array': '', 'replace': '<(pi)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'pi' to 'pi' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "import math; print math.pi"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "import math; print math.pi"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "<(pi)"' to 'python -c "import math; print math.pi"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "import math; print math.pi"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'python -c "print \'3.14159265359 ABCD\'"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'3.14159265359 ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:783:ExpandVariables Expanding 'python -c "print \'<!(python -c "<(pi)") ABCD\'"' to 'python -c "print \'3.14159265359 ABCD\'"' +VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print '3.14159265359 ABCD'"' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_str_int', 'is_array': '', 'replace': '<(check_str_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_str_int' to 'check_str_int' +VARIABLES:input.py:765:ExpandVariables Found output '6', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '6' to 6 +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_str_int)' to 6 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_int', 'is_array': '', 'replace': '<(check_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_int' to 'check_int' +VARIABLES:input.py:765:ExpandVariables Found output '5blah', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_int)blah' to '5blah' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_4', 'is_array': '', 'replace': '<(not_int_4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_4' to 'not_int_4' +VARIABLES:input.py:765:ExpandVariables Found output '13.0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_4)' to '13.0' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_3', 'is_array': '', 'replace': '<(not_int_3)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_3' to 'not_int_3' +VARIABLES:input.py:765:ExpandVariables Found output '012', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_3)' to '012' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'negative_int', 'is_array': '', 'replace': '<(negative_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'negative_int' to 'negative_int' +VARIABLES:input.py:765:ExpandVariables Found output '-15', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '-15' to -15 +VARIABLES:input.py:783:ExpandVariables Expanding '<(negative_int)' to -15 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_5', 'is_array': '', 'replace': '<(not_int_5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_5' to 'not_int_5' +VARIABLES:input.py:765:ExpandVariables Found output '+14', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_5)' to '+14' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<(check_list_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output '7 8 9', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '<(check_list_int)' to '7 8 9' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_2', 'is_array': '', 'replace': '<(not_int_2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_2' to 'not_int_2' +VARIABLES:input.py:765:ExpandVariables Found output '11 ', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_2)' to '11 ' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'not_int_1', 'is_array': '', 'replace': '<(not_int_1)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'not_int_1' to 'not_int_1' +VARIABLES:input.py:765:ExpandVariables Found output ' 10', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding '<(not_int_1)' to ' 10' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'zero_int', 'is_array': '', 'replace': '<(zero_int)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'zero_int' to 'zero_int' +VARIABLES:input.py:765:ExpandVariables Found output '0', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '0' to 0 +VARIABLES:input.py:783:ExpandVariables Expanding '<(zero_int)' to 0 +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_list_int', 'is_array': '', 'replace': '<@(check_list_int)', 'type': '<@', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'check_list_int' to 'check_list_int' +VARIABLES:input.py:765:ExpandVariables Found output [7, 8, 9], recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 7 to 7 +VARIABLES:input.py:783:ExpandVariables Expanding 8 to 8 +VARIABLES:input.py:783:ExpandVariables Expanding 9 to 9 +VARIABLES:input.py:783:ExpandVariables Expanding '<@(check_list_int)' to [7, 8, 9] +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var6', 'is_array': '', 'replace': '<(var6)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var6' to 'var6' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'echo <(var5', 'is_array': '', 'replace': '<!(echo <(var5)', 'type': '<!', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var5', 'is_array': '', 'replace': '<(var5)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var5' to 'var5' +VARIABLES:input.py:765:ExpandVariables Found output 'echo letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'echo letters_list' to 'echo letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo <(var5)list' to 'echo letters_list' +VARIABLES:input.py:658:ExpandVariables Executing command 'echo letters_list' in directory 'None' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<!(echo <(var5)<(var6))' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_inputs', 'is_array': '', 'replace': '<(_inputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_inputs' to '_inputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var2', 'is_array': '', 'replace': '<(var2)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var2' to 'var2' +VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var2)' to '3.14159265359 ABCD' +VARIABLES:input.py:765:ExpandVariables Found output '"3.14159265359 ABCD"', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_inputs)' to '"3.14159265359 ABCD"' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_outputs', 'is_array': '', 'replace': '<(_outputs)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_outputs' to '_outputs' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var4', 'is_array': '', 'replace': '<(var4)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var4' to 'var4' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var4)' to 'ABCD' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'var7', 'is_array': '', 'replace': '<(var7)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'var7' to 'var7' +VARIABLES:input.py:765:ExpandVariables Found output 'letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(var7)' to 'letters_list' +VARIABLES:input.py:765:ExpandVariables Found output 'ABCD letters_list', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '<(_outputs)' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands.gyp' to 'commands.gyp' +VARIABLES:input.py:783:ExpandVariables Expanding 'commands.gypi' to 'commands.gypi' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy' to 'dummy' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_' to 'letters_' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah' +VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0' +VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012' +VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14' +VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9' +VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 ' +VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10' +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'echo' to 'echo' +VARIABLES:input.py:783:ExpandVariables Expanding '"3.14159265359 ABCD"' to '"3.14159265359 ABCD"' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD letters_list' to 'ABCD letters_list' +VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD' +VARIABLES:input.py:783:ExpandVariables Expanding 'letters_list' to 'letters_list' diff --git a/tools/gyp/test/variables/commands/commands.gypd.golden b/tools/gyp/test/variables/commands/commands.gypd.golden new file mode 100644 index 0000000000..e9aaf0202d --- /dev/null +++ b/tools/gyp/test/variables/commands/commands.gypd.golden @@ -0,0 +1,54 @@ +{'_DEPTH': '.', + 'included_files': ['commands.gyp', 'commands.gypi'], + 'targets': [{'actions': [{'action': ['echo', + '"3.14159265359 ABCD"', + 'ABCD letters_list'], + 'action_name': 'test_action', + 'inputs': ['3.14159265359 ABCD'], + 'outputs': ['ABCD', 'letters_list'], + 'variables': {'var7': 'letters_list'}}], + 'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'target_name': 'foo', + 'toolset': 'target', + 'type': 'none', + 'variables': {'var1': '3.14159265359', + 'var10': '7 8 9', + 'var11': ['7', '8', '9'], + 'var12': ' 10', + 'var13': '11 ', + 'var14': '012', + 'var15': '13.0', + 'var16': '+14', + 'var17': '-15', + 'var18': '0', + 'var2': '3.14159265359 ABCD', + 'var3': 'ABCD', + 'var4': 'ABCD', + 'var5': 'letters_', + 'var6': 'list', + 'var7': '5', + 'var8': '5blah', + 'var9': '6'}}, + {'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'target_name': 'dummy', + 'toolset': 'target', + 'type': 'none'}], + 'variables': {'check_included': 'XYZ', + 'check_int': '5', + 'check_list_int': ['7', '8', '9'], + 'check_lists': ['XYZ', 'ABCDEFGHIJK'], + 'check_str_int': '6', + 'included_variable': 'XYZ', + 'letters_list': 'ABCD', + 'negative_int': '-15', + 'not_int_1': ' 10', + 'not_int_2': '11 ', + 'not_int_3': '012', + 'not_int_4': '13.0', + 'not_int_5': '+14', + 'other_letters': 'ABCDEFG', + 'pi': 'import math; print math.pi', + 'third_letters': 'ABCDEFGHIJK', + 'zero_int': '0'}} diff --git a/tools/gyp/test/variables/commands/commands.gypi b/tools/gyp/test/variables/commands/commands.gypi new file mode 100644 index 0000000000..6b22497159 --- /dev/null +++ b/tools/gyp/test/variables/commands/commands.gypi @@ -0,0 +1,16 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file is included from commands.gyp to test evaluation order of includes. +{ + 'variables': { + 'included_variable': 'XYZ', + }, + 'targets': [ + { + 'target_name': 'dummy', + 'type': 'none', + }, + ], +} diff --git a/tools/gyp/test/variables/commands/gyptest-commands-ignore-env.py b/tools/gyp/test/variables/commands/gyptest-commands-ignore-env.py new file mode 100755 index 0000000000..cdc051079c --- /dev/null +++ b/tools/gyp/test/variables/commands/gyptest-commands-ignore-env.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test that environment variables are ignored when --ignore-environment is +specified. +""" + +import os + +import TestGyp + +test = TestGyp.TestGyp(format='gypd') + +os.environ['GYP_DEFINES'] = 'FOO=BAR' +os.environ['GYP_GENERATORS'] = 'foo' +os.environ['GYP_GENERATOR_FLAGS'] = 'genflag=foo' +os.environ['GYP_GENERATOR_OUTPUT'] = 'somedir' + +expect = test.read('commands.gyp.ignore-env.stdout').replace('\r', '') + +test.run_gyp('commands.gyp', + '--debug', 'variables', '--debug', 'general', + '--ignore-environment', + stdout=expect) + +# Verify the commands.gypd against the checked-in expected contents. +# +# Normally, we should canonicalize line endings in the expected +# contents file setting the Subversion svn:eol-style to native, +# but that would still fail if multiple systems are sharing a single +# workspace on a network-mounted file system. Consequently, we +# massage the Windows line endings ('\r\n') in the output to the +# checked-in UNIX endings ('\n'). + +contents = test.read('commands.gypd').replace('\r', '') +expect = test.read('commands.gypd.golden').replace('\r', '') +if not test.match(contents, expect): + print "Unexpected contents of `commands.gypd'" + test.diff(expect, contents, 'commands.gypd ') + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/variables/commands/gyptest-commands-repeated.py b/tools/gyp/test/variables/commands/gyptest-commands-repeated.py new file mode 100755 index 0000000000..ffb17f3b9b --- /dev/null +++ b/tools/gyp/test/variables/commands/gyptest-commands-repeated.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test variable expansion of '<!()' syntax commands where they are evaluated +more then once.. +""" + +import os + +import TestGyp + +test = TestGyp.TestGyp(format='gypd') + +expect = test.read('commands-repeated.gyp.stdout').replace('\r', '') + +test.run_gyp('commands-repeated.gyp', + '--debug', 'variables', '--debug', 'general', + stdout=expect) + +# Verify the commands-repeated.gypd against the checked-in expected contents. +# +# Normally, we should canonicalize line endings in the expected +# contents file setting the Subversion svn:eol-style to native, +# but that would still fail if multiple systems are sharing a single +# workspace on a network-mounted file system. Consequently, we +# massage the Windows line endings ('\r\n') in the output to the +# checked-in UNIX endings ('\n'). + +contents = test.read('commands-repeated.gypd').replace('\r', '') +expect = test.read('commands-repeated.gypd.golden').replace('\r', '') +if not test.match(contents, expect): + print "Unexpected contents of `commands-repeated.gypd'" + test.diff(expect, contents, 'commands-repeated.gypd ') + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/variables/commands/gyptest-commands.py b/tools/gyp/test/variables/commands/gyptest-commands.py new file mode 100755 index 0000000000..ccde592168 --- /dev/null +++ b/tools/gyp/test/variables/commands/gyptest-commands.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test variable expansion of '<!()' syntax commands. +""" + +import os + +import TestGyp + +test = TestGyp.TestGyp(format='gypd') + +expect = test.read('commands.gyp.stdout').replace('\r', '') + +test.run_gyp('commands.gyp', + '--debug', 'variables', '--debug', 'general', + stdout=expect) + +# Verify the commands.gypd against the checked-in expected contents. +# +# Normally, we should canonicalize line endings in the expected +# contents file setting the Subversion svn:eol-style to native, +# but that would still fail if multiple systems are sharing a single +# workspace on a network-mounted file system. Consequently, we +# massage the Windows line endings ('\r\n') in the output to the +# checked-in UNIX endings ('\n'). + +contents = test.read('commands.gypd').replace('\r', '') +expect = test.read('commands.gypd.golden').replace('\r', '') +if not test.match(contents, expect): + print "Unexpected contents of `commands.gypd'" + test.diff(expect, contents, 'commands.gypd ') + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/variables/commands/update_golden b/tools/gyp/test/variables/commands/update_golden new file mode 100755 index 0000000000..e8da558a28 --- /dev/null +++ b/tools/gyp/test/variables/commands/update_golden @@ -0,0 +1,11 @@ +#!/bin/bash + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +python ../../../gyp --debug variables --debug general --format gypd --depth . commands.gyp > commands.gyp.stdout +python ../../../gyp --ignore-environment --debug variables --debug general --format gypd --depth . commands.gyp > commands.gyp.ignore-env.stdout +cp -f commands.gypd commands.gypd.golden +python ../../../gyp --debug variables --debug general --format gypd --depth . commands-repeated.gyp > commands-repeated.gyp.stdout +cp -f commands-repeated.gypd commands-repeated.gypd.golden diff --git a/tools/gyp/test/variables/filelist/filelist.gyp.stdout b/tools/gyp/test/variables/filelist/filelist.gyp.stdout new file mode 100644 index 0000000000..ee2b0177c0 --- /dev/null +++ b/tools/gyp/test/variables/filelist/filelist.gyp.stdout @@ -0,0 +1,174 @@ +GENERAL:__init__.py:357:main running with these options: +GENERAL:__init__.py:364:main check: None +GENERAL:__init__.py:364:main circular_check: True +GENERAL:__init__.py:364:main debug: ['variables', 'general'] +GENERAL:__init__.py:364:main defines: None +GENERAL:__init__.py:362:main depth: '.' +GENERAL:__init__.py:364:main formats: ['gypd'] +GENERAL:__init__.py:364:main generator_flags: [] +GENERAL:__init__.py:364:main generator_output: None +GENERAL:__init__.py:364:main includes: None +GENERAL:__init__.py:364:main msvs_version: None +GENERAL:__init__.py:362:main suffix: '' +GENERAL:__init__.py:364:main toplevel_dir: None +GENERAL:__init__.py:364:main use_environment: True +GENERAL:__init__.py:418:main cmdline_default_variables: {} +GENERAL:__init__.py:444:main generator_flags: {} +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Sch.*' to 'Sch.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'include' to 'include' +VARIABLES:input.py:783:ExpandVariables Expanding '.*dt' to '.*dt' +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jer.*' to 'Jer.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jerome' to 'Jerome' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schultz' to 'Schultz' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding '.' to '.' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'names.txt <@(names', 'is_array': '', 'replace': '<|(names.txt <@(names)', 'type': '<|', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'names', 'is_array': '', 'replace': '<@(names)', 'type': '<@', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'names' to 'names' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:765:ExpandVariables Found output 'names.txt John Jacob Jingleheimer Schmidt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt John Jacob Jingleheimer Schmidt' to 'names.txt John Jacob Jingleheimer Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt <@(names)' to 'names.txt John Jacob Jingleheimer Schmidt' +VARIABLES:input.py:765:ExpandVariables Found output 'names.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<|(names.txt <@(names))' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'python' to 'python' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy.py' to 'dummy.py' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'names_listfile', 'is_array': '', 'replace': '<(names_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'names_listfile' to 'names_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'names.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<(names_listfile)' to 'names.txt' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'names_listfile', 'is_array': '', 'replace': '<(names_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'names_listfile' to 'names_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'names.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<(names_listfile)' to 'names.txt' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'cat <(names_listfile', 'is_array': '', 'replace': '<!@(cat <(names_listfile)', 'type': '<!@', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'names_listfile', 'is_array': '', 'replace': '<(names_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'names_listfile' to 'names_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'cat names.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'cat names.txt' to 'cat names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'cat <(names_listfile)' to 'cat names.txt' +VARIABLES:input.py:658:ExpandVariables Executing command 'cat names.txt' in directory 'src' +VARIABLES:input.py:765:ExpandVariables Found output ['John', 'Jacob', 'Jingleheimer', 'Schmidt'], recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding '<!@(cat <(names_listfile))' to ['John', 'Jacob', 'Jingleheimer', 'Schmidt'] +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy_foo' to 'dummy_foo' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'sources.txt <@(_sources', 'is_array': '', 'replace': '<|(sources.txt <@(_sources)', 'type': '<|', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': '_sources', 'is_array': '', 'replace': '<@(_sources)', 'type': '<@', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding '_sources' to '_sources' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:765:ExpandVariables Found output 'sources.txt John Jacob Jingleheimer Schmidt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt John Jacob Jingleheimer Schmidt' to 'sources.txt John Jacob Jingleheimer Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt <@(_sources)' to 'sources.txt John Jacob Jingleheimer Schmidt' +VARIABLES:input.py:765:ExpandVariables Found output 'sources.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<|(sources.txt <@(_sources))' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'bar' to 'bar' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Sch.*' to 'Sch.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'include' to 'include' +VARIABLES:input.py:783:ExpandVariables Expanding '.*dt' to '.*dt' +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jer.*' to 'Jer.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'python' to 'python' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy.py' to 'dummy.py' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'sources_listfile', 'is_array': '', 'replace': '<(sources_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'sources_listfile' to 'sources_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'sources.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<(sources_listfile)' to 'sources.txt' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'sources_listfile', 'is_array': '', 'replace': '<(sources_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'sources_listfile' to 'sources_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'sources.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding '<(sources_listfile)' to 'sources.txt' +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'cat <(sources_listfile', 'is_array': '', 'replace': '<!@(cat <(sources_listfile)', 'type': '<!@', 'command_string': None} +VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'sources_listfile', 'is_array': '', 'replace': '<(sources_listfile)', 'type': '<', 'command_string': None} +VARIABLES:input.py:783:ExpandVariables Expanding 'sources_listfile' to 'sources_listfile' +VARIABLES:input.py:765:ExpandVariables Found output 'cat sources.txt', recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'cat sources.txt' to 'cat sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'cat <(sources_listfile)' to 'cat sources.txt' +VARIABLES:input.py:658:ExpandVariables Executing command 'cat sources.txt' in directory 'src' +VARIABLES:input.py:765:ExpandVariables Found output ['John', 'Jacob', 'Jingleheimer', 'Schmidt'], recursing. +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding '<!@(cat <(sources_listfile))' to ['John', 'Jacob', 'Jingleheimer', 'Schmidt'] +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy_foo' to 'dummy_foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jerome' to 'Jerome' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schultz' to 'Schultz' +VARIABLES:input.py:783:ExpandVariables Expanding 'filelist.gyp' to 'filelist.gyp' +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'python' to 'python' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy.py' to 'dummy.py' +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'names.txt' to 'names.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy_foo' to 'dummy_foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'bar' to 'bar' +VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target' +VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none' +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Sch.*' to 'Sch.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'include' to 'include' +VARIABLES:input.py:783:ExpandVariables Expanding '.*dt' to '.*dt' +VARIABLES:input.py:783:ExpandVariables Expanding 'exclude' to 'exclude' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jer.*' to 'Jer.*' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding 'test_action' to 'test_action' +VARIABLES:input.py:783:ExpandVariables Expanding 'python' to 'python' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy.py' to 'dummy.py' +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'sources.txt' to 'sources.txt' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'dummy_foo' to 'dummy_foo' +VARIABLES:input.py:783:ExpandVariables Expanding 'John' to 'John' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jacob' to 'Jacob' +VARIABLES:input.py:783:ExpandVariables Expanding 'Astor' to 'Astor' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jingleheimer' to 'Jingleheimer' +VARIABLES:input.py:783:ExpandVariables Expanding 'Jerome' to 'Jerome' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schmidt' to 'Schmidt' +VARIABLES:input.py:783:ExpandVariables Expanding 'Schultz' to 'Schultz' diff --git a/tools/gyp/test/variables/filelist/filelist.gypd.golden b/tools/gyp/test/variables/filelist/filelist.gypd.golden new file mode 100644 index 0000000000..09d9116047 --- /dev/null +++ b/tools/gyp/test/variables/filelist/filelist.gypd.golden @@ -0,0 +1,43 @@ +{'_DEPTH': '.', + 'included_files': ['filelist.gyp'], + 'targets': [{'actions': [{'action': ['python', 'dummy.py', 'names.txt'], + 'action_name': 'test_action', + 'inputs': ['names.txt', + 'John', + 'Jacob', + 'Jingleheimer', + 'Schmidt'], + 'outputs': ['dummy_foo']}], + 'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'target_name': 'foo', + 'toolset': 'target', + 'type': 'none', + 'variables': {'names_listfile': 'names.txt'}}, + {'actions': [{'action': ['python', 'dummy.py', 'sources.txt'], + 'action_name': 'test_action', + 'inputs': ['sources.txt', + 'John', + 'Jacob', + 'Jingleheimer', + 'Schmidt'], + 'outputs': ['dummy_foo']}], + 'configurations': {'Default': {}}, + 'default_configuration': 'Default', + 'sources': ['John', 'Jacob', 'Jingleheimer', 'Schmidt'], + 'sources_excluded': ['Astor', 'Jerome', 'Schultz'], + 'target_name': 'bar', + 'toolset': 'target', + 'type': 'none', + 'variables': {'sources_listfile': 'sources.txt'}}], + 'variables': {'names': ['John', + 'Jacob', + 'Astor', + 'Jingleheimer', + 'Jerome', + 'Schmidt', + 'Schultz'], + 'names!': ['Astor'], + 'names/': [['exclude', 'Sch.*'], + ['include', '.*dt'], + ['exclude', 'Jer.*']]}} diff --git a/tools/gyp/test/variables/filelist/gyptest-filelist.py b/tools/gyp/test/variables/filelist/gyptest-filelist.py new file mode 100755 index 0000000000..ac57355418 --- /dev/null +++ b/tools/gyp/test/variables/filelist/gyptest-filelist.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test variable expansion of '<|(list.txt ...)' syntax commands. +""" + +import os +import sys + +import TestGyp + +test = TestGyp.TestGyp(format='gypd') + +expect = test.read('filelist.gyp.stdout') +if sys.platform == 'win32': + expect = expect.replace('/', r'\\').replace('\r', '') + +test.run_gyp('src/filelist.gyp', + '--debug', 'variables', '--debug', 'general', + stdout=expect) + +# Verify the filelist.gypd against the checked-in expected contents. +# +# Normally, we should canonicalize line endings in the expected +# contents file setting the Subversion svn:eol-style to native, +# but that would still fail if multiple systems are sharing a single +# workspace on a network-mounted file system. Consequently, we +# massage the Windows line endings ('\r\n') in the output to the +# checked-in UNIX endings ('\n'). + +contents = test.read('src/filelist.gypd').replace( + '\r', '').replace('\\\\', '/') +expect = test.read('filelist.gypd.golden').replace('\r', '') +if not test.match(contents, expect): + print "Unexpected contents of `src/filelist.gypd'" + test.diff(expect, contents, 'src/filelist.gypd ') + test.fail_test() + +contents = test.read('src/names.txt') +expect = 'John\nJacob\nJingleheimer\nSchmidt\n' +if not test.match(contents, expect): + print "Unexpected contents of `src/names.txt'" + test.diff(expect, contents, 'src/names.txt ') + test.fail_test() + +test.pass_test() diff --git a/tools/gyp/test/variables/filelist/src/filelist.gyp b/tools/gyp/test/variables/filelist/src/filelist.gyp new file mode 100644 index 0000000000..df48eb3e4a --- /dev/null +++ b/tools/gyp/test/variables/filelist/src/filelist.gyp @@ -0,0 +1,93 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a test to make sure that <|(foo.txt a b c) generates +# a pre-calculated file list at gyp time and returns foo.txt. +# This feature is useful to work around limits in the number of arguments that +# can be passed to rule/action. + +{ + 'variables': { + 'names': [ + 'John', + 'Jacob', + 'Astor', + 'Jingleheimer', + 'Jerome', + 'Schmidt', + 'Schultz', + ], + 'names!': [ + 'Astor', + ], + 'names/': [ + ['exclude', 'Sch.*'], + ['include', '.*dt'], + ['exclude', 'Jer.*'], + ], + }, + 'targets': [ + { + 'target_name': 'foo', + 'type': 'none', + 'variables': { + 'names_listfile': '<|(names.txt <@(names))', + }, + 'actions': [ + { + 'action_name': 'test_action', + 'inputs' : [ + '<(names_listfile)', + '<!@(cat <(names_listfile))', + ], + 'outputs': [ + 'dummy_foo', + ], + 'action': [ + 'python', 'dummy.py', '<(names_listfile)', + ], + }, + ], + }, + { + 'target_name': 'bar', + 'type': 'none', + 'sources': [ + 'John', + 'Jacob', + 'Astor', + 'Jingleheimer', + 'Jerome', + 'Schmidt', + 'Schultz', + ], + 'sources!': [ + 'Astor', + ], + 'sources/': [ + ['exclude', 'Sch.*'], + ['include', '.*dt'], + ['exclude', 'Jer.*'], + ], + 'variables': { + 'sources_listfile': '<|(sources.txt <@(_sources))', + }, + 'actions': [ + { + 'action_name': 'test_action', + 'inputs' : [ + '<(sources_listfile)', + '<!@(cat <(sources_listfile))', + ], + 'outputs': [ + 'dummy_foo', + ], + 'action': [ + 'python', 'dummy.py', '<(sources_listfile)', + ], + }, + ], + }, + ], +} diff --git a/tools/gyp/test/variables/filelist/update_golden b/tools/gyp/test/variables/filelist/update_golden new file mode 100755 index 0000000000..b4d489a342 --- /dev/null +++ b/tools/gyp/test/variables/filelist/update_golden @@ -0,0 +1,8 @@ +#!/bin/bash + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +python ../../../gyp --debug variables --debug general --format gypd --depth . src/filelist.gyp > filelist.gyp.stdout +cp -f src/filelist.gypd filelist.gypd.golden diff --git a/tools/gyp/test/variants/gyptest-variants.py b/tools/gyp/test/variants/gyptest-variants.py new file mode 100755 index 0000000000..ce2455f663 --- /dev/null +++ b/tools/gyp/test/variants/gyptest-variants.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Verify handling of build variants. + +TODO: Right now, only the SCons generator supports this, so the +test case is SCons-specific. In particular, it relise on SCons' +ability to rebuild in response to changes on the command line. It +may be simpler to just drop this feature if the other generators +can't be made to behave the same way. +""" + +import TestGyp + +test = TestGyp.TestGyp(formats=['scons']) + +test.run_gyp('variants.gyp', chdir='src') + +test.relocate('src', 'relocate/src') + +test.build('variants.gyp', chdir='relocate/src') + +test.run_built_executable('variants', + chdir='relocate/src', + stdout="Hello, world!\n") + +test.sleep() +test.build('variants.gyp', 'VARIANT1=1', chdir='relocate/src') + +test.run_built_executable('variants', + chdir='relocate/src', + stdout="Hello from VARIANT1\n") + +test.sleep() +test.build('variants.gyp', 'VARIANT2=1', chdir='relocate/src') + +test.run_built_executable('variants', + chdir='relocate/src', + stdout="Hello from VARIANT2\n") + +test.pass_test() diff --git a/tools/gyp/test/variants/src/variants.c b/tools/gyp/test/variants/src/variants.c new file mode 100644 index 0000000000..3018e40df2 --- /dev/null +++ b/tools/gyp/test/variants/src/variants.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ +#if defined(VARIANT1) + printf("Hello from VARIANT1\n"); +#elif defined(VARIANT2) + printf("Hello from VARIANT2\n"); +#else + printf("Hello, world!\n"); +#endif + return 0; +} diff --git a/tools/gyp/test/variants/src/variants.gyp b/tools/gyp/test/variants/src/variants.gyp new file mode 100644 index 0000000000..0305ca7473 --- /dev/null +++ b/tools/gyp/test/variants/src/variants.gyp @@ -0,0 +1,27 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'variants', + 'type': 'executable', + 'sources': [ + 'variants.c', + ], + 'variants': { + 'variant1' : { + 'defines': [ + 'VARIANT1', + ], + }, + 'variant2' : { + 'defines': [ + 'VARIANT2', + ], + }, + }, + }, + ], +} diff --git a/tools/gyp/tools/graphviz.py b/tools/gyp/tools/graphviz.py index 7f7166802b..326ae221cf 100755 --- a/tools/gyp/tools/graphviz.py +++ b/tools/gyp/tools/graphviz.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -83,13 +83,18 @@ def WriteGraph(edges): print '}' -if __name__ == '__main__': +def main(): if len(sys.argv) < 2: print >>sys.stderr, __doc__ print >>sys.stderr print >>sys.stderr, 'usage: %s target1 target2...' % (sys.argv[0]) - sys.exit(1) + return 1 edges = LoadEdges('dump.json', sys.argv[1:]) WriteGraph(edges) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/gyp/tools/pretty_gyp.py b/tools/gyp/tools/pretty_gyp.py index 04c79012ee..3749792aac 100644..100755 --- a/tools/gyp/tools/pretty_gyp.py +++ b/tools/gyp/tools/pretty_gyp.py @@ -1,142 +1,154 @@ -#!/usr/bin/env python
-# Copyright (c) 2009 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This file pretty-prints the contents of a GYP file.
-
-import sys
-import re
-
-input = []
-if len(sys.argv) > 1:
- input_file = open(sys.argv[1])
- input = input_file.read().splitlines()
- input_file.close()
-else:
- input = sys.stdin.read().splitlines()
-
-# This is used to remove comments when we're counting braces.
-comment_re = re.compile(r'\s*#.*')
-
-# This is used to remove quoted strings when we're counting braces.
-# It takes into account quoted quotes, and makes sure that the quotes
-# match.
-# NOTE: It does not handle quotes that span more than one line, or
-# cases where an escaped quote is preceeded by an escaped backslash.
-quote_re_str = r'(?P<q>[\'"])(.*?)(?<![^\\][\\])(?P=q)'
-quote_re = re.compile(quote_re_str)
-
-def comment_replace(matchobj):
- return matchobj.group(1) + matchobj.group(2) + '#' * len(matchobj.group(3))
-
-def mask_comments(input):
- # This is used to mask the quoted strings so we skip braces inside
- # quoted strings.
- search_re = re.compile(r'(.*?)(#)(.*)')
- return [search_re.sub(comment_replace, line) for line in input]
-
-def quote_replace(matchobj):
- return "%s%s%s%s" % (matchobj.group(1),
- matchobj.group(2),
- 'x'*len(matchobj.group(3)),
- matchobj.group(2))
-
-def mask_quotes(input):
- # This is used to mask the quoted strings so we skip braces inside
- # quoted strings.
- search_re = re.compile(r'(.*?)' + quote_re_str)
- return [search_re.sub(quote_replace, line) for line in input]
-
-def do_split(input, masked_input, search_re):
- output = []
- mask_output = []
- for (line, masked_line) in zip(input, masked_input):
- m = search_re.match(masked_line)
- while m:
- split = len(m.group(1))
- line = line[:split] + r'\n' + line[split:]
- masked_line = masked_line[:split] + r'\n' + masked_line[split:]
- m = search_re.match(masked_line)
- output.extend(line.split(r'\n'))
- mask_output.extend(masked_line.split(r'\n'))
- return (output, mask_output)
-
-# This masks out the quotes and comments, and then splits appropriate
-# lines (lines that matche the double_*_brace re's above) before
-# indenting them below.
-def split_double_braces(input):
- # These are used to split lines which have multiple braces on them, so
- # that the indentation looks prettier when all laid out (e.g. closing
- # braces make a nice diagonal line).
- double_open_brace_re = re.compile(r'(.*?[\[\{\(,])(\s*)([\[\{\(])')
- double_close_brace_re = re.compile(r'(.*?[\]\}\)],?)(\s*)([\]\}\)])')
-
- masked_input = mask_quotes(input)
- masked_input = mask_comments(masked_input)
-
- (output, mask_output) = do_split(input, masked_input, double_open_brace_re)
- (output, mask_output) = do_split(output, mask_output, double_close_brace_re)
-
- return output
-
-# This keeps track of the number of braces on a given line and returns
-# the result. It starts at zero and subtracts for closed braces, and
-# adds for open braces.
-def count_braces(line):
- open_braces = ['[', '(', '{']
- close_braces = [']', ')', '}']
- closing_prefix_re = re.compile(r'(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$')
- cnt = 0
- stripline = comment_re.sub(r'', line)
- stripline = quote_re.sub(r"''", stripline)
- for char in stripline:
- for brace in open_braces:
- if char == brace:
- cnt += 1
- for brace in close_braces:
- if char == brace:
- cnt -= 1
-
- after = False
- if cnt > 0:
- after = True
-
- # This catches the special case of a closing brace having something
- # other than just whitespace ahead of it -- we don't want to
- # unindent that until after this line is printed so it stays with
- # the previous indentation level.
- if cnt < 0 and closing_prefix_re.match(stripline):
- after = True
- return (cnt, after)
-
-# This does the main work of indenting the input based on the brace counts.
-def prettyprint_input(lines):
- indent = 0
- basic_offset = 2
- last_line = ""
- for line in lines:
- if comment_re.match(line):
- print line
- else:
- line = line.strip('\r\n\t ') # Otherwise doesn't strip \r on Unix.
- if len(line) > 0:
- (brace_diff, after) = count_braces(line)
- if brace_diff != 0:
- if after:
- print " " * (basic_offset * indent) + line
- indent += brace_diff
- else:
- indent += brace_diff
- print " " * (basic_offset * indent) + line
- else:
- print " " * (basic_offset * indent) + line
- else:
- print ""
- last_line = line
-
-# Split up the double braces.
-lines = split_double_braces(input)
-
-# Indent and print the output.
-prettyprint_input(lines)
+#!/usr/bin/env python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Pretty-prints the contents of a GYP file.""" + +import sys +import re + + +# Regex to remove comments when we're counting braces. +COMMENT_RE = re.compile(r'\s*#.*') + +# Regex to remove quoted strings when we're counting braces. +# It takes into account quoted quotes, and makes sure that the quotes match. +# NOTE: It does not handle quotes that span more than one line, or +# cases where an escaped quote is preceeded by an escaped backslash. +quote_re_str = r'(?P<q>[\'"])(.*?)(?<![^\\][\\])(?P=q)' +QUOTE_RE = re.compile(QUOTE_RE_STR) + + +def comment_replace(matchobj): + return matchobj.group(1) + matchobj.group(2) + '#' * len(matchobj.group(3)) + + +def mask_comments(input): + """Mask the quoted strings so we skip braces inside quoted strings.""" + search_re = re.compile(r'(.*?)(#)(.*)') + return [search_re.sub(comment_replace, line) for line in input] + + +def quote_replace(matchobj): + return "%s%s%s%s" % (matchobj.group(1), + matchobj.group(2), + 'x'*len(matchobj.group(3)), + matchobj.group(2)) + + +def mask_quotes(input): + """Mask the quoted strings so we skip braces inside quoted strings.""" + search_re = re.compile(r'(.*?)' + QUOTE_RE_STR) + return [search_re.sub(quote_replace, line) for line in input] + + +def do_split(input, masked_input, search_re): + output = [] + mask_output = [] + for (line, masked_line) in zip(input, masked_input): + m = search_re.match(masked_line) + while m: + split = len(m.group(1)) + line = line[:split] + r'\n' + line[split:] + masked_line = masked_line[:split] + r'\n' + masked_line[split:] + m = search_re.match(masked_line) + output.extend(line.split(r'\n')) + mask_output.extend(masked_line.split(r'\n')) + return (output, mask_output) + + +def split_double_braces(input): + """Masks out the quotes and comments, and then splits appropriate + lines (lines that matche the double_*_brace re's above) before + indenting them below. + + These are used to split lines which have multiple braces on them, so + that the indentation looks prettier when all laid out (e.g. closing + braces make a nice diagonal line). + """ + double_open_brace_re = re.compile(r'(.*?[\[\{\(,])(\s*)([\[\{\(])') + double_close_brace_re = re.compile(r'(.*?[\]\}\)],?)(\s*)([\]\}\)])') + + masked_input = mask_quotes(input) + masked_input = mask_comments(masked_input) + + (output, mask_output) = do_split(input, masked_input, double_open_brace_re) + (output, mask_output) = do_split(output, mask_output, double_close_brace_re) + + return output + + +def count_braces(line): + """keeps track of the number of braces on a given line and returns the result. + + It starts at zero and subtracts for closed braces, and adds for open braces. + """ + open_braces = ['[', '(', '{'] + close_braces = [']', ')', '}'] + closing_prefix_re = re.compile(r'(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$') + cnt = 0 + stripline = COMMENT_RE.sub(r'', line) + stripline = QUOTE_RE.sub(r"''", stripline) + for char in stripline: + for brace in open_braces: + if char == brace: + cnt += 1 + for brace in close_braces: + if char == brace: + cnt -= 1 + + after = False + if cnt > 0: + after = True + + # This catches the special case of a closing brace having something + # other than just whitespace ahead of it -- we don't want to + # unindent that until after this line is printed so it stays with + # the previous indentation level. + if cnt < 0 and closing_prefix_re.match(stripline): + after = True + return (cnt, after) + + +def prettyprint_input(lines): + """Does the main work of indenting the input based on the brace counts.""" + indent = 0 + basic_offset = 2 + last_line = "" + for line in lines: + if COMMENT_RE.match(line): + print line + else: + line = line.strip('\r\n\t ') # Otherwise doesn't strip \r on Unix. + if len(line) > 0: + (brace_diff, after) = count_braces(line) + if brace_diff != 0: + if after: + print " " * (basic_offset * indent) + line + indent += brace_diff + else: + indent += brace_diff + print " " * (basic_offset * indent) + line + else: + print " " * (basic_offset * indent) + line + else: + print "" + last_line = line + + +def main(): + if len(sys.argv) > 1: + data = open(sys.argv[1]).read().splitlines() + else: + data = sys.stdin.read().splitlines() + # Split up the double braces. + lines = split_double_braces(data) + + # Indent and print the output. + prettyprint_input(lines) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/gyp/tools/pretty_sln.py b/tools/gyp/tools/pretty_sln.py index 0741fff177..7013f2b660 100755 --- a/tools/gyp/tools/pretty_sln.py +++ b/tools/gyp/tools/pretty_sln.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2.5 +#!/usr/bin/env python # Copyright (c) 2009 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -153,7 +153,7 @@ def main(): # check if we have exactly 1 parameter. if len(sys.argv) < 2: print 'Usage: %s "c:\\path\\to\\project.sln"' % sys.argv[0] - return + return 1 (projects, deps) = ParseSolution(sys.argv[1]) PrintDependencies(projects, deps) @@ -161,7 +161,8 @@ def main(): if '--recursive' in sys.argv: PrintVCProj(projects) + return 0 -if __name__ == '__main__': - main() +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/gyp/tools/pretty_vcproj.py b/tools/gyp/tools/pretty_vcproj.py index 292a39f7cf..ba65bf2386 100755 --- a/tools/gyp/tools/pretty_vcproj.py +++ b/tools/gyp/tools/pretty_vcproj.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2.5 +#!/usr/bin/env python # Copyright (c) 2009 Google Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -23,14 +23,16 @@ from xml.dom.minidom import Node REPLACEMENTS = dict() ARGUMENTS = None -class CmpTuple: + +class CmpTuple(object): """Compare function between 2 tuple.""" def __call__(self, x, y): (key1, value1) = x (key2, value2) = y return cmp(key1, key2) -class CmpNode: + +class CmpNode(object): """Compare function between 2 xml nodes.""" def get_string(self, node): @@ -57,6 +59,7 @@ class CmpNode: def __call__(self, x, y): return cmp(self.get_string(x), self.get_string(y)) + def PrettyPrintNode(node, indent=0): if node.nodeType == Node.TEXT_NODE: if node.data.strip(): @@ -90,6 +93,7 @@ def PrettyPrintNode(node, indent=0): PrettyPrintNode(sub_node, indent=indent+2) print '%s</%s>' % (' '*indent, node.nodeName) + def FlattenFilter(node): """Returns a list of all the node and sub nodes.""" node_list = [] @@ -107,6 +111,7 @@ def FlattenFilter(node): return node_list + def FixFilenames(filenames, current_directory): new_list = [] for filename in filenames: @@ -121,8 +126,9 @@ def FixFilenames(filenames, current_directory): new_list.append(os.path.abspath(filename)) return new_list + def AbsoluteNode(node): - # Make all the properties we know about in this node absolute. + """Makes all the properties we know about in this node absolute.""" if node.attributes: for (name, value) in node.attributes.items(): if name in ['InheritedPropertySheets', 'RelativePath', @@ -136,8 +142,9 @@ def AbsoluteNode(node): if not value: node.removeAttribute(name) + def CleanupVcproj(node): - # For each sub node, we call recursively this function. + """For each sub node, we call recursively this function.""" for sub_node in node.childNodes: AbsoluteNode(sub_node) CleanupVcproj(sub_node) @@ -192,6 +199,7 @@ def CleanupVcproj(node): continue node.appendChild(new_node) + def GetConfiguationNodes(vcproj): #TODO(nsylvain): Find a better way to navigate the xml. nodes = [] @@ -203,6 +211,7 @@ def GetConfiguationNodes(vcproj): return nodes + def GetChildrenVsprops(filename): dom = parse(filename) if dom.documentElement.attributes: @@ -231,6 +240,7 @@ def SeekToNode(node1, child2): # No match. We give up. return None + def MergeAttributes(node1, node2): # No attributes to merge? if not node2.attributes: @@ -255,6 +265,7 @@ def MergeAttributes(node1, node2): if name == 'InheritedPropertySheets': node1.removeAttribute(name) + def MergeProperties(node1, node2): MergeAttributes(node1, node2) for child2 in node2.childNodes: @@ -264,6 +275,7 @@ def MergeProperties(node1, node2): else: node1.appendChild(child2.cloneNode(True)) + def main(argv): global REPLACEMENTS global ARGUMENTS @@ -274,7 +286,7 @@ def main(argv): if len(argv) < 2: print ('Usage: %s "c:\\path\\to\\vcproj.vcproj" [key1=value1] ' '[key2=value2]' % argv[0]) - return + return 1 # Parse the keys for i in range(2, len(argv)): @@ -311,6 +323,8 @@ def main(argv): # user. #print dom.toprettyxml(newl="\n") PrettyPrintNode(dom.documentElement) + return 0 + if __name__ == '__main__': - main(sys.argv) + sys.exit(main(sys.argv)) diff --git a/tools/gyp_addon b/tools/gyp_addon new file mode 100755 index 0000000000..2dac0e004d --- /dev/null +++ b/tools/gyp_addon @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import os +import sys + +script_dir = os.path.dirname(__file__) +node_root = os.path.normpath(os.path.join(script_dir, os.pardir)) + +sys.path.insert(0, os.path.join(node_root, 'tools', 'gyp', 'pylib')) +import gyp + +if __name__ == '__main__': + args = sys.argv[1:] + addon_gypi = os.path.join(node_root, 'tools', 'addon.gypi') + common_gypi = os.path.join(node_root, 'common.gypi') + args.extend(['-I', addon_gypi]) + args.extend(['-I', common_gypi]) + args.extend(['-Dlibrary=shared_library']) + args.extend(['-Dvisibility=default']) + args.extend(['-Dnode_root_dir=%s' % node_root]) + args.extend(['--depth=.']); + + gyp_args = list(args) + rc = gyp.main(gyp_args) + if rc != 0: + print 'Error running GYP' + sys.exit(rc) + diff --git a/tools/gyp_node b/tools/gyp_node index e66ca9a953..5c4882f4f2 100755 --- a/tools/gyp_node +++ b/tools/gyp_node @@ -29,11 +29,11 @@ if __name__ == '__main__': if sys.platform == 'win32': args.append(os.path.join(node_root, 'node.gyp')) common_fn = os.path.join(node_root, 'common.gypi') - options_fn = os.path.join(node_root, 'options.gypi') + options_fn = os.path.join(node_root, 'config.gypi') else: args.append(os.path.join(os.path.abspath(node_root), 'node.gyp')) common_fn = os.path.join(os.path.abspath(node_root), 'common.gypi') - options_fn = os.path.join(os.path.abspath(node_root), 'options.gypi') + options_fn = os.path.join(os.path.abspath(node_root), 'config.gypi') if os.path.exists(common_fn): args.extend(['-I', common_fn]) @@ -55,5 +55,4 @@ if __name__ == '__main__': args.append('-Dcomponent=static_library') args.append('-Dlibrary=static_library') gyp_args = list(args) - print gyp_args run_gyp(gyp_args) diff --git a/tools/installer.js b/tools/installer.js new file mode 100644 index 0000000000..487376d83f --- /dev/null +++ b/tools/installer.js @@ -0,0 +1,120 @@ +var fs = require('fs'), + path = require('path'), + exec = require('child_process').exec, + options = fs.readFileSync(process.argv[2]).toString(), + cmd = process.argv[3]; + +if (cmd !== 'install' && cmd !== 'uninstall') { + console.error('Unknown command: ' + cmd); + process.exit(1); +} + +// Python pprint.pprint() uses single quotes instead of double. +// awful. +options = options.replace(/'/gi, '"') + +// Parse options file and remove first comment line +options = JSON.parse(options.split('\n').slice(1).join('')); +var variables = options.variables, + node_prefix = variables.node_prefix || '/usr/local'; + +// Execution queue +var queue = [], + dirs = []; + +// Copy file from src to dst +function copy(src, dst, callback) { + // If src is array - copy each file separately + if (Array.isArray(src)) { + src.forEach(function(src) { + copy(src, dst, callback); + }); + return; + } + + dst = path.join(node_prefix, dst); + var dir = dst.replace(/\/[^\/]*$/, '/'); + + // Create directory if hasn't done this yet + if (dirs.indexOf(dir) === -1) { + dirs.push(dir); + queue.push('mkdir -p ' + dir); + } + + // Queue file/dir copy + queue.push('cp -rf ' + src + ' ' + dst); +} + +// Remove files +function remove(files) { + files.forEach(function(file) { + file = path.join(node_prefix, file); + queue.push('rm -rf ' + file); + }); +} + +// Run every command in queue, one-by-one +function run() { + var cmd = queue.shift(); + if (!cmd) return; + + console.log(cmd); + exec(cmd, function(err, stdout, stderr) { + if (stderr) console.error(stderr); + if (err) process.exit(1); + + run(); + }); +} + +if (cmd === 'install') { + // Copy includes + copy([ + // Node + 'src/node.h', 'src/node_buffer.h', 'src/node_object_wrap.h', + 'src/node_version.h', + // v8 + 'deps/v8/include/v8-debug.h', 'deps/v8/include/v8-preparser.h', + 'deps/v8/include/v8-profiler.h', 'deps/v8/include/v8-testing.h', + 'deps/v8/include/v8.h', 'deps/v8/include/v8stdint.h', + // uv + 'deps/uv/include/uv.h' + ], 'include/node/'); + + // Private uv headers + copy([ + 'deps/uv/include/uv-private/eio.h', 'deps/uv/include/uv-private/ev.h', + 'deps/uv/include/uv-private/ngx-queue.h', + 'deps/uv/include/uv-private/tree.h', + 'deps/uv/include/uv-private/uv-unix.h', + 'deps/uv/include/uv-private/uv-win.h', + ], 'include/node/uv-private/'); + + copy([ + 'deps/uv/include/ares.h', + 'deps/uv/include/ares_version.h' + ], 'include/node/'); + + // Copy binary file + copy('out/Release/node', 'bin/node'); + + // Install node-waf + if (variables.node_install_waf == 'true') { + copy('tools/wafadmin', 'lib/node/'); + copy('tools/node-waf', 'bin/node-waf'); + } + + // Install npm (eventually) + if (variables.node_install_npm == 'true') { + copy('deps/npm', 'lib/node_modules/npm'); + queue.push('ln -sf ../lib/node_modules/npm/bin/npm-cli.js ' + + path.join(node_prefix, 'bin/npm')); + } +} else { + remove([ + 'bin/node', 'bin/npm', 'bin/node-waf', + 'include/node/*', 'lib/node_modules', 'lib/node' + ]); +} + +run(); diff --git a/tools/scons/scons-LICENSE b/tools/scons/scons-LICENSE deleted file mode 100644 index 804ed86821..0000000000 --- a/tools/scons/scons-LICENSE +++ /dev/null @@ -1,25 +0,0 @@ - Copyright and license for SCons - a software construction tool - - This copyright and license do not apply to any other software - with which this software may have been included. - -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/scons/scons-README b/tools/scons/scons-README deleted file mode 100644 index 298a221682..0000000000 --- a/tools/scons/scons-README +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation - - SCons - a software construction tool - -This is the scons-README file for a version of SCons packaged for local -execution--that is, execution out of a specific local directory, without -having to install SCons as a system-wide utility. - -You are likely reading this file in one of the following two situations: - - 1) You have unpacked an scons-local-{version} package and are - examining the contents. - - In this case, you are presumably interested in using this - package to include a local copy of SCons with some other - software that you package, so that you can use SCons to build - your software without forcing all of your users to have it fully - installed. Instructions for this can be found below. - - If you are not looking to use SCons in this way, then please - use either the scons-{version} package to install SCons on your - system, or the scons-src-{version} package if you want the full - source to SCons, including its packaging code and underlying - tests and testing infrastructure. - - 2) This file was included in some other software package so that - the package could be built using SCons. - - In this case, follow the instructions provided with the - rest of the software package for how to use SCons to build - and/or install the software. The file containing build and - installation instructions will typically be named README or - INSTALL. - -LATEST VERSION -============== - -Before going further, you can check for the latest version of the -scons-local package, or any SCons package, at the SCons download page: - - http://www.scons.org/download.html - - -EXECUTION REQUIREMENTS -====================== - -Running SCons requires Python version 1.5.2 or later. There should be -no other dependencies or requirements to run SCons. - -The default SCons configuration assumes use of the Microsoft Visual C++ -compiler suite on WIN32 systems, and assumes a C compiler named 'cc', -a C++ compiler named 'c++', and a Fortran compiler named 'g77' (such -as found in the GNU C compiler suite) on any other type of system. -You may, of course, override these default values by appropriate -configuration of Environment construction variables. - - -INSTALLATION -============ - -Installation of this package should be as simple as unpacking the -archive (either .tar.gz or .zip) in any directory (top-level or a -subdirectory) within the software package with which you want to ship -SCons. - -Once you have installed this package, you should write an SConstruct -file at the top level of your source tree to build your software as you -see fit. - -Then modify the build/install instructions for your package to instruct -your users to execute SCons as follows (if you installed this package in -your top-level directory): - - $ python scons.py - -Or (if, for example, you installed this package in a subdirectory named -"scons"): - - $ python scons/scons.py - -That should be all you have to do. (If it isn't that simple, please let -us know!) - - -CONTENTS OF THIS PACKAGE -======================== - -This scons-local package consists of the following: - -scons-LICENSE - A copy of the copyright and terms under which SCons is - distributed (the Open Source Initiative-approved MIT license). - - A disclaimer has been added to the beginning to make clear that - this license applies only to SCons, and not to any separate - software you've written with which you're planning to package - SCons. - -scons-README - What you're looking at right now. - -scons-local-{version}/ - The SCons build engine. This is structured as a Python - library. - -scons.py - The SCons script itself. The script sets up the Python - sys.path variable to use the build engine found in the - scons-local-{version}/ directory in preference to any other - SCons build engine installed on your system. - - -DOCUMENTATION -============= - -Because this package is intended to be included with other software by -experienced users, we have not included any SCons documentation in this -package (other than this scons-README file you're reading right now). - -If, however, you need documentation about SCons, then consult any of the -following from the corresponding scons-{version} or scons-src-{version} -package: - - The RELEASE.txt file (src/RELEASE.txt file in the - scons-src-{version} package), which contains notes about this - specific release, including known problems. - - The CHANGES.txt file (src/CHANGES.txt file in the - scons-src-{version} package), which contains a list of changes - since the previous release. - - The scons.1 man page (doc/man/scons.1 in the scons-src-{version} - package), which contains a section of small examples for getting - started using SCons. - -Additional documentation for SCons is available at: - - http://www.scons.org/doc.html - - -LICENSING -========= - -SCons is distributed under the MIT license, a full copy of which is -available in the scons-LICENSE file in this package. The MIT license is -an approved Open Source license, which means: - - This software is OSI Certified Open Source Software. OSI - Certified is a certification mark of the Open Source Initiative. - -More information about OSI certifications and Open Source software is -available at: - - http://www.opensource.org/ - - -REPORTING BUGS -============== - -You can report bugs either by following the "Tracker - Bugs" link -on the SCons project page: - - http://sourceforge.net/projects/scons/ - -or by sending mail to the SCons developers mailing list: - - scons-devel@lists.sourceforge.net - - -MAILING LISTS -============= - -A mailing list for users of SCons is available. You may send questions -or comments to the list at: - - scons-users@lists.sourceforge.net - -You may subscribe to the scons-users mailing list at: - - http://lists.sourceforge.net/lists/listinfo/scons-users - - -FOR MORE INFORMATION -==================== - -Check the SCons web site at: - - http://www.scons.org/ - - -AUTHOR INFO -=========== - -Steven Knight -knight at baldmt dot com -http://www.baldmt.com/~knight/ - -With plenty of help from the SCons Development team: - Chad Austin - Charles Crain - Steve Leblanc - Anthony Roach - Terrel Shumway - diff --git a/tools/scons/scons-local-1.2.0/SCons/Action.py b/tools/scons/scons-local-1.2.0/SCons/Action.py deleted file mode 100644 index d740de66d1..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Action.py +++ /dev/null @@ -1,1147 +0,0 @@ -"""SCons.Action - -This encapsulates information about executing any sort of action that -can build one or more target Nodes (typically files) from one or more -source Nodes (also typically files) given a specific Environment. - -The base class here is ActionBase. The base class supplies just a few -OO utility methods and some generic methods for displaying information -about an Action in response to the various commands that control printing. - -A second-level base class is _ActionAction. This extends ActionBase -by providing the methods that can be used to show and perform an -action. True Action objects will subclass _ActionAction; Action -factory class objects will subclass ActionBase. - -The heavy lifting is handled by subclasses for the different types of -actions we might execute: - - CommandAction - CommandGeneratorAction - FunctionAction - ListAction - -The subclasses supply the following public interface methods used by -other modules: - - __call__() - THE public interface, "calling" an Action object executes the - command or Python function. This also takes care of printing - a pre-substitution command for debugging purposes. - - get_contents() - Fetches the "contents" of an Action for signature calculation - plus the varlist. This is what gets MD5 checksummed to decide - if a target needs to be rebuilt because its action changed. - - genstring() - Returns a string representation of the Action *without* - command substitution, but allows a CommandGeneratorAction to - generate the right action based on the specified target, - source and env. This is used by the Signature subsystem - (through the Executor) to obtain an (imprecise) representation - of the Action operation for informative purposes. - - -Subclasses also supply the following methods for internal use within -this module: - - __str__() - Returns a string approximation of the Action; no variable - substitution is performed. - - execute() - The internal method that really, truly, actually handles the - execution of a command or Python function. This is used so - that the __call__() methods can take care of displaying any - pre-substitution representations, and *then* execute an action - without worrying about the specific Actions involved. - - get_presig() - Fetches the "contents" of a subclass for signature calculation. - The varlist is added to this to produce the Action's contents. - - strfunction() - Returns a substituted string representation of the Action. - This is used by the _ActionAction.show() command to display the - command/function that will be executed to generate the target(s). - -There is a related independent ActionCaller class that looks like a -regular Action, and which serves as a wrapper for arbitrary functions -that we want to let the user specify the arguments to now, but actually -execute later (when an out-of-date check determines that it's needed to -be executed, for example). Objects of this class are returned by an -ActionFactory class that provides a __call__() method as a convenient -way for wrapping up the functions. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -__revision__ = "src/engine/SCons/Action.py 3842 2008/12/20 22:59:52 scons" - -import cPickle -import dis -import os -import string -import sys -import subprocess - -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Executor -import SCons.Util -import SCons.Subst - -# we use these a lot, so try to optimize them -is_String = SCons.Util.is_String -is_List = SCons.Util.is_List - -class _null: - pass - -print_actions = 1 -execute_actions = 1 -print_actions_presub = 0 - -def rfile(n): - try: - return n.rfile() - except AttributeError: - return n - -def default_exitstatfunc(s): - return s - -try: - SET_LINENO = dis.SET_LINENO - HAVE_ARGUMENT = dis.HAVE_ARGUMENT -except AttributeError: - remove_set_lineno_codes = lambda x: x -else: - def remove_set_lineno_codes(code): - result = [] - n = len(code) - i = 0 - while i < n: - c = code[i] - op = ord(c) - if op >= HAVE_ARGUMENT: - if op != SET_LINENO: - result.append(code[i:i+3]) - i = i+3 - else: - result.append(c) - i = i+1 - return string.join(result, '') - - -def _callable_contents(obj): - """Return the signature contents of a callable Python object. - """ - try: - # Test if obj is a method. - return _function_contents(obj.im_func) - - except AttributeError: - try: - # Test if obj is a callable object. - return _function_contents(obj.__call__.im_func) - - except AttributeError: - try: - # Test if obj is a code object. - return _code_contents(obj) - - except AttributeError: - # Test if obj is a function object. - return _function_contents(obj) - - -def _object_contents(obj): - """Return the signature contents of any Python object. - - We have to handle the case where object contains a code object - since it can be pickled directly. - """ - try: - # Test if obj is a method. - return _function_contents(obj.im_func) - - except AttributeError: - try: - # Test if obj is a callable object. - return _function_contents(obj.__call__.im_func) - - except AttributeError: - try: - # Test if obj is a code object. - return _code_contents(obj) - - except AttributeError: - try: - # Test if obj is a function object. - return _function_contents(obj) - - except AttributeError: - # Should be a pickable Python object. - try: - return cPickle.dumps(obj) - except (cPickle.PicklingError, TypeError): - # This is weird, but it seems that nested classes - # are unpickable. The Python docs say it should - # always be a PicklingError, but some Python - # versions seem to return TypeError. Just do - # the best we can. - return str(obj) - - -def _code_contents(code): - """Return the signature contents of a code object. - - By providing direct access to the code object of the - function, Python makes this extremely easy. Hooray! - - Unfortunately, older versions of Python include line - number indications in the compiled byte code. Boo! - So we remove the line number byte codes to prevent - recompilations from moving a Python function. - """ - - contents = [] - - # The code contents depends on the number of local variables - # but not their actual names. - contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) - try: - contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) - except AttributeError: - # Older versions of Python do not support closures. - contents.append(",0,0") - - # The code contents depends on any constants accessed by the - # function. Note that we have to call _object_contents on each - # constants because the code object of nested functions can - # show-up among the constants. - # - # Note that we also always ignore the first entry of co_consts - # which contains the function doc string. We assume that the - # function does not access its doc string. - contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')') - - # The code contents depends on the variable names used to - # accessed global variable, as changing the variable name changes - # the variable actually accessed and therefore changes the - # function result. - contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')') - - - # The code contents depends on its actual code!!! - contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') - - return string.join(contents, '') - - -def _function_contents(func): - """Return the signature contents of a function.""" - - contents = [_code_contents(func.func_code)] - - # The function contents depends on the value of defaults arguments - if func.func_defaults: - contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')') - else: - contents.append(',()') - - # The function contents depends on the closure captured cell values. - try: - closure = func.func_closure or [] - except AttributeError: - # Older versions of Python do not support closures. - closure = [] - - #xxx = [_object_contents(x.cell_contents) for x in closure] - try: - xxx = map(lambda x: _object_contents(x.cell_contents), closure) - except AttributeError: - xxx = [] - contents.append(',(' + string.join(xxx, ',') + ')') - - return string.join(contents, '') - - -def _actionAppend(act1, act2): - # This function knows how to slap two actions together. - # Mainly, it handles ListActions by concatenating into - # a single ListAction. - a1 = Action(act1) - a2 = Action(act2) - if a1 is None or a2 is None: - raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2)) - if isinstance(a1, ListAction): - if isinstance(a2, ListAction): - return ListAction(a1.list + a2.list) - else: - return ListAction(a1.list + [ a2 ]) - else: - if isinstance(a2, ListAction): - return ListAction([ a1 ] + a2.list) - else: - return ListAction([ a1, a2 ]) - -def _do_create_keywords(args, kw): - """This converts any arguments after the action argument into - their equivalent keywords and adds them to the kw argument. - """ - v = kw.get('varlist', ()) - # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] - if is_String(v): v = (v,) - kw['varlist'] = tuple(v) - if args: - # turn positional args into equivalent keywords - cmdstrfunc = args[0] - if cmdstrfunc is None or is_String(cmdstrfunc): - kw['cmdstr'] = cmdstrfunc - elif callable(cmdstrfunc): - kw['strfunction'] = cmdstrfunc - else: - raise SCons.Errors.UserError( - 'Invalid command display variable type. ' - 'You must either pass a string or a callback which ' - 'accepts (target, source, env) as parameters.') - if len(args) > 1: - kw['varlist'] = args[1:] + kw['varlist'] - if kw.get('strfunction', _null) is not _null \ - and kw.get('cmdstr', _null) is not _null: - raise SCons.Errors.UserError( - 'Cannot have both strfunction and cmdstr args to Action()') - -def _do_create_action(act, kw): - """This is the actual "implementation" for the - Action factory method, below. This handles the - fact that passing lists to Action() itself has - different semantics than passing lists as elements - of lists. - - The former will create a ListAction, the latter - will create a CommandAction by converting the inner - list elements to strings.""" - - if isinstance(act, ActionBase): - return act - - if is_List(act): - #TODO(1.5) return CommandAction(act, **kw) - return apply(CommandAction, (act,), kw) - - if callable(act): - try: - gen = kw['generator'] - del kw['generator'] - except KeyError: - gen = 0 - if gen: - action_type = CommandGeneratorAction - else: - action_type = FunctionAction - return action_type(act, kw) - - if is_String(act): - var=SCons.Util.get_environment_var(act) - if var: - # This looks like a string that is purely an Environment - # variable reference, like "$FOO" or "${FOO}". We do - # something special here...we lazily evaluate the contents - # of that Environment variable, so a user could put something - # like a function or a CommandGenerator in that variable - # instead of a string. - return LazyAction(var, kw) - commands = string.split(str(act), '\n') - if len(commands) == 1: - #TODO(1.5) return CommandAction(commands[0], **kw) - return apply(CommandAction, (commands[0],), kw) - # The list of string commands may include a LazyAction, so we - # reprocess them via _do_create_list_action. - return _do_create_list_action(commands, kw) - return None - -def _do_create_list_action(act, kw): - """A factory for list actions. Convert the input list into Actions - and then wrap them in a ListAction.""" - acts = [] - for a in act: - aa = _do_create_action(a, kw) - if aa is not None: acts.append(aa) - if not acts: - return None - elif len(acts) == 1: - return acts[0] - else: - return ListAction(acts) - -def Action(act, *args, **kw): - """A factory for action objects.""" - # Really simple: the _do_create_* routines do the heavy lifting. - _do_create_keywords(args, kw) - if is_List(act): - return _do_create_list_action(act, kw) - return _do_create_action(act, kw) - -class ActionBase: - """Base class for all types of action objects that can be held by - other objects (Builders, Executors, etc.) This provides the - common methods for manipulating and combining those actions.""" - - def __cmp__(self, other): - return cmp(self.__dict__, other) - - def genstring(self, target, source, env): - return str(self) - - def get_contents(self, target, source, env): - result = [ self.get_presig(target, source, env) ] - # This should never happen, as the Action() factory should wrap - # the varlist, but just in case an action is created directly, - # we duplicate this check here. - vl = self.varlist - if is_String(vl): vl = (vl,) - for v in vl: - result.append(env.subst('${'+v+'}')) - return string.join(result, '') - - def __add__(self, other): - return _actionAppend(self, other) - - def __radd__(self, other): - return _actionAppend(other, self) - - def presub_lines(self, env): - # CommandGeneratorAction needs a real environment - # in order to return the proper string here, since - # it may call LazyAction, which looks up a key - # in that env. So we temporarily remember the env here, - # and CommandGeneratorAction will use this env - # when it calls its _generate method. - self.presub_env = env - lines = string.split(str(self), '\n') - self.presub_env = None # don't need this any more - return lines - - def get_executor(self, env, overrides, tlist, slist, executor_kw): - """Return the Executor for this Action.""" - return SCons.Executor.Executor(self, env, overrides, - tlist, slist, executor_kw) - -class _ActionAction(ActionBase): - """Base class for actions that create output objects.""" - def __init__(self, cmdstr=_null, strfunction=_null, varlist=(), - presub=_null, chdir=None, exitstatfunc=None, - **kw): - self.cmdstr = cmdstr - if strfunction is not _null: - if strfunction is None: - self.cmdstr = None - else: - self.strfunction = strfunction - self.varlist = varlist - self.presub = presub - self.chdir = chdir - if not exitstatfunc: - exitstatfunc = default_exitstatfunc - self.exitstatfunc = exitstatfunc - - def print_cmd_line(self, s, target, source, env): - sys.stdout.write(s + "\n") - - def __call__(self, target, source, env, - exitstatfunc=_null, - presub=_null, - show=_null, - execute=_null, - chdir=_null): - if not is_List(target): - target = [target] - if not is_List(source): - source = [source] - - if presub is _null: - presub = self.presub - if presub is _null: - presub = print_actions_presub - if exitstatfunc is _null: exitstatfunc = self.exitstatfunc - if show is _null: show = print_actions - if execute is _null: execute = execute_actions - if chdir is _null: chdir = self.chdir - save_cwd = None - if chdir: - save_cwd = os.getcwd() - try: - chdir = str(chdir.abspath) - except AttributeError: - if not is_String(chdir): - chdir = str(target[0].dir) - if presub: - t = string.join(map(str, target), ' and ') - l = string.join(self.presub_lines(env), '\n ') - out = "Building %s with action:\n %s\n" % (t, l) - sys.stdout.write(out) - cmd = None - if show and self.strfunction: - cmd = self.strfunction(target, source, env) - if cmd: - if chdir: - cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd - try: - get = env.get - except AttributeError: - print_func = self.print_cmd_line - else: - print_func = get('PRINT_CMD_LINE_FUNC') - if not print_func: - print_func = self.print_cmd_line - print_func(cmd, target, source, env) - stat = 0 - if execute: - if chdir: - os.chdir(chdir) - try: - stat = self.execute(target, source, env) - if isinstance(stat, SCons.Errors.BuildError): - s = exitstatfunc(stat.status) - if s: - stat.status = s - else: - stat = s - else: - stat = exitstatfunc(stat) - finally: - if save_cwd: - os.chdir(save_cwd) - if cmd and save_cwd: - print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) - - return stat - - -def _string_from_cmd_list(cmd_list): - """Takes a list of command line arguments and returns a pretty - representation for printing.""" - cl = [] - for arg in map(str, cmd_list): - if ' ' in arg or '\t' in arg: - arg = '"' + arg + '"' - cl.append(arg) - return string.join(cl) - -# A fiddlin' little function that has an 'import SCons.Environment' which -# can't be moved to the top level without creating an import loop. Since -# this import creates a local variable named 'SCons', it blocks access to -# the global variable, so we move it here to prevent complaints about local -# variables being used uninitialized. -default_ENV = None -def get_default_ENV(env): - global default_ENV - try: - return env['ENV'] - except KeyError: - if not default_ENV: - import SCons.Environment - # This is a hideously expensive way to get a default shell - # environment. What it really should do is run the platform - # setup to get the default ENV. Fortunately, it's incredibly - # rare for an Environment not to have a shell environment, so - # we're not going to worry about it overmuch. - default_ENV = SCons.Environment.Environment()['ENV'] - return default_ENV - -# This function is still in draft mode. We're going to need something like -# it in the long run as more and more places use subprocess, but I'm sure -# it'll have to be tweaked to get the full desired functionality. -# one special arg (so far?), 'error', to tell what to do with exceptions. -def _subproc(env, cmd, error = 'ignore', **kw): - """Do common setup for a subprocess.Popen() call""" - # allow std{in,out,err} to be "'devnull'" - io = kw.get('stdin') - if is_String(io) and io == 'devnull': - kw['stdin'] = open(os.devnull) - io = kw.get('stdout') - if is_String(io) and io == 'devnull': - kw['stdout'] = open(os.devnull, 'w') - io = kw.get('stderr') - if is_String(io) and io == 'devnull': - kw['stderr'] = open(os.devnull, 'w') - - # Figure out what shell environment to use - ENV = kw.get('env', None) - if ENV is None: ENV = get_default_ENV(env) - - # Ensure that the ENV values are all strings: - new_env = {} - for key, value in ENV.items(): - if is_List(value): - # If the value is a list, then we assume it is a path list, - # because that's a pretty common list-like value to stick - # in an environment variable: - value = SCons.Util.flatten_sequence(value) - new_env[key] = string.join(map(str, value), os.pathsep) - else: - # It's either a string or something else. If it's a string, - # we still want to call str() because it might be a *Unicode* - # string, which makes subprocess.Popen() gag. If it isn't a - # string or a list, then we just coerce it to a string, which - # is the proper way to handle Dir and File instances and will - # produce something reasonable for just about everything else: - new_env[key] = str(value) - kw['env'] = new_env - - try: - #FUTURE return subprocess.Popen(cmd, **kw) - return apply(subprocess.Popen, (cmd,), kw) - except EnvironmentError, e: - if error == 'raise': raise - # return a dummy Popen instance that only returns error - class dummyPopen: - def __init__(self, e): self.exception = e - def communicate(self): return ('','') - def wait(self): return -self.exception.errno - stdin = None - class f: - def read(self): return '' - def readline(self): return '' - stdout = stderr = f() - return dummyPopen(e) - -class CommandAction(_ActionAction): - """Class for command-execution actions.""" - def __init__(self, cmd, **kw): - # Cmd can actually be a list or a single item; if it's a - # single item it should be the command string to execute; if a - # list then it should be the words of the command string to - # execute. Only a single command should be executed by this - # object; lists of commands should be handled by embedding - # these objects in a ListAction object (which the Action() - # factory above does). cmd will be passed to - # Environment.subst_list() for substituting environment - # variables. - if __debug__: logInstanceCreation(self, 'Action.CommandAction') - - #TODO(1.5) _ActionAction.__init__(self, **kw) - apply(_ActionAction.__init__, (self,), kw) - if is_List(cmd): - if filter(is_List, cmd): - raise TypeError, "CommandAction should be given only " \ - "a single command" - self.cmd_list = cmd - - def __str__(self): - if is_List(self.cmd_list): - return string.join(map(str, self.cmd_list), ' ') - return str(self.cmd_list) - - def process(self, target, source, env): - result = env.subst_list(self.cmd_list, 0, target, source) - silent = None - ignore = None - while 1: - try: c = result[0][0][0] - except IndexError: c = None - if c == '@': silent = 1 - elif c == '-': ignore = 1 - else: break - result[0][0] = result[0][0][1:] - try: - if not result[0][0]: - result[0] = result[0][1:] - except IndexError: - pass - return result, ignore, silent - - def strfunction(self, target, source, env): - if self.cmdstr is None: - return None - if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW - c = env.subst(self.cmdstr, SUBST_RAW, target, source) - if c: - return c - cmd_list, ignore, silent = self.process(target, source, env) - if silent: - return '' - return _string_from_cmd_list(cmd_list[0]) - - def execute(self, target, source, env): - """Execute a command action. - - This will handle lists of commands as well as individual commands, - because construction variable substitution may turn a single - "command" into a list. This means that this class can actually - handle lists of commands, even though that's not how we use it - externally. - """ - escape_list = SCons.Subst.escape_list - flatten_sequence = SCons.Util.flatten_sequence - - try: - shell = env['SHELL'] - except KeyError: - raise SCons.Errors.UserError('Missing SHELL construction variable.') - - try: - spawn = env['SPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing SPAWN construction variable.') - else: - if is_String(spawn): - spawn = env.subst(spawn, raw=1, conv=lambda x: x) - - escape = env.get('ESCAPE', lambda x: x) - - ENV = get_default_ENV(env) - - # Ensure that the ENV values are all strings: - for key, value in ENV.items(): - if not is_String(value): - if is_List(value): - # If the value is a list, then we assume it is a - # path list, because that's a pretty common list-like - # value to stick in an environment variable: - value = flatten_sequence(value) - ENV[key] = string.join(map(str, value), os.pathsep) - else: - # If it isn't a string or a list, then we just coerce - # it to a string, which is the proper way to handle - # Dir and File instances and will produce something - # reasonable for just about everything else: - ENV[key] = str(value) - - cmd_list, ignore, silent = self.process(target, map(rfile, source), env) - - # Use len() to filter out any "command" that's zero-length. - for cmd_line in filter(len, cmd_list): - # Escape the command line for the interpreter we are using. - cmd_line = escape_list(cmd_line, escape) - result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) - if not ignore and result: - msg = "Error %s" % result - return SCons.Errors.BuildError(errstr=msg, - status=result, - action=self, - command=cmd_line) - return 0 - - def get_presig(self, target, source, env): - """Return the signature contents of this action's command line. - - This strips $(-$) and everything in between the string, - since those parts don't affect signatures. - """ - from SCons.Subst import SUBST_SIG - cmd = self.cmd_list - if is_List(cmd): - cmd = string.join(map(str, cmd)) - else: - cmd = str(cmd) - return env.subst_target_source(cmd, SUBST_SIG, target, source) - - def get_implicit_deps(self, target, source, env): - icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) - if is_String(icd) and icd[:1] == '$': - icd = env.subst(icd) - if not icd or icd in ('0', 'None'): - return [] - from SCons.Subst import SUBST_SIG - cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) - res = [] - for cmd_line in cmd_list: - if cmd_line: - d = env.WhereIs(str(cmd_line[0])) - if d: - res.append(env.fs.File(d)) - return res - -class CommandGeneratorAction(ActionBase): - """Class for command-generator actions.""" - def __init__(self, generator, kw): - if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') - self.generator = generator - self.gen_kw = kw - self.varlist = kw.get('varlist', ()) - - def _generate(self, target, source, env, for_signature): - # ensure that target is a list, to make it easier to write - # generator functions: - if not is_List(target): - target = [target] - - ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) - #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw) - gen_cmd = apply(Action, (ret,), self.gen_kw) - if not gen_cmd: - raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) - return gen_cmd - - def __str__(self): - try: - env = self.presub_env - except AttributeError: - env = None - if env is None: - env = SCons.Defaults.DefaultEnvironment() - act = self._generate([], [], env, 1) - return str(act) - - def genstring(self, target, source, env): - return self._generate(target, source, env, 1).genstring(target, source, env) - - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null): - act = self._generate(target, source, env, 0) - return act(target, source, env, exitstatfunc, presub, - show, execute, chdir) - - def get_presig(self, target, source, env): - """Return the signature contents of this action's command line. - - This strips $(-$) and everything in between the string, - since those parts don't affect signatures. - """ - return self._generate(target, source, env, 1).get_presig(target, source, env) - - def get_implicit_deps(self, target, source, env): - return self._generate(target, source, env, 1).get_implicit_deps(target, source, env) - - - -# A LazyAction is a kind of hybrid generator and command action for -# strings of the form "$VAR". These strings normally expand to other -# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also -# want to be able to replace them with functions in the construction -# environment. Consequently, we want lazy evaluation and creation of -# an Action in the case of the function, but that's overkill in the more -# normal case of expansion to other strings. -# -# So we do this with a subclass that's both a generator *and* -# a command action. The overridden methods all do a quick check -# of the construction variable, and if it's a string we just call -# the corresponding CommandAction method to do the heavy lifting. -# If not, then we call the same-named CommandGeneratorAction method. -# The CommandGeneratorAction methods work by using the overridden -# _generate() method, that is, our own way of handling "generation" of -# an action based on what's in the construction variable. - -class LazyAction(CommandGeneratorAction, CommandAction): - - def __init__(self, var, kw): - if __debug__: logInstanceCreation(self, 'Action.LazyAction') - #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) - apply(CommandAction.__init__, (self, '${'+var+'}'), kw) - self.var = SCons.Util.to_String(var) - self.gen_kw = kw - - def get_parent_class(self, env): - c = env.get(self.var) - if is_String(c) and not '\n' in c: - return CommandAction - return CommandGeneratorAction - - def _generate_cache(self, env): - c = env.get(self.var, '') - #TODO(1.5) gen_cmd = Action(c, **self.gen_kw) - gen_cmd = apply(Action, (c,), self.gen_kw) - if not gen_cmd: - raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) - return gen_cmd - - def _generate(self, target, source, env, for_signature): - return self._generate_cache(env) - - def __call__(self, target, source, env, *args, **kw): - args = (self, target, source, env) + args - c = self.get_parent_class(env) - #TODO(1.5) return c.__call__(*args, **kw) - return apply(c.__call__, args, kw) - - def get_presig(self, target, source, env): - c = self.get_parent_class(env) - return c.get_presig(self, target, source, env) - - - -class FunctionAction(_ActionAction): - """Class for Python function actions.""" - - def __init__(self, execfunction, kw): - if __debug__: logInstanceCreation(self, 'Action.FunctionAction') - - self.execfunction = execfunction - try: - self.funccontents = _callable_contents(execfunction) - except AttributeError: - try: - # See if execfunction will do the heavy lifting for us. - self.gc = execfunction.get_contents - except AttributeError: - # This is weird, just do the best we can. - self.funccontents = _object_contents(execfunction) - - #TODO(1.5) _ActionAction.__init__(self, **kw) - apply(_ActionAction.__init__, (self,), kw) - - def function_name(self): - try: - return self.execfunction.__name__ - except AttributeError: - try: - return self.execfunction.__class__.__name__ - except AttributeError: - return "unknown_python_function" - - def strfunction(self, target, source, env): - if self.cmdstr is None: - return None - if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW - c = env.subst(self.cmdstr, SUBST_RAW, target, source) - if c: - return c - def array(a): - def quote(s): - try: - str_for_display = s.str_for_display - except AttributeError: - s = repr(s) - else: - s = str_for_display() - return s - return '[' + string.join(map(quote, a), ", ") + ']' - try: - strfunc = self.execfunction.strfunction - except AttributeError: - pass - else: - if strfunc is None: - return None - if callable(strfunc): - return strfunc(target, source, env) - name = self.function_name() - tstr = array(target) - sstr = array(source) - return "%s(%s, %s)" % (name, tstr, sstr) - - def __str__(self): - name = self.function_name() - if name == 'ActionCaller': - return str(self.execfunction) - return "%s(target, source, env)" % name - - def execute(self, target, source, env): - exc_info = (None,None,None) - try: - rsources = map(rfile, source) - try: - result = self.execfunction(target=target, source=rsources, env=env) - except KeyboardInterrupt, e: - raise - except SystemExit, e: - raise - except Exception, e: - result = e - exc_info = sys.exc_info() - - if result: - result = SCons.Errors.convert_to_BuildError(result, exc_info) - result.node=target - result.action=self - result.command=self.strfunction(target, source, env) - - # FIXME: This maintains backward compatibility with respect to - # which type of exceptions were returned by raising an - # exception and which ones were returned by value. It would - # probably be best to always return them by value here, but - # some codes do not check the return value of Actions and I do - # not have the time to modify them at this point. - if (exc_info[1] and - not isinstance(exc_info[1],EnvironmentError)): - raise result - - return result - finally: - # Break the cycle between the traceback object and this - # function stack frame. See the sys.exc_info() doc info for - # more information about this issue. - del exc_info - - - def get_presig(self, target, source, env): - """Return the signature contents of this callable action.""" - try: - return self.gc(target, source, env) - except AttributeError: - return self.funccontents - - def get_implicit_deps(self, target, source, env): - return [] - -class ListAction(ActionBase): - """Class for lists of other actions.""" - def __init__(self, list): - if __debug__: logInstanceCreation(self, 'Action.ListAction') - def list_of_actions(x): - if isinstance(x, ActionBase): - return x - return Action(x) - self.list = map(list_of_actions, list) - # our children will have had any varlist - # applied; we don't need to do it again - self.varlist = () - - def genstring(self, target, source, env): - return string.join(map(lambda a, t=target, s=source, e=env: - a.genstring(t, s, e), - self.list), - '\n') - - def __str__(self): - return string.join(map(str, self.list), '\n') - - def presub_lines(self, env): - return SCons.Util.flatten_sequence( - map(lambda a, env=env: a.presub_lines(env), self.list)) - - def get_presig(self, target, source, env): - """Return the signature contents of this action list. - - Simple concatenation of the signatures of the elements. - """ - return string.join(map(lambda x, t=target, s=source, e=env: - x.get_contents(t, s, e), - self.list), - "") - - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null): - for act in self.list: - stat = act(target, source, env, exitstatfunc, presub, - show, execute, chdir) - if stat: - return stat - return 0 - - def get_implicit_deps(self, target, source, env): - result = [] - for act in self.list: - result.extend(act.get_implicit_deps(target, source, env)) - return result - -class ActionCaller: - """A class for delaying calling an Action function with specific - (positional and keyword) arguments until the Action is actually - executed. - - This class looks to the rest of the world like a normal Action object, - but what it's really doing is hanging on to the arguments until we - have a target, source and env to use for the expansion. - """ - def __init__(self, parent, args, kw): - self.parent = parent - self.args = args - self.kw = kw - - def get_contents(self, target, source, env): - actfunc = self.parent.actfunc - try: - # "self.actfunc" is a function. - contents = str(actfunc.func_code.co_code) - except AttributeError: - # "self.actfunc" is a callable object. - try: - contents = str(actfunc.__call__.im_func.func_code.co_code) - except AttributeError: - # No __call__() method, so it might be a builtin - # or something like that. Do the best we can. - contents = str(actfunc) - contents = remove_set_lineno_codes(contents) - return contents - - def subst(self, s, target, source, env): - # If s is a list, recursively apply subst() - # to every element in the list - if is_List(s): - result = [] - for elem in s: - result.append(self.subst(elem, target, source, env)) - return self.parent.convert(result) - - # Special-case hack: Let a custom function wrapped in an - # ActionCaller get at the environment through which the action - # was called by using this hard-coded value as a special return. - if s == '$__env__': - return env - elif is_String(s): - return env.subst(s, 1, target, source) - return self.parent.convert(s) - - def subst_args(self, target, source, env): - return map(lambda x, self=self, t=target, s=source, e=env: - self.subst(x, t, s, e), - self.args) - - def subst_kw(self, target, source, env): - kw = {} - for key in self.kw.keys(): - kw[key] = self.subst(self.kw[key], target, source, env) - return kw - - def __call__(self, target, source, env): - args = self.subst_args(target, source, env) - kw = self.subst_kw(target, source, env) - #TODO(1.5) return self.parent.actfunc(*args, **kw) - return apply(self.parent.actfunc, args, kw) - - def strfunction(self, target, source, env): - args = self.subst_args(target, source, env) - kw = self.subst_kw(target, source, env) - #TODO(1.5) return self.parent.strfunc(*args, **kw) - return apply(self.parent.strfunc, args, kw) - - def __str__(self): - #TODO(1.5) return self.parent.strfunc(*self.args, **self.kw) - return apply(self.parent.strfunc, self.args, self.kw) - -class ActionFactory: - """A factory class that will wrap up an arbitrary function - as an SCons-executable Action object. - - The real heavy lifting here is done by the ActionCaller class. - We just collect the (positional and keyword) arguments that we're - called with and give them to the ActionCaller object we create, - so it can hang onto them until it needs them. - """ - def __init__(self, actfunc, strfunc, convert=lambda x: x): - self.actfunc = actfunc - self.strfunc = strfunc - self.convert = convert - - def __call__(self, *args, **kw): - ac = ActionCaller(self, args, kw) - action = Action(ac, strfunction=ac.strfunction) - return action diff --git a/tools/scons/scons-local-1.2.0/SCons/Builder.py b/tools/scons/scons-local-1.2.0/SCons/Builder.py deleted file mode 100644 index 97aabb4837..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Builder.py +++ /dev/null @@ -1,844 +0,0 @@ -"""SCons.Builder - -Builder object subsystem. - -A Builder object is a callable that encapsulates information about how -to execute actions to create a target Node (file) from source Nodes -(files), and how to create those dependencies for tracking. - -The main entry point here is the Builder() factory method. This provides -a procedural interface that creates the right underlying Builder object -based on the keyword arguments supplied and the types of the arguments. - -The goal is for this external interface to be simple enough that the -vast majority of users can create new Builders as necessary to support -building new types of files in their configurations, without having to -dive any deeper into this subsystem. - -The base class here is BuilderBase. This is a concrete base class which -does, in fact, represent the Builder objects that we (or users) create. - -There is also a proxy that looks like a Builder: - - CompositeBuilder - - This proxies for a Builder with an action that is actually a - dictionary that knows how to map file suffixes to a specific - action. This is so that we can invoke different actions - (compilers, compile options) for different flavors of source - files. - -Builders and their proxies have the following public interface methods -used by other modules: - - __call__() - THE public interface. Calling a Builder object (with the - use of internal helper methods) sets up the target and source - dependencies, appropriate mapping to a specific action, and the - environment manipulation necessary for overridden construction - variable. This also takes care of warning about possible mistakes - in keyword arguments. - - add_emitter() - Adds an emitter for a specific file suffix, used by some Tool - modules to specify that (for example) a yacc invocation on a .y - can create a .h *and* a .c file. - - add_action() - Adds an action for a specific file suffix, heavily used by - Tool modules to add their specific action(s) for turning - a source file into an object file to the global static - and shared object file Builders. - -There are the following methods for internal use within this module: - - _execute() - The internal method that handles the heavily lifting when a - Builder is called. This is used so that the __call__() methods - can set up warning about possible mistakes in keyword-argument - overrides, and *then* execute all of the steps necessary so that - the warnings only occur once. - - get_name() - Returns the Builder's name within a specific Environment, - primarily used to try to return helpful information in error - messages. - - adjust_suffix() - get_prefix() - get_suffix() - get_src_suffix() - set_src_suffix() - Miscellaneous stuff for handling the prefix and suffix - manipulation we use in turning source file names into target - file names. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Builder.py 3842 2008/12/20 22:59:52 scons" - -import UserDict -import UserList - -import SCons.Action -from SCons.Debug import logInstanceCreation -from SCons.Errors import InternalError, UserError -import SCons.Executor -import SCons.Memoize -import SCons.Node -import SCons.Node.FS -import SCons.Util -import SCons.Warnings - -class _Null: - pass - -_null = _Null - -class DictCmdGenerator(SCons.Util.Selector): - """This is a callable class that can be used as a - command generator function. It holds on to a dictionary - mapping file suffixes to Actions. It uses that dictionary - to return the proper action based on the file suffix of - the source file.""" - - def __init__(self, dict=None, source_ext_match=1): - SCons.Util.Selector.__init__(self, dict) - self.source_ext_match = source_ext_match - - def src_suffixes(self): - return self.keys() - - def add_action(self, suffix, action): - """Add a suffix-action pair to the mapping. - """ - self[suffix] = action - - def __call__(self, target, source, env, for_signature): - if not source: - return [] - - if self.source_ext_match: - ext = None - for src in map(str, source): - my_ext = SCons.Util.splitext(src)[1] - if ext and my_ext != ext: - raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) - ext = my_ext - else: - ext = SCons.Util.splitext(str(source[0]))[1] - - if not ext: - raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) - - try: - ret = SCons.Util.Selector.__call__(self, env, source) - except KeyError, e: - raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2])) - if ret is None: - raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ - (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys()))) - return ret - -class CallableSelector(SCons.Util.Selector): - """A callable dictionary that will, in turn, call the value it - finds if it can.""" - def __call__(self, env, source): - value = SCons.Util.Selector.__call__(self, env, source) - if callable(value): - value = value(env, source) - return value - -class DictEmitter(SCons.Util.Selector): - """A callable dictionary that maps file suffixes to emitters. - When called, it finds the right emitter in its dictionary for the - suffix of the first source file, and calls that emitter to get the - right lists of targets and sources to return. If there's no emitter - for the suffix in its dictionary, the original target and source are - returned. - """ - def __call__(self, target, source, env): - emitter = SCons.Util.Selector.__call__(self, env, source) - if emitter: - target, source = emitter(target, source, env) - return (target, source) - -class ListEmitter(UserList.UserList): - """A callable list of emitters that calls each in sequence, - returning the result. - """ - def __call__(self, target, source, env): - for e in self.data: - target, source = e(target, source, env) - return (target, source) - -# These are a common errors when calling a Builder; -# they are similar to the 'target' and 'source' keyword args to builders, -# so we issue warnings when we see them. The warnings can, of course, -# be disabled. -misleading_keywords = { - 'targets' : 'target', - 'sources' : 'source', -} - -class OverrideWarner(UserDict.UserDict): - """A class for warning about keyword arguments that we use as - overrides in a Builder call. - - This class exists to handle the fact that a single Builder call - can actually invoke multiple builders. This class only emits the - warnings once, no matter how many Builders are invoked. - """ - def __init__(self, dict): - UserDict.UserDict.__init__(self, dict) - if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') - self.already_warned = None - def warn(self): - if self.already_warned: - return - for k in self.keys(): - if misleading_keywords.has_key(k): - alt = misleading_keywords[k] - msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) - SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) - self.already_warned = 1 - -def Builder(**kw): - """A factory for builder objects.""" - composite = None - if kw.has_key('generator'): - if kw.has_key('action'): - raise UserError, "You must not specify both an action and a generator." - kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) - del kw['generator'] - elif kw.has_key('action'): - source_ext_match = kw.get('source_ext_match', 1) - if kw.has_key('source_ext_match'): - del kw['source_ext_match'] - if SCons.Util.is_Dict(kw['action']): - composite = DictCmdGenerator(kw['action'], source_ext_match) - kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) - kw['src_suffix'] = composite.src_suffixes() - else: - kw['action'] = SCons.Action.Action(kw['action']) - - if kw.has_key('emitter'): - emitter = kw['emitter'] - if SCons.Util.is_String(emitter): - # This allows users to pass in an Environment - # variable reference (like "$FOO") as an emitter. - # We will look in that Environment variable for - # a callable to use as the actual emitter. - var = SCons.Util.get_environment_var(emitter) - if not var: - raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter - kw['emitter'] = EmitterProxy(var) - elif SCons.Util.is_Dict(emitter): - kw['emitter'] = DictEmitter(emitter) - elif SCons.Util.is_List(emitter): - kw['emitter'] = ListEmitter(emitter) - - result = apply(BuilderBase, (), kw) - - if not composite is None: - result = CompositeBuilder(result, composite) - - return result - -def _node_errors(builder, env, tlist, slist): - """Validate that the lists of target and source nodes are - legal for this builder and environment. Raise errors or - issue warnings as appropriate. - """ - - # First, figure out if there are any errors in the way the targets - # were specified. - for t in tlist: - if t.side_effect: - raise UserError, "Multiple ways to build the same target were specified for: %s" % t - if t.has_explicit_builder(): - if not t.env is None and not t.env is env: - action = t.builder.action - t_contents = action.get_contents(tlist, slist, t.env) - contents = action.get_contents(tlist, slist, env) - - if t_contents == contents: - msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) - SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) - else: - msg = "Two environments with different actions were specified for the same target: %s" % t - raise UserError, msg - if builder.multi: - if t.builder != builder: - msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) - raise UserError, msg - if t.get_executor().targets != tlist: - msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tlist)) - raise UserError, msg - elif t.sources != slist: - msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist)) - raise UserError, msg - - if builder.single_source: - if len(slist) > 1: - raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist)) - -class EmitterProxy: - """This is a callable class that can act as a - Builder emitter. It holds on to a string that - is a key into an Environment dictionary, and will - look there at actual build time to see if it holds - a callable. If so, we will call that as the actual - emitter.""" - def __init__(self, var): - self.var = SCons.Util.to_String(var) - - def __call__(self, target, source, env): - emitter = self.var - - # Recursively substitute the variable. - # We can't use env.subst() because it deals only - # in strings. Maybe we should change that? - while SCons.Util.is_String(emitter) and env.has_key(emitter): - emitter = env[emitter] - if callable(emitter): - target, source = emitter(target, source, env) - elif SCons.Util.is_List(emitter): - for e in emitter: - target, source = e(target, source, env) - - return (target, source) - - - def __cmp__(self, other): - return cmp(self.var, other.var) - -class BuilderBase: - """Base class for Builders, objects that create output - nodes (files) from input nodes (files). - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self, action = None, - prefix = '', - suffix = '', - src_suffix = '', - target_factory = None, - source_factory = None, - target_scanner = None, - source_scanner = None, - emitter = None, - multi = 0, - env = None, - single_source = 0, - name = None, - chdir = _null, - is_explicit = 1, - src_builder = None, - ensure_suffix = False, - **overrides): - if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') - self._memo = {} - self.action = action - self.multi = multi - if SCons.Util.is_Dict(prefix): - prefix = CallableSelector(prefix) - self.prefix = prefix - if SCons.Util.is_Dict(suffix): - suffix = CallableSelector(suffix) - self.env = env - self.single_source = single_source - if overrides.has_key('overrides'): - SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, - "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ - "\tspecify the items as keyword arguments to the Builder() call instead.") - overrides.update(overrides['overrides']) - del overrides['overrides'] - if overrides.has_key('scanner'): - SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, - "The \"scanner\" keyword to Builder() creation has been deprecated;\n" - "\tuse: source_scanner or target_scanner as appropriate.") - del overrides['scanner'] - self.overrides = overrides - - self.set_suffix(suffix) - self.set_src_suffix(src_suffix) - self.ensure_suffix = ensure_suffix - - self.target_factory = target_factory - self.source_factory = source_factory - self.target_scanner = target_scanner - self.source_scanner = source_scanner - - self.emitter = emitter - - # Optional Builder name should only be used for Builders - # that don't get attached to construction environments. - if name: - self.name = name - self.executor_kw = {} - if not chdir is _null: - self.executor_kw['chdir'] = chdir - self.is_explicit = is_explicit - - if src_builder is None: - src_builder = [] - elif not SCons.Util.is_List(src_builder): - src_builder = [ src_builder ] - self.src_builder = src_builder - - def __nonzero__(self): - raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead" - - def get_name(self, env): - """Attempts to get the name of the Builder. - - Look at the BUILDERS variable of env, expecting it to be a - dictionary containing this Builder, and return the key of the - dictionary. If there's no key, then return a directly-configured - name (if there is one) or the name of the class (by default).""" - - try: - index = env['BUILDERS'].values().index(self) - return env['BUILDERS'].keys()[index] - except (AttributeError, KeyError, TypeError, ValueError): - try: - return self.name - except AttributeError: - return str(self.__class__) - - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) - - def splitext(self, path, env=None): - if not env: - env = self.env - if env: - matchsuf = filter(lambda S,path=path: path[-len(S):] == S, - self.src_suffixes(env)) - if matchsuf: - suf = max(map(None, map(len, matchsuf), matchsuf))[1] - return [path[:-len(suf)], path[-len(suf):]] - return SCons.Util.splitext(path) - - def get_single_executor(self, env, tlist, slist, executor_kw): - if not self.action: - raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist)) - return self.action.get_executor(env or self.env, - [], # env already has overrides - tlist, - slist, - executor_kw) - - def get_multi_executor(self, env, tlist, slist, executor_kw): - try: - executor = tlist[0].get_executor(create = 0) - except (AttributeError, IndexError): - return self.get_single_executor(env, tlist, slist, executor_kw) - else: - executor.add_sources(slist) - return executor - - def _adjustixes(self, files, pre, suf, ensure_suffix=False): - if not files: - return [] - result = [] - if not SCons.Util.is_List(files): - files = [files] - - for f in files: - if SCons.Util.is_String(f): - f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) - result.append(f) - return result - - def _create_nodes(self, env, target = None, source = None): - """Create and return lists of target and source nodes. - """ - src_suf = self.get_src_suffix(env) - - target_factory = env.get_factory(self.target_factory) - source_factory = env.get_factory(self.source_factory) - - source = self._adjustixes(source, None, src_suf) - slist = env.arg2nodes(source, source_factory) - - pre = self.get_prefix(env, slist) - suf = self.get_suffix(env, slist) - - if target is None: - try: - t_from_s = slist[0].target_from_source - except AttributeError: - raise UserError("Do not know how to create a target from source `%s'" % slist[0]) - except IndexError: - tlist = [] - else: - splitext = lambda S,self=self,env=env: self.splitext(S,env) - tlist = [ t_from_s(pre, suf, splitext) ] - else: - target = self._adjustixes(target, pre, suf, self.ensure_suffix) - tlist = env.arg2nodes(target, target_factory, target=target, source=source) - - if self.emitter: - # The emitter is going to do str(node), but because we're - # being called *from* a builder invocation, the new targets - # don't yet have a builder set on them and will look like - # source files. Fool the emitter's str() calls by setting - # up a temporary builder on the new targets. - new_targets = [] - for t in tlist: - if not t.is_derived(): - t.builder_set(self) - new_targets.append(t) - - orig_tlist = tlist[:] - orig_slist = slist[:] - - target, source = self.emitter(target=tlist, source=slist, env=env) - - # Now delete the temporary builders that we attached to any - # new targets, so that _node_errors() doesn't do weird stuff - # to them because it thinks they already have builders. - for t in new_targets: - if t.builder is self: - # Only delete the temporary builder if the emitter - # didn't change it on us. - t.builder_set(None) - - # Have to call arg2nodes yet again, since it is legal for - # emitters to spit out strings as well as Node instances. - tlist = env.arg2nodes(target, target_factory, - target=orig_tlist, source=orig_slist) - slist = env.arg2nodes(source, source_factory, - target=orig_tlist, source=orig_slist) - - return tlist, slist - - def _execute(self, env, target, source, overwarn={}, executor_kw={}): - # We now assume that target and source are lists or None. - if self.src_builder: - source = self.src_builder_sources(env, source, overwarn) - - if self.single_source and len(source) > 1 and target is None: - result = [] - if target is None: target = [None]*len(source) - for tgt, src in zip(target, source): - if not tgt is None: tgt = [tgt] - if not src is None: src = [src] - result.extend(self._execute(env, tgt, src, overwarn)) - return SCons.Node.NodeList(result) - - overwarn.warn() - - tlist, slist = self._create_nodes(env, target, source) - - # Check for errors with the specified target/source lists. - _node_errors(self, env, tlist, slist) - - # The targets are fine, so find or make the appropriate Executor to - # build this particular list of targets from this particular list of - # sources. - if self.multi: - get_executor = self.get_multi_executor - else: - get_executor = self.get_single_executor - executor = get_executor(env, tlist, slist, executor_kw) - - # Now set up the relevant information in the target Nodes themselves. - for t in tlist: - t.cwd = env.fs.getcwd() - t.builder_set(self) - t.env_set(env) - t.add_source(slist) - t.set_executor(executor) - t.set_explicit(self.is_explicit) - - return SCons.Node.NodeList(tlist) - - def __call__(self, env, target=None, source=None, chdir=_null, **kw): - # We now assume that target and source are lists or None. - # The caller (typically Environment.BuilderWrapper) is - # responsible for converting any scalar values to lists. - if chdir is _null: - ekw = self.executor_kw - else: - ekw = self.executor_kw.copy() - ekw['chdir'] = chdir - if kw: - if kw.has_key('srcdir'): - def prependDirIfRelative(f, srcdir=kw['srcdir']): - import os.path - if SCons.Util.is_String(f) and not os.path.isabs(f): - f = os.path.join(srcdir, f) - return f - if not SCons.Util.is_List(source): - source = [source] - source = map(prependDirIfRelative, source) - del kw['srcdir'] - if self.overrides: - env_kw = self.overrides.copy() - env_kw.update(kw) - else: - env_kw = kw - else: - env_kw = self.overrides - env = env.Override(env_kw) - return self._execute(env, target, source, OverrideWarner(kw), ekw) - - def adjust_suffix(self, suff): - if suff and not suff[0] in [ '.', '_', '$' ]: - return '.' + suff - return suff - - def get_prefix(self, env, sources=[]): - prefix = self.prefix - if callable(prefix): - prefix = prefix(env, sources) - return env.subst(prefix) - - def set_suffix(self, suffix): - if not callable(suffix): - suffix = self.adjust_suffix(suffix) - self.suffix = suffix - - def get_suffix(self, env, sources=[]): - suffix = self.suffix - if callable(suffix): - suffix = suffix(env, sources) - return env.subst(suffix) - - def set_src_suffix(self, src_suffix): - if not src_suffix: - src_suffix = [] - elif not SCons.Util.is_List(src_suffix): - src_suffix = [ src_suffix ] - adjust = lambda suf, s=self: \ - callable(suf) and suf or s.adjust_suffix(suf) - self.src_suffix = map(adjust, src_suffix) - - def get_src_suffix(self, env): - """Get the first src_suffix in the list of src_suffixes.""" - ret = self.src_suffixes(env) - if not ret: - return '' - return ret[0] - - def add_emitter(self, suffix, emitter): - """Add a suffix-emitter mapping to this Builder. - - This assumes that emitter has been initialized with an - appropriate dictionary type, and will throw a TypeError if - not, so the caller is responsible for knowing that this is an - appropriate method to call for the Builder in question. - """ - self.emitter[suffix] = emitter - - def add_src_builder(self, builder): - """ - Add a new Builder to the list of src_builders. - - This requires wiping out cached values so that the computed - lists of source suffixes get re-calculated. - """ - self._memo = {} - self.src_builder.append(builder) - - def _get_sdict(self, env): - """ - Returns a dictionary mapping all of the source suffixes of all - src_builders of this Builder to the underlying Builder that - should be called first. - - This dictionary is used for each target specified, so we save a - lot of extra computation by memoizing it for each construction - environment. - - Note that this is re-computed each time, not cached, because there - might be changes to one of our source Builders (or one of their - source Builders, and so on, and so on...) that we can't "see." - - The underlying methods we call cache their computed values, - though, so we hope repeatedly aggregating them into a dictionary - like this won't be too big a hit. We may need to look for a - better way to do this if performance data show this has turned - into a significant bottleneck. - """ - sdict = {} - for bld in self.get_src_builders(env): - for suf in bld.src_suffixes(env): - sdict[suf] = bld - return sdict - - def src_builder_sources(self, env, source, overwarn={}): - sdict = self._get_sdict(env) - - src_suffixes = self.src_suffixes(env) - - lengths = list(set(map(len, src_suffixes))) - - def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): - node_suffixes = map(lambda l, n=name: n[-l:], lengths) - for suf in src_suffixes: - if suf in node_suffixes: - return suf - return None - - result = [] - for s in SCons.Util.flatten(source): - if SCons.Util.is_String(s): - match_suffix = match_src_suffix(env.subst(s)) - if not match_suffix and not '.' in s: - src_suf = self.get_src_suffix(env) - s = self._adjustixes(s, None, src_suf)[0] - else: - match_suffix = match_src_suffix(s.name) - if match_suffix: - try: - bld = sdict[match_suffix] - except KeyError: - result.append(s) - else: - tlist = bld._execute(env, None, [s], overwarn) - # If the subsidiary Builder returned more than one - # target, then filter out any sources that this - # Builder isn't capable of building. - if len(tlist) > 1: - mss = lambda t, m=match_src_suffix: m(t.name) - tlist = filter(mss, tlist) - result.extend(tlist) - else: - result.append(s) - - source_factory = env.get_factory(self.source_factory) - - return env.arg2nodes(result, source_factory) - - def _get_src_builders_key(self, env): - return id(env) - - memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) - - def get_src_builders(self, env): - """ - Returns the list of source Builders for this Builder. - - This exists mainly to look up Builders referenced as - strings in the 'BUILDER' variable of the construction - environment and cache the result. - """ - memo_key = id(env) - try: - memo_dict = self._memo['get_src_builders'] - except KeyError: - memo_dict = {} - self._memo['get_src_builders'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - builders = [] - for bld in self.src_builder: - if SCons.Util.is_String(bld): - try: - bld = env['BUILDERS'][bld] - except KeyError: - continue - builders.append(bld) - - memo_dict[memo_key] = builders - return builders - - def _subst_src_suffixes_key(self, env): - return id(env) - - memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) - - def subst_src_suffixes(self, env): - """ - The suffix list may contain construction variable expansions, - so we have to evaluate the individual strings. To avoid doing - this over and over, we memoize the results for each construction - environment. - """ - memo_key = id(env) - try: - memo_dict = self._memo['subst_src_suffixes'] - except KeyError: - memo_dict = {} - self._memo['subst_src_suffixes'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix) - memo_dict[memo_key] = suffixes - return suffixes - - def src_suffixes(self, env): - """ - Returns the list of source suffixes for all src_builders of this - Builder. - - This is essentially a recursive descent of the src_builder "tree." - (This value isn't cached because there may be changes in a - src_builder many levels deep that we can't see.) - """ - sdict = {} - suffixes = self.subst_src_suffixes(env) - for s in suffixes: - sdict[s] = 1 - for builder in self.get_src_builders(env): - for s in builder.src_suffixes(env): - if not sdict.has_key(s): - sdict[s] = 1 - suffixes.append(s) - return suffixes - -class CompositeBuilder(SCons.Util.Proxy): - """A Builder Proxy whose main purpose is to always have - a DictCmdGenerator as its action, and to provide access - to the DictCmdGenerator's add_action() method. - """ - - def __init__(self, builder, cmdgen): - if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') - SCons.Util.Proxy.__init__(self, builder) - - # cmdgen should always be an instance of DictCmdGenerator. - self.cmdgen = cmdgen - self.builder = builder - - def add_action(self, suffix, action): - self.cmdgen.add_action(suffix, action) - self.set_src_suffix(self.cmdgen.src_suffixes()) diff --git a/tools/scons/scons-local-1.2.0/SCons/CacheDir.py b/tools/scons/scons-local-1.2.0/SCons/CacheDir.py deleted file mode 100644 index 6eb6f173ba..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/CacheDir.py +++ /dev/null @@ -1,217 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/CacheDir.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """ -CacheDir support -""" - -import os.path -import stat -import string -import sys - -import SCons.Action - -cache_enabled = True -cache_debug = False -cache_force = False -cache_show = False - -def CacheRetrieveFunc(target, source, env): - t = target[0] - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if not fs.exists(cachefile): - cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) - return 1 - cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) - if SCons.Action.execute_actions: - if fs.islink(cachefile): - fs.symlink(fs.readlink(cachefile), t.path) - else: - env.copy_from_cache(cachefile, t.path) - st = fs.stat(cachefile) - fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - return 0 - -def CacheRetrieveString(target, source, env): - t = target[0] - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if t.fs.exists(cachefile): - return "Retrieved `%s' from cache" % t.path - return None - -CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) - -CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) - -def CachePushFunc(target, source, env): - t = target[0] - if t.nocache: - return - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if fs.exists(cachefile): - # Don't bother copying it if it's already there. Note that - # usually this "shouldn't happen" because if the file already - # existed in cache, we'd have retrieved the file from there, - # not built it. This can happen, though, in a race, if some - # other person running the same build pushes their copy to - # the cache after we decide we need to build it but before our - # build completes. - cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) - return - - cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) - - tempfile = cachefile+'.tmp'+str(os.getpid()) - errfmt = "Unable to copy %s to cache. Cache file is %s" - - if not fs.isdir(cachedir): - try: - fs.makedirs(cachedir) - except EnvironmentError: - # We may have received an exception because another process - # has beaten us creating the directory. - if not fs.isdir(cachedir): - msg = errfmt % (str(target), cachefile) - raise SCons.Errors.EnvironmentError, msg - - try: - if fs.islink(t.path): - fs.symlink(fs.readlink(t.path), tempfile) - else: - fs.copy2(t.path, tempfile) - fs.rename(tempfile, cachefile) - st = fs.stat(t.path) - fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - except EnvironmentError: - # It's possible someone else tried writing the file at the - # same time we did, or else that there was some problem like - # the CacheDir being on a separate file system that's full. - # In any case, inability to push a file to cache doesn't affect - # the correctness of the build, so just print a warning. - msg = errfmt % (str(target), cachefile) - SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) - -CachePush = SCons.Action.Action(CachePushFunc, None) - -class CacheDir: - - def __init__(self, path): - try: - import hashlib - except ImportError: - msg = "No hashlib or MD5 module available, CacheDir() not supported" - SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) - self.path = None - else: - self.path = path - self.current_cache_debug = None - self.debugFP = None - - def CacheDebug(self, fmt, target, cachefile): - if cache_debug != self.current_cache_debug: - if cache_debug == '-': - self.debugFP = sys.stdout - elif cache_debug: - self.debugFP = open(cache_debug, 'w') - else: - self.debugFP = None - self.current_cache_debug = cache_debug - if self.debugFP: - self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) - - def is_enabled(self): - return (cache_enabled and not self.path is None) - - def cachepath(self, node): - """ - """ - if not self.is_enabled(): - return None, None - - sig = node.get_cachedir_bsig() - subdir = string.upper(sig[0]) - dir = os.path.join(self.path, subdir) - return dir, os.path.join(dir, sig) - - def retrieve(self, node): - """ - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Note that there's a special trick here with the execute flag - (one that's not normally done for other actions). Basically - if the user requested a no_exec (-n) build, then - SCons.Action.execute_actions is set to 0 and when any action - is called, it does its showing but then just returns zero - instead of actually calling the action execution operation. - The problem for caching is that if the file does NOT exist in - cache then the CacheRetrieveString won't return anything to - show for the task, but the Action.__call__ won't call - CacheRetrieveFunc; instead it just returns zero, which makes - the code below think that the file *was* successfully - retrieved from the cache, therefore it doesn't do any - subsequent building. However, the CacheRetrieveString didn't - print anything because it didn't actually exist in the cache, - and no more build actions will be performed, so the user just - sees nothing. The fix is to tell Action.__call__ to always - execute the CacheRetrieveFunc and then have the latter - explicitly check SCons.Action.execute_actions itself. - """ - if not self.is_enabled(): - return False - - retrieved = False - - if cache_show: - if CacheRetrieveSilent(node, [], node.get_build_env(), execute=1) == 0: - node.build(presub=0, execute=0) - retrieved = 1 - else: - if CacheRetrieve(node, [], node.get_build_env(), execute=1) == 0: - retrieved = 1 - if retrieved: - # Record build signature information, but don't - # push it out to cache. (We just got it from there!) - node.set_state(SCons.Node.executed) - SCons.Node.Node.built(node) - - return retrieved - - def push(self, node): - if not self.is_enabled(): - return - return CachePush(node, [], node.get_build_env()) - - def push_if_forced(self, node): - if cache_force: - return self.push(node) diff --git a/tools/scons/scons-local-1.2.0/SCons/Conftest.py b/tools/scons/scons-local-1.2.0/SCons/Conftest.py deleted file mode 100644 index ba7dbf1361..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Conftest.py +++ /dev/null @@ -1,778 +0,0 @@ -"""SCons.Conftest - -Autoconf-like configuration support; low level implementation of tests. -""" - -# -# Copyright (c) 2003 Stichting NLnet Labs -# Copyright (c) 2001, 2002, 2003 Steven Knight -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -# -# The purpose of this module is to define how a check is to be performed. -# Use one of the Check...() functions below. -# - -# -# A context class is used that defines functions for carrying out the tests, -# logging and messages. The following methods and members must be present: -# -# context.Display(msg) Function called to print messages that are normally -# displayed for the user. Newlines are explicitly used. -# The text should also be written to the logfile! -# -# context.Log(msg) Function called to write to a log file. -# -# context.BuildProg(text, ext) -# Function called to build a program, using "ext" for the -# file extention. Must return an empty string for -# success, an error message for failure. -# For reliable test results building should be done just -# like an actual program would be build, using the same -# command and arguments (including configure results so -# far). -# -# context.CompileProg(text, ext) -# Function called to compile a program, using "ext" for -# the file extention. Must return an empty string for -# success, an error message for failure. -# For reliable test results compiling should be done just -# like an actual source file would be compiled, using the -# same command and arguments (including configure results -# so far). -# -# context.AppendLIBS(lib_name_list) -# Append "lib_name_list" to the value of LIBS. -# "lib_namelist" is a list of strings. -# Return the value of LIBS before changing it (any type -# can be used, it is passed to SetLIBS() later. -# -# context.SetLIBS(value) -# Set LIBS to "value". The type of "value" is what -# AppendLIBS() returned. -# Return the value of LIBS before changing it (any type -# can be used, it is passed to SetLIBS() later. -# -# context.headerfilename -# Name of file to append configure results to, usually -# "confdefs.h". -# The file must not exist or be empty when starting. -# Empty or None to skip this (some tests will not work!). -# -# context.config_h (may be missing). If present, must be a string, which -# will be filled with the contents of a config_h file. -# -# context.vardict Dictionary holding variables used for the tests and -# stores results from the tests, used for the build -# commands. -# Normally contains "CC", "LIBS", "CPPFLAGS", etc. -# -# context.havedict Dictionary holding results from the tests that are to -# be used inside a program. -# Names often start with "HAVE_". These are zero -# (feature not present) or one (feature present). Other -# variables may have any value, e.g., "PERLVERSION" can -# be a number and "SYSTEMNAME" a string. -# - -import re -import string -from types import IntType - -# -# PUBLIC VARIABLES -# - -LogInputFiles = 1 # Set that to log the input files in case of a failed test -LogErrorMessages = 1 # Set that to log Conftest-generated error messages - -# -# PUBLIC FUNCTIONS -# - -# Generic remarks: -# - When a language is specified which is not supported the test fails. The -# message is a bit different, because not all the arguments for the normal -# message are available yet (chicken-egg problem). - - -def CheckBuilder(context, text = None, language = None): - """ - Configure check to see if the compiler works. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - "text" may be used to specify the code to be build. - Returns an empty string for success, an error message for failure. - """ - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("%s\n" % msg) - return msg - - if not text: - text = """ -int main() { - return 0; -} -""" - - context.Display("Checking if building a %s file works... " % lang) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, None, text) - return ret - -def CheckCC(context): - """ - Configure check for a working C compiler. - - This checks whether the C compiler, as defined in the $CC construction - variable, can compile a C source file. It uses the current $CCCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the C compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'CC', text, 'C') - _YesNoResult(context, ret, None, text) - return ret - -def CheckSHCC(context): - """ - Configure check for a working shared C compiler. - - This checks whether the C compiler, as defined in the $SHCC construction - variable, can compile a C source file. It uses the current $SHCCCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the (shared) C compiler works") - text = """ -int foo() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True) - _YesNoResult(context, ret, None, text) - return ret - -def CheckCXX(context): - """ - Configure check for a working CXX compiler. - - This checks whether the CXX compiler, as defined in the $CXX construction - variable, can compile a CXX source file. It uses the current $CXXCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the C++ compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'CXX', text, 'C++') - _YesNoResult(context, ret, None, text) - return ret - -def CheckSHCXX(context): - """ - Configure check for a working shared CXX compiler. - - This checks whether the CXX compiler, as defined in the $SHCXX construction - variable, can compile a CXX source file. It uses the current $SHCXXCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the (shared) C++ compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True) - _YesNoResult(context, ret, None, text) - return ret - -def _check_empty_program(context, comp, text, language, use_shared = False): - """Return 0 on success, 1 otherwise.""" - if not context.env.has_key(comp) or not context.env[comp]: - # The compiler construction variable is not set or empty - return 1 - - lang, suffix, msg = _lang2suffix(language) - if msg: - return 1 - - if use_shared: - return context.CompileSharedObject(text, suffix) - else: - return context.CompileProg(text, suffix) - - -def CheckFunc(context, function_name, header = None, language = None): - """ - Configure check for a function "function_name". - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Optional "header" can be defined to define a function prototype, include a - header file or anything else that comes before main(). - Sets HAVE_function_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - - # Remarks from autoconf: - # - Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h> - # which includes <sys/select.h> which contains a prototype for select. - # Similarly for bzero. - # - assert.h is included to define __stub macros and hopefully few - # prototypes, which can conflict with char $1(); below. - # - Override any gcc2 internal prototype to avoid an error. - # - We use char for the function declaration because int might match the - # return type of a gcc2 builtin and then its argument prototype would - # still apply. - # - The GNU C library defines this for functions which it implements to - # always fail with ENOSYS. Some functions are actually named something - # starting with __ and the normal name is an alias. - - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = """ -#ifdef __cplusplus -extern "C" -#endif -char %s();""" % function_name - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) - return msg - - text = """ -%(include)s -#include <assert.h> -%(hdr)s - -int main() { -#if defined (__stub_%(name)s) || defined (__stub___%(name)s) - fail fail fail -#else - %(name)s(); -#endif - - return 0; -} -""" % { 'name': function_name, - 'include': includetext, - 'hdr': header } - - context.Display("Checking for %s function %s()... " % (lang, function_name)) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + function_name, text, - "Define to 1 if the system has the function `%s'." %\ - function_name) - return ret - - -def CheckHeader(context, header_name, header = None, language = None, - include_quotes = None): - """ - Configure check for a C or C++ header file "header_name". - Optional "header" can be defined to do something before including the - header file (unusual, supported for consistency). - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Sets HAVE_header_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS and $CPPFLAGS are set correctly. - Returns an empty string for success, an error message for failure. - """ - # Why compile the program instead of just running the preprocessor? - # It is possible that the header file exists, but actually using it may - # fail (e.g., because it depends on other header files). Thus this test is - # more strict. It may require using the "header" argument. - # - # Use <> by default, because the check is normally used for system header - # files. SCons passes '""' to overrule this. - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"\n' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for header file %s: %s\n" - % (header_name, msg)) - return msg - - if not include_quotes: - include_quotes = "<>" - - text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, - include_quotes[0], header_name, include_quotes[1]) - - context.Display("Checking for %s header file %s... " % (lang, header_name)) - ret = context.CompileProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + header_name, text, - "Define to 1 if you have the <%s> header file." % header_name) - return ret - - -def CheckType(context, type_name, fallback = None, - header = None, language = None): - """ - Configure check for a C or C++ type "type_name". - Optional "header" can be defined to include a header file. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Sets HAVE_type_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) - return msg - - # Remarks from autoconf about this test: - # - Grepping for the type in include files is not reliable (grep isn't - # portable anyway). - # - Using "TYPE my_var;" doesn't work for const qualified types in C++. - # Adding an initializer is not valid for some C++ classes. - # - Using the type as parameter to a function either fails for K&$ C or for - # C++. - # - Using "TYPE *my_var;" is valid in C for some types that are not - # declared (struct something). - # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. - # - Using the previous two together works reliably. - text = """ -%(include)s -%(header)s - -int main() { - if ((%(name)s *) 0) - return 0; - if (sizeof (%(name)s)) - return 0; -} -""" % { 'include': includetext, - 'header': header, - 'name': type_name } - - context.Display("Checking for %s type %s... " % (lang, type_name)) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + type_name, text, - "Define to 1 if the system has the type `%s'." % type_name) - if ret and fallback and context.headerfilename: - f = open(context.headerfilename, "a") - f.write("typedef %s %s;\n" % (fallback, type_name)) - f.close() - - return ret - -def CheckTypeSize(context, type_name, header = None, language = None, expect = None): - """This check can be used to get the size of a given type, or to check whether - the type is of expected size. - - Arguments: - - type : str - the type to check - - includes : sequence - list of headers to include in the test code before testing the type - - language : str - 'C' or 'C++' - - expect : int - if given, will test wether the type has the given number of bytes. - If not given, will automatically find the size. - - Returns: - status : int - 0 if the check failed, or the found size of the type if the check succeeded.""" - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) - return msg - - src = includetext + header - if not expect is None: - # Only check if the given size is the right one - context.Display('Checking %s is %d bytes... ' % (type_name, expect)) - - # test code taken from autoconf: this is a pretty clever hack to find that - # a type is of a given size using only compilation. This speeds things up - # quite a bit compared to straightforward code using TryRun - src = src + r""" -typedef %s scons_check_type; - -int main() -{ - static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; - test_array[0] = 0; - - return 0; -} -""" - - st = context.CompileProg(src % (type_name, expect), suffix) - if not st: - context.Display("yes\n") - _Have(context, "SIZEOF_%s" % type_name, expect, - "The size of `%s', as computed by sizeof." % type_name) - return expect - else: - context.Display("no\n") - _LogFailed(context, src, st) - return 0 - else: - # Only check if the given size is the right one - context.Message('Checking size of %s ... ' % type_name) - - # We have to be careful with the program we wish to test here since - # compilation will be attempted using the current environment's flags. - # So make sure that the program will compile without any warning. For - # example using: 'int main(int argc, char** argv)' will fail with the - # '-Wall -Werror' flags since the variables argc and argv would not be - # used in the program... - # - src = src + """ -#include <stdlib.h> -#include <stdio.h> -int main() { - printf("%d", (int)sizeof(""" + type_name + """)); - return 0; -} - """ - st, out = context.RunProg(src, suffix) - try: - size = int(out) - except ValueError: - # If cannot convert output of test prog to an integer (the size), - # something went wront, so just fail - st = 1 - size = 0 - - if not st: - context.Display("yes\n") - _Have(context, "SIZEOF_%s" % type_name, size, - "The size of `%s', as computed by sizeof." % type_name) - return size - else: - context.Display("no\n") - _LogFailed(context, src, st) - return 0 - - return 0 - -def CheckDeclaration(context, symbol, includes = None, language = None): - """Checks whether symbol is declared. - - Use the same test as autoconf, that is test whether the symbol is defined - as a macro or can be used as an r-value. - - Arguments: - symbol : str - the symbol to check - includes : str - Optional "header" can be defined to include a header file. - language : str - only C and C++ supported. - - Returns: - status : bool - True if the check failed, False if succeeded.""" - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - - if not includes: - includes = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for declaration %s: %s\n" % (type_name, msg)) - return msg - - src = includetext + includes - context.Display('Checking whether %s is declared... ' % symbol) - - src = src + r""" -int main() -{ -#ifndef %s - (void) %s; -#endif - ; - return 0; -} -""" % (symbol, symbol) - - st = context.CompileProg(src, suffix) - _YesNoResult(context, st, "HAVE_DECL_" + symbol, src, - "Set to 1 if %s is defined." % symbol) - return st - -def CheckLib(context, libs, func_name = None, header = None, - extra_libs = None, call = None, language = None, autoadd = 1): - """ - Configure check for a C or C++ libraries "libs". Searches through - the list of libraries, until one is found where the test succeeds. - Tests if "func_name" or "call" exists in the library. Note: if it exists - in another library the test succeeds anyway! - Optional "header" can be defined to include a header file. If not given a - default prototype for "func_name" is added. - Optional "extra_libs" is a list of library names to be added after - "lib_name" in the build command. To be used for libraries that "lib_name" - depends on. - Optional "call" replaces the call to "func_name" in the test code. It must - consist of complete C statements, including a trailing ";". - Both "func_name" and "call" arguments are optional, and in that case, just - linking against the libs is tested. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - text = """ -%s -%s""" % (includetext, header) - - # Add a function declaration if needed. - if func_name and func_name != "main": - if not header: - text = text + """ -#ifdef __cplusplus -extern "C" -#endif -char %s(); -""" % func_name - - # The actual test code. - if not call: - call = "%s();" % func_name - - # if no function to test, leave main() blank - text = text + """ -int -main() { - %s -return 0; -} -""" % (call or "") - - if call: - i = string.find(call, "\n") - if i > 0: - calltext = call[:i] + ".." - elif call[-1] == ';': - calltext = call[:-1] - else: - calltext = call - - for lib_name in libs: - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) - return msg - - # if a function was specified to run in main(), say it - if call: - context.Display("Checking for %s in %s library %s... " - % (calltext, lang, lib_name)) - # otherwise, just say the name of library and language - else: - context.Display("Checking for %s library %s... " - % (lang, lib_name)) - - if lib_name: - l = [ lib_name ] - if extra_libs: - l.extend(extra_libs) - oldLIBS = context.AppendLIBS(l) - sym = "HAVE_LIB" + lib_name - else: - oldLIBS = -1 - sym = None - - ret = context.BuildProg(text, suffix) - - _YesNoResult(context, ret, sym, text, - "Define to 1 if you have the `%s' library." % lib_name) - if oldLIBS != -1 and (ret or not autoadd): - context.SetLIBS(oldLIBS) - - if not ret: - return ret - - return ret - -# -# END OF PUBLIC FUNCTIONS -# - -def _YesNoResult(context, ret, key, text, comment = None): - """ - Handle the result of a test with a "yes" or "no" result. - "ret" is the return value: empty if OK, error message when not. - "key" is the name of the symbol to be defined (HAVE_foo). - "text" is the source code of the program used for testing. - "comment" is the C comment to add above the line defining the symbol (the - comment is automatically put inside a /* */). If None, no comment is added. - """ - if key: - _Have(context, key, not ret, comment) - if ret: - context.Display("no\n") - _LogFailed(context, text, ret) - else: - context.Display("yes\n") - - -def _Have(context, key, have, comment = None): - """ - Store result of a test in context.havedict and context.headerfilename. - "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- - alphanumerics are replaced by an underscore. - The value of "have" can be: - 1 - Feature is defined, add "#define key". - 0 - Feature is not defined, add "/* #undef key */". - Adding "undef" is what autoconf does. Not useful for the - compiler, but it shows that the test was done. - number - Feature is defined to this number "#define key have". - Doesn't work for 0 or 1, use a string then. - string - Feature is defined to this string "#define key have". - Give "have" as is should appear in the header file, include quotes - when desired and escape special characters! - """ - key_up = string.upper(key) - key_up = re.sub('[^A-Z0-9_]', '_', key_up) - context.havedict[key_up] = have - if have == 1: - line = "#define %s 1\n" % key_up - elif have == 0: - line = "/* #undef %s */\n" % key_up - elif type(have) == IntType: - line = "#define %s %d\n" % (key_up, have) - else: - line = "#define %s %s\n" % (key_up, str(have)) - - if comment is not None: - lines = "\n/* %s */\n" % comment + line - else: - lines = "\n" + line - - if context.headerfilename: - f = open(context.headerfilename, "a") - f.write(lines) - f.close() - elif hasattr(context,'config_h'): - context.config_h = context.config_h + lines - - -def _LogFailed(context, text, msg): - """ - Write to the log about a failed program. - Add line numbers, so that error messages can be understood. - """ - if LogInputFiles: - context.Log("Failed program was:\n") - lines = string.split(text, '\n') - if len(lines) and lines[-1] == '': - lines = lines[:-1] # remove trailing empty line - n = 1 - for line in lines: - context.Log("%d: %s\n" % (n, line)) - n = n + 1 - if LogErrorMessages: - context.Log("Error message: %s\n" % msg) - - -def _lang2suffix(lang): - """ - Convert a language name to a suffix. - When "lang" is empty or None C is assumed. - Returns a tuple (lang, suffix, None) when it works. - For an unrecognized language returns (None, None, msg). - Where: - lang = the unified language name - suffix = the suffix, including the leading dot - msg = an error message - """ - if not lang or lang in ["C", "c"]: - return ("C", ".c", None) - if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: - return ("C++", ".cpp", None) - - return None, None, "Unsupported language: %s" % lang - - -# vim: set sw=4 et sts=4 tw=79 fo+=l: diff --git a/tools/scons/scons-local-1.2.0/SCons/Debug.py b/tools/scons/scons-local-1.2.0/SCons/Debug.py deleted file mode 100644 index c6485b6fa3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Debug.py +++ /dev/null @@ -1,216 +0,0 @@ -"""SCons.Debug - -Code for debugging SCons internal things. Not everything here is -guaranteed to work all the way back to Python 1.5.2, and shouldn't be -needed by most users. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Debug.py 3842 2008/12/20 22:59:52 scons" - -import os -import string -import sys - -# Recipe 14.10 from the Python Cookbook. -try: - import weakref -except ImportError: - def logInstanceCreation(instance, name=None): - pass -else: - def logInstanceCreation(instance, name=None): - if name is None: - name = instance.__class__.__name__ - if not tracked_classes.has_key(name): - tracked_classes[name] = [] - tracked_classes[name].append(weakref.ref(instance)) - - - -tracked_classes = {} - -def string_to_classes(s): - if s == '*': - c = tracked_classes.keys() - c.sort() - return c - else: - return string.split(s) - -def fetchLoggedInstances(classes="*"): - classnames = string_to_classes(classes) - return map(lambda cn: (cn, len(tracked_classes[cn])), classnames) - -def countLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) - -def listLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write('\n%s:\n' % classname) - for ref in tracked_classes[classname]: - obj = ref() - if obj is not None: - file.write(' %s\n' % repr(obj)) - -def dumpLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write('\n%s:\n' % classname) - for ref in tracked_classes[classname]: - obj = ref() - if obj is not None: - file.write(' %s:\n' % obj) - for key, value in obj.__dict__.items(): - file.write(' %20s : %s\n' % (key, value)) - - - -if sys.platform[:5] == "linux": - # Linux doesn't actually support memory usage stats from getrusage(). - def memory(): - mstr = open('/proc/self/stat').read() - mstr = string.split(mstr)[22] - return int(mstr) -else: - try: - import resource - except ImportError: - try: - import win32process - import win32api - except ImportError: - def memory(): - return 0 - else: - def memory(): - process_handle = win32api.GetCurrentProcess() - memory_info = win32process.GetProcessMemoryInfo( process_handle ) - return memory_info['PeakWorkingSetSize'] - else: - def memory(): - res = resource.getrusage(resource.RUSAGE_SELF) - return res[4] - -# returns caller's stack -def caller_stack(*backlist): - import traceback - if not backlist: - backlist = [0] - result = [] - for back in backlist: - tb = traceback.extract_stack(limit=3+back) - key = tb[0][:3] - result.append('%s:%d(%s)' % func_shorten(key)) - return result - -caller_bases = {} -caller_dicts = {} - -# trace a caller's stack -def caller_trace(back=0): - import traceback - tb = traceback.extract_stack(limit=3+back) - tb.reverse() - callee = tb[1][:3] - caller_bases[callee] = caller_bases.get(callee, 0) + 1 - for caller in tb[2:]: - caller = callee + caller[:3] - try: - entry = caller_dicts[callee] - except KeyError: - caller_dicts[callee] = entry = {} - entry[caller] = entry.get(caller, 0) + 1 - callee = caller - -# print a single caller and its callers, if any -def _dump_one_caller(key, file, level=0): - l = [] - for c,v in caller_dicts[key].items(): - l.append((-v,c)) - l.sort() - leader = ' '*level - for v,c in l: - file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) - if caller_dicts.has_key(c): - _dump_one_caller(c, file, level+1) - -# print each call tree -def dump_caller_counts(file=sys.stdout): - keys = caller_bases.keys() - keys.sort() - for k in keys: - file.write("Callers of %s:%d(%s), %d calls:\n" - % (func_shorten(k) + (caller_bases[k],))) - _dump_one_caller(k, file) - -shorten_list = [ - ( '/scons/SCons/', 1), - ( '/src/engine/SCons/', 1), - ( '/usr/lib/python', 0), -] - -if os.sep != '/': - def platformize(t): - return (string.replace(t[0], '/', os.sep), t[1]) - shorten_list = map(platformize, shorten_list) - del platformize - -def func_shorten(func_tuple): - f = func_tuple[0] - for t in shorten_list: - i = string.find(f, t[0]) - if i >= 0: - if t[1]: - i = i + len(t[0]) - return (f[i:],)+func_tuple[1:] - return func_tuple - - -TraceFP = {} -if sys.platform == 'win32': - TraceDefault = 'con' -else: - TraceDefault = '/dev/tty' - -def Trace(msg, file=None, mode='w'): - """Write a trace message to a file. Whenever a file is specified, - it becomes the default for the next call to Trace().""" - global TraceDefault - if file is None: - file = TraceDefault - else: - TraceDefault = file - try: - fp = TraceFP[file] - except KeyError: - try: - fp = TraceFP[file] = open(file, mode) - except TypeError: - # Assume we were passed an open file pointer. - fp = file - fp.write(msg) - fp.flush() diff --git a/tools/scons/scons-local-1.2.0/SCons/Defaults.py b/tools/scons/scons-local-1.2.0/SCons/Defaults.py deleted file mode 100644 index fc0ab26ba3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Defaults.py +++ /dev/null @@ -1,463 +0,0 @@ -"""SCons.Defaults - -Builders and other things for the local site. Here's where we'll -duplicate the functionality of autoconf until we move it into the -installation procedure or use something like qmconf. - -The code that reads the registry to find MSVC components was borrowed -from distutils.msvccompiler. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Defaults.py 3842 2008/12/20 22:59:52 scons" - - - -import os -import os.path -import shutil -import stat -import string -import time -import types -import sys - -import SCons.Action -import SCons.Builder -import SCons.CacheDir -import SCons.Environment -import SCons.PathList -import SCons.Subst -import SCons.Tool - -# A placeholder for a default Environment (for fetching source files -# from source code management systems and the like). This must be -# initialized later, after the top-level directory is set by the calling -# interface. -_default_env = None - -# Lazily instantiate the default environment so the overhead of creating -# it doesn't apply when it's not needed. -def _fetch_DefaultEnvironment(*args, **kw): - """ - Returns the already-created default construction environment. - """ - global _default_env - return _default_env - -def DefaultEnvironment(*args, **kw): - """ - Initial public entry point for creating the default construction - Environment. - - After creating the environment, we overwrite our name - (DefaultEnvironment) with the _fetch_DefaultEnvironment() function, - which more efficiently returns the initialized default construction - environment without checking for its existence. - - (This function still exists with its _default_check because someone - else (*cough* Script/__init__.py *cough*) may keep a reference - to this function. So we can't use the fully functional idiom of - having the name originally be a something that *only* creates the - construction environment and then overwrites the name.) - """ - global _default_env - if not _default_env: - import SCons.Util - _default_env = apply(SCons.Environment.Environment, args, kw) - if SCons.Util.md5: - _default_env.Decider('MD5') - else: - _default_env.Decider('timestamp-match') - global DefaultEnvironment - DefaultEnvironment = _fetch_DefaultEnvironment - _default_env._CacheDir_path = None - return _default_env - -# Emitters for setting the shared attribute on object files, -# and an action for checking that all of the source files -# going into a shared library are, in fact, shared. -def StaticObjectEmitter(target, source, env): - for tgt in target: - tgt.attributes.shared = None - return (target, source) - -def SharedObjectEmitter(target, source, env): - for tgt in target: - tgt.attributes.shared = 1 - return (target, source) - -def SharedFlagChecker(source, target, env): - same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') - if same == '0' or same == '' or same == 'False': - for src in source: - try: - shared = src.attributes.shared - except AttributeError: - shared = None - if not shared: - raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0]) - -SharedCheck = SCons.Action.Action(SharedFlagChecker, None) - -# Some people were using these variable name before we made -# SourceFileScanner part of the public interface. Don't break their -# SConscript files until we've given them some fair warning and a -# transition period. -CScan = SCons.Tool.CScanner -DScan = SCons.Tool.DScanner -LaTeXScan = SCons.Tool.LaTeXScanner -ObjSourceScan = SCons.Tool.SourceFileScanner -ProgScan = SCons.Tool.ProgramScanner - -# These aren't really tool scanners, so they don't quite belong with -# the rest of those in Tool/__init__.py, but I'm not sure where else -# they should go. Leave them here for now. -import SCons.Scanner.Dir -DirScanner = SCons.Scanner.Dir.DirScanner() -DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() - -# Actions for common languages. -CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") -ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") -CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") -ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") - -ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") -ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") - -LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") -ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") - -LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") - -# Common tasks that we allow users to perform in platform-independent -# ways by creating ActionFactory instances. -ActionFactory = SCons.Action.ActionFactory - -def get_paths_str(dest): - # If dest is a list, we need to manually call str() on each element - if SCons.Util.is_List(dest): - elem_strs = [] - for element in dest: - elem_strs.append('"' + str(element) + '"') - return '[' + string.join(elem_strs, ', ') + ']' - else: - return '"' + str(dest) + '"' - -def chmod_func(dest, mode): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for element in dest: - os.chmod(str(element), mode) - -def chmod_strfunc(dest, mode): - return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) - -Chmod = ActionFactory(chmod_func, chmod_strfunc) - -def copy_func(dest, src): - SCons.Node.FS.invalidate_node_memos(dest) - if SCons.Util.is_List(src) and os.path.isdir(dest): - for file in src: - shutil.copy2(file, dest) - return 0 - elif os.path.isfile(src): - return shutil.copy2(src, dest) - else: - return shutil.copytree(src, dest, 1) - -Copy = ActionFactory(copy_func, - lambda dest, src: 'Copy("%s", "%s")' % (dest, src), - convert=str) - -def delete_func(dest, must_exist=0): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for entry in dest: - entry = str(entry) - if not must_exist and not os.path.exists(entry): - continue - if not os.path.exists(entry) or os.path.isfile(entry): - os.unlink(entry) - continue - else: - shutil.rmtree(entry, 1) - continue - -def delete_strfunc(dest, must_exist=0): - return 'Delete(%s)' % get_paths_str(dest) - -Delete = ActionFactory(delete_func, delete_strfunc) - -def mkdir_func(dest): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for entry in dest: - os.makedirs(str(entry)) - -Mkdir = ActionFactory(mkdir_func, - lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) - -def move_func(dest, src): - SCons.Node.FS.invalidate_node_memos(dest) - SCons.Node.FS.invalidate_node_memos(src) - os.rename(src, dest) - -Move = ActionFactory(move_func, - lambda dest, src: 'Move("%s", "%s")' % (dest, src), - convert=str) - -def touch_func(dest): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for file in dest: - file = str(file) - mtime = int(time.time()) - if os.path.exists(file): - atime = os.path.getatime(file) - else: - open(file, 'w') - atime = mtime - os.utime(file, (atime, mtime)) - -Touch = ActionFactory(touch_func, - lambda file: 'Touch(%s)' % get_paths_str(file)) - -# Internal utility functions - -def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): - """ - Creates a new list from 'list' by first interpolating each element - in the list using the 'env' dictionary and then calling f on the - list, and finally calling _concat_ixes to concatenate 'prefix' and - 'suffix' onto each element of the list. - """ - if not list: - return list - - l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) - if not l is None: - list = l - - return _concat_ixes(prefix, list, suffix, env) - -def _concat_ixes(prefix, list, suffix, env): - """ - Creates a new list from 'list' by concatenating the 'prefix' and - 'suffix' arguments onto each element of the list. A trailing space - on 'prefix' or leading space on 'suffix' will cause them to be put - into separate list elements rather than being concatenated. - """ - - result = [] - - # ensure that prefix and suffix are strings - prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) - suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) - - for x in list: - if isinstance(x, SCons.Node.FS.File): - result.append(x) - continue - x = str(x) - if x: - - if prefix: - if prefix[-1] == ' ': - result.append(prefix[:-1]) - elif x[:len(prefix)] != prefix: - x = prefix + x - - result.append(x) - - if suffix: - if suffix[0] == ' ': - result.append(suffix[1:]) - elif x[-len(suffix):] != suffix: - result[-1] = result[-1]+suffix - - return result - -def _stripixes(prefix, list, suffix, stripprefixes, stripsuffixes, env, c=None): - """ - This is a wrapper around _concat()/_concat_ixes() that checks for the - existence of prefixes or suffixes on list elements and strips them - where it finds them. This is used by tools (like the GNU linker) - that need to turn something like 'libfoo.a' into '-lfoo'. - """ - - if not list: - return list - - if not callable(c): - env_c = env['_concat'] - if env_c != _concat and callable(env_c): - # There's a custom _concat() method in the construction - # environment, and we've allowed people to set that in - # the past (see test/custom-concat.py), so preserve the - # backwards compatibility. - c = env_c - else: - c = _concat_ixes - - stripprefixes = map(env.subst, SCons.Util.flatten(stripprefixes)) - stripsuffixes = map(env.subst, SCons.Util.flatten(stripsuffixes)) - - stripped = [] - for l in SCons.PathList.PathList(list).subst_path(env, None, None): - if isinstance(l, SCons.Node.FS.File): - stripped.append(l) - continue - - if not SCons.Util.is_String(l): - l = str(l) - - for stripprefix in stripprefixes: - lsp = len(stripprefix) - if l[:lsp] == stripprefix: - l = l[lsp:] - # Do not strip more than one prefix - break - - for stripsuffix in stripsuffixes: - lss = len(stripsuffix) - if l[-lss:] == stripsuffix: - l = l[:-lss] - # Do not strip more than one suffix - break - - stripped.append(l) - - return c(prefix, stripped, suffix, env) - -def _defines(prefix, defs, suffix, env, c=_concat_ixes): - """A wrapper around _concat_ixes that turns a list or string - into a list of C preprocessor command-line definitions. - """ - if SCons.Util.is_List(defs): - l = [] - for d in defs: - if SCons.Util.is_List(d) or type(d) is types.TupleType: - l.append(str(d[0]) + '=' + str(d[1])) - else: - l.append(str(d)) - elif SCons.Util.is_Dict(defs): - # The items in a dictionary are stored in random order, but - # if the order of the command-line options changes from - # invocation to invocation, then the signature of the command - # line will change and we'll get random unnecessary rebuilds. - # Consequently, we have to sort the keys to ensure a - # consistent order... - l = [] - keys = defs.keys() - keys.sort() - for k in keys: - v = defs[k] - if v is None: - l.append(str(k)) - else: - l.append(str(k) + '=' + str(v)) - else: - l = [str(defs)] - return c(prefix, env.subst_path(l), suffix, env) - -class NullCmdGenerator: - """This is a callable class that can be used in place of other - command generators if you don't want them to do anything. - - The __call__ method for this class simply returns the thing - you instantiated it with. - - Example usage: - env["DO_NOTHING"] = NullCmdGenerator - env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" - """ - - def __init__(self, cmd): - self.cmd = cmd - - def __call__(self, target, source, env, for_signature=None): - return self.cmd - -class Variable_Method_Caller: - """A class for finding a construction variable on the stack and - calling one of its methods. - - We use this to support "construction variables" in our string - eval()s that actually stand in for methods--specifically, use - of "RDirs" in call to _concat that should actually execute the - "TARGET.RDirs" method. (We used to support this by creating a little - "build dictionary" that mapped RDirs to the method, but this got in - the way of Memoizing construction environments, because we had to - create new environment objects to hold the variables.) - """ - def __init__(self, variable, method): - self.variable = variable - self.method = method - def __call__(self, *args, **kw): - try: 1/0 - except ZeroDivisionError: - # Don't start iterating with the current stack-frame to - # prevent creating reference cycles (f_back is safe). - frame = sys.exc_info()[2].tb_frame.f_back - variable = self.variable - while frame: - if frame.f_locals.has_key(variable): - v = frame.f_locals[variable] - if v: - method = getattr(v, self.method) - return apply(method, args, kw) - frame = frame.f_back - return None - -ConstructionEnvironment = { - 'BUILDERS' : {}, - 'SCANNERS' : [], - 'CONFIGUREDIR' : '#/.sconf_temp', - 'CONFIGURELOG' : '#/config.log', - 'CPPSUFFIXES' : SCons.Tool.CSuffixes, - 'DSUFFIXES' : SCons.Tool.DSuffixes, - 'ENV' : {}, - 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, - 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, - '_concat' : _concat, - '_defines' : _defines, - '_stripixes' : _stripixes, - '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - 'TEMPFILE' : NullCmdGenerator, - 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), - 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), - 'File' : Variable_Method_Caller('TARGET', 'File'), - 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), -} diff --git a/tools/scons/scons-local-1.2.0/SCons/Environment.py b/tools/scons/scons-local-1.2.0/SCons/Environment.py deleted file mode 100644 index e1a8ec2c66..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Environment.py +++ /dev/null @@ -1,2300 +0,0 @@ -"""SCons.Environment - -Base class for construction Environments. These are -the primary objects used to communicate dependency and -construction information to the build engine. - -Keyword arguments supplied when the construction Environment -is created are construction variables used to initialize the -Environment -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Environment.py 3842 2008/12/20 22:59:52 scons" - - -import copy -import os -import sys -import re -import shlex -import string -from UserDict import UserDict - -import SCons.Action -import SCons.Builder -from SCons.Debug import logInstanceCreation -import SCons.Defaults -import SCons.Errors -import SCons.Memoize -import SCons.Node -import SCons.Node.Alias -import SCons.Node.FS -import SCons.Node.Python -import SCons.Platform -import SCons.SConsign -import SCons.Subst -import SCons.Tool -import SCons.Util -import SCons.Warnings - -class _Null: - pass - -_null = _Null - -_warn_copy_deprecated = True -_warn_source_signatures_deprecated = True -_warn_target_signatures_deprecated = True - -CleanTargets = {} -CalculatorArgs = {} - -semi_deepcopy = SCons.Util.semi_deepcopy - -# Pull UserError into the global name space for the benefit of -# Environment().SourceSignatures(), which has some import statements -# which seem to mess up its ability to reference SCons directly. -UserError = SCons.Errors.UserError - -def alias_builder(env, target, source): - pass - -AliasBuilder = SCons.Builder.Builder(action = alias_builder, - target_factory = SCons.Node.Alias.default_ans.Alias, - source_factory = SCons.Node.FS.Entry, - multi = 1, - is_explicit = None, - name='AliasBuilder') - -def apply_tools(env, tools, toolpath): - # Store the toolpath in the Environment. - if toolpath is not None: - env['toolpath'] = toolpath - - if not tools: - return - # Filter out null tools from the list. - for tool in filter(None, tools): - if SCons.Util.is_List(tool) or type(tool)==type(()): - toolname = tool[0] - toolargs = tool[1] # should be a dict of kw args - tool = apply(env.Tool, [toolname], toolargs) - else: - env.Tool(tool) - -# These names are (or will be) controlled by SCons; users should never -# set or override them. This warning can optionally be turned off, -# but scons will still ignore the illegal variable names even if it's off. -reserved_construction_var_names = [ - 'SOURCE', - 'SOURCES', - 'TARGET', - 'TARGETS', -] - -future_reserved_construction_var_names = [ - 'CHANGED_SOURCES', - 'CHANGED_TARGETS', - 'UNCHANGED_SOURCES', - 'UNCHANGED_TARGETS', -] - -def copy_non_reserved_keywords(dict): - result = semi_deepcopy(dict) - for k in result.keys(): - if k in reserved_construction_var_names: - msg = "Ignoring attempt to set reserved variable `$%s'" - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) - del result[k] - return result - -def _set_reserved(env, key, value): - msg = "Ignoring attempt to set reserved variable `$%s'" - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key) - -def _set_future_reserved(env, key, value): - env._dict[key] = value - msg = "`$%s' will be reserved in a future release and setting it will become ignored" - SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key) - -def _set_BUILDERS(env, key, value): - try: - bd = env._dict[key] - for k in bd.keys(): - del bd[k] - except KeyError: - bd = BuilderDict(kwbd, env) - env._dict[key] = bd - bd.update(value) - -def _del_SCANNERS(env, key): - del env._dict[key] - env.scanner_map_delete() - -def _set_SCANNERS(env, key, value): - env._dict[key] = value - env.scanner_map_delete() - -def _delete_duplicates(l, keep_last): - """Delete duplicates from a sequence, keeping the first or last.""" - seen={} - result=[] - if keep_last: # reverse in & out, then keep first - l.reverse() - for i in l: - try: - if not seen.has_key(i): - result.append(i) - seen[i]=1 - except TypeError: - # probably unhashable. Just keep it. - result.append(i) - if keep_last: - result.reverse() - return result - - - -# The following is partly based on code in a comment added by Peter -# Shannon at the following page (there called the "transplant" class): -# -# ASPN : Python Cookbook : Dynamically added methods to a class -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 -# -# We had independently been using the idiom as BuilderWrapper, but -# factoring out the common parts into this base class, and making -# BuilderWrapper a subclass that overrides __call__() to enforce specific -# Builder calling conventions, simplified some of our higher-layer code. - -class MethodWrapper: - """ - A generic Wrapper class that associates a method (which can - actually be any callable) with an object. As part of creating this - MethodWrapper object an attribute with the specified (by default, - the name of the supplied method) is added to the underlying object. - When that new "method" is called, our __call__() method adds the - object as the first argument, simulating the Python behavior of - supplying "self" on method calls. - - We hang on to the name by which the method was added to the underlying - base class so that we can provide a method to "clone" ourselves onto - a new underlying object being copied (without which we wouldn't need - to save that info). - """ - def __init__(self, object, method, name=None): - if name is None: - name = method.__name__ - self.object = object - self.method = method - self.name = name - setattr(self.object, name, self) - - def __call__(self, *args, **kwargs): - nargs = (self.object,) + args - return apply(self.method, nargs, kwargs) - - def clone(self, new_object): - """ - Returns an object that re-binds the underlying "method" to - the specified new object. - """ - return self.__class__(new_object, self.method, self.name) - -class BuilderWrapper(MethodWrapper): - """ - A MethodWrapper subclass that that associates an environment with - a Builder. - - This mainly exists to wrap the __call__() function so that all calls - to Builders can have their argument lists massaged in the same way - (treat a lone argument as the source, treat two arguments as target - then source, make sure both target and source are lists) without - having to have cut-and-paste code to do it. - - As a bit of obsessive backwards compatibility, we also intercept - attempts to get or set the "env" or "builder" attributes, which were - the names we used before we put the common functionality into the - MethodWrapper base class. We'll keep this around for a while in case - people shipped Tool modules that reached into the wrapper (like the - Tool/qt.py module does, or did). There shouldn't be a lot attribute - fetching or setting on these, so a little extra work shouldn't hurt. - """ - def __call__(self, target=None, source=_null, *args, **kw): - if source is _null: - source = target - target = None - if not target is None and not SCons.Util.is_List(target): - target = [target] - if not source is None and not SCons.Util.is_List(source): - source = [source] - return apply(MethodWrapper.__call__, (self, target, source) + args, kw) - - def __repr__(self): - return '<BuilderWrapper %s>' % repr(self.name) - - def __str__(self): - return self.__repr__() - - def __getattr__(self, name): - if name == 'env': - return self.object - elif name == 'builder': - return self.method - else: - raise AttributeError, name - - def __setattr__(self, name, value): - if name == 'env': - self.object = value - elif name == 'builder': - self.method = value - else: - self.__dict__[name] = value - - # This allows a Builder to be executed directly - # through the Environment to which it's attached. - # In practice, we shouldn't need this, because - # builders actually get executed through a Node. - # But we do have a unit test for this, and can't - # yet rule out that it would be useful in the - # future, so leave it for now. - #def execute(self, **kw): - # kw['env'] = self.env - # apply(self.builder.execute, (), kw) - -class BuilderDict(UserDict): - """This is a dictionary-like class used by an Environment to hold - the Builders. We need to do this because every time someone changes - the Builders in the Environment's BUILDERS dictionary, we must - update the Environment's attributes.""" - def __init__(self, dict, env): - # Set self.env before calling the superclass initialization, - # because it will end up calling our other methods, which will - # need to point the values in this dictionary to self.env. - self.env = env - UserDict.__init__(self, dict) - - def __semi_deepcopy__(self): - return self.__class__(self.data, self.env) - - def __setitem__(self, item, val): - try: - method = getattr(self.env, item).method - except AttributeError: - pass - else: - self.env.RemoveMethod(method) - UserDict.__setitem__(self, item, val) - BuilderWrapper(self.env, val, item) - - def __delitem__(self, item): - UserDict.__delitem__(self, item) - delattr(self.env, item) - - def update(self, dict): - for i, v in dict.items(): - self.__setitem__(i, v) - - - -_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') - -def is_valid_construction_var(varstr): - """Return if the specified string is a legitimate construction - variable. - """ - return _is_valid_var.match(varstr) - - - -class SubstitutionEnvironment: - """Base class for different flavors of construction environments. - - This class contains a minimal set of methods that handle contruction - variable expansion and conversion of strings to Nodes, which may or - may not be actually useful as a stand-alone class. Which methods - ended up in this class is pretty arbitrary right now. They're - basically the ones which we've empirically determined are common to - the different construction environment subclasses, and most of the - others that use or touch the underlying dictionary of construction - variables. - - Eventually, this class should contain all the methods that we - determine are necessary for a "minimal" interface to the build engine. - A full "native Python" SCons environment has gotten pretty heavyweight - with all of the methods and Tools and construction variables we've - jammed in there, so it would be nice to have a lighter weight - alternative for interfaces that don't need all of the bells and - whistles. (At some point, we'll also probably rename this class - "Base," since that more reflects what we want this class to become, - but because we've released comments that tell people to subclass - Environment.Base to create their own flavors of construction - environment, we'll save that for a future refactoring when this - class actually becomes useful.) - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - def __init__(self, **kw): - """Initialization of an underlying SubstitutionEnvironment class. - """ - if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') - self.fs = SCons.Node.FS.get_default_fs() - self.ans = SCons.Node.Alias.default_ans - self.lookup_list = SCons.Node.arg2nodes_lookups - self._dict = kw.copy() - self._init_special() - self.added_methods = [] - #self._memo = {} - - def _init_special(self): - """Initial the dispatch tables for special handling of - special construction variables.""" - self._special_del = {} - self._special_del['SCANNERS'] = _del_SCANNERS - - self._special_set = {} - for key in reserved_construction_var_names: - self._special_set[key] = _set_reserved - for key in future_reserved_construction_var_names: - self._special_set[key] = _set_future_reserved - self._special_set['BUILDERS'] = _set_BUILDERS - self._special_set['SCANNERS'] = _set_SCANNERS - - # Freeze the keys of self._special_set in a list for use by - # methods that need to check. (Empirically, list scanning has - # gotten better than dict.has_key() in Python 2.5.) - self._special_set_keys = self._special_set.keys() - - def __cmp__(self, other): - return cmp(self._dict, other._dict) - - def __delitem__(self, key): - special = self._special_del.get(key) - if special: - special(self, key) - else: - del self._dict[key] - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - # This is heavily used. This implementation is the best we have - # according to the timings in bench/env.__setitem__.py. - # - # The "key in self._special_set_keys" test here seems to perform - # pretty well for the number of keys we have. A hard-coded - # list works a little better in Python 2.5, but that has the - # disadvantage of maybe getting out of sync if we ever add more - # variable names. Using self._special_set.has_key() works a - # little better in Python 2.4, but is worse then this test. - # So right now it seems like a good trade-off, but feel free to - # revisit this with bench/env.__setitem__.py as needed (and - # as newer versions of Python come out). - if key in self._special_set_keys: - self._special_set[key](self, key, value) - else: - # If we already have the entry, then it's obviously a valid - # key and we don't need to check. If we do check, using a - # global, pre-compiled regular expression directly is more - # efficient than calling another function or a method. - if not self._dict.has_key(key) \ - and not _is_valid_var.match(key): - raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key - self._dict[key] = value - - def get(self, key, default=None): - "Emulates the get() method of dictionaries.""" - return self._dict.get(key, default) - - def has_key(self, key): - return self._dict.has_key(key) - - def __contains__(self, key): - return self._dict.__contains__(key) - - def items(self): - return self._dict.items() - - def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): - if node_factory is _null: - node_factory = self.fs.File - if lookup_list is _null: - lookup_list = self.lookup_list - - if not args: - return [] - - args = SCons.Util.flatten(args) - - nodes = [] - for v in args: - if SCons.Util.is_String(v): - n = None - for l in lookup_list: - n = l(v) - if not n is None: - break - if not n is None: - if SCons.Util.is_String(n): - # n = self.subst(n, raw=1, **kw) - kw['raw'] = 1 - n = apply(self.subst, (n,), kw) - if node_factory: - n = node_factory(n) - if SCons.Util.is_List(n): - nodes.extend(n) - else: - nodes.append(n) - elif node_factory: - # v = node_factory(self.subst(v, raw=1, **kw)) - kw['raw'] = 1 - v = node_factory(apply(self.subst, (v,), kw)) - if SCons.Util.is_List(v): - nodes.extend(v) - else: - nodes.append(v) - else: - nodes.append(v) - - return nodes - - def gvars(self): - return self._dict - - def lvars(self): - return {} - - def subst(self, string, raw=0, target=None, source=None, conv=None): - """Recursively interpolates construction variables from the - Environment into the specified string, returning the expanded - result. Construction variables are specified by a $ prefix - in the string and begin with an initial underscore or - alphabetic character followed by any number of underscores - or alphanumeric characters. The construction variable names - may be surrounded by curly braces to separate the name from - trailing characters. - """ - gvars = self.gvars() - lvars = self.lvars() - lvars['__env__'] = self - return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv) - - def subst_kw(self, kw, raw=0, target=None, source=None): - nkw = {} - for k, v in kw.items(): - k = self.subst(k, raw, target, source) - if SCons.Util.is_String(v): - v = self.subst(v, raw, target, source) - nkw[k] = v - return nkw - - def subst_list(self, string, raw=0, target=None, source=None, conv=None): - """Calls through to SCons.Subst.scons_subst_list(). See - the documentation for that function.""" - gvars = self.gvars() - lvars = self.lvars() - lvars['__env__'] = self - return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv) - - def subst_path(self, path, target=None, source=None): - """Substitute a path list, turning EntryProxies into Nodes - and leaving Nodes (and other objects) as-is.""" - - if not SCons.Util.is_List(path): - path = [path] - - def s(obj): - """This is the "string conversion" routine that we have our - substitutions use to return Nodes, not strings. This relies - on the fact that an EntryProxy object has a get() method that - returns the underlying Node that it wraps, which is a bit of - architectural dependence that we might need to break or modify - in the future in response to additional requirements.""" - try: - get = obj.get - except AttributeError: - obj = SCons.Util.to_String_for_subst(obj) - else: - obj = get() - return obj - - r = [] - for p in path: - if SCons.Util.is_String(p): - p = self.subst(p, target=target, source=source, conv=s) - if SCons.Util.is_List(p): - if len(p) == 1: - p = p[0] - else: - # We have an object plus a string, or multiple - # objects that we need to smush together. No choice - # but to make them into a string. - p = string.join(map(SCons.Util.to_String_for_subst, p), '') - else: - p = s(p) - r.append(p) - return r - - subst_target_source = subst - - def backtick(self, command): - import subprocess - # common arguments - kw = { 'stdin' : 'devnull', - 'stdout' : subprocess.PIPE, - 'stderr' : subprocess.PIPE, - 'universal_newlines' : True, - } - # if the command is a list, assume it's been quoted - # othewise force a shell - if not SCons.Util.is_List(command): kw['shell'] = True - # run constructed command - #TODO(1.5) p = SCons.Action._subproc(self, command, **kw) - p = apply(SCons.Action._subproc, (self, command), kw) - out,err = p.communicate() - status = p.wait() - if err: - sys.stderr.write(err) - if status: - raise OSError("'%s' exited %d" % (command, status)) - return out - - def AddMethod(self, function, name=None): - """ - Adds the specified function as a method of this construction - environment with the specified name. If the name is omitted, - the default name is the name of the function itself. - """ - method = MethodWrapper(self, function, name) - self.added_methods.append(method) - - def RemoveMethod(self, function): - """ - Removes the specified function's MethodWrapper from the - added_methods list, so we don't re-bind it when making a clone. - """ - is_not_func = lambda dm, f=function: not dm.method is f - self.added_methods = filter(is_not_func, self.added_methods) - - def Override(self, overrides): - """ - Produce a modified environment whose variables are overriden by - the overrides dictionaries. "overrides" is a dictionary that - will override the variables of this environment. - - This function is much more efficient than Clone() or creating - a new Environment because it doesn't copy the construction - environment dictionary, it just wraps the underlying construction - environment, and doesn't even create a wrapper object if there - are no overrides. - """ - if not overrides: return self - o = copy_non_reserved_keywords(overrides) - if not o: return self - overrides = {} - merges = None - for key, value in o.items(): - if key == 'parse_flags': - merges = value - else: - overrides[key] = SCons.Subst.scons_subst_once(value, self, key) - env = OverrideEnvironment(self, overrides) - if merges: env.MergeFlags(merges) - return env - - def ParseFlags(self, *flags): - """ - Parse the set of flags and return a dict with the flags placed - in the appropriate entry. The flags are treated as a typical - set of command-line flags for a GNU-like toolchain and used to - populate the entries in the dict immediately below. If one of - the flag strings begins with a bang (exclamation mark), it is - assumed to be a command and the rest of the string is executed; - the result of that evaluation is then added to the dict. - """ - dict = { - 'ASFLAGS' : SCons.Util.CLVar(''), - 'CFLAGS' : SCons.Util.CLVar(''), - 'CCFLAGS' : SCons.Util.CLVar(''), - 'CPPDEFINES' : [], - 'CPPFLAGS' : SCons.Util.CLVar(''), - 'CPPPATH' : [], - 'FRAMEWORKPATH' : SCons.Util.CLVar(''), - 'FRAMEWORKS' : SCons.Util.CLVar(''), - 'LIBPATH' : [], - 'LIBS' : [], - 'LINKFLAGS' : SCons.Util.CLVar(''), - 'RPATH' : [], - } - - # The use of the "me" parameter to provide our own name for - # recursion is an egregious hack to support Python 2.1 and before. - def do_parse(arg, me, self = self, dict = dict): - # if arg is a sequence, recurse with each element - if not arg: - return - - if not SCons.Util.is_String(arg): - for t in arg: me(t, me) - return - - # if arg is a command, execute it - if arg[0] == '!': - arg = self.backtick(arg[1:]) - - # utility function to deal with -D option - def append_define(name, dict = dict): - t = string.split(name, '=') - if len(t) == 1: - dict['CPPDEFINES'].append(name) - else: - dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')]) - - # Loop through the flags and add them to the appropriate option. - # This tries to strike a balance between checking for all possible - # flags and keeping the logic to a finite size, so it doesn't - # check for some that don't occur often. It particular, if the - # flag is not known to occur in a config script and there's a way - # of passing the flag to the right place (by wrapping it in a -W - # flag, for example) we don't check for it. Note that most - # preprocessor options are not handled, since unhandled options - # are placed in CCFLAGS, so unless the preprocessor is invoked - # separately, these flags will still get to the preprocessor. - # Other options not currently handled: - # -iqoutedir (preprocessor search path) - # -u symbol (linker undefined symbol) - # -s (linker strip files) - # -static* (linker static binding) - # -shared* (linker dynamic binding) - # -symbolic (linker global binding) - # -R dir (deprecated linker rpath) - # IBM compilers may also accept -qframeworkdir=foo - - params = shlex.split(arg) - append_next_arg_to = None # for multi-word args - for arg in params: - if append_next_arg_to: - if append_next_arg_to == 'CPPDEFINES': - append_define(arg) - elif append_next_arg_to == '-include': - t = ('-include', self.fs.File(arg)) - dict['CCFLAGS'].append(t) - elif append_next_arg_to == '-isysroot': - t = ('-isysroot', arg) - dict['CCFLAGS'].append(t) - dict['LINKFLAGS'].append(t) - elif append_next_arg_to == '-arch': - t = ('-arch', arg) - dict['CCFLAGS'].append(t) - dict['LINKFLAGS'].append(t) - else: - dict[append_next_arg_to].append(arg) - append_next_arg_to = None - elif not arg[0] in ['-', '+']: - dict['LIBS'].append(self.fs.File(arg)) - elif arg[:2] == '-L': - if arg[2:]: - dict['LIBPATH'].append(arg[2:]) - else: - append_next_arg_to = 'LIBPATH' - elif arg[:2] == '-l': - if arg[2:]: - dict['LIBS'].append(arg[2:]) - else: - append_next_arg_to = 'LIBS' - elif arg[:2] == '-I': - if arg[2:]: - dict['CPPPATH'].append(arg[2:]) - else: - append_next_arg_to = 'CPPPATH' - elif arg[:4] == '-Wa,': - dict['ASFLAGS'].append(arg[4:]) - dict['CCFLAGS'].append(arg) - elif arg[:4] == '-Wl,': - if arg[:11] == '-Wl,-rpath=': - dict['RPATH'].append(arg[11:]) - elif arg[:7] == '-Wl,-R,': - dict['RPATH'].append(arg[7:]) - elif arg[:6] == '-Wl,-R': - dict['RPATH'].append(arg[6:]) - else: - dict['LINKFLAGS'].append(arg) - elif arg[:4] == '-Wp,': - dict['CPPFLAGS'].append(arg) - elif arg[:2] == '-D': - if arg[2:]: - append_define(arg[2:]) - else: - append_next_arg_to = 'CPPDEFINES' - elif arg == '-framework': - append_next_arg_to = 'FRAMEWORKS' - elif arg[:14] == '-frameworkdir=': - dict['FRAMEWORKPATH'].append(arg[14:]) - elif arg[:2] == '-F': - if arg[2:]: - dict['FRAMEWORKPATH'].append(arg[2:]) - else: - append_next_arg_to = 'FRAMEWORKPATH' - elif arg == '-mno-cygwin': - dict['CCFLAGS'].append(arg) - dict['LINKFLAGS'].append(arg) - elif arg == '-mwindows': - dict['LINKFLAGS'].append(arg) - elif arg == '-pthread': - dict['CCFLAGS'].append(arg) - dict['LINKFLAGS'].append(arg) - elif arg[:5] == '-std=': - dict['CFLAGS'].append(arg) # C only - elif arg[0] == '+': - dict['CCFLAGS'].append(arg) - dict['LINKFLAGS'].append(arg) - elif arg in ['-include', '-isysroot', '-arch']: - append_next_arg_to = arg - else: - dict['CCFLAGS'].append(arg) - - for arg in flags: - do_parse(arg, do_parse) - return dict - - def MergeFlags(self, args, unique=1, dict=None): - """ - Merge the dict in args into the construction variables of this - env, or the passed-in dict. If args is not a dict, it is - converted into a dict using ParseFlags. If unique is not set, - the flags are appended rather than merged. - """ - - if dict is None: - dict = self - if not SCons.Util.is_Dict(args): - args = self.ParseFlags(args) - if not unique: - apply(self.Append, (), args) - return self - for key, value in args.items(): - if not value: - continue - try: - orig = self[key] - except KeyError: - orig = value - else: - if not orig: - orig = value - elif value: - # Add orig and value. The logic here was lifted from - # part of env.Append() (see there for a lot of comments - # about the order in which things are tried) and is - # used mainly to handle coercion of strings to CLVar to - # "do the right thing" given (e.g.) an original CCFLAGS - # string variable like '-pipe -Wall'. - try: - orig = orig + value - except (KeyError, TypeError): - try: - add_to_orig = orig.append - except AttributeError: - value.insert(0, orig) - orig = value - else: - add_to_orig(value) - t = [] - if key[-4:] == 'PATH': - ### keep left-most occurence - for v in orig: - if v not in t: - t.append(v) - else: - ### keep right-most occurence - orig.reverse() - for v in orig: - if v not in t: - t.insert(0, v) - self[key] = t - return self - -# def MergeShellPaths(self, args, prepend=1): -# """ -# Merge the dict in args into the shell environment in env['ENV']. -# Shell path elements are appended or prepended according to prepend. - -# Uses Pre/AppendENVPath, so it always appends or prepends uniquely. - -# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) -# prepends /usr/local/lib to env['ENV']['LIBPATH']. -# """ - -# for pathname, pathval in args.items(): -# if not pathval: -# continue -# if prepend: -# apply(self.PrependENVPath, (pathname, pathval)) -# else: -# apply(self.AppendENVPath, (pathname, pathval)) - - -# Used by the FindSourceFiles() method, below. -# Stuck here for support of pre-2.2 Python versions. -def build_source(ss, result): - for s in ss: - if isinstance(s, SCons.Node.FS.Dir): - build_source(s.all_children(), result) - elif s.has_builder(): - build_source(s.sources, result) - elif isinstance(s.disambiguate(), SCons.Node.FS.File): - result.append(s) - -def default_decide_source(dependency, target, prev_ni): - f = SCons.Defaults.DefaultEnvironment().decide_source - return f(dependency, target, prev_ni) - -def default_decide_target(dependency, target, prev_ni): - f = SCons.Defaults.DefaultEnvironment().decide_target - return f(dependency, target, prev_ni) - -def default_copy_from_cache(src, dst): - f = SCons.Defaults.DefaultEnvironment().copy_from_cache - return f(src, dst) - -class Base(SubstitutionEnvironment): - """Base class for "real" construction Environments. These are the - primary objects used to communicate dependency and construction - information to the build engine. - - Keyword arguments supplied when the construction Environment - is created are construction variables used to initialize the - Environment. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - ####################################################################### - # This is THE class for interacting with the SCons build engine, - # and it contains a lot of stuff, so we're going to try to keep this - # a little organized by grouping the methods. - ####################################################################### - - ####################################################################### - # Methods that make an Environment act like a dictionary. These have - # the expected standard names for Python mapping objects. Note that - # we don't actually make an Environment a subclass of UserDict for - # performance reasons. Note also that we only supply methods for - # dictionary functionality that we actually need and use. - ####################################################################### - - def __init__(self, - platform=None, - tools=None, - toolpath=None, - variables=None, - parse_flags = None, - **kw): - """ - Initialization of a basic SCons construction environment, - including setting up special construction variables like BUILDER, - PLATFORM, etc., and searching for and applying available Tools. - - Note that we do *not* call the underlying base class - (SubsitutionEnvironment) initialization, because we need to - initialize things in a very specific order that doesn't work - with the much simpler base class initialization. - """ - if __debug__: logInstanceCreation(self, 'Environment.Base') - self._memo = {} - self.fs = SCons.Node.FS.get_default_fs() - self.ans = SCons.Node.Alias.default_ans - self.lookup_list = SCons.Node.arg2nodes_lookups - self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) - self._init_special() - self.added_methods = [] - - # We don't use AddMethod, or define these as methods in this - # class, because we *don't* want these functions to be bound - # methods. They need to operate independently so that the - # settings will work properly regardless of whether a given - # target ends up being built with a Base environment or an - # OverrideEnvironment or what have you. - self.decide_target = default_decide_target - self.decide_source = default_decide_source - - self.copy_from_cache = default_copy_from_cache - - self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) - - if platform is None: - platform = self._dict.get('PLATFORM', None) - if platform is None: - platform = SCons.Platform.Platform() - if SCons.Util.is_String(platform): - platform = SCons.Platform.Platform(platform) - self._dict['PLATFORM'] = str(platform) - platform(self) - - # Apply the passed-in and customizable variables to the - # environment before calling the tools, because they may use - # some of them during initialization. - if kw.has_key('options'): - # Backwards compatibility: they may stll be using the - # old "options" keyword. - variables = kw['options'] - del kw['options'] - apply(self.Replace, (), kw) - keys = kw.keys() - if variables: - keys = keys + variables.keys() - variables.Update(self) - - save = {} - for k in keys: - try: - save[k] = self._dict[k] - except KeyError: - # No value may have been set if they tried to pass in a - # reserved variable name like TARGETS. - pass - - SCons.Tool.Initializers(self) - - if tools is None: - tools = self._dict.get('TOOLS', None) - if tools is None: - tools = ['default'] - apply_tools(self, tools, toolpath) - - # Now restore the passed-in and customized variables - # to the environment, since the values the user set explicitly - # should override any values set by the tools. - for key, val in save.items(): - self._dict[key] = val - - # Finally, apply any flags to be merged in - if parse_flags: self.MergeFlags(parse_flags) - - ####################################################################### - # Utility methods that are primarily for internal use by SCons. - # These begin with lower-case letters. - ####################################################################### - - def get_builder(self, name): - """Fetch the builder with the specified name from the environment. - """ - try: - return self._dict['BUILDERS'][name] - except KeyError: - return None - - def get_CacheDir(self): - try: - path = self._CacheDir_path - except AttributeError: - path = SCons.Defaults.DefaultEnvironment()._CacheDir_path - try: - if path == self._last_CacheDir_path: - return self._last_CacheDir - except AttributeError: - pass - cd = SCons.CacheDir.CacheDir(path) - self._last_CacheDir_path = path - self._last_CacheDir = cd - return cd - - def get_factory(self, factory, default='File'): - """Return a factory function for creating Nodes for this - construction environment. - """ - name = default - try: - is_node = issubclass(factory, SCons.Node.Node) - except TypeError: - # The specified factory isn't a Node itself--it's - # most likely None, or possibly a callable. - pass - else: - if is_node: - # The specified factory is a Node (sub)class. Try to - # return the FS method that corresponds to the Node's - # name--that is, we return self.fs.Dir if they want a Dir, - # self.fs.File for a File, etc. - try: name = factory.__name__ - except AttributeError: pass - else: factory = None - if not factory: - # They passed us None, or we picked up a name from a specified - # class, so return the FS method. (Note that we *don't* - # use our own self.{Dir,File} methods because that would - # cause env.subst() to be called twice on the file name, - # interfering with files that have $$ in them.) - factory = getattr(self.fs, name) - return factory - - memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) - - def _gsm(self): - try: - return self._memo['_gsm'] - except KeyError: - pass - - result = {} - - try: - scanners = self._dict['SCANNERS'] - except KeyError: - pass - else: - # Reverse the scanner list so that, if multiple scanners - # claim they can scan the same suffix, earlier scanners - # in the list will overwrite later scanners, so that - # the result looks like a "first match" to the user. - if not SCons.Util.is_List(scanners): - scanners = [scanners] - else: - scanners = scanners[:] # copy so reverse() doesn't mod original - scanners.reverse() - for scanner in scanners: - for k in scanner.get_skeys(self): - result[k] = scanner - - self._memo['_gsm'] = result - - return result - - def get_scanner(self, skey): - """Find the appropriate scanner given a key (usually a file suffix). - """ - return self._gsm().get(skey) - - def scanner_map_delete(self, kw=None): - """Delete the cached scanner map (if we need to). - """ - try: - del self._memo['_gsm'] - except KeyError: - pass - - def _update(self, dict): - """Update an environment's values directly, bypassing the normal - checks that occur when users try to set items. - """ - self._dict.update(dict) - - def get_src_sig_type(self): - try: - return self.src_sig_type - except AttributeError: - t = SCons.Defaults.DefaultEnvironment().src_sig_type - self.src_sig_type = t - return t - - def get_tgt_sig_type(self): - try: - return self.tgt_sig_type - except AttributeError: - t = SCons.Defaults.DefaultEnvironment().tgt_sig_type - self.tgt_sig_type = t - return t - - ####################################################################### - # Public methods for manipulating an Environment. These begin with - # upper-case letters. The essential characteristic of methods in - # this section is that they do *not* have corresponding same-named - # global functions. For example, a stand-alone Append() function - # makes no sense, because Append() is all about appending values to - # an Environment's construction variables. - ####################################################################### - - def Append(self, **kw): - """Append values to existing construction variables - in an Environment. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - # It would be easier on the eyes to write this using - # "continue" statements whenever we finish processing an item, - # but Python 1.5.2 apparently doesn't let you use "continue" - # within try:-except: blocks, so we have to nest our code. - try: - orig = self._dict[key] - except KeyError: - # No existing variable in the environment, so just set - # it to the new value. - self._dict[key] = val - else: - try: - # Check if the original looks like a dictionary. - # If it is, we can't just try adding the value because - # dictionaries don't have __add__() methods, and - # things like UserList will incorrectly coerce the - # original dict to a list (which we don't want). - update_dict = orig.update - except AttributeError: - try: - # Most straightforward: just try to add them - # together. This will work in most cases, when the - # original and new values are of compatible types. - self._dict[key] = orig + val - except (KeyError, TypeError): - try: - # Check if the original is a list. - add_to_orig = orig.append - except AttributeError: - # The original isn't a list, but the new - # value is (by process of elimination), - # so insert the original in the new value - # (if there's one to insert) and replace - # the variable with it. - if orig: - val.insert(0, orig) - self._dict[key] = val - else: - # The original is a list, so append the new - # value to it (if there's a value to append). - if val: - add_to_orig(val) - else: - # The original looks like a dictionary, so update it - # based on what we think the value looks like. - if SCons.Util.is_List(val): - for v in val: - orig[v] = None - else: - try: - update_dict(val) - except (AttributeError, TypeError, ValueError): - if SCons.Util.is_Dict(val): - for k, v in val.items(): - orig[k] = v - else: - orig[val] = None - self.scanner_map_delete(kw) - - def AppendENVPath(self, name, newpath, envname = 'ENV', - sep = os.pathsep, delete_existing=1): - """Append path elements to the path 'name' in the 'ENV' - dictionary for this environment. Will only add any particular - path once, and will normpath and normcase all paths to help - assure this. This can also handle the case where the env - variable is a list instead of a string. - - If delete_existing is 0, a newpath which is already in the path - will not be moved to the end (it will be left where it is). - """ - - orig = '' - if self._dict.has_key(envname) and self._dict[envname].has_key(name): - orig = self._dict[envname][name] - - nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing) - - if not self._dict.has_key(envname): - self._dict[envname] = {} - - self._dict[envname][name] = nv - - def AppendUnique(self, delete_existing=0, **kw): - """Append values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is 1, removes existing values first, so - values move to end. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - if SCons.Util.is_List(val): - val = _delete_duplicates(val, delete_existing) - if not self._dict.has_key(key) or self._dict[key] in ('', None): - self._dict[key] = val - elif SCons.Util.is_Dict(self._dict[key]) and \ - SCons.Util.is_Dict(val): - self._dict[key].update(val) - elif SCons.Util.is_List(val): - dk = self._dict[key] - if not SCons.Util.is_List(dk): - dk = [dk] - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - else: - val = filter(lambda x, dk=dk: x not in dk, val) - self._dict[key] = dk + val - else: - dk = self._dict[key] - if SCons.Util.is_List(dk): - # By elimination, val is not a list. Since dk is a - # list, wrap val in a list first. - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = dk + [val] - else: - if not val in dk: - self._dict[key] = dk + [val] - else: - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = dk + val - self.scanner_map_delete(kw) - - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): - """Return a copy of a construction Environment. The - copy is like a Python "deep copy"--that is, independent - copies are made recursively of each objects--except that - a reference is copied when an object is not deep-copyable - (like a function). There are no references to any mutable - objects in the original Environment. - """ - clone = copy.copy(self) - clone._dict = semi_deepcopy(self._dict) - - try: - cbd = clone._dict['BUILDERS'] - except KeyError: - pass - else: - clone._dict['BUILDERS'] = BuilderDict(cbd, clone) - - # Check the methods added via AddMethod() and re-bind them to - # the cloned environment. Only do this if the attribute hasn't - # been overwritten by the user explicitly and still points to - # the added method. - clone.added_methods = [] - for mw in self.added_methods: - if mw == getattr(self, mw.name): - clone.added_methods.append(mw.clone(clone)) - - clone._memo = {} - - # Apply passed-in variables before the tools - # so the tools can use the new variables - kw = copy_non_reserved_keywords(kw) - new = {} - for key, value in kw.items(): - new[key] = SCons.Subst.scons_subst_once(value, self, key) - apply(clone.Replace, (), new) - - apply_tools(clone, tools, toolpath) - - # apply them again in case the tools overwrote them - apply(clone.Replace, (), new) - - # Finally, apply any flags to be merged in - if parse_flags: clone.MergeFlags(parse_flags) - - if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') - return clone - - def Copy(self, *args, **kw): - global _warn_copy_deprecated - if _warn_copy_deprecated: - msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) - _warn_copy_deprecated = False - return apply(self.Clone, args, kw) - - def _changed_build(self, dependency, target, prev_ni): - if dependency.changed_state(target, prev_ni): - return 1 - return self.decide_source(dependency, target, prev_ni) - - def _changed_content(self, dependency, target, prev_ni): - return dependency.changed_content(target, prev_ni) - - def _changed_source(self, dependency, target, prev_ni): - target_env = dependency.get_build_env() - type = target_env.get_tgt_sig_type() - if type == 'source': - return target_env.decide_source(dependency, target, prev_ni) - else: - return target_env.decide_target(dependency, target, prev_ni) - - def _changed_timestamp_then_content(self, dependency, target, prev_ni): - return dependency.changed_timestamp_then_content(target, prev_ni) - - def _changed_timestamp_newer(self, dependency, target, prev_ni): - return dependency.changed_timestamp_newer(target, prev_ni) - - def _changed_timestamp_match(self, dependency, target, prev_ni): - return dependency.changed_timestamp_match(target, prev_ni) - - def _copy_from_cache(self, src, dst): - return self.fs.copy(src, dst) - - def _copy2_from_cache(self, src, dst): - return self.fs.copy2(src, dst) - - def Decider(self, function): - copy_function = self._copy2_from_cache - if function in ('MD5', 'content'): - if not SCons.Util.md5: - raise UserError, "MD5 signatures are not available in this version of Python." - function = self._changed_content - elif function == 'MD5-timestamp': - function = self._changed_timestamp_then_content - elif function in ('timestamp-newer', 'make'): - function = self._changed_timestamp_newer - copy_function = self._copy_from_cache - elif function == 'timestamp-match': - function = self._changed_timestamp_match - elif not callable(function): - raise UserError, "Unknown Decider value %s" % repr(function) - - # We don't use AddMethod because we don't want to turn the - # function, which only expects three arguments, into a bound - # method, which would add self as an initial, fourth argument. - self.decide_target = function - self.decide_source = function - - self.copy_from_cache = copy_function - - def Detect(self, progs): - """Return the first available program in progs. - """ - if not SCons.Util.is_List(progs): - progs = [ progs ] - for prog in progs: - path = self.WhereIs(prog) - if path: return prog - return None - - def Dictionary(self, *args): - if not args: - return self._dict - dlist = map(lambda x, s=self: s._dict[x], args) - if len(dlist) == 1: - dlist = dlist[0] - return dlist - - def Dump(self, key = None): - """ - Using the standard Python pretty printer, dump the contents of the - scons build environment to stdout. - - If the key passed in is anything other than None, then that will - be used as an index into the build environment dictionary and - whatever is found there will be fed into the pretty printer. Note - that this key is case sensitive. - """ - import pprint - pp = pprint.PrettyPrinter(indent=2) - if key: - dict = self.Dictionary(key) - else: - dict = self.Dictionary() - return pp.pformat(dict) - - def FindIxes(self, paths, prefix, suffix): - """ - Search a list of paths for something that matches the prefix and suffix. - - paths - the list of paths or nodes. - prefix - construction variable for the prefix. - suffix - construction variable for the suffix. - """ - - suffix = self.subst('$'+suffix) - prefix = self.subst('$'+prefix) - - for path in paths: - dir,name = os.path.split(str(path)) - if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: - return path - - def ParseConfig(self, command, function=None, unique=1): - """ - Use the specified function to parse the output of the command - in order to modify the current environment. The 'command' can - be a string or a list of strings representing a command and - its arguments. 'Function' is an optional argument that takes - the environment, the output of the command, and the unique flag. - If no function is specified, MergeFlags, which treats the output - as the result of a typical 'X-config' command (i.e. gtk-config), - will merge the output into the appropriate variables. - """ - if function is None: - def parse_conf(env, cmd, unique=unique): - return env.MergeFlags(cmd, unique) - function = parse_conf - if SCons.Util.is_List(command): - command = string.join(command) - command = self.subst(command) - return function(self, self.backtick(command)) - - def ParseDepends(self, filename, must_exist=None, only_one=0): - """ - Parse a mkdep-style file for explicit dependencies. This is - completely abusable, and should be unnecessary in the "normal" - case of proper SCons configuration, but it may help make - the transition from a Make hierarchy easier for some people - to swallow. It can also be genuinely useful when using a tool - that can write a .d file, but for which writing a scanner would - be too complicated. - """ - filename = self.subst(filename) - try: - fp = open(filename, 'r') - except IOError: - if must_exist: - raise - return - lines = SCons.Util.LogicalLines(fp).readlines() - lines = filter(lambda l: l[0] != '#', lines) - tdlist = [] - for line in lines: - try: - target, depends = string.split(line, ':', 1) - except (AttributeError, TypeError, ValueError): - # Python 1.5.2 throws TypeError if line isn't a string, - # Python 2.x throws AttributeError because it tries - # to call line.split(). Either can throw ValueError - # if the line doesn't split into two or more elements. - pass - else: - tdlist.append((string.split(target), string.split(depends))) - if only_one: - targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist)) - if len(targets) > 1: - raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets) - for target, depends in tdlist: - self.Depends(target, depends) - - def Platform(self, platform): - platform = self.subst(platform) - return SCons.Platform.Platform(platform)(self) - - def Prepend(self, **kw): - """Prepend values to existing construction variables - in an Environment. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - # It would be easier on the eyes to write this using - # "continue" statements whenever we finish processing an item, - # but Python 1.5.2 apparently doesn't let you use "continue" - # within try:-except: blocks, so we have to nest our code. - try: - orig = self._dict[key] - except KeyError: - # No existing variable in the environment, so just set - # it to the new value. - self._dict[key] = val - else: - try: - # Check if the original looks like a dictionary. - # If it is, we can't just try adding the value because - # dictionaries don't have __add__() methods, and - # things like UserList will incorrectly coerce the - # original dict to a list (which we don't want). - update_dict = orig.update - except AttributeError: - try: - # Most straightforward: just try to add them - # together. This will work in most cases, when the - # original and new values are of compatible types. - self._dict[key] = val + orig - except (KeyError, TypeError): - try: - # Check if the added value is a list. - add_to_val = val.append - except AttributeError: - # The added value isn't a list, but the - # original is (by process of elimination), - # so insert the the new value in the original - # (if there's one to insert). - if val: - orig.insert(0, val) - else: - # The added value is a list, so append - # the original to it (if there's a value - # to append). - if orig: - add_to_val(orig) - self._dict[key] = val - else: - # The original looks like a dictionary, so update it - # based on what we think the value looks like. - if SCons.Util.is_List(val): - for v in val: - orig[v] = None - else: - try: - update_dict(val) - except (AttributeError, TypeError, ValueError): - if SCons.Util.is_Dict(val): - for k, v in val.items(): - orig[k] = v - else: - orig[val] = None - self.scanner_map_delete(kw) - - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, - delete_existing=1): - """Prepend path elements to the path 'name' in the 'ENV' - dictionary for this environment. Will only add any particular - path once, and will normpath and normcase all paths to help - assure this. This can also handle the case where the env - variable is a list instead of a string. - - If delete_existing is 0, a newpath which is already in the path - will not be moved to the front (it will be left where it is). - """ - - orig = '' - if self._dict.has_key(envname) and self._dict[envname].has_key(name): - orig = self._dict[envname][name] - - nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing) - - if not self._dict.has_key(envname): - self._dict[envname] = {} - - self._dict[envname][name] = nv - - def PrependUnique(self, delete_existing=0, **kw): - """Prepend values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is 1, removes existing values first, so - values move to front. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - if SCons.Util.is_List(val): - val = _delete_duplicates(val, not delete_existing) - if not self._dict.has_key(key) or self._dict[key] in ('', None): - self._dict[key] = val - elif SCons.Util.is_Dict(self._dict[key]) and \ - SCons.Util.is_Dict(val): - self._dict[key].update(val) - elif SCons.Util.is_List(val): - dk = self._dict[key] - if not SCons.Util.is_List(dk): - dk = [dk] - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - else: - val = filter(lambda x, dk=dk: x not in dk, val) - self._dict[key] = val + dk - else: - dk = self._dict[key] - if SCons.Util.is_List(dk): - # By elimination, val is not a list. Since dk is a - # list, wrap val in a list first. - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = [val] + dk - else: - if not val in dk: - self._dict[key] = [val] + dk - else: - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = val + dk - self.scanner_map_delete(kw) - - def Replace(self, **kw): - """Replace existing construction variables in an Environment - with new construction variables and/or values. - """ - try: - kwbd = kw['BUILDERS'] - except KeyError: - pass - else: - kwbd = semi_deepcopy(kwbd) - del kw['BUILDERS'] - self.__setitem__('BUILDERS', kwbd) - kw = copy_non_reserved_keywords(kw) - self._update(semi_deepcopy(kw)) - self.scanner_map_delete(kw) - - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): - """ - Replace old_prefix with new_prefix and old_suffix with new_suffix. - - env - Environment used to interpolate variables. - path - the path that will be modified. - old_prefix - construction variable for the old prefix. - old_suffix - construction variable for the old suffix. - new_prefix - construction variable for the new prefix. - new_suffix - construction variable for the new suffix. - """ - old_prefix = self.subst('$'+old_prefix) - old_suffix = self.subst('$'+old_suffix) - - new_prefix = self.subst('$'+new_prefix) - new_suffix = self.subst('$'+new_suffix) - - dir,name = os.path.split(str(path)) - if name[:len(old_prefix)] == old_prefix: - name = name[len(old_prefix):] - if name[-len(old_suffix):] == old_suffix: - name = name[:-len(old_suffix)] - return os.path.join(dir, new_prefix+name+new_suffix) - - def SetDefault(self, **kw): - for k in kw.keys(): - if self._dict.has_key(k): - del kw[k] - apply(self.Replace, (), kw) - - def _find_toolpath_dir(self, tp): - return self.fs.Dir(self.subst(tp)).srcnode().abspath - - def Tool(self, tool, toolpath=None, **kw): - if SCons.Util.is_String(tool): - tool = self.subst(tool) - if toolpath is None: - toolpath = self.get('toolpath', []) - toolpath = map(self._find_toolpath_dir, toolpath) - tool = apply(SCons.Tool.Tool, (tool, toolpath), kw) - tool(self) - - def WhereIs(self, prog, path=None, pathext=None, reject=[]): - """Find prog in the path. - """ - if path is None: - try: - path = self['ENV']['PATH'] - except KeyError: - pass - elif SCons.Util.is_String(path): - path = self.subst(path) - if pathext is None: - try: - pathext = self['ENV']['PATHEXT'] - except KeyError: - pass - elif SCons.Util.is_String(pathext): - pathext = self.subst(pathext) - prog = self.subst(prog) - path = SCons.Util.WhereIs(prog, path, pathext, reject) - if path: return path - return None - - ####################################################################### - # Public methods for doing real "SCons stuff" (manipulating - # dependencies, setting attributes on targets, etc.). These begin - # with upper-case letters. The essential characteristic of methods - # in this section is that they all *should* have corresponding - # same-named global functions. - ####################################################################### - - def Action(self, *args, **kw): - def subst_string(a, self=self): - if SCons.Util.is_String(a): - a = self.subst(a) - return a - nargs = map(subst_string, args) - nkw = self.subst_kw(kw) - return apply(SCons.Action.Action, nargs, nkw) - - def AddPreAction(self, files, action): - nodes = self.arg2nodes(files, self.fs.Entry) - action = SCons.Action.Action(action) - uniq = {} - for executor in map(lambda n: n.get_executor(), nodes): - uniq[executor] = 1 - for executor in uniq.keys(): - executor.add_pre_action(action) - return nodes - - def AddPostAction(self, files, action): - nodes = self.arg2nodes(files, self.fs.Entry) - action = SCons.Action.Action(action) - uniq = {} - for executor in map(lambda n: n.get_executor(), nodes): - uniq[executor] = 1 - for executor in uniq.keys(): - executor.add_post_action(action) - return nodes - - def Alias(self, target, source=[], action=None, **kw): - tlist = self.arg2nodes(target, self.ans.Alias) - if not SCons.Util.is_List(source): - source = [source] - source = filter(None, source) - - if not action: - if not source: - # There are no source files and no action, so just - # return a target list of classic Alias Nodes, without - # any builder. The externally visible effect is that - # this will make the wrapping Script.BuildTask class - # say that there's "Nothing to be done" for this Alias, - # instead of that it's "up to date." - return tlist - - # No action, but there are sources. Re-call all the target - # builders to add the sources to each target. - result = [] - for t in tlist: - bld = t.get_builder(AliasBuilder) - result.extend(bld(self, t, source)) - return result - - nkw = self.subst_kw(kw) - nkw.update({ - 'action' : SCons.Action.Action(action), - 'source_factory' : self.fs.Entry, - 'multi' : 1, - 'is_explicit' : None, - }) - bld = apply(SCons.Builder.Builder, (), nkw) - - # Apply the Builder separately to each target so that the Aliases - # stay separate. If we did one "normal" Builder call with the - # whole target list, then all of the target Aliases would be - # associated under a single Executor. - result = [] - for t in tlist: - # Calling the convert() method will cause a new Executor to be - # created from scratch, so we have to explicitly initialize - # it with the target's existing sources, plus our new ones, - # so nothing gets lost. - b = t.get_builder() - if b is None or b is AliasBuilder: - b = bld - else: - nkw['action'] = b.action + action - b = apply(SCons.Builder.Builder, (), nkw) - t.convert() - result.extend(b(self, t, t.sources + source)) - return result - - def AlwaysBuild(self, *targets): - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_always_build() - return tlist - - def BuildDir(self, *args, **kw): - if kw.has_key('build_dir'): - kw['variant_dir'] = kw['build_dir'] - del kw['build_dir'] - return apply(self.VariantDir, args, kw) - - def Builder(self, **kw): - nkw = self.subst_kw(kw) - return apply(SCons.Builder.Builder, [], nkw) - - def CacheDir(self, path): - import SCons.CacheDir - if not path is None: - path = self.subst(path) - self._CacheDir_path = path - - def Clean(self, targets, files): - global CleanTargets - tlist = self.arg2nodes(targets, self.fs.Entry) - flist = self.arg2nodes(files, self.fs.Entry) - for t in tlist: - try: - CleanTargets[t].extend(flist) - except KeyError: - CleanTargets[t] = flist - - def Configure(self, *args, **kw): - nargs = [self] - if args: - nargs = nargs + self.subst_list(args)[0] - nkw = self.subst_kw(kw) - nkw['_depth'] = kw.get('_depth', 0) + 1 - try: - nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) - except KeyError: - pass - return apply(SCons.SConf.SConf, nargs, nkw) - - def Command(self, target, source, action, **kw): - """Builds the supplied target files from the supplied - source files using the supplied action. Action may - be any type that the Builder constructor will accept - for an action.""" - bkw = { - 'action' : action, - 'target_factory' : self.fs.Entry, - 'source_factory' : self.fs.Entry, - } - try: bkw['source_scanner'] = kw['source_scanner'] - except KeyError: pass - else: del kw['source_scanner'] - bld = apply(SCons.Builder.Builder, (), bkw) - return apply(bld, (self, target, source), kw) - - def Depends(self, target, dependency): - """Explicity specify that 'target's depend on 'dependency'.""" - tlist = self.arg2nodes(target, self.fs.Entry) - dlist = self.arg2nodes(dependency, self.fs.Entry) - for t in tlist: - t.add_dependency(dlist) - return tlist - - def Dir(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(apply(self.fs.Dir, (e,) + args, kw)) - return result - return apply(self.fs.Dir, (s,) + args, kw) - - def NoClean(self, *targets): - """Tags a target so that it will not be cleaned by -c""" - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_noclean() - return tlist - - def NoCache(self, *targets): - """Tags a target so that it will not be cached""" - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_nocache() - return tlist - - def Entry(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(apply(self.fs.Entry, (e,) + args, kw)) - return result - return apply(self.fs.Entry, (s,) + args, kw) - - def Environment(self, **kw): - return apply(SCons.Environment.Environment, [], self.subst_kw(kw)) - - def Execute(self, action, *args, **kw): - """Directly execute an action through an Environment - """ - action = apply(self.Action, (action,) + args, kw) - result = action([], [], self) - if isinstance(result, SCons.Errors.BuildError): - errstr = result.errstr - if result.filename: - errstr = result.filename + ': ' + errstr - sys.stderr.write("scons: *** %s\n" % errstr) - return result.status - else: - return result - - def File(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(apply(self.fs.File, (e,) + args, kw)) - return result - return apply(self.fs.File, (s,) + args, kw) - - def FindFile(self, file, dirs): - file = self.subst(file) - nodes = self.arg2nodes(dirs, self.fs.Dir) - return SCons.Node.FS.find_file(file, tuple(nodes)) - - def Flatten(self, sequence): - return SCons.Util.flatten(sequence) - - def GetBuildPath(self, files): - result = map(str, self.arg2nodes(files, self.fs.Entry)) - if SCons.Util.is_List(files): - return result - else: - return result[0] - - def Glob(self, pattern, ondisk=True, source=False, strings=False): - return self.fs.Glob(self.subst(pattern), ondisk, source, strings) - - def Ignore(self, target, dependency): - """Ignore a dependency.""" - tlist = self.arg2nodes(target, self.fs.Entry) - dlist = self.arg2nodes(dependency, self.fs.Entry) - for t in tlist: - t.add_ignore(dlist) - return tlist - - def Literal(self, string): - return SCons.Subst.Literal(string) - - def Local(self, *targets): - ret = [] - for targ in targets: - if isinstance(targ, SCons.Node.Node): - targ.set_local() - ret.append(targ) - else: - for t in self.arg2nodes(targ, self.fs.Entry): - t.set_local() - ret.append(t) - return ret - - def Precious(self, *targets): - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_precious() - return tlist - - def Repository(self, *dirs, **kw): - dirs = self.arg2nodes(list(dirs), self.fs.Dir) - apply(self.fs.Repository, dirs, kw) - - def Requires(self, target, prerequisite): - """Specify that 'prerequisite' must be built before 'target', - (but 'target' does not actually depend on 'prerequisite' - and need not be rebuilt if it changes).""" - tlist = self.arg2nodes(target, self.fs.Entry) - plist = self.arg2nodes(prerequisite, self.fs.Entry) - for t in tlist: - t.add_prerequisite(plist) - return tlist - - def Scanner(self, *args, **kw): - nargs = [] - for arg in args: - if SCons.Util.is_String(arg): - arg = self.subst(arg) - nargs.append(arg) - nkw = self.subst_kw(kw) - return apply(SCons.Scanner.Base, nargs, nkw) - - def SConsignFile(self, name=".sconsign", dbm_module=None): - if not name is None: - name = self.subst(name) - if not os.path.isabs(name): - name = os.path.join(str(self.fs.SConstruct_dir), name) - if name: - name = os.path.normpath(name) - sconsign_dir = os.path.dirname(name) - if sconsign_dir and not os.path.exists(sconsign_dir): - self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) - SCons.SConsign.File(name, dbm_module) - - def SideEffect(self, side_effect, target): - """Tell scons that side_effects are built as side - effects of building targets.""" - side_effects = self.arg2nodes(side_effect, self.fs.Entry) - targets = self.arg2nodes(target, self.fs.Entry) - - for side_effect in side_effects: - if side_effect.multiple_side_effect_has_builder(): - raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) - side_effect.add_source(targets) - side_effect.side_effect = 1 - self.Precious(side_effect) - for target in targets: - target.side_effects.append(side_effect) - return side_effects - - def SourceCode(self, entry, builder): - """Arrange for a source code builder for (part of) a tree.""" - entries = self.arg2nodes(entry, self.fs.Entry) - for entry in entries: - entry.set_src_builder(builder) - return entries - - def SourceSignatures(self, type): - global _warn_source_signatures_deprecated - if _warn_source_signatures_deprecated: - msg = "The env.SourceSignatures() method is deprecated;\n" + \ - "\tconvert your build to use the env.Decider() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) - _warn_source_signatures_deprecated = False - type = self.subst(type) - self.src_sig_type = type - if type == 'MD5': - if not SCons.Util.md5: - raise UserError, "MD5 signatures are not available in this version of Python." - self.decide_source = self._changed_content - elif type == 'timestamp': - self.decide_source = self._changed_timestamp_match - else: - raise UserError, "Unknown source signature type '%s'" % type - - def Split(self, arg): - """This function converts a string or list into a list of strings - or Nodes. This makes things easier for users by allowing files to - be specified as a white-space separated list to be split. - The input rules are: - - A single string containing names separated by spaces. These will be - split apart at the spaces. - - A single Node instance - - A list containing either strings or Node instances. Any strings - in the list are not split at spaces. - In all cases, the function returns a list of Nodes and strings.""" - if SCons.Util.is_List(arg): - return map(self.subst, arg) - elif SCons.Util.is_String(arg): - return string.split(self.subst(arg)) - else: - return [self.subst(arg)] - - def TargetSignatures(self, type): - global _warn_target_signatures_deprecated - if _warn_target_signatures_deprecated: - msg = "The env.TargetSignatures() method is deprecated;\n" + \ - "\tconvert your build to use the env.Decider() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) - _warn_target_signatures_deprecated = False - type = self.subst(type) - self.tgt_sig_type = type - if type in ('MD5', 'content'): - if not SCons.Util.md5: - raise UserError, "MD5 signatures are not available in this version of Python." - self.decide_target = self._changed_content - elif type == 'timestamp': - self.decide_target = self._changed_timestamp_match - elif type == 'build': - self.decide_target = self._changed_build - elif type == 'source': - self.decide_target = self._changed_source - else: - raise UserError, "Unknown target signature type '%s'"%type - - def Value(self, value, built_value=None): - """ - """ - return SCons.Node.Python.Value(value, built_value) - - def VariantDir(self, variant_dir, src_dir, duplicate=1): - variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] - src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] - self.fs.VariantDir(variant_dir, src_dir, duplicate) - - def FindSourceFiles(self, node='.'): - """ returns a list of all source files. - """ - node = self.arg2nodes(node, self.fs.Entry)[0] - - sources = [] - # Uncomment this and get rid of the global definition when we - # drop support for pre-2.2 Python versions. - #def build_source(ss, result): - # for s in ss: - # if isinstance(s, SCons.Node.FS.Dir): - # build_source(s.all_children(), result) - # elif s.has_builder(): - # build_source(s.sources, result) - # elif isinstance(s.disambiguate(), SCons.Node.FS.File): - # result.append(s) - build_source(node.all_children(), sources) - - # now strip the build_node from the sources by calling the srcnode - # function - def get_final_srcnode(file): - srcnode = file.srcnode() - while srcnode != file.srcnode(): - srcnode = file.srcnode() - return srcnode - - # get the final srcnode for all nodes, this means stripping any - # attached build node. - map( get_final_srcnode, sources ) - - # remove duplicates - return list(set(sources)) - - def FindInstalledFiles(self): - """ returns the list of all targets of the Install and InstallAs Builder. - """ - from SCons.Tool import install - if install._UNIQUE_INSTALLED_FILES is None: - install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) - return install._UNIQUE_INSTALLED_FILES - -class OverrideEnvironment(Base): - """A proxy that overrides variables in a wrapped construction - environment by returning values from an overrides dictionary in - preference to values from the underlying subject environment. - - This is a lightweight (I hope) proxy that passes through most use of - attributes to the underlying Environment.Base class, but has just - enough additional methods defined to act like a real construction - environment with overridden values. It can wrap either a Base - construction environment, or another OverrideEnvironment, which - can in turn nest arbitrary OverrideEnvironments... - - Note that we do *not* call the underlying base class - (SubsitutionEnvironment) initialization, because we get most of those - from proxying the attributes of the subject construction environment. - But because we subclass SubstitutionEnvironment, this class also - has inherited arg2nodes() and subst*() methods; those methods can't - be proxied because they need *this* object's methods to fetch the - values from the overrides dictionary. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - def __init__(self, subject, overrides={}): - if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') - self.__dict__['__subject'] = subject - self.__dict__['overrides'] = overrides - - # Methods that make this class act like a proxy. - def __getattr__(self, name): - return getattr(self.__dict__['__subject'], name) - def __setattr__(self, name, value): - setattr(self.__dict__['__subject'], name, value) - - # Methods that make this class act like a dictionary. - def __getitem__(self, key): - try: - return self.__dict__['overrides'][key] - except KeyError: - return self.__dict__['__subject'].__getitem__(key) - def __setitem__(self, key, value): - if not is_valid_construction_var(key): - raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key - self.__dict__['overrides'][key] = value - def __delitem__(self, key): - try: - del self.__dict__['overrides'][key] - except KeyError: - deleted = 0 - else: - deleted = 1 - try: - result = self.__dict__['__subject'].__delitem__(key) - except KeyError: - if not deleted: - raise - result = None - return result - def get(self, key, default=None): - """Emulates the get() method of dictionaries.""" - try: - return self.__dict__['overrides'][key] - except KeyError: - return self.__dict__['__subject'].get(key, default) - def has_key(self, key): - try: - self.__dict__['overrides'][key] - return 1 - except KeyError: - return self.__dict__['__subject'].has_key(key) - def __contains__(self, key): - if self.__dict__['overrides'].__contains__(key): - return 1 - return self.__dict__['__subject'].__contains__(key) - def Dictionary(self): - """Emulates the items() method of dictionaries.""" - d = self.__dict__['__subject'].Dictionary().copy() - d.update(self.__dict__['overrides']) - return d - def items(self): - """Emulates the items() method of dictionaries.""" - return self.Dictionary().items() - - # Overridden private construction environment methods. - def _update(self, dict): - """Update an environment's values directly, bypassing the normal - checks that occur when users try to set items. - """ - self.__dict__['overrides'].update(dict) - - def gvars(self): - return self.__dict__['__subject'].gvars() - - def lvars(self): - lvars = self.__dict__['__subject'].lvars() - lvars.update(self.__dict__['overrides']) - return lvars - - # Overridden public construction environment methods. - def Replace(self, **kw): - kw = copy_non_reserved_keywords(kw) - self.__dict__['overrides'].update(semi_deepcopy(kw)) - -# The entry point that will be used by the external world -# to refer to a construction environment. This allows the wrapper -# interface to extend a construction environment for its own purposes -# by subclassing SCons.Environment.Base and then assigning the -# class to SCons.Environment.Environment. - -Environment = Base - -# An entry point for returning a proxy subclass instance that overrides -# the subst*() methods so they don't actually perform construction -# variable substitution. This is specifically intended to be the shim -# layer in between global function calls (which don't want construction -# variable substitution) and the DefaultEnvironment() (which would -# substitute variables if left to its own devices).""" -# -# We have to wrap this in a function that allows us to delay definition of -# the class until it's necessary, so that when it subclasses Environment -# it will pick up whatever Environment subclass the wrapper interface -# might have assigned to SCons.Environment.Environment. - -def NoSubstitutionProxy(subject): - class _NoSubstitutionProxy(Environment): - def __init__(self, subject): - self.__dict__['__subject'] = subject - def __getattr__(self, name): - return getattr(self.__dict__['__subject'], name) - def __setattr__(self, name, value): - return setattr(self.__dict__['__subject'], name, value) - def raw_to_mode(self, dict): - try: - raw = dict['raw'] - except KeyError: - pass - else: - del dict['raw'] - dict['mode'] = raw - def subst(self, string, *args, **kwargs): - return string - def subst_kw(self, kw, *args, **kwargs): - return kw - def subst_list(self, string, *args, **kwargs): - nargs = (string, self,) + args - nkw = kwargs.copy() - nkw['gvars'] = {} - self.raw_to_mode(nkw) - return apply(SCons.Subst.scons_subst_list, nargs, nkw) - def subst_target_source(self, string, *args, **kwargs): - nargs = (string, self,) + args - nkw = kwargs.copy() - nkw['gvars'] = {} - self.raw_to_mode(nkw) - return apply(SCons.Subst.scons_subst, nargs, nkw) - return _NoSubstitutionProxy(subject) diff --git a/tools/scons/scons-local-1.2.0/SCons/Errors.py b/tools/scons/scons-local-1.2.0/SCons/Errors.py deleted file mode 100644 index 8369873c7e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Errors.py +++ /dev/null @@ -1,198 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -"""SCons.Errors - -This file contains the exception classes used to handle internal -and user errors in SCons. - -""" - -__revision__ = "src/engine/SCons/Errors.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import exceptions - -class BuildError(Exception): - """ Errors occuring while building. - - BuildError have the following attributes: - - Information about the cause of the build error: - ----------------------------------------------- - - errstr : a description of the error message - - status : the return code of the action that caused the build - error. Must be set to a non-zero value even if the - build error is not due to an action returning a - non-zero returned code. - - exitstatus : SCons exit status due to this build error. - Must be nonzero unless due to an explicit Exit() - call. Not always the same as status, since - actions return a status code that should be - respected, but SCons typically exits with 2 - irrespective of the return value of the failed - action. - - filename : The name of the file or directory that caused the - build error. Set to None if no files are associated with - this error. This might be different from the target - being built. For example, failure to create the - directory in which the target file will appear. It - can be None if the error is not due to a particular - filename. - - exc_info : Info about exception that caused the build - error. Set to (None, None, None) if this build - error is not due to an exception. - - - Information about the cause of the location of the error: - --------------------------------------------------------- - - node : the error occured while building this target node(s) - - executor : the executor that caused the build to fail (might - be None if the build failures is not due to the - executor failing) - - action : the action that caused the build to fail (might be - None if the build failures is not due to the an - action failure) - - command : the command line for the action that caused the - build to fail (might be None if the build failures - is not due to the an action failure) - """ - - def __init__(self, - node=None, errstr="Unknown error", status=2, exitstatus=2, - filename=None, executor=None, action=None, command=None, - exc_info=(None, None, None)): - - self.errstr = errstr - self.status = status - self.exitstatus = exitstatus - self.filename = filename - self.exc_info = exc_info - - self.node = node - self.executor = executor - self.action = action - self.command = command - - Exception.__init__(self, node, errstr, status, exitstatus, filename, - executor, action, command, exc_info) - - def __str__(self): - if self.filename: - return self.filename + ': ' + self.errstr - else: - return self.errstr - -class InternalError(Exception): - pass - -class UserError(Exception): - pass - -class StopError(Exception): - pass - -class EnvironmentError(Exception): - pass - -class ExplicitExit(Exception): - def __init__(self, node=None, status=None, *args): - self.node = node - self.status = status - self.exitstatus = status - apply(Exception.__init__, (self,) + args) - -def convert_to_BuildError(status, exc_info=None): - """ - Convert any return code a BuildError Exception. - - `status' can either be a return code or an Exception. - The buildError.status we set here will normally be - used as the exit status of the "scons" process. - """ - if not exc_info and isinstance(status, Exception): - exc_info = (status.__class__, status, None) - - if isinstance(status, BuildError): - buildError = status - buildError.exitstatus = 2 # always exit with 2 on build errors - elif isinstance(status, ExplicitExit): - status = status.status - errstr = 'Explicit exit, status %s' % status - buildError = BuildError( - errstr=errstr, - status=status, # might be 0, OK here - exitstatus=status, # might be 0, OK here - exc_info=exc_info) - # TODO(1.5): - #elif isinstance(status, (StopError, UserError)): - elif isinstance(status, StopError) or isinstance(status, UserError): - buildError = BuildError( - errstr=str(status), - status=2, - exitstatus=2, - exc_info=exc_info) - elif isinstance(status, exceptions.EnvironmentError): - # If an IOError/OSError happens, raise a BuildError. - # Report the name of the file or directory that caused the - # error, which might be different from the target being built - # (for example, failure to create the directory in which the - # target file will appear). - try: filename = status.filename - except AttributeError: filename = None - buildError = BuildError( - errstr=status.strerror, - status=status.errno, - exitstatus=2, - filename=filename, - exc_info=exc_info) - elif isinstance(status, Exception): - buildError = BuildError( - errstr='%s : %s' % (status.__class__.__name__, status), - status=2, - exitstatus=2, - exc_info=exc_info) - elif SCons.Util.is_String(status): - buildError = BuildError( - errstr=status, - status=2, - exitstatus=2) - else: - buildError = BuildError( - errstr="Error %s" % status, - status=status, - exitstatus=2) - - #import sys - #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status)) - return buildError diff --git a/tools/scons/scons-local-1.2.0/SCons/Executor.py b/tools/scons/scons-local-1.2.0/SCons/Executor.py deleted file mode 100644 index a37da0719e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Executor.py +++ /dev/null @@ -1,393 +0,0 @@ -"""SCons.Executor - -A module for executing actions with specific lists of target and source -Nodes. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Executor.py 3842 2008/12/20 22:59:52 scons" - -import string - -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Memoize - - -class Executor: - """A class for controlling instances of executing an action. - - This largely exists to hold a single association of an action, - environment, list of environment override dictionaries, targets - and sources for later processing as needed. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self, action, env=None, overridelist=[{}], - targets=[], sources=[], builder_kw={}): - if __debug__: logInstanceCreation(self, 'Executor.Executor') - self.set_action_list(action) - self.pre_actions = [] - self.post_actions = [] - self.env = env - self.overridelist = overridelist - self.targets = targets - self.sources = sources[:] - self.sources_need_sorting = False - self.builder_kw = builder_kw - self._memo = {} - - def set_action_list(self, action): - import SCons.Util - if not SCons.Util.is_List(action): - if not action: - import SCons.Errors - raise SCons.Errors.UserError, "Executor must have an action." - action = [action] - self.action_list = action - - def get_action_list(self): - return self.pre_actions + self.action_list + self.post_actions - - memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) - - def get_build_env(self): - """Fetch or create the appropriate build Environment - for this Executor. - """ - try: - return self._memo['get_build_env'] - except KeyError: - pass - - # Create the build environment instance with appropriate - # overrides. These get evaluated against the current - # environment's construction variables so that users can - # add to existing values by referencing the variable in - # the expansion. - overrides = {} - for odict in self.overridelist: - overrides.update(odict) - - import SCons.Defaults - env = self.env or SCons.Defaults.DefaultEnvironment() - build_env = env.Override(overrides) - - self._memo['get_build_env'] = build_env - - return build_env - - def get_build_scanner_path(self, scanner): - """Fetch the scanner path for this executor's targets and sources. - """ - env = self.get_build_env() - try: - cwd = self.targets[0].cwd - except (IndexError, AttributeError): - cwd = None - return scanner.path(env, cwd, self.targets, self.get_sources()) - - def get_kw(self, kw={}): - result = self.builder_kw.copy() - result.update(kw) - return result - - def do_nothing(self, target, kw): - return 0 - - def do_execute(self, target, kw): - """Actually execute the action list.""" - env = self.get_build_env() - kw = self.get_kw(kw) - status = 0 - for act in self.get_action_list(): - status = apply(act, (self.targets, self.get_sources(), env), kw) - if isinstance(status, SCons.Errors.BuildError): - status.executor = self - raise status - elif status: - msg = "Error %s" % status - raise SCons.Errors.BuildError( - errstr=msg, - node=self.targets, - executor=self, - action=act) - return status - - # use extra indirection because with new-style objects (Python 2.2 - # and above) we can't override special methods, and nullify() needs - # to be able to do this. - - def __call__(self, target, **kw): - return self.do_execute(target, kw) - - def cleanup(self): - self._memo = {} - - def add_sources(self, sources): - """Add source files to this Executor's list. This is necessary - for "multi" Builders that can be called repeatedly to build up - a source file list for a given target.""" - self.sources.extend(sources) - self.sources_need_sorting = True - - def get_sources(self): - if self.sources_need_sorting: - self.sources = SCons.Util.uniquer_hashables(self.sources) - self.sources_need_sorting = False - return self.sources - - def prepare(self): - """ - Preparatory checks for whether this Executor can go ahead - and (try to) build its targets. - """ - for s in self.get_sources(): - if s.missing(): - msg = "Source `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError, msg % (s, self.targets[0]) - - def add_pre_action(self, action): - self.pre_actions.append(action) - - def add_post_action(self, action): - self.post_actions.append(action) - - # another extra indirection for new-style objects and nullify... - - def my_str(self): - env = self.get_build_env() - get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ - action.genstring(t, s, e) - return string.join(map(get, self.get_action_list()), "\n") - - - def __str__(self): - return self.my_str() - - def nullify(self): - self.cleanup() - self.do_execute = self.do_nothing - self.my_str = lambda S=self: '' - - memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) - - def get_contents(self): - """Fetch the signature contents. This is the main reason this - class exists, so we can compute this once and cache it regardless - of how many target or source Nodes there are. - """ - try: - return self._memo['get_contents'] - except KeyError: - pass - env = self.get_build_env() - get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ - action.get_contents(t, s, e) - result = string.join(map(get, self.get_action_list()), "") - self._memo['get_contents'] = result - return result - - def get_timestamp(self): - """Fetch a time stamp for this Executor. We don't have one, of - course (only files do), but this is the interface used by the - timestamp module. - """ - return 0 - - def scan_targets(self, scanner): - self.scan(scanner, self.targets) - - def scan_sources(self, scanner): - if self.sources: - self.scan(scanner, self.get_sources()) - - def scan(self, scanner, node_list): - """Scan a list of this Executor's files (targets or sources) for - implicit dependencies and update all of the targets with them. - This essentially short-circuits an N*M scan of the sources for - each individual target, which is a hell of a lot more efficient. - """ - env = self.get_build_env() - - deps = [] - if scanner: - for node in node_list: - node.disambiguate() - s = scanner.select(node) - if not s: - continue - path = self.get_build_scanner_path(s) - deps.extend(node.get_implicit_deps(env, s, path)) - else: - kw = self.get_kw() - for node in node_list: - node.disambiguate() - scanner = node.get_env_scanner(env, kw) - if not scanner: - continue - scanner = scanner.select(node) - if not scanner: - continue - path = self.get_build_scanner_path(scanner) - deps.extend(node.get_implicit_deps(env, scanner, path)) - - deps.extend(self.get_implicit_deps()) - - for tgt in self.targets: - tgt.add_to_implicit(deps) - - def _get_unignored_sources_key(self, ignore=()): - return tuple(ignore) - - memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) - - def get_unignored_sources(self, ignore=()): - ignore = tuple(ignore) - try: - memo_dict = self._memo['get_unignored_sources'] - except KeyError: - memo_dict = {} - self._memo['get_unignored_sources'] = memo_dict - else: - try: - return memo_dict[ignore] - except KeyError: - pass - - sourcelist = self.get_sources() - if ignore: - idict = {} - for i in ignore: - idict[i] = 1 - sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist) - - memo_dict[ignore] = sourcelist - - return sourcelist - - def _process_sources_key(self, func, ignore=()): - return (func, tuple(ignore)) - - memoizer_counters.append(SCons.Memoize.CountDict('process_sources', _process_sources_key)) - - def process_sources(self, func, ignore=()): - memo_key = (func, tuple(ignore)) - try: - memo_dict = self._memo['process_sources'] - except KeyError: - memo_dict = {} - self._memo['process_sources'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - result = map(func, self.get_unignored_sources(ignore)) - - memo_dict[memo_key] = result - - return result - - def get_implicit_deps(self): - """Return the executor's implicit dependencies, i.e. the nodes of - the commands to be executed.""" - result = [] - build_env = self.get_build_env() - for act in self.get_action_list(): - result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env)) - return result - -nullenv = None - -def get_NullEnvironment(): - """Use singleton pattern for Null Environments.""" - global nullenv - - import SCons.Util - class NullEnvironment(SCons.Util.Null): - import SCons.CacheDir - _CacheDir_path = None - _CacheDir = SCons.CacheDir.CacheDir(None) - def get_CacheDir(self): - return self._CacheDir - - if not nullenv: - nullenv = NullEnvironment() - return nullenv - -class Null: - """A null Executor, with a null build Environment, that does - nothing when the rest of the methods call it. - - This might be able to disapper when we refactor things to - disassociate Builders from Nodes entirely, so we're not - going to worry about unit tests for this--at least for now. - """ - def __init__(self, *args, **kw): - if __debug__: logInstanceCreation(self, 'Executor.Null') - self.targets = kw['targets'] - def get_build_env(self): - return get_NullEnvironment() - def get_build_scanner_path(self): - return None - def cleanup(self): - pass - def prepare(self): - pass - def get_unignored_sources(self, *args, **kw): - return tuple(()) - def get_action_list(self): - return [] - def __call__(self, *args, **kw): - return 0 - def get_contents(self): - return '' - - def _morph(self): - """Morph this Null executor to a real Executor object.""" - self.__class__ = Executor - self.__init__([], targets=self.targets) - - # The following methods require morphing this Null Executor to a - # real Executor object. - - def add_pre_action(self, action): - self._morph() - self.add_pre_action(action) - def add_post_action(self, action): - self._morph() - self.add_post_action(action) - def set_action_list(self, action): - self._morph() - self.set_action_list(action) - - diff --git a/tools/scons/scons-local-1.2.0/SCons/Job.py b/tools/scons/scons-local-1.2.0/SCons/Job.py deleted file mode 100644 index bcd39819a1..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Job.py +++ /dev/null @@ -1,429 +0,0 @@ -"""SCons.Job - -This module defines the Serial and Parallel classes that execute tasks to -complete a build. The Jobs class provides a higher level interface to start, -stop, and wait on jobs. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Job.py 3842 2008/12/20 22:59:52 scons" - -import os -import signal - -import SCons.Errors - -# The default stack size (in kilobytes) of the threads used to execute -# jobs in parallel. -# -# We use a stack size of 256 kilobytes. The default on some platforms -# is too large and prevents us from creating enough threads to fully -# parallelized the build. For example, the default stack size on linux -# is 8 MBytes. - -explicit_stack_size = None -default_stack_size = 256 - -interrupt_msg = 'Build interrupted.' - - -class InterruptState: - def __init__(self): - self.interrupted = False - - def set(self): - self.interrupted = True - - def __call__(self): - return self.interrupted - - -class Jobs: - """An instance of this class initializes N jobs, and provides - methods for starting, stopping, and waiting on all N jobs. - """ - - def __init__(self, num, taskmaster): - """ - create 'num' jobs using the given taskmaster. - - If 'num' is 1 or less, then a serial job will be used, - otherwise a parallel job with 'num' worker threads will - be used. - - The 'num_jobs' attribute will be set to the actual number of jobs - allocated. If more than one job is requested but the Parallel - class can't do it, it gets reset to 1. Wrapping interfaces that - care should check the value of 'num_jobs' after initialization. - """ - - self.job = None - if num > 1: - stack_size = explicit_stack_size - if stack_size is None: - stack_size = default_stack_size - - try: - self.job = Parallel(taskmaster, num, stack_size) - self.num_jobs = num - except NameError: - pass - if self.job is None: - self.job = Serial(taskmaster) - self.num_jobs = 1 - - def run(self, postfunc=lambda: None): - """Run the jobs. - - postfunc() will be invoked after the jobs has run. It will be - invoked even if the jobs are interrupted by a keyboard - interrupt (well, in fact by a signal such as either SIGINT, - SIGTERM or SIGHUP). The execution of postfunc() is protected - against keyboard interrupts and is guaranteed to run to - completion.""" - self._setup_sig_handler() - try: - self.job.start() - finally: - postfunc() - self._reset_sig_handler() - - def were_interrupted(self): - """Returns whether the jobs were interrupted by a signal.""" - return self.job.interrupted() - - def _setup_sig_handler(self): - """Setup an interrupt handler so that SCons can shutdown cleanly in - various conditions: - - a) SIGINT: Keyboard interrupt - b) SIGTERM: kill or system shutdown - c) SIGHUP: Controlling shell exiting - - We handle all of these cases by stopping the taskmaster. It - turns out that it very difficult to stop the build process - by throwing asynchronously an exception such as - KeyboardInterrupt. For example, the python Condition - variables (threading.Condition) and Queue's do not seem to - asynchronous-exception-safe. It would require adding a whole - bunch of try/finally block and except KeyboardInterrupt all - over the place. - - Note also that we have to be careful to handle the case when - SCons forks before executing another process. In that case, we - want the child to exit immediately. - """ - def handler(signum, stack, self=self, parentpid=os.getpid()): - if os.getpid() == parentpid: - self.job.taskmaster.stop() - self.job.interrupted.set() - else: - os._exit(2) - - self.old_sigint = signal.signal(signal.SIGINT, handler) - self.old_sigterm = signal.signal(signal.SIGTERM, handler) - try: - self.old_sighup = signal.signal(signal.SIGHUP, handler) - except AttributeError: - pass - - def _reset_sig_handler(self): - """Restore the signal handlers to their previous state (before the - call to _setup_sig_handler().""" - - signal.signal(signal.SIGINT, self.old_sigint) - signal.signal(signal.SIGTERM, self.old_sigterm) - try: - signal.signal(signal.SIGHUP, self.old_sighup) - except AttributeError: - pass - -class Serial: - """This class is used to execute tasks in series, and is more efficient - than Parallel, but is only appropriate for non-parallel builds. Only - one instance of this class should be in existence at a time. - - This class is not thread safe. - """ - - def __init__(self, taskmaster): - """Create a new serial job given a taskmaster. - - The taskmaster's next_task() method should return the next task - that needs to be executed, or None if there are no more tasks. The - taskmaster's executed() method will be called for each task when it - is successfully executed or failed() will be called if it failed to - execute (e.g. execute() raised an exception).""" - - self.taskmaster = taskmaster - self.interrupted = InterruptState() - - def start(self): - """Start the job. This will begin pulling tasks from the taskmaster - and executing them, and return when there are no more tasks. If a task - fails to execute (i.e. execute() raises an exception), then the job will - stop.""" - - while 1: - task = self.taskmaster.next_task() - - if task is None: - break - - try: - task.prepare() - if task.needs_execute(): - task.execute() - except: - if self.interrupted(): - try: - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - except: - task.exception_set() - else: - task.exception_set() - - # Let the failed() callback function arrange for the - # build to stop if that's appropriate. - task.failed() - else: - task.executed() - - task.postprocess() - self.taskmaster.cleanup() - - -# Trap import failure so that everything in the Job module but the -# Parallel class (and its dependent classes) will work if the interpreter -# doesn't support threads. -try: - import Queue - import threading -except ImportError: - pass -else: - class Worker(threading.Thread): - """A worker thread waits on a task to be posted to its request queue, - dequeues the task, executes it, and posts a tuple including the task - and a boolean indicating whether the task executed successfully. """ - - def __init__(self, requestQueue, resultsQueue, interrupted): - threading.Thread.__init__(self) - self.setDaemon(1) - self.requestQueue = requestQueue - self.resultsQueue = resultsQueue - self.interrupted = interrupted - self.start() - - def run(self): - while 1: - task = self.requestQueue.get() - - if task is None: - # The "None" value is used as a sentinel by - # ThreadPool.cleanup(). This indicates that there - # are no more tasks, so we should quit. - break - - try: - if self.interrupted(): - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - task.execute() - except: - task.exception_set() - ok = False - else: - ok = True - - self.resultsQueue.put((task, ok)) - - class ThreadPool: - """This class is responsible for spawning and managing worker threads.""" - - def __init__(self, num, stack_size, interrupted): - """Create the request and reply queues, and 'num' worker threads. - - One must specify the stack size of the worker threads. The - stack size is specified in kilobytes. - """ - self.requestQueue = Queue.Queue(0) - self.resultsQueue = Queue.Queue(0) - - try: - prev_size = threading.stack_size(stack_size*1024) - except AttributeError, e: - # Only print a warning if the stack size has been - # explicitly set. - if not explicit_stack_size is None: - msg = "Setting stack size is unsupported by this version of Python:\n " + \ - e.args[0] - SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) - except ValueError, e: - msg = "Setting stack size failed:\n " + str(e) - SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) - - # Create worker threads - self.workers = [] - for _ in range(num): - worker = Worker(self.requestQueue, self.resultsQueue, interrupted) - self.workers.append(worker) - - # Once we drop Python 1.5 we can change the following to: - #if 'prev_size' in locals(): - if 'prev_size' in locals().keys(): - threading.stack_size(prev_size) - - def put(self, task): - """Put task into request queue.""" - self.requestQueue.put(task) - - def get(self): - """Remove and return a result tuple from the results queue.""" - return self.resultsQueue.get() - - def preparation_failed(self, task): - self.resultsQueue.put((task, False)) - - def cleanup(self): - """ - Shuts down the thread pool, giving each worker thread a - chance to shut down gracefully. - """ - # For each worker thread, put a sentinel "None" value - # on the requestQueue (indicating that there's no work - # to be done) so that each worker thread will get one and - # terminate gracefully. - for _ in self.workers: - self.requestQueue.put(None) - - # Wait for all of the workers to terminate. - # - # If we don't do this, later Python versions (2.4, 2.5) often - # seem to raise exceptions during shutdown. This happens - # in requestQueue.get(), as an assertion failure that - # requestQueue.not_full is notified while not acquired, - # seemingly because the main thread has shut down (or is - # in the process of doing so) while the workers are still - # trying to pull sentinels off the requestQueue. - # - # Normally these terminations should happen fairly quickly, - # but we'll stick a one-second timeout on here just in case - # someone gets hung. - for worker in self.workers: - worker.join(1.0) - self.workers = [] - - class Parallel: - """This class is used to execute tasks in parallel, and is somewhat - less efficient than Serial, but is appropriate for parallel builds. - - This class is thread safe. - """ - - def __init__(self, taskmaster, num, stack_size): - """Create a new parallel job given a taskmaster. - - The taskmaster's next_task() method should return the next - task that needs to be executed, or None if there are no more - tasks. The taskmaster's executed() method will be called - for each task when it is successfully executed or failed() - will be called if the task failed to execute (i.e. execute() - raised an exception). - - Note: calls to taskmaster are serialized, but calls to - execute() on distinct tasks are not serialized, because - that is the whole point of parallel jobs: they can execute - multiple tasks simultaneously. """ - - self.taskmaster = taskmaster - self.interrupted = InterruptState() - self.tp = ThreadPool(num, stack_size, self.interrupted) - - self.maxjobs = num - - def start(self): - """Start the job. This will begin pulling tasks from the - taskmaster and executing them, and return when there are no - more tasks. If a task fails to execute (i.e. execute() raises - an exception), then the job will stop.""" - - jobs = 0 - - while 1: - # Start up as many available tasks as we're - # allowed to. - while jobs < self.maxjobs: - task = self.taskmaster.next_task() - if task is None: - break - - try: - # prepare task for execution - task.prepare() - except: - task.exception_set() - task.failed() - task.postprocess() - else: - if task.needs_execute(): - # dispatch task - self.tp.put(task) - jobs = jobs + 1 - else: - task.executed() - task.postprocess() - - if not task and not jobs: break - - # Let any/all completed tasks finish up before we go - # back and put the next batch of tasks on the queue. - while 1: - task, ok = self.tp.get() - jobs = jobs - 1 - - if ok: - task.executed() - else: - if self.interrupted(): - try: - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - except: - task.exception_set() - - # Let the failed() callback function arrange - # for the build to stop if that's appropriate. - task.failed() - - task.postprocess() - - if self.tp.resultsQueue.empty(): - break - - self.tp.cleanup() - self.taskmaster.cleanup() diff --git a/tools/scons/scons-local-1.2.0/SCons/Memoize.py b/tools/scons/scons-local-1.2.0/SCons/Memoize.py deleted file mode 100644 index f79dd6b930..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Memoize.py +++ /dev/null @@ -1,286 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Memoize.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Memoizer - -A metaclass implementation to count hits and misses of the computed -values that various methods cache in memory. - -Use of this modules assumes that wrapped methods be coded to cache their -values in a consistent way. Here is an example of wrapping a method -that returns a computed value, with no input parameters: - - memoizer_counters = [] # Memoization - - memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization - - def foo(self): - - try: # Memoization - return self._memo['foo'] # Memoization - except KeyError: # Memoization - pass # Memoization - - result = self.compute_foo_value() - - self._memo['foo'] = result # Memoization - - return result - -Here is an example of wrapping a method that will return different values -based on one or more input arguments: - - def _bar_key(self, argument): # Memoization - return argument # Memoization - - memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization - - def bar(self, argument): - - memo_key = argument # Memoization - try: # Memoization - memo_dict = self._memo['bar'] # Memoization - except KeyError: # Memoization - memo_dict = {} # Memoization - self._memo['dict'] = memo_dict # Memoization - else: # Memoization - try: # Memoization - return memo_dict[memo_key] # Memoization - except KeyError: # Memoization - pass # Memoization - - result = self.compute_bar_value(argument) - - memo_dict[memo_key] = result # Memoization - - return result - -At one point we avoided replicating this sort of logic in all the methods -by putting it right into this module, but we've moved away from that at -present (see the "Historical Note," below.). - -Deciding what to cache is tricky, because different configurations -can have radically different performance tradeoffs, and because the -tradeoffs involved are often so non-obvious. Consequently, deciding -whether or not to cache a given method will likely be more of an art than -a science, but should still be based on available data from this module. -Here are some VERY GENERAL guidelines about deciding whether or not to -cache return values from a method that's being called a lot: - - -- The first question to ask is, "Can we change the calling code - so this method isn't called so often?" Sometimes this can be - done by changing the algorithm. Sometimes the *caller* should - be memoized, not the method you're looking at. - - -- The memoized function should be timed with multiple configurations - to make sure it doesn't inadvertently slow down some other - configuration. - - -- When memoizing values based on a dictionary key composed of - input arguments, you don't need to use all of the arguments - if some of them don't affect the return values. - -Historical Note: The initial Memoizer implementation actually handled -the caching of values for the wrapped methods, based on a set of generic -algorithms for computing hashable values based on the method's arguments. -This collected caching logic nicely, but had two drawbacks: - - Running arguments through a generic key-conversion mechanism is slower - (and less flexible) than just coding these things directly. Since the - methods that need memoized values are generally performance-critical, - slowing them down in order to collect the logic isn't the right - tradeoff. - - Use of the memoizer really obscured what was being called, because - all the memoized methods were wrapped with re-used generic methods. - This made it more difficult, for example, to use the Python profiler - to figure out how to optimize the underlying methods. -""" - -import new - -# A flag controlling whether or not we actually use memoization. -use_memoizer = None - -CounterList = [] - -class Counter: - """ - Base class for counting memoization hits and misses. - - We expect that the metaclass initialization will have filled in - the .name attribute that represents the name of the function - being counted. - """ - def __init__(self, method_name): - """ - """ - self.method_name = method_name - self.hit = 0 - self.miss = 0 - CounterList.append(self) - def display(self): - fmt = " %7d hits %7d misses %s()" - print fmt % (self.hit, self.miss, self.name) - def __cmp__(self, other): - try: - return cmp(self.name, other.name) - except AttributeError: - return 0 - -class CountValue(Counter): - """ - A counter class for simple, atomic memoized values. - - A CountValue object should be instantiated in a class for each of - the class's methods that memoizes its return value by simply storing - the return value in its _memo dictionary. - - We expect that the metaclass initialization will fill in the - .underlying_method attribute with the method that we're wrapping. - We then call the underlying_method method after counting whether - its memoized value has already been set (a hit) or not (a miss). - """ - def __call__(self, *args, **kw): - obj = args[0] - if obj._memo.has_key(self.method_name): - self.hit = self.hit + 1 - else: - self.miss = self.miss + 1 - return apply(self.underlying_method, args, kw) - -class CountDict(Counter): - """ - A counter class for memoized values stored in a dictionary, with - keys based on the method's input arguments. - - A CountDict object is instantiated in a class for each of the - class's methods that memoizes its return value in a dictionary, - indexed by some key that can be computed from one or more of - its input arguments. - - We expect that the metaclass initialization will fill in the - .underlying_method attribute with the method that we're wrapping. - We then call the underlying_method method after counting whether the - computed key value is already present in the memoization dictionary - (a hit) or not (a miss). - """ - def __init__(self, method_name, keymaker): - """ - """ - Counter.__init__(self, method_name) - self.keymaker = keymaker - def __call__(self, *args, **kw): - obj = args[0] - try: - memo_dict = obj._memo[self.method_name] - except KeyError: - self.miss = self.miss + 1 - else: - key = apply(self.keymaker, args, kw) - if memo_dict.has_key(key): - self.hit = self.hit + 1 - else: - self.miss = self.miss + 1 - return apply(self.underlying_method, args, kw) - -class Memoizer: - """Object which performs caching of method calls for its 'primary' - instance.""" - - def __init__(self): - pass - -# Find out if we support metaclasses (Python 2.2 and later). - -class M: - def __init__(cls, name, bases, cls_dict): - cls.use_metaclass = 1 - def fake_method(self): - pass - new.instancemethod(fake_method, None, cls) - -try: - class A: - __metaclass__ = M - - use_metaclass = A.use_metaclass -except AttributeError: - use_metaclass = None - reason = 'no metaclasses' -except TypeError: - use_metaclass = None - reason = 'new.instancemethod() bug' -else: - del A - -del M - -if not use_metaclass: - - def Dump(title): - pass - - try: - class Memoized_Metaclass(type): - # Just a place-holder so pre-metaclass Python versions don't - # have to have special code for the Memoized classes. - pass - except TypeError: - class Memoized_Metaclass: - # A place-holder so pre-metaclass Python versions don't - # have to have special code for the Memoized classes. - pass - - def EnableMemoization(): - import SCons.Warnings - msg = 'memoization is not supported in this version of Python (%s)' - raise SCons.Warnings.NoMetaclassSupportWarning, msg % reason - -else: - - def Dump(title=None): - if title: - print title - CounterList.sort() - for counter in CounterList: - counter.display() - - class Memoized_Metaclass(type): - def __init__(cls, name, bases, cls_dict): - super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict) - - for counter in cls_dict.get('memoizer_counters', []): - method_name = counter.method_name - - counter.name = cls.__name__ + '.' + method_name - counter.underlying_method = cls_dict[method_name] - - replacement_method = new.instancemethod(counter, None, cls) - setattr(cls, method_name, replacement_method) - - def EnableMemoization(): - global use_memoizer - use_memoizer = 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Node/Alias.py b/tools/scons/scons-local-1.2.0/SCons/Node/Alias.py deleted file mode 100644 index 4ce9fff7d1..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Node/Alias.py +++ /dev/null @@ -1,147 +0,0 @@ - -"""scons.Node.Alias - -Alias nodes. - -This creates a hash of global Aliases (dummy targets). - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Node/Alias.py 3842 2008/12/20 22:59:52 scons" - -import string -import UserDict - -import SCons.Errors -import SCons.Node -import SCons.Util - -class AliasNameSpace(UserDict.UserDict): - def Alias(self, name, **kw): - if isinstance(name, SCons.Node.Alias.Alias): - return name - try: - a = self[name] - except KeyError: - a = apply(SCons.Node.Alias.Alias, (name,), kw) - self[name] = a - return a - - def lookup(self, name, **kw): - try: - return self[name] - except KeyError: - return None - -class AliasNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - field_list = ['csig'] - def str_to_node(self, s): - return default_ans.Alias(s) - -class AliasBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -class Alias(SCons.Node.Node): - - NodeInfo = AliasNodeInfo - BuildInfo = AliasBuildInfo - - def __init__(self, name): - SCons.Node.Node.__init__(self) - self.name = name - - def str_for_display(self): - return '"' + self.__str__() + '"' - - def __str__(self): - return self.name - - def make_ready(self): - self.get_csig() - - really_build = SCons.Node.Node.build - is_up_to_date = SCons.Node.Node.children_are_up_to_date - - def is_under(self, dir): - # Make Alias nodes get built regardless of - # what directory scons was run from. Alias nodes - # are outside the filesystem: - return 1 - - def get_contents(self): - """The contents of an alias is the concatenation - of the content signatures of all its sources.""" - childsigs = map(lambda n: n.get_csig(), self.children()) - return string.join(childsigs, '') - - def sconsign(self): - """An Alias is not recorded in .sconsign files""" - pass - - # - # - # - - def changed_since_last_build(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def build(self): - """A "builder" for aliases.""" - pass - - def convert(self): - try: del self.builder - except AttributeError: pass - self.reset_executor() - self.build = self.really_build - - def get_csig(self): - """ - Generate a node's content signature, the digested signature - of its content. - - node - the node - cache - alternate node to use for the signature cache - returns - the content signature - """ - try: - return self.ninfo.csig - except AttributeError: - pass - - contents = self.get_contents() - csig = SCons.Util.MD5signature(contents) - self.get_ninfo().csig = csig - return csig - -default_ans = AliasNameSpace() - -SCons.Node.arg2nodes_lookups.append(default_ans.lookup) diff --git a/tools/scons/scons-local-1.2.0/SCons/Node/FS.py b/tools/scons/scons-local-1.2.0/SCons/Node/FS.py deleted file mode 100644 index 15368f029f..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Node/FS.py +++ /dev/null @@ -1,3075 +0,0 @@ -"""scons.Node.FS - -File system nodes. - -These Nodes represent the canonical external objects that people think -of when they think of building software: files and directories. - -This holds a "default_fs" variable that should be initialized with an FS -that can be used by scripts or modules looking for the canonical default. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Node/FS.py 3842 2008/12/20 22:59:52 scons" - -import fnmatch -from itertools import izip -import os -import os.path -import re -import shutil -import stat -import string -import sys -import time -import cStringIO - -import SCons.Action -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Memoize -import SCons.Node -import SCons.Node.Alias -import SCons.Subst -import SCons.Util -import SCons.Warnings - -from SCons.Debug import Trace - -do_store_info = True - - -class EntryProxyAttributeError(AttributeError): - """ - An AttributeError subclass for recording and displaying the name - of the underlying Entry involved in an AttributeError exception. - """ - def __init__(self, entry_proxy, attribute): - AttributeError.__init__(self) - self.entry_proxy = entry_proxy - self.attribute = attribute - def __str__(self): - entry = self.entry_proxy.get() - fmt = "%s instance %s has no attribute %s" - return fmt % (entry.__class__.__name__, - repr(entry.name), - repr(self.attribute)) - -# The max_drift value: by default, use a cached signature value for -# any file that's been untouched for more than two days. -default_max_drift = 2*24*60*60 - -# -# We stringify these file system Nodes a lot. Turning a file system Node -# into a string is non-trivial, because the final string representation -# can depend on a lot of factors: whether it's a derived target or not, -# whether it's linked to a repository or source directory, and whether -# there's duplication going on. The normal technique for optimizing -# calculations like this is to memoize (cache) the string value, so you -# only have to do the calculation once. -# -# A number of the above factors, however, can be set after we've already -# been asked to return a string for a Node, because a Repository() or -# VariantDir() call or the like may not occur until later in SConscript -# files. So this variable controls whether we bother trying to save -# string values for Nodes. The wrapper interface can set this whenever -# they're done mucking with Repository and VariantDir and the other stuff, -# to let this module know it can start returning saved string values -# for Nodes. -# -Save_Strings = None - -def save_strings(val): - global Save_Strings - Save_Strings = val - -# -# Avoid unnecessary function calls by recording a Boolean value that -# tells us whether or not os.path.splitdrive() actually does anything -# on this system, and therefore whether we need to bother calling it -# when looking up path names in various methods below. -# - -do_splitdrive = None - -def initialize_do_splitdrive(): - global do_splitdrive - drive, path = os.path.splitdrive('X:/foo') - do_splitdrive = not not drive - -initialize_do_splitdrive() - -# - -needs_normpath_check = None - -def initialize_normpath_check(): - """ - Initialize the normpath_check regular expression. - - This function is used by the unit tests to re-initialize the pattern - when testing for behavior with different values of os.sep. - """ - global needs_normpath_check - if os.sep == '/': - pattern = r'.*/|\.$|\.\.$' - else: - pattern = r'.*[/%s]|\.$|\.\.$' % re.escape(os.sep) - needs_normpath_check = re.compile(pattern) - -initialize_normpath_check() - -# -# SCons.Action objects for interacting with the outside world. -# -# The Node.FS methods in this module should use these actions to -# create and/or remove files and directories; they should *not* use -# os.{link,symlink,unlink,mkdir}(), etc., directly. -# -# Using these SCons.Action objects ensures that descriptions of these -# external activities are properly displayed, that the displays are -# suppressed when the -s (silent) option is used, and (most importantly) -# the actions are disabled when the the -n option is used, in which case -# there should be *no* changes to the external file system(s)... -# - -if hasattr(os, 'link'): - def _hardlink_func(fs, src, dst): - # If the source is a symlink, we can't just hard-link to it - # because a relative symlink may point somewhere completely - # different. We must disambiguate the symlink and then - # hard-link the final destination file. - while fs.islink(src): - link = fs.readlink(src) - if not os.path.isabs(link): - src = link - else: - src = os.path.join(os.path.dirname(src), link) - fs.link(src, dst) -else: - _hardlink_func = None - -if hasattr(os, 'symlink'): - def _softlink_func(fs, src, dst): - fs.symlink(src, dst) -else: - _softlink_func = None - -def _copy_func(fs, src, dest): - shutil.copy2(src, dest) - st = fs.stat(src) - fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - -Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', - 'hard-copy', 'soft-copy', 'copy'] - -Link_Funcs = [] # contains the callables of the specified duplication style - -def set_duplicate(duplicate): - # Fill in the Link_Funcs list according to the argument - # (discarding those not available on the platform). - - # Set up the dictionary that maps the argument names to the - # underlying implementations. We do this inside this function, - # not in the top-level module code, so that we can remap os.link - # and os.symlink for testing purposes. - link_dict = { - 'hard' : _hardlink_func, - 'soft' : _softlink_func, - 'copy' : _copy_func - } - - if not duplicate in Valid_Duplicates: - raise SCons.Errors.InternalError, ("The argument of set_duplicate " - "should be in Valid_Duplicates") - global Link_Funcs - Link_Funcs = [] - for func in string.split(duplicate,'-'): - if link_dict[func]: - Link_Funcs.append(link_dict[func]) - -def LinkFunc(target, source, env): - # Relative paths cause problems with symbolic links, so - # we use absolute paths, which may be a problem for people - # who want to move their soft-linked src-trees around. Those - # people should use the 'hard-copy' mode, softlinks cannot be - # used for that; at least I have no idea how ... - src = source[0].abspath - dest = target[0].abspath - dir, file = os.path.split(dest) - if dir and not target[0].fs.isdir(dir): - os.makedirs(dir) - if not Link_Funcs: - # Set a default order of link functions. - set_duplicate('hard-soft-copy') - fs = source[0].fs - # Now link the files with the previously specified order. - for func in Link_Funcs: - try: - func(fs, src, dest) - break - except (IOError, OSError): - # An OSError indicates something happened like a permissions - # problem or an attempt to symlink across file-system - # boundaries. An IOError indicates something like the file - # not existing. In either case, keeping trying additional - # functions in the list and only raise an error if the last - # one failed. - if func == Link_Funcs[-1]: - # exception of the last link method (copy) are fatal - raise - return 0 - -Link = SCons.Action.Action(LinkFunc, None) -def LocalString(target, source, env): - return 'Local copy of %s from %s' % (target[0], source[0]) - -LocalCopy = SCons.Action.Action(LinkFunc, LocalString) - -def UnlinkFunc(target, source, env): - t = target[0] - t.fs.unlink(t.abspath) - return 0 - -Unlink = SCons.Action.Action(UnlinkFunc, None) - -def MkdirFunc(target, source, env): - t = target[0] - if not t.exists(): - t.fs.mkdir(t.abspath) - return 0 - -Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) - -MkdirBuilder = None - -def get_MkdirBuilder(): - global MkdirBuilder - if MkdirBuilder is None: - import SCons.Builder - import SCons.Defaults - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - MkdirBuilder = SCons.Builder.Builder(action = Mkdir, - env = None, - explain = None, - is_explicit = None, - target_scanner = SCons.Defaults.DirEntryScanner, - name = "MkdirBuilder") - return MkdirBuilder - -class _Null: - pass - -_null = _Null() - -DefaultSCCSBuilder = None -DefaultRCSBuilder = None - -def get_DefaultSCCSBuilder(): - global DefaultSCCSBuilder - if DefaultSCCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - DefaultSCCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultSCCSBuilder") - return DefaultSCCSBuilder - -def get_DefaultRCSBuilder(): - global DefaultRCSBuilder - if DefaultRCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - DefaultRCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultRCSBuilder") - return DefaultRCSBuilder - -# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. -_is_cygwin = sys.platform == "cygwin" -if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: - def _my_normcase(x): - return x -else: - def _my_normcase(x): - return string.upper(x) - - - -class DiskChecker: - def __init__(self, type, do, ignore): - self.type = type - self.do = do - self.ignore = ignore - self.set_do() - def set_do(self): - self.__call__ = self.do - def set_ignore(self): - self.__call__ = self.ignore - def set(self, list): - if self.type in list: - self.set_do() - else: - self.set_ignore() - -def do_diskcheck_match(node, predicate, errorfmt): - result = predicate() - try: - # If calling the predicate() cached a None value from stat(), - # remove it so it doesn't interfere with later attempts to - # build this Node as we walk the DAG. (This isn't a great way - # to do this, we're reaching into an interface that doesn't - # really belong to us, but it's all about performance, so - # for now we'll just document the dependency...) - if node._memo['stat'] is None: - del node._memo['stat'] - except (AttributeError, KeyError): - pass - if result: - raise TypeError, errorfmt % node.abspath - -def ignore_diskcheck_match(node, predicate, errorfmt): - pass - -def do_diskcheck_rcs(node, name): - try: - rcs_dir = node.rcs_dir - except AttributeError: - if node.entry_exists_on_disk('RCS'): - rcs_dir = node.Dir('RCS') - else: - rcs_dir = None - node.rcs_dir = rcs_dir - if rcs_dir: - return rcs_dir.entry_exists_on_disk(name+',v') - return None - -def ignore_diskcheck_rcs(node, name): - return None - -def do_diskcheck_sccs(node, name): - try: - sccs_dir = node.sccs_dir - except AttributeError: - if node.entry_exists_on_disk('SCCS'): - sccs_dir = node.Dir('SCCS') - else: - sccs_dir = None - node.sccs_dir = sccs_dir - if sccs_dir: - return sccs_dir.entry_exists_on_disk('s.'+name) - return None - -def ignore_diskcheck_sccs(node, name): - return None - -diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) -diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) -diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) - -diskcheckers = [ - diskcheck_match, - diskcheck_rcs, - diskcheck_sccs, -] - -def set_diskcheck(list): - for dc in diskcheckers: - dc.set(list) - -def diskcheck_types(): - return map(lambda dc: dc.type, diskcheckers) - - - -class EntryProxy(SCons.Util.Proxy): - def __get_abspath(self): - entry = self.get() - return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), - entry.name + "_abspath") - - def __get_filebase(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], - name + "_filebase") - - def __get_suffix(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1], - name + "_suffix") - - def __get_file(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(name, name + "_file") - - def __get_base_path(self): - """Return the file's directory and file name, with the - suffix stripped.""" - entry = self.get() - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], - entry.name + "_base") - - def __get_posix_path(self): - """Return the path with / as the path separator, - regardless of platform.""" - if os.sep == '/': - return self - else: - entry = self.get() - r = string.replace(entry.get_path(), os.sep, '/') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") - - def __get_windows_path(self): - """Return the path with \ as the path separator, - regardless of platform.""" - if os.sep == '\\': - return self - else: - entry = self.get() - r = string.replace(entry.get_path(), os.sep, '\\') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") - - def __get_srcnode(self): - return EntryProxy(self.get().srcnode()) - - def __get_srcdir(self): - """Returns the directory containing the source node linked to this - node via VariantDir(), or the directory of this node if not linked.""" - return EntryProxy(self.get().srcnode().dir) - - def __get_rsrcnode(self): - return EntryProxy(self.get().srcnode().rfile()) - - def __get_rsrcdir(self): - """Returns the directory containing the source node linked to this - node via VariantDir(), or the directory of this node if not linked.""" - return EntryProxy(self.get().srcnode().rfile().dir) - - def __get_dir(self): - return EntryProxy(self.get().dir) - - dictSpecialAttrs = { "base" : __get_base_path, - "posix" : __get_posix_path, - "windows" : __get_windows_path, - "win32" : __get_windows_path, - "srcpath" : __get_srcnode, - "srcdir" : __get_srcdir, - "dir" : __get_dir, - "abspath" : __get_abspath, - "filebase" : __get_filebase, - "suffix" : __get_suffix, - "file" : __get_file, - "rsrcpath" : __get_rsrcnode, - "rsrcdir" : __get_rsrcdir, - } - - def __getattr__(self, name): - # This is how we implement the "special" attributes - # such as base, posix, srcdir, etc. - try: - attr_function = self.dictSpecialAttrs[name] - except KeyError: - try: - attr = SCons.Util.Proxy.__getattr__(self, name) - except AttributeError, e: - # Raise our own AttributeError subclass with an - # overridden __str__() method that identifies the - # name of the entry that caused the exception. - raise EntryProxyAttributeError(self, name) - return attr - else: - return attr_function(self) - -class Base(SCons.Node.Node): - """A generic class for file system entries. This class is for - when we don't know yet whether the entry being looked up is a file - or a directory. Instances of this class can morph into either - Dir or File objects by a later, more precise lookup. - - Note: this class does not define __cmp__ and __hash__ for - efficiency reasons. SCons does a lot of comparing of - Node.FS.{Base,Entry,File,Dir} objects, so those operations must be - as fast as possible, which means we want to use Python's built-in - object identity comparisons. - """ - - memoizer_counters = [] - - def __init__(self, name, directory, fs): - """Initialize a generic Node.FS.Base object. - - Call the superclass initialization, take care of setting up - our relative and absolute paths, identify our parent - directory, and indicate that this node should use - signatures.""" - if __debug__: logInstanceCreation(self, 'Node.FS.Base') - SCons.Node.Node.__init__(self) - - self.name = name - self.suffix = SCons.Util.splitext(name)[1] - self.fs = fs - - assert directory, "A directory must be provided" - - self.abspath = directory.entry_abspath(name) - self.labspath = directory.entry_labspath(name) - if directory.path == '.': - self.path = name - else: - self.path = directory.entry_path(name) - if directory.tpath == '.': - self.tpath = name - else: - self.tpath = directory.entry_tpath(name) - self.path_elements = directory.path_elements + [self] - - self.dir = directory - self.cwd = None # will hold the SConscript directory for target nodes - self.duplicate = directory.duplicate - - def str_for_display(self): - return '"' + self.__str__() + '"' - - def must_be_same(self, klass): - """ - This node, which already existed, is being looked up as the - specified klass. Raise an exception if it isn't. - """ - if self.__class__ is klass or klass is Entry: - return - raise TypeError, "Tried to lookup %s '%s' as a %s." %\ - (self.__class__.__name__, self.path, klass.__name__) - - def get_dir(self): - return self.dir - - def get_suffix(self): - return self.suffix - - def rfile(self): - return self - - def __str__(self): - """A Node.FS.Base object's string representation is its path - name.""" - global Save_Strings - if Save_Strings: - return self._save_str() - return self._get_str() - - memoizer_counters.append(SCons.Memoize.CountValue('_save_str')) - - def _save_str(self): - try: - return self._memo['_save_str'] - except KeyError: - pass - result = self._get_str() - self._memo['_save_str'] = result - return result - - def _get_str(self): - global Save_Strings - if self.duplicate or self.is_derived(): - return self.get_path() - srcnode = self.srcnode() - if srcnode.stat() is None and self.stat() is not None: - result = self.get_path() - else: - result = srcnode.get_path() - if not Save_Strings: - # We're not at the point where we're saving the string string - # representations of FS Nodes (because we haven't finished - # reading the SConscript files and need to have str() return - # things relative to them). That also means we can't yet - # cache values returned (or not returned) by stat(), since - # Python code in the SConscript files might still create - # or otherwise affect the on-disk file. So get rid of the - # values that the underlying stat() method saved. - try: del self._memo['stat'] - except KeyError: pass - if self is not srcnode: - try: del srcnode._memo['stat'] - except KeyError: pass - return result - - rstr = __str__ - - memoizer_counters.append(SCons.Memoize.CountValue('stat')) - - def stat(self): - try: return self._memo['stat'] - except KeyError: pass - try: result = self.fs.stat(self.abspath) - except os.error: result = None - self._memo['stat'] = result - return result - - def exists(self): - return self.stat() is not None - - def rexists(self): - return self.rfile().exists() - - def getmtime(self): - st = self.stat() - if st: return st[stat.ST_MTIME] - else: return None - - def getsize(self): - st = self.stat() - if st: return st[stat.ST_SIZE] - else: return None - - def isdir(self): - st = self.stat() - return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) - - def isfile(self): - st = self.stat() - return st is not None and stat.S_ISREG(st[stat.ST_MODE]) - - if hasattr(os, 'symlink'): - def islink(self): - try: st = self.fs.lstat(self.abspath) - except os.error: return 0 - return stat.S_ISLNK(st[stat.ST_MODE]) - else: - def islink(self): - return 0 # no symlinks - - def is_under(self, dir): - if self is dir: - return 1 - else: - return self.dir.is_under(dir) - - def set_local(self): - self._local = 1 - - def srcnode(self): - """If this node is in a build path, return the node - corresponding to its source file. Otherwise, return - ourself. - """ - srcdir_list = self.dir.srcdir_list() - if srcdir_list: - srcnode = srcdir_list[0].Entry(self.name) - srcnode.must_be_same(self.__class__) - return srcnode - return self - - def get_path(self, dir=None): - """Return path relative to the current working directory of the - Node.FS.Base object that owns us.""" - if not dir: - dir = self.fs.getcwd() - if self == dir: - return '.' - path_elems = self.path_elements - try: i = path_elems.index(dir) - except ValueError: pass - else: path_elems = path_elems[i+1:] - path_elems = map(lambda n: n.name, path_elems) - return string.join(path_elems, os.sep) - - def set_src_builder(self, builder): - """Set the source code builder for this node.""" - self.sbuilder = builder - if not self.has_builder(): - self.builder_set(builder) - - def src_builder(self): - """Fetch the source code builder for this node. - - If there isn't one, we cache the source code builder specified - for the directory (which in turn will cache the value from its - parent directory, and so on up to the file system root). - """ - try: - scb = self.sbuilder - except AttributeError: - scb = self.dir.src_builder() - self.sbuilder = scb - return scb - - def get_abspath(self): - """Get the absolute path of the file.""" - return self.abspath - - def for_signature(self): - # Return just our name. Even an absolute path would not work, - # because that can change thanks to symlinks or remapped network - # paths. - return self.name - - def get_subst_proxy(self): - try: - return self._proxy - except AttributeError: - ret = EntryProxy(self) - self._proxy = ret - return ret - - def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): - """ - - Generates a target entry that corresponds to this entry (usually - a source file) with the specified prefix and suffix. - - Note that this method can be overridden dynamically for generated - files that need different behavior. See Tool/swig.py for - an example. - """ - return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) - - def _Rfindalldirs_key(self, pathlist): - return pathlist - - memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key)) - - def Rfindalldirs(self, pathlist): - """ - Return all of the directories for a given path list, including - corresponding "backing" directories in any repositories. - - The Node lookups are relative to this Node (typically a - directory), so memoizing result saves cycles from looking - up the same path for each target in a given directory. - """ - try: - memo_dict = self._memo['Rfindalldirs'] - except KeyError: - memo_dict = {} - self._memo['Rfindalldirs'] = memo_dict - else: - try: - return memo_dict[pathlist] - except KeyError: - pass - - create_dir_relative_to_self = self.Dir - result = [] - for path in pathlist: - if isinstance(path, SCons.Node.Node): - result.append(path) - else: - dir = create_dir_relative_to_self(path) - result.extend(dir.get_all_rdirs()) - - memo_dict[pathlist] = result - - return result - - def RDirs(self, pathlist): - """Search for a list of directories in the Repository list.""" - cwd = self.cwd or self.fs._cwd - return cwd.Rfindalldirs(pathlist) - - memoizer_counters.append(SCons.Memoize.CountValue('rentry')) - - def rentry(self): - try: - return self._memo['rentry'] - except KeyError: - pass - result = self - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: - node = dir.entries[norm_name] - except KeyError: - if dir.entry_exists_on_disk(self.name): - result = dir.Entry(self.name) - break - self._memo['rentry'] = result - return result - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - return [] - -class Entry(Base): - """This is the class for generic Node.FS entries--that is, things - that could be a File or a Dir, but we're just not sure yet. - Consequently, the methods in this class really exist just to - transform their associated object into the right class when the - time comes, and then call the same-named method in the transformed - class.""" - - def diskcheck_match(self): - pass - - def disambiguate(self, must_exist=None): - """ - """ - if self.isdir(): - self.__class__ = Dir - self._morph() - elif self.isfile(): - self.__class__ = File - self._morph() - self.clear() - else: - # There was nothing on-disk at this location, so look in - # the src directory. - # - # We can't just use self.srcnode() straight away because - # that would create an actual Node for this file in the src - # directory, and there might not be one. Instead, use the - # dir_on_disk() method to see if there's something on-disk - # with that name, in which case we can go ahead and call - # self.srcnode() to create the right type of entry. - srcdir = self.dir.srcnode() - if srcdir != self.dir and \ - srcdir.entry_exists_on_disk(self.name) and \ - self.srcnode().isdir(): - self.__class__ = Dir - self._morph() - elif must_exist: - msg = "No such file or directory: '%s'" % self.abspath - raise SCons.Errors.UserError, msg - else: - self.__class__ = File - self._morph() - self.clear() - return self - - def rfile(self): - """We're a generic Entry, but the caller is actually looking for - a File at this point, so morph into one.""" - self.__class__ = File - self._morph() - self.clear() - return File.rfile(self) - - def scanner_key(self): - return self.get_suffix() - - def get_contents(self): - """Fetch the contents of the entry. - - Since this should return the real contents from the file - system, we check to see into what sort of subclass we should - morph this Entry.""" - try: - self = self.disambiguate(must_exist=1) - except SCons.Errors.UserError: - # There was nothing on disk with which to disambiguate - # this entry. Leave it as an Entry, but return a null - # string so calls to get_contents() in emitters and the - # like (e.g. in qt.py) don't have to disambiguate by hand - # or catch the exception. - return '' - else: - return self.get_contents() - - def must_be_same(self, klass): - """Called to make sure a Node is a Dir. Since we're an - Entry, we can morph into one.""" - if self.__class__ is not klass: - self.__class__ = klass - self._morph() - self.clear() - - # The following methods can get called before the Taskmaster has - # had a chance to call disambiguate() directly to see if this Entry - # should really be a Dir or a File. We therefore use these to call - # disambiguate() transparently (from our caller's point of view). - # - # Right now, this minimal set of methods has been derived by just - # looking at some of the methods that will obviously be called early - # in any of the various Taskmasters' calling sequences, and then - # empirically figuring out which additional methods are necessary - # to make various tests pass. - - def exists(self): - """Return if the Entry exists. Check the file system to see - what we should turn into first. Assume a file if there's no - directory.""" - return self.disambiguate().exists() - - def rel_path(self, other): - d = self.disambiguate() - if d.__class__ is Entry: - raise "rel_path() could not disambiguate File/Dir" - return d.rel_path(other) - - def new_ninfo(self): - return self.disambiguate().new_ninfo() - - def changed_since_last_build(self, target, prev_ni): - return self.disambiguate().changed_since_last_build(target, prev_ni) - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - return self.disambiguate()._glob1(pattern, ondisk, source, strings) - -# This is for later so we can differentiate between Entry the class and Entry -# the method of the FS class. -_classEntry = Entry - - -class LocalFS: - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - # This class implements an abstraction layer for operations involving - # a local file system. Essentially, this wraps any function in - # the os, os.path or shutil modules that we use to actually go do - # anything with or to the local file system. - # - # Note that there's a very good chance we'll refactor this part of - # the architecture in some way as we really implement the interface(s) - # for remote file system Nodes. For example, the right architecture - # might be to have this be a subclass instead of a base class. - # Nevertheless, we're using this as a first step in that direction. - # - # We're not using chdir() yet because the calling subclass method - # needs to use os.chdir() directly to avoid recursion. Will we - # really need this one? - #def chdir(self, path): - # return os.chdir(path) - def chmod(self, path, mode): - return os.chmod(path, mode) - def copy(self, src, dst): - return shutil.copy(src, dst) - def copy2(self, src, dst): - return shutil.copy2(src, dst) - def exists(self, path): - return os.path.exists(path) - def getmtime(self, path): - return os.path.getmtime(path) - def getsize(self, path): - return os.path.getsize(path) - def isdir(self, path): - return os.path.isdir(path) - def isfile(self, path): - return os.path.isfile(path) - def link(self, src, dst): - return os.link(src, dst) - def lstat(self, path): - return os.lstat(path) - def listdir(self, path): - return os.listdir(path) - def makedirs(self, path): - return os.makedirs(path) - def mkdir(self, path): - return os.mkdir(path) - def rename(self, old, new): - return os.rename(old, new) - def stat(self, path): - return os.stat(path) - def symlink(self, src, dst): - return os.symlink(src, dst) - def open(self, path): - return open(path) - def unlink(self, path): - return os.unlink(path) - - if hasattr(os, 'symlink'): - def islink(self, path): - return os.path.islink(path) - else: - def islink(self, path): - return 0 # no symlinks - - if hasattr(os, 'readlink'): - def readlink(self, file): - return os.readlink(file) - else: - def readlink(self, file): - return '' - - -#class RemoteFS: -# # Skeleton for the obvious methods we might need from the -# # abstraction layer for a remote filesystem. -# def upload(self, local_src, remote_dst): -# pass -# def download(self, remote_src, local_dst): -# pass - - -class FS(LocalFS): - - memoizer_counters = [] - - def __init__(self, path = None): - """Initialize the Node.FS subsystem. - - The supplied path is the top of the source tree, where we - expect to find the top-level build file. If no path is - supplied, the current directory is the default. - - The path argument must be a valid absolute path. - """ - if __debug__: logInstanceCreation(self, 'Node.FS') - - self._memo = {} - - self.Root = {} - self.SConstruct_dir = None - self.max_drift = default_max_drift - - self.Top = None - if path is None: - self.pathTop = os.getcwd() - else: - self.pathTop = path - self.defaultDrive = _my_normcase(os.path.splitdrive(self.pathTop)[0]) - - self.Top = self.Dir(self.pathTop) - self.Top.path = '.' - self.Top.tpath = '.' - self._cwd = self.Top - - DirNodeInfo.fs = self - FileNodeInfo.fs = self - - def set_SConstruct_dir(self, dir): - self.SConstruct_dir = dir - - def get_max_drift(self): - return self.max_drift - - def set_max_drift(self, max_drift): - self.max_drift = max_drift - - def getcwd(self): - return self._cwd - - def chdir(self, dir, change_os_dir=0): - """Change the current working directory for lookups. - If change_os_dir is true, we will also change the "real" cwd - to match. - """ - curr=self._cwd - try: - if dir is not None: - self._cwd = dir - if change_os_dir: - os.chdir(dir.abspath) - except OSError: - self._cwd = curr - raise - - def get_root(self, drive): - """ - Returns the root directory for the specified drive, creating - it if necessary. - """ - drive = _my_normcase(drive) - try: - return self.Root[drive] - except KeyError: - root = RootDir(drive, self) - self.Root[drive] = root - if not drive: - self.Root[self.defaultDrive] = root - elif drive == self.defaultDrive: - self.Root[''] = root - return root - - def _lookup(self, p, directory, fsclass, create=1): - """ - The generic entry point for Node lookup with user-supplied data. - - This translates arbitrary input into a canonical Node.FS object - of the specified fsclass. The general approach for strings is - to turn it into a fully normalized absolute path and then call - the root directory's lookup_abs() method for the heavy lifting. - - If the path name begins with '#', it is unconditionally - interpreted relative to the top-level directory of this FS. '#' - is treated as a synonym for the top-level SConstruct directory, - much like '~' is treated as a synonym for the user's home - directory in a UNIX shell. So both '#foo' and '#/foo' refer - to the 'foo' subdirectory underneath the top-level SConstruct - directory. - - If the path name is relative, then the path is looked up relative - to the specified directory, or the current directory (self._cwd, - typically the SConscript directory) if the specified directory - is None. - """ - if isinstance(p, Base): - # It's already a Node.FS object. Make sure it's the right - # class and return. - p.must_be_same(fsclass) - return p - # str(p) in case it's something like a proxy object - p = str(p) - - initial_hash = (p[0:1] == '#') - if initial_hash: - # There was an initial '#', so we strip it and override - # whatever directory they may have specified with the - # top-level SConstruct directory. - p = p[1:] - directory = self.Top - - if directory and not isinstance(directory, Dir): - directory = self.Dir(directory) - - if do_splitdrive: - drive, p = os.path.splitdrive(p) - else: - drive = '' - if drive and not p: - # This causes a naked drive letter to be treated as a synonym - # for the root directory on that drive. - p = os.sep - absolute = os.path.isabs(p) - - needs_normpath = needs_normpath_check.match(p) - - if initial_hash or not absolute: - # This is a relative lookup, either to the top-level - # SConstruct directory (because of the initial '#') or to - # the current directory (the path name is not absolute). - # Add the string to the appropriate directory lookup path, - # after which the whole thing gets normalized. - if not directory: - directory = self._cwd - if p: - p = directory.labspath + '/' + p - else: - p = directory.labspath - - if needs_normpath: - p = os.path.normpath(p) - - if drive or absolute: - root = self.get_root(drive) - else: - if not directory: - directory = self._cwd - root = directory.root - - if os.sep != '/': - p = string.replace(p, os.sep, '/') - return root._lookup_abs(p, fsclass, create) - - def Entry(self, name, directory = None, create = 1): - """Look up or create a generic Entry node with the specified name. - If the name is a relative path (begins with ./, ../, or a file - name), then it is looked up relative to the supplied directory - node, or to the top level directory of the FS (supplied at - construction time) if no directory is supplied. - """ - return self._lookup(name, directory, Entry, create) - - def File(self, name, directory = None, create = 1): - """Look up or create a File node with the specified name. If - the name is a relative path (begins with ./, ../, or a file name), - then it is looked up relative to the supplied directory node, - or to the top level directory of the FS (supplied at construction - time) if no directory is supplied. - - This method will raise TypeError if a directory is found at the - specified path. - """ - return self._lookup(name, directory, File, create) - - def Dir(self, name, directory = None, create = True): - """Look up or create a Dir node with the specified name. If - the name is a relative path (begins with ./, ../, or a file name), - then it is looked up relative to the supplied directory node, - or to the top level directory of the FS (supplied at construction - time) if no directory is supplied. - - This method will raise TypeError if a normal file is found at the - specified path. - """ - return self._lookup(name, directory, Dir, create) - - def VariantDir(self, variant_dir, src_dir, duplicate=1): - """Link the supplied variant directory to the source directory - for purposes of building files.""" - - if not isinstance(src_dir, SCons.Node.Node): - src_dir = self.Dir(src_dir) - if not isinstance(variant_dir, SCons.Node.Node): - variant_dir = self.Dir(variant_dir) - if src_dir.is_under(variant_dir): - raise SCons.Errors.UserError, "Source directory cannot be under variant directory." - if variant_dir.srcdir: - if variant_dir.srcdir == src_dir: - return # We already did this. - raise SCons.Errors.UserError, "'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir) - variant_dir.link(src_dir, duplicate) - - def Repository(self, *dirs): - """Specify Repository directories to search.""" - for d in dirs: - if not isinstance(d, SCons.Node.Node): - d = self.Dir(d) - self.Top.addRepository(d) - - def variant_dir_target_climb(self, orig, dir, tail): - """Create targets in corresponding variant directories - - Climb the directory tree, and look up path names - relative to any linked variant directories we find. - - Even though this loops and walks up the tree, we don't memoize - the return value because this is really only used to process - the command-line targets. - """ - targets = [] - message = None - fmt = "building associated VariantDir targets: %s" - start_dir = dir - while dir: - for bd in dir.variant_dirs: - if start_dir.is_under(bd): - # If already in the build-dir location, don't reflect - return [orig], fmt % str(orig) - p = apply(os.path.join, [bd.path] + tail) - targets.append(self.Entry(p)) - tail = [dir.name] + tail - dir = dir.up() - if targets: - message = fmt % string.join(map(str, targets)) - return targets, message - - def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): - """ - Globs - - This is mainly a shim layer - """ - if cwd is None: - cwd = self.getcwd() - return cwd.glob(pathname, ondisk, source, strings) - -class DirNodeInfo(SCons.Node.NodeInfoBase): - # This should get reset by the FS initialization. - current_version_id = 1 - - fs = None - - def str_to_node(self, s): - top = self.fs.Top - root = top.root - if do_splitdrive: - drive, s = os.path.splitdrive(s) - if drive: - root = self.fs.get_root(drive) - if not os.path.isabs(s): - s = top.labspath + '/' + s - return root._lookup_abs(s, Entry) - -class DirBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -glob_magic_check = re.compile('[*?[]') - -def has_glob_magic(s): - return glob_magic_check.search(s) is not None - -class Dir(Base): - """A class for directories in a file system. - """ - - memoizer_counters = [] - - NodeInfo = DirNodeInfo - BuildInfo = DirBuildInfo - - def __init__(self, name, directory, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.Dir') - Base.__init__(self, name, directory, fs) - self._morph() - - def _morph(self): - """Turn a file system Node (either a freshly initialized directory - object or a separate Entry object) into a proper directory object. - - Set up this directory's entries and hook it into the file - system tree. Specify that directories (this Node) don't use - signatures for calculating whether they're current. - """ - - self.repositories = [] - self.srcdir = None - - self.entries = {} - self.entries['.'] = self - self.entries['..'] = self.dir - self.cwd = self - self.searched = 0 - self._sconsign = None - self.variant_dirs = [] - self.root = self.dir.root - - # Don't just reset the executor, replace its action list, - # because it might have some pre-or post-actions that need to - # be preserved. - self.builder = get_MkdirBuilder() - self.get_executor().set_action_list(self.builder.action) - - def diskcheck_match(self): - diskcheck_match(self, self.isfile, - "File %s found where directory expected.") - - def __clearRepositoryCache(self, duplicate=None): - """Called when we change the repository(ies) for a directory. - This clears any cached information that is invalidated by changing - the repository.""" - - for node in self.entries.values(): - if node != self.dir: - if node != self and isinstance(node, Dir): - node.__clearRepositoryCache(duplicate) - else: - node.clear() - try: - del node._srcreps - except AttributeError: - pass - if duplicate is not None: - node.duplicate=duplicate - - def __resetDuplicate(self, node): - if node != self: - node.duplicate = node.get_dir().duplicate - - def Entry(self, name): - """ - Looks up or creates an entry node named 'name' relative to - this directory. - """ - return self.fs.Entry(name, self) - - def Dir(self, name, create=True): - """ - Looks up or creates a directory node named 'name' relative to - this directory. - """ - return self.fs.Dir(name, self, create) - - def File(self, name): - """ - Looks up or creates a file node named 'name' relative to - this directory. - """ - return self.fs.File(name, self) - - def _lookup_rel(self, name, klass, create=1): - """ - Looks up a *normalized* relative path name, relative to this - directory. - - This method is intended for use by internal lookups with - already-normalized path data. For general-purpose lookups, - use the Entry(), Dir() and File() methods above. - - This method does *no* input checking and will die or give - incorrect results if it's passed a non-normalized path name (e.g., - a path containing '..'), an absolute path name, a top-relative - ('#foo') path name, or any kind of object. - """ - name = self.entry_labspath(name) - return self.root._lookup_abs(name, klass, create) - - def link(self, srcdir, duplicate): - """Set this directory as the variant directory for the - supplied source directory.""" - self.srcdir = srcdir - self.duplicate = duplicate - self.__clearRepositoryCache(duplicate) - srcdir.variant_dirs.append(self) - - def getRepositories(self): - """Returns a list of repositories for this directory. - """ - if self.srcdir and not self.duplicate: - return self.srcdir.get_all_rdirs() + self.repositories - return self.repositories - - memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs')) - - def get_all_rdirs(self): - try: - return list(self._memo['get_all_rdirs']) - except KeyError: - pass - - result = [self] - fname = '.' - dir = self - while dir: - for rep in dir.getRepositories(): - result.append(rep.Dir(fname)) - if fname == '.': - fname = dir.name - else: - fname = dir.name + os.sep + fname - dir = dir.up() - - self._memo['get_all_rdirs'] = list(result) - - return result - - def addRepository(self, dir): - if dir != self and not dir in self.repositories: - self.repositories.append(dir) - dir.tpath = '.' - self.__clearRepositoryCache() - - def up(self): - return self.entries['..'] - - def _rel_path_key(self, other): - return str(other) - - memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key)) - - def rel_path(self, other): - """Return a path to "other" relative to this directory. - """ - - # This complicated and expensive method, which constructs relative - # paths between arbitrary Node.FS objects, is no longer used - # by SCons itself. It was introduced to store dependency paths - # in .sconsign files relative to the target, but that ended up - # being significantly inefficient. - # - # We're continuing to support the method because some SConstruct - # files out there started using it when it was available, and - # we're all about backwards compatibility.. - - try: - memo_dict = self._memo['rel_path'] - except KeyError: - memo_dict = {} - self._memo['rel_path'] = memo_dict - else: - try: - return memo_dict[other] - except KeyError: - pass - - if self is other: - result = '.' - - elif not other in self.path_elements: - try: - other_dir = other.get_dir() - except AttributeError: - result = str(other) - else: - if other_dir is None: - result = other.name - else: - dir_rel_path = self.rel_path(other_dir) - if dir_rel_path == '.': - result = other.name - else: - result = dir_rel_path + os.sep + other.name - else: - i = self.path_elements.index(other) + 1 - - path_elems = ['..'] * (len(self.path_elements) - i) \ - + map(lambda n: n.name, other.path_elements[i:]) - - result = string.join(path_elems, os.sep) - - memo_dict[other] = result - - return result - - def get_env_scanner(self, env, kw={}): - import SCons.Defaults - return SCons.Defaults.DirEntryScanner - - def get_target_scanner(self): - import SCons.Defaults - return SCons.Defaults.DirEntryScanner - - def get_found_includes(self, env, scanner, path): - """Return this directory's implicit dependencies. - - We don't bother caching the results because the scan typically - shouldn't be requested more than once (as opposed to scanning - .h file contents, which can be requested as many times as the - files is #included by other files). - """ - if not scanner: - return [] - # Clear cached info for this Dir. If we already visited this - # directory on our walk down the tree (because we didn't know at - # that point it was being used as the source for another Node) - # then we may have calculated build signature before realizing - # we had to scan the disk. Now that we have to, though, we need - # to invalidate the old calculated signature so that any node - # dependent on our directory structure gets one that includes - # info about everything on disk. - self.clear() - return scanner(self, env, path) - - # - # Taskmaster interface subsystem - # - - def prepare(self): - pass - - def build(self, **kw): - """A null "builder" for directories.""" - global MkdirBuilder - if self.builder is not MkdirBuilder: - apply(SCons.Node.Node.build, [self,], kw) - - # - # - # - - def _create(self): - """Create this directory, silently and without worrying about - whether the builder is the default or not.""" - listDirs = [] - parent = self - while parent: - if parent.exists(): - break - listDirs.append(parent) - parent = parent.up() - else: - raise SCons.Errors.StopError, parent.path - listDirs.reverse() - for dirnode in listDirs: - try: - # Don't call dirnode.build(), call the base Node method - # directly because we definitely *must* create this - # directory. The dirnode.build() method will suppress - # the build if it's the default builder. - SCons.Node.Node.build(dirnode) - dirnode.get_executor().nullify() - # The build() action may or may not have actually - # created the directory, depending on whether the -n - # option was used or not. Delete the _exists and - # _rexists attributes so they can be reevaluated. - dirnode.clear() - except OSError: - pass - - def multiple_side_effect_has_builder(self): - global MkdirBuilder - return self.builder is not MkdirBuilder and self.has_builder() - - def alter_targets(self): - """Return any corresponding targets in a variant directory. - """ - return self.fs.variant_dir_target_climb(self, self, []) - - def scanner_key(self): - """A directory does not get scanned.""" - return None - - def get_contents(self): - """Return content signatures and names of all our children - separated by new-lines. Ensure that the nodes are sorted.""" - contents = [] - name_cmp = lambda a, b: cmp(a.name, b.name) - sorted_children = self.children()[:] - sorted_children.sort(name_cmp) - for node in sorted_children: - contents.append('%s %s\n' % (node.get_csig(), node.name)) - return string.join(contents, '') - - def get_csig(self): - """Compute the content signature for Directory nodes. In - general, this is not needed and the content signature is not - stored in the DirNodeInfo. However, if get_contents on a Dir - node is called which has a child directory, the child - directory should return the hash of its contents.""" - contents = self.get_contents() - return SCons.Util.MD5signature(contents) - - def do_duplicate(self, src): - pass - - changed_since_last_build = SCons.Node.Node.state_has_changed - - def is_up_to_date(self): - """If any child is not up-to-date, then this directory isn't, - either.""" - if self.builder is not MkdirBuilder and not self.exists(): - return 0 - up_to_date = SCons.Node.up_to_date - for kid in self.children(): - if kid.get_state() > up_to_date: - return 0 - return 1 - - def rdir(self): - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: node = dir.entries[norm_name] - except KeyError: node = dir.dir_on_disk(self.name) - if node and node.exists() and \ - (isinstance(dir, Dir) or isinstance(dir, Entry)): - return node - return self - - def sconsign(self): - """Return the .sconsign file info for this directory, - creating it first if necessary.""" - if not self._sconsign: - import SCons.SConsign - self._sconsign = SCons.SConsign.ForDirectory(self) - return self._sconsign - - def srcnode(self): - """Dir has a special need for srcnode()...if we - have a srcdir attribute set, then that *is* our srcnode.""" - if self.srcdir: - return self.srcdir - return Base.srcnode(self) - - def get_timestamp(self): - """Return the latest timestamp from among our children""" - stamp = 0 - for kid in self.children(): - if kid.get_timestamp() > stamp: - stamp = kid.get_timestamp() - return stamp - - def entry_abspath(self, name): - return self.abspath + os.sep + name - - def entry_labspath(self, name): - return self.labspath + '/' + name - - def entry_path(self, name): - return self.path + os.sep + name - - def entry_tpath(self, name): - return self.tpath + os.sep + name - - def entry_exists_on_disk(self, name): - try: - d = self.on_disk_entries - except AttributeError: - d = {} - try: - entries = os.listdir(self.abspath) - except OSError: - pass - else: - for entry in map(_my_normcase, entries): - d[entry] = 1 - self.on_disk_entries = d - return d.has_key(_my_normcase(name)) - - memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) - - def srcdir_list(self): - try: - return self._memo['srcdir_list'] - except KeyError: - pass - - result = [] - - dirname = '.' - dir = self - while dir: - if dir.srcdir: - result.append(dir.srcdir.Dir(dirname)) - dirname = dir.name + os.sep + dirname - dir = dir.up() - - self._memo['srcdir_list'] = result - - return result - - def srcdir_duplicate(self, name): - for dir in self.srcdir_list(): - if self.is_under(dir): - # We shouldn't source from something in the build path; - # variant_dir is probably under src_dir, in which case - # we are reflecting. - break - if dir.entry_exists_on_disk(name): - srcnode = dir.Entry(name).disambiguate() - if self.duplicate: - node = self.Entry(name).disambiguate() - node.do_duplicate(srcnode) - return node - else: - return srcnode - return None - - def _srcdir_find_file_key(self, filename): - return filename - - memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key)) - - def srcdir_find_file(self, filename): - try: - memo_dict = self._memo['srcdir_find_file'] - except KeyError: - memo_dict = {} - self._memo['srcdir_find_file'] = memo_dict - else: - try: - return memo_dict[filename] - except KeyError: - pass - - def func(node): - if (isinstance(node, File) or isinstance(node, Entry)) and \ - (node.is_derived() or node.exists()): - return node - return None - - norm_name = _my_normcase(filename) - - for rdir in self.get_all_rdirs(): - try: node = rdir.entries[norm_name] - except KeyError: node = rdir.file_on_disk(filename) - else: node = func(node) - if node: - result = (node, self) - memo_dict[filename] = result - return result - - for srcdir in self.srcdir_list(): - for rdir in srcdir.get_all_rdirs(): - try: node = rdir.entries[norm_name] - except KeyError: node = rdir.file_on_disk(filename) - else: node = func(node) - if node: - result = (File(filename, self, self.fs), srcdir) - memo_dict[filename] = result - return result - - result = (None, None) - memo_dict[filename] = result - return result - - def dir_on_disk(self, name): - if self.entry_exists_on_disk(name): - try: return self.Dir(name) - except TypeError: pass - node = self.srcdir_duplicate(name) - if isinstance(node, File): - return None - return node - - def file_on_disk(self, name): - if self.entry_exists_on_disk(name) or \ - diskcheck_rcs(self, name) or \ - diskcheck_sccs(self, name): - try: return self.File(name) - except TypeError: pass - node = self.srcdir_duplicate(name) - if isinstance(node, Dir): - return None - return node - - def walk(self, func, arg): - """ - Walk this directory tree by calling the specified function - for each directory in the tree. - - This behaves like the os.path.walk() function, but for in-memory - Node.FS.Dir objects. The function takes the same arguments as - the functions passed to os.path.walk(): - - func(arg, dirname, fnames) - - Except that "dirname" will actually be the directory *Node*, - not the string. The '.' and '..' entries are excluded from - fnames. The fnames list may be modified in-place to filter the - subdirectories visited or otherwise impose a specific order. - The "arg" argument is always passed to func() and may be used - in any way (or ignored, passing None is common). - """ - entries = self.entries - names = entries.keys() - names.remove('.') - names.remove('..') - func(arg, self, names) - select_dirs = lambda n, e=entries: isinstance(e[n], Dir) - for dirname in filter(select_dirs, names): - entries[dirname].walk(func, arg) - - def glob(self, pathname, ondisk=True, source=False, strings=False): - """ - Returns a list of Nodes (or strings) matching a specified - pathname pattern. - - Pathname patterns follow UNIX shell semantics: * matches - any-length strings of any characters, ? matches any character, - and [] can enclose lists or ranges of characters. Matches do - not span directory separators. - - The matches take into account Repositories, returning local - Nodes if a corresponding entry exists in a Repository (either - an in-memory Node or something on disk). - - By defafult, the glob() function matches entries that exist - on-disk, in addition to in-memory Nodes. Setting the "ondisk" - argument to False (or some other non-true value) causes the glob() - function to only match in-memory Nodes. The default behavior is - to return both the on-disk and in-memory Nodes. - - The "source" argument, when true, specifies that corresponding - source Nodes must be returned if you're globbing in a build - directory (initialized with VariantDir()). The default behavior - is to return Nodes local to the VariantDir(). - - The "strings" argument, when true, returns the matches as strings, - not Nodes. The strings are path names relative to this directory. - - The underlying algorithm is adapted from the glob.glob() function - in the Python library (but heavily modified), and uses fnmatch() - under the covers. - """ - dirname, basename = os.path.split(pathname) - if not dirname: - return self._glob1(basename, ondisk, source, strings) - if has_glob_magic(dirname): - list = self.glob(dirname, ondisk, source, strings=False) - else: - list = [self.Dir(dirname, create=True)] - result = [] - for dir in list: - r = dir._glob1(basename, ondisk, source, strings) - if strings: - r = map(lambda x, d=str(dir): os.path.join(d, x), r) - result.extend(r) - result.sort(lambda a, b: cmp(str(a), str(b))) - return result - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - """ - Globs for and returns a list of entry names matching a single - pattern in this directory. - - This searches any repositories and source directories for - corresponding entries and returns a Node (or string) relative - to the current directory if an entry is found anywhere. - - TODO: handle pattern with no wildcard - """ - search_dir_list = self.get_all_rdirs() - for srcdir in self.srcdir_list(): - search_dir_list.extend(srcdir.get_all_rdirs()) - - selfEntry = self.Entry - names = [] - for dir in search_dir_list: - # We use the .name attribute from the Node because the keys of - # the dir.entries dictionary are normalized (that is, all upper - # case) on case-insensitive systems like Windows. - #node_names = [ v.name for k, v in dir.entries.items() if k not in ('.', '..') ] - entry_names = filter(lambda n: n not in ('.', '..'), dir.entries.keys()) - node_names = map(lambda n, e=dir.entries: e[n].name, entry_names) - names.extend(node_names) - if not strings: - # Make sure the working directory (self) actually has - # entries for all Nodes in repositories or variant dirs. - map(selfEntry, node_names) - if ondisk: - try: - disk_names = os.listdir(dir.abspath) - except os.error: - continue - names.extend(disk_names) - if not strings: - # We're going to return corresponding Nodes in - # the local directory, so we need to make sure - # those Nodes exist. We only want to create - # Nodes for the entries that will match the - # specified pattern, though, which means we - # need to filter the list here, even though - # the overall list will also be filtered later, - # after we exit this loop. - if pattern[0] != '.': - #disk_names = [ d for d in disk_names if d[0] != '.' ] - disk_names = filter(lambda x: x[0] != '.', disk_names) - disk_names = fnmatch.filter(disk_names, pattern) - dirEntry = dir.Entry - for name in disk_names: - # Add './' before disk filename so that '#' at - # beginning of filename isn't interpreted. - name = './' + name - node = dirEntry(name).disambiguate() - n = selfEntry(name) - if n.__class__ != node.__class__: - n.__class__ = node.__class__ - n._morph() - - names = set(names) - if pattern[0] != '.': - #names = [ n for n in names if n[0] != '.' ] - names = filter(lambda x: x[0] != '.', names) - names = fnmatch.filter(names, pattern) - - if strings: - return names - - #return [ self.entries[_my_normcase(n)] for n in names ] - return map(lambda n, e=self.entries: e[_my_normcase(n)], names) - -class RootDir(Dir): - """A class for the root directory of a file system. - - This is the same as a Dir class, except that the path separator - ('/' or '\\') is actually part of the name, so we don't need to - add a separator when creating the path names of entries within - this directory. - """ - def __init__(self, name, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') - # We're going to be our own parent directory (".." entry and .dir - # attribute) so we have to set up some values so Base.__init__() - # won't gag won't it calls some of our methods. - self.abspath = '' - self.labspath = '' - self.path = '' - self.tpath = '' - self.path_elements = [] - self.duplicate = 0 - self.root = self - Base.__init__(self, name, self, fs) - - # Now set our paths to what we really want them to be: the - # initial drive letter (the name) plus the directory separator, - # except for the "lookup abspath," which does not have the - # drive letter. - self.abspath = name + os.sep - self.labspath = '' - self.path = name + os.sep - self.tpath = name + os.sep - self._morph() - - self._lookupDict = {} - - # The // and os.sep + os.sep entries are necessary because - # os.path.normpath() seems to preserve double slashes at the - # beginning of a path (presumably for UNC path names), but - # collapses triple slashes to a single slash. - self._lookupDict[''] = self - self._lookupDict['/'] = self - self._lookupDict['//'] = self - self._lookupDict[os.sep] = self - self._lookupDict[os.sep + os.sep] = self - - def must_be_same(self, klass): - if klass is Dir: - return - Base.must_be_same(self, klass) - - def _lookup_abs(self, p, klass, create=1): - """ - Fast (?) lookup of a *normalized* absolute path. - - This method is intended for use by internal lookups with - already-normalized path data. For general-purpose lookups, - use the FS.Entry(), FS.Dir() or FS.File() methods. - - The caller is responsible for making sure we're passed a - normalized absolute path; we merely let Python's dictionary look - up and return the One True Node.FS object for the path. - - If no Node for the specified "p" doesn't already exist, and - "create" is specified, the Node may be created after recursive - invocation to find or create the parent directory or directories. - """ - k = _my_normcase(p) - try: - result = self._lookupDict[k] - except KeyError: - if not create: - raise SCons.Errors.UserError - # There is no Node for this path name, and we're allowed - # to create it. - dir_name, file_name = os.path.split(p) - dir_node = self._lookup_abs(dir_name, Dir) - result = klass(file_name, dir_node, self.fs) - - # Double-check on disk (as configured) that the Node we - # created matches whatever is out there in the real world. - result.diskcheck_match() - - self._lookupDict[k] = result - dir_node.entries[_my_normcase(file_name)] = result - dir_node.implicit = None - else: - # There is already a Node for this path name. Allow it to - # complain if we were looking for an inappropriate type. - result.must_be_same(klass) - return result - - def __str__(self): - return self.abspath - - def entry_abspath(self, name): - return self.abspath + name - - def entry_labspath(self, name): - return '/' + name - - def entry_path(self, name): - return self.path + name - - def entry_tpath(self, name): - return self.tpath + name - - def is_under(self, dir): - if self is dir: - return 1 - else: - return 0 - - def up(self): - return None - - def get_dir(self): - return None - - def src_builder(self): - return _null - -class FileNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - - field_list = ['csig', 'timestamp', 'size'] - - # This should get reset by the FS initialization. - fs = None - - def str_to_node(self, s): - top = self.fs.Top - root = top.root - if do_splitdrive: - drive, s = os.path.splitdrive(s) - if drive: - root = self.fs.get_root(drive) - if not os.path.isabs(s): - s = top.labspath + '/' + s - return root._lookup_abs(s, Entry) - -class FileBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - - def convert_to_sconsign(self): - """ - Converts this FileBuildInfo object for writing to a .sconsign file - - This replaces each Node in our various dependency lists with its - usual string representation: relative to the top-level SConstruct - directory, or an absolute path if it's outside. - """ - if os.sep == '/': - node_to_str = str - else: - def node_to_str(n): - try: - s = n.path - except AttributeError: - s = str(n) - else: - s = string.replace(s, os.sep, '/') - return s - for attr in ['bsources', 'bdepends', 'bimplicit']: - try: - val = getattr(self, attr) - except AttributeError: - pass - else: - setattr(self, attr, map(node_to_str, val)) - def convert_from_sconsign(self, dir, name): - """ - Converts a newly-read FileBuildInfo object for in-SCons use - - For normal up-to-date checking, we don't have any conversion to - perform--but we're leaving this method here to make that clear. - """ - pass - def prepare_dependencies(self): - """ - Prepares a FileBuildInfo object for explaining what changed - - The bsources, bdepends and bimplicit lists have all been - stored on disk as paths relative to the top-level SConstruct - directory. Convert the strings to actual Nodes (for use by the - --debug=explain code and --implicit-cache). - """ - attrs = [ - ('bsources', 'bsourcesigs'), - ('bdepends', 'bdependsigs'), - ('bimplicit', 'bimplicitsigs'), - ] - for (nattr, sattr) in attrs: - try: - strings = getattr(self, nattr) - nodeinfos = getattr(self, sattr) - except AttributeError: - continue - nodes = [] - for s, ni in izip(strings, nodeinfos): - if not isinstance(s, SCons.Node.Node): - s = ni.str_to_node(s) - nodes.append(s) - setattr(self, nattr, nodes) - def format(self, names=0): - result = [] - bkids = self.bsources + self.bdepends + self.bimplicit - bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs - for bkid, bkidsig in izip(bkids, bkidsigs): - result.append(str(bkid) + ': ' + - string.join(bkidsig.format(names=names), ' ')) - result.append('%s [%s]' % (self.bactsig, self.bact)) - return string.join(result, '\n') - -class File(Base): - """A class for files in a file system. - """ - - memoizer_counters = [] - - NodeInfo = FileNodeInfo - BuildInfo = FileBuildInfo - - md5_chunksize = 64 - - def diskcheck_match(self): - diskcheck_match(self, self.isdir, - "Directory %s found where file expected.") - - def __init__(self, name, directory, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.File') - Base.__init__(self, name, directory, fs) - self._morph() - - def Entry(self, name): - """Create an entry node named 'name' relative to - the directory of this file.""" - return self.dir.Entry(name) - - def Dir(self, name, create=True): - """Create a directory node named 'name' relative to - the directory of this file.""" - return self.dir.Dir(name, create=create) - - def Dirs(self, pathlist): - """Create a list of directories relative to the SConscript - directory of this file.""" - # TODO(1.5) - # return [self.Dir(p) for p in pathlist] - return map(lambda p, s=self: s.Dir(p), pathlist) - - def File(self, name): - """Create a file node named 'name' relative to - the directory of this file.""" - return self.dir.File(name) - - #def generate_build_dict(self): - # """Return an appropriate dictionary of values for building - # this File.""" - # return {'Dir' : self.Dir, - # 'File' : self.File, - # 'RDirs' : self.RDirs} - - def _morph(self): - """Turn a file system node into a File object.""" - self.scanner_paths = {} - if not hasattr(self, '_local'): - self._local = 0 - - # If there was already a Builder set on this entry, then - # we need to make sure we call the target-decider function, - # not the source-decider. Reaching in and doing this by hand - # is a little bogus. We'd prefer to handle this by adding - # an Entry.builder_set() method that disambiguates like the - # other methods, but that starts running into problems with the - # fragile way we initialize Dir Nodes with their Mkdir builders, - # yet still allow them to be overridden by the user. Since it's - # not clear right now how to fix that, stick with what works - # until it becomes clear... - if self.has_builder(): - self.changed_since_last_build = self.decide_target - - def scanner_key(self): - return self.get_suffix() - - def get_contents(self): - if not self.rexists(): - return '' - fname = self.rfile().abspath - try: - r = open(fname, "rb").read() - except EnvironmentError, e: - if not e.filename: - e.filename = fname - raise - return r - - def get_content_hash(self): - """ - Compute and return the MD5 hash for this file. - """ - if not self.rexists(): - return SCons.Util.MD5signature('') - fname = self.rfile().abspath - try: - cs = SCons.Util.MD5filesignature(fname, - chunksize=SCons.Node.FS.File.md5_chunksize*1024) - except EnvironmentError, e: - if not e.filename: - e.filename = fname - raise - return cs - - - memoizer_counters.append(SCons.Memoize.CountValue('get_size')) - - def get_size(self): - try: - return self._memo['get_size'] - except KeyError: - pass - - if self.rexists(): - size = self.rfile().getsize() - else: - size = 0 - - self._memo['get_size'] = size - - return size - - memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp')) - - def get_timestamp(self): - try: - return self._memo['get_timestamp'] - except KeyError: - pass - - if self.rexists(): - timestamp = self.rfile().getmtime() - else: - timestamp = 0 - - self._memo['get_timestamp'] = timestamp - - return timestamp - - def store_info(self): - # Merge our build information into the already-stored entry. - # This accomodates "chained builds" where a file that's a target - # in one build (SConstruct file) is a source in a different build. - # See test/chained-build.py for the use case. - if do_store_info: - self.dir.sconsign().store_info(self.name, self) - - convert_copy_attrs = [ - 'bsources', - 'bimplicit', - 'bdepends', - 'bact', - 'bactsig', - 'ninfo', - ] - - - convert_sig_attrs = [ - 'bsourcesigs', - 'bimplicitsigs', - 'bdependsigs', - ] - - def convert_old_entry(self, old_entry): - # Convert a .sconsign entry from before the Big Signature - # Refactoring, doing what we can to convert its information - # to the new .sconsign entry format. - # - # The old format looked essentially like this: - # - # BuildInfo - # .ninfo (NodeInfo) - # .bsig - # .csig - # .timestamp - # .size - # .bsources - # .bsourcesigs ("signature" list) - # .bdepends - # .bdependsigs ("signature" list) - # .bimplicit - # .bimplicitsigs ("signature" list) - # .bact - # .bactsig - # - # The new format looks like this: - # - # .ninfo (NodeInfo) - # .bsig - # .csig - # .timestamp - # .size - # .binfo (BuildInfo) - # .bsources - # .bsourcesigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bdepends - # .bdependsigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bimplicit - # .bimplicitsigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bact - # .bactsig - # - # The basic idea of the new structure is that a NodeInfo always - # holds all available information about the state of a given Node - # at a certain point in time. The various .b*sigs lists can just - # be a list of pointers to the .ninfo attributes of the different - # dependent nodes, without any copying of information until it's - # time to pickle it for writing out to a .sconsign file. - # - # The complicating issue is that the *old* format only stored one - # "signature" per dependency, based on however the *last* build - # was configured. We don't know from just looking at it whether - # it was a build signature, a content signature, or a timestamp - # "signature". Since we no longer use build signatures, the - # best we can do is look at the length and if it's thirty two, - # assume that it was (or might have been) a content signature. - # If it was actually a build signature, then it will cause a - # rebuild anyway when it doesn't match the new content signature, - # but that's probably the best we can do. - import SCons.SConsign - new_entry = SCons.SConsign.SConsignEntry() - new_entry.binfo = self.new_binfo() - binfo = new_entry.binfo - for attr in self.convert_copy_attrs: - try: - value = getattr(old_entry, attr) - except AttributeError: - continue - setattr(binfo, attr, value) - delattr(old_entry, attr) - for attr in self.convert_sig_attrs: - try: - sig_list = getattr(old_entry, attr) - except AttributeError: - continue - value = [] - for sig in sig_list: - ninfo = self.new_ninfo() - if len(sig) == 32: - ninfo.csig = sig - else: - ninfo.timestamp = sig - value.append(ninfo) - setattr(binfo, attr, value) - delattr(old_entry, attr) - return new_entry - - memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info')) - - def get_stored_info(self): - try: - return self._memo['get_stored_info'] - except KeyError: - pass - - try: - sconsign_entry = self.dir.sconsign().get_entry(self.name) - except (KeyError, EnvironmentError): - import SCons.SConsign - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = self.new_binfo() - sconsign_entry.ninfo = self.new_ninfo() - else: - if isinstance(sconsign_entry, FileBuildInfo): - # This is a .sconsign file from before the Big Signature - # Refactoring; convert it as best we can. - sconsign_entry = self.convert_old_entry(sconsign_entry) - try: - delattr(sconsign_entry.ninfo, 'bsig') - except AttributeError: - pass - - self._memo['get_stored_info'] = sconsign_entry - - return sconsign_entry - - def get_stored_implicit(self): - binfo = self.get_stored_info().binfo - binfo.prepare_dependencies() - try: return binfo.bimplicit - except AttributeError: return None - - def rel_path(self, other): - return self.dir.rel_path(other) - - def _get_found_includes_key(self, env, scanner, path): - return (id(env), id(scanner), path) - - memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key)) - - def get_found_includes(self, env, scanner, path): - """Return the included implicit dependencies in this file. - Cache results so we only scan the file once per path - regardless of how many times this information is requested. - """ - memo_key = (id(env), id(scanner), path) - try: - memo_dict = self._memo['get_found_includes'] - except KeyError: - memo_dict = {} - self._memo['get_found_includes'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - if scanner: - # result = [n.disambiguate() for n in scanner(self, env, path)] - result = scanner(self, env, path) - result = map(lambda N: N.disambiguate(), result) - else: - result = [] - - memo_dict[memo_key] = result - - return result - - def _createDir(self): - # ensure that the directories for this node are - # created. - self.dir._create() - - def retrieve_from_cache(self): - """Try to retrieve the node's content from a cache - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Returns true iff the node was successfully retrieved. - """ - if self.nocache: - return None - if not self.is_derived(): - return None - return self.get_build_env().get_CacheDir().retrieve(self) - - def built(self): - """ - Called just after this node is successfully built. - """ - # Push this file out to cache before the superclass Node.built() - # method has a chance to clear the build signature, which it - # will do if this file has a source scanner. - # - # We have to clear the memoized values *before* we push it to - # cache so that the memoization of the self.exists() return - # value doesn't interfere. - self.clear_memoized_values() - if self.exists(): - self.get_build_env().get_CacheDir().push(self) - SCons.Node.Node.built(self) - - def visited(self): - if self.exists(): - self.get_build_env().get_CacheDir().push_if_forced(self) - - ninfo = self.get_ninfo() - - csig = self.get_max_drift_csig() - if csig: - ninfo.csig = csig - - ninfo.timestamp = self.get_timestamp() - ninfo.size = self.get_size() - - if not self.has_builder(): - # This is a source file, but it might have been a target file - # in another build that included more of the DAG. Copy - # any build information that's stored in the .sconsign file - # into our binfo object so it doesn't get lost. - old = self.get_stored_info() - self.get_binfo().__dict__.update(old.binfo.__dict__) - - self.store_info() - - def find_src_builder(self): - if self.rexists(): - return None - scb = self.dir.src_builder() - if scb is _null: - if diskcheck_sccs(self.dir, self.name): - scb = get_DefaultSCCSBuilder() - elif diskcheck_rcs(self.dir, self.name): - scb = get_DefaultRCSBuilder() - else: - scb = None - if scb is not None: - try: - b = self.builder - except AttributeError: - b = None - if b is None: - self.builder_set(scb) - return scb - - def has_src_builder(self): - """Return whether this Node has a source builder or not. - - If this Node doesn't have an explicit source code builder, this - is where we figure out, on the fly, if there's a transparent - source code builder for it. - - Note that if we found a source builder, we also set the - self.builder attribute, so that all of the methods that actually - *build* this file don't have to do anything different. - """ - try: - scb = self.sbuilder - except AttributeError: - scb = self.sbuilder = self.find_src_builder() - return scb is not None - - def alter_targets(self): - """Return any corresponding targets in a variant directory. - """ - if self.is_derived(): - return [], None - return self.fs.variant_dir_target_climb(self, self.dir, [self.name]) - - def _rmv_existing(self): - self.clear_memoized_values() - e = Unlink(self, [], None) - if isinstance(e, SCons.Errors.BuildError): - raise e - - # - # Taskmaster interface subsystem - # - - def make_ready(self): - self.has_src_builder() - self.get_binfo() - - def prepare(self): - """Prepare for this file to be created.""" - SCons.Node.Node.prepare(self) - - if self.get_state() != SCons.Node.up_to_date: - if self.exists(): - if self.is_derived() and not self.precious: - self._rmv_existing() - else: - try: - self._createDir() - except SCons.Errors.StopError, drive: - desc = "No drive `%s' for target `%s'." % (drive, self) - raise SCons.Errors.StopError, desc - - # - # - # - - def remove(self): - """Remove this file.""" - if self.exists() or self.islink(): - self.fs.unlink(self.path) - return 1 - return None - - def do_duplicate(self, src): - self._createDir() - Unlink(self, None, None) - e = Link(self, src, None) - if isinstance(e, SCons.Errors.BuildError): - desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr) - raise SCons.Errors.StopError, desc - self.linked = 1 - # The Link() action may or may not have actually - # created the file, depending on whether the -n - # option was used or not. Delete the _exists and - # _rexists attributes so they can be reevaluated. - self.clear() - - memoizer_counters.append(SCons.Memoize.CountValue('exists')) - - def exists(self): - try: - return self._memo['exists'] - except KeyError: - pass - # Duplicate from source path if we are set up to do this. - if self.duplicate and not self.is_derived() and not self.linked: - src = self.srcnode() - if src is not self: - # At this point, src is meant to be copied in a variant directory. - src = src.rfile() - if src.abspath != self.abspath: - if src.exists(): - self.do_duplicate(src) - # Can't return 1 here because the duplication might - # not actually occur if the -n option is being used. - else: - # The source file does not exist. Make sure no old - # copy remains in the variant directory. - if Base.exists(self) or self.islink(): - self.fs.unlink(self.path) - # Return None explicitly because the Base.exists() call - # above will have cached its value if the file existed. - self._memo['exists'] = None - return None - result = Base.exists(self) - self._memo['exists'] = result - return result - - # - # SIGNATURE SUBSYSTEM - # - - def get_max_drift_csig(self): - """ - Returns the content signature currently stored for this node - if it's been unmodified longer than the max_drift value, or the - max_drift value is 0. Returns None otherwise. - """ - old = self.get_stored_info() - mtime = self.get_timestamp() - - max_drift = self.fs.max_drift - if max_drift > 0: - if (time.time() - mtime) > max_drift: - try: - n = old.ninfo - if n.timestamp and n.csig and n.timestamp == mtime: - return n.csig - except AttributeError: - pass - elif max_drift == 0: - try: - return old.ninfo.csig - except AttributeError: - pass - - return None - - def get_csig(self): - """ - Generate a node's content signature, the digested signature - of its content. - - node - the node - cache - alternate node to use for the signature cache - returns - the content signature - """ - ninfo = self.get_ninfo() - try: - return ninfo.csig - except AttributeError: - pass - - csig = self.get_max_drift_csig() - if csig is None: - - try: - if self.get_size() < SCons.Node.FS.File.md5_chunksize: - contents = self.get_contents() - else: - csig = self.get_content_hash() - except IOError: - # This can happen if there's actually a directory on-disk, - # which can be the case if they've disabled disk checks, - # or if an action with a File target actually happens to - # create a same-named directory by mistake. - csig = '' - else: - if not csig: - csig = SCons.Util.MD5signature(contents) - - ninfo.csig = csig - - return csig - - # - # DECISION SUBSYSTEM - # - - def builder_set(self, builder): - SCons.Node.Node.builder_set(self, builder) - self.changed_since_last_build = self.decide_target - - def changed_content(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def changed_state(self, target, prev_ni): - return self.state != SCons.Node.up_to_date - - def changed_timestamp_then_content(self, target, prev_ni): - if not self.changed_timestamp_match(target, prev_ni): - try: - self.get_ninfo().csig = prev_ni.csig - except AttributeError: - pass - return False - return self.changed_content(target, prev_ni) - - def changed_timestamp_newer(self, target, prev_ni): - try: - return self.get_timestamp() > target.get_timestamp() - except AttributeError: - return 1 - - def changed_timestamp_match(self, target, prev_ni): - try: - return self.get_timestamp() != prev_ni.timestamp - except AttributeError: - return 1 - - def decide_source(self, target, prev_ni): - return target.get_build_env().decide_source(self, target, prev_ni) - - def decide_target(self, target, prev_ni): - return target.get_build_env().decide_target(self, target, prev_ni) - - # Initialize this Node's decider function to decide_source() because - # every file is a source file until it has a Builder attached... - changed_since_last_build = decide_source - - def is_up_to_date(self): - T = 0 - if T: Trace('is_up_to_date(%s):' % self) - if not self.exists(): - if T: Trace(' not self.exists():') - # The file doesn't exist locally... - r = self.rfile() - if r != self: - # ...but there is one in a Repository... - if not self.changed(r): - if T: Trace(' changed(%s):' % r) - # ...and it's even up-to-date... - if self._local: - # ...and they'd like a local copy. - e = LocalCopy(self, r, None) - if isinstance(e, SCons.Errors.BuildError): - raise - self.store_info() - if T: Trace(' 1\n') - return 1 - self.changed() - if T: Trace(' None\n') - return None - else: - r = self.changed() - if T: Trace(' self.exists(): %s\n' % r) - return not r - - memoizer_counters.append(SCons.Memoize.CountValue('rfile')) - - def rfile(self): - try: - return self._memo['rfile'] - except KeyError: - pass - result = self - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: node = dir.entries[norm_name] - except KeyError: node = dir.file_on_disk(self.name) - if node and node.exists() and \ - (isinstance(node, File) or isinstance(node, Entry) \ - or not node.is_derived()): - result = node - break - self._memo['rfile'] = result - return result - - def rstr(self): - return str(self.rfile()) - - def get_cachedir_csig(self): - """ - Fetch a Node's content signature for purposes of computing - another Node's cachesig. - - This is a wrapper around the normal get_csig() method that handles - the somewhat obscure case of using CacheDir with the -n option. - Any files that don't exist would normally be "built" by fetching - them from the cache, but the normal get_csig() method will try - to open up the local file, which doesn't exist because the -n - option meant we didn't actually pull the file from cachedir. - But since the file *does* actually exist in the cachedir, we - can use its contents for the csig. - """ - try: - return self.cachedir_csig - except AttributeError: - pass - - cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) - if not self.exists() and cachefile and os.path.exists(cachefile): - self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \ - SCons.Node.FS.File.md5_chunksize * 1024) - else: - self.cachedir_csig = self.get_csig() - return self.cachedir_csig - - def get_cachedir_bsig(self): - try: - return self.cachesig - except AttributeError: - pass - - # Add the path to the cache signature, because multiple - # targets built by the same action will all have the same - # build signature, and we have to differentiate them somehow. - children = self.children() - executor = self.get_executor() - # sigs = [n.get_cachedir_csig() for n in children] - sigs = map(lambda n: n.get_cachedir_csig(), children) - sigs.append(SCons.Util.MD5signature(executor.get_contents())) - sigs.append(self.path) - result = self.cachesig = SCons.Util.MD5collect(sigs) - return result - - -default_fs = None - -def get_default_fs(): - global default_fs - if not default_fs: - default_fs = FS() - return default_fs - -class FileFinder: - """ - """ - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self): - self._memo = {} - - def filedir_lookup(self, p, fd=None): - """ - A helper method for find_file() that looks up a directory for - a file we're trying to find. This only creates the Dir Node if - it exists on-disk, since if the directory doesn't exist we know - we won't find any files in it... :-) - - It would be more compact to just use this as a nested function - with a default keyword argument (see the commented-out version - below), but that doesn't work unless you have nested scopes, - so we define it here just so this work under Python 1.5.2. - """ - if fd is None: - fd = self.default_filedir - dir, name = os.path.split(fd) - drive, d = os.path.splitdrive(dir) - if d in ('/', os.sep): - return p.fs.get_root(drive).dir_on_disk(name) - if dir: - p = self.filedir_lookup(p, dir) - if not p: - return None - norm_name = _my_normcase(name) - try: - node = p.entries[norm_name] - except KeyError: - return p.dir_on_disk(name) - if isinstance(node, Dir): - return node - if isinstance(node, Entry): - node.must_be_same(Dir) - return node - return None - - def _find_file_key(self, filename, paths, verbose=None): - return (filename, paths) - - memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key)) - - def find_file(self, filename, paths, verbose=None): - """ - find_file(str, [Dir()]) -> [nodes] - - filename - a filename to find - paths - a list of directory path *nodes* to search in. Can be - represented as a list, a tuple, or a callable that is - called with no arguments and returns the list or tuple. - - returns - the node created from the found file. - - Find a node corresponding to either a derived file or a file - that exists already. - - Only the first file found is returned, and none is returned - if no file is found. - """ - memo_key = self._find_file_key(filename, paths) - try: - memo_dict = self._memo['find_file'] - except KeyError: - memo_dict = {} - self._memo['find_file'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - if verbose and not callable(verbose): - if not SCons.Util.is_String(verbose): - verbose = "find_file" - verbose = ' %s: ' % verbose - verbose = lambda s, v=verbose: sys.stdout.write(v + s) - - filedir, filename = os.path.split(filename) - if filedir: - # More compact code that we can't use until we drop - # support for Python 1.5.2: - # - #def filedir_lookup(p, fd=filedir): - # """ - # A helper function that looks up a directory for a file - # we're trying to find. This only creates the Dir Node - # if it exists on-disk, since if the directory doesn't - # exist we know we won't find any files in it... :-) - # """ - # dir, name = os.path.split(fd) - # if dir: - # p = filedir_lookup(p, dir) - # if not p: - # return None - # norm_name = _my_normcase(name) - # try: - # node = p.entries[norm_name] - # except KeyError: - # return p.dir_on_disk(name) - # if isinstance(node, Dir): - # return node - # if isinstance(node, Entry): - # node.must_be_same(Dir) - # return node - # if isinstance(node, Dir) or isinstance(node, Entry): - # return node - # return None - #paths = filter(None, map(filedir_lookup, paths)) - - self.default_filedir = filedir - paths = filter(None, map(self.filedir_lookup, paths)) - - result = None - for dir in paths: - if verbose: - verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) - node, d = dir.srcdir_find_file(filename) - if node: - if verbose: - verbose("... FOUND '%s' in '%s'\n" % (filename, d)) - result = node - break - - memo_dict[memo_key] = result - - return result - -find_file = FileFinder().find_file - - -def invalidate_node_memos(targets): - """ - Invalidate the memoized values of all Nodes (files or directories) - that are associated with the given entries. Has been added to - clear the cache of nodes affected by a direct execution of an - action (e.g. Delete/Copy/Chmod). Existing Node caches become - inconsistent if the action is run through Execute(). The argument - `targets` can be a single Node object or filename, or a sequence - of Nodes/filenames. - """ - from traceback import extract_stack - - # First check if the cache really needs to be flushed. Only - # actions run in the SConscript with Execute() seem to be - # affected. XXX The way to check if Execute() is in the stacktrace - # is a very dirty hack and should be replaced by a more sensible - # solution. - for f in extract_stack(): - if f[2] == 'Execute' and f[0][-14:] == 'Environment.py': - break - else: - # Dont have to invalidate, so return - return - - if not SCons.Util.is_List(targets): - targets = [targets] - - for entry in targets: - # If the target is a Node object, clear the cache. If it is a - # filename, look up potentially existing Node object first. - try: - entry.clear_memoized_values() - except AttributeError: - # Not a Node object, try to look up Node by filename. XXX - # This creates Node objects even for those filenames which - # do not correspond to an existing Node object. - node = get_default_fs().Entry(entry) - if node: - node.clear_memoized_values() - diff --git a/tools/scons/scons-local-1.2.0/SCons/Node/Python.py b/tools/scons/scons-local-1.2.0/SCons/Node/Python.py deleted file mode 100644 index 21fbb157c3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Node/Python.py +++ /dev/null @@ -1,119 +0,0 @@ -"""scons.Node.Python - -Python nodes. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Node/Python.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Node - -class ValueNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - - field_list = ['csig'] - - def str_to_node(self, s): - return Value(s) - -class ValueBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -class Value(SCons.Node.Node): - """A class for Python variables, typically passed on the command line - or generated by a script, but not from a file or some other source. - """ - - NodeInfo = ValueNodeInfo - BuildInfo = ValueBuildInfo - - def __init__(self, value, built_value=None): - SCons.Node.Node.__init__(self) - self.value = value - if not built_value is None: - self.built_value = built_value - - def str_for_display(self): - return repr(self.value) - - def __str__(self): - return str(self.value) - - def make_ready(self): - self.get_csig() - - def build(self, **kw): - if not hasattr(self, 'built_value'): - apply (SCons.Node.Node.build, (self,), kw) - - is_up_to_date = SCons.Node.Node.children_are_up_to_date - - def is_under(self, dir): - # Make Value nodes get built regardless of - # what directory scons was run from. Value nodes - # are outside the filesystem: - return 1 - - def write(self, built_value): - """Set the value of the node.""" - self.built_value = built_value - - def read(self): - """Return the value. If necessary, the value is built.""" - self.build() - if not hasattr(self, 'built_value'): - self.built_value = self.value - return self.built_value - - def get_contents(self): - """By the assumption that the node.built_value is a - deterministic product of the sources, the contents of a Value - are the concatenation of all the contents of its sources. As - the value need not be built when get_contents() is called, we - cannot use the actual node.built_value.""" - contents = str(self.value) - for kid in self.children(None): - contents = contents + kid.get_contents() - return contents - - def changed_since_last_build(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def get_csig(self, calc=None): - """Because we're a Python value node and don't have a real - timestamp, we get to ignore the calculator and just use the - value contents.""" - try: - return self.ninfo.csig - except AttributeError: - pass - contents = self.get_contents() - self.get_ninfo().csig = contents - return contents diff --git a/tools/scons/scons-local-1.2.0/SCons/Node/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Node/__init__.py deleted file mode 100644 index 8ea6719e01..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Node/__init__.py +++ /dev/null @@ -1,1330 +0,0 @@ -"""SCons.Node - -The Node package for the SCons software construction utility. - -This is, in many ways, the heart of SCons. - -A Node is where we encapsulate all of the dependency information about -any thing that SCons can build, or about any thing which SCons can use -to build some other thing. The canonical "thing," of course, is a file, -but a Node can also represent something remote (like a web page) or -something completely abstract (like an Alias). - -Each specific type of "thing" is specifically represented by a subclass -of the Node base class: Node.FS.File for files, Node.Alias for aliases, -etc. Dependency information is kept here in the base class, and -information specific to files/aliases/etc. is in the subclass. The -goal, if we've done this correctly, is that any type of "thing" should -be able to depend on any other type of "thing." - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Node/__init__.py 3842 2008/12/20 22:59:52 scons" - -import copy -from itertools import chain, izip -import string -import UserList - -from SCons.Debug import logInstanceCreation -import SCons.Executor -import SCons.Memoize -import SCons.Util - -from SCons.Debug import Trace - -def classname(obj): - return string.split(str(obj.__class__), '.')[-1] - -# Node states -# -# These are in "priority" order, so that the maximum value for any -# child/dependency of a node represents the state of that node if -# it has no builder of its own. The canonical example is a file -# system directory, which is only up to date if all of its children -# were up to date. -no_state = 0 -pending = 1 -executing = 2 -up_to_date = 3 -executed = 4 -failed = 5 - -StateString = { - 0 : "no_state", - 1 : "pending", - 2 : "executing", - 3 : "up_to_date", - 4 : "executed", - 5 : "failed", -} - -# controls whether implicit dependencies are cached: -implicit_cache = 0 - -# controls whether implicit dep changes are ignored: -implicit_deps_unchanged = 0 - -# controls whether the cached implicit deps are ignored: -implicit_deps_changed = 0 - -# A variable that can be set to an interface-specific function be called -# to annotate a Node with information about its creation. -def do_nothing(node): pass - -Annotate = do_nothing - -# Classes for signature info for Nodes. - -class NodeInfoBase: - """ - The generic base class for signature information for a Node. - - Node subclasses should subclass NodeInfoBase to provide their own - logic for dealing with their own Node-specific signature information. - """ - current_version_id = 1 - def __init__(self, node): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - self._version_id = self.current_version_id - def update(self, node): - try: - field_list = self.field_list - except AttributeError: - return - for f in field_list: - try: - delattr(self, f) - except AttributeError: - pass - try: - func = getattr(node, 'get_' + f) - except AttributeError: - pass - else: - setattr(self, f, func()) - def convert(self, node, val): - pass - def merge(self, other): - self.__dict__.update(other.__dict__) - def format(self, field_list=None, names=0): - if field_list is None: - try: - field_list = self.field_list - except AttributeError: - field_list = self.__dict__.keys() - field_list.sort() - fields = [] - for field in field_list: - try: - f = getattr(self, field) - except AttributeError: - f = None - f = str(f) - if names: - f = field + ': ' + f - fields.append(f) - return fields - -class BuildInfoBase: - """ - The generic base class for build information for a Node. - - This is what gets stored in a .sconsign file for each target file. - It contains a NodeInfo instance for this node (signature information - that's specific to the type of Node) and direct attributes for the - generic build stuff we have to track: sources, explicit dependencies, - implicit dependencies, and action information. - """ - current_version_id = 1 - def __init__(self, node): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - self._version_id = self.current_version_id - self.bsourcesigs = [] - self.bdependsigs = [] - self.bimplicitsigs = [] - self.bactsig = None - def merge(self, other): - self.__dict__.update(other.__dict__) - -class Node: - """The base Node class, for entities that we know how to - build, or use to build other Nodes. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - class Attrs: - pass - - def __init__(self): - if __debug__: logInstanceCreation(self, 'Node.Node') - # Note that we no longer explicitly initialize a self.builder - # attribute to None here. That's because the self.builder - # attribute may be created on-the-fly later by a subclass (the - # canonical example being a builder to fetch a file from a - # source code system like CVS or Subversion). - - # Each list of children that we maintain is accompanied by a - # dictionary used to look up quickly whether a node is already - # present in the list. Empirical tests showed that it was - # fastest to maintain them as side-by-side Node attributes in - # this way, instead of wrapping up each list+dictionary pair in - # a class. (Of course, we could always still do that in the - # future if we had a good reason to...). - self.sources = [] # source files used to build node - self.sources_set = set() - self._specific_sources = False - self.depends = [] # explicit dependencies (from Depends) - self.depends_set = set() - self.ignore = [] # dependencies to ignore - self.ignore_set = set() - self.prerequisites = SCons.Util.UniqueList() - self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) - self.waiting_parents = set() - self.waiting_s_e = set() - self.ref_count = 0 - self.wkids = None # Kids yet to walk, when it's an array - - self.env = None - self.state = no_state - self.precious = None - self.noclean = 0 - self.nocache = 0 - self.always_build = None - self.includes = None - self.attributes = self.Attrs() # Generic place to stick information about the Node. - self.side_effect = 0 # true iff this node is a side effect - self.side_effects = [] # the side effects of building this target - self.linked = 0 # is this node linked to the variant directory? - - self.clear_memoized_values() - - # Let the interface in which the build engine is embedded - # annotate this Node with its own info (like a description of - # what line in what file created the node, for example). - Annotate(self) - - def disambiguate(self, must_exist=None): - return self - - def get_suffix(self): - return '' - - memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) - - def get_build_env(self): - """Fetch the appropriate Environment to build this node. - """ - try: - return self._memo['get_build_env'] - except KeyError: - pass - result = self.get_executor().get_build_env() - self._memo['get_build_env'] = result - return result - - def get_build_scanner_path(self, scanner): - """Fetch the appropriate scanner path for this node.""" - return self.get_executor().get_build_scanner_path(scanner) - - def set_executor(self, executor): - """Set the action executor for this node.""" - self.executor = executor - - def get_executor(self, create=1): - """Fetch the action executor for this node. Create one if - there isn't already one, and requested to do so.""" - try: - executor = self.executor - except AttributeError: - if not create: - raise - try: - act = self.builder.action - except AttributeError: - executor = SCons.Executor.Null(targets=[self]) - else: - executor = SCons.Executor.Executor(act, - self.env or self.builder.env, - [self.builder.overrides], - [self], - self.sources) - self.executor = executor - return executor - - def executor_cleanup(self): - """Let the executor clean up any cached information.""" - try: - executor = self.get_executor(create=None) - except AttributeError: - pass - else: - executor.cleanup() - - def reset_executor(self): - "Remove cached executor; forces recompute when needed." - try: - delattr(self, 'executor') - except AttributeError: - pass - - def retrieve_from_cache(self): - """Try to retrieve the node's content from a cache - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Returns true iff the node was successfully retrieved. - """ - return 0 - - # - # Taskmaster interface subsystem - # - - def make_ready(self): - """Get a Node ready for evaluation. - - This is called before the Taskmaster decides if the Node is - up-to-date or not. Overriding this method allows for a Node - subclass to be disambiguated if necessary, or for an implicit - source builder to be attached. - """ - pass - - def prepare(self): - """Prepare for this Node to be built. - - This is called after the Taskmaster has decided that the Node - is out-of-date and must be rebuilt, but before actually calling - the method to build the Node. - - This default implementation checks that explicit or implicit - dependencies either exist or are derived, and initializes the - BuildInfo structure that will hold the information about how - this node is, uh, built. - - (The existence of source files is checked separately by the - Executor, which aggregates checks for all of the targets built - by a specific action.) - - Overriding this method allows for for a Node subclass to remove - the underlying file from the file system. Note that subclass - methods should call this base class method to get the child - check and the BuildInfo structure. - """ - for d in self.depends: - if d.missing(): - msg = "Explicit dependency `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError, msg % (d, self) - if not self.implicit is None: - for i in self.implicit: - if i.missing(): - msg = "Implicit dependency `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError, msg % (i, self) - self.binfo = self.get_binfo() - - def build(self, **kw): - """Actually build the node. - - This is called by the Taskmaster after it's decided that the - Node is out-of-date and must be rebuilt, and after the prepare() - method has gotten everything, uh, prepared. - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff - in built(). - - """ - try: - apply(self.get_executor(), (self,), kw) - except SCons.Errors.BuildError, e: - e.node = self - raise - - def built(self): - """Called just after this node is successfully built.""" - - # Clear the implicit dependency caches of any Nodes - # waiting for this Node to be built. - for parent in self.waiting_parents: - parent.implicit = None - - self.clear() - - self.ninfo.update(self) - - def visited(self): - """Called just after this node has been visited (with or - without a build).""" - try: - binfo = self.binfo - except AttributeError: - # Apparently this node doesn't need build info, so - # don't bother calculating or storing it. - pass - else: - self.ninfo.update(self) - self.store_info() - - # - # - # - - def add_to_waiting_s_e(self, node): - self.waiting_s_e.add(node) - - def add_to_waiting_parents(self, node): - """ - Returns the number of nodes added to our waiting parents list: - 1 if we add a unique waiting parent, 0 if not. (Note that the - returned values are intended to be used to increment a reference - count, so don't think you can "clean up" this function by using - True and False instead...) - """ - wp = self.waiting_parents - if node in wp: - return 0 - wp.add(node) - return 1 - - def postprocess(self): - """Clean up anything we don't need to hang onto after we've - been built.""" - self.executor_cleanup() - self.waiting_parents = set() - - def clear(self): - """Completely clear a Node of all its cached state (so that it - can be re-evaluated by interfaces that do continuous integration - builds). - """ - # The del_binfo() call here isn't necessary for normal execution, - # but is for interactive mode, where we might rebuild the same - # target and need to start from scratch. - self.del_binfo() - self.clear_memoized_values() - self.ninfo = self.new_ninfo() - self.executor_cleanup() - try: - delattr(self, '_calculated_sig') - except AttributeError: - pass - self.includes = None - - def clear_memoized_values(self): - self._memo = {} - - def builder_set(self, builder): - self.builder = builder - try: - del self.executor - except AttributeError: - pass - - def has_builder(self): - """Return whether this Node has a builder or not. - - In Boolean tests, this turns out to be a *lot* more efficient - than simply examining the builder attribute directly ("if - node.builder: ..."). When the builder attribute is examined - directly, it ends up calling __getattr__ for both the __len__ - and __nonzero__ attributes on instances of our Builder Proxy - class(es), generating a bazillion extra calls and slowing - things down immensely. - """ - try: - b = self.builder - except AttributeError: - # There was no explicit builder for this Node, so initialize - # the self.builder attribute to None now. - b = self.builder = None - return not b is None - - def set_explicit(self, is_explicit): - self.is_explicit = is_explicit - - def has_explicit_builder(self): - """Return whether this Node has an explicit builder - - This allows an internal Builder created by SCons to be marked - non-explicit, so that it can be overridden by an explicit - builder that the user supplies (the canonical example being - directories).""" - try: - return self.is_explicit - except AttributeError: - self.is_explicit = None - return self.is_explicit - - def get_builder(self, default_builder=None): - """Return the set builder, or a specified default value""" - try: - return self.builder - except AttributeError: - return default_builder - - multiple_side_effect_has_builder = has_builder - - def is_derived(self): - """ - Returns true iff this node is derived (i.e. built). - - This should return true only for nodes whose path should be in - the variant directory when duplicate=0 and should contribute their build - signatures when they are used as source files to other derived files. For - example: source with source builders are not derived in this sense, - and hence should not return true. - """ - return self.has_builder() or self.side_effect - - def alter_targets(self): - """Return a list of alternate targets for this Node. - """ - return [], None - - def get_found_includes(self, env, scanner, path): - """Return the scanned include lines (implicit dependencies) - found in this node. - - The default is no implicit dependencies. We expect this method - to be overridden by any subclass that can be scanned for - implicit dependencies. - """ - return [] - - def get_implicit_deps(self, env, scanner, path): - """Return a list of implicit dependencies for this node. - - This method exists to handle recursive invocation of the scanner - on the implicit dependencies returned by the scanner, if the - scanner's recursive flag says that we should. - """ - if not scanner: - return [] - - # Give the scanner a chance to select a more specific scanner - # for this Node. - #scanner = scanner.select(self) - - nodes = [self] - seen = {} - seen[self] = 1 - deps = [] - while nodes: - n = nodes.pop(0) - d = filter(lambda x, seen=seen: not seen.has_key(x), - n.get_found_includes(env, scanner, path)) - if d: - deps.extend(d) - for n in d: - seen[n] = 1 - nodes.extend(scanner.recurse_nodes(d)) - - return deps - - def get_env_scanner(self, env, kw={}): - return env.get_scanner(self.scanner_key()) - - def get_target_scanner(self): - return self.builder.target_scanner - - def get_source_scanner(self, node): - """Fetch the source scanner for the specified node - - NOTE: "self" is the target being built, "node" is - the source file for which we want to fetch the scanner. - - Implies self.has_builder() is true; again, expect to only be - called from locations where this is already verified. - - This function may be called very often; it attempts to cache - the scanner found to improve performance. - """ - scanner = None - try: - scanner = self.builder.source_scanner - except AttributeError: - pass - if not scanner: - # The builder didn't have an explicit scanner, so go look up - # a scanner from env['SCANNERS'] based on the node's scanner - # key (usually the file extension). - scanner = self.get_env_scanner(self.get_build_env()) - if scanner: - scanner = scanner.select(node) - return scanner - - def add_to_implicit(self, deps): - if not hasattr(self, 'implicit') or self.implicit is None: - self.implicit = [] - self.implicit_set = set() - self._children_reset() - self._add_child(self.implicit, self.implicit_set, deps) - - def scan(self): - """Scan this node's dependents for implicit dependencies.""" - # Don't bother scanning non-derived files, because we don't - # care what their dependencies are. - # Don't scan again, if we already have scanned. - if not self.implicit is None: - return - self.implicit = [] - self.implicit_set = set() - self._children_reset() - if not self.has_builder(): - return - - build_env = self.get_build_env() - executor = self.get_executor() - - # Here's where we implement --implicit-cache. - if implicit_cache and not implicit_deps_changed: - implicit = self.get_stored_implicit() - if implicit is not None: - # We now add the implicit dependencies returned from the - # stored .sconsign entry to have already been converted - # to Nodes for us. (We used to run them through a - # source_factory function here.) - - # Update all of the targets with them. This - # essentially short-circuits an N*M scan of the - # sources for each individual target, which is a hell - # of a lot more efficient. - for tgt in executor.targets: - tgt.add_to_implicit(implicit) - - if implicit_deps_unchanged or self.is_up_to_date(): - return - # one of this node's sources has changed, - # so we must recalculate the implicit deps: - self.implicit = [] - self.implicit_set = set() - - # Have the executor scan the sources. - executor.scan_sources(self.builder.source_scanner) - - # If there's a target scanner, have the executor scan the target - # node itself and associated targets that might be built. - scanner = self.get_target_scanner() - if scanner: - executor.scan_targets(scanner) - - def scanner_key(self): - return None - - def select_scanner(self, scanner): - """Selects a scanner for this Node. - - This is a separate method so it can be overridden by Node - subclasses (specifically, Node.FS.Dir) that *must* use their - own Scanner and don't select one the Scanner.Selector that's - configured for the target. - """ - return scanner.select(self) - - def env_set(self, env, safe=0): - if safe and self.env: - return - self.env = env - - # - # SIGNATURE SUBSYSTEM - # - - NodeInfo = NodeInfoBase - BuildInfo = BuildInfoBase - - def new_ninfo(self): - ninfo = self.NodeInfo(self) - return ninfo - - def get_ninfo(self): - try: - return self.ninfo - except AttributeError: - self.ninfo = self.new_ninfo() - return self.ninfo - - def new_binfo(self): - binfo = self.BuildInfo(self) - return binfo - - def get_binfo(self): - """ - Fetch a node's build information. - - node - the node whose sources will be collected - cache - alternate node to use for the signature cache - returns - the build signature - - This no longer handles the recursive descent of the - node's children's signatures. We expect that they're - already built and updated by someone else, if that's - what's wanted. - """ - try: - return self.binfo - except AttributeError: - pass - - binfo = self.new_binfo() - self.binfo = binfo - - executor = self.get_executor() - ignore_set = self.ignore_set - - if self.has_builder(): - binfo.bact = str(executor) - binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) - - if self._specific_sources: - sources = [] - for s in self.sources: - if s not in ignore_set: - sources.append(s) - else: - sources = executor.get_unignored_sources(self.ignore) - seen = set() - bsources = [] - bsourcesigs = [] - for s in sources: - if not s in seen: - seen.add(s) - bsources.append(s) - bsourcesigs.append(s.get_ninfo()) - binfo.bsources = bsources - binfo.bsourcesigs = bsourcesigs - - depends = self.depends - dependsigs = [] - for d in depends: - if d not in ignore_set: - dependsigs.append(d.get_ninfo()) - binfo.bdepends = depends - binfo.bdependsigs = dependsigs - - implicit = self.implicit or [] - implicitsigs = [] - for i in implicit: - if i not in ignore_set: - implicitsigs.append(i.get_ninfo()) - binfo.bimplicit = implicit - binfo.bimplicitsigs = implicitsigs - - return binfo - - def del_binfo(self): - """Delete the build info from this node.""" - try: - delattr(self, 'binfo') - except AttributeError: - pass - - def get_csig(self): - try: - return self.ninfo.csig - except AttributeError: - ninfo = self.get_ninfo() - ninfo.csig = SCons.Util.MD5signature(self.get_contents()) - return self.ninfo.csig - - def get_cachedir_csig(self): - return self.get_csig() - - def store_info(self): - """Make the build signature permanent (that is, store it in the - .sconsign file or equivalent).""" - pass - - def do_not_store_info(self): - pass - - def get_stored_info(self): - return None - - def get_stored_implicit(self): - """Fetch the stored implicit dependencies""" - return None - - # - # - # - - def set_precious(self, precious = 1): - """Set the Node's precious value.""" - self.precious = precious - - def set_noclean(self, noclean = 1): - """Set the Node's noclean value.""" - # Make sure noclean is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.noclean = noclean and 1 or 0 - - def set_nocache(self, nocache = 1): - """Set the Node's nocache value.""" - # Make sure nocache is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.nocache = nocache and 1 or 0 - - def set_always_build(self, always_build = 1): - """Set the Node's always_build value.""" - self.always_build = always_build - - def exists(self): - """Does this node exists?""" - # All node exist by default: - return 1 - - def rexists(self): - """Does this node exist locally or in a repositiory?""" - # There are no repositories by default: - return self.exists() - - def missing(self): - return not self.is_derived() and \ - not self.linked and \ - not self.rexists() - - def remove(self): - """Remove this Node: no-op by default.""" - return None - - def add_dependency(self, depend): - """Adds dependencies.""" - try: - self._add_child(self.depends, self.depends_set, depend) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = map(str, e) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def add_prerequisite(self, prerequisite): - """Adds prerequisites""" - self.prerequisites.extend(prerequisite) - self._children_reset() - - def add_ignore(self, depend): - """Adds dependencies to ignore.""" - try: - self._add_child(self.ignore, self.ignore_set, depend) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = map(str, e) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def add_source(self, source): - """Adds sources.""" - if self._specific_sources: - return - try: - self._add_child(self.sources, self.sources_set, source) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = map(str, e) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def _add_child(self, collection, set, child): - """Adds 'child' to 'collection', first checking 'set' to see if it's - already present.""" - #if type(child) is not type([]): - # child = [child] - #for c in child: - # if not isinstance(c, Node): - # raise TypeError, c - added = None - for c in child: - if c not in set: - set.add(c) - collection.append(c) - added = 1 - if added: - self._children_reset() - - def set_specific_source(self, source): - self.add_source(source) - self._specific_sources = True - - def add_wkid(self, wkid): - """Add a node to the list of kids waiting to be evaluated""" - if self.wkids != None: - self.wkids.append(wkid) - - def _children_reset(self): - self.clear_memoized_values() - # We need to let the Executor clear out any calculated - # build info that it's cached so we can re-calculate it. - self.executor_cleanup() - - memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) - - def _children_get(self): - try: - return self._memo['children_get'] - except KeyError: - pass - - # The return list may contain duplicate Nodes, especially in - # source trees where there are a lot of repeated #includes - # of a tangle of .h files. Profiling shows, however, that - # eliminating the duplicates with a brute-force approach that - # preserves the order (that is, something like: - # - # u = [] - # for n in list: - # if n not in u: - # u.append(n)" - # - # takes more cycles than just letting the underlying methods - # hand back cached values if a Node's information is requested - # multiple times. (Other methods of removing duplicates, like - # using dictionary keys, lose the order, and the only ordered - # dictionary patterns I found all ended up using "not in" - # internally anyway...) - if self.ignore_set: - if self.implicit is None: - iter = chain(self.sources,self.depends) - else: - iter = chain(self.sources, self.depends, self.implicit) - - children = [] - for i in iter: - if i not in self.ignore_set: - children.append(i) - else: - if self.implicit is None: - children = self.sources + self.depends - else: - children = self.sources + self.depends + self.implicit - - self._memo['children_get'] = children - return children - - def all_children(self, scan=1): - """Return a list of all the node's direct children.""" - if scan: - self.scan() - - # The return list may contain duplicate Nodes, especially in - # source trees where there are a lot of repeated #includes - # of a tangle of .h files. Profiling shows, however, that - # eliminating the duplicates with a brute-force approach that - # preserves the order (that is, something like: - # - # u = [] - # for n in list: - # if n not in u: - # u.append(n)" - # - # takes more cycles than just letting the underlying methods - # hand back cached values if a Node's information is requested - # multiple times. (Other methods of removing duplicates, like - # using dictionary keys, lose the order, and the only ordered - # dictionary patterns I found all ended up using "not in" - # internally anyway...) - if self.implicit is None: - return self.sources + self.depends - else: - return self.sources + self.depends + self.implicit - - def children(self, scan=1): - """Return a list of the node's direct children, minus those - that are ignored by this node.""" - if scan: - self.scan() - return self._children_get() - - def set_state(self, state): - self.state = state - - def get_state(self): - return self.state - - def state_has_changed(self, target, prev_ni): - return (self.state != SCons.Node.up_to_date) - - def get_env(self): - env = self.env - if not env: - import SCons.Defaults - env = SCons.Defaults.DefaultEnvironment() - return env - - def changed_since_last_build(self, target, prev_ni): - """ - - Must be overridden in a specific subclass to return True if this - Node (a dependency) has changed since the last time it was used - to build the specified target. prev_ni is this Node's state (for - example, its file timestamp, length, maybe content signature) - as of the last time the target was built. - - Note that this method is called through the dependency, not the - target, because a dependency Node must be able to use its own - logic to decide if it changed. For example, File Nodes need to - obey if we're configured to use timestamps, but Python Value Nodes - never use timestamps and always use the content. If this method - were called through the target, then each Node's implementation - of this method would have to have more complicated logic to - handle all the different Node types on which it might depend. - """ - raise NotImplementedError - - def Decider(self, function): - SCons.Util.AddMethod(self, function, 'changed_since_last_build') - - def changed(self, node=None): - """ - Returns if the node is up-to-date with respect to the BuildInfo - stored last time it was built. The default behavior is to compare - it against our own previously stored BuildInfo, but the stored - BuildInfo from another Node (typically one in a Repository) - can be used instead. - - Note that we now *always* check every dependency. We used to - short-circuit the check by returning as soon as we detected - any difference, but we now rely on checking every dependency - to make sure that any necessary Node information (for example, - the content signature of an #included .h file) is updated. - """ - t = 0 - if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) - if node is None: - node = self - - result = False - - bi = node.get_stored_info().binfo - then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs - children = self.children() - - diff = len(children) - len(then) - if diff: - # The old and new dependency lists are different lengths. - # This always indicates that the Node must be rebuilt. - # We also extend the old dependency list with enough None - # entries to equal the new dependency list, for the benefit - # of the loop below that updates node information. - then.extend([None] * diff) - if t: Trace(': old %s new %s' % (len(then), len(children))) - result = True - - for child, prev_ni in izip(children, then): - if child.changed_since_last_build(self, prev_ni): - if t: Trace(': %s changed' % child) - result = True - - contents = self.get_executor().get_contents() - if self.has_builder(): - import SCons.Util - newsig = SCons.Util.MD5signature(contents) - if bi.bactsig != newsig: - if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) - result = True - - if not result: - if t: Trace(': up to date') - - if t: Trace('\n') - - return result - - def is_up_to_date(self): - """Default check for whether the Node is current: unknown Node - subtypes are always out of date, so they will always get built.""" - return None - - def children_are_up_to_date(self): - """Alternate check for whether the Node is current: If all of - our children were up-to-date, then this Node was up-to-date, too. - - The SCons.Node.Alias and SCons.Node.Python.Value subclasses - rebind their current() method to this method.""" - # Allow the children to calculate their signatures. - self.binfo = self.get_binfo() - if self.always_build: - return None - state = 0 - for kid in self.children(None): - s = kid.get_state() - if s and (not state or s > state): - state = s - return (state == 0 or state == SCons.Node.up_to_date) - - def is_literal(self): - """Always pass the string representation of a Node to - the command interpreter literally.""" - return 1 - - def render_include_tree(self): - """ - Return a text representation, suitable for displaying to the - user, of the include tree for the sources of this node. - """ - if self.is_derived() and self.env: - env = self.get_build_env() - for s in self.sources: - scanner = self.get_source_scanner(s) - if scanner: - path = self.get_build_scanner_path(scanner) - else: - path = None - def f(node, env=env, scanner=scanner, path=path): - return node.get_found_includes(env, scanner, path) - return SCons.Util.render_tree(s, f, 1) - else: - return None - - def get_abspath(self): - """ - Return an absolute path to the Node. This will return simply - str(Node) by default, but for Node types that have a concept of - relative path, this might return something different. - """ - return str(self) - - def for_signature(self): - """ - Return a string representation of the Node that will always - be the same for this particular Node, no matter what. This - is by contrast to the __str__() method, which might, for - instance, return a relative path for a file Node. The purpose - of this method is to generate a value to be used in signature - calculation for the command line used to build a target, and - we use this method instead of str() to avoid unnecessary - rebuilds. This method does not need to return something that - would actually work in a command line; it can return any kind of - nonsense, so long as it does not change. - """ - return str(self) - - def get_string(self, for_signature): - """This is a convenience function designed primarily to be - used in command generators (i.e., CommandGeneratorActions or - Environment variables that are callable), which are called - with a for_signature argument that is nonzero if the command - generator is being called to generate a signature for the - command line, which determines if we should rebuild or not. - - Such command generators should use this method in preference - to str(Node) when converting a Node to a string, passing - in the for_signature parameter, such that we will call - Node.for_signature() or str(Node) properly, depending on whether - we are calculating a signature or actually constructing a - command line.""" - if for_signature: - return self.for_signature() - return str(self) - - def get_subst_proxy(self): - """ - This method is expected to return an object that will function - exactly like this Node, except that it implements any additional - special features that we would like to be in effect for - Environment variable substitution. The principle use is that - some Nodes would like to implement a __getattr__() method, - but putting that in the Node type itself has a tendency to kill - performance. We instead put it in a proxy and return it from - this method. It is legal for this method to return self - if no new functionality is needed for Environment substitution. - """ - return self - - def explain(self): - if not self.exists(): - return "building `%s' because it doesn't exist\n" % self - - if self.always_build: - return "rebuilding `%s' because AlwaysBuild() is specified\n" % self - - old = self.get_stored_info() - if old is None: - return None - - old = old.binfo - old.prepare_dependencies() - - try: - old_bkids = old.bsources + old.bdepends + old.bimplicit - old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs - except AttributeError: - return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self - - new = self.get_binfo() - - new_bkids = new.bsources + new.bdepends + new.bimplicit - new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs - - osig = dict(izip(old_bkids, old_bkidsigs)) - nsig = dict(izip(new_bkids, new_bkidsigs)) - - # The sources and dependencies we'll want to report are all stored - # as relative paths to this target's directory, but we want to - # report them relative to the top-level SConstruct directory, - # so we only print them after running them through this lambda - # to turn them into the right relative Node and then return - # its string. - def stringify( s, E=self.dir.Entry ) : - if hasattr( s, 'dir' ) : - return str(E(s)) - return str(s) - - lines = [] - - removed = filter(lambda x, nk=new_bkids: not x in nk, old_bkids) - if removed: - removed = map(stringify, removed) - fmt = "`%s' is no longer a dependency\n" - lines.extend(map(lambda s, fmt=fmt: fmt % s, removed)) - - for k in new_bkids: - if not k in old_bkids: - lines.append("`%s' is a new dependency\n" % stringify(k)) - elif k.changed_since_last_build(self, osig[k]): - lines.append("`%s' changed\n" % stringify(k)) - - if len(lines) == 0 and old_bkids != new_bkids: - lines.append("the dependency order changed:\n" + - "%sold: %s\n" % (' '*15, map(stringify, old_bkids)) + - "%snew: %s\n" % (' '*15, map(stringify, new_bkids))) - - if len(lines) == 0: - def fmt_with_title(title, strlines): - lines = string.split(strlines, '\n') - sep = '\n' + ' '*(15 + len(title)) - return ' '*15 + title + string.join(lines, sep) + '\n' - if old.bactsig != new.bactsig: - if old.bact == new.bact: - lines.append("the contents of the build action changed\n" + - fmt_with_title('action: ', new.bact)) - else: - lines.append("the build action changed:\n" + - fmt_with_title('old: ', old.bact) + - fmt_with_title('new: ', new.bact)) - - if len(lines) == 0: - return "rebuilding `%s' for unknown reasons\n" % self - - preamble = "rebuilding `%s' because" % self - if len(lines) == 1: - return "%s %s" % (preamble, lines[0]) - else: - lines = ["%s:\n" % preamble] + lines - return string.join(lines, ' '*11) - -try: - [].extend(UserList.UserList([])) -except TypeError: - # Python 1.5.2 doesn't allow a list to be extended by list-like - # objects (such as UserList instances), so just punt and use - # real lists. - def NodeList(l): - return l -else: - class NodeList(UserList.UserList): - def __str__(self): - return str(map(str, self.data)) - -def get_children(node, parent): return node.children() -def ignore_cycle(node, stack): pass -def do_nothing(node, parent): pass - -class Walker: - """An iterator for walking a Node tree. - - This is depth-first, children are visited before the parent. - The Walker object can be initialized with any node, and - returns the next node on the descent with each next() call. - 'kids_func' is an optional function that will be called to - get the children of a node instead of calling 'children'. - 'cycle_func' is an optional function that will be called - when a cycle is detected. - - This class does not get caught in node cycles caused, for example, - by C header file include loops. - """ - def __init__(self, node, kids_func=get_children, - cycle_func=ignore_cycle, - eval_func=do_nothing): - self.kids_func = kids_func - self.cycle_func = cycle_func - self.eval_func = eval_func - node.wkids = copy.copy(kids_func(node, None)) - self.stack = [node] - self.history = {} # used to efficiently detect and avoid cycles - self.history[node] = None - - def next(self): - """Return the next node for this walk of the tree. - - This function is intentionally iterative, not recursive, - to sidestep any issues of stack size limitations. - """ - - while self.stack: - if self.stack[-1].wkids: - node = self.stack[-1].wkids.pop(0) - if not self.stack[-1].wkids: - self.stack[-1].wkids = None - if self.history.has_key(node): - self.cycle_func(node, self.stack) - else: - node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) - self.stack.append(node) - self.history[node] = None - else: - node = self.stack.pop() - del self.history[node] - if node: - if self.stack: - parent = self.stack[-1] - else: - parent = None - self.eval_func(node, parent) - return node - return None - - def is_done(self): - return not self.stack - - -arg2nodes_lookups = [] diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/BoolOption.py b/tools/scons/scons-local-1.2.0/SCons/Options/BoolOption.py deleted file mode 100644 index c5fed0a142..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/BoolOption.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/BoolOption.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def BoolOption(*args, **kw): - global warned - if not warned: - msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return apply(SCons.Variables.BoolVariable, args, kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/EnumOption.py b/tools/scons/scons-local-1.2.0/SCons/Options/EnumOption.py deleted file mode 100644 index 4f50d01b88..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/EnumOption.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/EnumOption.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def EnumOption(*args, **kw): - global warned - if not warned: - msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return apply(SCons.Variables.EnumVariable, args, kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/ListOption.py b/tools/scons/scons-local-1.2.0/SCons/Options/ListOption.py deleted file mode 100644 index b4cd923add..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/ListOption.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/ListOption.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def ListOption(*args, **kw): - global warned - if not warned: - msg = "The ListOption() function is deprecated; use the ListVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return apply(SCons.Variables.ListVariable, args, kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/PackageOption.py b/tools/scons/scons-local-1.2.0/SCons/Options/PackageOption.py deleted file mode 100644 index 7fcbe5f1dd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/PackageOption.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/PackageOption.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def PackageOption(*args, **kw): - global warned - if not warned: - msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return apply(SCons.Variables.PackageVariable, args, kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/PathOption.py b/tools/scons/scons-local-1.2.0/SCons/Options/PathOption.py deleted file mode 100644 index 649fc45eab..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/PathOption.py +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/PathOption.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -class _PathOptionClass: - def warn(self): - global warned - if not warned: - msg = "The PathOption() function is deprecated; use the PathVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - - def __call__(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable, args, kw) - - def PathAccept(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable.PathAccept, args, kw) - - def PathIsDir(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable.PathIsDir, args, kw) - - def PathIsDirCreate(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable.PathIsDirCreate, args, kw) - - def PathIsFile(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable.PathIsFile, args, kw) - - def PathExists(self, *args, **kw): - self.warn() - return apply(SCons.Variables.PathVariable.PathExists, args, kw) - -PathOption = _PathOptionClass() diff --git a/tools/scons/scons-local-1.2.0/SCons/Options/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Options/__init__.py deleted file mode 100644 index 3e41b8d634..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Options/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Options/__init__.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -from BoolOption import BoolOption # okay -from EnumOption import EnumOption # okay -from ListOption import ListOption # naja -from PackageOption import PackageOption # naja -from PathOption import PathOption # okay - -warned = False - -class Options(SCons.Variables.Variables): - def __init__(self, *args, **kw): - global warned - if not warned: - msg = "The Options class is deprecated; use the Variables class instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - apply(SCons.Variables.Variables.__init__, - (self,) + args, - kw) - - def AddOptions(self, *args, **kw): - return apply(SCons.Variables.Variables.AddVariables, - (self,) + args, - kw) - - def UnknownOptions(self, *args, **kw): - return apply(SCons.Variables.Variables.UnknownVariables, - (self,) + args, - kw) - - def FormatOptionHelpText(self, *args, **kw): - return apply(SCons.Variables.Variables.FormatVariableHelpText, - (self,) + args, - kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/PathList.py b/tools/scons/scons-local-1.2.0/SCons/PathList.py deleted file mode 100644 index 8b877fa4f1..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/PathList.py +++ /dev/null @@ -1,226 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/PathList.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """SCons.PathList - -A module for handling lists of directory paths (the sort of things -that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and -efficiency as we can while still keeping the evaluation delayed so that we -Do the Right Thing (almost) regardless of how the variable is specified. - -""" - -import os -import string - -import SCons.Memoize -import SCons.Node -import SCons.Util - -# -# Variables to specify the different types of entries in a PathList object: -# - -TYPE_STRING_NO_SUBST = 0 # string with no '$' -TYPE_STRING_SUBST = 1 # string containing '$' -TYPE_OBJECT = 2 # other object - -def node_conv(obj): - """ - This is the "string conversion" routine that we have our substitutions - use to return Nodes, not strings. This relies on the fact that an - EntryProxy object has a get() method that returns the underlying - Node that it wraps, which is a bit of architectural dependence - that we might need to break or modify in the future in response to - additional requirements. - """ - try: - get = obj.get - except AttributeError: - if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): - result = obj - else: - result = str(obj) - else: - result = get() - return result - -class _PathList: - """ - An actual PathList object. - """ - def __init__(self, pathlist): - """ - Initializes a PathList object, canonicalizing the input and - pre-processing it for quicker substitution later. - - The stored representation of the PathList is a list of tuples - containing (type, value), where the "type" is one of the TYPE_* - variables defined above. We distinguish between: - - strings that contain no '$' and therefore need no - delayed-evaluation string substitution (we expect that there - will be many of these and that we therefore get a pretty - big win from avoiding string substitution) - - strings that contain '$' and therefore need substitution - (the hard case is things like '${TARGET.dir}/include', - which require re-evaluation for every target + source) - - other objects (which may be something like an EntryProxy - that needs a method called to return a Node) - - Pre-identifying the type of each element in the PathList up-front - and storing the type in the list of tuples is intended to reduce - the amount of calculation when we actually do the substitution - over and over for each target. - """ - if SCons.Util.is_String(pathlist): - pathlist = string.split(pathlist, os.pathsep) - elif not SCons.Util.is_Sequence(pathlist): - pathlist = [pathlist] - - pl = [] - for p in pathlist: - try: - index = string.find(p, '$') - except (AttributeError, TypeError): - type = TYPE_OBJECT - else: - if index == -1: - type = TYPE_STRING_NO_SUBST - else: - type = TYPE_STRING_SUBST - pl.append((type, p)) - - self.pathlist = tuple(pl) - - def __len__(self): return len(self.pathlist) - - def __getitem__(self, i): return self.pathlist[i] - - def subst_path(self, env, target, source): - """ - Performs construction variable substitution on a pre-digested - PathList for a specific target and source. - """ - result = [] - for type, value in self.pathlist: - if type == TYPE_STRING_SUBST: - value = env.subst(value, target=target, source=source, - conv=node_conv) - if SCons.Util.is_Sequence(value): - result.extend(value) - continue - - elif type == TYPE_OBJECT: - value = node_conv(value) - if value: - result.append(value) - return tuple(result) - - -class PathListCache: - """ - A class to handle caching of PathList lookups. - - This class gets instantiated once and then deleted from the namespace, - so it's used as a Singleton (although we don't enforce that in the - usual Pythonic ways). We could have just made the cache a dictionary - in the module namespace, but putting it in this class allows us to - use the same Memoizer pattern that we use elsewhere to count cache - hits and misses, which is very valuable. - - Lookup keys in the cache are computed by the _PathList_key() method. - Cache lookup should be quick, so we don't spend cycles canonicalizing - all forms of the same lookup key. For example, 'x:y' and ['x', - 'y'] logically represent the same list, but we don't bother to - split string representations and treat those two equivalently. - (Note, however, that we do, treat lists and tuples the same.) - - The main type of duplication we're trying to catch will come from - looking up the same path list from two different clones of the - same construction environment. That is, given - - env2 = env1.Clone() - - both env1 and env2 will have the same CPPPATH value, and we can - cheaply avoid re-parsing both values of CPPPATH by using the - common value from this cache. - """ - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self): - self._memo = {} - - def _PathList_key(self, pathlist): - """ - Returns the key for memoization of PathLists. - - Note that we want this to be pretty quick, so we don't completely - canonicalize all forms of the same list. For example, - 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically - represent the same list if you're executing from $ROOT, but - we're not going to bother splitting strings into path elements, - or massaging strings into Nodes, to identify that equivalence. - We just want to eliminate obvious redundancy from the normal - case of re-using exactly the same cloned value for a path. - """ - if SCons.Util.is_Sequence(pathlist): - pathlist = tuple(SCons.Util.flatten(pathlist)) - return pathlist - - memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) - - def PathList(self, pathlist): - """ - Returns the cached _PathList object for the specified pathlist, - creating and caching a new object as necessary. - """ - pathlist = self._PathList_key(pathlist) - try: - memo_dict = self._memo['PathList'] - except KeyError: - memo_dict = {} - self._memo['PathList'] = memo_dict - else: - try: - return memo_dict[pathlist] - except KeyError: - pass - - result = _PathList(pathlist) - - memo_dict[pathlist] = result - - return result - -PathList = PathListCache().PathList - - -del PathListCache diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Platform/__init__.py deleted file mode 100644 index 12158650bc..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/__init__.py +++ /dev/null @@ -1,216 +0,0 @@ -"""SCons.Platform - -SCons platform selection. - -This looks for modules that define a callable object that can modify a -construction environment as appropriate for a given platform. - -Note that we take a more simplistic view of "platform" than Python does. -We're looking for a single string that determines a set of -tool-independent variables with which to initialize a construction -environment. Consequently, we'll examine both sys.platform and os.name -(and anything else that might come in to play) in order to return some -specification which is unique enough for our purposes. - -Note that because this subsysem just *selects* a callable that can -modify a construction environment, it's possible for people to define -their own "platform specification" in an arbitrary callable function. -No one needs to use or tie in to this subsystem in order to roll -their own platform definition. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/__init__.py 3842 2008/12/20 22:59:52 scons" - -import imp -import os -import string -import sys -import tempfile - -import SCons.Errors -import SCons.Tool - -def platform_default(): - """Return the platform string for our execution environment. - - The returned value should map to one of the SCons/Platform/*.py - files. Since we're architecture independent, though, we don't - care about the machine architecture. - """ - osname = os.name - if osname == 'java': - osname = os._osType - if osname == 'posix': - if sys.platform == 'cygwin': - return 'cygwin' - elif string.find(sys.platform, 'irix') != -1: - return 'irix' - elif string.find(sys.platform, 'sunos') != -1: - return 'sunos' - elif string.find(sys.platform, 'hp-ux') != -1: - return 'hpux' - elif string.find(sys.platform, 'aix') != -1: - return 'aix' - elif string.find(sys.platform, 'darwin') != -1: - return 'darwin' - else: - return 'posix' - elif os.name == 'os2': - return 'os2' - else: - return sys.platform - -def platform_module(name = platform_default()): - """Return the imported module for the platform. - - This looks for a module name that matches the specified argument. - If the name is unspecified, we fetch the appropriate default for - our execution environment. - """ - full_name = 'SCons.Platform.' + name - if not sys.modules.has_key(full_name): - if os.name == 'java': - eval(full_name) - else: - try: - file, path, desc = imp.find_module(name, - sys.modules['SCons.Platform'].__path__) - try: - mod = imp.load_module(full_name, file, path, desc) - finally: - if file: - file.close() - except ImportError: - try: - import zipimport - importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) - mod = importer.load_module(full_name) - except ImportError: - raise SCons.Errors.UserError, "No platform named '%s'" % name - setattr(SCons.Platform, name, mod) - return sys.modules[full_name] - -def DefaultToolList(platform, env): - """Select a default tool list for the specified platform. - """ - return SCons.Tool.tool_list(platform, env) - -class PlatformSpec: - def __init__(self, name): - self.name = name - - def __str__(self): - return self.name - -class TempFileMunge: - """A callable class. You can set an Environment variable to this, - then call it with a string argument, then it will perform temporary - file substitution on it. This is used to circumvent the long command - line limitation. - - Example usage: - env["TEMPFILE"] = TempFileMunge - env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" - - By default, the name of the temporary file used begins with a - prefix of '@'. This may be configred for other tool chains by - setting '$TEMPFILEPREFIX'. - - env["TEMPFILEPREFIX"] = '-@' # diab compiler - env["TEMPFILEPREFIX"] = '-via' # arm tool chain - """ - def __init__(self, cmd): - self.cmd = cmd - - def __call__(self, target, source, env, for_signature): - if for_signature: - return self.cmd - cmd = env.subst_list(self.cmd, 0, target, source)[0] - try: - maxline = int(env.subst('$MAXLINELENGTH')) - except ValueError: - maxline = 2048 - - if (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= maxline: - return self.cmd - - # We do a normpath because mktemp() has what appears to be - # a bug in Windows that will use a forward slash as a path - # delimiter. Windows's link mistakes that for a command line - # switch and barfs. - # - # We use the .lnk suffix for the benefit of the Phar Lap - # linkloc linker, which likes to append an .lnk suffix if - # none is given. - tmp = os.path.normpath(tempfile.mktemp('.lnk')) - native_tmp = SCons.Util.get_native_path(tmp) - - if env['SHELL'] and env['SHELL'] == 'sh': - # The sh shell will try to escape the backslashes in the - # path, so unescape them. - native_tmp = string.replace(native_tmp, '\\', r'\\\\') - # In Cygwin, we want to use rm to delete the temporary - # file, because del does not exist in the sh shell. - rm = env.Detect('rm') or 'del' - else: - # Don't use 'rm' if the shell is not sh, because rm won't - # work with the Windows shells (cmd.exe or command.com) or - # Windows path names. - rm = 'del' - - prefix = env.subst('$TEMPFILEPREFIX') - if not prefix: - prefix = '@' - - args = map(SCons.Subst.quote_spaces, cmd[1:]) - open(tmp, 'w').write(string.join(args, " ") + "\n") - # XXX Using the SCons.Action.print_actions value directly - # like this is bogus, but expedient. This class should - # really be rewritten as an Action that defines the - # __call__() and strfunction() methods and lets the - # normal action-execution logic handle whether or not to - # print/execute the action. The problem, though, is all - # of that is decided before we execute this method as - # part of expanding the $TEMPFILE construction variable. - # Consequently, refactoring this will have to wait until - # we get more flexible with allowing Actions to exist - # independently and get strung together arbitrarily like - # Ant tasks. In the meantime, it's going to be more - # user-friendly to not let obsession with architectural - # purity get in the way of just being helpful, so we'll - # reach into SCons.Action directly. - if SCons.Action.print_actions: - print("Using tempfile "+native_tmp+" for command line:\n"+ - str(cmd[0]) + " " + string.join(args," ")) - return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] - -def Platform(name = platform_default()): - """Select a canned Platform specification. - """ - module = platform_module(name) - spec = PlatformSpec(name) - spec.__call__ = module.generate - return spec diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/aix.py b/tools/scons/scons-local-1.2.0/SCons/Platform/aix.py deleted file mode 100644 index c8cb7e89f0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/aix.py +++ /dev/null @@ -1,65 +0,0 @@ -"""engine.SCons.Platform.aix - -Platform-specific initialization for IBM AIX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/aix.py 3842 2008/12/20 22:59:52 scons" - -import os -import string - -import posix - -def get_xlc(env, xlc=None, xlc_r=None, packages=[]): - # Use the AIX package installer tool lslpp to figure out where a - # given xl* compiler is installed and what version it is. - xlcPath = None - xlcVersion = None - - if xlc is None: - xlc = env.get('CC', 'xlc') - if xlc_r is None: - xlc_r = xlc + '_r' - for package in packages: - cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" - line = os.popen(cmd).readline() - if line: - v, p = string.split(line, ':')[1:3] - xlcVersion = string.split(v)[1] - xlcPath = string.split(p)[0] - xlcPath = xlcPath[:xlcPath.rindex('/')] - break - return (xlcPath, xlc, xlc_r, xlcVersion) - -def generate(env): - posix.generate(env) - #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion - env['MAXLINELENGTH'] = 21576 - diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/cygwin.py b/tools/scons/scons-local-1.2.0/SCons/Platform/cygwin.py deleted file mode 100644 index f51eeb16ee..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/cygwin.py +++ /dev/null @@ -1,49 +0,0 @@ -"""SCons.Platform.cygwin - -Platform-specific initialization for Cygwin systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/cygwin.py 3842 2008/12/20 22:59:52 scons" - -import posix -from SCons.Platform import TempFileMunge - -def generate(env): - posix.generate(env) - - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - env['MAXLINELENGTH'] = 2048 diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/darwin.py b/tools/scons/scons-local-1.2.0/SCons/Platform/darwin.py deleted file mode 100644 index 94365465cf..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/darwin.py +++ /dev/null @@ -1,40 +0,0 @@ -"""engine.SCons.Platform.darwin - -Platform-specific initialization for Mac OS X systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/darwin.py 3842 2008/12/20 22:59:52 scons" - -import posix - -def generate(env): - posix.generate(env) - env['SHLIBSUFFIX'] = '.dylib' - env['ENV']['PATH'] = env['ENV']['PATH'] + ':/sw/bin' diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/hpux.py b/tools/scons/scons-local-1.2.0/SCons/Platform/hpux.py deleted file mode 100644 index 2bd468b71d..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/hpux.py +++ /dev/null @@ -1,40 +0,0 @@ -"""engine.SCons.Platform.hpux - -Platform-specific initialization for HP-UX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/hpux.py 3842 2008/12/20 22:59:52 scons" - -import posix - -def generate(env): - posix.generate(env) - #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion - env['MAXLINELENGTH'] = 2045000 diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/irix.py b/tools/scons/scons-local-1.2.0/SCons/Platform/irix.py deleted file mode 100644 index b70481db29..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/irix.py +++ /dev/null @@ -1,38 +0,0 @@ -"""SCons.Platform.irix - -Platform-specific initialization for SGI IRIX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/irix.py 3842 2008/12/20 22:59:52 scons" - -import posix - -def generate(env): - posix.generate(env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/os2.py b/tools/scons/scons-local-1.2.0/SCons/Platform/os2.py deleted file mode 100644 index 803d890d9d..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/os2.py +++ /dev/null @@ -1,49 +0,0 @@ -"""SCons.Platform.os2 - -Platform-specific initialization for OS/2 systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/os2.py 3842 2008/12/20 22:59:52 scons" - -def generate(env): - if not env.has_key('ENV'): - env['ENV'] = {} - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = '$LIBPREFIX' - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/posix.py b/tools/scons/scons-local-1.2.0/SCons/Platform/posix.py deleted file mode 100644 index 2a7c544207..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/posix.py +++ /dev/null @@ -1,258 +0,0 @@ -"""SCons.Platform.posix - -Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/posix.py 3842 2008/12/20 22:59:52 scons" - -import errno -import os -import os.path -import string -import subprocess -import sys -import select - -import SCons.Util -from SCons.Platform import TempFileMunge - -exitvalmap = { - 2 : 127, - 13 : 126, -} - -def escape(arg): - "escape shell special characters" - slash = '\\' - special = '"$()' - - arg = string.replace(arg, slash, slash+slash) - for c in special: - arg = string.replace(arg, c, slash+c) - - return '"' + arg + '"' - -def exec_system(l, env): - stat = os.system(string.join(l)) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def exec_spawnvpe(l, env): - stat = os.spawnvpe(os.P_WAIT, l[0], l, env) - # os.spawnvpe() returns the actual exit code, not the encoding - # returned by os.waitpid() or os.system(). - return stat - -def exec_fork(l, env): - pid = os.fork() - if not pid: - # Child process. - exitval = 127 - try: - os.execvpe(l[0], l, env) - except OSError, e: - exitval = exitvalmap.get(e[0], e[0]) - sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) - os._exit(exitval) - else: - # Parent process. - pid, stat = os.waitpid(pid, 0) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def _get_env_command(sh, escape, cmd, args, env): - s = string.join(args) - if env: - l = ['env', '-'] + \ - map(lambda t, e=escape: e(t[0])+'='+e(t[1]), env.items()) + \ - [sh, '-c', escape(s)] - s = string.join(l) - return s - -def env_spawn(sh, escape, cmd, args, env): - return exec_system([_get_env_command( sh, escape, cmd, args, env)], env) - -def spawnvpe_spawn(sh, escape, cmd, args, env): - return exec_spawnvpe([sh, '-c', string.join(args)], env) - -def fork_spawn(sh, escape, cmd, args, env): - return exec_fork([sh, '-c', string.join(args)], env) - -def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): - stdout_eof = stderr_eof = 0 - while not (stdout_eof and stderr_eof): - try: - (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) - if cmd_stdout in i: - str = cmd_stdout.read() - if len(str) == 0: - stdout_eof = 1 - elif stdout != None: - stdout.write(str) - if cmd_stderr in i: - str = cmd_stderr.read() - if len(str) == 0: - #sys.__stderr__.write( "stderr_eof=1\n" ) - stderr_eof = 1 - else: - #sys.__stderr__.write( "str(stderr) = %s\n" % str ) - stderr.write(str) - except select.error, (_errno, _strerror): - if _errno != errno.EINTR: - raise - -def exec_popen3(l, env, stdout, stderr): - proc = subprocess.Popen(string.join(l), - stdout=stdout, - stderr=stderr, - shell=True) - stat = proc.wait() - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def exec_piped_fork(l, env, stdout, stderr): - # spawn using fork / exec and providing a pipe for the command's - # stdout / stderr stream - if stdout != stderr: - (rFdOut, wFdOut) = os.pipe() - (rFdErr, wFdErr) = os.pipe() - else: - (rFdOut, wFdOut) = os.pipe() - rFdErr = rFdOut - wFdErr = wFdOut - # do the fork - pid = os.fork() - if not pid: - # Child process - os.close( rFdOut ) - if rFdOut != rFdErr: - os.close( rFdErr ) - os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? - os.dup2( wFdErr, 2 ) - os.close( wFdOut ) - if stdout != stderr: - os.close( wFdErr ) - exitval = 127 - try: - os.execvpe(l[0], l, env) - except OSError, e: - exitval = exitvalmap.get(e[0], e[0]) - stderr.write("scons: %s: %s\n" % (l[0], e[1])) - os._exit(exitval) - else: - # Parent process - pid, stat = os.waitpid(pid, 0) - os.close( wFdOut ) - if stdout != stderr: - os.close( wFdErr ) - childOut = os.fdopen( rFdOut ) - if stdout != stderr: - childErr = os.fdopen( rFdErr ) - else: - childErr = childOut - process_cmd_output(childOut, childErr, stdout, stderr) - os.close( rFdOut ) - if stdout != stderr: - os.close( rFdErr ) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): - # spawn using Popen3 combined with the env command - # the command name and the command's stdout is written to stdout - # the command's stderr is written to stderr - return exec_popen3([_get_env_command(sh, escape, cmd, args, env)], - env, stdout, stderr) - -def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): - # spawn using fork / exec and providing a pipe for the command's - # stdout / stderr stream - return exec_piped_fork([sh, '-c', string.join(args)], - env, stdout, stderr) - - - -def generate(env): - # If os.spawnvpe() exists, we use it to spawn commands. Otherwise - # if the env utility exists, we use os.system() to spawn commands, - # finally we fall back on os.fork()/os.exec(). - # - # os.spawnvpe() is prefered because it is the most efficient. But - # for Python versions without it, os.system() is prefered because it - # is claimed that it works better with threads (i.e. -j) and is more - # efficient than forking Python. - # - # NB: Other people on the scons-users mailing list have claimed that - # os.fork()/os.exec() works better than os.system(). There may just - # not be a default that works best for all users. - - if os.__dict__.has_key('spawnvpe'): - spawn = spawnvpe_spawn - elif env.Detect('env'): - spawn = env_spawn - else: - spawn = fork_spawn - - if env.Detect('env'): - pspawn = piped_env_spawn - else: - pspawn = piped_fork_spawn - - if not env.has_key('ENV'): - env['ENV'] = {} - env['ENV']['PATH'] = os.environ.get("PATH") - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.o' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - env['SHLIBPREFIX'] = '$LIBPREFIX' - env['SHLIBSUFFIX'] = '.so' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] - env['PSPAWN'] = pspawn - env['SPAWN'] = spawn - env['SHELL'] = 'sh' - env['ESCAPE'] = escape - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion - #Note: specific platforms might rise or lower this value - env['MAXLINELENGTH'] = 128072 - - # This platform supports RPATH specifications. - env['__RPATH'] = '$_RPATH' diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/sunos.py b/tools/scons/scons-local-1.2.0/SCons/Platform/sunos.py deleted file mode 100644 index 03435c6918..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/sunos.py +++ /dev/null @@ -1,44 +0,0 @@ -"""engine.SCons.Platform.sunos - -Platform-specific initialization for Sun systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/sunos.py 3842 2008/12/20 22:59:52 scons" - -import posix - -def generate(env): - posix.generate(env) - # Based on sunSparc 8:32bit - # ARG_MAX=1048320 - 3000 for environment expansion - env['MAXLINELENGTH'] = 1045320 - env['PKGINFO'] = 'pkginfo' - env['PKGCHK'] = '/usr/sbin/pkgchk' - env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' diff --git a/tools/scons/scons-local-1.2.0/SCons/Platform/win32.py b/tools/scons/scons-local-1.2.0/SCons/Platform/win32.py deleted file mode 100644 index 3ec0a526d3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Platform/win32.py +++ /dev/null @@ -1,324 +0,0 @@ -"""SCons.Platform.win32 - -Platform-specific initialization for Win32 systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Platform/win32.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string -import sys -import tempfile - -from SCons.Platform.posix import exitvalmap -from SCons.Platform import TempFileMunge -import SCons.Util - - - -try: - import msvcrt - import win32api - import win32con - - msvcrt.get_osfhandle - win32api.SetHandleInformation - win32con.HANDLE_FLAG_INHERIT -except ImportError: - parallel_msg = \ - "you do not seem to have the pywin32 extensions installed;\n" + \ - "\tparallel (-j) builds may not work reliably with open Python files." -except AttributeError: - parallel_msg = \ - "your pywin32 extensions do not support file handle operations;\n" + \ - "\tparallel (-j) builds may not work reliably with open Python files." -else: - parallel_msg = None - - import __builtin__ - - _builtin_file = __builtin__.file - _builtin_open = __builtin__.open - - def _scons_file(*args, **kw): - fp = apply(_builtin_file, args, kw) - win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), - win32con.HANDLE_FLAG_INHERIT, - 0) - return fp - - def _scons_open(*args, **kw): - fp = apply(_builtin_open, args, kw) - win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), - win32con.HANDLE_FLAG_INHERIT, - 0) - return fp - - __builtin__.file = _scons_file - __builtin__.open = _scons_open - - - -# The upshot of all this is that, if you are using Python 1.5.2, -# you had better have cmd or command.com in your PATH when you run -# scons. - -def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): - # There is no direct way to do that in python. What we do - # here should work for most cases: - # In case stdout (stderr) is not redirected to a file, - # we redirect it into a temporary file tmpFileStdout - # (tmpFileStderr) and copy the contents of this file - # to stdout (stderr) given in the argument - if not sh: - sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") - return 127 - else: - # one temporary file for stdout and stderr - tmpFileStdout = os.path.normpath(tempfile.mktemp()) - tmpFileStderr = os.path.normpath(tempfile.mktemp()) - - # check if output is redirected - stdoutRedirected = 0 - stderrRedirected = 0 - for arg in args: - # are there more possibilities to redirect stdout ? - if (string.find( arg, ">", 0, 1 ) != -1 or - string.find( arg, "1>", 0, 2 ) != -1): - stdoutRedirected = 1 - # are there more possibilities to redirect stderr ? - if string.find( arg, "2>", 0, 2 ) != -1: - stderrRedirected = 1 - - # redirect output of non-redirected streams to our tempfiles - if stdoutRedirected == 0: - args.append(">" + str(tmpFileStdout)) - if stderrRedirected == 0: - args.append("2>" + str(tmpFileStderr)) - - # actually do the spawn - try: - args = [sh, '/C', escape(string.join(args)) ] - ret = os.spawnve(os.P_WAIT, sh, args, env) - except OSError, e: - # catch any error - try: - ret = exitvalmap[e[0]] - except KeyError: - sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) - if stderr != None: - stderr.write("scons: %s: %s\n" % (cmd, e[1])) - # copy child output from tempfiles to our streams - # and do clean up stuff - if stdout != None and stdoutRedirected == 0: - try: - stdout.write(open( tmpFileStdout, "r" ).read()) - os.remove( tmpFileStdout ) - except (IOError, OSError): - pass - - if stderr != None and stderrRedirected == 0: - try: - stderr.write(open( tmpFileStderr, "r" ).read()) - os.remove( tmpFileStderr ) - except (IOError, OSError): - pass - return ret - -def exec_spawn(l, env): - try: - result = os.spawnve(os.P_WAIT, l[0], l, env) - except OSError, e: - try: - result = exitvalmap[e[0]] - sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) - except KeyError: - result = 127 - if len(l) > 2: - if len(l[2]) < 1000: - command = string.join(l[0:3]) - else: - command = l[0] - else: - command = l[0] - sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1])) - return result - -def spawn(sh, escape, cmd, args, env): - if not sh: - sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") - return 127 - return exec_spawn([sh, '/C', escape(string.join(args))], env) - -# Windows does not allow special characters in file names anyway, so no -# need for a complex escape function, we will just quote the arg, except -# that "cmd /c" requires that if an argument ends with a backslash it -# needs to be escaped so as not to interfere with closing double quote -# that we add. -def escape(x): - if x[-1] == '\\': - x = x + '\\' - return '"' + x + '"' - -# Get the windows system directory name -def get_system_root(): - # A resonable default if we can't read the registry - try: - val = os.environ['SYSTEMROOT'] - except KeyError: - val = "C:/WINDOWS" - pass - - # First see if we can look in the registry... - if SCons.Util.can_read_reg: - try: - # Look for Windows NT system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows NT\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - except SCons.Util.RegError: - try: - # Okay, try the Windows 9x system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - except KeyboardInterrupt: - raise - except: - pass - return val - -# Get the location of the program files directory -def get_program_files_dir(): - # Now see if we can look in the registry... - val = '' - if SCons.Util.can_read_reg: - try: - # Look for Windows Program Files directory - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') - except SCons.Util.RegError: - val = '' - pass - - if val == '': - # A reasonable default if we can't read the registry - # (Actually, it's pretty reasonable even if we can :-) - val = os.path.join(os.path.dirname(get_system_root()),"Program Files") - - return val - -def generate(env): - # Attempt to find cmd.exe (for WinNT/2k/XP) or - # command.com for Win9x - cmd_interp = '' - # First see if we can look in the registry... - if SCons.Util.can_read_reg: - try: - # Look for Windows NT system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows NT\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - cmd_interp = os.path.join(val, 'System32\\cmd.exe') - except SCons.Util.RegError: - try: - # Okay, try the Windows 9x system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - cmd_interp = os.path.join(val, 'command.com') - except KeyboardInterrupt: - raise - except: - pass - - # For the special case of not having access to the registry, we - # use a temporary path and pathext to attempt to find the command - # interpreter. If we fail, we try to find the interpreter through - # the env's PATH. The problem with that is that it might not - # contain an ENV and a PATH. - if not cmd_interp: - systemroot = r'C:\Windows' - if os.environ.has_key('SYSTEMROOT'): - systemroot = os.environ['SYSTEMROOT'] - tmp_path = systemroot + os.pathsep + \ - os.path.join(systemroot,'System32') - tmp_pathext = '.com;.exe;.bat;.cmd' - if os.environ.has_key('PATHEXT'): - tmp_pathext = os.environ['PATHEXT'] - cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) - if not cmd_interp: - cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) - - if not cmd_interp: - cmd_interp = env.Detect('cmd') - if not cmd_interp: - cmd_interp = env.Detect('command') - - - if not env.has_key('ENV'): - env['ENV'] = {} - - # Import things from the external environment to the construction - # environment's ENV. This is a potential slippery slope, because we - # *don't* want to make builds dependent on the user's environment by - # default. We're doing this for SYSTEMROOT, though, because it's - # needed for anything that uses sockets, and seldom changes, and - # for SYSTEMDRIVE because it's related. - # - # Weigh the impact carefully before adding other variables to this list. - import_env = [ 'SYSTEMDRIVE', 'SYSTEMROOT', 'TEMP', 'TMP' ] - for var in import_env: - v = os.environ.get(var) - if v: - env['ENV'][var] = v - - env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] - env['PSPAWN'] = piped_spawn - env['SPAWN'] = spawn - env['SHELL'] = cmd_interp - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - env['MAXLINELENGTH'] = 2048 - env['ESCAPE'] = escape diff --git a/tools/scons/scons-local-1.2.0/SCons/SConf.py b/tools/scons/scons-local-1.2.0/SCons/SConf.py deleted file mode 100644 index ec80fe97a4..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/SConf.py +++ /dev/null @@ -1,1012 +0,0 @@ -"""SCons.SConf - -Autoconf-like configuration support. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/SConf.py 3842 2008/12/20 22:59:52 scons" - -import os -import re -import string -import StringIO -import sys -import traceback -import types - -import SCons.Action -import SCons.Builder -import SCons.Errors -import SCons.Job -import SCons.Node.FS -import SCons.Taskmaster -import SCons.Util -import SCons.Warnings -import SCons.Conftest - -from SCons.Debug import Trace - -# Turn off the Conftest error logging -SCons.Conftest.LogInputFiles = 0 -SCons.Conftest.LogErrorMessages = 0 - -# Set -build_type = None -build_types = ['clean', 'help'] - -def SetBuildType(type): - global build_type - build_type = type - -# to be set, if we are in dry-run mode -dryrun = 0 - -AUTO=0 # use SCons dependency scanning for up-to-date checks -FORCE=1 # force all tests to be rebuilt -CACHE=2 # force all tests to be taken from cache (raise an error, if necessary) -cache_mode = AUTO - -def SetCacheMode(mode): - """Set the Configure cache mode. mode must be one of "auto", "force", - or "cache".""" - global cache_mode - if mode == "auto": - cache_mode = AUTO - elif mode == "force": - cache_mode = FORCE - elif mode == "cache": - cache_mode = CACHE - else: - raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode - -progress_display = SCons.Util.display # will be overwritten by SCons.Script -def SetProgressDisplay(display): - """Set the progress display to use (called from SCons.Script)""" - global progress_display - progress_display = display - -SConfFS = None - -_ac_build_counter = 0 # incremented, whenever TryBuild is called -_ac_config_logs = {} # all config.log files created in this build -_ac_config_hs = {} # all config.h files created in this build -sconf_global = None # current sconf object - -def _createConfigH(target, source, env): - t = open(str(target[0]), "w") - defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0]))) - t.write("""#ifndef %(DEFNAME)s_SEEN -#define %(DEFNAME)s_SEEN - -""" % {'DEFNAME' : defname}) - t.write(source[0].get_contents()) - t.write(""" -#endif /* %(DEFNAME)s_SEEN */ -""" % {'DEFNAME' : defname}) - t.close() - -def _stringConfigH(target, source, env): - return "scons: Configure: creating " + str(target[0]) - -def CreateConfigHBuilder(env): - """Called just before the building targets phase begins.""" - if len(_ac_config_hs) == 0: - return - action = SCons.Action.Action(_createConfigH, - _stringConfigH) - sconfigHBld = SCons.Builder.Builder(action=action) - env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) - for k in _ac_config_hs.keys(): - env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) - -class SConfWarning(SCons.Warnings.Warning): - pass -SCons.Warnings.enableWarningClass(SConfWarning) - -# some error definitions -class SConfError(SCons.Errors.UserError): - def __init__(self,msg): - SCons.Errors.UserError.__init__(self,msg) - -class ConfigureDryRunError(SConfError): - """Raised when a file or directory needs to be updated during a Configure - process, but the user requested a dry-run""" - def __init__(self,target): - if not isinstance(target, SCons.Node.FS.File): - msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target) - else: - msg = 'Cannot update configure test "%s" within a dry-run.' % str(target) - SConfError.__init__(self,msg) - -class ConfigureCacheError(SConfError): - """Raised when a use explicitely requested the cache feature, but the test - is run the first time.""" - def __init__(self,target): - SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target)) - -# define actions for building text files -def _createSource( target, source, env ): - fd = open(str(target[0]), "w") - fd.write(source[0].get_contents()) - fd.close() -def _stringSource( target, source, env ): - return (str(target[0]) + ' <-\n |' + - string.replace( source[0].get_contents(), - '\n', "\n |" ) ) - -# python 2.2 introduces types.BooleanType -BooleanTypes = [types.IntType] -if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType) - -class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): - """ - Special build info for targets of configure tests. Additional members - are result (did the builder succeed last time?) and string, which - contains messages of the original build phase. - """ - result = None # -> 0/None -> no error, != 0 error - string = None # the stdout / stderr output when building the target - - def set_build_result(self, result, string): - self.result = result - self.string = string - - -class Streamer: - """ - 'Sniffer' for a file-like writable object. Similar to the unix tool tee. - """ - def __init__(self, orig): - self.orig = orig - self.s = StringIO.StringIO() - - def write(self, str): - if self.orig: - self.orig.write(str) - self.s.write(str) - - def writelines(self, lines): - for l in lines: - self.write(l + '\n') - - def getvalue(self): - """ - Return everything written to orig since the Streamer was created. - """ - return self.s.getvalue() - - def flush(self): - if self.orig: - self.orig.flush() - self.s.flush() - - -class SConfBuildTask(SCons.Taskmaster.Task): - """ - This is almost the same as SCons.Script.BuildTask. Handles SConfErrors - correctly and knows about the current cache_mode. - """ - def display(self, message): - if sconf_global.logstream: - sconf_global.logstream.write("scons: Configure: " + message + "\n") - - def display_cached_string(self, bi): - """ - Logs the original builder messages, given the SConfBuildInfo instance - bi. - """ - if not isinstance(bi, SConfBuildInfo): - SCons.Warnings.warn(SConfWarning, - "The stored build information has an unexpected class: %s" % bi.__class__) - else: - self.display("The original builder output was:\n" + - string.replace(" |" + str(bi.string), - "\n", "\n |")) - - def failed(self): - # check, if the reason was a ConfigureDryRunError or a - # ConfigureCacheError and if yes, reraise the exception - exc_type = self.exc_info()[0] - if issubclass(exc_type, SConfError): - raise - elif issubclass(exc_type, SCons.Errors.BuildError): - # we ignore Build Errors (occurs, when a test doesn't pass) - # Clear the exception to prevent the contained traceback - # to build a reference cycle. - self.exc_clear() - else: - self.display('Caught exception while building "%s":\n' % - self.targets[0]) - try: - excepthook = sys.excepthook - except AttributeError: - # Earlier versions of Python don't have sys.excepthook... - def excepthook(type, value, tb): - traceback.print_tb(tb) - print type, value - apply(excepthook, self.exc_info()) - return SCons.Taskmaster.Task.failed(self) - - def collect_node_states(self): - # returns (is_up_to_date, cached_error, cachable) - # where is_up_to_date is 1, if the node(s) are up_to_date - # cached_error is 1, if the node(s) are up_to_date, but the - # build will fail - # cachable is 0, if some nodes are not in our cache - T = 0 - changed = False - cached_error = False - cachable = True - for t in self.targets: - if T: Trace('%s' % (t)) - bi = t.get_stored_info().binfo - if isinstance(bi, SConfBuildInfo): - if T: Trace(': SConfBuildInfo') - if cache_mode == CACHE: - t.set_state(SCons.Node.up_to_date) - if T: Trace(': set_state(up_to-date)') - else: - if T: Trace(': get_state() %s' % t.get_state()) - if T: Trace(': changed() %s' % t.changed()) - if (t.get_state() != SCons.Node.up_to_date and t.changed()): - changed = True - if T: Trace(': changed %s' % changed) - cached_error = cached_error or bi.result - else: - if T: Trace(': else') - # the node hasn't been built in a SConf context or doesn't - # exist - cachable = False - changed = ( t.get_state() != SCons.Node.up_to_date ) - if T: Trace(': changed %s' % changed) - if T: Trace('\n') - return (not changed, cached_error, cachable) - - def execute(self): - if not self.targets[0].has_builder(): - return - - sconf = sconf_global - - is_up_to_date, cached_error, cachable = self.collect_node_states() - - if cache_mode == CACHE and not cachable: - raise ConfigureCacheError(self.targets[0]) - elif cache_mode == FORCE: - is_up_to_date = 0 - - if cached_error and is_up_to_date: - self.display("Building \"%s\" failed in a previous run and all " - "its sources are up to date." % str(self.targets[0])) - binfo = self.targets[0].get_stored_info().binfo - self.display_cached_string(binfo) - raise SCons.Errors.BuildError # will be 'caught' in self.failed - elif is_up_to_date: - self.display("\"%s\" is up to date." % str(self.targets[0])) - binfo = self.targets[0].get_stored_info().binfo - self.display_cached_string(binfo) - elif dryrun: - raise ConfigureDryRunError(self.targets[0]) - else: - # note stdout and stderr are the same here - s = sys.stdout = sys.stderr = Streamer(sys.stdout) - try: - env = self.targets[0].get_build_env() - env['PSTDOUT'] = env['PSTDERR'] = s - try: - sconf.cached = 0 - self.targets[0].build() - finally: - sys.stdout = sys.stderr = env['PSTDOUT'] = \ - env['PSTDERR'] = sconf.logstream - except KeyboardInterrupt: - raise - except SystemExit: - exc_value = sys.exc_info()[1] - raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) - except Exception, e: - for t in self.targets: - binfo = t.get_binfo() - binfo.__class__ = SConfBuildInfo - binfo.set_build_result(1, s.getvalue()) - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = binfo - #sconsign_entry.ninfo = self.get_ninfo() - # We'd like to do this as follows: - # t.store_info(binfo) - # However, we need to store it as an SConfBuildInfo - # object, and store_info() will turn it into a - # regular FileNodeInfo if the target is itself a - # regular File. - sconsign = t.dir.sconsign() - sconsign.set_entry(t.name, sconsign_entry) - sconsign.merge() - raise e - else: - for t in self.targets: - binfo = t.get_binfo() - binfo.__class__ = SConfBuildInfo - binfo.set_build_result(0, s.getvalue()) - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = binfo - #sconsign_entry.ninfo = self.get_ninfo() - # We'd like to do this as follows: - # t.store_info(binfo) - # However, we need to store it as an SConfBuildInfo - # object, and store_info() will turn it into a - # regular FileNodeInfo if the target is itself a - # regular File. - sconsign = t.dir.sconsign() - sconsign.set_entry(t.name, sconsign_entry) - sconsign.merge() - -class SConfBase: - """This is simply a class to represent a configure context. After - creating a SConf object, you can call any tests. After finished with your - tests, be sure to call the Finish() method, which returns the modified - environment. - Some words about caching: In most cases, it is not necessary to cache - Test results explicitely. Instead, we use the scons dependency checking - mechanism. For example, if one wants to compile a test program - (SConf.TryLink), the compiler is only called, if the program dependencies - have changed. However, if the program could not be compiled in a former - SConf run, we need to explicitely cache this error. - """ - - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', - log_file='$CONFIGURELOG', config_h = None, _depth = 0): - """Constructor. Pass additional tests in the custom_tests-dictinary, - e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest - defines a custom test. - Note also the conf_dir and log_file arguments (you may want to - build tests in the VariantDir, not in the SourceDir) - """ - global SConfFS - if not SConfFS: - SConfFS = SCons.Node.FS.default_fs or \ - SCons.Node.FS.FS(env.fs.pathTop) - if not sconf_global is None: - raise (SCons.Errors.UserError, - "Only one SConf object may be active at one time") - self.env = env - if log_file != None: - log_file = SConfFS.File(env.subst(log_file)) - self.logfile = log_file - self.logstream = None - self.lastTarget = None - self.depth = _depth - self.cached = 0 # will be set, if all test results are cached - - # add default tests - default_tests = { - 'CheckCC' : CheckCC, - 'CheckCXX' : CheckCXX, - 'CheckSHCC' : CheckSHCC, - 'CheckSHCXX' : CheckSHCXX, - 'CheckFunc' : CheckFunc, - 'CheckType' : CheckType, - 'CheckTypeSize' : CheckTypeSize, - 'CheckDeclaration' : CheckDeclaration, - 'CheckHeader' : CheckHeader, - 'CheckCHeader' : CheckCHeader, - 'CheckCXXHeader' : CheckCXXHeader, - 'CheckLib' : CheckLib, - 'CheckLibWithHeader' : CheckLibWithHeader, - } - self.AddTests(default_tests) - self.AddTests(custom_tests) - self.confdir = SConfFS.Dir(env.subst(conf_dir)) - if not config_h is None: - config_h = SConfFS.File(config_h) - self.config_h = config_h - self._startup() - - def Finish(self): - """Call this method after finished with your tests: - env = sconf.Finish() - """ - self._shutdown() - return self.env - - def Define(self, name, value = None, comment = None): - """ - Define a pre processor symbol name, with the optional given value in the - current config header. - - If value is None (default), then #define name is written. If value is not - none, then #define name value is written. - - comment is a string which will be put as a C comment in the - header, to explain the meaning of the value (appropriate C comments /* and - */ will be put automatically.""" - lines = [] - if comment: - comment_str = "/* %s */" % comment - lines.append(comment_str) - - if value is not None: - define_str = "#define %s %s" % (name, value) - else: - define_str = "#define %s" % name - lines.append(define_str) - lines.append('') - - self.config_h_text = self.config_h_text + string.join(lines, '\n') - - def BuildNodes(self, nodes): - """ - Tries to build the given nodes immediately. Returns 1 on success, - 0 on error. - """ - if self.logstream != None: - # override stdout / stderr to write in log file - oldStdout = sys.stdout - sys.stdout = self.logstream - oldStderr = sys.stderr - sys.stderr = self.logstream - - # the engine assumes the current path is the SConstruct directory ... - old_fs_dir = SConfFS.getcwd() - old_os_dir = os.getcwd() - SConfFS.chdir(SConfFS.Top, change_os_dir=1) - - # Because we take responsibility here for writing out our - # own .sconsign info (see SConfBuildTask.execute(), above), - # we override the store_info() method with a null place-holder - # so we really control how it gets written. - for n in nodes: - n.store_info = n.do_not_store_info - - ret = 1 - - try: - # ToDo: use user options for calc - save_max_drift = SConfFS.get_max_drift() - SConfFS.set_max_drift(0) - tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) - # we don't want to build tests in parallel - jobs = SCons.Job.Jobs(1, tm ) - jobs.run() - for n in nodes: - state = n.get_state() - if (state != SCons.Node.executed and - state != SCons.Node.up_to_date): - # the node could not be built. we return 0 in this case - ret = 0 - finally: - SConfFS.set_max_drift(save_max_drift) - os.chdir(old_os_dir) - SConfFS.chdir(old_fs_dir, change_os_dir=0) - if self.logstream != None: - # restore stdout / stderr - sys.stdout = oldStdout - sys.stderr = oldStderr - return ret - - def pspawn_wrapper(self, sh, escape, cmd, args, env): - """Wrapper function for handling piped spawns. - - This looks to the calling interface (in Action.py) like a "normal" - spawn, but associates the call with the PSPAWN variable from - the construction environment and with the streams to which we - want the output logged. This gets slid into the construction - environment as the SPAWN variable so Action.py doesn't have to - know or care whether it's spawning a piped command or not. - """ - return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream) - - - def TryBuild(self, builder, text = None, extension = ""): - """Low level TryBuild implementation. Normally you don't need to - call that - you can use TryCompile / TryLink / TryRun instead - """ - global _ac_build_counter - - # Make sure we have a PSPAWN value, and save the current - # SPAWN value. - try: - self.pspawn = self.env['PSPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing PSPAWN construction variable.') - try: - save_spawn = self.env['SPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing SPAWN construction variable.') - - nodesToBeBuilt = [] - - f = "conftest_" + str(_ac_build_counter) - pref = self.env.subst( builder.builder.prefix ) - suff = self.env.subst( builder.builder.suffix ) - target = self.confdir.File(pref + f + suff) - - try: - # Slide our wrapper into the construction environment as - # the SPAWN function. - self.env['SPAWN'] = self.pspawn_wrapper - sourcetext = self.env.Value(text) - - if text != None: - textFile = self.confdir.File(f + extension) - textFileNode = self.env.SConfSourceBuilder(target=textFile, - source=sourcetext) - nodesToBeBuilt.extend(textFileNode) - source = textFileNode - else: - source = None - - nodes = builder(target = target, source = source) - if not SCons.Util.is_List(nodes): - nodes = [nodes] - nodesToBeBuilt.extend(nodes) - result = self.BuildNodes(nodesToBeBuilt) - - finally: - self.env['SPAWN'] = save_spawn - - _ac_build_counter = _ac_build_counter + 1 - if result: - self.lastTarget = nodes[0] - else: - self.lastTarget = None - - return result - - def TryAction(self, action, text = None, extension = ""): - """Tries to execute the given action with optional source file - contents <text> and optional source file extension <extension>, - Returns the status (0 : failed, 1 : ok) and the contents of the - output file. - """ - builder = SCons.Builder.Builder(action=action) - self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) - ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) - del self.env['BUILDERS']['SConfActionBuilder'] - if ok: - outputStr = self.lastTarget.get_contents() - return (1, outputStr) - return (0, "") - - def TryCompile( self, text, extension): - """Compiles the program given in text to an env.Object, using extension - as file extension (e.g. '.c'). Returns 1, if compilation was - successful, 0 otherwise. The target is saved in self.lastTarget (for - further processing). - """ - return self.TryBuild(self.env.Object, text, extension) - - def TryLink( self, text, extension ): - """Compiles the program given in text to an executable env.Program, - using extension as file extension (e.g. '.c'). Returns 1, if - compilation was successful, 0 otherwise. The target is saved in - self.lastTarget (for further processing). - """ - return self.TryBuild(self.env.Program, text, extension ) - - def TryRun(self, text, extension ): - """Compiles and runs the program given in text, using extension - as file extension (e.g. '.c'). Returns (1, outputStr) on success, - (0, '') otherwise. The target (a file containing the program's stdout) - is saved in self.lastTarget (for further processing). - """ - ok = self.TryLink(text, extension) - if( ok ): - prog = self.lastTarget - pname = str(prog) - output = SConfFS.File(pname+'.out') - node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) - ok = self.BuildNodes(node) - if ok: - outputStr = output.get_contents() - return( 1, outputStr) - return (0, "") - - class TestWrapper: - """A wrapper around Tests (to ensure sanity)""" - def __init__(self, test, sconf): - self.test = test - self.sconf = sconf - def __call__(self, *args, **kw): - if not self.sconf.active: - raise (SCons.Errors.UserError, - "Test called after sconf.Finish()") - context = CheckContext(self.sconf) - ret = apply(self.test, (context,) + args, kw) - if not self.sconf.config_h is None: - self.sconf.config_h_text = self.sconf.config_h_text + context.config_h - context.Result("error: no result") - return ret - - def AddTest(self, test_name, test_instance): - """Adds test_class to this SConf instance. It can be called with - self.test_name(...)""" - setattr(self, test_name, SConfBase.TestWrapper(test_instance, self)) - - def AddTests(self, tests): - """Adds all the tests given in the tests dictionary to this SConf - instance - """ - for name in tests.keys(): - self.AddTest(name, tests[name]) - - def _createDir( self, node ): - dirName = str(node) - if dryrun: - if not os.path.isdir( dirName ): - raise ConfigureDryRunError(dirName) - else: - if not os.path.isdir( dirName ): - os.makedirs( dirName ) - node._exists = 1 - - def _startup(self): - """Private method. Set up logstream, and set the environment - variables necessary for a piped build - """ - global _ac_config_logs - global sconf_global - global SConfFS - - self.lastEnvFs = self.env.fs - self.env.fs = SConfFS - self._createDir(self.confdir) - self.confdir.up().add_ignore( [self.confdir] ) - - if self.logfile != None and not dryrun: - # truncate logfile, if SConf.Configure is called for the first time - # in a build - if _ac_config_logs.has_key(self.logfile): - log_mode = "a" - else: - _ac_config_logs[self.logfile] = None - log_mode = "w" - fp = open(str(self.logfile), log_mode) - self.logstream = SCons.Util.Unbuffered(fp) - # logfile may stay in a build directory, so we tell - # the build system not to override it with a eventually - # existing file with the same name in the source directory - self.logfile.dir.add_ignore( [self.logfile] ) - - tb = traceback.extract_stack()[-3-self.depth] - old_fs_dir = SConfFS.getcwd() - SConfFS.chdir(SConfFS.Top, change_os_dir=0) - self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % - (tb[0], tb[1], str(self.confdir)) ) - SConfFS.chdir(old_fs_dir) - else: - self.logstream = None - # we use a special builder to create source files from TEXT - action = SCons.Action.Action(_createSource, - _stringSource) - sconfSrcBld = SCons.Builder.Builder(action=action) - self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) - self.config_h_text = _ac_config_hs.get(self.config_h, "") - self.active = 1 - # only one SConf instance should be active at a time ... - sconf_global = self - - def _shutdown(self): - """Private method. Reset to non-piped spawn""" - global sconf_global, _ac_config_hs - - if not self.active: - raise SCons.Errors.UserError, "Finish may be called only once!" - if self.logstream != None and not dryrun: - self.logstream.write("\n") - self.logstream.close() - self.logstream = None - # remove the SConfSourceBuilder from the environment - blds = self.env['BUILDERS'] - del blds['SConfSourceBuilder'] - self.env.Replace( BUILDERS=blds ) - self.active = 0 - sconf_global = None - if not self.config_h is None: - _ac_config_hs[self.config_h] = self.config_h_text - self.env.fs = self.lastEnvFs - -class CheckContext: - """Provides a context for configure tests. Defines how a test writes to the - screen and log file. - - A typical test is just a callable with an instance of CheckContext as - first argument: - - def CheckCustom(context, ...) - context.Message('Checking my weird test ... ') - ret = myWeirdTestFunction(...) - context.Result(ret) - - Often, myWeirdTestFunction will be one of - context.TryCompile/context.TryLink/context.TryRun. The results of - those are cached, for they are only rebuild, if the dependencies have - changed. - """ - - def __init__(self, sconf): - """Constructor. Pass the corresponding SConf instance.""" - self.sconf = sconf - self.did_show_result = 0 - - # for Conftest.py: - self.vardict = {} - self.havedict = {} - self.headerfilename = None - self.config_h = "" # config_h text will be stored here - # we don't regenerate the config.h file after each test. That means, - # that tests won't be able to include the config.h file, and so - # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major - # issue, though. If it turns out, that we need to include config.h - # in tests, we must ensure, that the dependencies are worked out - # correctly. Note that we can't use Conftest.py's support for config.h, - # cause we will need to specify a builder for the config.h file ... - - def Message(self, text): - """Inform about what we are doing right now, e.g. - 'Checking for SOMETHING ... ' - """ - self.Display(text) - self.sconf.cached = 1 - self.did_show_result = 0 - - def Result(self, res): - """Inform about the result of the test. res may be an integer or a - string. In case of an integer, the written text will be 'ok' or - 'failed'. - The result is only displayed when self.did_show_result is not set. - """ - if type(res) in BooleanTypes: - if res: - text = "yes" - else: - text = "no" - elif type(res) == types.StringType: - text = res - else: - raise TypeError, "Expected string, int or bool, got " + str(type(res)) - - if self.did_show_result == 0: - # Didn't show result yet, do it now. - self.Display(text + "\n") - self.did_show_result = 1 - - def TryBuild(self, *args, **kw): - return apply(self.sconf.TryBuild, args, kw) - - def TryAction(self, *args, **kw): - return apply(self.sconf.TryAction, args, kw) - - def TryCompile(self, *args, **kw): - return apply(self.sconf.TryCompile, args, kw) - - def TryLink(self, *args, **kw): - return apply(self.sconf.TryLink, args, kw) - - def TryRun(self, *args, **kw): - return apply(self.sconf.TryRun, args, kw) - - def __getattr__( self, attr ): - if( attr == 'env' ): - return self.sconf.env - elif( attr == 'lastTarget' ): - return self.sconf.lastTarget - else: - raise AttributeError, "CheckContext instance has no attribute '%s'" % attr - - #### Stuff used by Conftest.py (look there for explanations). - - def BuildProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.Program, text, ext) - - def CompileProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.Object, text, ext) - - def CompileSharedObject(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.SharedObject, text, ext) - - def RunProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - st, out = self.TryRun(text, ext) - return not st, out - - def AppendLIBS(self, lib_name_list): - oldLIBS = self.env.get( 'LIBS', [] ) - self.env.Append(LIBS = lib_name_list) - return oldLIBS - - def SetLIBS(self, val): - oldLIBS = self.env.get( 'LIBS', [] ) - self.env.Replace(LIBS = val) - return oldLIBS - - def Display(self, msg): - if self.sconf.cached: - # We assume that Display is called twice for each test here - # once for the Checking for ... message and once for the result. - # The self.sconf.cached flag can only be set between those calls - msg = "(cached) " + msg - self.sconf.cached = 0 - progress_display(msg, append_newline=0) - self.Log("scons: Configure: " + msg + "\n") - - def Log(self, msg): - if self.sconf.logstream != None: - self.sconf.logstream.write(msg) - - #### End of stuff used by Conftest.py. - - -def SConf(*args, **kw): - if kw.get(build_type, True): - kw['_depth'] = kw.get('_depth', 0) + 1 - for bt in build_types: - try: - del kw[bt] - except KeyError: - pass - return apply(SConfBase, args, kw) - else: - return SCons.Util.Null() - - -def CheckFunc(context, function_name, header = None, language = None): - res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) - context.did_show_result = 1 - return not res - -def CheckType(context, type_name, includes = "", language = None): - res = SCons.Conftest.CheckType(context, type_name, - header = includes, language = language) - context.did_show_result = 1 - return not res - -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): - res = SCons.Conftest.CheckTypeSize(context, type_name, - header = includes, language = language, - expect = expect) - context.did_show_result = 1 - return res - -def CheckDeclaration(context, declaration, includes = "", language = None): - res = SCons.Conftest.CheckDeclaration(context, declaration, - includes = includes, - language = language) - context.did_show_result = 1 - return not res - -def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): - # used by CheckHeader and CheckLibWithHeader to produce C - #include - # statements from the specified header (list) - if not SCons.Util.is_List(headers): - headers = [headers] - l = [] - if leaveLast: - lastHeader = headers[-1] - headers = headers[:-1] - else: - lastHeader = None - for s in headers: - l.append("#include %s%s%s\n" - % (include_quotes[0], s, include_quotes[1])) - return string.join(l, ''), lastHeader - -def CheckHeader(context, header, include_quotes = '<>', language = None): - """ - A test for a C or C++ header file. - """ - prog_prefix, hdr_to_check = \ - createIncludesFromHeaders(header, 1, include_quotes) - res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, - language = language, - include_quotes = include_quotes) - context.did_show_result = 1 - return not res - -def CheckCC(context): - res = SCons.Conftest.CheckCC(context) - return not res - -def CheckCXX(context): - res = SCons.Conftest.CheckCXX(context) - return not res - -def CheckSHCC(context): - res = SCons.Conftest.CheckSHCC(context) - return not res - -def CheckSHCXX(context): - res = SCons.Conftest.CheckSHCXX(context) - return not res - -# Bram: Make this function obsolete? CheckHeader() is more generic. - -def CheckCHeader(context, header, include_quotes = '""'): - """ - A test for a C header file. - """ - return CheckHeader(context, header, include_quotes, language = "C") - - -# Bram: Make this function obsolete? CheckHeader() is more generic. - -def CheckCXXHeader(context, header, include_quotes = '""'): - """ - A test for a C++ header file. - """ - return CheckHeader(context, header, include_quotes, language = "C++") - - -def CheckLib(context, library = None, symbol = "main", - header = None, language = None, autoadd = 1): - """ - A test for a library. See also CheckLibWithHeader. - Note that library may also be None to test whether the given symbol - compiles without flags. - """ - - if library == []: - library = [None] - - if not SCons.Util.is_List(library): - library = [library] - - # ToDo: accept path for the library - res = SCons.Conftest.CheckLib(context, library, symbol, header = header, - language = language, autoadd = autoadd) - context.did_show_result = 1 - return not res - -# XXX -# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. - -def CheckLibWithHeader(context, libs, header, language, - call = None, autoadd = 1): - # ToDo: accept path for library. Support system header files. - """ - Another (more sophisticated) test for a library. - Checks, if library and header is available for language (may be 'C' - or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. - As in CheckLib, we support library=None, to test if the call compiles - without extra link flags. - """ - prog_prefix, dummy = \ - createIncludesFromHeaders(header, 0) - if libs == []: - libs = [None] - - if not SCons.Util.is_List(libs): - libs = [libs] - - res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, - call = call, language = language, autoadd = autoadd) - context.did_show_result = 1 - return not res diff --git a/tools/scons/scons-local-1.2.0/SCons/SConsign.py b/tools/scons/scons-local-1.2.0/SCons/SConsign.py deleted file mode 100644 index 8e4c30c145..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/SConsign.py +++ /dev/null @@ -1,375 +0,0 @@ -"""SCons.SConsign - -Writing and reading information to the .sconsign file or files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/SConsign.py 3842 2008/12/20 22:59:52 scons" - -import cPickle -import os -import os.path - -import SCons.dblite -import SCons.Warnings - -def corrupt_dblite_warning(filename): - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt .sconsign file: %s"%filename) - -SCons.dblite.ignore_corrupt_dbfiles = 1 -SCons.dblite.corruption_warning = corrupt_dblite_warning - -#XXX Get rid of the global array so this becomes re-entrant. -sig_files = [] - -# Info for the database SConsign implementation (now the default): -# "DataBase" is a dictionary that maps top-level SConstruct directories -# to open database handles. -# "DB_Module" is the Python database module to create the handles. -# "DB_Name" is the base name of the database file (minus any -# extension the underlying DB module will add). -DataBase = {} -DB_Module = SCons.dblite -DB_Name = ".sconsign" -DB_sync_list = [] - -def Get_DataBase(dir): - global DataBase, DB_Module, DB_Name - top = dir.fs.Top - if not os.path.isabs(DB_Name) and top.repositories: - mode = "c" - for d in [top] + top.repositories: - if dir.is_under(d): - try: - return DataBase[d], mode - except KeyError: - path = d.entry_abspath(DB_Name) - try: db = DataBase[d] = DB_Module.open(path, mode) - except (IOError, OSError): pass - else: - if mode != "r": - DB_sync_list.append(db) - return db, mode - mode = "r" - try: - return DataBase[top], "c" - except KeyError: - db = DataBase[top] = DB_Module.open(DB_Name, "c") - DB_sync_list.append(db) - return db, "c" - except TypeError: - print "DataBase =", DataBase - raise - -def Reset(): - """Reset global state. Used by unit tests that end up using - SConsign multiple times to get a clean slate for each test.""" - global sig_files, DB_sync_list - sig_files = [] - DB_sync_list = [] - -normcase = os.path.normcase - -def write(): - global sig_files - for sig_file in sig_files: - sig_file.write(sync=0) - for db in DB_sync_list: - try: - syncmethod = db.sync - except AttributeError: - pass # Not all anydbm modules have sync() methods. - else: - syncmethod() - -class SConsignEntry: - """ - Wrapper class for the generic entry in a .sconsign file. - The Node subclass populates it with attributes as it pleases. - - XXX As coded below, we do expect a '.binfo' attribute to be added, - but we'll probably generalize this in the next refactorings. - """ - current_version_id = 1 - def __init__(self): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - _version_id = self.current_version_id - def convert_to_sconsign(self): - self.binfo.convert_to_sconsign() - def convert_from_sconsign(self, dir, name): - self.binfo.convert_from_sconsign(dir, name) - -class Base: - """ - This is the controlling class for the signatures for the collection of - entries associated with a specific directory. The actual directory - association will be maintained by a subclass that is specific to - the underlying storage method. This class provides a common set of - methods for fetching and storing the individual bits of information - that make up signature entry. - """ - def __init__(self): - self.entries = {} - self.dirty = False - self.to_be_merged = {} - - def get_entry(self, filename): - """ - Fetch the specified entry attribute. - """ - return self.entries[filename] - - def set_entry(self, filename, obj): - """ - Set the entry. - """ - self.entries[filename] = obj - self.dirty = True - - def do_not_set_entry(self, filename, obj): - pass - - def store_info(self, filename, node): - entry = node.get_stored_info() - entry.binfo.merge(node.get_binfo()) - self.to_be_merged[filename] = node - self.dirty = True - - def do_not_store_info(self, filename, node): - pass - - def merge(self): - for key, node in self.to_be_merged.items(): - entry = node.get_stored_info() - try: - ninfo = entry.ninfo - except AttributeError: - # This happens with SConf Nodes, because the configuration - # subsystem takes direct control over how the build decision - # is made and its information stored. - pass - else: - ninfo.merge(node.get_ninfo()) - self.entries[key] = entry - self.to_be_merged = {} - -class DB(Base): - """ - A Base subclass that reads and writes signature information - from a global .sconsign.db* file--the actual file suffix is - determined by the database module. - """ - def __init__(self, dir): - Base.__init__(self) - - self.dir = dir - - db, mode = Get_DataBase(dir) - - # Read using the path relative to the top of the Repository - # (self.dir.tpath) from which we're fetching the signature - # information. - path = normcase(dir.tpath) - try: - rawentries = db[path] - except KeyError: - pass - else: - try: - self.entries = cPickle.loads(rawentries) - if type(self.entries) is not type({}): - self.entries = {} - raise TypeError - except KeyboardInterrupt: - raise - except Exception, e: - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) - for key, entry in self.entries.items(): - entry.convert_from_sconsign(dir, key) - - if mode == "r": - # This directory is actually under a repository, which means - # likely they're reaching in directly for a dependency on - # a file there. Don't actually set any entry info, so we - # won't try to write to that .sconsign.dblite file. - self.set_entry = self.do_not_set_entry - self.store_info = self.do_not_store_info - - global sig_files - sig_files.append(self) - - def write(self, sync=1): - if not self.dirty: - return - - self.merge() - - db, mode = Get_DataBase(self.dir) - - # Write using the path relative to the top of the SConstruct - # directory (self.dir.path), not relative to the top of - # the Repository; we only write to our own .sconsign file, - # not to .sconsign files in Repositories. - path = normcase(self.dir.path) - for key, entry in self.entries.items(): - entry.convert_to_sconsign() - db[path] = cPickle.dumps(self.entries, 1) - - if sync: - try: - syncmethod = db.sync - except AttributeError: - # Not all anydbm modules have sync() methods. - pass - else: - syncmethod() - -class Dir(Base): - def __init__(self, fp=None, dir=None): - """ - fp - file pointer to read entries from - """ - Base.__init__(self) - - if not fp: - return - - self.entries = cPickle.load(fp) - if type(self.entries) is not type({}): - self.entries = {} - raise TypeError - - if dir: - for key, entry in self.entries.items(): - entry.convert_from_sconsign(dir, key) - -class DirFile(Dir): - """ - Encapsulates reading and writing a per-directory .sconsign file. - """ - def __init__(self, dir): - """ - dir - the directory for the file - """ - - self.dir = dir - self.sconsign = os.path.join(dir.path, '.sconsign') - - try: - fp = open(self.sconsign, 'rb') - except IOError: - fp = None - - try: - Dir.__init__(self, fp, dir) - except KeyboardInterrupt: - raise - except: - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt .sconsign file: %s"%self.sconsign) - - global sig_files - sig_files.append(self) - - def write(self, sync=1): - """ - Write the .sconsign file to disk. - - Try to write to a temporary file first, and rename it if we - succeed. If we can't write to the temporary file, it's - probably because the directory isn't writable (and if so, - how did we build anything in this directory, anyway?), so - try to write directly to the .sconsign file as a backup. - If we can't rename, try to copy the temporary contents back - to the .sconsign file. Either way, always try to remove - the temporary file at the end. - """ - if not self.dirty: - return - - self.merge() - - temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) - try: - file = open(temp, 'wb') - fname = temp - except IOError: - try: - file = open(self.sconsign, 'wb') - fname = self.sconsign - except IOError: - return - for key, entry in self.entries.items(): - entry.convert_to_sconsign() - cPickle.dump(self.entries, file, 1) - file.close() - if fname != self.sconsign: - try: - mode = os.stat(self.sconsign)[0] - os.chmod(self.sconsign, 0666) - os.unlink(self.sconsign) - except (IOError, OSError): - # Try to carry on in the face of either OSError - # (things like permission issues) or IOError (disk - # or network issues). If there's a really dangerous - # issue, it should get re-raised by the calls below. - pass - try: - os.rename(fname, self.sconsign) - except OSError: - # An OSError failure to rename may indicate something - # like the directory has no write permission, but - # the .sconsign file itself might still be writable, - # so try writing on top of it directly. An IOError - # here, or in any of the following calls, would get - # raised, indicating something like a potentially - # serious disk or network issue. - open(self.sconsign, 'wb').write(open(fname, 'rb').read()) - os.chmod(self.sconsign, mode) - try: - os.unlink(temp) - except (IOError, OSError): - pass - -ForDirectory = DB - -def File(name, dbm_module=None): - """ - Arrange for all signatures to be stored in a global .sconsign.db* - file. - """ - global ForDirectory, DB_Name, DB_Module - if name is None: - ForDirectory = DirFile - DB_Module = None - else: - ForDirectory = DB - DB_Name = name - if not dbm_module is None: - DB_Module = dbm_module diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/C.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/C.py deleted file mode 100644 index 926493e767..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/C.py +++ /dev/null @@ -1,126 +0,0 @@ -"""SCons.Scanner.C - -This module implements the depenency scanner for C/C++ code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/C.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Node.FS -import SCons.Scanner -import SCons.Util - -import SCons.cpp - -class SConsCPPScanner(SCons.cpp.PreProcessor): - """ - SCons-specific subclass of the cpp.py module's processing. - - We subclass this so that: 1) we can deal with files represented - by Nodes, not strings; 2) we can keep track of the files that are - missing. - """ - def __init__(self, *args, **kw): - apply(SCons.cpp.PreProcessor.__init__, (self,)+args, kw) - self.missing = [] - def initialize_result(self, fname): - self.result = SCons.Util.UniqueList([fname]) - def finalize_result(self, fname): - return self.result[1:] - def find_include_file(self, t): - keyword, quote, fname = t - result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) - if not result: - self.missing.append((fname, self.current_file)) - return result - def read_file(self, file): - try: - fp = open(str(file.rfile())) - except EnvironmentError, e: - self.missing.append((file, self.current_file)) - return '' - else: - return fp.read() - -def dictify_CPPDEFINES(env): - cppdefines = env.get('CPPDEFINES', {}) - if cppdefines is None: - return {} - if SCons.Util.is_Sequence(cppdefines): - result = {} - for c in cppdefines: - if SCons.Util.is_Sequence(c): - result[c[0]] = c[1] - else: - result[c] = None - return result - if not SCons.Util.is_Dict(cppdefines): - return {cppdefines : None} - return cppdefines - -class SConsCPPScannerWrapper: - """ - The SCons wrapper around a cpp.py scanner. - - This is the actual glue between the calling conventions of generic - SCons scanners, and the (subclass of) cpp.py class that knows how - to look for #include lines with reasonably real C-preprocessor-like - evaluation of #if/#ifdef/#else/#elif lines. - """ - def __init__(self, name, variable): - self.name = name - self.path = SCons.Scanner.FindPathDirs(variable) - def __call__(self, node, env, path = ()): - cpp = SConsCPPScanner(current = node.get_dir(), - cpppath = path, - dict = dictify_CPPDEFINES(env)) - result = cpp(node) - for included, includer in cpp.missing: - fmt = "No dependency generated for file: %s (included from: %s) -- file not found" - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - fmt % (included, includer)) - return result - - def recurse_nodes(self, nodes): - return nodes - def select(self, node): - return self - -def CScanner(): - """Return a prototype Scanner instance for scanning source files - that use the C pre-processor""" - - # Here's how we would (or might) use the CPP scanner code above that - # knows how to evaluate #if/#ifdef/#else/#elif lines when searching - # for #includes. This is commented out for now until we add the - # right configurability to let users pick between the scanners. - #return SConsCPPScannerWrapper("CScanner", "CPPPATH") - - cs = SCons.Scanner.ClassicCPP("CScanner", - "$CPPSUFFIXES", - "CPPPATH", - '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') - return cs diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/D.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/D.py deleted file mode 100644 index 97ece3a18a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/D.py +++ /dev/null @@ -1,68 +0,0 @@ -"""SCons.Scanner.D - -Scanner for the Digital Mars "D" programming language. - -Coded by Andy Friesen -17 Nov 2003 - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/D.py 3842 2008/12/20 22:59:52 scons" - -import re -import string - -import SCons.Scanner - -def DScanner(): - """Return a prototype Scanner instance for scanning D source files""" - ds = D() - return ds - -class D(SCons.Scanner.Classic): - def __init__ (self): - SCons.Scanner.Classic.__init__ (self, - name = "DScanner", - suffixes = '$DSUFFIXES', - path_variable = 'DPATH', - regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') - - self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) - - def find_include(self, include, source_dir, path): - # translate dots (package separators) to slashes - inc = string.replace(include, '.', '/') - - i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) - if i is None: - i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) - return i, include - - def find_include_names(self, node): - includes = [] - for i in self.cre.findall(node.get_contents()): - includes = includes + self.cre2.findall(i) - return includes diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/Dir.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/Dir.py deleted file mode 100644 index 35d500861c..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/Dir.py +++ /dev/null @@ -1,105 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/Dir.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Node.FS -import SCons.Scanner - -def only_dirs(nodes): - is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) - return filter(is_Dir, nodes) - -def DirScanner(**kw): - """Return a prototype Scanner instance for scanning - directories for on-disk files""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = only_dirs - return apply(SCons.Scanner.Base, (scan_on_disk, "DirScanner"), kw) - -def DirEntryScanner(**kw): - """Return a prototype Scanner instance for "scanning" - directory Nodes for their in-memory entries""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = None - return apply(SCons.Scanner.Base, (scan_in_memory, "DirEntryScanner"), kw) - -skip_entry = {} - -skip_entry_list = [ - '.', - '..', - '.sconsign', - # Used by the native dblite.py module. - '.sconsign.dblite', - # Used by dbm and dumbdbm. - '.sconsign.dir', - # Used by dbm. - '.sconsign.pag', - # Used by dumbdbm. - '.sconsign.dat', - '.sconsign.bak', - # Used by some dbm emulations using Berkeley DB. - '.sconsign.db', -] - -for skip in skip_entry_list: - skip_entry[skip] = 1 - skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 - -do_not_scan = lambda k: not skip_entry.has_key(k) - -def scan_on_disk(node, env, path=()): - """ - Scans a directory for on-disk files and directories therein. - - Looking up the entries will add these to the in-memory Node tree - representation of the file system, so all we have to do is just - that and then call the in-memory scanning function. - """ - try: - flist = node.fs.listdir(node.abspath) - except (IOError, OSError): - return [] - e = node.Entry - for f in filter(do_not_scan, flist): - # Add ./ to the beginning of the file name so if it begins with a - # '#' we don't look it up relative to the top-level directory. - e('./' + f) - return scan_in_memory(node, env, path) - -def scan_in_memory(node, env, path=()): - """ - "Scans" a Node.FS.Dir for its in-memory entries. - """ - try: - entries = node.entries - except AttributeError: - # It's not a Node.FS.Dir (or doesn't look enough like one for - # our purposes), which can happen if a target list containing - # mixed Node types (Dirs and Files, for example) has a Dir as - # the first entry. - return [] - entry_list = filter(do_not_scan, entries.keys()) - entry_list.sort() - return map(lambda n, e=entries: e[n], entry_list) diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/Fortran.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/Fortran.py deleted file mode 100644 index e629b80b0e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/Fortran.py +++ /dev/null @@ -1,314 +0,0 @@ -"""SCons.Scanner.Fortran - -This module implements the dependency scanner for Fortran code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/Fortran.py 3842 2008/12/20 22:59:52 scons" - -import re -import string - -import SCons.Node -import SCons.Node.FS -import SCons.Scanner -import SCons.Util -import SCons.Warnings - -class F90Scanner(SCons.Scanner.Classic): - """ - A Classic Scanner subclass for Fortran source files which takes - into account both USE and INCLUDE statements. This scanner will - work for both F77 and F90 (and beyond) compilers. - - Currently, this scanner assumes that the include files do not contain - USE statements. To enable the ability to deal with USE statements - in include files, add logic right after the module names are found - to loop over each include file, search for and locate each USE - statement, and append each module name to the list of dependencies. - Caching the search results in a common dictionary somewhere so that - the same include file is not searched multiple times would be a - smart thing to do. - """ - - def __init__(self, name, suffixes, path_variable, - use_regex, incl_regex, def_regex, *args, **kw): - - self.cre_use = re.compile(use_regex, re.M) - self.cre_incl = re.compile(incl_regex, re.M) - self.cre_def = re.compile(def_regex, re.M) - - def _scan(node, env, path, self=self): - node = node.rfile() - - if not node.exists(): - return [] - - return self.scan(node, env, path) - - kw['function'] = _scan - kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['name'] = name - - apply(SCons.Scanner.Current.__init__, (self,) + args, kw) - - def scan(self, node, env, path=()): - - # cache the includes list in node so we only scan it once: - if node.includes != None: - mods_and_includes = node.includes - else: - # retrieve all included filenames - includes = self.cre_incl.findall(node.get_contents()) - # retrieve all USE'd module names - modules = self.cre_use.findall(node.get_contents()) - # retrieve all defined module names - defmodules = self.cre_def.findall(node.get_contents()) - - # Remove all USE'd module names that are defined in the same file - d = {} - for m in defmodules: - d[m] = 1 - modules = filter(lambda m, d=d: not d.has_key(m), modules) - #modules = self.undefinedModules(modules, defmodules) - - # Convert module name to a .mod filename - suffix = env.subst('$FORTRANMODSUFFIX') - modules = map(lambda x, s=suffix: string.lower(x) + s, modules) - # Remove unique items from the list - mods_and_includes = SCons.Util.unique(includes+modules) - node.includes = mods_and_includes - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the USE or INCLUDE line, which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally. - nodes = [] - source_dir = node.get_dir() - if callable(path): - path = path() - for dep in mods_and_includes: - n, i = self.find_include(dep, source_dir, path) - - if n is None: - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) - else: - sortkey = self.sort_key(dep) - nodes.append((sortkey, n)) - - nodes.sort() - nodes = map(lambda pair: pair[1], nodes) - return nodes - -def FortranScan(path_variable="FORTRANPATH"): - """Return a prototype Scanner instance for scanning source files - for Fortran USE & INCLUDE statements""" - -# The USE statement regex matches the following: -# -# USE module_name -# USE :: module_name -# USE, INTRINSIC :: module_name -# USE, NON_INTRINSIC :: module_name -# -# Limitations -# -# -- While the regex can handle multiple USE statements on one line, -# it cannot properly handle them if they are commented out. -# In either of the following cases: -# -# ! USE mod_a ; USE mod_b [entire line is commented out] -# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] -# -# the second module name (mod_b) will be picked up as a dependency -# even though it should be ignored. The only way I can see -# to rectify this would be to modify the scanner to eliminate -# the call to re.findall, read in the contents of the file, -# treating the comment character as an end-of-line character -# in addition to the normal linefeed, loop over each line, -# weeding out the comments, and looking for the USE statements. -# One advantage to this is that the regex passed to the scanner -# would no longer need to match a semicolon. -# -# -- I question whether or not we need to detect dependencies to -# INTRINSIC modules because these are built-in to the compiler. -# If we consider them a dependency, will SCons look for them, not -# find them, and kill the build? Or will we there be standard -# compiler-specific directories we will need to point to so the -# compiler and SCons can locate the proper object and mod files? - -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# ^ : start of line -# (?: : group a collection of regex symbols without saving the match as a "group" -# ^|; : matches either the start of the line or a semicolon - semicolon -# ) : end the unsaved grouping -# \s* : any amount of white space -# USE : match the string USE, case insensitive -# (?: : group a collection of regex symbols without saving the match as a "group" -# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) -# (?: : group a collection of regex symbols without saving the match as a "group" -# (?: : establish another unsaved grouping of regex symbols -# \s* : any amount of white space -# , : match a comma -# \s* : any amount of white space -# (?:NON_)? : optionally match the prefix NON_, case insensitive -# INTRINSIC : match the string INTRINSIC, case insensitive -# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression -# \s* : any amount of white space -# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute -# ) : end the unsaved grouping -# ) : end the unsaved grouping -# \s* : match any amount of white space -# (\w+) : match the module name that is being USE'd -# -# - use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" - - -# The INCLUDE statement regex matches the following: -# -# INCLUDE 'some_Text' -# INCLUDE "some_Text" -# INCLUDE "some_Text" ; INCLUDE "some_Text" -# INCLUDE kind_"some_Text" -# INCLUDE kind_'some_Text" -# -# where some_Text can include any alphanumeric and/or special character -# as defined by the Fortran 2003 standard. -# -# Limitations: -# -# -- The Fortran standard dictates that a " or ' in the INCLUDE'd -# string must be represented as a "" or '', if the quotes that wrap -# the entire string are either a ' or ", respectively. While the -# regular expression below can detect the ' or " characters just fine, -# the scanning logic, presently is unable to detect them and reduce -# them to a single instance. This probably isn't an issue since, -# in practice, ' or " are not generally used in filenames. -# -# -- This regex will not properly deal with multiple INCLUDE statements -# when the entire line has been commented out, ala -# -# ! INCLUDE 'some_file' ; INCLUDE 'some_file' -# -# In such cases, it will properly ignore the first INCLUDE file, -# but will actually still pick up the second. Interestingly enough, -# the regex will properly deal with these cases: -# -# INCLUDE 'some_file' -# INCLUDE 'some_file' !; INCLUDE 'some_file' -# -# To get around the above limitation, the FORTRAN programmer could -# simply comment each INCLUDE statement separately, like this -# -# ! INCLUDE 'some_file' !; INCLUDE 'some_file' -# -# The way I see it, the only way to get around this limitation would -# be to modify the scanning logic to replace the calls to re.findall -# with a custom loop that processes each line separately, throwing -# away fully commented out lines before attempting to match against -# the INCLUDE syntax. -# -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# (?: : begin a non-saving group that matches the following: -# ^ : either the start of the line -# | : or -# ['">]\s*; : a semicolon that follows a single quote, -# double quote or greater than symbol (with any -# amount of whitespace in between). This will -# allow the regex to match multiple INCLUDE -# statements per line (although it also requires -# the positive lookahead assertion that is -# used below). It will even properly deal with -# (i.e. ignore) cases in which the additional -# INCLUDES are part of an in-line comment, ala -# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " -# ) : end of non-saving group -# \s* : any amount of white space -# INCLUDE : match the string INCLUDE, case insensitive -# \s+ : match one or more white space characters -# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard -# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol -# (.+?) : match one or more characters that make up -# the included path and file name and save it -# in a group. The Fortran standard allows for -# any non-control character to be used. The dot -# operator will pick up any character, including -# control codes, but I can't conceive of anyone -# putting control codes in their file names. -# The question mark indicates it is non-greedy so -# that regex will match only up to the next quote, -# double quote, or greater than symbol -# (?=["'>]) : positive lookahead assertion to match the include -# delimiter - an apostrophe, double quote, or -# greater than symbol. This level of complexity -# is required so that the include delimiter is -# not consumed by the match, thus allowing the -# sub-regex discussed above to uniquely match a -# set of semicolon-separated INCLUDE statements -# (as allowed by the F2003 standard) - - include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" - -# The MODULE statement regex finds module definitions by matching -# the following: -# -# MODULE module_name -# -# but *not* the following: -# -# MODULE PROCEDURE procedure_name -# -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# ^\s* : any amount of white space -# MODULE : match the string MODULE, case insensitive -# \s+ : match one or more white space characters -# (?!PROCEDURE) : but *don't* match if the next word matches -# PROCEDURE (negative lookahead assertion), -# case insensitive -# (\w+) : match one or more alphanumeric characters -# that make up the defined module name and -# save it in a group - - def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" - - scanner = F90Scanner("FortranScan", - "$FORTRANSUFFIXES", - path_variable, - use_regex, - include_regex, - def_regex) - return scanner diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/IDL.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/IDL.py deleted file mode 100644 index 9bd1728737..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/IDL.py +++ /dev/null @@ -1,42 +0,0 @@ -"""SCons.Scanner.IDL - -This module implements the depenency scanner for IDL (Interface -Definition Language) files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/IDL.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Node.FS -import SCons.Scanner - -def IDLScan(): - """Return a prototype Scanner instance for scanning IDL source files""" - cs = SCons.Scanner.ClassicCPP("IDLScan", - "$IDLSUFFIXES", - "CPPPATH", - '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') - return cs diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/LaTeX.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/LaTeX.py deleted file mode 100644 index 3e17e25488..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/LaTeX.py +++ /dev/null @@ -1,334 +0,0 @@ -"""SCons.Scanner.LaTeX - -This module implements the dependency scanner for LaTeX code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/LaTeX.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string -import re - -import SCons.Scanner -import SCons.Util - -# list of graphics file extensions for TeX and LaTeX -TexGraphics = ['.eps', '.ps'] -LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] - -# Used as a return value of modify_env_var if the variable is not set. -class _Null: - pass -_null = _Null - -# The user specifies the paths in env[variable], similar to other builders. -# They may be relative and must be converted to absolute, as expected -# by LaTeX and Co. The environment may already have some paths in -# env['ENV'][var]. These paths are honored, but the env[var] paths have -# higher precedence. All changes are un-done on exit. -def modify_env_var(env, var, abspath): - try: - save = env['ENV'][var] - except KeyError: - save = _null - env.PrependENVPath(var, abspath) - try: - if SCons.Util.is_List(env[var]): - #TODO(1.5) - #env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) - env.PrependENVPath(var, map(lambda p: os.path.abspath(str(p)), env[var])) - else: - # Split at os.pathsep to convert into absolute path - #TODO(1.5) env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) - env.PrependENVPath(var, map(lambda p: os.path.abspath(p), string.split(str(env[var]), os.pathsep))) - except KeyError: - pass - - # Convert into a string explicitly to append ":" (without which it won't search system - # paths as well). The problem is that env.AppendENVPath(var, ":") - # does not work, refuses to append ":" (os.pathsep). - - if SCons.Util.is_List(env['ENV'][var]): - # TODO(1.5) - #env['ENV'][var] = os.pathsep.join(env['ENV'][var]) - env['ENV'][var] = string.join(env['ENV'][var], os.pathsep) - # Append the trailing os.pathsep character here to catch the case with no env[var] - env['ENV'][var] = env['ENV'][var] + os.pathsep - - return save - -class FindENVPathDirs: - """A class to bind a specific *PATH variable name to a function that - will return all of the *path directories.""" - def __init__(self, variable): - self.variable = variable - def __call__(self, env, dir=None, target=None, source=None, argument=None): - import SCons.PathList - try: - path = env['ENV'][self.variable] - except KeyError: - return () - - dir = dir or env.fs._cwd - path = SCons.PathList.PathList(path).subst_path(env, target, source) - return tuple(dir.Rfindalldirs(path)) - - - -def LaTeXScanner(): - """Return a prototype Scanner instance for scanning LaTeX source files - when built with latex. - """ - ds = LaTeX(name = "LaTeXScanner", - suffixes = '$LATEXSUFFIXES', - # in the search order, see below in LaTeX class docstring - graphics_extensions = TexGraphics, - recursive = 0) - return ds - -def PDFLaTeXScanner(): - """Return a prototype Scanner instance for scanning LaTeX source files - when built with pdflatex. - """ - ds = LaTeX(name = "PDFLaTeXScanner", - suffixes = '$LATEXSUFFIXES', - # in the search order, see below in LaTeX class docstring - graphics_extensions = LatexGraphics, - recursive = 0) - return ds - -class LaTeX(SCons.Scanner.Base): - """Class for scanning LaTeX files for included files. - - Unlike most scanners, which use regular expressions that just - return the included file name, this returns a tuple consisting - of the keyword for the inclusion ("include", "includegraphics", - "input", or "bibliography"), and then the file name itself. - Based on a quick look at LaTeX documentation, it seems that we - should append .tex suffix for the "include" keywords, append .tex if - there is no extension for the "input" keyword, and need to add .bib - for the "bibliography" keyword that does not accept extensions by itself. - - Finally, if there is no extension for an "includegraphics" keyword - latex will append .ps or .eps to find the file, while pdftex may use .pdf, - .jpg, .tif, .mps, or .png. - - The actual subset and search order may be altered by - DeclareGraphicsExtensions command. This complication is ignored. - The default order corresponds to experimentation with teTeX - $ latex --version - pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) - kpathsea version 3.5.4 - The order is: - ['.eps', '.ps'] for latex - ['.png', '.pdf', '.jpg', '.tif']. - - Another difference is that the search path is determined by the type - of the file being searched: - env['TEXINPUTS'] for "input" and "include" keywords - env['TEXINPUTS'] for "includegraphics" keyword - env['BIBINPUTS'] for "bibliography" keyword - env['BSTINPUTS'] for "bibliographystyle" keyword - - FIXME: also look for the class or style in document[class|style]{} - FIXME: also look for the argument of bibliographystyle{} - """ - keyword_paths = {'include': 'TEXINPUTS', - 'input': 'TEXINPUTS', - 'includegraphics': 'TEXINPUTS', - 'bibliography': 'BIBINPUTS', - 'bibliographystyle': 'BSTINPUTS', - 'usepackage': 'TEXINPUTS'} - env_variables = SCons.Util.unique(keyword_paths.values()) - - def __init__(self, name, suffixes, graphics_extensions, *args, **kw): - - # We have to include \n with the % we exclude from the first part - # part of the regex because the expression is compiled with re.M. - # Without the \n, the ^ could match the beginning of a *previous* - # line followed by one or more newline characters (i.e. blank - # lines), interfering with a match on the next line. - regex = r'^[^%\n]*\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}' - self.cre = re.compile(regex, re.M) - self.graphics_extensions = graphics_extensions - - def _scan(node, env, path=(), self=self): - node = node.rfile() - if not node.exists(): - return [] - return self.scan(node, path) - - class FindMultiPathDirs: - """The stock FindPathDirs function has the wrong granularity: - it is called once per target, while we need the path that depends - on what kind of included files is being searched. This wrapper - hides multiple instances of FindPathDirs, one per the LaTeX path - variable in the environment. When invoked, the function calculates - and returns all the required paths as a dictionary (converted into - a tuple to become hashable). Then the scan function converts it - back and uses a dictionary of tuples rather than a single tuple - of paths. - """ - def __init__(self, dictionary): - self.dictionary = {} - for k,n in dictionary.items(): - self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), - FindENVPathDirs(n) ) - - def __call__(self, env, dir=None, target=None, source=None, - argument=None): - di = {} - for k,(c,cENV) in self.dictionary.items(): - di[k] = ( c(env, dir=None, target=None, source=None, - argument=None) , - cENV(env, dir=None, target=None, source=None, - argument=None) ) - # To prevent "dict is not hashable error" - return tuple(di.items()) - - class LaTeXScanCheck: - """Skip all but LaTeX source files, i.e., do not scan *.eps, - *.pdf, *.jpg, etc. - """ - def __init__(self, suffixes): - self.suffixes = suffixes - def __call__(self, node, env): - current = not node.has_builder() or node.is_up_to_date() - scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] - # Returning false means that the file is not scanned. - return scannable and current - - kw['function'] = _scan - kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['scan_check'] = LaTeXScanCheck(suffixes) - kw['name'] = name - - apply(SCons.Scanner.Base.__init__, (self,) + args, kw) - - def _latex_names(self, include): - filename = include[1] - if include[0] == 'input': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.tex'] - if (include[0] == 'include'): - return [filename + '.tex'] - if include[0] == 'bibliography': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.bib'] - if include[0] == 'usepackage': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.sty'] - if include[0] == 'includegraphics': - base, ext = os.path.splitext( filename ) - if ext == "": - #TODO(1.5) return [filename + e for e in self.graphics_extensions] - return map(lambda e, f=filename: f+e, self.graphics_extensions) - return [filename] - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(str(include)) - - def find_include(self, include, source_dir, path): - try: - sub_path = path[include[0]] - except (IndexError, KeyError): - sub_path = () - try_names = self._latex_names(include) - for n in try_names: - # see if we find it using the path in env[var] - i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0]) - if i: - return i, include - # see if we find it using the path in env['ENV'][var] - i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1]) - if i: - return i, include - return i, include - - def scan(self, node, path=()): - # Modify the default scan function to allow for the regular - # expression to return a comma separated list of file names - # as can be the case with the bibliography keyword. - - # Cache the includes list in node so we only scan it once: - path_dict = dict(list(path)) - noopt_cre = re.compile('\[.*$') - if node.includes != None: - includes = node.includes - else: - includes = self.cre.findall(node.get_contents()) - # 1. Split comma-separated lines, e.g. - # ('bibliography', 'phys,comp') - # should become two entries - # ('bibliography', 'phys') - # ('bibliography', 'comp') - # 2. Remove the options, e.g., such as - # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') - # should become - # ('includegraphics', 'picture.eps') - split_includes = [] - for include in includes: - inc_type = noopt_cre.sub('', include[0]) - inc_list = string.split(include[1],',') - for j in range(len(inc_list)): - split_includes.append( (inc_type, inc_list[j]) ) - # - includes = split_includes - node.includes = includes - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the \include, \input, etc. line. - # TODO: what about the comment in the original Classic scanner: - # """which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally.""" - nodes = [] - source_dir = node.get_dir() - for include in includes: - # - # Handle multiple filenames in include[1] - # - n, i = self.find_include(include, source_dir, path_dict) - if n is None: - # Do not bother with 'usepackage' warnings, as they most - # likely refer to system-level files - if include[0] != 'usepackage': - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) - else: - sortkey = self.sort_key(n) - nodes.append((sortkey, n)) - # - nodes.sort() - nodes = map(lambda pair: pair[1], nodes) - return nodes diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/Prog.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/Prog.py deleted file mode 100644 index ad71ba4497..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/Prog.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/Prog.py 3842 2008/12/20 22:59:52 scons" - -import string - -import SCons.Node -import SCons.Node.FS -import SCons.Scanner -import SCons.Util - -# global, set by --debug=findlibs -print_find_libs = None - -def ProgramScanner(**kw): - """Return a prototype Scanner instance for scanning executable - files for static-lib dependencies""" - kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') - ps = apply(SCons.Scanner.Base, [scan, "ProgramScanner"], kw) - return ps - -def scan(node, env, libpath = ()): - """ - This scanner scans program files for static-library - dependencies. It will search the LIBPATH environment variable - for libraries specified in the LIBS variable, returning any - files it finds as dependencies. - """ - try: - libs = env['LIBS'] - except KeyError: - # There are no LIBS in this environment, so just return a null list: - return [] - if SCons.Util.is_String(libs): - libs = string.split(libs) - else: - libs = SCons.Util.flatten(libs) - - try: - prefix = env['LIBPREFIXES'] - if not SCons.Util.is_List(prefix): - prefix = [ prefix ] - except KeyError: - prefix = [ '' ] - - try: - suffix = env['LIBSUFFIXES'] - if not SCons.Util.is_List(suffix): - suffix = [ suffix ] - except KeyError: - suffix = [ '' ] - - pairs = [] - for suf in map(env.subst, suffix): - for pref in map(env.subst, prefix): - pairs.append((pref, suf)) - - result = [] - - if callable(libpath): - libpath = libpath() - - find_file = SCons.Node.FS.find_file - adjustixes = SCons.Util.adjustixes - for lib in libs: - if SCons.Util.is_String(lib): - lib = env.subst(lib) - for pref, suf in pairs: - l = adjustixes(lib, pref, suf) - l = find_file(l, libpath, verbose=print_find_libs) - if l: - result.append(l) - else: - result.append(lib) - - return result diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/RC.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/RC.py deleted file mode 100644 index ecbc572569..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/RC.py +++ /dev/null @@ -1,49 +0,0 @@ -"""SCons.Scanner.RC - -This module implements the depenency scanner for RC (Interface -Definition Language) files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/RC.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Node.FS -import SCons.Scanner -import re - -def RCScan(): - """Return a prototype Scanner instance for scanning RC source files""" - - res_re= r'^(?:\s*#\s*(?:include)|' \ - '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ - '\s*.*?)' \ - '\s*(<|"| )([^>"\s]+)(?:[>" ])*$' - resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner", - "$RCSUFFIXES", - "CPPPATH", - res_re ) - - return resScanner diff --git a/tools/scons/scons-local-1.2.0/SCons/Scanner/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Scanner/__init__.py deleted file mode 100644 index e18f0fe306..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Scanner/__init__.py +++ /dev/null @@ -1,406 +0,0 @@ -"""SCons.Scanner - -The Scanner package for the SCons software construction utility. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Scanner/__init__.py 3842 2008/12/20 22:59:52 scons" - -import re -import string - -import SCons.Node.FS -import SCons.Util - - -class _Null: - pass - -# This is used instead of None as a default argument value so None can be -# used as an actual argument value. -_null = _Null - -def Scanner(function, *args, **kw): - """ - Public interface factory function for creating different types - of Scanners based on the different types of "functions" that may - be supplied. - - TODO: Deprecate this some day. We've moved the functionality - inside the Base class and really don't need this factory function - any more. It was, however, used by some of our Tool modules, so - the call probably ended up in various people's custom modules - patterned on SCons code. - """ - if SCons.Util.is_Dict(function): - return apply(Selector, (function,) + args, kw) - else: - return apply(Base, (function,) + args, kw) - - - -class FindPathDirs: - """A class to bind a specific *PATH variable name to a function that - will return all of the *path directories.""" - def __init__(self, variable): - self.variable = variable - def __call__(self, env, dir=None, target=None, source=None, argument=None): - import SCons.PathList - try: - path = env[self.variable] - except KeyError: - return () - - dir = dir or env.fs._cwd - path = SCons.PathList.PathList(path).subst_path(env, target, source) - return tuple(dir.Rfindalldirs(path)) - - - -class Base: - """ - The base class for dependency scanners. This implements - straightforward, single-pass scanning of a single file. - """ - - def __init__(self, - function, - name = "NONE", - argument = _null, - skeys = _null, - path_function = None, - node_class = SCons.Node.FS.Entry, - node_factory = None, - scan_check = None, - recursive = None): - """ - Construct a new scanner object given a scanner function. - - 'function' - a scanner function taking two or three - arguments and returning a list of strings. - - 'name' - a name for identifying this scanner object. - - 'argument' - an optional argument that, if specified, will be - passed to both the scanner function and the path_function. - - 'skeys' - an optional list argument that can be used to determine - which scanner should be used for a given Node. In the case of File - nodes, for example, the 'skeys' would be file suffixes. - - 'path_function' - a function that takes four or five arguments - (a construction environment, Node for the directory containing - the SConscript file that defined the primary target, list of - target nodes, list of source nodes, and optional argument for - this instance) and returns a tuple of the directories that can - be searched for implicit dependency files. May also return a - callable() which is called with no args and returns the tuple - (supporting Bindable class). - - 'node_class' - the class of Nodes which this scan will return. - If node_class is None, then this scanner will not enforce any - Node conversion and will return the raw results from the - underlying scanner function. - - 'node_factory' - the factory function to be called to translate - the raw results returned by the scanner function into the - expected node_class objects. - - 'scan_check' - a function to be called to first check whether - this node really needs to be scanned. - - 'recursive' - specifies that this scanner should be invoked - recursively on all of the implicit dependencies it returns - (the canonical example being #include lines in C source files). - May be a callable, which will be called to filter the list - of nodes found to select a subset for recursive scanning - (the canonical example being only recursively scanning - subdirectories within a directory). - - The scanner function's first argument will be a Node that should - be scanned for dependencies, the second argument will be an - Environment object, the third argument will be the tuple of paths - returned by the path_function, and the fourth argument will be - the value passed into 'argument', and the returned list should - contain the Nodes for all the direct dependencies of the file. - - Examples: - - s = Scanner(my_scanner_function) - - s = Scanner(function = my_scanner_function) - - s = Scanner(function = my_scanner_function, argument = 'foo') - - """ - - # Note: this class could easily work with scanner functions that take - # something other than a filename as an argument (e.g. a database - # node) and a dependencies list that aren't file names. All that - # would need to be changed is the documentation. - - self.function = function - self.path_function = path_function - self.name = name - self.argument = argument - - if skeys is _null: - if SCons.Util.is_Dict(function): - skeys = function.keys() - else: - skeys = [] - self.skeys = skeys - - self.node_class = node_class - self.node_factory = node_factory - self.scan_check = scan_check - if callable(recursive): - self.recurse_nodes = recursive - elif recursive: - self.recurse_nodes = self._recurse_all_nodes - else: - self.recurse_nodes = self._recurse_no_nodes - - def path(self, env, dir=None, target=None, source=None): - if not self.path_function: - return () - if not self.argument is _null: - return self.path_function(env, dir, target, source, self.argument) - else: - return self.path_function(env, dir, target, source) - - def __call__(self, node, env, path = ()): - """ - This method scans a single object. 'node' is the node - that will be passed to the scanner function, and 'env' is the - environment that will be passed to the scanner function. A list of - direct dependency nodes for the specified node will be returned. - """ - if self.scan_check and not self.scan_check(node, env): - return [] - - self = self.select(node) - - if not self.argument is _null: - list = self.function(node, env, path, self.argument) - else: - list = self.function(node, env, path) - - kw = {} - if hasattr(node, 'dir'): - kw['directory'] = node.dir - node_factory = env.get_factory(self.node_factory) - nodes = [] - for l in list: - if self.node_class and not isinstance(l, self.node_class): - l = apply(node_factory, (l,), kw) - nodes.append(l) - return nodes - - def __cmp__(self, other): - try: - return cmp(self.__dict__, other.__dict__) - except AttributeError: - # other probably doesn't have a __dict__ - return cmp(self.__dict__, other) - - def __hash__(self): - return id(self) - - def __str__(self): - return self.name - - def add_skey(self, skey): - """Add a skey to the list of skeys""" - self.skeys.append(skey) - - def get_skeys(self, env=None): - if env and SCons.Util.is_String(self.skeys): - return env.subst_list(self.skeys)[0] - return self.skeys - - def select(self, node): - if SCons.Util.is_Dict(self.function): - key = node.scanner_key() - try: - return self.function[key] - except KeyError: - return None - else: - return self - - def _recurse_all_nodes(self, nodes): - return nodes - - def _recurse_no_nodes(self, nodes): - return [] - - recurse_nodes = _recurse_no_nodes - - def add_scanner(self, skey, scanner): - self.function[skey] = scanner - self.add_skey(skey) - - -class Selector(Base): - """ - A class for selecting a more specific scanner based on the - scanner_key() (suffix) for a specific Node. - - TODO: This functionality has been moved into the inner workings of - the Base class, and this class will be deprecated at some point. - (It was never exposed directly as part of the public interface, - although it is used by the Scanner() factory function that was - used by various Tool modules and therefore was likely a template - for custom modules that may be out there.) - """ - def __init__(self, dict, *args, **kw): - apply(Base.__init__, (self, None,)+args, kw) - self.dict = dict - self.skeys = dict.keys() - - def __call__(self, node, env, path = ()): - return self.select(node)(node, env, path) - - def select(self, node): - try: - return self.dict[node.scanner_key()] - except KeyError: - return None - - def add_scanner(self, skey, scanner): - self.dict[skey] = scanner - self.add_skey(skey) - - -class Current(Base): - """ - A class for scanning files that are source files (have no builder) - or are derived files and are current (which implies that they exist, - either locally or in a repository). - """ - - def __init__(self, *args, **kw): - def current_check(node, env): - return not node.has_builder() or node.is_up_to_date() - kw['scan_check'] = current_check - apply(Base.__init__, (self,) + args, kw) - -class Classic(Current): - """ - A Scanner subclass to contain the common logic for classic CPP-style - include scanning, but which can be customized to use different - regular expressions to find the includes. - - Note that in order for this to work "out of the box" (without - overriding the find_include() and sort_key() methods), the regular - expression passed to the constructor must return the name of the - include file in group 0. - """ - - def __init__(self, name, suffixes, path_variable, regex, *args, **kw): - - self.cre = re.compile(regex, re.M) - - def _scan(node, env, path=(), self=self): - node = node.rfile() - if not node.exists(): - return [] - return self.scan(node, path) - - kw['function'] = _scan - kw['path_function'] = FindPathDirs(path_variable) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['name'] = name - - apply(Current.__init__, (self,) + args, kw) - - def find_include(self, include, source_dir, path): - n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) - return n, include - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(include) - - def find_include_names(self, node): - return self.cre.findall(node.get_contents()) - - def scan(self, node, path=()): - - # cache the includes list in node so we only scan it once: - if node.includes != None: - includes = node.includes - else: - includes = self.find_include_names (node) - node.includes = includes - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the #include line (including the - # " or <, since that may affect what file is found), which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally. - nodes = [] - source_dir = node.get_dir() - if callable(path): - path = path() - for include in includes: - n, i = self.find_include(include, source_dir, path) - - if n is None: - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) - else: - sortkey = self.sort_key(include) - nodes.append((sortkey, n)) - - nodes.sort() - nodes = map(lambda pair: pair[1], nodes) - return nodes - -class ClassicCPP(Classic): - """ - A Classic Scanner subclass which takes into account the type of - bracketing used to include the file, and uses classic CPP rules - for searching for the files based on the bracketing. - - Note that in order for this to work, the regular expression passed - to the constructor must return the leading bracket in group 0, and - the contained filename in group 1. - """ - def find_include(self, include, source_dir, path): - if include[0] == '"': - paths = (source_dir,) + tuple(path) - else: - paths = tuple(path) + (source_dir,) - - n = SCons.Node.FS.find_file(include[1], paths) - - return n, include[1] - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(string.join(include)) diff --git a/tools/scons/scons-local-1.2.0/SCons/Script/Interactive.py b/tools/scons/scons-local-1.2.0/SCons/Script/Interactive.py deleted file mode 100644 index 13cc41409e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Script/Interactive.py +++ /dev/null @@ -1,376 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Script/Interactive.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """ -SCons interactive mode -""" - -# TODO: -# -# This has the potential to grow into something with a really big life -# of its own, which might or might not be a good thing. Nevertheless, -# here are some enhancements that will probably be requested some day -# and are worth keeping in mind (assuming this takes off): -# -# - A command to re-read / re-load the SConscript files. This may -# involve allowing people to specify command-line options (e.g. -f, -# -I, --no-site-dir) that affect how the SConscript files are read. -# -# - Additional command-line options on the "build" command. -# -# Of the supported options that seemed to make sense (after a quick -# pass through the list), the ones that seemed likely enough to be -# used are listed in the man page and have explicit test scripts. -# -# These had code changed in Script/Main.py to support them, but didn't -# seem likely to be used regularly, so had no test scripts added: -# -# build --diskcheck=* -# build --implicit-cache=* -# build --implicit-deps-changed=* -# build --implicit-deps-unchanged=* -# -# These look like they should "just work" with no changes to the -# existing code, but like those above, look unlikely to be used and -# therefore had no test scripts added: -# -# build --random -# -# These I'm not sure about. They might be useful for individual -# "build" commands, and may even work, but they seem unlikely enough -# that we'll wait until they're requested before spending any time on -# writing test scripts for them, or investigating whether they work. -# -# build -q [??? is there a useful analog to the exit status?] -# build --duplicate= -# build --profile= -# build --max-drift= -# build --warn=* -# build --Y -# -# - Most of the SCons command-line options that the "build" command -# supports should be settable as default options that apply to all -# subsequent "build" commands. Maybe a "set {option}" command that -# maps to "SetOption('{option}')". -# -# - Need something in the 'help' command that prints the -h output. -# -# - A command to run the configure subsystem separately (must see how -# this interacts with the new automake model). -# -# - Command-line completion of target names; maybe even of SCons options? -# Completion is something that's supported by the Python cmd module, -# so this should be doable without too much trouble. -# - -import cmd -import copy -import os -import re -import shlex -import string -import sys - -try: - import readline -except ImportError: - pass - -class SConsInteractiveCmd(cmd.Cmd): - """\ - build [TARGETS] Build the specified TARGETS and their dependencies. - 'b' is a synonym. - clean [TARGETS] Clean (remove) the specified TARGETS and their - dependencies. 'c' is a synonym. - exit Exit SCons interactive mode. - help [COMMAND] Prints help for the specified COMMAND. 'h' and - '?' are synonyms. - shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' - are synonyms. - version Prints SCons version information. - """ - - synonyms = { - 'b' : 'build', - 'c' : 'clean', - 'h' : 'help', - 'scons' : 'build', - 'sh' : 'shell', - } - - def __init__(self, **kw): - cmd.Cmd.__init__(self) - for key, val in kw.items(): - setattr(self, key, val) - - if sys.platform == 'win32': - self.shell_variable = 'COMSPEC' - else: - self.shell_variable = 'SHELL' - - def default(self, argv): - print "*** Unknown command: %s" % argv[0] - - def onecmd(self, line): - line = string.strip(line) - if not line: - print self.lastcmd - return self.emptyline() - self.lastcmd = line - if line[0] == '!': - line = 'shell ' + line[1:] - elif line[0] == '?': - line = 'help ' + line[1:] - if os.sep == '\\': - line = string.replace(line, '\\', '\\\\') - argv = shlex.split(line) - argv[0] = self.synonyms.get(argv[0], argv[0]) - if not argv[0]: - return self.default(line) - else: - try: - func = getattr(self, 'do_' + argv[0]) - except AttributeError: - return self.default(argv) - return func(argv) - - def do_build(self, argv): - """\ - build [TARGETS] Build the specified TARGETS and their - dependencies. 'b' is a synonym. - """ - import SCons.Node - import SCons.SConsign - import SCons.Script.Main - - options = copy.deepcopy(self.options) - - options, targets = self.parser.parse_args(argv[1:], values=options) - - SCons.Script.COMMAND_LINE_TARGETS = targets - - if targets: - SCons.Script.BUILD_TARGETS = targets - else: - # If the user didn't specify any targets on the command line, - # use the list of default targets. - SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default - - nodes = SCons.Script.Main._build_targets(self.fs, - options, - targets, - self.target_top) - - if not nodes: - return - - # Call each of the Node's alter_targets() methods, which may - # provide additional targets that ended up as part of the build - # (the canonical example being a VariantDir() when we're building - # from a source directory) and which we therefore need their - # state cleared, too. - x = [] - for n in nodes: - x.extend(n.alter_targets()[0]) - nodes.extend(x) - - # Clean up so that we can perform the next build correctly. - # - # We do this by walking over all the children of the targets, - # and clearing their state. - # - # We currently have to re-scan each node to find their - # children, because built nodes have already been partially - # cleared and don't remember their children. (In scons - # 0.96.1 and earlier, this wasn't the case, and we didn't - # have to re-scan the nodes.) - # - # Because we have to re-scan each node, we can't clear the - # nodes as we walk over them, because we may end up rescanning - # a cleared node as we scan a later node. Therefore, only - # store the list of nodes that need to be cleared as we walk - # the tree, and clear them in a separate pass. - # - # XXX: Someone more familiar with the inner workings of scons - # may be able to point out a more efficient way to do this. - - SCons.Script.Main.progress_display("scons: Clearing cached node information ...") - - seen_nodes = {} - - def get_unseen_children(node, parent, seen_nodes=seen_nodes): - def is_unseen(node, seen_nodes=seen_nodes): - return not seen_nodes.has_key(node) - return filter(is_unseen, node.children(scan=1)) - - def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): - seen_nodes[node] = 1 - - # If this file is in a VariantDir and has a - # corresponding source file in the source tree, remember the - # node in the source tree, too. This is needed in - # particular to clear cached implicit dependencies on the - # source file, since the scanner will scan it if the - # VariantDir was created with duplicate=0. - try: - rfile_method = node.rfile - except AttributeError: - return - else: - rfile = rfile_method() - if rfile != node: - seen_nodes[rfile] = 1 - - for node in nodes: - walker = SCons.Node.Walker(node, - kids_func=get_unseen_children, - eval_func=add_to_seen_nodes) - n = walker.next() - while n: - n = walker.next() - - for node in seen_nodes.keys(): - # Call node.clear() to clear most of the state - node.clear() - # node.clear() doesn't reset node.state, so call - # node.set_state() to reset it manually - node.set_state(SCons.Node.no_state) - node.implicit = None - - # Debug: Uncomment to verify that all Taskmaster reference - # counts have been reset to zero. - #if node.ref_count != 0: - # from SCons.Debug import Trace - # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) - - SCons.SConsign.Reset() - SCons.Script.Main.progress_display("scons: done clearing node information.") - - def do_clean(self, argv): - """\ - clean [TARGETS] Clean (remove) the specified TARGETS - and their dependencies. 'c' is a synonym. - """ - return self.do_build(['build', '--clean'] + argv[1:]) - - def do_EOF(self, argv): - print - self.do_exit(argv) - - def _do_one_help(self, arg): - try: - # If help_<arg>() exists, then call it. - func = getattr(self, 'help_' + arg) - except AttributeError: - try: - func = getattr(self, 'do_' + arg) - except AttributeError: - doc = None - else: - doc = self._doc_to_help(func) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - else: - doc = self.strip_initial_spaces(func()) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - - def _doc_to_help(self, obj): - doc = obj.__doc__ - if doc is None: - return '' - return self._strip_initial_spaces(doc) - - def _strip_initial_spaces(self, s): - #lines = s.split('\n') - lines = string.split(s, '\n') - spaces = re.match(' *', lines[0]).group(0) - #def strip_spaces(l): - # if l.startswith(spaces): - # l = l[len(spaces):] - # return l - #return '\n'.join([ strip_spaces(l) for l in lines ]) - def strip_spaces(l, spaces=spaces): - if l[:len(spaces)] == spaces: - l = l[len(spaces):] - return l - lines = map(strip_spaces, lines) - return string.join(lines, '\n') - - def do_exit(self, argv): - """\ - exit Exit SCons interactive mode. - """ - sys.exit(0) - - def do_help(self, argv): - """\ - help [COMMAND] Prints help for the specified COMMAND. 'h' - and '?' are synonyms. - """ - if argv[1:]: - for arg in argv[1:]: - if self._do_one_help(arg): - break - else: - # If bare 'help' is called, print this class's doc - # string (if it has one). - doc = self._doc_to_help(self.__class__) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - - def do_shell(self, argv): - """\ - shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and - '!' are synonyms. - """ - import subprocess - argv = argv[1:] - if not argv: - argv = os.environ[self.shell_variable] - try: - p = subprocess.Popen(argv) - except EnvironmentError, e: - sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) - else: - p.wait() - - def do_version(self, argv): - """\ - version Prints SCons version information. - """ - sys.stdout.write(self.parser.version + '\n') - -def interact(fs, parser, options, targets, target_top): - c = SConsInteractiveCmd(prompt = 'scons>>> ', - fs = fs, - parser = parser, - options = options, - targets = targets, - target_top = target_top) - c.cmdloop() diff --git a/tools/scons/scons-local-1.2.0/SCons/Script/Main.py b/tools/scons/scons-local-1.2.0/SCons/Script/Main.py deleted file mode 100644 index 5624038c08..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Script/Main.py +++ /dev/null @@ -1,1321 +0,0 @@ -"""SCons.Script - -This file implements the main() function used by the scons script. - -Architecturally, this *is* the scons script, and will likely only be -called from the external "scons" wrapper. Consequently, anything here -should not be, or be considered, part of the build engine. If it's -something that we expect other software to want to use, it should go in -some other module. If it's specific to the "scons" script invocation, -it goes here. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Script/Main.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string -import sys -import time -import traceback - -# Strip the script directory from sys.path() so on case-insensitive -# (Windows) systems Python doesn't think that the "scons" script is the -# "SCons" package. Replace it with our own version directory so, if -# if they're there, we pick up the right version of the build engine -# modules. -#sys.path = [os.path.join(sys.prefix, -# 'lib', -# 'scons-%d' % SCons.__version__)] + sys.path[1:] - -import SCons.CacheDir -import SCons.Debug -import SCons.Defaults -import SCons.Environment -import SCons.Errors -import SCons.Job -import SCons.Node -import SCons.Node.FS -import SCons.SConf -import SCons.Script -import SCons.Taskmaster -import SCons.Util -import SCons.Warnings - -import SCons.Script.Interactive - -def fetch_win32_parallel_msg(): - # A subsidiary function that exists solely to isolate this import - # so we don't have to pull it in on all platforms, and so that an - # in-line "import" statement in the _main() function below doesn't - # cause warnings about local names shadowing use of the 'SCons' - # globl in nest scopes and UnboundLocalErrors and the like in some - # versions (2.1) of Python. - import SCons.Platform.win32 - return SCons.Platform.win32.parallel_msg - -# - -class SConsPrintHelpException(Exception): - pass - -display = SCons.Util.display -progress_display = SCons.Util.DisplayEngine() - -first_command_start = None -last_command_end = None - -class Progressor: - prev = '' - count = 0 - target_string = '$TARGET' - - def __init__(self, obj, interval=1, file=None, overwrite=False): - if file is None: - file = sys.stdout - - self.obj = obj - self.file = file - self.interval = interval - self.overwrite = overwrite - - if callable(obj): - self.func = obj - elif SCons.Util.is_List(obj): - self.func = self.spinner - elif string.find(obj, self.target_string) != -1: - self.func = self.replace_string - else: - self.func = self.string - - def write(self, s): - self.file.write(s) - self.file.flush() - self.prev = s - - def erase_previous(self): - if self.prev: - length = len(self.prev) - if self.prev[-1] in ('\n', '\r'): - length = length - 1 - self.write(' ' * length + '\r') - self.prev = '' - - def spinner(self, node): - self.write(self.obj[self.count % len(self.obj)]) - - def string(self, node): - self.write(self.obj) - - def replace_string(self, node): - self.write(string.replace(self.obj, self.target_string, str(node))) - - def __call__(self, node): - self.count = self.count + 1 - if (self.count % self.interval) == 0: - if self.overwrite: - self.erase_previous() - self.func(node) - -ProgressObject = SCons.Util.Null() - -def Progress(*args, **kw): - global ProgressObject - ProgressObject = apply(Progressor, args, kw) - -# Task control. -# - -_BuildFailures = [] - -def GetBuildFailures(): - return _BuildFailures - -class BuildTask(SCons.Taskmaster.Task): - """An SCons build task.""" - progress = ProgressObject - - def display(self, message): - display('scons: ' + message) - - def prepare(self): - self.progress(self.targets[0]) - return SCons.Taskmaster.Task.prepare(self) - - def needs_execute(self): - target = self.targets[0] - if target.get_state() == SCons.Node.executing: - return True - else: - if self.top and target.has_builder(): - display("scons: `%s' is up to date." % str(self.node)) - return False - - def execute(self): - if print_time: - start_time = time.time() - global first_command_start - if first_command_start is None: - first_command_start = start_time - SCons.Taskmaster.Task.execute(self) - if print_time: - global cumulative_command_time - global last_command_end - finish_time = time.time() - last_command_end = finish_time - cumulative_command_time = cumulative_command_time+finish_time-start_time - sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time)) - - def do_failed(self, status=2): - _BuildFailures.append(self.exception[1]) - global exit_status - global this_build_status - if self.options.ignore_errors: - SCons.Taskmaster.Task.executed(self) - elif self.options.keep_going: - SCons.Taskmaster.Task.fail_continue(self) - exit_status = status - this_build_status = status - else: - SCons.Taskmaster.Task.fail_stop(self) - exit_status = status - this_build_status = status - - def executed(self): - t = self.targets[0] - if self.top and not t.has_builder() and not t.side_effect: - if not t.exists(): - errstr="Do not know how to make target `%s'." % t - sys.stderr.write("scons: *** " + errstr) - if not self.options.keep_going: - sys.stderr.write(" Stop.") - sys.stderr.write("\n") - try: - raise SCons.Errors.BuildError(t, errstr) - except KeyboardInterrupt: - raise - except: - self.exception_set() - self.do_failed() - else: - print "scons: Nothing to be done for `%s'." % t - SCons.Taskmaster.Task.executed(self) - else: - SCons.Taskmaster.Task.executed(self) - - def failed(self): - # Handle the failure of a build task. The primary purpose here - # is to display the various types of Errors and Exceptions - # appropriately. - exc_info = self.exc_info() - try: - t, e, tb = exc_info - except ValueError: - t, e = exc_info - tb = None - - if t is None: - # The Taskmaster didn't record an exception for this Task; - # see if the sys module has one. - try: - t, e, tb = sys.exc_info()[:] - except ValueError: - t, e = exc_info - tb = None - - # Deprecated string exceptions will have their string stored - # in the first entry of the tuple. - if e is None: - e = t - - buildError = SCons.Errors.convert_to_BuildError(e) - if not buildError.node: - buildError.node = self.node - - node = buildError.node - if not SCons.Util.is_List(node): - node = [ node ] - nodename = string.join(map(str, node), ', ') - - errfmt = "scons: *** [%s] %s\n" - sys.stderr.write(errfmt % (nodename, buildError)) - - if (buildError.exc_info[2] and buildError.exc_info[1] and - # TODO(1.5) - #not isinstance( - # buildError.exc_info[1], - # (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))): - not isinstance(buildError.exc_info[1], EnvironmentError) and - not isinstance(buildError.exc_info[1], SCons.Errors.StopError) and - not isinstance(buildError.exc_info[1], SCons.Errors.UserError)): - type, value, trace = buildError.exc_info - traceback.print_exception(type, value, trace) - elif tb and print_stacktrace: - sys.stderr.write("scons: internal stack trace:\n") - traceback.print_tb(tb, file=sys.stderr) - - self.exception = (e, buildError, tb) # type, value, traceback - self.do_failed(buildError.exitstatus) - - self.exc_clear() - - def postprocess(self): - if self.top: - t = self.targets[0] - for tp in self.options.tree_printers: - tp.display(t) - if self.options.debug_includes: - tree = t.render_include_tree() - if tree: - print - print tree - SCons.Taskmaster.Task.postprocess(self) - - def make_ready(self): - """Make a task ready for execution""" - SCons.Taskmaster.Task.make_ready(self) - if self.out_of_date and self.options.debug_explain: - explanation = self.out_of_date[0].explain() - if explanation: - sys.stdout.write("scons: " + explanation) - -class CleanTask(SCons.Taskmaster.Task): - """An SCons clean task.""" - def fs_delete(self, path, pathstr, remove=1): - try: - if os.path.exists(path): - if os.path.isfile(path): - if remove: os.unlink(path) - display("Removed " + pathstr) - elif os.path.isdir(path) and not os.path.islink(path): - # delete everything in the dir - entries = os.listdir(path) - # Sort for deterministic output (os.listdir() Can - # return entries in a random order). - entries.sort() - for e in entries: - p = os.path.join(path, e) - s = os.path.join(pathstr, e) - if os.path.isfile(p): - if remove: os.unlink(p) - display("Removed " + s) - else: - self.fs_delete(p, s, remove) - # then delete dir itself - if remove: os.rmdir(path) - display("Removed directory " + pathstr) - except (IOError, OSError), e: - print "scons: Could not remove '%s':" % pathstr, e.strerror - - def show(self): - target = self.targets[0] - if (target.has_builder() or target.side_effect) and not target.noclean: - for t in self.targets: - if not t.isdir(): - display("Removed " + str(t)) - if SCons.Environment.CleanTargets.has_key(target): - files = SCons.Environment.CleanTargets[target] - for f in files: - self.fs_delete(f.abspath, str(f), 0) - - def remove(self): - target = self.targets[0] - if (target.has_builder() or target.side_effect) and not target.noclean: - for t in self.targets: - try: - removed = t.remove() - except OSError, e: - # An OSError may indicate something like a permissions - # issue, an IOError would indicate something like - # the file not existing. In either case, print a - # message and keep going to try to remove as many - # targets aa possible. - print "scons: Could not remove '%s':" % str(t), e.strerror - else: - if removed: - display("Removed " + str(t)) - if SCons.Environment.CleanTargets.has_key(target): - files = SCons.Environment.CleanTargets[target] - for f in files: - self.fs_delete(f.abspath, str(f)) - - execute = remove - - # We want the Taskmaster to update the Node states (and therefore - # handle reference counts, etc.), but we don't want to call - # back to the Node's post-build methods, which would do things - # we don't want, like store .sconsign information. - executed = SCons.Taskmaster.Task.executed_without_callbacks - - # Have the taskmaster arrange to "execute" all of the targets, because - # we'll figure out ourselves (in remove() or show() above) whether - # anything really needs to be done. - make_ready = SCons.Taskmaster.Task.make_ready_all - - def prepare(self): - pass - -class QuestionTask(SCons.Taskmaster.Task): - """An SCons task for the -q (question) option.""" - def prepare(self): - pass - - def execute(self): - if self.targets[0].get_state() != SCons.Node.up_to_date or \ - (self.top and not self.targets[0].exists()): - global exit_status - global this_build_status - exit_status = 1 - this_build_status = 1 - self.tm.stop() - - def executed(self): - pass - - -class TreePrinter: - def __init__(self, derived=False, prune=False, status=False): - self.derived = derived - self.prune = prune - self.status = status - def get_all_children(self, node): - return node.all_children() - def get_derived_children(self, node): - children = node.all_children(None) - return filter(lambda x: x.has_builder(), children) - def display(self, t): - if self.derived: - func = self.get_derived_children - else: - func = self.get_all_children - s = self.status and 2 or 0 - SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) - - -def python_version_string(): - return string.split(sys.version)[0] - -def python_version_unsupported(version=sys.version_info): - return version < (1, 5, 2) - -def python_version_deprecated(version=sys.version_info): - return version < (2, 2, 0) - - -# Global variables - -print_objects = 0 -print_memoizer = 0 -print_stacktrace = 0 -print_time = 0 -sconscript_time = 0 -cumulative_command_time = 0 -exit_status = 0 # final exit status, assume success by default -this_build_status = 0 # "exit status" of an individual build -num_jobs = None -delayed_warnings = [] - -class FakeOptionParser: - """ - A do-nothing option parser, used for the initial OptionsParser variable. - - During normal SCons operation, the OptionsParser is created right - away by the main() function. Certain tests scripts however, can - introspect on different Tool modules, the initialization of which - can try to add a new, local option to an otherwise uninitialized - OptionsParser object. This allows that introspection to happen - without blowing up. - - """ - class FakeOptionValues: - def __getattr__(self, attr): - return None - values = FakeOptionValues() - def add_local_option(self, *args, **kw): - pass - -OptionsParser = FakeOptionParser() - -def AddOption(*args, **kw): - if not kw.has_key('default'): - kw['default'] = None - result = apply(OptionsParser.add_local_option, args, kw) - return result - -def GetOption(name): - return getattr(OptionsParser.values, name) - -def SetOption(name, value): - return OptionsParser.values.set_option(name, value) - -# -class Stats: - def __init__(self): - self.stats = [] - self.labels = [] - self.append = self.do_nothing - self.print_stats = self.do_nothing - def enable(self, outfp): - self.outfp = outfp - self.append = self.do_append - self.print_stats = self.do_print - def do_nothing(self, *args, **kw): - pass - -class CountStats(Stats): - def do_append(self, label): - self.labels.append(label) - self.stats.append(SCons.Debug.fetchLoggedInstances()) - def do_print(self): - stats_table = {} - for s in self.stats: - for n in map(lambda t: t[0], s): - stats_table[n] = [0, 0, 0, 0] - i = 0 - for s in self.stats: - for n, c in s: - stats_table[n][i] = c - i = i + 1 - keys = stats_table.keys() - keys.sort() - self.outfp.write("Object counts:\n") - pre = [" "] - post = [" %s\n"] - l = len(self.stats) - fmt1 = string.join(pre + [' %7s']*l + post, '') - fmt2 = string.join(pre + [' %7d']*l + post, '') - labels = self.labels[:l] - labels.append(("", "Class")) - self.outfp.write(fmt1 % tuple(map(lambda x: x[0], labels))) - self.outfp.write(fmt1 % tuple(map(lambda x: x[1], labels))) - for k in keys: - r = stats_table[k][:l] + [k] - self.outfp.write(fmt2 % tuple(r)) - -count_stats = CountStats() - -class MemStats(Stats): - def do_append(self, label): - self.labels.append(label) - self.stats.append(SCons.Debug.memory()) - def do_print(self): - fmt = 'Memory %-32s %12d\n' - for label, stats in map(None, self.labels, self.stats): - self.outfp.write(fmt % (label, stats)) - -memory_stats = MemStats() - -# utility functions - -def _scons_syntax_error(e): - """Handle syntax errors. Print out a message and show where the error - occurred. - """ - etype, value, tb = sys.exc_info() - lines = traceback.format_exception_only(etype, value) - for line in lines: - sys.stderr.write(line+'\n') - sys.exit(2) - -def find_deepest_user_frame(tb): - """ - Find the deepest stack frame that is not part of SCons. - - Input is a "pre-processed" stack trace in the form - returned by traceback.extract_tb() or traceback.extract_stack() - """ - - tb.reverse() - - # find the deepest traceback frame that is not part - # of SCons: - for frame in tb: - filename = frame[0] - if string.find(filename, os.sep+'SCons'+os.sep) == -1: - return frame - return tb[0] - -def _scons_user_error(e): - """Handle user errors. Print out a message and a description of the - error, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - global print_stacktrace - etype, value, tb = sys.exc_info() - if print_stacktrace: - traceback.print_exception(etype, value, tb) - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: *** %s\n" % value) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - sys.exit(2) - -def _scons_user_warning(e): - """Handle user warnings. Print out a message and a description of - the warning, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - etype, value, tb = sys.exc_info() - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: warning: %s\n" % e) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_warning(e): - """Slightly different from _scons_user_warning in that we use the - *current call stack* rather than sys.exc_info() to get our stack trace. - This is used by the warnings framework to print warnings.""" - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) - sys.stderr.write("\nscons: warning: %s\n" % e[0]) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_error(): - """Handle all errors but user errors. Print out a message telling - the user what to do in this case and print a normal trace. - """ - print 'internal error' - traceback.print_exc() - sys.exit(2) - -def _SConstruct_exists(dirname='', repositories=[], filelist=None): - """This function checks that an SConstruct file exists in a directory. - If so, it returns the path of the file. By default, it checks the - current directory. - """ - if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] - for file in filelist: - sfile = os.path.join(dirname, file) - if os.path.isfile(sfile): - return sfile - if not os.path.isabs(sfile): - for rep in repositories: - if os.path.isfile(os.path.join(rep, sfile)): - return sfile - return None - -def _set_debug_values(options): - global print_memoizer, print_objects, print_stacktrace, print_time - - debug_values = options.debug - - if "count" in debug_values: - # All of the object counts are within "if __debug__:" blocks, - # which get stripped when running optimized (with python -O or - # from compiled *.pyo files). Provide a warning if __debug__ is - # stripped, so it doesn't just look like --debug=count is broken. - enable_count = False - if __debug__: enable_count = True - if enable_count: - count_stats.enable(sys.stdout) - else: - msg = "--debug=count is not supported when running SCons\n" + \ - "\twith the python -O option or optimized (.pyo) modules." - SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) - if "dtree" in debug_values: - options.tree_printers.append(TreePrinter(derived=True)) - options.debug_explain = ("explain" in debug_values) - if "findlibs" in debug_values: - SCons.Scanner.Prog.print_find_libs = "findlibs" - options.debug_includes = ("includes" in debug_values) - print_memoizer = ("memoizer" in debug_values) - if "memory" in debug_values: - memory_stats.enable(sys.stdout) - print_objects = ("objects" in debug_values) - if "presub" in debug_values: - SCons.Action.print_actions_presub = 1 - if "stacktrace" in debug_values: - print_stacktrace = 1 - if "stree" in debug_values: - options.tree_printers.append(TreePrinter(status=True)) - if "time" in debug_values: - print_time = 1 - if "tree" in debug_values: - options.tree_printers.append(TreePrinter()) - -def _create_path(plist): - path = '.' - for d in plist: - if os.path.isabs(d): - path = d - else: - path = path + '/' + d - return path - -def _load_site_scons_dir(topdir, site_dir_name=None): - """Load the site_scons dir under topdir. - Adds site_scons to sys.path, imports site_scons/site_init.py, - and adds site_scons/site_tools to default toolpath.""" - if site_dir_name: - err_if_not_found = True # user specified: err if missing - else: - site_dir_name = "site_scons" - err_if_not_found = False - - site_dir = os.path.join(topdir.path, site_dir_name) - if not os.path.exists(site_dir): - if err_if_not_found: - raise SCons.Errors.UserError, "site dir %s not found."%site_dir - return - - site_init_filename = "site_init.py" - site_init_modname = "site_init" - site_tools_dirname = "site_tools" - sys.path = [os.path.abspath(site_dir)] + sys.path - site_init_file = os.path.join(site_dir, site_init_filename) - site_tools_dir = os.path.join(site_dir, site_tools_dirname) - if os.path.exists(site_init_file): - import imp - try: - fp, pathname, description = imp.find_module(site_init_modname, - [site_dir]) - try: - imp.load_module(site_init_modname, fp, pathname, description) - finally: - if fp: - fp.close() - except ImportError, e: - sys.stderr.write("Can't import site init file '%s': %s\n"%(site_init_file, e)) - raise - except Exception, e: - sys.stderr.write("Site init file '%s' raised exception: %s\n"%(site_init_file, e)) - raise - if os.path.exists(site_tools_dir): - SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir)) - -def version_string(label, module): - version = module.__version__ - build = module.__build__ - if build: - if build[0] != '.': - build = '.' + build - version = version + build - fmt = "\t%s: v%s, %s, by %s on %s\n" - return fmt % (label, - version, - module.__date__, - module.__developer__, - module.__buildsys__) - -def _main(parser): - global exit_status - global this_build_status - - options = parser.values - - # Here's where everything really happens. - - # First order of business: set up default warnings and then - # handle the user's warning options, so that we can issue (or - # suppress) appropriate warnings about anything that might happen, - # as configured by the user. - - default_warnings = [ SCons.Warnings.CorruptSConsignWarning, - SCons.Warnings.DeprecatedWarning, - SCons.Warnings.DuplicateEnvironmentWarning, - SCons.Warnings.FutureReservedVariableWarning, - SCons.Warnings.LinkWarning, - SCons.Warnings.MissingSConscriptWarning, - SCons.Warnings.NoMD5ModuleWarning, - SCons.Warnings.NoMetaclassSupportWarning, - SCons.Warnings.NoObjectCountWarning, - SCons.Warnings.NoParallelSupportWarning, - SCons.Warnings.MisleadingKeywordsWarning, - SCons.Warnings.ReservedVariableWarning, - SCons.Warnings.StackSizeWarning, - ] - - for warning in default_warnings: - SCons.Warnings.enableWarningClass(warning) - SCons.Warnings._warningOut = _scons_internal_warning - SCons.Warnings.process_warn_strings(options.warn) - - # Now that we have the warnings configuration set up, we can actually - # issue (or suppress) any warnings about warning-worthy things that - # occurred while the command-line options were getting parsed. - try: - dw = options.delayed_warnings - except AttributeError: - pass - else: - delayed_warnings.extend(dw) - for warning_type, message in delayed_warnings: - SCons.Warnings.warn(warning_type, message) - - if options.diskcheck: - SCons.Node.FS.set_diskcheck(options.diskcheck) - - # Next, we want to create the FS object that represents the outside - # world's file system, as that's central to a lot of initialization. - # To do this, however, we need to be in the directory from which we - # want to start everything, which means first handling any relevant - # options that might cause us to chdir somewhere (-C, -D, -U, -u). - if options.directory: - cdir = _create_path(options.directory) - try: - os.chdir(cdir) - except OSError: - sys.stderr.write("Could not change directory to %s\n" % cdir) - - target_top = None - if options.climb_up: - target_top = '.' # directory to prepend to targets - script_dir = os.getcwd() # location of script - while script_dir and not _SConstruct_exists(script_dir, - options.repository, - options.file): - script_dir, last_part = os.path.split(script_dir) - if last_part: - target_top = os.path.join(last_part, target_top) - else: - script_dir = '' - if script_dir and script_dir != os.getcwd(): - display("scons: Entering directory `%s'" % script_dir) - os.chdir(script_dir) - - # Now that we're in the top-level SConstruct directory, go ahead - # and initialize the FS object that represents the file system, - # and make it the build engine default. - fs = SCons.Node.FS.get_default_fs() - - for rep in options.repository: - fs.Repository(rep) - - # Now that we have the FS object, the next order of business is to - # check for an SConstruct file (or other specified config file). - # If there isn't one, we can bail before doing any more work. - scripts = [] - if options.file: - scripts.extend(options.file) - if not scripts: - sfile = _SConstruct_exists(repositories=options.repository, - filelist=options.file) - if sfile: - scripts.append(sfile) - - if not scripts: - if options.help: - # There's no SConstruct, but they specified -h. - # Give them the options usage now, before we fail - # trying to read a non-existent SConstruct file. - raise SConsPrintHelpException - raise SCons.Errors.UserError, "No SConstruct file found." - - if scripts[0] == "-": - d = fs.getcwd() - else: - d = fs.File(scripts[0]).dir - fs.set_SConstruct_dir(d) - - _set_debug_values(options) - SCons.Node.implicit_cache = options.implicit_cache - SCons.Node.implicit_deps_changed = options.implicit_deps_changed - SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged - - if options.no_exec: - SCons.SConf.dryrun = 1 - SCons.Action.execute_actions = None - if options.question: - SCons.SConf.dryrun = 1 - if options.clean: - SCons.SConf.SetBuildType('clean') - if options.help: - SCons.SConf.SetBuildType('help') - SCons.SConf.SetCacheMode(options.config) - SCons.SConf.SetProgressDisplay(progress_display) - - if options.no_progress or options.silent: - progress_display.set_mode(0) - - if options.site_dir: - _load_site_scons_dir(d, options.site_dir) - elif not options.no_site_dir: - _load_site_scons_dir(d) - - if options.include_dir: - sys.path = options.include_dir + sys.path - - # That should cover (most of) the options. Next, set up the variables - # that hold command-line arguments, so the SConscript files that we - # read and execute have access to them. - targets = [] - xmit_args = [] - for a in parser.largs: - if a[0] == '-': - continue - if '=' in a: - xmit_args.append(a) - else: - targets.append(a) - SCons.Script._Add_Targets(targets + parser.rargs) - SCons.Script._Add_Arguments(xmit_args) - - # If stdout is not a tty, replace it with a wrapper object to call flush - # after every write. - # - # Tty devices automatically flush after every newline, so the replacement - # isn't necessary. Furthermore, if we replace sys.stdout, the readline - # module will no longer work. This affects the behavior during - # --interactive mode. --interactive should only be used when stdin and - # stdout refer to a tty. - if not sys.stdout.isatty(): - sys.stdout = SCons.Util.Unbuffered(sys.stdout) - if not sys.stderr.isatty(): - sys.stderr = SCons.Util.Unbuffered(sys.stderr) - - memory_stats.append('before reading SConscript files:') - count_stats.append(('pre-', 'read')) - - # And here's where we (finally) read the SConscript files. - - progress_display("scons: Reading SConscript files ...") - - start_time = time.time() - try: - for script in scripts: - SCons.Script._SConscript._SConscript(fs, script) - except SCons.Errors.StopError, e: - # We had problems reading an SConscript file, such as it - # couldn't be copied in to the VariantDir. Since we're just - # reading SConscript files and haven't started building - # things yet, stop regardless of whether they used -i or -k - # or anything else. - sys.stderr.write("scons: *** %s Stop.\n" % e) - exit_status = 2 - sys.exit(exit_status) - global sconscript_time - sconscript_time = time.time() - start_time - - progress_display("scons: done reading SConscript files.") - - memory_stats.append('after reading SConscript files:') - count_stats.append(('post-', 'read')) - - # Re-{enable,disable} warnings in case they disabled some in - # the SConscript file. - # - # We delay enabling the PythonVersionWarning class until here so that, - # if they explicity disabled it in either in the command line or in - # $SCONSFLAGS, or in the SConscript file, then the search through - # the list of deprecated warning classes will find that disabling - # first and not issue the warning. - SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) - SCons.Warnings.process_warn_strings(options.warn) - - # Now that we've read the SConscript files, we can check for the - # warning about deprecated Python versions--delayed until here - # in case they disabled the warning in the SConscript files. - if python_version_deprecated(): - msg = "Support for pre-2.2 Python (%s) is deprecated.\n" + \ - " If this will cause hardship, contact dev@scons.tigris.org." - SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, - msg % python_version_string()) - - if not options.help: - SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) - - # Now re-parse the command-line options (any to the left of a '--' - # argument, that is) with any user-defined command-line options that - # the SConscript files may have added to the parser object. This will - # emit the appropriate error message and exit if any unknown option - # was specified on the command line. - - parser.preserve_unknown_options = False - parser.parse_args(parser.largs, options) - - if options.help: - help_text = SCons.Script.help_text - if help_text is None: - # They specified -h, but there was no Help() inside the - # SConscript files. Give them the options usage. - raise SConsPrintHelpException - else: - print help_text - print "Use scons -H for help about command-line options." - exit_status = 0 - return - - # Change directory to the top-level SConstruct directory, then tell - # the Node.FS subsystem that we're all done reading the SConscript - # files and calling Repository() and VariantDir() and changing - # directories and the like, so it can go ahead and start memoizing - # the string values of file system nodes. - - fs.chdir(fs.Top) - - SCons.Node.FS.save_strings(1) - - # Now that we've read the SConscripts we can set the options - # that are SConscript settable: - SCons.Node.implicit_cache = options.implicit_cache - SCons.Node.FS.set_duplicate(options.duplicate) - fs.set_max_drift(options.max_drift) - - SCons.Job.explicit_stack_size = options.stack_size - - if options.md5_chunksize: - SCons.Node.FS.File.md5_chunksize = options.md5_chunksize - - platform = SCons.Platform.platform_module() - - if options.interactive: - SCons.Script.Interactive.interact(fs, OptionsParser, options, - targets, target_top) - - else: - - # Build the targets - nodes = _build_targets(fs, options, targets, target_top) - if not nodes: - exit_status = 2 - -def _build_targets(fs, options, targets, target_top): - - global this_build_status - this_build_status = 0 - - progress_display.set_mode(not (options.no_progress or options.silent)) - display.set_mode(not options.silent) - SCons.Action.print_actions = not options.silent - SCons.Action.execute_actions = not options.no_exec - SCons.Node.FS.do_store_info = not options.no_exec - SCons.SConf.dryrun = options.no_exec - - if options.diskcheck: - SCons.Node.FS.set_diskcheck(options.diskcheck) - - SCons.CacheDir.cache_enabled = not options.cache_disable - SCons.CacheDir.cache_debug = options.cache_debug - SCons.CacheDir.cache_force = options.cache_force - SCons.CacheDir.cache_show = options.cache_show - - if options.no_exec: - CleanTask.execute = CleanTask.show - else: - CleanTask.execute = CleanTask.remove - - lookup_top = None - if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: - # They specified targets on the command line or modified - # BUILD_TARGETS in the SConscript file(s), so if they used -u, - # -U or -D, we have to look up targets relative to the top, - # but we build whatever they specified. - if target_top: - lookup_top = fs.Dir(target_top) - target_top = None - - targets = SCons.Script.BUILD_TARGETS - else: - # There are no targets specified on the command line, - # so if they used -u, -U or -D, we may have to restrict - # what actually gets built. - d = None - if target_top: - if options.climb_up == 1: - # -u, local directory and below - target_top = fs.Dir(target_top) - lookup_top = target_top - elif options.climb_up == 2: - # -D, all Default() targets - target_top = None - lookup_top = None - elif options.climb_up == 3: - # -U, local SConscript Default() targets - target_top = fs.Dir(target_top) - def check_dir(x, target_top=target_top): - if hasattr(x, 'cwd') and not x.cwd is None: - cwd = x.cwd.srcnode() - return cwd == target_top - else: - # x doesn't have a cwd, so it's either not a target, - # or not a file, so go ahead and keep it as a default - # target and let the engine sort it out: - return 1 - d = filter(check_dir, SCons.Script.DEFAULT_TARGETS) - SCons.Script.DEFAULT_TARGETS[:] = d - target_top = None - lookup_top = None - - targets = SCons.Script._Get_Default_Targets(d, fs) - - if not targets: - sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") - return None - - def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): - if isinstance(x, SCons.Node.Node): - node = x - else: - node = None - # Why would ltop be None? Unfortunately this happens. - if ltop == None: ltop = '' - # Curdir becomes important when SCons is called with -u, -C, - # or similar option that changes directory, and so the paths - # of targets given on the command line need to be adjusted. - curdir = os.path.join(os.getcwd(), str(ltop)) - for lookup in SCons.Node.arg2nodes_lookups: - node = lookup(x, curdir=curdir) - if node != None: - break - if node is None: - node = fs.Entry(x, directory=ltop, create=1) - if ttop and not node.is_under(ttop): - if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): - node = ttop - else: - node = None - return node - - nodes = filter(None, map(Entry, targets)) - - task_class = BuildTask # default action is to build targets - opening_message = "Building targets ..." - closing_message = "done building targets." - if options.keep_going: - failure_message = "done building targets (errors occurred during build)." - else: - failure_message = "building terminated because of errors." - if options.question: - task_class = QuestionTask - try: - if options.clean: - task_class = CleanTask - opening_message = "Cleaning targets ..." - closing_message = "done cleaning targets." - if options.keep_going: - failure_message = "done cleaning targets (errors occurred during clean)." - else: - failure_message = "cleaning terminated because of errors." - except AttributeError: - pass - - task_class.progress = ProgressObject - - if options.random: - def order(dependencies): - """Randomize the dependencies.""" - import random - # This is cribbed from the implementation of - # random.shuffle() in Python 2.X. - d = dependencies - for i in xrange(len(d)-1, 0, -1): - j = int(random.random() * (i+1)) - d[i], d[j] = d[j], d[i] - return d - else: - def order(dependencies): - """Leave the order of dependencies alone.""" - return dependencies - - if options.taskmastertrace_file == '-': - tmtrace = sys.stdout - elif options.taskmastertrace_file: - tmtrace = open(options.taskmastertrace_file, 'wb') - else: - tmtrace = None - taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) - - # Let the BuildTask objects get at the options to respond to the - # various print_* settings, tree_printer list, etc. - BuildTask.options = options - - global num_jobs - num_jobs = options.num_jobs - jobs = SCons.Job.Jobs(num_jobs, taskmaster) - if num_jobs > 1: - msg = None - if jobs.num_jobs == 1: - msg = "parallel builds are unsupported by this version of Python;\n" + \ - "\tignoring -j or num_jobs option.\n" - elif sys.platform == 'win32': - msg = fetch_win32_parallel_msg() - if msg: - SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) - - memory_stats.append('before building targets:') - count_stats.append(('pre-', 'build')) - - def jobs_postfunc( - jobs=jobs, - options=options, - closing_message=closing_message, - failure_message=failure_message - ): - if jobs.were_interrupted(): - progress_display("scons: Build interrupted.") - global exit_status - global this_build_status - exit_status = 2 - this_build_status = 2 - - if this_build_status: - progress_display("scons: " + failure_message) - else: - progress_display("scons: " + closing_message) - if not options.no_exec: - if jobs.were_interrupted(): - progress_display("scons: writing .sconsign file.") - SCons.SConsign.write() - - progress_display("scons: " + opening_message) - jobs.run(postfunc = jobs_postfunc) - - memory_stats.append('after building targets:') - count_stats.append(('post-', 'build')) - - return nodes - -def _exec_main(parser, values): - sconsflags = os.environ.get('SCONSFLAGS', '') - all_args = string.split(sconsflags) + sys.argv[1:] - - options, args = parser.parse_args(all_args, values) - - if type(options.debug) == type([]) and "pdb" in options.debug: - import pdb - pdb.Pdb().runcall(_main, parser) - elif options.profile_file: - try: - from cProfile import Profile - except ImportError, e: - from profile import Profile - - # Some versions of Python 2.4 shipped a profiler that had the - # wrong 'c_exception' entry in its dispatch table. Make sure - # we have the right one. (This may put an unnecessary entry - # in the table in earlier versions of Python, but its presence - # shouldn't hurt anything). - try: - dispatch = Profile.dispatch - except AttributeError: - pass - else: - dispatch['c_exception'] = Profile.trace_dispatch_return - - prof = Profile() - try: - prof.runcall(_main, parser) - except SConsPrintHelpException, e: - prof.dump_stats(options.profile_file) - raise e - except SystemExit: - pass - prof.dump_stats(options.profile_file) - else: - _main(parser) - -def main(): - global OptionsParser - global exit_status - global first_command_start - - # Check up front for a Python version we do not support. We - # delay the check for deprecated Python versions until later, - # after the SConscript files have been read, in case they - # disable that warning. - if python_version_unsupported(): - msg = "scons: *** SCons version %s does not run under Python version %s.\n" - sys.stderr.write(msg % (SCons.__version__, python_version_string())) - sys.exit(1) - - parts = ["SCons by Steven Knight et al.:\n"] - try: - import __main__ - parts.append(version_string("script", __main__)) - except (ImportError, AttributeError): - # On Windows there is no scons.py, so there is no - # __main__.__version__, hence there is no script version. - pass - parts.append(version_string("engine", SCons)) - parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation") - version = string.join(parts, '') - - import SConsOptions - parser = SConsOptions.Parser(version) - values = SConsOptions.SConsValues(parser.get_default_values()) - - OptionsParser = parser - - try: - _exec_main(parser, values) - except SystemExit, s: - if s: - exit_status = s - except KeyboardInterrupt: - print("scons: Build interrupted.") - sys.exit(2) - except SyntaxError, e: - _scons_syntax_error(e) - except SCons.Errors.InternalError: - _scons_internal_error() - except SCons.Errors.UserError, e: - _scons_user_error(e) - except SConsPrintHelpException: - parser.print_help() - exit_status = 0 - except SCons.Errors.BuildError, e: - exit_status = e.exitstatus - except: - # An exception here is likely a builtin Python exception Python - # code in an SConscript file. Show them precisely what the - # problem was and where it happened. - SCons.Script._SConscript.SConscript_exception() - sys.exit(2) - - memory_stats.print_stats() - count_stats.print_stats() - - if print_objects: - SCons.Debug.listLoggedInstances('*') - #SCons.Debug.dumpLoggedInstances('*') - - if print_memoizer: - SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") - - # Dump any development debug info that may have been enabled. - # These are purely for internal debugging during development, so - # there's no need to control them with --debug= options; they're - # controlled by changing the source code. - SCons.Debug.dump_caller_counts() - SCons.Taskmaster.dump_stats() - - if print_time: - total_time = time.time() - SCons.Script.start_time - if num_jobs == 1: - ct = cumulative_command_time - else: - if last_command_end is None or first_command_start is None: - ct = 0.0 - else: - ct = last_command_end - first_command_start - scons_time = total_time - sconscript_time - ct - print "Total build time: %f seconds"%total_time - print "Total SConscript file execution time: %f seconds"%sconscript_time - print "Total SCons execution time: %f seconds"%scons_time - print "Total command execution time: %f seconds"%ct - - sys.exit(exit_status) diff --git a/tools/scons/scons-local-1.2.0/SCons/Script/SConsOptions.py b/tools/scons/scons-local-1.2.0/SCons/Script/SConsOptions.py deleted file mode 100644 index 636fd2024e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Script/SConsOptions.py +++ /dev/null @@ -1,940 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Script/SConsOptions.py 3842 2008/12/20 22:59:52 scons" - -import optparse -import re -import string -import sys -import textwrap - -try: - no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') -except re.error: - # Pre-2.0 Python versions don't have the (?<= negative - # look-behind assertion. - no_hyphen_re = re.compile(r'(\s+|-*\w{2,}-(?=\w{2,}))') - -try: - from gettext import gettext -except ImportError: - def gettext(message): - return message -_ = gettext - -import SCons.Node.FS -import SCons.Warnings - -OptionValueError = optparse.OptionValueError -SUPPRESS_HELP = optparse.SUPPRESS_HELP - -diskcheck_all = SCons.Node.FS.diskcheck_types() - -def diskcheck_convert(value): - if value is None: - return [] - if not SCons.Util.is_List(value): - value = string.split(value, ',') - result = [] - for v in map(string.lower, value): - if v == 'all': - result = diskcheck_all - elif v == 'none': - result = [] - elif v in diskcheck_all: - result.append(v) - else: - raise ValueError, v - return result - -class SConsValues(optparse.Values): - """ - Holder class for uniform access to SCons options, regardless - of whether or not they can be set on the command line or in the - SConscript files (using the SetOption() function). - - A SCons option value can originate three different ways: - - 1) set on the command line; - 2) set in an SConscript file; - 3) the default setting (from the the op.add_option() - calls in the Parser() function, below). - - The command line always overrides a value set in a SConscript file, - which in turn always overrides default settings. Because we want - to support user-specified options in the SConscript file itself, - though, we may not know about all of the options when the command - line is first parsed, so we can't make all the necessary precedence - decisions at the time the option is configured. - - The solution implemented in this class is to keep these different sets - of settings separate (command line, SConscript file, and default) - and to override the __getattr__() method to check them in turn. - This should allow the rest of the code to just fetch values as - attributes of an instance of this class, without having to worry - about where they came from. - - Note that not all command line options are settable from SConscript - files, and the ones that are must be explicitly added to the - "settable" list in this class, and optionally validated and coerced - in the set_option() method. - """ - - def __init__(self, defaults): - self.__dict__['__defaults__'] = defaults - self.__dict__['__SConscript_settings__'] = {} - - def __getattr__(self, attr): - """ - Fetches an options value, checking first for explicit settings - from the command line (which are direct attributes), then the - SConscript file settings, then the default values. - """ - try: - return self.__dict__[attr] - except KeyError: - try: - return self.__dict__['__SConscript_settings__'][attr] - except KeyError: - return getattr(self.__dict__['__defaults__'], attr) - - settable = [ - 'clean', - 'diskcheck', - 'duplicate', - 'help', - 'implicit_cache', - 'max_drift', - 'md5_chunksize', - 'no_exec', - 'num_jobs', - 'random', - 'stack_size', - 'warn', - ] - - def set_option(self, name, value): - """ - Sets an option from an SConscript file. - """ - if not name in self.settable: - raise SCons.Errors.UserError, "This option is not settable from a SConscript file: %s"%name - - if name == 'num_jobs': - try: - value = int(value) - if value < 1: - raise ValueError - except ValueError: - raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value) - elif name == 'max_drift': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) - elif name == 'duplicate': - try: - value = str(value) - except ValueError: - raise SCons.Errors.UserError, "A string is required: %s"%repr(value) - if not value in SCons.Node.FS.Valid_Duplicates: - raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value - # Set the duplicate style right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - elif name == 'diskcheck': - try: - value = diskcheck_convert(value) - except ValueError, v: - raise SCons.Errors.UserError, "Not a valid diskcheck value: %s"%v - if not self.__dict__.has_key('diskcheck'): - # No --diskcheck= option was specified on the command line. - # Set this right away so it can affect the rest of the - # file/Node lookups while processing the SConscript files. - SCons.Node.FS.set_diskcheck(value) - elif name == 'stack_size': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) - elif name == 'md5_chunksize': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) - elif name == 'warn': - if SCons.Util.is_String(value): - value = [value] - value = self.__SConscript_settings__.get(name, []) + value - SCons.Warnings.process_warn_strings(value) - - self.__SConscript_settings__[name] = value - -class SConsOption(optparse.Option): - def convert_value(self, opt, value): - if value is not None: - if self.nargs in (1, '?'): - return self.check_value(opt, value) - else: - return tuple(map(lambda v, o=opt, s=self: s.check_value(o, v), value)) - - def process(self, opt, value, values, parser): - - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - def _check_nargs_optional(self): - if self.nargs == '?' and self._short_opts: - fmt = "option %s: nargs='?' is incompatible with short options" - raise SCons.Errors.UserError, fmt % self._short_opts[0] - - try: - _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS - - _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS - - except AttributeError: - # optparse.Option had no CONST_ACTIONS before Python 2.5. - - _orig_CONST_ACTIONS = ("store_const",) - - def _check_const(self): - if self.action not in self.CONST_ACTIONS and self.const is not None: - raise OptionError( - "'const' must not be supplied for action %r" % self.action, - self) - - # optparse.Option collects its list of unbound check functions - # up front. This sucks because it means we can't just override - # the _check_const() function like a normal method, we have to - # actually replace it in the list. This seems to be the most - # straightforward way to do that. - - _orig_CHECK_METHODS = [optparse.Option._check_action, - optparse.Option._check_type, - optparse.Option._check_choice, - optparse.Option._check_dest, - _check_const, - optparse.Option._check_nargs, - optparse.Option._check_callback] - - CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] - - CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS - -class SConsOptionGroup(optparse.OptionGroup): - """ - A subclass for SCons-specific option groups. - - The only difference between this and the base class is that we print - the group's help text flush left, underneath their own title but - lined up with the normal "SCons Options". - """ - def format_help(self, formatter): - """ - Format an option group's help text, outdenting the title so it's - flush with the "SCons Options" title we print at the top. - """ - formatter.dedent() - result = formatter.format_heading(self.title) - formatter.indent() - result = result + optparse.OptionContainer.format_help(self, formatter) - return result - -class SConsOptionParser(optparse.OptionParser): - preserve_unknown_options = False - - def error(self, msg): - self.print_usage(sys.stderr) - sys.stderr.write("SCons error: %s\n" % msg) - sys.exit(2) - - def _process_long_opt(self, rargs, values): - """ - SCons-specific processing of long options. - - This is copied directly from the normal - optparse._process_long_opt() method, except that, if configured - to do so, we catch the exception thrown when an unknown option - is encountered and just stick it back on the "leftover" arguments - for later (re-)processing. - """ - arg = rargs.pop(0) - - # Value explicitly attached to arg? Pretend it's the next - # argument. - if "=" in arg: - (opt, next_arg) = string.split(arg, "=", 1) - rargs.insert(0, next_arg) - had_explicit_value = True - else: - opt = arg - had_explicit_value = False - - try: - opt = self._match_long_opt(opt) - except optparse.BadOptionError: - if self.preserve_unknown_options: - # SCons-specific: if requested, add unknown options to - # the "leftover arguments" list for later processing. - self.largs.append(arg) - if had_explicit_value: - # The unknown option will be re-processed later, - # so undo the insertion of the explicit value. - rargs.pop(0) - return - raise - - option = self._long_opt[opt] - if option.takes_value(): - nargs = option.nargs - if nargs == '?': - if had_explicit_value: - value = rargs.pop(0) - else: - value = option.const - elif len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - elif had_explicit_value: - self.error(_("%s option does not take a value") % opt) - - else: - value = None - - option.process(opt, value, values, self) - - def add_local_option(self, *args, **kw): - """ - Adds a local option to the parser. - - This is initiated by a SetOption() call to add a user-defined - command-line option. We add the option to a separate option - group for the local options, creating the group if necessary. - """ - try: - group = self.local_option_group - except AttributeError: - group = SConsOptionGroup(self, 'Local Options') - group = self.add_option_group(group) - self.local_option_group = group - - result = apply(group.add_option, args, kw) - - if result: - # The option was added succesfully. We now have to add the - # default value to our object that holds the default values - # (so that an attempt to fetch the option's attribute will - # yield the default value when not overridden) and then - # we re-parse the leftover command-line options, so that - # any value overridden on the command line is immediately - # available if the user turns around and does a GetOption() - # right away. - setattr(self.values.__defaults__, result.dest, result.default) - self.parse_args(self.largs, self.values) - - return result - -class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): - def format_usage(self, usage): - return "usage: %s\n" % usage - - def format_heading(self, heading): - """ - This translates any heading of "options" or "Options" into - "SCons Options." Unfortunately, we have to do this here, - because those titles are hard-coded in the optparse calls. - """ - if heading == 'options': - # The versions of optparse.py shipped with Pythons 2.3 and - # 2.4 pass this in uncapitalized; override that so we get - # consistent output on all versions. - heading = "Options" - if heading == 'Options': - heading = "SCons Options" - return optparse.IndentedHelpFormatter.format_heading(self, heading) - - def format_option(self, option): - """ - A copy of the normal optparse.IndentedHelpFormatter.format_option() - method. This has been snarfed so we can modify text wrapping to - out liking: - - -- add our own regular expression that doesn't break on hyphens - (so things like --no-print-directory don't get broken); - - -- wrap the list of options themselves when it's too long - (the wrapper.fill(opts) call below); - - -- set the subsequent_indent when wrapping the help_text. - """ - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("-x", or "-fFILENAME, --file=FILENAME") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # -x turn on expert mode - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # -fFILENAME, --file=FILENAME - # read data from FILENAME - result = [] - - try: - opts = self.option_strings[option] - except AttributeError: - # The Python 2.3 version of optparse attaches this to - # to the option argument, not to this object. - opts = option.option_strings - - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - wrapper = textwrap.TextWrapper(width=self.width, - initial_indent = ' ', - subsequent_indent = ' ') - wrapper.wordsep_re = no_hyphen_re - opts = wrapper.fill(opts) + '\n' - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - - try: - expand_default = self.expand_default - except AttributeError: - # The HelpFormatter base class in the Python 2.3 version - # of optparse has no expand_default() method. - help_text = option.help - else: - help_text = expand_default(option) - - # SCons: indent every line of the help text but the first. - wrapper = textwrap.TextWrapper(width=self.help_width, - subsequent_indent = ' ') - wrapper.wordsep_re = no_hyphen_re - help_lines = wrapper.wrap(help_text) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - for line in help_lines[1:]: - result.append("%*s%s\n" % (self.help_position, "", line)) - elif opts[-1] != "\n": - result.append("\n") - return string.join(result, "") - - # For consistent help output across Python versions, we provide a - # subclass copy of format_option_strings() and these two variables. - # This is necessary (?) for Python2.3, which otherwise concatenates - # a short option with its metavar. - _short_opt_fmt = "%s %s" - _long_opt_fmt = "%s=%s" - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - if option.takes_value(): - metavar = option.metavar or string.upper(option.dest) - short_opts = [] - for sopt in option._short_opts: - short_opts.append(self._short_opt_fmt % (sopt, metavar)) - long_opts = [] - for lopt in option._long_opts: - long_opts.append(self._long_opt_fmt % (lopt, metavar)) - else: - short_opts = option._short_opts - long_opts = option._long_opts - - if self.short_first: - opts = short_opts + long_opts - else: - opts = long_opts + short_opts - - return string.join(opts, ", ") - -def Parser(version): - """ - Returns an options parser object initialized with the standard - SCons options. - """ - - formatter = SConsIndentedHelpFormatter(max_help_position=30) - - op = SConsOptionParser(option_class=SConsOption, - add_help_option=False, - formatter=formatter, - usage="usage: scons [OPTION] [TARGET] ...",) - - op.preserve_unknown_options = True - op.version = version - - # Add the options to the parser we just created. - # - # These are in the order we want them to show up in the -H help - # text, basically alphabetical. Each op.add_option() call below - # should have a consistent format: - # - # op.add_option("-L", "--long-option-name", - # nargs=1, type="string", - # dest="long_option_name", default='foo', - # action="callback", callback=opt_long_option, - # help="help text goes here", - # metavar="VAR") - # - # Even though the optparse module constructs reasonable default - # destination names from the long option names, we're going to be - # explicit about each one for easier readability and so this code - # will at least show up when grepping the source for option attribute - # names, or otherwise browsing the source code. - - # options ignored for compatibility - def opt_ignore(option, opt, value, parser): - sys.stderr.write("Warning: ignoring %s option\n" % opt) - op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", - "--environment-overrides", - "--no-keep-going", - "--no-print-directory", - "--print-directory", - "--stop", - "--touch", - action="callback", callback=opt_ignore, - help="Ignored for compatibility.") - - op.add_option('-c', '--clean', '--remove', - dest="clean", default=False, - action="store_true", - help="Remove specified targets and dependencies.") - - op.add_option('-C', '--directory', - nargs=1, type="string", - dest="directory", default=[], - action="append", - help="Change to DIR before doing anything.", - metavar="DIR") - - op.add_option('--cache-debug', - nargs=1, - dest="cache_debug", default=None, - action="store", - help="Print CacheDir debug info to FILE.", - metavar="FILE") - - op.add_option('--cache-disable', '--no-cache', - dest='cache_disable', default=False, - action="store_true", - help="Do not retrieve built targets from CacheDir.") - - op.add_option('--cache-force', '--cache-populate', - dest='cache_force', default=False, - action="store_true", - help="Copy already-built targets into the CacheDir.") - - op.add_option('--cache-show', - dest='cache_show', default=False, - action="store_true", - help="Print build actions for files from CacheDir.") - - config_options = ["auto", "force" ,"cache"] - - def opt_config(option, opt, value, parser, c_options=config_options): - if not value in c_options: - raise OptionValueError("Warning: %s is not a valid config type" % value) - setattr(parser.values, option.dest, value) - opt_config_help = "Controls Configure subsystem: %s." \ - % string.join(config_options, ", ") - op.add_option('--config', - nargs=1, type="string", - dest="config", default="auto", - action="callback", callback=opt_config, - help = opt_config_help, - metavar="MODE") - - op.add_option('-D', - dest="climb_up", default=None, - action="store_const", const=2, - help="Search up directory tree for SConstruct, " - "build all Default() targets.") - - deprecated_debug_options = { - "dtree" : '; please use --tree=derived instead', - "nomemoizer" : ' and has no effect', - "stree" : '; please use --tree=all,status instead', - "tree" : '; please use --tree=all instead', - } - - debug_options = ["count", "explain", "findlibs", - "includes", "memoizer", "memory", "objects", - "pdb", "presub", "stacktrace", - "time"] + deprecated_debug_options.keys() - - def opt_debug(option, opt, value, parser, - debug_options=debug_options, - deprecated_debug_options=deprecated_debug_options): - if value in debug_options: - parser.values.debug.append(value) - if value in deprecated_debug_options.keys(): - try: - parser.values.delayed_warnings - except AttributeError: - parser.values.delayed_warnings = [] - msg = deprecated_debug_options[value] - w = "The --debug=%s option is deprecated%s." % (value, msg) - t = (SCons.Warnings.DeprecatedWarning, w) - parser.values.delayed_warnings.append(t) - else: - raise OptionValueError("Warning: %s is not a valid debug type" % value) - opt_debug_help = "Print various types of debugging information: %s." \ - % string.join(debug_options, ", ") - op.add_option('--debug', - nargs=1, type="string", - dest="debug", default=[], - action="callback", callback=opt_debug, - help=opt_debug_help, - metavar="TYPE") - - def opt_diskcheck(option, opt, value, parser): - try: - diskcheck_value = diskcheck_convert(value) - except ValueError, e: - raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e) - setattr(parser.values, option.dest, diskcheck_value) - - op.add_option('--diskcheck', - nargs=1, type="string", - dest='diskcheck', default=None, - action="callback", callback=opt_diskcheck, - help="Enable specific on-disk checks.", - metavar="TYPE") - - def opt_duplicate(option, opt, value, parser): - if not value in SCons.Node.FS.Valid_Duplicates: - raise OptionValueError("`%s' is not a valid duplication style." % value) - setattr(parser.values, option.dest, value) - # Set the duplicate style right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - - opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ - + string.join(SCons.Node.FS.Valid_Duplicates, ", ") - - op.add_option('--duplicate', - nargs=1, type="string", - dest="duplicate", default='hard-soft-copy', - action="callback", callback=opt_duplicate, - help=opt_duplicate_help) - - op.add_option('-f', '--file', '--makefile', '--sconstruct', - nargs=1, type="string", - dest="file", default=[], - action="append", - help="Read FILE as the top-level SConstruct file.") - - op.add_option('-h', '--help', - dest="help", default=False, - action="store_true", - help="Print defined help message, or this one.") - - op.add_option("-H", "--help-options", - action="help", - help="Print this message and exit.") - - op.add_option('-i', '--ignore-errors', - dest='ignore_errors', default=False, - action="store_true", - help="Ignore errors from build actions.") - - op.add_option('-I', '--include-dir', - nargs=1, - dest='include_dir', default=[], - action="append", - help="Search DIR for imported Python modules.", - metavar="DIR") - - op.add_option('--implicit-cache', - dest='implicit_cache', default=False, - action="store_true", - help="Cache implicit dependencies") - - def opt_implicit_deps(option, opt, value, parser): - setattr(parser.values, 'implicit_cache', True) - setattr(parser.values, option.dest, True) - - op.add_option('--implicit-deps-changed', - dest="implicit_deps_changed", default=False, - action="callback", callback=opt_implicit_deps, - help="Ignore cached implicit dependencies.") - - op.add_option('--implicit-deps-unchanged', - dest="implicit_deps_unchanged", default=False, - action="callback", callback=opt_implicit_deps, - help="Ignore changes in implicit dependencies.") - - op.add_option('--interact', '--interactive', - dest='interactive', default=False, - action="store_true", - help="Run in interactive mode.") - - op.add_option('-j', '--jobs', - nargs=1, type="int", - dest="num_jobs", default=1, - action="store", - help="Allow N jobs at once.", - metavar="N") - - op.add_option('-k', '--keep-going', - dest='keep_going', default=False, - action="store_true", - help="Keep going when a target can't be made.") - - op.add_option('--max-drift', - nargs=1, type="int", - dest='max_drift', default=SCons.Node.FS.default_max_drift, - action="store", - help="Set maximum system clock drift to N seconds.", - metavar="N") - - op.add_option('--md5-chunksize', - nargs=1, type="int", - dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, - action="store", - help="Set chunk-size for MD5 signature computation to N kilobytes.", - metavar="N") - - op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', - dest='no_exec', default=False, - action="store_true", - help="Don't build; just print commands.") - - op.add_option('--no-site-dir', - dest='no_site_dir', default=False, - action="store_true", - help="Don't search or use the usual site_scons dir.") - - op.add_option('--profile', - nargs=1, - dest="profile_file", default=None, - action="store", - help="Profile SCons and put results in FILE.", - metavar="FILE") - - op.add_option('-q', '--question', - dest="question", default=False, - action="store_true", - help="Don't build; exit status says if up to date.") - - op.add_option('-Q', - dest='no_progress', default=False, - action="store_true", - help="Suppress \"Reading/Building\" progress messages.") - - op.add_option('--random', - dest="random", default=False, - action="store_true", - help="Build dependencies in random order.") - - op.add_option('-s', '--silent', '--quiet', - dest="silent", default=False, - action="store_true", - help="Don't print commands.") - - op.add_option('--site-dir', - nargs=1, - dest='site_dir', default=None, - action="store", - help="Use DIR instead of the usual site_scons dir.", - metavar="DIR") - - op.add_option('--stack-size', - nargs=1, type="int", - dest='stack_size', - action="store", - help="Set the stack size of the threads used to run jobs to N kilobytes.", - metavar="N") - - op.add_option('--taskmastertrace', - nargs=1, - dest="taskmastertrace_file", default=None, - action="store", - help="Trace Node evaluation to FILE.", - metavar="FILE") - - tree_options = ["all", "derived", "prune", "status"] - - def opt_tree(option, opt, value, parser, tree_options=tree_options): - import Main - tp = Main.TreePrinter() - for o in string.split(value, ','): - if o == 'all': - tp.derived = False - elif o == 'derived': - tp.derived = True - elif o == 'prune': - tp.prune = True - elif o == 'status': - tp.status = True - else: - raise OptionValueError("Warning: %s is not a valid --tree option" % o) - parser.values.tree_printers.append(tp) - - opt_tree_help = "Print a dependency tree in various formats: %s." \ - % string.join(tree_options, ", ") - - op.add_option('--tree', - nargs=1, type="string", - dest="tree_printers", default=[], - action="callback", callback=opt_tree, - help=opt_tree_help, - metavar="OPTIONS") - - op.add_option('-u', '--up', '--search-up', - dest="climb_up", default=0, - action="store_const", const=1, - help="Search up directory tree for SConstruct, " - "build targets at or below current directory.") - - op.add_option('-U', - dest="climb_up", default=0, - action="store_const", const=3, - help="Search up directory tree for SConstruct, " - "build Default() targets from local SConscript.") - - def opt_version(option, opt, value, parser): - sys.stdout.write(parser.version + '\n') - sys.exit(0) - op.add_option("-v", "--version", - action="callback", callback=opt_version, - help="Print the SCons version number and exit.") - - def opt_warn(option, opt, value, parser, tree_options=tree_options): - if SCons.Util.is_String(value): - value = string.split(value, ',') - parser.values.warn.extend(value) - - op.add_option('--warn', '--warning', - nargs=1, type="string", - dest="warn", default=[], - action="callback", callback=opt_warn, - help="Enable or disable warnings.", - metavar="WARNING-SPEC") - - op.add_option('-Y', '--repository', '--srcdir', - nargs=1, - dest="repository", default=[], - action="append", - help="Search REPOSITORY for source and target files.") - - # Options from Make and Cons classic that we do not yet support, - # but which we may support someday and whose (potential) meanings - # we don't want to change. These all get a "the -X option is not - # yet implemented" message and don't show up in the help output. - - def opt_not_yet(option, opt, value, parser): - msg = "Warning: the %s option is not yet implemented\n" % opt - sys.stderr.write(msg) - sys.exit(0) - - - op.add_option('-l', '--load-average', '--max-load', - nargs=1, type="int", - dest="load_average", default=0, - action="callback", callback=opt_not_yet, - # action="store", - # help="Don't start multiple jobs unless load is below " - # "LOAD-AVERAGE." - help=SUPPRESS_HELP) - op.add_option('--list-actions', - dest="list_actions", - action="callback", callback=opt_not_yet, - # help="Don't build; list files and build actions." - help=SUPPRESS_HELP) - op.add_option('--list-derived', - dest="list_derived", - action="callback", callback=opt_not_yet, - # help="Don't build; list files that would be built." - help=SUPPRESS_HELP) - op.add_option('--list-where', - dest="list_where", - action="callback", callback=opt_not_yet, - # help="Don't build; list files and where defined." - help=SUPPRESS_HELP) - op.add_option('-o', '--old-file', '--assume-old', - nargs=1, type="string", - dest="old_file", default=[], - action="callback", callback=opt_not_yet, - # action="append", - # help = "Consider FILE to be old; don't rebuild it." - help=SUPPRESS_HELP) - op.add_option('--override', - nargs=1, type="string", - action="callback", callback=opt_not_yet, - dest="override", - # help="Override variables as specified in FILE." - help=SUPPRESS_HELP) - op.add_option('-p', - action="callback", callback=opt_not_yet, - dest="p", - # help="Print internal environments/objects." - help=SUPPRESS_HELP) - op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', - action="callback", callback=opt_not_yet, - dest="no_builtin_rules", - # help="Clear default environments and variables." - help=SUPPRESS_HELP) - op.add_option('--write-filenames', - nargs=1, type="string", - dest="write_filenames", - action="callback", callback=opt_not_yet, - # help="Write all filenames examined into FILE." - help=SUPPRESS_HELP) - op.add_option('-W', '--new-file', '--assume-new', '--what-if', - nargs=1, type="string", - dest="new_file", - action="callback", callback=opt_not_yet, - # help="Consider FILE to be changed." - help=SUPPRESS_HELP) - op.add_option('--warn-undefined-variables', - dest="warn_undefined_variables", - action="callback", callback=opt_not_yet, - # help="Warn when an undefined variable is referenced." - help=SUPPRESS_HELP) - - return op diff --git a/tools/scons/scons-local-1.2.0/SCons/Script/SConscript.py b/tools/scons/scons-local-1.2.0/SCons/Script/SConscript.py deleted file mode 100644 index c52c9798a5..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Script/SConscript.py +++ /dev/null @@ -1,632 +0,0 @@ -"""SCons.Script.SConscript - -This module defines the Python API provided to SConscript and SConstruct -files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Script/SConscript.py 3842 2008/12/20 22:59:52 scons" - -import SCons -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Environment -import SCons.Errors -import SCons.Node -import SCons.Node.Alias -import SCons.Node.FS -import SCons.Platform -import SCons.SConf -import SCons.Script.Main -import SCons.Tool -import SCons.Util - -import os -import os.path -import re -import string -import sys -import traceback -import types -import UserList - -# The following variables used to live in this module. Some -# SConscript files out there may have referred to them directly as -# SCons.Script.SConscript.*. This is now supported by some special -# handling towards the bottom of the SConscript.__init__.py module. -#Arguments = {} -#ArgList = [] -#BuildTargets = TargetList() -#CommandLineTargets = [] -#DefaultTargets = [] - -class SConscriptReturn(Exception): - pass - -launch_dir = os.path.abspath(os.curdir) - -GlobalDict = None - -# global exports set by Export(): -global_exports = {} - -# chdir flag -sconscript_chdir = 1 - -def get_calling_namespaces(): - """Return the locals and globals for the function that called - into this module in the current call stack.""" - try: 1/0 - except ZeroDivisionError: - # Don't start iterating with the current stack-frame to - # prevent creating reference cycles (f_back is safe). - frame = sys.exc_info()[2].tb_frame.f_back - - # Find the first frame that *isn't* from this file. This means - # that we expect all of the SCons frames that implement an Export() - # or SConscript() call to be in this file, so that we can identify - # the first non-Script.SConscript frame as the user's local calling - # environment, and the locals and globals dictionaries from that - # frame as the calling namespaces. See the comment below preceding - # the DefaultEnvironmentCall block for even more explanation. - while frame.f_globals.get("__name__") == __name__: - frame = frame.f_back - - return frame.f_locals, frame.f_globals - - -def compute_exports(exports): - """Compute a dictionary of exports given one of the parameters - to the Export() function or the exports argument to SConscript().""" - - loc, glob = get_calling_namespaces() - - retval = {} - try: - for export in exports: - if SCons.Util.is_Dict(export): - retval.update(export) - else: - try: - retval[export] = loc[export] - except KeyError: - retval[export] = glob[export] - except KeyError, x: - raise SCons.Errors.UserError, "Export of non-existent variable '%s'"%x - - return retval - -class Frame: - """A frame on the SConstruct/SConscript call stack""" - def __init__(self, fs, exports, sconscript): - self.globals = BuildDefaultGlobals() - self.retval = None - self.prev_dir = fs.getcwd() - self.exports = compute_exports(exports) # exports from the calling SConscript - # make sure the sconscript attr is a Node. - if isinstance(sconscript, SCons.Node.Node): - self.sconscript = sconscript - elif sconscript == '-': - self.sconscript = None - else: - self.sconscript = fs.File(str(sconscript)) - -# the SConstruct/SConscript call stack: -call_stack = [] - -# For documentation on the methods in this file, see the scons man-page - -def Return(*vars, **kw): - retval = [] - try: - fvars = SCons.Util.flatten(vars) - for var in fvars: - for v in string.split(var): - retval.append(call_stack[-1].globals[v]) - except KeyError, x: - raise SCons.Errors.UserError, "Return of non-existent variable '%s'"%x - - if len(retval) == 1: - call_stack[-1].retval = retval[0] - else: - call_stack[-1].retval = tuple(retval) - - stop = kw.get('stop', True) - - if stop: - raise SConscriptReturn - - -stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) - -def _SConscript(fs, *files, **kw): - top = fs.Top - sd = fs.SConstruct_dir.rdir() - exports = kw.get('exports', []) - - # evaluate each SConscript file - results = [] - for fn in files: - call_stack.append(Frame(fs, exports, fn)) - old_sys_path = sys.path - try: - SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 - if fn == "-": - exec sys.stdin in call_stack[-1].globals - else: - if isinstance(fn, SCons.Node.Node): - f = fn - else: - f = fs.File(str(fn)) - _file_ = None - - # Change directory to the top of the source - # tree to make sure the os's cwd and the cwd of - # fs match so we can open the SConscript. - fs.chdir(top, change_os_dir=1) - if f.rexists(): - _file_ = open(f.rfile().get_abspath(), "r") - elif f.has_src_builder(): - # The SConscript file apparently exists in a source - # code management system. Build it, but then clear - # the builder so that it doesn't get built *again* - # during the actual build phase. - f.build() - f.built() - f.builder_set(None) - if f.exists(): - _file_ = open(f.get_abspath(), "r") - if _file_: - # Chdir to the SConscript directory. Use a path - # name relative to the SConstruct file so that if - # we're using the -f option, we're essentially - # creating a parallel SConscript directory structure - # in our local directory tree. - # - # XXX This is broken for multiple-repository cases - # where the SConstruct and SConscript files might be - # in different Repositories. For now, cross that - # bridge when someone comes to it. - try: - src_dir = kw['src_dir'] - except KeyError: - ldir = fs.Dir(f.dir.get_path(sd)) - else: - ldir = fs.Dir(src_dir) - if not ldir.is_under(f.dir): - # They specified a source directory, but - # it's above the SConscript directory. - # Do the sensible thing and just use the - # SConcript directory. - ldir = fs.Dir(f.dir.get_path(sd)) - try: - fs.chdir(ldir, change_os_dir=sconscript_chdir) - except OSError: - # There was no local directory, so we should be - # able to chdir to the Repository directory. - # Note that we do this directly, not through - # fs.chdir(), because we still need to - # interpret the stuff within the SConscript file - # relative to where we are logically. - fs.chdir(ldir, change_os_dir=0) - # TODO Not sure how to handle src_dir here - os.chdir(f.rfile().dir.get_abspath()) - - # Append the SConscript directory to the beginning - # of sys.path so Python modules in the SConscript - # directory can be easily imported. - sys.path = [ f.dir.get_abspath() ] + sys.path - - # This is the magic line that actually reads up - # and executes the stuff in the SConscript file. - # The locals for this frame contain the special - # bottom-of-the-stack marker so that any - # exceptions that occur when processing this - # SConscript can base the printed frames at this - # level and not show SCons internals as well. - call_stack[-1].globals.update({stack_bottom:1}) - old_file = call_stack[-1].globals.get('__file__') - try: - del call_stack[-1].globals['__file__'] - except KeyError: - pass - try: - try: - exec _file_ in call_stack[-1].globals - except SConscriptReturn: - pass - finally: - if old_file is not None: - call_stack[-1].globals.update({__file__:old_file}) - else: - SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, - "Ignoring missing SConscript '%s'" % f.path) - - finally: - SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 - sys.path = old_sys_path - frame = call_stack.pop() - try: - fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) - except OSError: - # There was no local directory, so chdir to the - # Repository directory. Like above, we do this - # directly. - fs.chdir(frame.prev_dir, change_os_dir=0) - rdir = frame.prev_dir.rdir() - rdir._create() # Make sure there's a directory there. - try: - os.chdir(rdir.get_abspath()) - except OSError, e: - # We still couldn't chdir there, so raise the error, - # but only if actions are being executed. - # - # If the -n option was used, the directory would *not* - # have been created and we should just carry on and - # let things muddle through. This isn't guaranteed - # to work if the SConscript files are reading things - # from disk (for example), but it should work well - # enough for most configurations. - if SCons.Action.execute_actions: - raise e - - results.append(frame.retval) - - # if we only have one script, don't return a tuple - if len(results) == 1: - return results[0] - else: - return tuple(results) - -def SConscript_exception(file=sys.stderr): - """Print an exception stack trace just for the SConscript file(s). - This will show users who have Python errors where the problem is, - without cluttering the output with all of the internal calls leading - up to where we exec the SConscript.""" - exc_type, exc_value, exc_tb = sys.exc_info() - tb = exc_tb - while tb and not tb.tb_frame.f_locals.has_key(stack_bottom): - tb = tb.tb_next - if not tb: - # We did not find our exec statement, so this was actually a bug - # in SCons itself. Show the whole stack. - tb = exc_tb - stack = traceback.extract_tb(tb) - try: - type = exc_type.__name__ - except AttributeError: - type = str(exc_type) - if type[:11] == "exceptions.": - type = type[11:] - file.write('%s: %s:\n' % (type, exc_value)) - for fname, line, func, text in stack: - file.write(' File "%s", line %d:\n' % (fname, line)) - file.write(' %s\n' % text) - -def annotate(node): - """Annotate a node with the stack frame describing the - SConscript file and line number that created it.""" - tb = sys.exc_info()[2] - while tb and not tb.tb_frame.f_locals.has_key(stack_bottom): - tb = tb.tb_next - if not tb: - # We did not find any exec of an SConscript file: what?! - raise SCons.Errors.InternalError, "could not find SConscript stack frame" - node.creator = traceback.extract_stack(tb)[0] - -# The following line would cause each Node to be annotated using the -# above function. Unfortunately, this is a *huge* performance hit, so -# leave this disabled until we find a more efficient mechanism. -#SCons.Node.Annotate = annotate - -class SConsEnvironment(SCons.Environment.Base): - """An Environment subclass that contains all of the methods that - are particular to the wrapper SCons interface and which aren't - (or shouldn't be) part of the build engine itself. - - Note that not all of the methods of this class have corresponding - global functions, there are some private methods. - """ - - # - # Private methods of an SConsEnvironment. - # - def _exceeds_version(self, major, minor, v_major, v_minor): - """Return 1 if 'major' and 'minor' are greater than the version - in 'v_major' and 'v_minor', and 0 otherwise.""" - return (major > v_major or (major == v_major and minor > v_minor)) - - def _get_major_minor_revision(self, version_string): - """Split a version string into major, minor and (optionally) - revision parts. - - This is complicated by the fact that a version string can be - something like 3.2b1.""" - version = string.split(string.split(version_string, ' ')[0], '.') - v_major = int(version[0]) - v_minor = int(re.match('\d+', version[1]).group()) - if len(version) >= 3: - v_revision = int(re.match('\d+', version[2]).group()) - else: - v_revision = 0 - return v_major, v_minor, v_revision - - def _get_SConscript_filenames(self, ls, kw): - """ - Convert the parameters passed to # SConscript() calls into a list - of files and export variables. If the parameters are invalid, - throws SCons.Errors.UserError. Returns a tuple (l, e) where l - is a list of SConscript filenames and e is a list of exports. - """ - exports = [] - - if len(ls) == 0: - try: - dirs = kw["dirs"] - except KeyError: - raise SCons.Errors.UserError, \ - "Invalid SConscript usage - no parameters" - - if not SCons.Util.is_List(dirs): - dirs = [ dirs ] - dirs = map(str, dirs) - - name = kw.get('name', 'SConscript') - - files = map(lambda n, name = name: os.path.join(n, name), dirs) - - elif len(ls) == 1: - - files = ls[0] - - elif len(ls) == 2: - - files = ls[0] - exports = self.Split(ls[1]) - - else: - - raise SCons.Errors.UserError, \ - "Invalid SConscript() usage - too many arguments" - - if not SCons.Util.is_List(files): - files = [ files ] - - if kw.get('exports'): - exports.extend(self.Split(kw['exports'])) - - variant_dir = kw.get('variant_dir') or kw.get('build_dir') - if variant_dir: - if len(files) != 1: - raise SCons.Errors.UserError, \ - "Invalid SConscript() usage - can only specify one SConscript with a variant_dir" - duplicate = kw.get('duplicate', 1) - src_dir = kw.get('src_dir') - if not src_dir: - src_dir, fname = os.path.split(str(files[0])) - files = [os.path.join(str(variant_dir), fname)] - else: - if not isinstance(src_dir, SCons.Node.Node): - src_dir = self.fs.Dir(src_dir) - fn = files[0] - if not isinstance(fn, SCons.Node.Node): - fn = self.fs.File(fn) - if fn.is_under(src_dir): - # Get path relative to the source directory. - fname = fn.get_path(src_dir) - files = [os.path.join(str(variant_dir), fname)] - else: - files = [fn.abspath] - kw['src_dir'] = variant_dir - self.fs.VariantDir(variant_dir, src_dir, duplicate) - - return (files, exports) - - # - # Public methods of an SConsEnvironment. These get - # entry points in the global name space so they can be called - # as global functions. - # - - def Configure(self, *args, **kw): - if not SCons.Script.sconscript_reading: - raise SCons.Errors.UserError, "Calling Configure from Builders is not supported." - kw['_depth'] = kw.get('_depth', 0) + 1 - return apply(SCons.Environment.Base.Configure, (self,)+args, kw) - - def Default(self, *targets): - SCons.Script._Set_Default_Targets(self, targets) - - def EnsureSConsVersion(self, major, minor, revision=0): - """Exit abnormally if the SCons version is not late enough.""" - scons_ver = self._get_major_minor_revision(SCons.__version__) - if scons_ver < (major, minor, revision): - if revision: - scons_ver_string = '%d.%d.%d' % (major, minor, revision) - else: - scons_ver_string = '%d.%d' % (major, minor) - print "SCons %s or greater required, but you have SCons %s" % \ - (scons_ver_string, SCons.__version__) - sys.exit(2) - - def EnsurePythonVersion(self, major, minor): - """Exit abnormally if the Python version is not late enough.""" - try: - v_major, v_minor, v_micro, release, serial = sys.version_info - python_ver = (v_major, v_minor) - except AttributeError: - python_ver = self._get_major_minor_revision(sys.version)[:2] - if python_ver < (major, minor): - v = string.split(sys.version, " ", 1)[0] - print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) - sys.exit(2) - - def Exit(self, value=0): - sys.exit(value) - - def Export(self, *vars): - for var in vars: - global_exports.update(compute_exports(self.Split(var))) - - def GetLaunchDir(self): - global launch_dir - return launch_dir - - def GetOption(self, name): - name = self.subst(name) - return SCons.Script.Main.GetOption(name) - - def Help(self, text): - text = self.subst(text, raw=1) - SCons.Script.HelpFunction(text) - - def Import(self, *vars): - try: - frame = call_stack[-1] - globals = frame.globals - exports = frame.exports - for var in vars: - var = self.Split(var) - for v in var: - if v == '*': - globals.update(global_exports) - globals.update(exports) - else: - if exports.has_key(v): - globals[v] = exports[v] - else: - globals[v] = global_exports[v] - except KeyError,x: - raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x - - def SConscript(self, *ls, **kw): - def subst_element(x, subst=self.subst): - if SCons.Util.is_List(x): - x = map(subst, x) - else: - x = subst(x) - return x - ls = map(subst_element, ls) - subst_kw = {} - for key, val in kw.items(): - if SCons.Util.is_String(val): - val = self.subst(val) - elif SCons.Util.is_List(val): - result = [] - for v in val: - if SCons.Util.is_String(v): - v = self.subst(v) - result.append(v) - val = result - subst_kw[key] = val - - files, exports = self._get_SConscript_filenames(ls, subst_kw) - subst_kw['exports'] = exports - return apply(_SConscript, [self.fs,] + files, subst_kw) - - def SConscriptChdir(self, flag): - global sconscript_chdir - sconscript_chdir = flag - - def SetOption(self, name, value): - name = self.subst(name) - SCons.Script.Main.SetOption(name, value) - -# -# -# -SCons.Environment.Environment = SConsEnvironment - -def Configure(*args, **kw): - if not SCons.Script.sconscript_reading: - raise SCons.Errors.UserError, "Calling Configure from Builders is not supported." - kw['_depth'] = 1 - return apply(SCons.SConf.SConf, args, kw) - -# It's very important that the DefaultEnvironmentCall() class stay in this -# file, with the get_calling_namespaces() function, the compute_exports() -# function, the Frame class and the SConsEnvironment.Export() method. -# These things make up the calling stack leading up to the actual global -# Export() or SConscript() call that the user issued. We want to allow -# users to export local variables that they define, like so: -# -# def func(): -# x = 1 -# Export('x') -# -# To support this, the get_calling_namespaces() function assumes that -# the *first* stack frame that's not from this file is the local frame -# for the Export() or SConscript() call. - -_DefaultEnvironmentProxy = None - -def get_DefaultEnvironmentProxy(): - global _DefaultEnvironmentProxy - if not _DefaultEnvironmentProxy: - default_env = SCons.Defaults.DefaultEnvironment() - _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) - return _DefaultEnvironmentProxy - -class DefaultEnvironmentCall: - """A class that implements "global function" calls of - Environment methods by fetching the specified method from the - DefaultEnvironment's class. Note that this uses an intermediate - proxy class instead of calling the DefaultEnvironment method - directly so that the proxy can override the subst() method and - thereby prevent expansion of construction variables (since from - the user's point of view this was called as a global function, - with no associated construction environment).""" - def __init__(self, method_name, subst=0): - self.method_name = method_name - if subst: - self.factory = SCons.Defaults.DefaultEnvironment - else: - self.factory = get_DefaultEnvironmentProxy - def __call__(self, *args, **kw): - env = self.factory() - method = getattr(env, self.method_name) - return apply(method, args, kw) - - -def BuildDefaultGlobals(): - """ - Create a dictionary containing all the default globals for - SConstruct and SConscript files. - """ - - global GlobalDict - if GlobalDict is None: - GlobalDict = {} - - import SCons.Script - d = SCons.Script.__dict__ - def not_a_module(m, d=d, mtype=type(SCons.Script)): - return type(d[m]) != mtype - for m in filter(not_a_module, dir(SCons.Script)): - GlobalDict[m] = d[m] - - return GlobalDict.copy() diff --git a/tools/scons/scons-local-1.2.0/SCons/Script/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Script/__init__.py deleted file mode 100644 index ad99991313..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Script/__init__.py +++ /dev/null @@ -1,408 +0,0 @@ -"""SCons.Script - -This file implements the main() function used by the scons script. - -Architecturally, this *is* the scons script, and will likely only be -called from the external "scons" wrapper. Consequently, anything here -should not be, or be considered, part of the build engine. If it's -something that we expect other software to want to use, it should go in -some other module. If it's specific to the "scons" script invocation, -it goes here. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Script/__init__.py 3842 2008/12/20 22:59:52 scons" - -import time -start_time = time.time() - -import os -import string -import sys -import UserList - -# Special chicken-and-egg handling of the "--debug=memoizer" flag: -# -# SCons.Memoize contains a metaclass implementation that affects how -# the other classes are instantiated. The Memoizer may add shim methods -# to classes that have methods that cache computed values in order to -# count and report the hits and misses. -# -# If we wait to enable the Memoization until after we've parsed the -# command line options normally, it will be too late, because the Memoizer -# will have already analyzed the classes that it's Memoizing and decided -# to not add the shims. So we use a special-case, up-front check for -# the "--debug=memoizer" flag and enable Memoizer before we import any -# of the other modules that use it. - -_args = sys.argv + string.split(os.environ.get('SCONSFLAGS', '')) -if "--debug=memoizer" in _args: - import SCons.Memoize - import SCons.Warnings - try: - SCons.Memoize.EnableMemoization() - except SCons.Warnings.Warning: - # Some warning was thrown (inability to --debug=memoizer on - # Python 1.5.2 because it doesn't have metaclasses). Arrange - # for it to be displayed or not after warnings are configured. - import Main - exc_type, exc_value, tb = sys.exc_info() - Main.delayed_warnings.append((exc_type, exc_value)) -del _args - -import SCons.Action -import SCons.Builder -import SCons.Environment -import SCons.Node.FS -import SCons.Options -import SCons.Platform -import SCons.Scanner -import SCons.SConf -import SCons.Subst -import SCons.Tool -import SCons.Util -import SCons.Variables -import SCons.Defaults - -import Main - -main = Main.main - -# The following are global class definitions and variables that used to -# live directly in this module back before 0.96.90, when it contained -# a lot of code. Some SConscript files in widely-distributed packages -# (Blender is the specific example) actually reached into SCons.Script -# directly to use some of these. Rather than break those SConscript -# files, we're going to propagate these names into the SCons.Script -# namespace here. -# -# Some of these are commented out because it's *really* unlikely anyone -# used them, but we're going to leave the comment here to try to make -# it obvious what to do if the situation arises. -BuildTask = Main.BuildTask -CleanTask = Main.CleanTask -QuestionTask = Main.QuestionTask -#PrintHelp = Main.PrintHelp -#SConscriptSettableOptions = Main.SConscriptSettableOptions - -AddOption = Main.AddOption -GetOption = Main.GetOption -SetOption = Main.SetOption -Progress = Main.Progress -GetBuildFailures = Main.GetBuildFailures - -#keep_going_on_error = Main.keep_going_on_error -#print_dtree = Main.print_dtree -#print_explanations = Main.print_explanations -#print_includes = Main.print_includes -#print_objects = Main.print_objects -#print_time = Main.print_time -#print_tree = Main.print_tree -#memory_stats = Main.memory_stats -#ignore_errors = Main.ignore_errors -#sconscript_time = Main.sconscript_time -#command_time = Main.command_time -#exit_status = Main.exit_status -#profiling = Main.profiling -#repositories = Main.repositories - -# -import SConscript -_SConscript = SConscript - -call_stack = _SConscript.call_stack - -# -Action = SCons.Action.Action -AddMethod = SCons.Util.AddMethod -AllowSubstExceptions = SCons.Subst.SetAllowableExceptions -Builder = SCons.Builder.Builder -Configure = _SConscript.Configure -Environment = SCons.Environment.Environment -#OptParser = SCons.SConsOptions.OptParser -FindPathDirs = SCons.Scanner.FindPathDirs -Platform = SCons.Platform.Platform -Return = _SConscript.Return -Scanner = SCons.Scanner.Base -Tool = SCons.Tool.Tool -WhereIs = SCons.Util.WhereIs - -# -BoolVariable = SCons.Variables.BoolVariable -EnumVariable = SCons.Variables.EnumVariable -ListVariable = SCons.Variables.ListVariable -PackageVariable = SCons.Variables.PackageVariable -PathVariable = SCons.Variables.PathVariable - -# Deprecated names that will go away some day. -BoolOption = SCons.Options.BoolOption -EnumOption = SCons.Options.EnumOption -ListOption = SCons.Options.ListOption -PackageOption = SCons.Options.PackageOption -PathOption = SCons.Options.PathOption - -# Action factories. -Chmod = SCons.Defaults.Chmod -Copy = SCons.Defaults.Copy -Delete = SCons.Defaults.Delete -Mkdir = SCons.Defaults.Mkdir -Move = SCons.Defaults.Move -Touch = SCons.Defaults.Touch - -# Pre-made, public scanners. -CScanner = SCons.Tool.CScanner -DScanner = SCons.Tool.DScanner -DirScanner = SCons.Defaults.DirScanner -ProgramScanner = SCons.Tool.ProgramScanner -SourceFileScanner = SCons.Tool.SourceFileScanner - -# Functions we might still convert to Environment methods. -CScan = SCons.Defaults.CScan -DefaultEnvironment = SCons.Defaults.DefaultEnvironment - -# Other variables we provide. -class TargetList(UserList.UserList): - def _do_nothing(self, *args, **kw): - pass - def _add_Default(self, list): - self.extend(list) - def _clear(self): - del self[:] - -ARGUMENTS = {} -ARGLIST = [] -BUILD_TARGETS = TargetList() -COMMAND_LINE_TARGETS = [] -DEFAULT_TARGETS = [] - -# BUILD_TARGETS can be modified in the SConscript files. If so, we -# want to treat the modified BUILD_TARGETS list as if they specified -# targets on the command line. To do that, though, we need to know if -# BUILD_TARGETS was modified through "official" APIs or by hand. We do -# this by updating two lists in parallel, the documented BUILD_TARGETS -# list, above, and this internal _build_plus_default targets list which -# should only have "official" API changes. Then Script/Main.py can -# compare these two afterwards to figure out if the user added their -# own targets to BUILD_TARGETS. -_build_plus_default = TargetList() - -def _Add_Arguments(alist): - for arg in alist: - a, b = string.split(arg, '=', 1) - ARGUMENTS[a] = b - ARGLIST.append((a, b)) - -def _Add_Targets(tlist): - if tlist: - COMMAND_LINE_TARGETS.extend(tlist) - BUILD_TARGETS.extend(tlist) - BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing - BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing - _build_plus_default.extend(tlist) - _build_plus_default._add_Default = _build_plus_default._do_nothing - _build_plus_default._clear = _build_plus_default._do_nothing - -def _Set_Default_Targets_Has_Been_Called(d, fs): - return DEFAULT_TARGETS - -def _Set_Default_Targets_Has_Not_Been_Called(d, fs): - if d is None: - d = [fs.Dir('.')] - return d - -_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called - -def _Set_Default_Targets(env, tlist): - global DEFAULT_TARGETS - global _Get_Default_Targets - _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called - for t in tlist: - if t is None: - # Delete the elements from the list in-place, don't - # reassign an empty list to DEFAULT_TARGETS, so that the - # variables will still point to the same object we point to. - del DEFAULT_TARGETS[:] - BUILD_TARGETS._clear() - _build_plus_default._clear() - elif isinstance(t, SCons.Node.Node): - DEFAULT_TARGETS.append(t) - BUILD_TARGETS._add_Default([t]) - _build_plus_default._add_Default([t]) - else: - nodes = env.arg2nodes(t, env.fs.Entry) - DEFAULT_TARGETS.extend(nodes) - BUILD_TARGETS._add_Default(nodes) - _build_plus_default._add_Default(nodes) - -# -help_text = None - -def HelpFunction(text): - global help_text - if SCons.Script.help_text is None: - SCons.Script.help_text = text - else: - help_text = help_text + text - -# -# Will be non-zero if we are reading an SConscript file. -sconscript_reading = 0 - -# -def Variables(files=[], args=ARGUMENTS): - return SCons.Variables.Variables(files, args) - -def Options(files=[], args=ARGUMENTS): - return SCons.Options.Options(files, args) - -# The list of global functions to add to the SConscript name space -# that end up calling corresponding methods or Builders in the -# DefaultEnvironment(). -GlobalDefaultEnvironmentFunctions = [ - # Methods from the SConsEnvironment class, above. - 'Default', - 'EnsurePythonVersion', - 'EnsureSConsVersion', - 'Exit', - 'Export', - 'GetLaunchDir', - 'Help', - 'Import', - #'SConscript', is handled separately, below. - 'SConscriptChdir', - - # Methods from the Environment.Base class. - 'AddPostAction', - 'AddPreAction', - 'Alias', - 'AlwaysBuild', - 'BuildDir', - 'CacheDir', - 'Clean', - #The Command() method is handled separately, below. - 'Decider', - 'Depends', - 'Dir', - 'NoClean', - 'NoCache', - 'Entry', - 'Execute', - 'File', - 'FindFile', - 'FindInstalledFiles', - 'FindSourceFiles', - 'Flatten', - 'GetBuildPath', - 'Glob', - 'Ignore', - 'Install', - 'InstallAs', - 'Literal', - 'Local', - 'ParseDepends', - 'Precious', - 'Repository', - 'Requires', - 'SConsignFile', - 'SideEffect', - 'SourceCode', - 'SourceSignatures', - 'Split', - 'Tag', - 'TargetSignatures', - 'Value', - 'VariantDir', -] - -GlobalDefaultBuilders = [ - # Supported builders. - 'CFile', - 'CXXFile', - 'DVI', - 'Jar', - 'Java', - 'JavaH', - 'Library', - 'M4', - 'MSVSProject', - 'Object', - 'PCH', - 'PDF', - 'PostScript', - 'Program', - 'RES', - 'RMIC', - 'SharedLibrary', - 'SharedObject', - 'StaticLibrary', - 'StaticObject', - 'Tar', - 'TypeLibrary', - 'Zip', - 'Package', -] - -for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: - exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) -del name - -# There are a handful of variables that used to live in the -# Script/SConscript.py module that some SConscript files out there were -# accessing directly as SCons.Script.SConscript.*. The problem is that -# "SConscript" in this namespace is no longer a module, it's a global -# function call--or more precisely, an object that implements a global -# function call through the default Environment. Nevertheless, we can -# maintain backwards compatibility for SConscripts that were reaching in -# this way by hanging some attributes off the "SConscript" object here. -SConscript = _SConscript.DefaultEnvironmentCall('SConscript') - -# Make SConscript look enough like the module it used to be so -# that pychecker doesn't barf. -SConscript.__name__ = 'SConscript' - -SConscript.Arguments = ARGUMENTS -SConscript.ArgList = ARGLIST -SConscript.BuildTargets = BUILD_TARGETS -SConscript.CommandLineTargets = COMMAND_LINE_TARGETS -SConscript.DefaultTargets = DEFAULT_TARGETS - -# The global Command() function must be handled differently than the -# global functions for other construction environment methods because -# we want people to be able to use Actions that must expand $TARGET -# and $SOURCE later, when (and if) the Action is invoked to build -# the target(s). We do this with the subst=1 argument, which creates -# a DefaultEnvironmentCall instance that wraps up a normal default -# construction environment that performs variable substitution, not a -# proxy that doesn't. -# -# There's a flaw here, though, because any other $-variables on a command -# line will *also* be expanded, each to a null string, but that should -# only be a problem in the unusual case where someone was passing a '$' -# on a command line and *expected* the $ to get through to the shell -# because they were calling Command() and not env.Command()... This is -# unlikely enough that we're going to leave this as is and cross that -# bridge if someone actually comes to it. -Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) diff --git a/tools/scons/scons-local-1.2.0/SCons/Sig.py b/tools/scons/scons-local-1.2.0/SCons/Sig.py deleted file mode 100644 index 2e50308c51..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Sig.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Sig.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """Place-holder for the old SCons.Sig module hierarchy - -This is no longer used, but code out there (such as the NSIS module on -the SCons wiki) may try to import SCons.Sig. If so, we generate a warning -that points them to the line that caused the import, and don't die. - -If someone actually tried to use the sub-modules or functions within -the package (for example, SCons.Sig.MD5.signature()), then they'll still -get an AttributeError, but at least they'll know where to start looking. -""" - -import SCons.Util -import SCons.Warnings - -msg = 'The SCons.Sig module no longer exists.\n' \ - ' Remove the following "import SCons.Sig" line to eliminate this warning:' - -SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, msg) - -default_calc = None -default_module = None - -class MD5Null(SCons.Util.Null): - def __repr__(self): - return "MD5Null()" - -class TimeStampNull(SCons.Util.Null): - def __repr__(self): - return "TimeStampNull()" - -MD5 = MD5Null() -TimeStamp = TimeStampNull() diff --git a/tools/scons/scons-local-1.2.0/SCons/Subst.py b/tools/scons/scons-local-1.2.0/SCons/Subst.py deleted file mode 100644 index afebca43fb..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Subst.py +++ /dev/null @@ -1,884 +0,0 @@ -"""SCons.Subst - -SCons string substitution. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Subst.py 3842 2008/12/20 22:59:52 scons" - -import re -import string -import types -import UserList -import UserString - -import SCons.Errors - -from SCons.Util import is_String, is_Sequence - -# Indexed by the SUBST_* constants below. -_strconv = [SCons.Util.to_String_for_subst, - SCons.Util.to_String_for_subst, - SCons.Util.to_String_for_signature] - - - -AllowableExceptions = (IndexError, NameError) - -def SetAllowableExceptions(*excepts): - global AllowableExceptions - AllowableExceptions = filter(None, excepts) - -def raise_exception(exception, target, s): - name = exception.__class__.__name__ - msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) - if target: - raise SCons.Errors.BuildError, (target[0], msg) - else: - raise SCons.Errors.UserError, msg - - - -class Literal: - """A wrapper for a string. If you use this object wrapped - around a string, then it will be interpreted as literal. - When passed to the command interpreter, all special - characters will be escaped.""" - def __init__(self, lstr): - self.lstr = lstr - - def __str__(self): - return self.lstr - - def escape(self, escape_func): - return escape_func(self.lstr) - - def for_signature(self): - return self.lstr - - def is_literal(self): - return 1 - -class SpecialAttrWrapper: - """This is a wrapper for what we call a 'Node special attribute.' - This is any of the attributes of a Node that we can reference from - Environment variable substitution, such as $TARGET.abspath or - $SOURCES[1].filebase. We implement the same methods as Literal - so we can handle special characters, plus a for_signature method, - such that we can return some canonical string during signature - calculation to avoid unnecessary rebuilds.""" - - def __init__(self, lstr, for_signature=None): - """The for_signature parameter, if supplied, will be the - canonical string we return from for_signature(). Else - we will simply return lstr.""" - self.lstr = lstr - if for_signature: - self.forsig = for_signature - else: - self.forsig = lstr - - def __str__(self): - return self.lstr - - def escape(self, escape_func): - return escape_func(self.lstr) - - def for_signature(self): - return self.forsig - - def is_literal(self): - return 1 - -def quote_spaces(arg): - """Generic function for putting double quotes around any string that - has white space in it.""" - if ' ' in arg or '\t' in arg: - return '"%s"' % arg - else: - return str(arg) - -class CmdStringHolder(UserString.UserString): - """This is a special class used to hold strings generated by - scons_subst() and scons_subst_list(). It defines a special method - escape(). When passed a function with an escape algorithm for a - particular platform, it will return the contained string with the - proper escape sequences inserted. - """ - def __init__(self, cmd, literal=None): - UserString.UserString.__init__(self, cmd) - self.literal = literal - - def is_literal(self): - return self.literal - - def escape(self, escape_func, quote_func=quote_spaces): - """Escape the string with the supplied function. The - function is expected to take an arbitrary string, then - return it with all special characters escaped and ready - for passing to the command interpreter. - - After calling this function, the next call to str() will - return the escaped string. - """ - - if self.is_literal(): - return escape_func(self.data) - elif ' ' in self.data or '\t' in self.data: - return quote_func(self.data) - else: - return self.data - -def escape_list(list, escape_func): - """Escape a list of arguments by running the specified escape_func - on every object in the list that has an escape() method.""" - def escape(obj, escape_func=escape_func): - try: - e = obj.escape - except AttributeError: - return obj - else: - return e(escape_func) - return map(escape, list) - -class NLWrapper: - """A wrapper class that delays turning a list of sources or targets - into a NodeList until it's needed. The specified function supplied - when the object is initialized is responsible for turning raw nodes - into proxies that implement the special attributes like .abspath, - .source, etc. This way, we avoid creating those proxies just - "in case" someone is going to use $TARGET or the like, and only - go through the trouble if we really have to. - - In practice, this might be a wash performance-wise, but it's a little - cleaner conceptually... - """ - - def __init__(self, list, func): - self.list = list - self.func = func - def _return_nodelist(self): - return self.nodelist - def _gen_nodelist(self): - list = self.list - if list is None: - list = [] - elif not is_Sequence(list): - list = [list] - # The map(self.func) call is what actually turns - # a list into appropriate proxies. - self.nodelist = SCons.Util.NodeList(map(self.func, list)) - self._create_nodelist = self._return_nodelist - return self.nodelist - _create_nodelist = _gen_nodelist - - -class Targets_or_Sources(UserList.UserList): - """A class that implements $TARGETS or $SOURCES expansions by in turn - wrapping a NLWrapper. This class handles the different methods used - to access the list, calling the NLWrapper to create proxies on demand. - - Note that we subclass UserList.UserList purely so that the - is_Sequence() function will identify an object of this class as - a list during variable expansion. We're not really using any - UserList.UserList methods in practice. - """ - def __init__(self, nl): - self.nl = nl - def __getattr__(self, attr): - nl = self.nl._create_nodelist() - return getattr(nl, attr) - def __getitem__(self, i): - nl = self.nl._create_nodelist() - return nl[i] - def __getslice__(self, i, j): - nl = self.nl._create_nodelist() - i = max(i, 0); j = max(j, 0) - return nl[i:j] - def __str__(self): - nl = self.nl._create_nodelist() - return str(nl) - def __repr__(self): - nl = self.nl._create_nodelist() - return repr(nl) - -class Target_or_Source: - """A class that implements $TARGET or $SOURCE expansions by in turn - wrapping a NLWrapper. This class handles the different methods used - to access an individual proxy Node, calling the NLWrapper to create - a proxy on demand. - """ - def __init__(self, nl): - self.nl = nl - def __getattr__(self, attr): - nl = self.nl._create_nodelist() - try: - nl0 = nl[0] - except IndexError: - # If there is nothing in the list, then we have no attributes to - # pass through, so raise AttributeError for everything. - raise AttributeError, "NodeList has no attribute: %s" % attr - return getattr(nl0, attr) - def __str__(self): - nl = self.nl._create_nodelist() - if nl: - return str(nl[0]) - return '' - def __repr__(self): - nl = self.nl._create_nodelist() - if nl: - return repr(nl[0]) - return '' - -def subst_dict(target, source): - """Create a dictionary for substitution of special - construction variables. - - This translates the following special arguments: - - target - the target (object or array of objects), - used to generate the TARGET and TARGETS - construction variables - - source - the source (object or array of objects), - used to generate the SOURCES and SOURCE - construction variables - """ - dict = {} - - if target: - def get_tgt_subst_proxy(thing): - try: - subst_proxy = thing.get_subst_proxy() - except AttributeError: - subst_proxy = thing # probably a string, just return it - return subst_proxy - tnl = NLWrapper(target, get_tgt_subst_proxy) - dict['TARGETS'] = Targets_or_Sources(tnl) - dict['TARGET'] = Target_or_Source(tnl) - else: - dict['TARGETS'] = None - dict['TARGET'] = None - - if source: - def get_src_subst_proxy(node): - try: - rfile = node.rfile - except AttributeError: - pass - else: - node = rfile() - try: - return node.get_subst_proxy() - except AttributeError: - return node # probably a String, just return it - snl = NLWrapper(source, get_src_subst_proxy) - dict['SOURCES'] = Targets_or_Sources(snl) - dict['SOURCE'] = Target_or_Source(snl) - else: - dict['SOURCES'] = None - dict['SOURCE'] = None - - return dict - -# Constants for the "mode" parameter to scons_subst_list() and -# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD -# gives a command line suitable for passing to a shell. SUBST_SIG -# gives a command line appropriate for calculating the signature -# of a command line...if this changes, we should rebuild. -SUBST_CMD = 0 -SUBST_RAW = 1 -SUBST_SIG = 2 - -_rm = re.compile(r'\$[()]') -_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') - -# Indexed by the SUBST_* constants above. -_regex_remove = [ _rm, None, _remove ] - -def _rm_list(list): - #return [ l for l in list if not l in ('$(', '$)') ] - return filter(lambda l: not l in ('$(', '$)'), list) - -def _remove_list(list): - result = [] - do_append = result.append - for l in list: - if l == '$(': - do_append = lambda x: None - elif l == '$)': - do_append = result.append - else: - do_append(l) - return result - -# Indexed by the SUBST_* constants above. -_list_remove = [ _rm_list, None, _remove_list ] - -# Regular expressions for splitting strings and handling substitutions, -# for use by the scons_subst() and scons_subst_list() functions: -# -# The first expression compiled matches all of the $-introduced tokens -# that we need to process in some way, and is used for substitutions. -# The expressions it matches are: -# -# "$$" -# "$(" -# "$)" -# "$variable" [must begin with alphabetic or underscore] -# "${any stuff}" -# -# The second expression compiled is used for splitting strings into tokens -# to be processed, and it matches all of the tokens listed above, plus -# the following that affect how arguments do or don't get joined together: -# -# " " [white space] -# "non-white-space" [without any dollar signs] -# "$" [single dollar sign] -# -_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' -_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) -_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) - -# This regular expression is used to replace strings of multiple white -# space characters in the string result from the scons_subst() function. -_space_sep = re.compile(r'[\t ]+(?![^{]*})') - -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): - """Expand a string or list containing construction variable - substitutions. - - This is the work-horse function for substitutions in file names - and the like. The companion scons_subst_list() function (below) - handles separating command lines into lists of arguments, so see - that function if that's what you're looking for. - """ - if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0: - return strSubst - - class StringSubber: - """A class to construct the results of a scons_subst() call. - - This binds a specific construction environment, mode, target and - source with two methods (substitute() and expand()) that handle - the expansion. - """ - def __init__(self, env, mode, target, source, conv, gvars): - self.env = env - self.mode = mode - self.target = target - self.source = source - self.conv = conv - self.gvars = gvars - - def expand(self, s, lvars): - """Expand a single "token" as necessary, returning an - appropriate string containing the expansion. - - This handles expanding different types of things (strings, - lists, callables) appropriately. It calls the wrapper - substitute() method to re-expand things as necessary, so that - the results of expansions of side-by-side strings still get - re-evaluated separately, not smushed together. - """ - if is_String(s): - try: - s0, s1 = s[:2] - except (IndexError, ValueError): - return s - if s0 != '$': - return s - if s1 == '$': - return '$' - elif s1 in '()': - return s - else: - key = s[1:] - if key[0] == '{' or string.find(key, '.') >= 0: - if key[0] == '{': - key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception, e: - if e.__class__ in AllowableExceptions: - return '' - raise_exception(e, self.target, s) - else: - if lvars.has_key(key): - s = lvars[key] - elif self.gvars.has_key(key): - s = self.gvars[key] - elif not NameError in AllowableExceptions: - raise_exception(NameError(key), self.target, s) - else: - return '' - - # Before re-expanding the result, handle - # recursive expansion by copying the local - # variable dictionary and overwriting a null - # string for the value of the variable name - # we just expanded. - # - # This could potentially be optimized by only - # copying lvars when s contains more expansions, - # but lvars is usually supposed to be pretty - # small, and deeply nested variable expansions - # are probably more the exception than the norm, - # so it should be tolerable for now. - lv = lvars.copy() - var = string.split(key, '.')[0] - lv[var] = '' - return self.substitute(s, lv) - elif is_Sequence(s): - def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): - return conv(substitute(l, lvars)) - return map(func, s) - elif callable(s): - try: - s = s(target=self.target, - source=self.source, - env=self.env, - for_signature=(self.mode != SUBST_CMD)) - except TypeError: - # This probably indicates that it's a callable - # object that doesn't match our calling arguments - # (like an Action). - if self.mode == SUBST_RAW: - return s - s = self.conv(s) - return self.substitute(s, lvars) - elif s is None: - return '' - else: - return s - - def substitute(self, args, lvars): - """Substitute expansions in an argument or list of arguments. - - This serves as a wrapper for splitting up a string into - separate tokens. - """ - if is_String(args) and not isinstance(args, CmdStringHolder): - args = str(args) # In case it's a UserString. - try: - def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars): - return conv(expand(match.group(1), lvars)) - result = _dollar_exps.sub(sub_match, args) - except TypeError: - # If the internal conversion routine doesn't return - # strings (it could be overridden to return Nodes, for - # example), then the 1.5.2 re module will throw this - # exception. Back off to a slower, general-purpose - # algorithm that works for all data types. - args = _separate_args.findall(args) - result = [] - for a in args: - result.append(self.conv(self.expand(a, lvars))) - if len(result) == 1: - result = result[0] - else: - result = string.join(map(str, result), '') - return result - else: - return self.expand(args, lvars) - - if conv is None: - conv = _strconv[mode] - - # Doing this every time is a bit of a waste, since the Executor - # has typically already populated the OverrideEnvironment with - # $TARGET/$SOURCE variables. We're keeping this (for now), though, - # because it supports existing behavior that allows us to call - # an Action directly with an arbitrary target+source pair, which - # we use in Tool/tex.py to handle calling $BIBTEX when necessary. - # If we dropped that behavior (or found another way to cover it), - # we could get rid of this call completely and just rely on the - # Executor setting the variables. - d = subst_dict(target, source) - if d: - lvars = lvars.copy() - lvars.update(d) - - # We're (most likely) going to eval() things. If Python doesn't - # find a __builtins__ value in the global dictionary used for eval(), - # it copies the current global values for you. Avoid this by - # setting it explicitly and then deleting, so we don't pollute the - # construction environment Dictionary(ies) that are typically used - # for expansion. - gvars['__builtins__'] = __builtins__ - - ss = StringSubber(env, mode, target, source, conv, gvars) - result = ss.substitute(strSubst, lvars) - - try: - del gvars['__builtins__'] - except KeyError: - pass - - if is_String(result): - # Remove $(-$) pairs and any stuff in between, - # if that's appropriate. - remove = _regex_remove[mode] - if remove: - result = remove.sub('', result) - if mode != SUBST_RAW: - # Compress strings of white space characters into - # a single space. - result = string.strip(_space_sep.sub(' ', result)) - elif is_Sequence(result): - remove = _list_remove[mode] - if remove: - result = remove(result) - - return result - -#Subst_List_Strings = {} - -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): - """Substitute construction variables in a string (or list or other - object) and separate the arguments into a command list. - - The companion scons_subst() function (above) handles basic - substitutions within strings, so see that function instead - if that's what you're looking for. - """ -# try: -# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 -# except KeyError: -# Subst_List_Strings[strSubst] = 1 -# import SCons.Debug -# SCons.Debug.caller_trace(1) - class ListSubber(UserList.UserList): - """A class to construct the results of a scons_subst_list() call. - - Like StringSubber, this class binds a specific construction - environment, mode, target and source with two methods - (substitute() and expand()) that handle the expansion. - - In addition, however, this class is used to track the state of - the result(s) we're gathering so we can do the appropriate thing - whenever we have to append another word to the result--start a new - line, start a new word, append to the current word, etc. We do - this by setting the "append" attribute to the right method so - that our wrapper methods only need ever call ListSubber.append(), - and the rest of the object takes care of doing the right thing - internally. - """ - def __init__(self, env, mode, target, source, conv, gvars): - UserList.UserList.__init__(self, []) - self.env = env - self.mode = mode - self.target = target - self.source = source - self.conv = conv - self.gvars = gvars - - if self.mode == SUBST_RAW: - self.add_strip = lambda x, s=self: s.append(x) - else: - self.add_strip = lambda x, s=self: None - self.in_strip = None - self.next_line() - - def expand(self, s, lvars, within_list): - """Expand a single "token" as necessary, appending the - expansion to the current result. - - This handles expanding different types of things (strings, - lists, callables) appropriately. It calls the wrapper - substitute() method to re-expand things as necessary, so that - the results of expansions of side-by-side strings still get - re-evaluated separately, not smushed together. - """ - - if is_String(s): - try: - s0, s1 = s[:2] - except (IndexError, ValueError): - self.append(s) - return - if s0 != '$': - self.append(s) - return - if s1 == '$': - self.append('$') - elif s1 == '(': - self.open_strip('$(') - elif s1 == ')': - self.close_strip('$)') - else: - key = s[1:] - if key[0] == '{' or string.find(key, '.') >= 0: - if key[0] == '{': - key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception, e: - if e.__class__ in AllowableExceptions: - return - raise_exception(e, self.target, s) - else: - if lvars.has_key(key): - s = lvars[key] - elif self.gvars.has_key(key): - s = self.gvars[key] - elif not NameError in AllowableExceptions: - raise_exception(NameError(), self.target, s) - else: - return - - # Before re-expanding the result, handle - # recursive expansion by copying the local - # variable dictionary and overwriting a null - # string for the value of the variable name - # we just expanded. - lv = lvars.copy() - var = string.split(key, '.')[0] - lv[var] = '' - self.substitute(s, lv, 0) - self.this_word() - elif is_Sequence(s): - for a in s: - self.substitute(a, lvars, 1) - self.next_word() - elif callable(s): - try: - s = s(target=self.target, - source=self.source, - env=self.env, - for_signature=(self.mode != SUBST_CMD)) - except TypeError: - # This probably indicates that it's a callable - # object that doesn't match our calling arguments - # (like an Action). - if self.mode == SUBST_RAW: - self.append(s) - return - s = self.conv(s) - self.substitute(s, lvars, within_list) - elif s is None: - self.this_word() - else: - self.append(s) - - def substitute(self, args, lvars, within_list): - """Substitute expansions in an argument or list of arguments. - - This serves as a wrapper for splitting up a string into - separate tokens. - """ - - if is_String(args) and not isinstance(args, CmdStringHolder): - args = str(args) # In case it's a UserString. - args = _separate_args.findall(args) - for a in args: - if a[0] in ' \t\n\r\f\v': - if '\n' in a: - self.next_line() - elif within_list: - self.append(a) - else: - self.next_word() - else: - self.expand(a, lvars, within_list) - else: - self.expand(args, lvars, within_list) - - def next_line(self): - """Arrange for the next word to start a new line. This - is like starting a new word, except that we have to append - another line to the result.""" - UserList.UserList.append(self, []) - self.next_word() - - def this_word(self): - """Arrange for the next word to append to the end of the - current last word in the result.""" - self.append = self.add_to_current_word - - def next_word(self): - """Arrange for the next word to start a new word.""" - self.append = self.add_new_word - - def add_to_current_word(self, x): - """Append the string x to the end of the current last word - in the result. If that is not possible, then just add - it as a new word. Make sure the entire concatenated string - inherits the object attributes of x (in particular, the - escape function) by wrapping it as CmdStringHolder.""" - - if not self.in_strip or self.mode != SUBST_SIG: - try: - current_word = self[-1][-1] - except IndexError: - self.add_new_word(x) - else: - # All right, this is a hack and it should probably - # be refactored out of existence in the future. - # The issue is that we want to smoosh words together - # and make one file name that gets escaped if - # we're expanding something like foo$EXTENSION, - # but we don't want to smoosh them together if - # it's something like >$TARGET, because then we'll - # treat the '>' like it's part of the file name. - # So for now, just hard-code looking for the special - # command-line redirection characters... - try: - last_char = str(current_word)[-1] - except IndexError: - last_char = '\0' - if last_char in '<>|': - self.add_new_word(x) - else: - y = current_word + x - - # We used to treat a word appended to a literal - # as a literal itself, but this caused problems - # with interpreting quotes around space-separated - # targets on command lines. Removing this makes - # none of the "substantive" end-to-end tests fail, - # so we'll take this out but leave it commented - # for now in case there's a problem not covered - # by the test cases and we need to resurrect this. - #literal1 = self.literal(self[-1][-1]) - #literal2 = self.literal(x) - y = self.conv(y) - if is_String(y): - #y = CmdStringHolder(y, literal1 or literal2) - y = CmdStringHolder(y, None) - self[-1][-1] = y - - def add_new_word(self, x): - if not self.in_strip or self.mode != SUBST_SIG: - literal = self.literal(x) - x = self.conv(x) - if is_String(x): - x = CmdStringHolder(x, literal) - self[-1].append(x) - self.append = self.add_to_current_word - - def literal(self, x): - try: - l = x.is_literal - except AttributeError: - return None - else: - return l() - - def open_strip(self, x): - """Handle the "open strip" $( token.""" - self.add_strip(x) - self.in_strip = 1 - - def close_strip(self, x): - """Handle the "close strip" $) token.""" - self.add_strip(x) - self.in_strip = None - - if conv is None: - conv = _strconv[mode] - - # Doing this every time is a bit of a waste, since the Executor - # has typically already populated the OverrideEnvironment with - # $TARGET/$SOURCE variables. We're keeping this (for now), though, - # because it supports existing behavior that allows us to call - # an Action directly with an arbitrary target+source pair, which - # we use in Tool/tex.py to handle calling $BIBTEX when necessary. - # If we dropped that behavior (or found another way to cover it), - # we could get rid of this call completely and just rely on the - # Executor setting the variables. - d = subst_dict(target, source) - if d: - lvars = lvars.copy() - lvars.update(d) - - # We're (most likely) going to eval() things. If Python doesn't - # find a __builtins__ value in the global dictionary used for eval(), - # it copies the current global values for you. Avoid this by - # setting it explicitly and then deleting, so we don't pollute the - # construction environment Dictionary(ies) that are typically used - # for expansion. - gvars['__builtins__'] = __builtins__ - - ls = ListSubber(env, mode, target, source, conv, gvars) - ls.substitute(strSubst, lvars, 0) - - try: - del gvars['__builtins__'] - except KeyError: - pass - - return ls.data - -def scons_subst_once(strSubst, env, key): - """Perform single (non-recursive) substitution of a single - construction variable keyword. - - This is used when setting a variable when copying or overriding values - in an Environment. We want to capture (expand) the old value before - we override it, so people can do things like: - - env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') - - We do this with some straightforward, brute-force code here... - """ - if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0: - return strSubst - - matchlist = ['$' + key, '${' + key + '}'] - val = env.get(key, '') - def sub_match(match, val=val, matchlist=matchlist): - a = match.group(1) - if a in matchlist: - a = val - if is_Sequence(a): - return string.join(map(str, a)) - else: - return str(a) - - if is_Sequence(strSubst): - result = [] - for arg in strSubst: - if is_String(arg): - if arg in matchlist: - arg = val - if is_Sequence(arg): - result.extend(arg) - else: - result.append(arg) - else: - result.append(_dollar_exps.sub(sub_match, arg)) - else: - result.append(arg) - return result - elif is_String(strSubst): - return _dollar_exps.sub(sub_match, strSubst) - else: - return strSubst diff --git a/tools/scons/scons-local-1.2.0/SCons/Taskmaster.py b/tools/scons/scons-local-1.2.0/SCons/Taskmaster.py deleted file mode 100644 index 354fcca4f0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Taskmaster.py +++ /dev/null @@ -1,985 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__doc__ = """ -Generic Taskmaster module for the SCons build engine. - -This module contains the primary interface(s) between a wrapping user -interface and the SCons build engine. There are two key classes here: - - Taskmaster - This is the main engine for walking the dependency graph and - calling things to decide what does or doesn't need to be built. - - Task - This is the base class for allowing a wrapping interface to - decide what does or doesn't actually need to be done. The - intention is for a wrapping interface to subclass this as - appropriate for different types of behavior it may need. - - The canonical example is the SCons native Python interface, - which has Task subclasses that handle its specific behavior, - like printing "`foo' is up to date" when a top-level target - doesn't need to be built, and handling the -c option by removing - targets as its "build" action. There is also a separate subclass - for suppressing this output when the -q option is used. - - The Taskmaster instantiates a Task object for each (set of) - target(s) that it decides need to be evaluated and/or built. -""" - -__revision__ = "src/engine/SCons/Taskmaster.py 3842 2008/12/20 22:59:52 scons" - -from itertools import chain -import operator -import string -import sys -import traceback - -import SCons.Errors -import SCons.Node - -StateString = SCons.Node.StateString -NODE_NO_STATE = SCons.Node.no_state -NODE_PENDING = SCons.Node.pending -NODE_EXECUTING = SCons.Node.executing -NODE_UP_TO_DATE = SCons.Node.up_to_date -NODE_EXECUTED = SCons.Node.executed -NODE_FAILED = SCons.Node.failed - - -# A subsystem for recording stats about how different Nodes are handled by -# the main Taskmaster loop. There's no external control here (no need for -# a --debug= option); enable it by changing the value of CollectStats. - -CollectStats = None - -class Stats: - """ - A simple class for holding statistics about the disposition of a - Node by the Taskmaster. If we're collecting statistics, each Node - processed by the Taskmaster gets one of these attached, in which case - the Taskmaster records its decision each time it processes the Node. - (Ideally, that's just once per Node.) - """ - def __init__(self): - """ - Instantiates a Taskmaster.Stats object, initializing all - appropriate counters to zero. - """ - self.considered = 0 - self.already_handled = 0 - self.problem = 0 - self.child_failed = 0 - self.not_built = 0 - self.side_effects = 0 - self.build = 0 - -StatsNodes = [] - -fmt = "%(considered)3d "\ - "%(already_handled)3d " \ - "%(problem)3d " \ - "%(child_failed)3d " \ - "%(not_built)3d " \ - "%(side_effects)3d " \ - "%(build)3d " - -def dump_stats(): - StatsNodes.sort(lambda a, b: cmp(str(a), str(b))) - for n in StatsNodes: - print (fmt % n.stats.__dict__) + str(n) - - - -class Task: - """ - Default SCons build engine task. - - This controls the interaction of the actual building of node - and the rest of the engine. - - This is expected to handle all of the normally-customizable - aspects of controlling a build, so any given application - *should* be able to do what it wants by sub-classing this - class and overriding methods as appropriate. If an application - needs to customze something by sub-classing Taskmaster (or - some other build engine class), we should first try to migrate - that functionality into this class. - - Note that it's generally a good idea for sub-classes to call - these methods explicitly to update state, etc., rather than - roll their own interaction with Taskmaster from scratch. - """ - def __init__(self, tm, targets, top, node): - self.tm = tm - self.targets = targets - self.top = top - self.node = node - self.exc_clear() - - def trace_message(self, method, node, description='node'): - fmt = '%-20s %s %s\n' - return fmt % (method + ':', description, self.tm.trace_node(node)) - - def display(self, message): - """ - Hook to allow the calling interface to display a message. - - This hook gets called as part of preparing a task for execution - (that is, a Node to be built). As part of figuring out what Node - should be built next, the actually target list may be altered, - along with a message describing the alteration. The calling - interface can subclass Task and provide a concrete implementation - of this method to see those messages. - """ - pass - - def prepare(self): - """ - Called just before the task is executed. - - This is mainly intended to give the target Nodes a chance to - unlink underlying files and make all necessary directories before - the Action is actually called to build the targets. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.prepare()', self.node)) - - # Now that it's the appropriate time, give the TaskMaster a - # chance to raise any exceptions it encountered while preparing - # this task. - self.exception_raise() - - if self.tm.message: - self.display(self.tm.message) - self.tm.message = None - - # Let the targets take care of any necessary preparations. - # This includes verifying that all of the necessary sources - # and dependencies exist, removing the target file(s), etc. - # - # As of April 2008, the get_executor().prepare() method makes - # sure that all of the aggregate sources necessary to build this - # Task's target(s) exist in one up-front check. The individual - # target t.prepare() methods check that each target's explicit - # or implicit dependencies exists, and also initialize the - # .sconsign info. - self.targets[0].get_executor().prepare() - for t in self.targets: - t.prepare() - for s in t.side_effects: - s.prepare() - - def get_target(self): - """Fetch the target being built or updated by this task. - """ - return self.node - - def needs_execute(self): - """ - Called to determine whether the task's execute() method should - be run. - - This method allows one to skip the somethat costly execution - of the execute() method in a seperate thread. For example, - that would be unnecessary for up-to-date targets. - """ - return True - - def execute(self): - """ - Called to execute the task. - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - prepare(), executed() or failed(). - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.execute()', self.node)) - - try: - everything_was_cached = 1 - for t in self.targets: - if not t.retrieve_from_cache(): - everything_was_cached = 0 - break - if not everything_was_cached: - self.targets[0].build() - except SystemExit: - exc_value = sys.exc_info()[1] - raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) - except SCons.Errors.UserError: - raise - except SCons.Errors.BuildError: - raise - except Exception, e: - buildError = SCons.Errors.convert_to_BuildError(e) - buildError.node = self.targets[0] - buildError.exc_info = sys.exc_info() - raise buildError - - def executed_without_callbacks(self): - """ - Called when the task has been successfully executed - and the Taskmaster instance doesn't want to call - the Node's callback methods. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.executed_without_callbacks()', - self.node)) - - for t in self.targets: - if t.get_state() == NODE_EXECUTING: - for side_effect in t.side_effects: - side_effect.set_state(NODE_NO_STATE) - t.set_state(NODE_EXECUTED) - - def executed_with_callbacks(self): - """ - Called when the task has been successfully executed and - the Taskmaster instance wants to call the Node's callback - methods. - - This may have been a do-nothing operation (to preserve build - order), so we must check the node's state before deciding whether - it was "built", in which case we call the appropriate Node method. - In any event, we always call "visited()", which will handle any - post-visit actions that must take place regardless of whether - or not the target was an actual built target or a source Node. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.executed_with_callbacks()', - self.node)) - - for t in self.targets: - if t.get_state() == NODE_EXECUTING: - for side_effect in t.side_effects: - side_effect.set_state(NODE_NO_STATE) - t.set_state(NODE_EXECUTED) - t.built() - t.visited() - - executed = executed_with_callbacks - - def failed(self): - """ - Default action when a task fails: stop the build. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - self.fail_stop() - - def fail_stop(self): - """ - Explicit stop-the-build failure. - - This sets failure status on the target nodes and all of - their dependent parent nodes. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.failed_stop()', self.node)) - - # Invoke will_not_build() to clean-up the pending children - # list. - self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) - - # Tell the taskmaster to not start any new tasks - self.tm.stop() - - # We're stopping because of a build failure, but give the - # calling Task class a chance to postprocess() the top-level - # target under which the build failure occurred. - self.targets = [self.tm.current_top] - self.top = 1 - - def fail_continue(self): - """ - Explicit continue-the-build failure. - - This sets failure status on the target nodes and all of - their dependent parent nodes. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.failed_continue()', self.node)) - - self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) - - def make_ready_all(self): - """ - Marks all targets in a task ready for execution. - - This is used when the interface needs every target Node to be - visited--the canonical example being the "scons -c" option. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.make_ready_all()', self.node)) - - self.out_of_date = self.targets[:] - for t in self.targets: - t.disambiguate().set_state(NODE_EXECUTING) - for s in t.side_effects: - s.set_state(NODE_EXECUTING) - - def make_ready_current(self): - """ - Marks all targets in a task ready for execution if any target - is not current. - - This is the default behavior for building only what's necessary. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.make_ready_current()', - self.node)) - - self.out_of_date = [] - needs_executing = False - for t in self.targets: - try: - t.disambiguate().make_ready() - is_up_to_date = not t.has_builder() or \ - (not t.always_build and t.is_up_to_date()) - except EnvironmentError, e: - raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) - - if not is_up_to_date: - self.out_of_date.append(t) - needs_executing = True - - if needs_executing: - for t in self.targets: - t.set_state(NODE_EXECUTING) - for s in t.side_effects: - s.set_state(NODE_EXECUTING) - else: - for t in self.targets: - # We must invoke visited() to ensure that the node - # information has been computed before allowing the - # parent nodes to execute. (That could occur in a - # parallel build...) - t.visited() - t.set_state(NODE_UP_TO_DATE) - - make_ready = make_ready_current - - def postprocess(self): - """ - Post-processes a task after it's been executed. - - This examines all the targets just built (or not, we don't care - if the build was successful, or even if there was no build - because everything was up-to-date) to see if they have any - waiting parent Nodes, or Nodes waiting on a common side effect, - that can be put back on the candidates list. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.postprocess()', self.node)) - - # We may have built multiple targets, some of which may have - # common parents waiting for this build. Count up how many - # targets each parent was waiting for so we can subtract the - # values later, and so we *don't* put waiting side-effect Nodes - # back on the candidates list if the Node is also a waiting - # parent. - - targets = set(self.targets) - - pending_children = self.tm.pending_children - parents = {} - for t in targets: - # A node can only be in the pending_children set if it has - # some waiting_parents. - if t.waiting_parents: - if T: T.write(self.trace_message('Task.postprocess()', - t, - 'removing')) - pending_children.discard(t) - for p in t.waiting_parents: - parents[p] = parents.get(p, 0) + 1 - - for t in targets: - for s in t.side_effects: - if s.get_state() == NODE_EXECUTING: - s.set_state(NODE_NO_STATE) - for p in s.waiting_parents: - parents[p] = parents.get(p, 0) + 1 - for p in s.waiting_s_e: - if p.ref_count == 0: - self.tm.candidates.append(p) - - for p, subtract in parents.items(): - p.ref_count = p.ref_count - subtract - if T: T.write(self.trace_message('Task.postprocess()', - p, - 'adjusted parent ref count')) - if p.ref_count == 0: - self.tm.candidates.append(p) - - for t in targets: - t.postprocess() - - # Exception handling subsystem. - # - # Exceptions that occur while walking the DAG or examining Nodes - # must be raised, but must be raised at an appropriate time and in - # a controlled manner so we can, if necessary, recover gracefully, - # possibly write out signature information for Nodes we've updated, - # etc. This is done by having the Taskmaster tell us about the - # exception, and letting - - def exc_info(self): - """ - Returns info about a recorded exception. - """ - return self.exception - - def exc_clear(self): - """ - Clears any recorded exception. - - This also changes the "exception_raise" attribute to point - to the appropriate do-nothing method. - """ - self.exception = (None, None, None) - self.exception_raise = self._no_exception_to_raise - - def exception_set(self, exception=None): - """ - Records an exception to be raised at the appropriate time. - - This also changes the "exception_raise" attribute to point - to the method that will, in fact - """ - if not exception: - exception = sys.exc_info() - self.exception = exception - self.exception_raise = self._exception_raise - - def _no_exception_to_raise(self): - pass - - def _exception_raise(self): - """ - Raises a pending exception that was recorded while getting a - Task ready for execution. - """ - exc = self.exc_info()[:] - try: - exc_type, exc_value, exc_traceback = exc - except ValueError: - exc_type, exc_value = exc - exc_traceback = None - raise exc_type, exc_value, exc_traceback - - -def find_cycle(stack, visited): - if stack[-1] in visited: - return None - visited.add(stack[-1]) - for n in stack[-1].waiting_parents: - stack.append(n) - if stack[0] == stack[-1]: - return stack - if find_cycle(stack, visited): - return stack - stack.pop() - return None - - -class Taskmaster: - """ - The Taskmaster for walking the dependency DAG. - """ - - def __init__(self, targets=[], tasker=Task, order=None, trace=None): - self.original_top = targets - self.top_targets_left = targets[:] - self.top_targets_left.reverse() - self.candidates = [] - self.tasker = tasker - if not order: - order = lambda l: l - self.order = order - self.message = None - self.trace = trace - self.next_candidate = self.find_next_candidate - self.pending_children = set() - - def find_next_candidate(self): - """ - Returns the next candidate Node for (potential) evaluation. - - The candidate list (really a stack) initially consists of all of - the top-level (command line) targets provided when the Taskmaster - was initialized. While we walk the DAG, visiting Nodes, all the - children that haven't finished processing get pushed on to the - candidate list. Each child can then be popped and examined in - turn for whether *their* children are all up-to-date, in which - case a Task will be created for their actual evaluation and - potential building. - - Here is where we also allow candidate Nodes to alter the list of - Nodes that should be examined. This is used, for example, when - invoking SCons in a source directory. A source directory Node can - return its corresponding build directory Node, essentially saying, - "Hey, you really need to build this thing over here instead." - """ - try: - return self.candidates.pop() - except IndexError: - pass - try: - node = self.top_targets_left.pop() - except IndexError: - return None - self.current_top = node - alt, message = node.alter_targets() - if alt: - self.message = message - self.candidates.append(node) - self.candidates.extend(self.order(alt)) - node = self.candidates.pop() - return node - - def no_next_candidate(self): - """ - Stops Taskmaster processing by not returning a next candidate. - - Note that we have to clean-up the Taskmaster candidate list - because the cycle detection depends on the fact all nodes have - been processed somehow. - """ - while self.candidates: - candidates = self.candidates - self.candidates = [] - self.will_not_build(candidates) - return None - - def _validate_pending_children(self): - """ - Validate the content of the pending_children set. Assert if an - internal error is found. - - This function is used strictly for debugging the taskmaster by - checking that no invariants are violated. It is not used in - normal operation. - - The pending_children set is used to detect cycles in the - dependency graph. We call a "pending child" a child that is - found in the "pending" state when checking the dependencies of - its parent node. - - A pending child can occur when the Taskmaster completes a loop - through a cycle. For example, lets imagine a graph made of - three node (A, B and C) making a cycle. The evaluation starts - at node A. The taskmaster first consider whether node A's - child B is up-to-date. Then, recursively, node B needs to - check whether node C is up-to-date. This leaves us with a - dependency graph looking like: - - Next candidate \ - \ - Node A (Pending) --> Node B(Pending) --> Node C (NoState) - ^ | - | | - +-------------------------------------+ - - Now, when the Taskmaster examines the Node C's child Node A, - it finds that Node A is in the "pending" state. Therefore, - Node A is a pending child of node C. - - Pending children indicate that the Taskmaster has potentially - loop back through a cycle. We say potentially because it could - also occur when a DAG is evaluated in parallel. For example, - consider the following graph: - - - Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... - | ^ - | | - +----------> Node D (NoState) --------+ - / - Next candidate / - - The Taskmaster first evaluates the nodes A, B, and C and - starts building some children of node C. Assuming, that the - maximum parallel level has not been reached, the Taskmaster - will examine Node D. It will find that Node C is a pending - child of Node D. - - In summary, evaluating a graph with a cycle will always - involve a pending child at one point. A pending child might - indicate either a cycle or a diamond-shaped DAG. Only a - fraction of the nodes ends-up being a "pending child" of - another node. This keeps the pending_children set small in - practice. - - We can differentiate between the two cases if we wait until - the end of the build. At this point, all the pending children - nodes due to a diamond-shaped DAG will have been properly - built (or will have failed to build). But, the pending - children involved in a cycle will still be in the pending - state. - - The taskmaster removes nodes from the pending_children set as - soon as a pending_children node moves out of the pending - state. This also helps to keep the pending_children set small. - """ - - for n in self.pending_children: - assert n.state in (NODE_PENDING, NODE_EXECUTING), \ - (str(n), StateString[n.state]) - assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents)) - for p in n.waiting_parents: - assert p.ref_count > 0, (str(n), str(p), p.ref_count) - - - def trace_message(self, message): - return 'Taskmaster: %s\n' % message - - def trace_node(self, node): - return '<%-10s %-3s %s>' % (StateString[node.get_state()], - node.ref_count, - repr(str(node))) - - def _find_next_ready_node(self): - """ - Finds the next node that is ready to be built. - - This is *the* main guts of the DAG walk. We loop through the - list of candidates, looking for something that has no un-built - children (i.e., that is a leaf Node or has dependencies that are - all leaf Nodes or up-to-date). Candidate Nodes are re-scanned - (both the target Node itself and its sources, which are always - scanned in the context of a given target) to discover implicit - dependencies. A Node that must wait for some children to be - built will be put back on the candidates list after the children - have finished building. A Node that has been put back on the - candidates list in this way may have itself (or its sources) - re-scanned, in order to handle generated header files (e.g.) and - the implicit dependencies therein. - - Note that this method does not do any signature calculation or - up-to-date check itself. All of that is handled by the Task - class. This is purely concerned with the dependency graph walk. - """ - - self.ready_exc = None - - T = self.trace - if T: T.write('\n' + self.trace_message('Looking for a node to evaluate')) - - while 1: - node = self.next_candidate() - if node is None: - if T: T.write(self.trace_message('No candidate anymore.') + '\n') - return None - - node = node.disambiguate() - state = node.get_state() - - # For debugging only: - # - # try: - # self._validate_pending_children() - # except: - # self.ready_exc = sys.exc_info() - # return node - - if CollectStats: - if not hasattr(node, 'stats'): - node.stats = Stats() - StatsNodes.append(node) - S = node.stats - S.considered = S.considered + 1 - else: - S = None - - if T: T.write(self.trace_message(' Considering node %s and its children:' % self.trace_node(node))) - - if state == NODE_NO_STATE: - # Mark this node as being on the execution stack: - node.set_state(NODE_PENDING) - elif state > NODE_PENDING: - # Skip this node if it has already been evaluated: - if S: S.already_handled = S.already_handled + 1 - if T: T.write(self.trace_message(' already handled (executed)')) - continue - - try: - children = node.children() - except SystemExit: - exc_value = sys.exc_info()[1] - e = SCons.Errors.ExplicitExit(node, exc_value.code) - self.ready_exc = (SCons.Errors.ExplicitExit, e) - if T: T.write(self.trace_message(' SystemExit')) - return node - except Exception, e: - # We had a problem just trying to figure out the - # children (like a child couldn't be linked in to a - # VariantDir, or a Scanner threw something). Arrange to - # raise the exception when the Task is "executed." - self.ready_exc = sys.exc_info() - if S: S.problem = S.problem + 1 - if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e)) - return node - - children_not_visited = [] - children_pending = set() - children_not_ready = [] - children_failed = False - - for child in chain(children,node.prerequisites): - childstate = child.get_state() - - if T: T.write(self.trace_message(' ' + self.trace_node(child))) - - if childstate == NODE_NO_STATE: - children_not_visited.append(child) - elif childstate == NODE_PENDING: - children_pending.add(child) - elif childstate == NODE_FAILED: - children_failed = True - - if childstate <= NODE_EXECUTING: - children_not_ready.append(child) - - - # These nodes have not even been visited yet. Add - # them to the list so that on some next pass we can - # take a stab at evaluating them (or their children). - children_not_visited.reverse() - self.candidates.extend(self.order(children_not_visited)) - #if T and children_not_visited: - # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited))) - # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates))) - - # Skip this node if any of its children have failed. - # - # This catches the case where we're descending a top-level - # target and one of our children failed while trying to be - # built by a *previous* descent of an earlier top-level - # target. - # - # It can also occur if a node is reused in multiple - # targets. One first descends though the one of the - # target, the next time occurs through the other target. - # - # Note that we can only have failed_children if the - # --keep-going flag was used, because without it the build - # will stop before diving in the other branch. - # - # Note that even if one of the children fails, we still - # added the other children to the list of candidate nodes - # to keep on building (--keep-going). - if children_failed: - node.set_state(NODE_FAILED) - - if S: S.child_failed = S.child_failed + 1 - if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node))) - continue - - if children_not_ready: - for child in children_not_ready: - # We're waiting on one or more derived targets - # that have not yet finished building. - if S: S.not_built = S.not_built + 1 - - # Add this node to the waiting parents lists of - # anything we're waiting on, with a reference - # count so we can be put back on the list for - # re-evaluation when they've all finished. - node.ref_count = node.ref_count + child.add_to_waiting_parents(node) - if T: T.write(self.trace_message(' adjusted ref count: %s, child %s' % - (self.trace_node(node), repr(str(child))))) - - if T: - for pc in children_pending: - T.write(self.trace_message(' adding %s to the pending children set\n' % - self.trace_node(pc))) - self.pending_children = self.pending_children | children_pending - - continue - - # Skip this node if it has side-effects that are - # currently being built: - wait_side_effects = False - for se in node.side_effects: - if se.get_state() == NODE_EXECUTING: - se.add_to_waiting_s_e(node) - wait_side_effects = True - - if wait_side_effects: - if S: S.side_effects = S.side_effects + 1 - continue - - # The default when we've gotten through all of the checks above: - # this node is ready to be built. - if S: S.build = S.build + 1 - if T: T.write(self.trace_message('Evaluating %s\n' % - self.trace_node(node))) - - # For debugging only: - # - # try: - # self._validate_pending_children() - # except: - # self.ready_exc = sys.exc_info() - # return node - - return node - - return None - - def next_task(self): - """ - Returns the next task to be executed. - - This simply asks for the next Node to be evaluated, and then wraps - it in the specific Task subclass with which we were initialized. - """ - node = self._find_next_ready_node() - - if node is None: - return None - - tlist = node.get_executor().targets - - task = self.tasker(self, tlist, node in self.original_top, node) - try: - task.make_ready() - except: - # We had a problem just trying to get this task ready (like - # a child couldn't be linked in to a VariantDir when deciding - # whether this node is current). Arrange to raise the - # exception when the Task is "executed." - self.ready_exc = sys.exc_info() - - if self.ready_exc: - task.exception_set(self.ready_exc) - - self.ready_exc = None - - return task - - def will_not_build(self, nodes, node_func=lambda n: None): - """ - Perform clean-up about nodes that will never be built. Invokes - a user defined function on all of these nodes (including all - of their parents). - """ - - T = self.trace - - pending_children = self.pending_children - - to_visit = set(nodes) - pending_children = pending_children - to_visit - - if T: - for n in nodes: - T.write(self.trace_message(' removing node %s from the pending children set\n' % - self.trace_node(n))) - try: - while 1: - try: - node = to_visit.pop() - except AttributeError: - # Python 1.5.2 - if len(to_visit): - node = to_visit[0] - to_visit.remove(node) - else: - break - - node_func(node) - - # Prune recursion by flushing the waiting children - # list immediately. - parents = node.waiting_parents - node.waiting_parents = set() - - to_visit = to_visit | parents - pending_children = pending_children - parents - - for p in parents: - p.ref_count = p.ref_count - 1 - if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' % - self.trace_node(p))) - except KeyError: - # The container to_visit has been emptied. - pass - - # We have the stick back the pending_children list into the - # task master because the python 1.5.2 compatibility does not - # allow us to use in-place updates - self.pending_children = pending_children - - def stop(self): - """ - Stops the current build completely. - """ - self.next_candidate = self.no_next_candidate - - def cleanup(self): - """ - Check for dependency cycles. - """ - if not self.pending_children: - return - - # TODO(1.5) - #nclist = [ (n, find_cycle([n], set())) for n in self.pending_children ] - nclist = map(lambda n: (n, find_cycle([n], set())), self.pending_children) - - # TODO(1.5) - #genuine_cycles = [ - # node for node, cycle in nclist - # if cycle or node.get_state() != NODE_EXECUTED - #] - genuine_cycles = filter(lambda t: t[1] or t[0].get_state() != NODE_EXECUTED, nclist) - if not genuine_cycles: - # All of the "cycles" found were single nodes in EXECUTED state, - # which is to say, they really weren't cycles. Just return. - return - - desc = 'Found dependency cycle(s):\n' - for node, cycle in nclist: - if cycle: - desc = desc + " " + string.join(map(str, cycle), " -> ") + "\n" - else: - desc = desc + \ - " Internal Error: no cycle found for node %s (%s) in state %s\n" % \ - (node, repr(node), StateString[node.get_state()]) - - raise SCons.Errors.UserError, desc diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/386asm.py b/tools/scons/scons-local-1.2.0/SCons/Tool/386asm.py deleted file mode 100644 index fc5c500048..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/386asm.py +++ /dev/null @@ -1,55 +0,0 @@ -"""SCons.Tool.386asm - -Tool specification for the 386ASM assembler for the Phar Lap ETS embedded -operating system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/386asm.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.PharLapCommon import addPharLapPaths -import SCons.Util - -as_module = __import__('as', globals(), locals(), []) - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - as_module.generate(env) - - env['AS'] = '386asm' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' - - addPharLapPaths(env) - -def exists(env): - return env.Detect('386asm') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/BitKeeper.py b/tools/scons/scons-local-1.2.0/SCons/Tool/BitKeeper.py deleted file mode 100644 index 15d1f0ad3c..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/BitKeeper.py +++ /dev/null @@ -1,59 +0,0 @@ -"""SCons.Tool.BitKeeper.py - -Tool-specific initialization for the BitKeeper source code control -system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/BitKeeper.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - BitKeeper to an Environment.""" - - def BitKeeperFactory(env=env): - """ """ - act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'BitKeeper', BitKeeperFactory) - env.BitKeeper = BitKeeperFactory - - env['BITKEEPER'] = 'bk' - env['BITKEEPERGET'] = '$BITKEEPER get' - env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') - env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' - -def exists(env): - return env.Detect('bk') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/CVS.py b/tools/scons/scons-local-1.2.0/SCons/Tool/CVS.py deleted file mode 100644 index e1cc04d1ed..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/CVS.py +++ /dev/null @@ -1,67 +0,0 @@ -"""SCons.Tool.CVS.py - -Tool-specific initialization for CVS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/CVS.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - CVS to an Environment.""" - - def CVSFactory(repos, module='', env=env): - """ """ - # fail if repos is not an absolute path name? - if module != '': - # Don't use os.path.join() because the name we fetch might - # be across a network and must use POSIX slashes as separators. - module = module + '/' - env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' - act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR') - return SCons.Builder.Builder(action = act, - env = env, - CVSREPOSITORY = repos, - CVSMODULE = module) - - #setattr(env, 'CVS', CVSFactory) - env.CVS = CVSFactory - - env['CVS'] = 'cvs' - env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') - env['CVSCOFLAGS'] = SCons.Util.CLVar('') - env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' - -def exists(env): - return env.Detect('cvs') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/FortranCommon.py b/tools/scons/scons-local-1.2.0/SCons/Tool/FortranCommon.py deleted file mode 100644 index 8d3204ff14..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/FortranCommon.py +++ /dev/null @@ -1,241 +0,0 @@ -"""SCons.Tool.FortranCommon - -Stuff for processing Fortran, common to all fortran dialects. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/FortranCommon.py 3842 2008/12/20 22:59:52 scons" - -import re -import string -import os.path - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util - -def isfortran(env, source): - """Return 1 if any of code in source has fortran files in it, 0 - otherwise.""" - try: - fsuffixes = env['FORTRANSUFFIXES'] - except KeyError: - # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look - # for fortran sources. - return 0 - - if not source: - # Source might be None for unusual cases like SConf. - return 0 - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext in fsuffixes: - return 1 - return 0 - -def _fortranEmitter(target, source, env): - node = source[0].rfile() - if not node.exists() and not node.is_derived(): - print "Could not locate " + str(node.name) - return ([], []) - mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" - cre = re.compile(mod_regex,re.M) - # Retrieve all USE'd module names - modules = cre.findall(node.get_contents()) - # Remove unique items from the list - modules = SCons.Util.unique(modules) - # Convert module name to a .mod filename - suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) - moddir = env.subst('$FORTRANMODDIR', target=target, source=source) - modules = map(lambda x, s=suffix: string.lower(x) + s, modules) - for m in modules: - target.append(env.fs.File(m, moddir)) - return (target, source) - -def FortranEmitter(target, source, env): - target, source = _fortranEmitter(target, source, env) - return SCons.Defaults.StaticObjectEmitter(target, source, env) - -def ShFortranEmitter(target, source, env): - target, source = _fortranEmitter(target, source, env) - return SCons.Defaults.SharedObjectEmitter(target, source, env) - -def ComputeFortranSuffixes(suffixes, ppsuffixes): - """suffixes are fortran source files, and ppsuffixes the ones to be - pre-processed. Both should be sequences, not strings.""" - assert len(suffixes) > 0 - s = suffixes[0] - sup = string.upper(s) - upper_suffixes = map(string.upper, suffixes) - if SCons.Util.case_sensitive_suffixes(s, sup): - ppsuffixes.extend(upper_suffixes) - else: - suffixes.extend(upper_suffixes) - -def CreateDialectActions(dialect): - """Create dialect specific actions.""" - CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) - CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) - ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) - ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) - - return CompAction, CompPPAction, ShCompAction, ShCompPPAction - -def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): - """Add dialect specific construction variables.""" - ComputeFortranSuffixes(suffixes, ppsuffixes) - - fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) - - for suffix in suffixes + ppsuffixes: - SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) - - env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) - - compaction, compppaction, shcompaction, shcompppaction = \ - CreateDialectActions(dialect) - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in suffixes: - static_obj.add_action(suffix, compaction) - shared_obj.add_action(suffix, shcompaction) - static_obj.add_emitter(suffix, FortranEmitter) - shared_obj.add_emitter(suffix, ShFortranEmitter) - - for suffix in ppsuffixes: - static_obj.add_action(suffix, compppaction) - shared_obj.add_action(suffix, shcompppaction) - static_obj.add_emitter(suffix, FortranEmitter) - shared_obj.add_emitter(suffix, ShFortranEmitter) - - if not env.has_key('%sFLAGS' % dialect): - env['%sFLAGS' % dialect] = SCons.Util.CLVar('') - - if not env.has_key('SH%sFLAGS' % dialect): - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) - - # If a tool does not define fortran prefix/suffix for include path, use C ones - if not env.has_key('INC%sPREFIX' % dialect): - env['INC%sPREFIX' % dialect] = '$INCPREFIX' - - if not env.has_key('INC%sSUFFIX' % dialect): - env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' - - env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) - - if support_module == 1: - env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - else: - env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - -def add_fortran_to_env(env): - """Add Builders and construction variables for Fortran to an Environment.""" - try: - FortranSuffixes = env['FORTRANFILESUFFIXES'] - except KeyError: - FortranSuffixes = ['.f', '.for', '.ftn'] - - #print "Adding %s to fortran suffixes" % FortranSuffixes - try: - FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] - except KeyError: - FortranPPSuffixes = ['.fpp', '.FPP'] - - DialectAddToEnv(env, "FORTRAN", FortranSuffixes, - FortranPPSuffixes, support_module = 1) - - env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX - env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX - - env['FORTRANMODDIR'] = '' # where the compiler should place .mod files - env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX - env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX - env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - -def add_f77_to_env(env): - """Add Builders and construction variables for f77 to an Environment.""" - try: - F77Suffixes = env['F77FILESUFFIXES'] - except KeyError: - F77Suffixes = ['.f77'] - - #print "Adding %s to f77 suffixes" % F77Suffixes - try: - F77PPSuffixes = env['F77PPFILESUFFIXES'] - except KeyError: - F77PPSuffixes = [] - - DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) - -def add_f90_to_env(env): - """Add Builders and construction variables for f90 to an Environment.""" - try: - F90Suffixes = env['F90FILESUFFIXES'] - except KeyError: - F90Suffixes = ['.f90'] - - #print "Adding %s to f90 suffixes" % F90Suffixes - try: - F90PPSuffixes = env['F90PPFILESUFFIXES'] - except KeyError: - F90PPSuffixes = [] - - DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, - support_module = 1) - -def add_f95_to_env(env): - """Add Builders and construction variables for f95 to an Environment.""" - try: - F95Suffixes = env['F95FILESUFFIXES'] - except KeyError: - F95Suffixes = ['.f95'] - - #print "Adding %s to f95 suffixes" % F95Suffixes - try: - F95PPSuffixes = env['F95PPFILESUFFIXES'] - except KeyError: - F95PPSuffixes = [] - - DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, - support_module = 1) - -def add_all_to_env(env): - """Add builders and construction variables for all supported fortran - dialects.""" - add_fortran_to_env(env) - add_f77_to_env(env) - add_f90_to_env(env) - add_f95_to_env(env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/JavaCommon.py b/tools/scons/scons-local-1.2.0/SCons/Tool/JavaCommon.py deleted file mode 100644 index 12c31f37fb..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/JavaCommon.py +++ /dev/null @@ -1,317 +0,0 @@ -"""SCons.Tool.JavaCommon - -Stuff for processing Java. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/JavaCommon.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import re -import string - -java_parsing = 1 - -default_java_version = '1.4' - -if java_parsing: - # Parse Java files for class names. - # - # This is a really cool parser from Charles Crain - # that finds appropriate class names in Java source. - - # A regular expression that will find, in a java file: - # newlines; - # double-backslashes; - # a single-line comment "//"; - # single or double quotes preceeded by a backslash; - # single quotes, double quotes, open or close braces, semi-colons, - # periods, open or close parentheses; - # floating-point numbers; - # any alphanumeric token (keyword, class name, specifier); - # any alphanumeric token surrounded by angle brackets (generics); - # the multi-line comment begin and end tokens /* and */; - # array declarations "[]". - _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + - r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + - r'/\*|\*/|\[\])') - - class OuterState: - """The initial state for parsing a Java file for classes, - interfaces, and anonymous inner classes.""" - def __init__(self, version=default_java_version): - - if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6'): - msg = "Java version %s not supported" % version - raise NotImplementedError, msg - - self.version = version - self.listClasses = [] - self.listOutputs = [] - self.stackBrackets = [] - self.brackets = 0 - self.nextAnon = 1 - self.localClasses = [] - self.stackAnonClassBrackets = [] - self.anonStacksStack = [[0]] - self.package = None - - def trace(self): - pass - - def __getClassState(self): - try: - return self.classState - except AttributeError: - ret = ClassState(self) - self.classState = ret - return ret - - def __getPackageState(self): - try: - return self.packageState - except AttributeError: - ret = PackageState(self) - self.packageState = ret - return ret - - def __getAnonClassState(self): - try: - return self.anonState - except AttributeError: - self.outer_state = self - ret = SkipState(1, AnonClassState(self)) - self.anonState = ret - return ret - - def __getSkipState(self): - try: - return self.skipState - except AttributeError: - ret = SkipState(1, self) - self.skipState = ret - return ret - - def __getAnonStack(self): - return self.anonStacksStack[-1] - - def openBracket(self): - self.brackets = self.brackets + 1 - - def closeBracket(self): - self.brackets = self.brackets - 1 - if len(self.stackBrackets) and \ - self.brackets == self.stackBrackets[-1]: - self.listOutputs.append(string.join(self.listClasses, '$')) - self.localClasses.pop() - self.listClasses.pop() - self.anonStacksStack.pop() - self.stackBrackets.pop() - if len(self.stackAnonClassBrackets) and \ - self.brackets == self.stackAnonClassBrackets[-1]: - self.__getAnonStack().pop() - self.stackAnonClassBrackets.pop() - - def parseToken(self, token): - if token[:2] == '//': - return IgnoreState('\n', self) - elif token == '/*': - return IgnoreState('*/', self) - elif token == '{': - self.openBracket() - elif token == '}': - self.closeBracket() - elif token in [ '"', "'" ]: - return IgnoreState(token, self) - elif token == "new": - # anonymous inner class - if len(self.listClasses) > 0: - return self.__getAnonClassState() - return self.__getSkipState() # Skip the class name - elif token in ['class', 'interface', 'enum']: - if len(self.listClasses) == 0: - self.nextAnon = 1 - self.stackBrackets.append(self.brackets) - return self.__getClassState() - elif token == 'package': - return self.__getPackageState() - elif token == '.': - # Skip the attribute, it might be named "class", in which - # case we don't want to treat the following token as - # an inner class name... - return self.__getSkipState() - return self - - def addAnonClass(self): - """Add an anonymous inner class""" - if self.version in ('1.1', '1.2', '1.3', '1.4'): - clazz = self.listClasses[0] - self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6'): - self.stackAnonClassBrackets.append(self.brackets) - className = [] - className.extend(self.listClasses) - self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 - for anon in self.__getAnonStack(): - className.append(str(anon)) - self.listOutputs.append(string.join(className, '$')) - - self.nextAnon = self.nextAnon + 1 - self.__getAnonStack().append(0) - - def setPackage(self, package): - self.package = package - - class AnonClassState: - """A state that looks for anonymous inner classes.""" - def __init__(self, old_state): - # outer_state is always an instance of OuterState - self.outer_state = old_state.outer_state - self.old_state = old_state - self.brace_level = 0 - def parseToken(self, token): - # This is an anonymous class if and only if the next - # non-whitespace token is a bracket. Everything between - # braces should be parsed as normal java code. - if token[:2] == '//': - return IgnoreState('\n', self) - elif token == '/*': - return IgnoreState('*/', self) - elif token == '\n': - return self - elif token[0] == '<' and token[-1] == '>': - return self - elif token == '(': - self.brace_level = self.brace_level + 1 - return self - if self.brace_level > 0: - if token == 'new': - # look further for anonymous inner class - return SkipState(1, AnonClassState(self)) - elif token in [ '"', "'" ]: - return IgnoreState(token, self) - elif token == ')': - self.brace_level = self.brace_level - 1 - return self - if token == '{': - self.outer_state.addAnonClass() - return self.old_state.parseToken(token) - - class SkipState: - """A state that will skip a specified number of tokens before - reverting to the previous state.""" - def __init__(self, tokens_to_skip, old_state): - self.tokens_to_skip = tokens_to_skip - self.old_state = old_state - def parseToken(self, token): - self.tokens_to_skip = self.tokens_to_skip - 1 - if self.tokens_to_skip < 1: - return self.old_state - return self - - class ClassState: - """A state we go into when we hit a class or interface keyword.""" - def __init__(self, outer_state): - # outer_state is always an instance of OuterState - self.outer_state = outer_state - def parseToken(self, token): - # the next non-whitespace token should be the name of the class - if token == '\n': - return self - # If that's an inner class which is declared in a method, it - # requires an index prepended to the class-name, e.g. - # 'Foo$1Inner' (Tigris Issue 2087) - if self.outer_state.localClasses and \ - self.outer_state.stackBrackets[-1] > \ - self.outer_state.stackBrackets[-2]+1: - locals = self.outer_state.localClasses[-1] - try: - idx = locals[token] - locals[token] = locals[token]+1 - except KeyError: - locals[token] = 1 - token = str(locals[token]) + token - self.outer_state.localClasses.append({}) - self.outer_state.listClasses.append(token) - self.outer_state.anonStacksStack.append([0]) - return self.outer_state - - class IgnoreState: - """A state that will ignore all tokens until it gets to a - specified token.""" - def __init__(self, ignore_until, old_state): - self.ignore_until = ignore_until - self.old_state = old_state - def parseToken(self, token): - if self.ignore_until == token: - return self.old_state - return self - - class PackageState: - """The state we enter when we encounter the package keyword. - We assume the next token will be the package name.""" - def __init__(self, outer_state): - # outer_state is always an instance of OuterState - self.outer_state = outer_state - def parseToken(self, token): - self.outer_state.setPackage(token) - return self.outer_state - - def parse_java_file(fn, version=default_java_version): - return parse_java(open(fn, 'r').read(), version) - - def parse_java(contents, version=default_java_version, trace=None): - """Parse a .java file and return a double of package directory, - plus a list of .class files that compiling that .java file will - produce""" - package = None - initial = OuterState(version) - currstate = initial - for token in _reToken.findall(contents): - # The regex produces a bunch of groups, but only one will - # have anything in it. - currstate = currstate.parseToken(token) - if trace: trace(token, currstate) - if initial.package: - package = string.replace(initial.package, '.', os.sep) - return (package, initial.listOutputs) - -else: - # Don't actually parse Java files for class names. - # - # We might make this a configurable option in the future if - # Java-file parsing takes too long (although it shouldn't relative - # to how long the Java compiler itself seems to take...). - - def parse_java_file(fn): - """ "Parse" a .java file. - - This actually just splits the file name, so the assumption here - is that the file name matches the public class name, and that - the path to the file is the same as the package name. - """ - return os.path.split(file) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/Perforce.py b/tools/scons/scons-local-1.2.0/SCons/Tool/Perforce.py deleted file mode 100644 index 97049f6467..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/Perforce.py +++ /dev/null @@ -1,98 +0,0 @@ -"""SCons.Tool.Perforce.py - -Tool-specific initialization for Perforce Source Code Management system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/Perforce.py 3842 2008/12/20 22:59:52 scons" - -import os - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Util - -# This function should maybe be moved to SCons.Util? -from SCons.Tool.PharLapCommon import addPathIfNotExists - - - -# Variables that we want to import from the base OS environment. -_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', - 'P4CHARSET', 'P4LANGUAGE', 'SYSTEMROOT' ] - -PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR') - -def generate(env): - """Add a Builder factory function and construction variables for - Perforce to an Environment.""" - - def PerforceFactory(env=env): - """ """ - return SCons.Builder.Builder(action = PerforceAction, env = env) - - #setattr(env, 'Perforce', PerforceFactory) - env.Perforce = PerforceFactory - - env['P4'] = 'p4' - env['P4FLAGS'] = SCons.Util.CLVar('') - env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' - try: - environ = env['ENV'] - except KeyError: - environ = {} - env['ENV'] = environ - - # Perforce seems to use the PWD environment variable rather than - # calling getcwd() for itself, which is odd. If no PWD variable - # is present, p4 WILL call getcwd, but this seems to cause problems - # with good ol' Windows's tilde-mangling for long file names. - environ['PWD'] = env.Dir('#').get_abspath() - - for var in _import_env: - v = os.environ.get(var) - if v: - environ[var] = v - - if SCons.Util.can_read_reg: - # If we can read the registry, add the path to Perforce to our environment. - try: - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Perforce\\environment') - val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') - addPathIfNotExists(environ, 'PATH', val) - except SCons.Util.RegError: - # Can't detect where Perforce is, hope the user has it set in the - # PATH. - pass - -def exists(env): - return env.Detect('p4') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/PharLapCommon.py b/tools/scons/scons-local-1.2.0/SCons/Tool/PharLapCommon.py deleted file mode 100644 index 76a566ab83..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/PharLapCommon.py +++ /dev/null @@ -1,132 +0,0 @@ -"""SCons.Tool.PharLapCommon - -This module contains common code used by all Tools for the -Phar Lap ETS tool chain. Right now, this is linkloc and -386asm. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import SCons.Errors -import SCons.Util -import re -import string - -def getPharLapPath(): - """Reads the registry to find the installed path of the Phar Lap ETS - development kit. - - Raises UserError if no installed version of Phar Lap can - be found.""" - - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - try: - k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - 'SOFTWARE\\Pharlap\\ETS') - val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') - - # The following is a hack...there is (not surprisingly) - # an odd issue in the Phar Lap plug in that inserts - # a bunch of junk data after the phar lap path in the - # registry. We must trim it. - idx=val.find('\0') - if idx >= 0: - val = val[:idx] - - return os.path.normpath(val) - except SCons.Util.RegError: - raise SCons.Errors.UserError, "Cannot find Phar Lap ETS path in the registry. Is it installed properly?" - -REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') - -def getPharLapVersion(): - """Returns the version of the installed ETS Tool Suite as a - decimal number. This version comes from the ETS_VER #define in - the embkern.h header. For example, '#define ETS_VER 1010' (which - is what Phar Lap 10.1 defines) would cause this method to return - 1010. Phar Lap 9.1 does not have such a #define, but this method - will return 910 as a default. - - Raises UserError if no installed version of Phar Lap can - be found.""" - - include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) - if not os.path.exists(include_path): - raise SCons.Errors.UserError, "Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?" - mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) - if mo: - return int(mo.group(1)) - # Default return for Phar Lap 9.1 - return 910 - -def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): - """This function will take 'key' out of the dictionary - 'env_dict', then add the path 'path' to that key if it is not - already there. This treats the value of env_dict[key] as if it - has a similar format to the PATH variable...a list of paths - separated by tokens. The 'path' will get added to the list if it - is not already there.""" - try: - is_list = 1 - paths = env_dict[key] - if not SCons.Util.is_List(env_dict[key]): - paths = string.split(paths, sep) - is_list = 0 - if not os.path.normcase(path) in map(os.path.normcase, paths): - paths = [ path ] + paths - if is_list: - env_dict[key] = paths - else: - env_dict[key] = string.join(paths, sep) - except KeyError: - env_dict[key] = path - -def addPharLapPaths(env): - """This function adds the path to the Phar Lap binaries, includes, - and libraries, if they are not already there.""" - ph_path = getPharLapPath() - - try: - env_dict = env['ENV'] - except KeyError: - env_dict = {} - env['ENV'] = env_dict - addPathIfNotExists(env_dict, 'PATH', - os.path.join(ph_path, 'bin')) - addPathIfNotExists(env_dict, 'INCLUDE', - os.path.join(ph_path, 'include')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, 'lib')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, os.path.normpath('lib/vclib'))) - - env['PHARLAP_PATH'] = getPharLapPath() - env['PHARLAP_VERSION'] = str(getPharLapVersion()) - diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/RCS.py b/tools/scons/scons-local-1.2.0/SCons/Tool/RCS.py deleted file mode 100644 index 6d47060487..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/RCS.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.RCS.py - -Tool-specific initialization for RCS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/RCS.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - RCS to an Environment.""" - - def RCSFactory(env=env): - """ """ - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'RCS', RCSFactory) - env.RCS = RCSFactory - - env['RCS'] = 'rcs' - env['RCS_CO'] = 'co' - env['RCS_COFLAGS'] = SCons.Util.CLVar('') - env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' - -def exists(env): - return env.Detect('rcs') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/SCCS.py b/tools/scons/scons-local-1.2.0/SCons/Tool/SCCS.py deleted file mode 100644 index 842db137fc..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/SCCS.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.SCCS.py - -Tool-specific initialization for SCCS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/SCCS.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - SCCS to an Environment.""" - - def SCCSFactory(env=env): - """ """ - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'SCCS', SCCSFactory) - env.SCCS = SCCSFactory - - env['SCCS'] = 'sccs' - env['SCCSFLAGS'] = SCons.Util.CLVar('') - env['SCCSGETFLAGS'] = SCons.Util.CLVar('') - env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' - -def exists(env): - return env.Detect('sccs') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/Subversion.py b/tools/scons/scons-local-1.2.0/SCons/Tool/Subversion.py deleted file mode 100644 index a593c6abe0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/Subversion.py +++ /dev/null @@ -1,65 +0,0 @@ -"""SCons.Tool.Subversion.py - -Tool-specific initialization for Subversion. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/Subversion.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - Subversion to an Environment.""" - - def SubversionFactory(repos, module='', env=env): - """ """ - # fail if repos is not an absolute path name? - if module != '': - module = os.path.join(module, '') - act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR') - return SCons.Builder.Builder(action = act, - env = env, - SVNREPOSITORY = repos, - SVNMODULE = module) - - #setattr(env, 'Subversion', SubversionFactory) - env.Subversion = SubversionFactory - - env['SVN'] = 'svn' - env['SVNFLAGS'] = SCons.Util.CLVar('') - env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' - -def exists(env): - return env.Detect('svn') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Tool/__init__.py deleted file mode 100644 index 0b032820bc..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/__init__.py +++ /dev/null @@ -1,667 +0,0 @@ -"""SCons.Tool - -SCons tool selection. - -This looks for modules that define a callable object that can modify -a construction environment as appropriate for a given tool (or tool -chain). - -Note that because this subsystem just *selects* a callable that can -modify a construction environment, it's possible for people to define -their own "tool specification" in an arbitrary callable function. No -one needs to use or tie in to this subsystem in order to roll their own -tool definition. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/__init__.py 3842 2008/12/20 22:59:52 scons" - -import imp -import sys - -import SCons.Builder -import SCons.Errors -import SCons.Node.FS -import SCons.Scanner -import SCons.Scanner.C -import SCons.Scanner.D -import SCons.Scanner.LaTeX -import SCons.Scanner.Prog - -DefaultToolpath=[] - -CScanner = SCons.Scanner.C.CScanner() -DScanner = SCons.Scanner.D.DScanner() -LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() -PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() -ProgramScanner = SCons.Scanner.Prog.ProgramScanner() -SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') - -CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", - ".h", ".H", ".hxx", ".hpp", ".hh", - ".F", ".fpp", ".FPP", - ".m", ".mm", - ".S", ".spp", ".SPP"] - -DSuffixes = ['.d'] - -IDLSuffixes = [".idl", ".IDL"] - -LaTeXSuffixes = [".tex", ".ltx", ".latex"] - -for suffix in CSuffixes: - SourceFileScanner.add_scanner(suffix, CScanner) - -for suffix in DSuffixes: - SourceFileScanner.add_scanner(suffix, DScanner) - -# FIXME: what should be done here? Two scanners scan the same extensions, -# but look for different files, e.g., "picture.eps" vs. "picture.pdf". -# The builders for DVI and PDF explicitly reference their scanners -# I think that means this is not needed??? -for suffix in LaTeXSuffixes: - SourceFileScanner.add_scanner(suffix, LaTeXScanner) - SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner) - -class Tool: - def __init__(self, name, toolpath=[], **kw): - self.name = name - self.toolpath = toolpath + DefaultToolpath - # remember these so we can merge them into the call - self.init_kw = kw - - module = self._tool_module() - self.generate = module.generate - self.exists = module.exists - if hasattr(module, 'options'): - self.options = module.options - - def _tool_module(self): - # TODO: Interchange zipimport with normal initilization for better error reporting - oldpythonpath = sys.path - sys.path = self.toolpath + sys.path - - try: - try: - file, path, desc = imp.find_module(self.name, self.toolpath) - try: - return imp.load_module(self.name, file, path, desc) - finally: - if file: - file.close() - except ImportError, e: - if str(e)!="No module named %s"%self.name: - raise SCons.Errors.EnvironmentError, e - try: - import zipimport - except ImportError: - pass - else: - for aPath in self.toolpath: - try: - importer = zipimport.zipimporter(aPath) - return importer.load_module(self.name) - except ImportError, e: - pass - finally: - sys.path = oldpythonpath - - full_name = 'SCons.Tool.' + self.name - try: - return sys.modules[full_name] - except KeyError: - try: - smpath = sys.modules['SCons.Tool'].__path__ - try: - file, path, desc = imp.find_module(self.name, smpath) - module = imp.load_module(full_name, file, path, desc) - setattr(SCons.Tool, self.name, module) - if file: - file.close() - return module - except ImportError, e: - if str(e)!="No module named %s"%self.name: - raise SCons.Errors.EnvironmentError, e - try: - import zipimport - importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) - module = importer.load_module(full_name) - setattr(SCons.Tool, self.name, module) - return module - except ImportError, e: - m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError, m - except ImportError, e: - m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError, m - - def __call__(self, env, *args, **kw): - if self.init_kw is not None: - # Merge call kws into init kws; - # but don't bash self.init_kw. - if kw is not None: - call_kw = kw - kw = self.init_kw.copy() - kw.update(call_kw) - else: - kw = self.init_kw - env.Append(TOOLS = [ self.name ]) - if hasattr(self, 'options'): - import SCons.Variables - if not env.has_key('options'): - from SCons.Script import ARGUMENTS - env['options']=SCons.Variables.Variables(args=ARGUMENTS) - opts=env['options'] - - self.options(opts) - opts.Update(env) - - apply(self.generate, ( env, ) + args, kw) - - def __str__(self): - return self.name - -########################################################################## -# Create common executable program / library / object builders - -def createProgBuilder(env): - """This is a utility function that creates the Program - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - program = env['BUILDERS']['Program'] - except KeyError: - import SCons.Defaults - program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction, - emitter = '$PROGEMITTER', - prefix = '$PROGPREFIX', - suffix = '$PROGSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'Object', - target_scanner = ProgramScanner) - env['BUILDERS']['Program'] = program - - return program - -def createStaticLibBuilder(env): - """This is a utility function that creates the StaticLibrary - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - static_lib = env['BUILDERS']['StaticLibrary'] - except KeyError: - action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] - if env.Detect('ranlib'): - ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") - action_list.append(ranlib_action) - - static_lib = SCons.Builder.Builder(action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'StaticObject') - env['BUILDERS']['StaticLibrary'] = static_lib - env['BUILDERS']['Library'] = static_lib - - return static_lib - -def createSharedLibBuilder(env): - """This is a utility function that creates the SharedLibrary - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - shared_lib = env['BUILDERS']['SharedLibrary'] - except KeyError: - import SCons.Defaults - action_list = [ SCons.Defaults.SharedCheck, - SCons.Defaults.ShLinkAction ] - shared_lib = SCons.Builder.Builder(action = action_list, - emitter = "$SHLIBEMITTER", - prefix = '$SHLIBPREFIX', - suffix = '$SHLIBSUFFIX', - target_scanner = ProgramScanner, - src_suffix = '$SHOBJSUFFIX', - src_builder = 'SharedObject') - env['BUILDERS']['SharedLibrary'] = shared_lib - - return shared_lib - -def createLoadableModuleBuilder(env): - """This is a utility function that creates the LoadableModule - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - ld_module = env['BUILDERS']['LoadableModule'] - except KeyError: - import SCons.Defaults - action_list = [ SCons.Defaults.SharedCheck, - SCons.Defaults.LdModuleLinkAction ] - ld_module = SCons.Builder.Builder(action = action_list, - emitter = "$SHLIBEMITTER", - prefix = '$LDMODULEPREFIX', - suffix = '$LDMODULESUFFIX', - target_scanner = ProgramScanner, - src_suffix = '$SHOBJSUFFIX', - src_builder = 'SharedObject') - env['BUILDERS']['LoadableModule'] = ld_module - - return ld_module - -def createObjBuilders(env): - """This is a utility function that creates the StaticObject - and SharedObject Builders in an Environment if they - are not there already. - - If they are there already, we return the existing ones. - - This is a separate function because soooo many Tools - use this functionality. - - The return is a 2-tuple of (StaticObject, SharedObject) - """ - - - try: - static_obj = env['BUILDERS']['StaticObject'] - except KeyError: - static_obj = SCons.Builder.Builder(action = {}, - emitter = {}, - prefix = '$OBJPREFIX', - suffix = '$OBJSUFFIX', - src_builder = ['CFile', 'CXXFile'], - source_scanner = SourceFileScanner, - single_source = 1) - env['BUILDERS']['StaticObject'] = static_obj - env['BUILDERS']['Object'] = static_obj - - try: - shared_obj = env['BUILDERS']['SharedObject'] - except KeyError: - shared_obj = SCons.Builder.Builder(action = {}, - emitter = {}, - prefix = '$SHOBJPREFIX', - suffix = '$SHOBJSUFFIX', - src_builder = ['CFile', 'CXXFile'], - source_scanner = SourceFileScanner, - single_source = 1) - env['BUILDERS']['SharedObject'] = shared_obj - - return (static_obj, shared_obj) - -def createCFileBuilders(env): - """This is a utility function that creates the CFile/CXXFile - Builders in an Environment if they - are not there already. - - If they are there already, we return the existing ones. - - This is a separate function because soooo many Tools - use this functionality. - - The return is a 2-tuple of (CFile, CXXFile) - """ - - try: - c_file = env['BUILDERS']['CFile'] - except KeyError: - c_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$CFILESUFFIX'}) - env['BUILDERS']['CFile'] = c_file - - env.SetDefault(CFILESUFFIX = '.c') - - try: - cxx_file = env['BUILDERS']['CXXFile'] - except KeyError: - cxx_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$CXXFILESUFFIX'}) - env['BUILDERS']['CXXFile'] = cxx_file - env.SetDefault(CXXFILESUFFIX = '.cc') - - return (c_file, cxx_file) - -########################################################################## -# Create common Java builders - -def CreateJarBuilder(env): - try: - java_jar = env['BUILDERS']['Jar'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') - java_jar = SCons.Builder.Builder(action = jar_com, - suffix = '$JARSUFFIX', - src_suffix = '$JAVACLASSSUFIX', - src_builder = 'JavaClassFile', - source_factory = fs.Entry) - env['BUILDERS']['Jar'] = java_jar - return java_jar - -def CreateJavaHBuilder(env): - try: - java_javah = env['BUILDERS']['JavaH'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') - java_javah = SCons.Builder.Builder(action = java_javah_com, - src_suffix = '$JAVACLASSSUFFIX', - target_factory = fs.Entry, - source_factory = fs.File, - src_builder = 'JavaClassFile') - env['BUILDERS']['JavaH'] = java_javah - return java_javah - -def CreateJavaClassFileBuilder(env): - try: - java_class_file = env['BUILDERS']['JavaClassFile'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - java_class_file = SCons.Builder.Builder(action = javac_com, - emitter = {}, - #suffix = '$JAVACLASSSUFFIX', - src_suffix = '$JAVASUFFIX', - src_builder = ['JavaFile'], - target_factory = fs.Entry, - source_factory = fs.File) - env['BUILDERS']['JavaClassFile'] = java_class_file - return java_class_file - -def CreateJavaClassDirBuilder(env): - try: - java_class_dir = env['BUILDERS']['JavaClassDir'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - java_class_dir = SCons.Builder.Builder(action = javac_com, - emitter = {}, - target_factory = fs.Dir, - source_factory = fs.Dir) - env['BUILDERS']['JavaClassDir'] = java_class_dir - return java_class_dir - -def CreateJavaFileBuilder(env): - try: - java_file = env['BUILDERS']['JavaFile'] - except KeyError: - java_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$JAVASUFFIX'}) - env['BUILDERS']['JavaFile'] = java_file - env['JAVASUFFIX'] = '.java' - return java_file - -class ToolInitializerMethod: - """ - This is added to a construction environment in place of a - method(s) normally called for a Builder (env.Object, env.StaticObject, - etc.). When called, it has its associated ToolInitializer - object search the specified list of tools and apply the first - one that exists to the construction environment. It then calls - whatever builder was (presumably) added to the construction - environment in place of this particular instance. - """ - def __init__(self, name, initializer): - """ - Note: we store the tool name as __name__ so it can be used by - the class that attaches this to a construction environment. - """ - self.__name__ = name - self.initializer = initializer - - def get_builder(self, env): - """ - Returns the appropriate real Builder for this method name - after having the associated ToolInitializer object apply - the appropriate Tool module. - """ - builder = getattr(env, self.__name__) - - self.initializer.apply_tools(env) - - builder = getattr(env, self.__name__) - if builder is self: - # There was no Builder added, which means no valid Tool - # for this name was found (or possibly there's a mismatch - # between the name we were called by and the Builder name - # added by the Tool module). - return None - - self.initializer.remove_methods(env) - - return builder - - def __call__(self, env, *args, **kw): - """ - """ - builder = self.get_builder(env) - if builder is None: - return [], [] - return apply(builder, args, kw) - -class ToolInitializer: - """ - A class for delayed initialization of Tools modules. - - Instances of this class associate a list of Tool modules with - a list of Builder method names that will be added by those Tool - modules. As part of instantiating this object for a particular - construction environment, we also add the appropriate - ToolInitializerMethod objects for the various Builder methods - that we want to use to delay Tool searches until necessary. - """ - def __init__(self, env, tools, names): - if not SCons.Util.is_List(tools): - tools = [tools] - if not SCons.Util.is_List(names): - names = [names] - self.env = env - self.tools = tools - self.names = names - self.methods = {} - for name in names: - method = ToolInitializerMethod(name, self) - self.methods[name] = method - env.AddMethod(method) - - def remove_methods(self, env): - """ - Removes the methods that were added by the tool initialization - so we no longer copy and re-bind them when the construction - environment gets cloned. - """ - for method in self.methods.values(): - env.RemoveMethod(method) - - def apply_tools(self, env): - """ - Searches the list of associated Tool modules for one that - exists, and applies that to the construction environment. - """ - for t in self.tools: - tool = SCons.Tool.Tool(t) - if tool.exists(env): - env.Tool(tool) - return - - # If we fall through here, there was no tool module found. - # This is where we can put an informative error message - # about the inability to find the tool. We'll start doing - # this as we cut over more pre-defined Builder+Tools to use - # the ToolInitializer class. - -def Initializers(env): - ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs']) - def Install(self, *args, **kw): - return apply(self._InternalInstall, args, kw) - def InstallAs(self, *args, **kw): - return apply(self._InternalInstallAs, args, kw) - env.AddMethod(Install) - env.AddMethod(InstallAs) - -def FindTool(tools, env): - for tool in tools: - t = Tool(tool) - if t.exists(env): - return tool - return None - -def FindAllTools(tools, env): - def ToolExists(tool, env=env): - return Tool(tool).exists(env) - return filter (ToolExists, tools) - -def tool_list(platform, env): - - # XXX this logic about what tool to prefer on which platform - # should be moved into either the platform files or - # the tool files themselves. - # The search orders here are described in the man page. If you - # change these search orders, update the man page as well. - if str(platform) == 'win32': - "prefer Microsoft tools on Windows" - linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] - c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] - cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] - assemblers = ['masm', 'nasm', 'gas', '386asm' ] - fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] - ars = ['mslib', 'ar', 'tlib'] - elif str(platform) == 'os2': - "prefer IBM tools on OS/2" - linkers = ['ilink', 'gnulink', 'mslink'] - c_compilers = ['icc', 'gcc', 'msvc', 'cc'] - cxx_compilers = ['icc', 'g++', 'msvc', 'c++'] - assemblers = ['nasm', 'masm', 'gas'] - fortran_compilers = ['ifl', 'g77'] - ars = ['ar', 'mslib'] - elif str(platform) == 'irix': - "prefer MIPSPro on IRIX" - linkers = ['sgilink', 'gnulink'] - c_compilers = ['sgicc', 'gcc', 'cc'] - cxx_compilers = ['sgic++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] - ars = ['sgiar'] - elif str(platform) == 'sunos': - "prefer Forte tools on SunOS" - linkers = ['sunlink', 'gnulink'] - c_compilers = ['suncc', 'gcc', 'cc'] - cxx_compilers = ['sunc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77', - 'gfortran', 'g77', 'fortran'] - ars = ['sunar'] - elif str(platform) == 'hpux': - "prefer aCC tools on HP-UX" - linkers = ['hplink', 'gnulink'] - c_compilers = ['hpcc', 'gcc', 'cc'] - cxx_compilers = ['hpc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] - ars = ['ar'] - elif str(platform) == 'aix': - "prefer AIX Visual Age tools on AIX" - linkers = ['aixlink', 'gnulink'] - c_compilers = ['aixcc', 'gcc', 'cc'] - cxx_compilers = ['aixc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran'] - ars = ['ar'] - elif str(platform) == 'darwin': - "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" - linkers = ['applelink', 'gnulink'] - c_compilers = ['gcc', 'cc'] - cxx_compilers = ['g++', 'c++'] - assemblers = ['as'] - fortran_compilers = ['gfortran', 'f95', 'f90', 'g77'] - ars = ['ar'] - else: - "prefer GNU tools on all other platforms" - linkers = ['gnulink', 'mslink', 'ilink'] - c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] - cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] - assemblers = ['gas', 'nasm', 'masm'] - fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] - ars = ['ar', 'mslib'] - - c_compiler = FindTool(c_compilers, env) or c_compilers[0] - - # XXX this logic about what tool provides what should somehow be - # moved into the tool files themselves. - if c_compiler and c_compiler == 'mingw': - # MinGW contains a linker, C compiler, C++ compiler, - # Fortran compiler, archiver and assembler: - cxx_compiler = None - linker = None - assembler = None - fortran_compiler = None - ar = None - else: - # Don't use g++ if the C compiler has built-in C++ support: - if c_compiler in ('msvc', 'intelc', 'icc'): - cxx_compiler = None - else: - cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] - linker = FindTool(linkers, env) or linkers[0] - assembler = FindTool(assemblers, env) or assemblers[0] - fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] - ar = FindTool(ars, env) or ars[0] - - other_tools = FindAllTools(['BitKeeper', 'CVS', - 'dmd', - 'filesystem', - 'dvipdf', 'dvips', 'gs', - 'jar', 'javac', 'javah', - 'latex', 'lex', - 'm4', 'midl', 'msvs', - 'pdflatex', 'pdftex', 'Perforce', - 'RCS', 'rmic', 'rpcgen', - 'SCCS', - # 'Subversion', - 'swig', - 'tar', 'tex', - 'yacc', 'zip', 'rpm', 'wix'], - env) - - tools = ([linker, c_compiler, cxx_compiler, - fortran_compiler, assembler, ar] - + other_tools) - - return filter(lambda x: x, tools) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/aixc++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/aixc++.py deleted file mode 100644 index 5db91f7686..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/aixc++.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.aixc++ - -Tool-specific initialization for IBM xlC / Visual Age C++ compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/aixc++.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Platform.aix - -cplusplus = __import__('c++', globals(), locals(), []) - -packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] - -def get_xlc(env): - xlc = env.get('CXX', 'xlC') - xlc_r = env.get('SHCXX', 'xlC_r') - return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) - -def smart_cxxflags(source, target, env, for_signature): - build_dir = env.GetBuildPath() - if build_dir: - return '-qtempinc=' + os.path.join(build_dir, 'tempinc') - return '' - -def generate(env): - """Add Builders and construction variables for xlC / Visual Age - suite to an Environment.""" - path, _cxx, _shcxx, version = get_xlc(env) - if path: - _cxx = os.path.join(path, _cxx) - _shcxx = os.path.join(path, _shcxx) - - cplusplus.generate(env) - - env['CXX'] = _cxx - env['SHCXX'] = _shcxx - env['CXXVERSION'] = version - env['SHOBJSUFFIX'] = '.pic.o' - -def exists(env): - path, _cxx, _shcxx, version = get_xlc(env) - if path and _cxx: - xlc = os.path.join(path, _cxx) - if os.path.exists(xlc): - return xlc - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/aixcc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/aixcc.py deleted file mode 100644 index 3c0b9d7686..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/aixcc.py +++ /dev/null @@ -1,68 +0,0 @@ -"""SCons.Tool.aixcc - -Tool-specific initialization for IBM xlc / Visual Age C compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/aixcc.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Platform.aix - -import cc - -packages = ['vac.C', 'ibmcxx.cmp'] - -def get_xlc(env): - xlc = env.get('CC', 'xlc') - xlc_r = env.get('SHCC', 'xlc_r') - return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) - -def generate(env): - """Add Builders and construction variables for xlc / Visual Age - suite to an Environment.""" - path, _cc, _shcc, version = get_xlc(env) - if path: - _cc = os.path.join(path, _cc) - _shcc = os.path.join(path, _shcc) - - cc.generate(env) - - env['CC'] = _cc - env['SHCC'] = _shcc - env['CCVERSION'] = version - -def exists(env): - path, _cc, _shcc, version = get_xlc(env) - if path and _cc: - xlc = os.path.join(path, _cc) - if os.path.exists(xlc): - return xlc - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/aixf77.py b/tools/scons/scons-local-1.2.0/SCons/Tool/aixf77.py deleted file mode 100644 index 794f7e2200..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/aixf77.py +++ /dev/null @@ -1,74 +0,0 @@ -"""engine.SCons.Tool.aixf77 - -Tool-specific initialization for IBM Visual Age f77 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/aixf77.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -#import SCons.Platform.aix - -import f77 - -# It would be good to look for the AIX F77 package the same way we're now -# looking for the C and C++ packages. This should be as easy as supplying -# the correct package names in the following list and uncommenting the -# SCons.Platform.aix_get_xlc() call the in the function below. -packages = [] - -def get_xlf77(env): - xlf77 = env.get('F77', 'xlf77') - xlf77_r = env.get('SHF77', 'xlf77_r') - #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) - return (None, xlf77, xlf77_r, None) - -def generate(env): - """ - Add Builders and construction variables for the Visual Age FORTRAN - compiler to an Environment. - """ - path, _f77, _shf77, version = get_xlf77(env) - if path: - _f77 = os.path.join(path, _f77) - _shf77 = os.path.join(path, _shf77) - - f77.generate(env) - - env['F77'] = _f77 - env['SHF77'] = _shf77 - -def exists(env): - path, _f77, _shf77, version = get_xlf77(env) - if path and _f77: - xlf77 = os.path.join(path, _f77) - if os.path.exists(xlf77): - return xlf77 - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/aixlink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/aixlink.py deleted file mode 100644 index 3a1182a645..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/aixlink.py +++ /dev/null @@ -1,70 +0,0 @@ -"""SCons.Tool.aixlink - -Tool-specific initialization for the IBM Visual Age linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/aixlink.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path - -import SCons.Util - -import aixcc -import link - -cplusplus = __import__('c++', globals(), locals(), []) - -def smart_linkflags(source, target, env, for_signature): - if cplusplus.iscplusplus(source): - build_dir = env.subst('$BUILDDIR', target=target, source=source) - if build_dir: - return '-qtempinc=' + os.path.join(build_dir, 'tempinc') - return '' - -def generate(env): - """ - Add Builders and construction variables for Visual Age linker to - an Environment. - """ - link.generate(env) - - env['SMARTLINKFLAGS'] = smart_linkflags - env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') - env['SHLIBSUFFIX'] = '.a' - -def exists(env): - path, _cc, _shcc, version = aixcc.get_xlc(env) - if path and _cc: - xlc = os.path.join(path, _cc) - if os.path.exists(xlc): - return xlc - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/applelink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/applelink.py deleted file mode 100644 index eb8df8caf6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/applelink.py +++ /dev/null @@ -1,65 +0,0 @@ -"""SCons.Tool.applelink - -Tool-specific initialization for the Apple gnu-like linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/applelink.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -# Even though the Mac is based on the GNU toolchain, it doesn't understand -# the -rpath option, so we use the "link" tool instead of "gnulink". -import link - -def generate(env): - """Add Builders and construction variables for applelink to an - Environment.""" - link.generate(env) - - env['FRAMEWORKPATHPREFIX'] = '-F' - env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' - env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' - env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') - env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - # override the default for loadable modules, which are different - # on OS X than dynamic shared libs. echoing what XCode does for - # pre/suffixes: - env['LDMODULEPREFIX'] = '' - env['LDMODULESUFFIX'] = '' - env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') - env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - - -def exists(env): - return env['PLATFORM'] == 'darwin' diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/ar.py b/tools/scons/scons-local-1.2.0/SCons/Tool/ar.py deleted file mode 100644 index 7812fb3f2c..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/ar.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.ar - -Tool-specific initialization for ar (library archive). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/ar.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('rc') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - - if env.Detect('ranlib'): - env['RANLIB'] = 'ranlib' - env['RANLIBFLAGS'] = SCons.Util.CLVar('') - env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' - -def exists(env): - return env.Detect('ar') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/as.py b/tools/scons/scons-local-1.2.0/SCons/Tool/as.py deleted file mode 100644 index 623c8d75ce..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/as.py +++ /dev/null @@ -1,72 +0,0 @@ -"""SCons.Tool.as - -Tool-specific initialization for as, the generic Posix assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/as.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -assemblers = ['as'] - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for as to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - shared_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['AS'] = env.Detect(assemblers) or 'as' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' - -def exists(env): - return env.Detect(assemblers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/bcc32.py b/tools/scons/scons-local-1.2.0/SCons/Tool/bcc32.py deleted file mode 100644 index 0488ba780f..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/bcc32.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.bcc32 - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/bcc32.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def findIt(program, env): - # First search in the SCons path and then the OS path: - borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) - if borwin: - dir = os.path.dirname(borwin) - env.PrependENVPath('PATH', dir) - return borwin - -def generate(env): - findIt('bcc32', env) - """Add Builders and construction variables for bcc to an - Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - for suffix in ['.c', '.cpp']: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['CC'] = 'bcc32' - env['CCFLAGS'] = SCons.Util.CLVar('') - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.dll' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - env['CFILESUFFIX'] = '.cpp' - -def exists(env): - return findIt('bcc32', env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/c++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/c++.py deleted file mode 100644 index 979814983e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/c++.py +++ /dev/null @@ -1,93 +0,0 @@ -"""SCons.Tool.c++ - -Tool-specific initialization for generic Posix C++ compilers. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/c++.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Tool -import SCons.Defaults -import SCons.Util - -compilers = ['CC', 'c++'] - -CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] -if SCons.Util.case_sensitive_suffixes('.c', '.C'): - CXXSuffixes.append('.C') - -def iscplusplus(source): - if not source: - # Source might be None for unusual cases like SConf. - return 0 - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext in CXXSuffixes: - return 1 - return 0 - -def generate(env): - """ - Add Builders and construction variables for Visual Age C++ compilers - to an Environment. - """ - import SCons.Tool - import SCons.Tool.cc - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - SCons.Tool.cc.add_common_cc_variables(env) - - env['CXX'] = 'c++' - env['CXXFLAGS'] = SCons.Util.CLVar('') - env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' - - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.os' - env['OBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - - env['CXXFILESUFFIX'] = '.cc' - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/cc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/cc.py deleted file mode 100644 index ef1249d4c2..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/cc.py +++ /dev/null @@ -1,108 +0,0 @@ -"""SCons.Tool.cc - -Tool-specific initialization for generic Posix C compilers. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/cc.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool -import SCons.Defaults -import SCons.Util - -CSuffixes = ['.c', '.m'] -if not SCons.Util.case_sensitive_suffixes('.c', '.C'): - CSuffixes.append('.C') - -def add_common_cc_variables(env): - """ - Add underlying common "C compiler" variables that - are used by multiple tools (specifically, c++). - """ - if not env.has_key('_CCCOMCOM'): - env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' - # It's a hack to test for darwin here, but the alternative - # of creating an applecc.py to contain this seems overkill. - # Maybe someday the Apple platform will require more setup and - # this logic will be moved. - env['FRAMEWORKS'] = SCons.Util.CLVar('') - env['FRAMEWORKPATH'] = SCons.Util.CLVar('') - if env['PLATFORM'] == 'darwin': - env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' - - if not env.has_key('CCFLAGS'): - env['CCFLAGS'] = SCons.Util.CLVar('') - - if not env.has_key('SHCCFLAGS'): - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - -def generate(env): - """ - Add Builders and construction variables for C compilers to an Environment. - """ - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) -#<<<<<<< .working -# -# env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' -# # It's a hack to test for darwin here, but the alternative of creating -# # an applecc.py to contain this seems overkill. Maybe someday the Apple -# # platform will require more setup and this logic will be moved. -# env['FRAMEWORKS'] = SCons.Util.CLVar('') -# env['FRAMEWORKPATH'] = SCons.Util.CLVar('') -# if env['PLATFORM'] == 'darwin': -# env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' -#======= -#>>>>>>> .merge-right.r1907 - - add_common_cc_variables(env) - - env['CC'] = 'cc' - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' - env['SHCC'] = '$CC' - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' - - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.os' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - - env['CFILESUFFIX'] = '.c' - -def exists(env): - return env.Detect('cc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/cvf.py b/tools/scons/scons-local-1.2.0/SCons/Tool/cvf.py deleted file mode 100644 index 203d9e4142..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/cvf.py +++ /dev/null @@ -1,52 +0,0 @@ -"""engine.SCons.Tool.cvf - -Tool-specific initialization for the Compaq Visual Fortran compiler. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/cvf.py 3842 2008/12/20 22:59:52 scons" - -import fortran - -compilers = ['f90'] - -def generate(env): - """Add Builders and construction variables for compaq visual fortran to an Environment.""" - - fortran.generate(env) - - env['FORTRAN'] = 'f90' - env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['OBJSUFFIX'] = '.obj' - env['FORTRANMODDIR'] = '${TARGET.dir}' - env['FORTRANMODDIRPREFIX'] = '/module:' - env['FORTRANMODDIRSUFFIX'] = '' - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/default.py b/tools/scons/scons-local-1.2.0/SCons/Tool/default.py deleted file mode 100644 index a105f7f0cd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/default.py +++ /dev/null @@ -1,44 +0,0 @@ -"""SCons.Tool.default - -Initialization with a default tool list. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/default.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool - -def generate(env): - """Add default tools.""" - for t in SCons.Tool.tool_list(env['PLATFORM'], env): - SCons.Tool.Tool(t)(env) - -def exists(env): - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/dmd.py b/tools/scons/scons-local-1.2.0/SCons/Tool/dmd.py deleted file mode 100644 index 88bff8abd0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/dmd.py +++ /dev/null @@ -1,218 +0,0 @@ -"""SCons.Tool.dmd - -Tool-specific initialization for the Digital Mars D compiler. -(http://digitalmars.com/d) - -Coded by Andy Friesen (andy@ikagames.com) -15 November 2003 - -There are a number of problems with this script at this point in time. -The one that irritates me the most is the Windows linker setup. The D -linker doesn't have a way to add lib paths on the commandline, as far -as I can see. You have to specify paths relative to the SConscript or -use absolute paths. To hack around it, add '#/blah'. This will link -blah.lib from the directory where SConstruct resides. - -Compiler variables: - DC - The name of the D compiler to use. Defaults to dmd or gdmd, - whichever is found. - DPATH - List of paths to search for import modules. - DVERSIONS - List of version tags to enable when compiling. - DDEBUG - List of debug tags to enable when compiling. - -Linker related variables: - LIBS - List of library files to link in. - DLINK - Name of the linker to use. Defaults to dmd or gdmd. - DLINKFLAGS - List of linker flags. - -Lib tool variables: - DLIB - Name of the lib tool to use. Defaults to lib. - DLIBFLAGS - List of flags to pass to the lib tool. - LIBS - Same as for the linker. (libraries to pull into the .lib) -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/dmd.py 3842 2008/12/20 22:59:52 scons" - -import os -import string - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner.D -import SCons.Tool - -# Adapted from c++.py -def isD(source): - if not source: - return 0 - - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext == '.d': - return 1 - return 0 - -smart_link = {} - -smart_lib = {} - -def generate(env): - global smart_link - global smart_lib - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - DAction = SCons.Action.Action('$DCOM', '$DCOMSTR') - - static_obj.add_action('.d', DAction) - shared_obj.add_action('.d', DAction) - static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) - - dc = env.Detect(['dmd', 'gdmd']) - env['DC'] = dc - env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' - env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' - env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' - env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' - - env['DPATH'] = ['#/'] - env['DFLAGS'] = [] - env['DVERSIONS'] = [] - env['DDEBUG'] = [] - - if dc: - # Add the path to the standard library. - # This is merely for the convenience of the dependency scanner. - dmd_path = env.WhereIs(dc) - if dmd_path: - x = string.rindex(dmd_path, dc) - phobosDir = dmd_path[:x] + '/../src/phobos' - if os.path.isdir(phobosDir): - env.Append(DPATH = [phobosDir]) - - env['DINCPREFIX'] = '-I' - env['DINCSUFFIX'] = '' - env['DVERPREFIX'] = '-version=' - env['DVERSUFFIX'] = '' - env['DDEBUGPREFIX'] = '-debug=' - env['DDEBUGSUFFIX'] = '' - env['DFLAGPREFIX'] = '-' - env['DFLAGSUFFIX'] = '' - env['DFILESUFFIX'] = '.d' - - # Need to use the Digital Mars linker/lib on windows. - # *nix can just use GNU link. - if env['PLATFORM'] == 'win32': - env['DLINK'] = '$DC' - env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' - env['DLIB'] = 'lib' - env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' - - env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' - env['DLINKFLAGS'] = [] - env['DLIBLINKPREFIX'] = '' - env['DLIBLINKSUFFIX'] = '.lib' - env['DLIBFLAGPREFIX'] = '-' - env['DLIBFLAGSUFFIX'] = '' - env['DLINKFLAGPREFIX'] = '-' - env['DLINKFLAGSUFFIX'] = '' - - SCons.Tool.createStaticLibBuilder(env) - - # Basically, we hijack the link and ar builders with our own. - # these builders check for the presence of D source, and swap out - # the system's defaults for the Digital Mars tools. If there's no D - # source, then we silently return the previous settings. - linkcom = env.get('LINKCOM') - try: - env['SMART_LINKCOM'] = smart_link[linkcom] - except KeyError: - def _smartLink(source, target, env, for_signature, - defaultLinker=linkcom): - if isD(source): - # XXX I'm not sure how to add a $DLINKCOMSTR variable - # so that it works with this _smartLink() logic, - # and I don't have a D compiler/linker to try it out, - # so we'll leave it alone for now. - return '$DLINKCOM' - else: - return defaultLinker - env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink - - arcom = env.get('ARCOM') - try: - env['SMART_ARCOM'] = smart_lib[arcom] - except KeyError: - def _smartLib(source, target, env, for_signature, - defaultLib=arcom): - if isD(source): - # XXX I'm not sure how to add a $DLIBCOMSTR variable - # so that it works with this _smartLib() logic, and - # I don't have a D compiler/archiver to try it out, - # so we'll leave it alone for now. - return '$DLIBCOM' - else: - return defaultLib - env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib - - # It is worth noting that the final space in these strings is - # absolutely pivotal. SCons sees these as actions and not generators - # if it is not there. (very bad) - env['ARCOM'] = '$SMART_ARCOM ' - env['LINKCOM'] = '$SMART_LINKCOM ' - else: # assuming linux - linkcom = env.get('LINKCOM') - try: - env['SMART_LINKCOM'] = smart_link[linkcom] - except KeyError: - def _smartLink(source, target, env, for_signature, - defaultLinker=linkcom, dc=dc): - if isD(source): - try: - libs = env['LIBS'] - except KeyError: - libs = [] - if 'phobos' not in libs: - if dc is 'dmd': - env.Append(LIBS = ['phobos']) - elif dc is 'gdmd': - env.Append(LIBS = ['gphobos']) - if 'pthread' not in libs: - env.Append(LIBS = ['pthread']) - if 'm' not in libs: - env.Append(LIBS = ['m']) - return defaultLinker - env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink - - env['LINKCOM'] = '$SMART_LINKCOM ' - -def exists(env): - return env.Detect(['dmd', 'gdmd']) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/dvi.py b/tools/scons/scons-local-1.2.0/SCons/Tool/dvi.py deleted file mode 100644 index af65671eac..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/dvi.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.dvi - -Common DVI Builder definition for various other Tool modules that use it. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/dvi.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Builder -import SCons.Tool - -DVIBuilder = None - -def generate(env): - try: - env['BUILDERS']['DVI'] - except KeyError: - global DVIBuilder - - if DVIBuilder is None: - # The suffix is hard-coded to '.dvi', not configurable via a - # construction variable like $DVISUFFIX, because the output - # file name is hard-coded within TeX. - DVIBuilder = SCons.Builder.Builder(action = {}, - source_scanner = SCons.Tool.LaTeXScanner, - suffix = '.dvi', - emitter = {}, - source_ext_match = None) - - env['BUILDERS']['DVI'] = DVIBuilder - -def exists(env): - # This only puts a skeleton Builder in place, so if someone - # references this Tool directly, it's always "available." - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/dvipdf.py b/tools/scons/scons-local-1.2.0/SCons/Tool/dvipdf.py deleted file mode 100644 index 821d125e67..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/dvipdf.py +++ /dev/null @@ -1,119 +0,0 @@ -"""SCons.Tool.dvipdf - -Tool-specific initialization for dvipdf. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/dvipdf.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Defaults -import SCons.Tool.pdf -import SCons.Tool.tex -import SCons.Util - -_null = SCons.Scanner.LaTeX._null - -def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): - """A builder for DVI files that sets the TEXPICTS environment - variable before running dvi2ps or dvipdf.""" - - try: - abspath = source[0].attributes.path - except AttributeError : - abspath = '' - - saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) - - result = XXXDviAction(target, source, env) - - if saved_env is _null: - try: - del env['ENV']['TEXPICTS'] - except KeyError: - pass # was never set - else: - env['ENV']['TEXPICTS'] = saved_env - - return result - -def DviPdfFunction(target = None, source= None, env=None): - result = DviPdfPsFunction(PDFAction,target,source,env) - return result - -def DviPdfStrFunction(target = None, source= None, env=None): - """A strfunction for dvipdf that returns the appropriate - command string for the no_exec options.""" - if env.GetOption("no_exec"): - result = env.subst('$DVIPDFCOM',0,target,source) - else: - result = '' - return result - -PDFAction = None -DVIPDFAction = None - -def PDFEmitter(target, source, env): - """Strips any .aux or .log files from the input source list. - These are created by the TeX Builder that in all likelihood was - used to generate the .dvi file we're using as input, and we only - care about the .dvi file. - """ - def strip_suffixes(n): - return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] - source = filter(strip_suffixes, source) - return (target, source) - -def generate(env): - """Add Builders and construction variables for dvipdf to an Environment.""" - global PDFAction - if PDFAction is None: - PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') - - global DVIPDFAction - if DVIPDFAction is None: - DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.dvi', DVIPDFAction) - bld.add_emitter('.dvi', PDFEmitter) - - env['DVIPDF'] = 'dvipdf' - env['DVIPDFFLAGS'] = SCons.Util.CLVar('') - env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' - - # Deprecated synonym. - env['PDFCOM'] = ['$DVIPDFCOM'] - -def exists(env): - return env.Detect('dvipdf') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/dvips.py b/tools/scons/scons-local-1.2.0/SCons/Tool/dvips.py deleted file mode 100644 index db763f1d08..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/dvips.py +++ /dev/null @@ -1,88 +0,0 @@ -"""SCons.Tool.dvips - -Tool-specific initialization for dvips. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/dvips.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Tool.dvipdf -import SCons.Util - -def DviPsFunction(target = None, source= None, env=None): - result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) - return result - -def DviPsStrFunction(target = None, source= None, env=None): - """A strfunction for dvipdf that returns the appropriate - command string for the no_exec options.""" - if env.GetOption("no_exec"): - result = env.subst('$PSCOM',0,target,source) - else: - result = '' - return result - -PSAction = None -DVIPSAction = None -PSBuilder = None - -def generate(env): - """Add Builders and construction variables for dvips to an Environment.""" - global PSAction - if PSAction is None: - PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') - - global DVIPSAction - if DVIPSAction is None: - DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) - - global PSBuilder - if PSBuilder is None: - PSBuilder = SCons.Builder.Builder(action = PSAction, - prefix = '$PSPREFIX', - suffix = '$PSSUFFIX', - src_suffix = '.dvi', - src_builder = 'DVI', - single_source=True) - - env['BUILDERS']['PostScript'] = PSBuilder - - env['DVIPS'] = 'dvips' - env['DVIPSFLAGS'] = SCons.Util.CLVar('') - # I'm not quite sure I got the directories and filenames right for variant_dir - # We need to be in the correct directory for the sake of latex \includegraphics eps included files. - env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' - env['PSPREFIX'] = '' - env['PSSUFFIX'] = '.ps' - -def exists(env): - return env.Detect('dvips') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/f77.py b/tools/scons/scons-local-1.2.0/SCons/Tool/f77.py deleted file mode 100644 index 21ab6d82dd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/f77.py +++ /dev/null @@ -1,56 +0,0 @@ -"""engine.SCons.Tool.f77 - -Tool-specific initialization for the generic Posix f77 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/f77.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env - -compilers = ['f77'] - -def generate(env): - add_all_to_env(env) - add_f77_to_env(env) - - fcomp = env.Detect(compilers) or 'f77' - env['F77'] = fcomp - env['SHF77'] = fcomp - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = fcomp - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/f90.py b/tools/scons/scons-local-1.2.0/SCons/Tool/f90.py deleted file mode 100644 index 1078d2ccaf..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/f90.py +++ /dev/null @@ -1,56 +0,0 @@ -"""engine.SCons.Tool.f90 - -Tool-specific initialization for the generic Posix f90 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/f90.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env - -compilers = ['f90'] - -def generate(env): - add_all_to_env(env) - add_f90_to_env(env) - - fc = env.Detect(compilers) or 'f90' - env['F90'] = fc - env['SHF90'] = fc - - env['FORTRAN'] = fc - env['SHFORTRAN'] = fc - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/f95.py b/tools/scons/scons-local-1.2.0/SCons/Tool/f95.py deleted file mode 100644 index 012930ca7e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/f95.py +++ /dev/null @@ -1,57 +0,0 @@ -"""engine.SCons.Tool.f95 - -Tool-specific initialization for the generic Posix f95 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/f95.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util -import fortran -from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env - -compilers = ['f95'] - -def generate(env): - add_all_to_env(env) - add_f95_to_env(env) - - fcomp = env.Detect(compilers) or 'f95' - env['F95'] = fcomp - env['SHF95'] = fcomp - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = fcomp - - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/filesystem.py b/tools/scons/scons-local-1.2.0/SCons/Tool/filesystem.py deleted file mode 100644 index dbab56202e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/filesystem.py +++ /dev/null @@ -1,92 +0,0 @@ -"""SCons.Tool.filesystem - -Tool-specific initialization for the filesystem tools. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/filesystem.py 3842 2008/12/20 22:59:52 scons" - -import SCons -from SCons.Tool.install import copyFunc - -copyToBuilder, copyAsBuilder = None, None - -def copyto_emitter(target, source, env): - """ changes the path of the source to be under the target (which - are assumed to be directories. - """ - n_target = [] - - for t in target: - n_target = n_target + map( lambda s, t=t: t.File( str( s ) ), source ) - - return (n_target, source) - -def copy_action_func(target, source, env): - assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(map(str, target),map(str, source)) - - for t, s in zip(target, source): - if copyFunc(t.get_path(), s.get_path(), env): - return 1 - - return 0 - -def copy_action_str(target, source, env): - return env.subst_target_source(env['COPYSTR'], 0, target, source) - -copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) - -def generate(env): - try: - env['BUILDERS']['CopyTo'] - env['BUILDERS']['CopyAs'] - except KeyError, e: - global copyToBuilder - if copyToBuilder is None: - copyToBuilder = SCons.Builder.Builder( - action = copy_action, - target_factory = env.fs.Dir, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ copyto_emitter, ] ) - - global copyAsBuilder - if copyAsBuilder is None: - copyAsBuilder = SCons.Builder.Builder( - action = copy_action, - target_factory = env.fs.Entry, - source_factory = env.fs.Entry ) - - env['BUILDERS']['CopyTo'] = copyToBuilder - env['BUILDERS']['CopyAs'] = copyAsBuilder - - env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' - -def exists(env): - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/fortran.py b/tools/scons/scons-local-1.2.0/SCons/Tool/fortran.py deleted file mode 100644 index aa53cf61ba..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/fortran.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.fortran - -Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/fortran.py 3842 2008/12/20 22:59:52 scons" - -import re -import string - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env - -compilers = ['f95', 'f90', 'f77'] - -def generate(env): - add_all_to_env(env) - add_fortran_to_env(env) - - fc = env.Detect(compilers) or 'f77' - env['SHFORTRAN'] = fc - env['FORTRAN'] = fc - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/g++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/g++.py deleted file mode 100644 index feb39519e5..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/g++.py +++ /dev/null @@ -1,84 +0,0 @@ -"""SCons.Tool.g++ - -Tool-specific initialization for g++. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/g++.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re -import subprocess - -import SCons.Tool -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -compilers = ['g++'] - -def generate(env): - """Add Builders and construction variables for g++ to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - cplusplus.generate(env) - - env['CXX'] = env.Detect(compilers) - - # platform specific settings - if env['PLATFORM'] == 'aix': - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - elif env['PLATFORM'] == 'hpux': - env['SHOBJSUFFIX'] = '.pic.o' - elif env['PLATFORM'] == 'sunos': - env['SHOBJSUFFIX'] = '.pic.o' - # determine compiler version - if env['CXX']: - #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'], - pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - # -dumpversion was added in GCC 3.0. As long as we're supporting - # GCC versions older than that, we should use --version and a - # regular expression. - #line = pipe.stdout.read().strip() - #if line: - # env['CXXVERSION'] = line - line = pipe.stdout.readline() - match = re.search(r'[0-9]+(\.[0-9]+)+', line) - if match: - env['CXXVERSION'] = match.group(0) - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/g77.py b/tools/scons/scons-local-1.2.0/SCons/Tool/g77.py deleted file mode 100644 index effc9fcfcc..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/g77.py +++ /dev/null @@ -1,67 +0,0 @@ -"""engine.SCons.Tool.g77 - -Tool-specific initialization for g77. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/g77.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env - -compilers = ['g77', 'f77'] - -def generate(env): - """Add Builders and construction variables for g77 to an Environment.""" - add_all_to_env(env) - add_f77_to_env(env) - - fcomp = env.Detect(compilers) or 'g77' - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') - else: - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = '$FORTRAN' - - env['F77'] = fcomp - env['SHF77'] = '$F77' - - env['INCFORTRANPREFIX'] = "-I" - env['INCFORTRANSUFFIX'] = "" - - env['INCF77PREFIX'] = "-I" - env['INCF77SUFFIX'] = "" - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/gas.py b/tools/scons/scons-local-1.2.0/SCons/Tool/gas.py deleted file mode 100644 index 5595e9e136..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/gas.py +++ /dev/null @@ -1,47 +0,0 @@ -"""SCons.Tool.gas - -Tool-specific initialization for as, the Gnu assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/gas.py 3842 2008/12/20 22:59:52 scons" - -as_module = __import__('as', globals(), locals(), []) - -assemblers = ['as', 'gas'] - -def generate(env): - """Add Builders and construction variables for as to an Environment.""" - as_module.generate(env) - - env['AS'] = env.Detect(assemblers) or 'as' - -def exists(env): - return env.Detect(assemblers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/gcc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/gcc.py deleted file mode 100644 index db07575b2d..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/gcc.py +++ /dev/null @@ -1,74 +0,0 @@ -"""SCons.Tool.gcc - -Tool-specific initialization for gcc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/gcc.py 3842 2008/12/20 22:59:52 scons" - -import cc -import os -import re -import subprocess - -import SCons.Util - -compilers = ['gcc', 'cc'] - -def generate(env): - """Add Builders and construction variables for gcc to an Environment.""" - cc.generate(env) - - env['CC'] = env.Detect(compilers) or 'gcc' - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - else: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') - # determine compiler version - if env['CC']: - #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], - pipe = SCons.Action._subproc(env, [env['CC'], '--version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - # -dumpversion was added in GCC 3.0. As long as we're supporting - # GCC versions older than that, we should use --version and a - # regular expression. - #line = pipe.stdout.read().strip() - #if line: - # env['CCVERSION'] = line - line = pipe.stdout.readline() - match = re.search(r'[0-9]+(\.[0-9]+)+', line) - if match: - env['CCVERSION'] = match.group(0) - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/gfortran.py b/tools/scons/scons-local-1.2.0/SCons/Tool/gfortran.py deleted file mode 100644 index 7da19e4fd6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/gfortran.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.gfortran - -Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran -2003 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/gfortran.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import fortran - -def generate(env): - """Add Builders and construction variables for gfortran to an - Environment.""" - fortran.generate(env) - - for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: - env['%s' % dialect] = 'gfortran' - env['SH%s' % dialect] = '$%s' % dialect - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) - else: - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) - - env['INC%sPREFIX' % dialect] = "-I" - env['INC%sSUFFIX' % dialect] = "" - -def exists(env): - return env.Detect('gfortran') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/gnulink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/gnulink.py deleted file mode 100644 index de95ee1bbf..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/gnulink.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.gnulink - -Tool-specific initialization for the gnu linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/gnulink.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import link - -linkers = ['g++', 'gcc'] - -def generate(env): - """Add Builders and construction variables for gnulink to an Environment.""" - link.generate(env) - - if env['PLATFORM'] == 'hpux': - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') - - # __RPATH is set to $_RPATH in the platform specification if that - # platform supports it. - env.Append(LINKFLAGS=['$__RPATH']) - env['RPATHPREFIX'] = '-Wl,-rpath=' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return env.Detect(linkers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/gs.py b/tools/scons/scons-local-1.2.0/SCons/Tool/gs.py deleted file mode 100644 index c52440af63..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/gs.py +++ /dev/null @@ -1,75 +0,0 @@ -"""SCons.Tool.gs - -Tool-specific initialization for Ghostscript. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/gs.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Platform -import SCons.Util - -# Ghostscript goes by different names on different platforms... -platform = SCons.Platform.platform_default() - -if platform == 'os2': - gs = 'gsos2' -elif platform == 'win32': - gs = 'gswin32c' -else: - gs = 'gs' - -GhostscriptAction = None - -def generate(env): - """Add Builders and construction variables for Ghostscript to an - Environment.""" - - global GhostscriptAction - if GhostscriptAction is None: - GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR') - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.ps', GhostscriptAction) - - env['GS'] = gs - env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') - env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' - - -def exists(env): - if env.has_key('PS2PDF'): - return env.Detect(env['PS2PDF']) - else: - return env.Detect(gs) or SCons.Util.WhereIs(gs) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/hpc++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/hpc++.py deleted file mode 100644 index 299c701ed4..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/hpc++.py +++ /dev/null @@ -1,79 +0,0 @@ -"""SCons.Tool.hpc++ - -Tool-specific initialization for c++ on HP/UX. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/hpc++.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string - -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -acc = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for dir in dirs: - cc = '/opt/' + dir + '/bin/aCC' - if os.path.exists(cc): - acc = cc - break - - -def generate(env): - """Add Builders and construction variables for g++ to an Environment.""" - cplusplus.generate(env) - - if acc: - env['CXX'] = acc or 'aCC' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') - # determine version of aCC - line = os.popen(acc + ' -V 2>&1').readline().rstrip() - if string.find(line, 'aCC: HP ANSI C++') == 0: - env['CXXVERSION'] = string.split(line)[-1] - - if env['PLATFORM'] == 'cygwin': - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - else: - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') - -def exists(env): - return acc diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/hpcc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/hpcc.py deleted file mode 100644 index a4da9568b5..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/hpcc.py +++ /dev/null @@ -1,47 +0,0 @@ -"""SCons.Tool.hpcc - -Tool-specific initialization for HP aCC and cc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/hpcc.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import cc - -def generate(env): - """Add Builders and construction variables for aCC & cc to an Environment.""" - cc.generate(env) - - env['CXX'] = 'aCC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z') - -def exists(env): - return env.Detect('aCC') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/hplink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/hplink.py deleted file mode 100644 index 0eb5b0a6ba..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/hplink.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.Tool.hplink - -Tool-specific initialization for the HP linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/hplink.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path - -import SCons.Util - -import link - -ccLinker = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for dir in dirs: - linker = '/opt/' + dir + '/bin/aCC' - if os.path.exists(linker): - ccLinker = linker - break - -def generate(env): - """ - Add Builders and construction variables for Visual Age linker to - an Environment. - """ - link.generate(env) - - env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') - env['SHLIBSUFFIX'] = '.sl' - -def exists(env): - return ccLinker diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/icc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/icc.py deleted file mode 100644 index ac6d6aadea..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/icc.py +++ /dev/null @@ -1,53 +0,0 @@ -"""engine.SCons.Tool.icc - -Tool-specific initialization for the OS/2 icc compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/icc.py 3842 2008/12/20 22:59:52 scons" - -import cc - -def generate(env): - """Add Builders and construction variables for the OS/2 to an Environment.""" - cc.generate(env) - - env['CC'] = 'icc' - env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' - env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' - env['CPPDEFPREFIX'] = '/D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '/I' - env['INCSUFFIX'] = '' - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cc' - -def exists(env): - return env.Detect('icc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/icl.py b/tools/scons/scons-local-1.2.0/SCons/Tool/icl.py deleted file mode 100644 index 322de79350..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/icl.py +++ /dev/null @@ -1,46 +0,0 @@ -"""engine.SCons.Tool.icl - -Tool-specific initialization for the Intel C/C++ compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/icl.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool.intelc - -# This has been completely superceded by intelc.py, which can -# handle both Windows and Linux versions. - -def generate(*args, **kw): - """Add Builders and construction variables for icl to an Environment.""" - return apply(SCons.Tool.intelc.generate, args, kw) - -def exists(*args, **kw): - return apply(SCons.Tool.intelc.exists, args, kw) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/ifl.py b/tools/scons/scons-local-1.2.0/SCons/Tool/ifl.py deleted file mode 100644 index bfb157e6e8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/ifl.py +++ /dev/null @@ -1,66 +0,0 @@ -"""SCons.Tool.ifl - -Tool-specific initialization for the Intel Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/ifl.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -from SCons.Scanner.Fortran import FortranScan -from FortranCommon import add_all_to_env - -def generate(env): - """Add Builders and construction variables for ifl to an Environment.""" - fscan = FortranScan("FORTRANPATH") - SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) - SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) - - if not env.has_key('FORTRANFILESUFFIXES'): - env['FORTRANFILESUFFIXES'] = ['.i'] - else: - env['FORTRANFILESUFFIXES'].append('.i') - - if not env.has_key('F90FILESUFFIXES'): - env['F90FILESUFFIXES'] = ['.i90'] - else: - env['F90FILESUFFIXES'].append('.i90') - - add_all_to_env(env) - - env['FORTRAN'] = 'ifl' - env['SHFORTRAN'] = '$FORTRAN' - env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - -def exists(env): - return env.Detect('ifl') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/ifort.py b/tools/scons/scons-local-1.2.0/SCons/Tool/ifort.py deleted file mode 100644 index 17b7bf7b3a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/ifort.py +++ /dev/null @@ -1,83 +0,0 @@ -"""SCons.Tool.ifort - -Tool-specific initialization for newer versions of the Intel Fortran Compiler -for Linux/Windows (and possibly Mac OS X). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/ifort.py 3842 2008/12/20 22:59:52 scons" - -import string - -import SCons.Defaults -from SCons.Scanner.Fortran import FortranScan -from FortranCommon import add_all_to_env - -def generate(env): - """Add Builders and construction variables for ifort to an Environment.""" - # ifort supports Fortran 90 and Fortran 95 - # Additionally, ifort recognizes more file extensions. - fscan = FortranScan("FORTRANPATH") - SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) - SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) - - if not env.has_key('FORTRANFILESUFFIXES'): - env['FORTRANFILESUFFIXES'] = ['.i'] - else: - env['FORTRANFILESUFFIXES'].append('.i') - - if not env.has_key('F90FILESUFFIXES'): - env['F90FILESUFFIXES'] = ['.i90'] - else: - env['F90FILESUFFIXES'].append('.i90') - - add_all_to_env(env) - - fc = 'ifort' - - for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: - env['%s' % dialect] = fc - env['SH%s' % dialect] = '$%s' % dialect - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) - - if env['PLATFORM'] == 'win32': - # On Windows, the ifort compiler specifies the object on the - # command line with -object:, not -o. Massage the necessary - # command-line construction variables. - for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: - for var in ['%sCOM' % dialect, '%sPPCOM' % dialect, - 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]: - env[var] = string.replace(env[var], '-o $TARGET', '-object:$TARGET') - env['FORTRANMODDIRPREFIX'] = "/module:" - else: - env['FORTRANMODDIRPREFIX'] = "-module " - -def exists(env): - return env.Detect('ifort') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/ilink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/ilink.py deleted file mode 100644 index b443a6b688..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/ilink.py +++ /dev/null @@ -1,53 +0,0 @@ -"""SCons.Tool.ilink - -Tool-specific initialization for the OS/2 ilink linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/ilink.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ilink to an Environment.""" - SCons.Tool.createProgBuilder(env) - - env['LINK'] = 'ilink' - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $( $_LIBDIRFLAGS $) $_LIBFLAGS' - env['LIBDIRPREFIX']='/LIBPATH:' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - -def exists(env): - return env.Detect('ilink') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/ilink32.py b/tools/scons/scons-local-1.2.0/SCons/Tool/ilink32.py deleted file mode 100644 index f357bec676..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/ilink32.py +++ /dev/null @@ -1,54 +0,0 @@ -"""SCons.Tool.ilink32 - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/ilink32.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool -import SCons.Tool.bcc32 -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ilink to an - Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['LINK'] = '$CC' - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '$LINK -q $LINKFLAGS $SOURCES $LIBS' - env['LIBDIRPREFIX']='' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - -def exists(env): - # Uses bcc32 to do linking as it generally knows where the standard - # LIBS are and set up the linking correctly - return SCons.Tool.bcc32.findIt('bcc32', env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/install.py b/tools/scons/scons-local-1.2.0/SCons/Tool/install.py deleted file mode 100644 index be36be08c9..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/install.py +++ /dev/null @@ -1,223 +0,0 @@ -"""SCons.Tool.install - -Tool-specific initialization for the install tool. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/install.py 3842 2008/12/20 22:59:52 scons" - -import os -import shutil -import stat - -import SCons.Action -from SCons.Util import make_path_relative - -# -# We keep track of *all* installed files. -_INSTALLED_FILES = [] -_UNIQUE_INSTALLED_FILES = None - -# -# Functions doing the actual work of the Install Builder. -# -def copyFunc(dest, source, env): - """Install a source file or directory into a destination by copying, - (including copying permission/mode bits).""" - - if os.path.isdir(source): - if os.path.exists(dest): - if not os.path.isdir(dest): - raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source)) - else: - parent = os.path.split(dest)[0] - if not os.path.exists(parent): - os.makedirs(parent) - shutil.copytree(source, dest) - else: - shutil.copy2(source, dest) - st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - return 0 - -def installFunc(target, source, env): - """Install a source file into a target using the function specified - as the INSTALL construction variable.""" - try: - install = env['INSTALL'] - except KeyError: - raise SCons.Errors.UserError('Missing INSTALL construction variable.') - - assert len(target)==len(source), \ - "Installing source %s into target %s: target and source lists must have same length."%(map(str, source), map(str, target)) - for t,s in zip(target,source): - if install(t.get_path(),s.get_path(),env): - return 1 - - return 0 - -def stringFunc(target, source, env): - installstr = env.get('INSTALLSTR') - if installstr: - return env.subst_target_source(installstr, 0, target, source) - target = str(target[0]) - source = str(source[0]) - if os.path.isdir(source): - type = 'directory' - else: - type = 'file' - return 'Install %s: "%s" as "%s"' % (type, source, target) - -# -# Emitter functions -# -def add_targets_to_INSTALLED_FILES(target, source, env): - """ an emitter that adds all target files to the list stored in the - _INSTALLED_FILES global variable. This way all installed files of one - scons call will be collected. - """ - global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES - _INSTALLED_FILES.extend(target) - _UNIQUE_INSTALLED_FILES = None - return (target, source) - -class DESTDIR_factory: - """ a node factory, where all files will be relative to the dir supplied - in the constructor. - """ - def __init__(self, env, dir): - self.env = env - self.dir = env.arg2nodes( dir, env.fs.Dir )[0] - - def Entry(self, name): - name = make_path_relative(name) - return self.dir.Entry(name) - - def Dir(self, name): - name = make_path_relative(name) - return self.dir.Dir(name) - -# -# The Builder Definition -# -install_action = SCons.Action.Action(installFunc, stringFunc) -installas_action = SCons.Action.Action(installFunc, stringFunc) - -BaseInstallBuilder = None - -def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): - if target and dir: - import SCons.Errors - raise SCons.Errors.UserError, "Both target and dir defined for Install(), only one may be defined." - if not dir: - dir=target - - import SCons.Script - install_sandbox = SCons.Script.GetOption('install_sandbox') - if install_sandbox: - target_factory = DESTDIR_factory(env, install_sandbox) - else: - target_factory = env.fs - - try: - dnodes = env.arg2nodes(dir, target_factory.Dir) - except TypeError: - raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir) - sources = env.arg2nodes(source, env.fs.Entry) - tgt = [] - for dnode in dnodes: - for src in sources: - # Prepend './' so the lookup doesn't interpret an initial - # '#' on the file name portion as meaning the Node should - # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.'+os.sep+src.name, dnode) - #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) - tgt.extend(apply(BaseInstallBuilder, (env, target, src), kw)) - return tgt - -def InstallAsBuilderWrapper(env, target=None, source=None, **kw): - result = [] - for src, tgt in map(lambda x, y: (x, y), source, target): - #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) - result.extend(apply(BaseInstallBuilder, (env, tgt, src), kw)) - return result - -added = None - -def generate(env): - - from SCons.Script import AddOption, GetOption - global added - if not added: - added = 1 - AddOption('--install-sandbox', - dest='install_sandbox', - type="string", - action="store", - help='A directory under which all installed files will be placed.') - - global BaseInstallBuilder - if BaseInstallBuilder is None: - install_sandbox = GetOption('install_sandbox') - if install_sandbox: - target_factory = DESTDIR_factory(env, install_sandbox) - else: - target_factory = env.fs - - BaseInstallBuilder = SCons.Builder.Builder( - action = install_action, - target_factory = target_factory.Entry, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ add_targets_to_INSTALLED_FILES, ], - name = 'InstallBuilder') - - env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper - env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper - - # We'd like to initialize this doing something like the following, - # but there isn't yet support for a ${SOURCE.type} expansion that - # will print "file" or "directory" depending on what's being - # installed. For now we punt by not initializing it, and letting - # the stringFunc() that we put in the action fall back to the - # hand-crafted default string if it's not set. - # - #try: - # env['INSTALLSTR'] - #except KeyError: - # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' - - try: - env['INSTALL'] - except KeyError: - env['INSTALL'] = copyFunc - -def exists(env): - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/intelc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/intelc.py deleted file mode 100644 index dfdedc4abf..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/intelc.py +++ /dev/null @@ -1,482 +0,0 @@ -"""SCons.Tool.icl - -Tool-specific initialization for the Intel C/C++ compiler. -Supports Linux and Windows compilers, v7 and up. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/intelc.py 3842 2008/12/20 22:59:52 scons" - -import math, sys, os.path, glob, string, re - -is_windows = sys.platform == 'win32' -is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or - (os.environ.has_key('PROCESSOR_ARCHITEW6432') and - os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) -is_linux = sys.platform == 'linux2' -is_mac = sys.platform == 'darwin' - -if is_windows: - import SCons.Tool.msvc -elif is_linux: - import SCons.Tool.gcc -elif is_mac: - import SCons.Tool.gcc -import SCons.Util -import SCons.Warnings - -# Exceptions for this tool -class IntelCError(SCons.Errors.InternalError): - pass -class MissingRegistryError(IntelCError): # missing registry entry - pass -class MissingDirError(IntelCError): # dir not found - pass -class NoRegistryModuleError(IntelCError): # can't read registry at all - pass - -def uniquify(s): - """Return a sequence containing only one copy of each unique element from input sequence s. - Does not preserve order. - Input sequence must be hashable (i.e. must be usable as a dictionary key).""" - u = {} - for x in s: - u[x] = 1 - return u.keys() - -def linux_ver_normalize(vstr): - """Normalize a Linux compiler version number. - Intel changed from "80" to "9.0" in 2005, so we assume if the number - is greater than 60 it's an old-style number and otherwise new-style. - Always returns an old-style float like 80 or 90 for compatibility with Windows. - Shades of Y2K!""" - # Check for version number like 9.1.026: return 91.026 - m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) - if m: - vmaj,vmin,build = m.groups() - return float(vmaj) * 10 + float(vmin) + float(build) / 1000.; - else: - f = float(vstr) - if is_windows: - return f - else: - if f < 60: return f * 10.0 - else: return f - -def check_abi(abi): - """Check for valid ABI (application binary interface) name, - and map into canonical one""" - if not abi: - return None - abi = abi.lower() - # valid_abis maps input name to canonical name - if is_windows: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'ia64' : 'ia64', - 'em64t' : 'em64t', - 'amd64' : 'em64t'} - if is_linux: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'x86_64' : 'x86_64', - 'em64t' : 'x86_64', - 'amd64' : 'x86_64'} - if is_mac: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'x86_64' : 'x86_64', - 'em64t' : 'x86_64'} - try: - abi = valid_abis[abi] - except KeyError: - raise SCons.Errors.UserError, \ - "Intel compiler: Invalid ABI %s, valid values are %s"% \ - (abi, valid_abis.keys()) - return abi - -def vercmp(a, b): - """Compare strings as floats, - but Intel changed Linux naming convention at 9.0""" - return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) - -def get_version_from_list(v, vlist): - """See if we can match v (string) in vlist (list of strings) - Linux has to match in a fuzzy way.""" - if is_windows: - # Simple case, just find it in the list - if v in vlist: return v - else: return None - else: - # Fuzzy match: normalize version number first, but still return - # original non-normalized form. - fuzz = 0.001 - for vi in vlist: - if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz: - return vi - # Not found - return None - -def get_intel_registry_value(valuename, version=None, abi=None): - """ - Return a value from the Intel compiler registry tree. (Windows only) - """ - # Open the key: - if is_win64: - K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() - else: - K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) - except SCons.Util.RegError: - raise MissingRegistryError, \ - "%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi) - - # Get the value: - try: - v = SCons.Util.RegQueryValueEx(k, valuename)[0] - return v # or v.encode('iso-8859-1', 'replace') to remove unicode? - except SCons.Util.RegError: - raise MissingRegistryError, \ - "%s\\%s was not found in the registry."%(K, valuename) - - -def get_all_compiler_versions(): - """Returns a sorted list of strings, like "70" or "80" or "9.0" - with most recent compiler version first. - """ - versions=[] - if is_windows: - if is_win64: - keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++' - else: - keyname = 'Software\\Intel\\Compilers\\C++' - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - keyname) - except WindowsError: - return [] - i = 0 - versions = [] - try: - while i < 100: - subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError - # Check that this refers to an existing dir. - # This is not 100% perfect but should catch common - # installation issues like when the compiler was installed - # and then the install directory deleted or moved (rather - # than uninstalling properly), so the registry values - # are still there. - ok = False - for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'): - try: - d = get_intel_registry_value('ProductDir', subkey, try_abi) - except MissingRegistryError: - continue # not found in reg, keep going - if os.path.exists(d): ok = True - if ok: - versions.append(subkey) - else: - try: - # Registry points to nonexistent dir. Ignore this - # version. - value = get_intel_registry_value('ProductDir', subkey, 'IA32') - except MissingRegistryError, e: - - # Registry key is left dangling (potentially - # after uninstalling). - - print \ - "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \ - "scons: *** It seems that the compiler was uninstalled and that the registry\n" \ - "scons: *** was not cleaned up properly.\n" % subkey - else: - print "scons: *** Ignoring "+str(value) - - i = i + 1 - except EnvironmentError: - # no more subkeys - pass - elif is_linux: - for d in glob.glob('/opt/intel_cc_*'): - # Typical dir here is /opt/intel_cc_80. - m = re.search(r'cc_(.*)$', d) - if m: - versions.append(m.group(1)) - for d in glob.glob('/opt/intel/cc*/*'): - # Typical dir here is /opt/intel/cc/9.0 for IA32, - # /opt/intel/cce/9.0 for EMT64 (AMD64) - m = re.search(r'([0-9.]+)$', d) - if m: - versions.append(m.group(1)) - elif is_mac: - for d in glob.glob('/opt/intel/cc*/*'): - # Typical dir here is /opt/intel/cc/9.0 for IA32, - # /opt/intel/cce/9.0 for EMT64 (AMD64) - m = re.search(r'([0-9.]+)$', d) - if m: - versions.append(m.group(1)) - versions = uniquify(versions) # remove dups - versions.sort(vercmp) - return versions - -def get_intel_compiler_top(version, abi): - """ - Return the main path to the top-level dir of the Intel compiler, - using the given version. - The compiler will be in <top>/bin/icl.exe (icc on linux), - the include dir is <top>/include, etc. - """ - - if is_windows: - if not SCons.Util.can_read_reg: - raise NoRegistryModuleError, "No Windows registry module was found" - top = get_intel_registry_value('ProductDir', version, abi) - if not os.path.exists(os.path.join(top, "Bin", "icl.exe")): - raise MissingDirError, \ - "Can't find Intel compiler in %s"%(top) - elif is_mac or is_linux: - # first dir is new (>=9.0) style, second is old (8.0) style. - dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s') - if abi == 'x86_64': - dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64 - top=None - for d in dirs: - if os.path.exists(os.path.join(d%version, "bin", "icc")): - top = d%version - break - if not top: - raise MissingDirError, \ - "Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi) - return top - - -def generate(env, version=None, abi=None, topdir=None, verbose=0): - """Add Builders and construction variables for Intel C/C++ compiler - to an Environment. - args: - version: (string) compiler version to use, like "80" - abi: (string) 'win32' or whatever Itanium version wants - topdir: (string) compiler top dir, like - "c:\Program Files\Intel\Compiler70" - If topdir is used, version and abi are ignored. - verbose: (int) if >0, prints compiler version used. - """ - if not (is_mac or is_linux or is_windows): - # can't handle this platform - return - - if is_windows: - SCons.Tool.msvc.generate(env) - elif is_linux: - SCons.Tool.gcc.generate(env) - elif is_mac: - SCons.Tool.gcc.generate(env) - - # if version is unspecified, use latest - vlist = get_all_compiler_versions() - if not version: - if vlist: - version = vlist[0] - else: - # User may have specified '90' but we need to get actual dirname '9.0'. - # get_version_from_list does that mapping. - v = get_version_from_list(version, vlist) - if not v: - raise SCons.Errors.UserError, \ - "Invalid Intel compiler version %s: "%version + \ - "installed versions are %s"%(', '.join(vlist)) - version = v - - # if abi is unspecified, use ia32 - # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here) - abi = check_abi(abi) - if abi is None: - if is_mac or is_linux: - # Check if we are on 64-bit linux, default to 64 then. - uname_m = os.uname()[4] - if uname_m == 'x86_64': - abi = 'x86_64' - else: - abi = 'ia32' - else: - if is_win64: - abi = 'em64t' - else: - abi = 'ia32' - - if version and not topdir: - try: - topdir = get_intel_compiler_top(version, abi) - except (SCons.Util.RegError, IntelCError): - topdir = None - - if not topdir: - # Normally this is an error, but it might not be if the compiler is - # on $PATH and the user is importing their env. - class ICLTopDirWarning(SCons.Warnings.Warning): - pass - if (is_mac or is_linux) and not env.Detect('icc') or \ - is_windows and not env.Detect('icl'): - - SCons.Warnings.enableWarningClass(ICLTopDirWarning) - SCons.Warnings.warn(ICLTopDirWarning, - "Failed to find Intel compiler for version='%s', abi='%s'"% - (str(version), str(abi))) - else: - # should be cleaned up to say what this other version is - # since in this case we have some other Intel compiler installed - SCons.Warnings.enableWarningClass(ICLTopDirWarning) - SCons.Warnings.warn(ICLTopDirWarning, - "Can't find Intel compiler top dir for version='%s', abi='%s'"% - (str(version), str(abi))) - - if topdir: - if verbose: - print "Intel C compiler: using version %s (%g), abi %s, in '%s'"%\ - (repr(version), linux_ver_normalize(version),abi,topdir) - if is_linux: - # Show the actual compiler version by running the compiler. - os.system('%s/bin/icc --version'%topdir) - if is_mac: - # Show the actual compiler version by running the compiler. - os.system('%s/bin/icc --version'%topdir) - - env['INTEL_C_COMPILER_TOP'] = topdir - if is_linux: - paths={'INCLUDE' : 'include', - 'LIB' : 'lib', - 'PATH' : 'bin', - 'LD_LIBRARY_PATH' : 'lib'} - for p in paths.keys(): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) - if is_mac: - paths={'INCLUDE' : 'include', - 'LIB' : 'lib', - 'PATH' : 'bin', - 'LD_LIBRARY_PATH' : 'lib'} - for p in paths.keys(): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) - if is_windows: - # env key reg valname default subdir of top - paths=(('INCLUDE', 'IncludeDir', 'Include'), - ('LIB' , 'LibDir', 'Lib'), - ('PATH' , 'BinDir', 'Bin')) - # We are supposed to ignore version if topdir is set, so set - # it to the emptry string if it's not already set. - if version is None: - version = '' - # Each path has a registry entry, use that or default to subdir - for p in paths: - try: - path=get_intel_registry_value(p[1], version, abi) - # These paths may have $(ICInstallDir) - # which needs to be substituted with the topdir. - path=path.replace('$(ICInstallDir)', topdir + os.sep) - except IntelCError: - # Couldn't get it from registry: use default subdir of topdir - env.PrependENVPath(p[0], os.path.join(topdir, p[2])) - else: - env.PrependENVPath(p[0], string.split(path, os.pathsep)) - # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]])) - - if is_windows: - env['CC'] = 'icl' - env['CXX'] = 'icl' - env['LINK'] = 'xilink' - else: - env['CC'] = 'icc' - env['CXX'] = 'icpc' - # Don't reset LINK here; - # use smart_link which should already be here from link.py. - #env['LINK'] = '$CC' - env['AR'] = 'xiar' - env['LD'] = 'xild' # not used by default - - # This is not the exact (detailed) compiler version, - # just the major version as determined above or specified - # by the user. It is a float like 80 or 90, in normalized form for Linux - # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0) - if version: - env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version) - - if is_windows: - # Look for license file dir - # in system environment, registry, and default location. - envlicdir = os.environ.get("INTEL_LICENSE_FILE", '') - K = ('SOFTWARE\Intel\Licenses') - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) - reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0] - except (AttributeError, SCons.Util.RegError): - reglicdir = "" - defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses' - - licdir = None - for ld in [envlicdir, reglicdir]: - # If the string contains an '@', then assume it's a network - # license (port@system) and good by definition. - if ld and (string.find(ld, '@') != -1 or os.path.exists(ld)): - licdir = ld - break - if not licdir: - licdir = defaultlicdir - if not os.path.exists(licdir): - class ICLLicenseDirWarning(SCons.Warnings.Warning): - pass - SCons.Warnings.enableWarningClass(ICLLicenseDirWarning) - SCons.Warnings.warn(ICLLicenseDirWarning, - "Intel license dir was not found." - " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)." - " Using the default path as a last resort." - % (envlicdir, reglicdir, defaultlicdir)) - env['ENV']['INTEL_LICENSE_FILE'] = licdir - -def exists(env): - if not (is_mac or is_linux or is_windows): - # can't handle this platform - return 0 - - try: - versions = get_all_compiler_versions() - except (SCons.Util.RegError, IntelCError): - versions = None - detected = versions is not None and len(versions) > 0 - if not detected: - # try env.Detect, maybe that will work - if is_windows: - return env.Detect('icl') - elif is_linux: - return env.Detect('icc') - elif is_mac: - return env.Detect('icc') - return detected - -# end of file diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/jar.py b/tools/scons/scons-local-1.2.0/SCons/Tool/jar.py deleted file mode 100644 index be50b016d7..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/jar.py +++ /dev/null @@ -1,104 +0,0 @@ -"""SCons.Tool.jar - -Tool-specific initialization for jar. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/jar.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Subst -import SCons.Util - -def jarSources(target, source, env, for_signature): - """Only include sources that are not a manifest file.""" - try: - env['JARCHDIR'] - except KeyError: - jarchdir_set = False - else: - jarchdir_set = True - jarchdir = env.subst('$JARCHDIR', target=target, source=source) - if jarchdir: - jarchdir = env.fs.Dir(jarchdir) - result = [] - for src in source: - contents = src.get_contents() - if contents[:16] != "Manifest-Version": - if jarchdir_set: - _chdir = jarchdir - else: - try: - _chdir = src.attributes.java_classdir - except AttributeError: - _chdir = None - if _chdir: - # If we are changing the dir with -C, then sources should - # be relative to that directory. - src = SCons.Subst.Literal(src.get_path(_chdir)) - result.append('-C') - result.append(_chdir) - result.append(src) - return result - -def jarManifest(target, source, env, for_signature): - """Look in sources for a manifest file, if any.""" - for src in source: - contents = src.get_contents() - if contents[:16] == "Manifest-Version": - return src - return '' - -def jarFlags(target, source, env, for_signature): - """If we have a manifest, make sure that the 'm' - flag is specified.""" - jarflags = env.subst('$JARFLAGS', target=target, source=source) - for src in source: - contents = src.get_contents() - if contents[:16] == "Manifest-Version": - if not 'm' in jarflags: - return jarflags + 'm' - break - return jarflags - -def generate(env): - """Add Builders and construction variables for jar to an Environment.""" - SCons.Tool.CreateJarBuilder(env) - - env['JAR'] = 'jar' - env['JARFLAGS'] = SCons.Util.CLVar('cf') - env['_JARFLAGS'] = jarFlags - env['_JARMANIFEST'] = jarManifest - env['_JARSOURCES'] = jarSources - env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' - env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" - env['JARSUFFIX'] = '.jar' - -def exists(env): - return env.Detect('jar') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/javac.py b/tools/scons/scons-local-1.2.0/SCons/Tool/javac.py deleted file mode 100644 index b8cabe89b9..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/javac.py +++ /dev/null @@ -1,228 +0,0 @@ -"""SCons.Tool.javac - -Tool-specific initialization for javac. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/javac.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string - -import SCons.Action -import SCons.Builder -from SCons.Node.FS import _my_normcase -from SCons.Tool.JavaCommon import parse_java_file -import SCons.Util - -def classname(path): - """Turn a string (path name) into a Java class name.""" - return string.replace(os.path.normpath(path), os.sep, '.') - -def emit_java_classes(target, source, env): - """Create and return lists of source java files - and their corresponding target class files. - """ - java_suffix = env.get('JAVASUFFIX', '.java') - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - - target[0].must_be_same(SCons.Node.FS.Dir) - classdir = target[0] - - s = source[0].rentry().disambiguate() - if isinstance(s, SCons.Node.FS.File): - sourcedir = s.dir.rdir() - elif isinstance(s, SCons.Node.FS.Dir): - sourcedir = s.rdir() - else: - raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) - - slist = [] - js = _my_normcase(java_suffix) - find_java = lambda n, js=js, ljs=len(js): _my_normcase(n[-ljs:]) == js - for entry in source: - entry = entry.rentry().disambiguate() - if isinstance(entry, SCons.Node.FS.File): - slist.append(entry) - elif isinstance(entry, SCons.Node.FS.Dir): - result = SCons.Util.OrderedDict() - def visit(arg, dirname, names, fj=find_java, dirnode=entry.rdir()): - java_files = filter(fj, names) - # The on-disk entries come back in arbitrary order. Sort - # them so our target and source lists are determinate. - java_files.sort() - mydir = dirnode.Dir(dirname) - java_paths = map(lambda f, d=mydir: d.File(f), java_files) - for jp in java_paths: - arg[jp] = True - - os.path.walk(entry.rdir().get_abspath(), visit, result) - entry.walk(visit, result) - - slist.extend(result.keys()) - else: - raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) - - version = env.get('JAVAVERSION', '1.4') - full_tlist = [] - for f in slist: - tlist = [] - source_file_based = True - pkg_dir = None - if not f.is_derived(): - pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) - if classes: - source_file_based = False - if pkg_dir: - d = target[0].Dir(pkg_dir) - p = pkg_dir + os.sep - else: - d = target[0] - p = '' - for c in classes: - t = d.File(c + class_suffix) - t.attributes.java_classdir = classdir - t.attributes.java_sourcedir = sourcedir - t.attributes.java_classname = classname(p + c) - tlist.append(t) - - if source_file_based: - base = f.name[:-len(java_suffix)] - if pkg_dir: - t = target[0].Dir(pkg_dir).File(base + class_suffix) - else: - t = target[0].File(base + class_suffix) - t.attributes.java_classdir = classdir - t.attributes.java_sourcedir = f.dir - t.attributes.java_classname = classname(base) - tlist.append(t) - - for t in tlist: - t.set_specific_source([f]) - - full_tlist.extend(tlist) - - return full_tlist, slist - -JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - -JavaBuilder = SCons.Builder.Builder(action = JavaAction, - emitter = emit_java_classes, - target_factory = SCons.Node.FS.Entry, - source_factory = SCons.Node.FS.Entry) - -class pathopt: - """ - Callable object for generating javac-style path options from - a construction variable (e.g. -classpath, -sourcepath). - """ - def __init__(self, opt, var, default=None): - self.opt = opt - self.var = var - self.default = default - - def __call__(self, target, source, env, for_signature): - path = env[self.var] - if path and not SCons.Util.is_List(path): - path = [path] - if self.default: - path = path + [ env[self.default] ] - if path: - return [self.opt, string.join(path, os.pathsep)] - #return self.opt + " " + string.join(path, os.pathsep) - else: - return [] - #return "" - -def Java(env, target, source, *args, **kw): - """ - A pseudo-Builder wrapper around the separate JavaClass{File,Dir} - Builders. - """ - if not SCons.Util.is_List(target): - target = [target] - if not SCons.Util.is_List(source): - source = [source] - - # Pad the target list with repetitions of the last element in the - # list so we have a target for every source element. - target = target + ([target[-1]] * (len(source) - len(target))) - - java_suffix = env.subst('$JAVASUFFIX') - result = [] - - for t, s in zip(target, source): - if isinstance(s, SCons.Node.FS.Base): - if isinstance(s, SCons.Node.FS.File): - b = env.JavaClassFile - else: - b = env.JavaClassDir - else: - if os.path.isfile(s): - b = env.JavaClassFile - elif os.path.isdir(s): - b = env.JavaClassDir - elif s[-len(java_suffix):] == java_suffix: - b = env.JavaClassFile - else: - b = env.JavaClassDir - result.extend(apply(b, (t, s) + args, kw)) - - return result - -def generate(env): - """Add Builders and construction variables for javac to an Environment.""" - java_file = SCons.Tool.CreateJavaFileBuilder(env) - java_class = SCons.Tool.CreateJavaClassFileBuilder(env) - java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) - java_class.add_emitter(None, emit_java_classes) - java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) - java_class_dir.emitter = emit_java_classes - - env.AddMethod(Java) - - env['JAVAC'] = 'javac' - env['JAVACFLAGS'] = SCons.Util.CLVar('') - env['JAVABOOTCLASSPATH'] = [] - env['JAVACLASSPATH'] = [] - env['JAVASOURCEPATH'] = [] - env['_javapathopt'] = pathopt - env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} ' - env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} ' - env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' - env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' - env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' - env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" - env['JAVACLASSSUFFIX'] = '.class' - env['JAVASUFFIX'] = '.java' - -def exists(env): - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/javah.py b/tools/scons/scons-local-1.2.0/SCons/Tool/javah.py deleted file mode 100644 index 3a39aebcaf..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/javah.py +++ /dev/null @@ -1,132 +0,0 @@ -"""SCons.Tool.javah - -Tool-specific initialization for javah. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/javah.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Tool.javac -import SCons.Util - -def emit_java_headers(target, source, env): - """Create and return lists of Java stub header files that will - be created from a set of class files. - """ - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - classdir = env.get('JAVACLASSDIR') - - if not classdir: - try: - s = source[0] - except IndexError: - classdir = '.' - else: - try: - classdir = s.attributes.java_classdir - except AttributeError: - classdir = '.' - classdir = env.Dir(classdir).rdir() - - if str(classdir) == '.': - c_ = None - else: - c_ = str(classdir) + os.sep - - slist = [] - for src in source: - try: - classname = src.attributes.java_classname - except AttributeError: - classname = str(src) - if c_ and classname[:len(c_)] == c_: - classname = classname[len(c_):] - if class_suffix and classname[-len(class_suffix):] == class_suffix: - classname = classname[:-len(class_suffix)] - classname = SCons.Tool.javac.classname(classname) - s = src.rfile() - s.attributes.java_classname = classname - slist.append(s) - - s = source[0].rfile() - if not hasattr(s.attributes, 'java_classdir'): - s.attributes.java_classdir = classdir - - if target[0].__class__ is SCons.Node.FS.File: - tlist = target - else: - if not isinstance(target[0], SCons.Node.FS.Dir): - target[0].__class__ = SCons.Node.FS.Dir - target[0]._morph() - tlist = [] - for s in source: - fname = string.replace(s.attributes.java_classname, '.', '_') + '.h' - t = target[0].File(fname) - t.attributes.java_lookupdir = target[0] - tlist.append(t) - - return tlist, source - -def JavaHOutFlagGenerator(target, source, env, for_signature): - try: - t = target[0] - except (AttributeError, TypeError): - t = target - try: - return '-d ' + str(t.attributes.java_lookupdir) - except AttributeError: - return '-o ' + str(t) - -def getJavaHClassPath(env,target, source, for_signature): - path = "${SOURCE.attributes.java_classdir}" - if env.has_key('JAVACLASSPATH') and env['JAVACLASSPATH']: - path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) - return "-classpath %s" % (path) - -def generate(env): - """Add Builders and construction variables for javah to an Environment.""" - java_javah = SCons.Tool.CreateJavaHBuilder(env) - java_javah.emitter = emit_java_headers - - env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator - env['JAVAH'] = 'javah' - env['JAVAHFLAGS'] = SCons.Util.CLVar('') - env['_JAVAHCLASSPATH'] = getJavaHClassPath - env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' - env['JAVACLASSSUFFIX'] = '.class' - -def exists(env): - return env.Detect('javah') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/latex.py b/tools/scons/scons-local-1.2.0/SCons/Tool/latex.py deleted file mode 100644 index 549f6d3740..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/latex.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.latex - -Tool-specific initialization for LaTeX. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/latex.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.LaTeX -import SCons.Util -import SCons.Tool -import SCons.Tool.tex - -LaTeXAction = None - -def LaTeXAuxFunction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( LaTeXAction, target, source, env ) - return result - -LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - -def generate(env): - """Add Builders and construction variables for LaTeX to an Environment.""" - global LaTeXAction - if LaTeXAction is None: - LaTeXAction = SCons.Action.Action('$LATEXCOM', '$LATEXCOMSTR') - - import dvi - dvi.generate(env) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['DVI'] - bld.add_action('.ltx', LaTeXAuxAction) - bld.add_action('.latex', LaTeXAuxAction) - bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter) - bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter) - - env['LATEX'] = 'latex' - env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['LATEXCOM'] = 'cd ${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 3 - -def exists(env): - return env.Detect('latex') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/lex.py b/tools/scons/scons-local-1.2.0/SCons/Tool/lex.py deleted file mode 100644 index f2e0e856d5..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/lex.py +++ /dev/null @@ -1,93 +0,0 @@ -"""SCons.Tool.lex - -Tool-specific initialization for lex. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/lex.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import string - -import SCons.Action -import SCons.Tool -import SCons.Util - -LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") - -def lexEmitter(target, source, env): - sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) - - if sourceExt == ".lm": # If using Objective-C - target = [sourceBase + ".m"] # the extension is ".m". - - # This emitter essentially tries to add to the target all extra - # files generated by flex. - - # Different options that are used to trigger the creation of extra files. - fileGenOptions = ["--header-file=", "--tables-file="] - - lexflags = env.subst("$LEXFLAGS", target=target, source=source) - for option in SCons.Util.CLVar(lexflags): - for fileGenOption in fileGenOptions: - l = len(fileGenOption) - if option[:l] == fileGenOption: - # A file generating option is present, so add the - # file name to the target list. - fileName = string.strip(option[l:]) - target.append(fileName) - return (target, source) - -def generate(env): - """Add Builders and construction variables for lex to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - # C - c_file.add_action(".l", LexAction) - c_file.add_emitter(".l", lexEmitter) - - c_file.add_action(".lex", LexAction) - c_file.add_emitter(".lex", lexEmitter) - - # Objective-C - cxx_file.add_action(".lm", LexAction) - cxx_file.add_emitter(".lm", lexEmitter) - - # C++ - cxx_file.add_action(".ll", LexAction) - cxx_file.add_emitter(".ll", lexEmitter) - - env["LEX"] = env.Detect("flex") or "lex" - env["LEXFLAGS"] = SCons.Util.CLVar("") - env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" - -def exists(env): - return env.Detect(["flex", "lex"]) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/link.py b/tools/scons/scons-local-1.2.0/SCons/Tool/link.py deleted file mode 100644 index d02bb25fb4..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/link.py +++ /dev/null @@ -1,112 +0,0 @@ -"""SCons.Tool.link - -Tool-specific initialization for the generic Posix linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/link.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util -import SCons.Warnings - -from SCons.Tool.FortranCommon import isfortran - -cplusplus = __import__('c++', globals(), locals(), []) - -issued_mixed_link_warning = False - -def smart_link(source, target, env, for_signature): - has_cplusplus = cplusplus.iscplusplus(source) - has_fortran = isfortran(env, source) - if has_cplusplus and has_fortran: - global issued_mixed_link_warning - if not issued_mixed_link_warning: - msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ - "This may generate a buggy executable if the %s\n\t" + \ - "compiler does not know how to deal with Fortran runtimes." - SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, - msg % repr(env.subst('$CXX'))) - issued_mixed_link_warning = True - return '$CXX' - elif has_fortran: - return '$FORTRAN' - elif has_cplusplus: - return '$CXX' - return '$CC' - -def shlib_emitter(target, source, env): - for tgt in target: - tgt.attributes.shared = 1 - return (target, source) - -def generate(env): - """Add Builders and construction variables for gnulink to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - # don't set up the emitter, cause AppendUnique will generate a list - # starting with None :-( - env.Append(SHLIBEMITTER = [shlib_emitter]) - env['SMARTLINK'] = smart_link - env['LINK'] = "$SMARTLINK" - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBDIRPREFIX']='-L' - env['LIBDIRSUFFIX']='' - env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' - env['LIBLINKPREFIX']='-l' - env['LIBLINKSUFFIX']='' - - if env['PLATFORM'] == 'hpux': - env['SHLIBSUFFIX'] = '.sl' - elif env['PLATFORM'] == 'aix': - env['SHLIBSUFFIX'] = '.a' - - # For most platforms, a loadable module is the same as a shared - # library. Platforms which are different can override these, but - # setting them the same means that LoadableModule works everywhere. - SCons.Tool.createLoadableModuleBuilder(env) - env['LDMODULE'] = '$SHLINK' - env['LDMODULEPREFIX'] = '$SHLIBPREFIX' - env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' - env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - env['LDMODULECOM'] = '$SHLINKCOM' - - - -def exists(env): - # This module isn't really a Tool on its own, it's common logic for - # other linkers. - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/linkloc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/linkloc.py deleted file mode 100644 index b0550c6e3b..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/linkloc.py +++ /dev/null @@ -1,105 +0,0 @@ -"""SCons.Tool.linkloc - -Tool specification for the LinkLoc linker for the Phar Lap ETS embedded -operating system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/linkloc.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re - -import SCons.Action -import SCons.Defaults -import SCons.Errors -import SCons.Tool -import SCons.Util - -from SCons.Tool.msvc import get_msvc_paths -from SCons.Tool.PharLapCommon import addPharLapPaths - -_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') - -def repl_linker_command(m): - # Replaces any linker command file directives (e.g. "@foo.lnk") with - # the actual contents of the file. - try: - f=open(m.group(2), "r") - return m.group(1) + f.read() - except IOError: - # the linker should return an error if it can't - # find the linker command file so we will remain quiet. - # However, we will replace the @ with a # so we will not continue - # to find it with recursive substitution - return m.group(1) + '#' + m.group(2) - -class LinklocGenerator: - def __init__(self, cmdline): - self.cmdline = cmdline - - def __call__(self, env, target, source, for_signature): - if for_signature: - # Expand the contents of any linker command files recursively - subs = 1 - strsub = env.subst(self.cmdline, target=target, source=source) - while subs: - strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) - return strsub - else: - return "${TEMPFILE('" + self.cmdline + "')}" - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SUBST_CMD_FILE'] = LinklocGenerator - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') - env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -dll $TARGET $SOURCES")}' - env['SHLIBEMITTER']= None - env['LINK'] = "linkloc" - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -exe $TARGET $SOURCES")}' - env['LIBDIRPREFIX']='-libpath ' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='-lib ' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - msvs_version = env.get('MSVS_VERSION') - include_path, lib_path, exe_path = get_msvc_paths(env, version = msvs_version) - env['ENV']['LIB'] = lib_path - env.PrependENVPath('PATH', exe_path) - - addPharLapPaths(env) - -def exists(env): - return env.Detect('linkloc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/m4.py b/tools/scons/scons-local-1.2.0/SCons/Tool/m4.py deleted file mode 100644 index 0d81d7146f..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/m4.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.m4 - -Tool-specific initialization for m4. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/m4.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add Builders and construction variables for m4 to an Environment.""" - M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR') - bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4') - - env['BUILDERS']['M4'] = bld - - # .m4 files might include other files, and it would be pretty hard - # to write a scanner for it, so let's just cd to the dir of the m4 - # file and run from there. - # The src_suffix setup is like so: file.c.m4 -> file.c, - # file.cpp.m4 -> file.cpp etc. - env['M4'] = 'm4' - env['M4FLAGS'] = SCons.Util.CLVar('-E') - env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' - -def exists(env): - return env.Detect('m4') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/masm.py b/tools/scons/scons-local-1.2.0/SCons/Tool/masm.py deleted file mode 100644 index 8508900874..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/masm.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.Tool.masm - -Tool-specific initialization for the Microsoft Assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/masm.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for masm to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - shared_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['AS'] = 'ml' - env['ASFLAGS'] = SCons.Util.CLVar('/nologo') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('ml') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/midl.py b/tools/scons/scons-local-1.2.0/SCons/Tool/midl.py deleted file mode 100644 index df1bf9a5d3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/midl.py +++ /dev/null @@ -1,90 +0,0 @@ -"""SCons.Tool.midl - -Tool-specific initialization for midl (Microsoft IDL compiler). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/midl.py 3842 2008/12/20 22:59:52 scons" - -import string - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner.IDL -import SCons.Util - -def midl_emitter(target, source, env): - """Produces a list of outputs from the MIDL compiler""" - base, ext = SCons.Util.splitext(str(target[0])) - tlb = target[0] - incl = base + '.h' - interface = base + '_i.c' - t = [tlb, incl, interface] - - midlcom = env['MIDLCOM'] - - if string.find(midlcom, '/proxy') != -1: - proxy = base + '_p.c' - t.append(proxy) - if string.find(midlcom, '/dlldata') != -1: - dlldata = base + '_data.c' - t.append(dlldata) - - return (t,source) - -idl_scanner = SCons.Scanner.IDL.IDLScan() - -midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR') - -midl_builder = SCons.Builder.Builder(action = midl_action, - src_suffix = '.idl', - suffix='.tlb', - emitter = midl_emitter, - source_scanner = idl_scanner) - -def generate(env): - """Add Builders and construction variables for midl to an Environment.""" - - env['MIDL'] = 'MIDL.EXE' - env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') - env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' - env['BUILDERS']['TypeLibrary'] = midl_builder - -def exists(env): - if not env['PLATFORM'] in ('win32', 'cygwin'): - return 0 - - import SCons.Tool.msvs - if SCons.Tool.msvs.is_msvs_installed(): - # there's at least one version of MSVS installed, which comes with midl: - return 1 - else: - return env.Detect('midl') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/mingw.py b/tools/scons/scons-local-1.2.0/SCons/Tool/mingw.py deleted file mode 100644 index faec2e9edb..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/mingw.py +++ /dev/null @@ -1,151 +0,0 @@ -"""SCons.Tool.gcc - -Tool-specific initialization for MinGW (http://www.mingw.org/) - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/mingw.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Tool -import SCons.Util - -# This is what we search for to find mingw: -key_program = 'mingw32-gcc' - -def find(env): - # First search in the SCons path and then the OS path: - return env.WhereIs(key_program) or SCons.Util.WhereIs(key_program) - -def shlib_generator(target, source, env, for_signature): - cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) - - def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) - - return [cmd] - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Append an import library to the list of targets. - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX')) - - # Append a def file target if there isn't already a def file target - # or a def file source. There is no option to disable def file - # target emitting, because I can't figure out why someone would ever - # want to turn it off. - def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - if not def_source and not def_target: - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX')) - - return (target, source) - - -shlib_action = SCons.Action.Action(shlib_generator, generator=1) - -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') - -res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', - source_scanner=SCons.Tool.SourceFileScanner) -SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) - -def generate(env): - mingw = find(env) - if mingw: - dir = os.path.dirname(mingw) - env.PrependENVPath('PATH', dir ) - - - # Most of mingw is the same as gcc and friends... - gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'm4'] - for tool in gnu_tools: - SCons.Tool.Tool(tool)(env) - - #... but a few things differ: - env['CC'] = 'gcc' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['CXX'] = 'g++' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = shlib_action - env['LDMODULECOM'] = shlib_action - env.Append(SHLIBEMITTER = [shlib_emitter]) - env['AS'] = 'as' - - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' - env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' - - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = 'windres' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['RCINCPREFIX'] = '--include-dir ' - env['RCINCSUFFIX'] = '' - env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' - env['BUILDERS']['RES'] = res_builder - - # Some setting from the platform also have to be overridden: - env['OBJSUFFIX'] = '.o' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return find(env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/mslib.py b/tools/scons/scons-local-1.2.0/SCons/Tool/mslib.py deleted file mode 100644 index 340f9927dd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/mslib.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.mslib - -Tool-specific initialization for lib (MicroSoft library archiver). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/mslib.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Tool.msvs -import SCons.Tool.msvc -import SCons.Util - -def generate(env): - """Add Builders and construction variables for lib to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - try: - version = SCons.Tool.msvs.get_default_visualstudio_version(env) - - if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: - include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(env,version) - else: - include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(env,version) - - # since other tools can set this, we just make sure that the - # relevant stuff from MSVS is in there somewhere. - env.PrependENVPath('PATH', exe_path) - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass - - env['AR'] = 'lib' - env['ARFLAGS'] = SCons.Util.CLVar('/nologo') - env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - -def exists(env): - try: - v = SCons.Tool.msvs.get_visualstudio_versions() - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass - - if not v: - return env.Detect('lib') - else: - # there's at least one version of MSVS installed. - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/mslink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/mslink.py deleted file mode 100644 index 298ae7c649..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/mslink.py +++ /dev/null @@ -1,249 +0,0 @@ -"""SCons.Tool.mslink - -Tool-specific initialization for the Microsoft linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/mslink.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Action -import SCons.Defaults -import SCons.Errors -import SCons.Platform.win32 -import SCons.Tool -import SCons.Tool.msvc -import SCons.Tool.msvs -import SCons.Util - -def pdbGenerator(env, target, source, for_signature): - try: - return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] - except (AttributeError, IndexError): - return None - -def windowsShlinkTargets(target, source, env, for_signature): - listCmd = [] - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) - - return listCmd - -def windowsShlinkSources(target, source, env, for_signature): - listCmd = [] - - deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX") - for src in source: - if src == deffile: - # Treat this source as a .def file. - listCmd.append("/def:%s" % src.get_string(for_signature)) - else: - # Just treat it as a generic source file. - listCmd.append(src) - return listCmd - -def windowsLibEmitter(target, source, env): - SCons.Tool.msvc.validate_vars(env) - - extratargets = [] - extrasources = [] - - dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX") - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") - - insert_def = env.subst("$WINDOWS_INSERT_DEF") - if not insert_def in ['', '0', 0] and \ - not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): - - # append a def file to the list of sources - extrasources.append( - env.ReplaceIxes(dll, - "SHLIBPREFIX", "SHLIBSUFFIX", - "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0): - # MSVC 8 automatically generates .manifest files that must be installed - extratargets.append( - env.ReplaceIxes(dll, - "SHLIBPREFIX", "SHLIBSUFFIX", - "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) - - if env.has_key('PDB') and env['PDB']: - pdb = env.arg2nodes('$PDB', target=target, source=source)[0] - extratargets.append(pdb) - target[0].attributes.pdb = pdb - - if not no_import_lib and \ - not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): - # Append an import library to the list of targets. - extratargets.append( - env.ReplaceIxes(dll, - "SHLIBPREFIX", "SHLIBSUFFIX", - "LIBPREFIX", "LIBSUFFIX")) - # and .exp file is created if there are exports from a DLL - extratargets.append( - env.ReplaceIxes(dll, - "SHLIBPREFIX", "SHLIBSUFFIX", - "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX")) - - return (target+extratargets, source+extrasources) - -def prog_emitter(target, source, env): - SCons.Tool.msvc.validate_vars(env) - - extratargets = [] - - exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX") - if not exe: - raise SCons.Errors.UserError, "An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX") - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0): - # MSVC 8 automatically generates .manifest files that have to be installed - extratargets.append( - env.ReplaceIxes(exe, - "PROGPREFIX", "PROGSUFFIX", - "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) - - if env.has_key('PDB') and env['PDB']: - pdb = env.arg2nodes('$PDB', target=target, source=source)[0] - extratargets.append(pdb) - target[0].attributes.pdb = pdb - - return (target+extratargets,source) - -def RegServerFunc(target, source, env): - if env.has_key('register') and env['register']: - ret = regServerAction([target[0]], [source[0]], env) - if ret: - raise SCons.Errors.UserError, "Unable to register %s" % target[0] - else: - print "Registered %s sucessfully" % target[0] - return ret - return 0 - -regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") -regServerCheck = SCons.Action.Action(RegServerFunc, None) -shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}') -compositeLinkAction = shlibLinkAction + regServerCheck - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') - env['_SHLINK_TARGETS'] = windowsShlinkTargets - env['_SHLINK_SOURCES'] = windowsShlinkSources - env['SHLINKCOM'] = compositeLinkAction - env.Append(SHLIBEMITTER = [windowsLibEmitter]) - env['LINK'] = 'link' - env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') - env['_PDB'] = pdbGenerator - env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $SOURCES.windows")}' - env.Append(PROGEMITTER = [prog_emitter]) - env['LIBDIRPREFIX']='/LIBPATH:' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['WIN32_INSERT_DEF'] = 0 - env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' - env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' - env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}' - - env['WIN32EXPPREFIX'] = '' - env['WIN32EXPSUFFIX'] = '.exp' - env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}' - env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}' - - env['WINDOWSSHLIBMANIFESTPREFIX'] = '' - env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest' - env['WINDOWSPROGMANIFESTPREFIX'] = '' - env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest' - - env['REGSVRACTION'] = regServerCheck - env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') - env['REGSVRFLAGS'] = '/s ' - env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' - - try: - version = SCons.Tool.msvs.get_default_visualstudio_version(env) - - if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: - include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(env,version) - else: - include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(env,version) - - # since other tools can set these, we just make sure that the - # relevant stuff from MSVS is in there somewhere. - env.PrependENVPath('INCLUDE', include_path) - env.PrependENVPath('LIB', lib_path) - env.PrependENVPath('PATH', exe_path) - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass - - # For most platforms, a loadable module is the same as a shared - # library. Platforms which are different can override these, but - # setting them the same means that LoadableModule works everywhere. - SCons.Tool.createLoadableModuleBuilder(env) - env['LDMODULE'] = '$SHLINK' - env['LDMODULEPREFIX'] = '$SHLIBPREFIX' - env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' - env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - # We can't use '$SHLINKCOM' here because that will stringify the - # action list on expansion, and will then try to execute expanded - # strings, with the upshot that it would try to execute RegServerFunc - # as a command. - env['LDMODULECOM'] = compositeLinkAction - -def exists(env): - platform = env.get('PLATFORM', '') - if SCons.Tool.msvs.is_msvs_installed(): - # there's at least one version of MSVS installed. - return 1 - elif platform in ('win32', 'cygwin'): - # Only explicitly search for a 'link' executable on Windows - # systems. Some other systems (e.g. Ubuntu Linux) have an - # executable named 'link' and we don't want that to make SCons - # think Visual Studio is installed. - return env.Detect('link') - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/msvc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/msvc.py deleted file mode 100644 index 5b7874a202..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/msvc.py +++ /dev/null @@ -1,766 +0,0 @@ -"""engine.SCons.Tool.msvc - -Tool-specific initialization for Microsoft Visual C/C++. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/msvc.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re -import string - -import SCons.Action -import SCons.Builder -import SCons.Errors -import SCons.Platform.win32 -import SCons.Tool -import SCons.Tool.msvs -import SCons.Util -import SCons.Warnings -import SCons.Scanner.RC - -CSuffixes = ['.c', '.C'] -CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] - -def _parse_msvc7_overrides(version,platform): - """ Parse any overridden defaults for MSVS directory locations - in MSVS .NET. """ - - # First, we get the shell folder for this user: - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - comps = "" - try: - (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion' +\ - r'\Explorer\Shell Folders\Local AppData') - except SCons.Util.RegError: - raise SCons.Errors.InternalError, \ - "The Local AppData directory was not found in the registry." - - comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat' - dirs = {} - - if os.path.exists(comps): - # now we parse the directories from this file, if it exists. - # We only look for entries after: - # [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories], - # since this file could contain a number of things... - lines = None - try: - import codecs - except ImportError: - pass - else: - try: - f = codecs.open(comps, 'r', 'utf16') - encoder = codecs.getencoder('ascii') - lines = map(lambda l, e=encoder: e(l)[0], f.readlines()) - except (LookupError, UnicodeError): - lines = codecs.open(comps, 'r', 'utf8').readlines() - if lines is None: - lines = open(comps, 'r').readlines() - if 'x86' == platform: platform = 'Win32' - - found = 0 - for line in lines: - line.strip() - if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories]'%platform) >= 0: - found = 1 - elif line == '' or line[:1] == '[': - found = 0 - elif found == 1: - kv = line.split('=', 1) - if len(kv) == 2: - (key, val) = kv - key = key.replace(' Dirs','') - dirs[key.upper()] = val - f.close() - else: - # since the file didn't exist, we have only the defaults in - # the registry to work with. - - if 'x86' == platform: platform = 'Win32' - - try: - K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version - K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories'%platform - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K) - i = 0 - while 1: - try: - (key,val,t) = SCons.Util.RegEnumValue(k,i) - key = key.replace(' Dirs','') - dirs[key.upper()] = val - i = i + 1 - except SCons.Util.RegError: - break - except SCons.Util.RegError: - # if we got here, then we didn't find the registry entries: - raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." - return dirs - -def _parse_msvc8_overrides(version,platform,suite): - """ Parse any overridden defaults for MSVC directory locations - in MSVC 2005. """ - - # In VS8 the user can change the location of the settings file that - # contains the include, lib and binary paths. Try to get the location - # from registry - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - # XXX This code assumes anything that isn't EXPRESS uses the default - # registry key string. Is this really true for all VS suites? - if suite == 'EXPRESS': - s = '\\VCExpress\\' - else: - s = '\\VisualStudio\\' - - settings_path = "" - try: - (settings_path, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft' + s + version +\ - r'\Profile\AutoSaveFile') - settings_path = settings_path.upper() - except SCons.Util.RegError: - raise SCons.Errors.InternalError, \ - "The VS8 settings file location was not found in the registry." - - # Look for potential environment variables in the settings path - if settings_path.find('%VSSPV_VISUALSTUDIO_DIR%') >= 0: - # First replace a special variable named %vsspv_visualstudio_dir% - # that is not found in the OSs environment variables... - try: - (value, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft' + s + version +\ - r'\VisualStudioLocation') - settings_path = settings_path.replace('%VSSPV_VISUALSTUDIO_DIR%', value) - except SCons.Util.RegError: - raise SCons.Errors.InternalError, "The VS8 settings file location was not found in the registry." - - if settings_path.find('%') >= 0: - # Collect global environment variables - env_vars = {} - - # Read all the global environment variables of the current user - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_CURRENT_USER, r'Environment') - i = 0 - while 1: - try: - (key,val,t) = SCons.Util.RegEnumValue(k,i) - env_vars[key.upper()] = val.upper() - i = i + 1 - except SCons.Util.RegError: - break - - # And some more variables that are not found in the registry - env_vars['USERPROFILE'] = os.getenv('USERPROFILE') - env_vars['SystemDrive'] = os.getenv('SystemDrive') - - found_var = 1 - while found_var: - found_var = 0 - for env_var in env_vars: - if settings_path.find(r'%' + env_var + r'%') >= 0: - settings_path = settings_path.replace(r'%' + env_var + r'%', env_vars[env_var]) - found_var = 1 - - dirs = {} - - if os.path.exists(settings_path): - # now we parse the directories from this file, if it exists. - import xml.dom.minidom - doc = xml.dom.minidom.parse(settings_path) - user_settings = doc.getElementsByTagName('UserSettings')[0] - tool_options = user_settings.getElementsByTagName('ToolsOptions')[0] - tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory') - environment_var_map = { - 'IncludeDirectories' : 'INCLUDE', - 'LibraryDirectories' : 'LIBRARY', - 'ExecutableDirectories' : 'PATH', - } - for category in tool_options_categories: - category_name = category.attributes.get('name') - if category_name is not None and category_name.value == 'Projects': - subcategories = category.getElementsByTagName('ToolsOptionsSubCategory') - for subcategory in subcategories: - subcategory_name = subcategory.attributes.get('name') - if subcategory_name is not None and subcategory_name.value == 'VCDirectories': - properties = subcategory.getElementsByTagName('PropertyValue') - for property in properties: - property_name = property.attributes.get('name') - if property_name is None: - continue - var_name = environment_var_map.get(property_name) - if var_name: - data = property.childNodes[0].data - value_list = string.split(data, '|') - if len(value_list) == 1: - dirs[var_name] = value_list[0] - else: - while value_list: - dest, value = value_list[:2] - del value_list[:2] - # ToDo: Support for destinations - # other than Win32 - if dest == 'Win32': - dirs[var_name] = value - break - else: - # There are no default directories in the registry for VS8 Express :( - raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." - return dirs - -def _get_msvc7_path(path, version, platform): - """ - Get Visual Studio directories from version 7 (MSVS .NET) - (it has a different registry structure than versions before it) - """ - # first, look for a customization of the default values in the - # registry: These are sometimes stored in the Local Settings area - # for Visual Studio, in a file, so we have to parse it. - dirs = _parse_msvc7_overrides(version,platform) - - if dirs.has_key(path): - p = dirs[path] - else: - raise SCons.Errors.InternalError, \ - "Unable to retrieve the %s path from MS VC++."%path - - # collect some useful information for later expansions... - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - - # expand the directory path variables that we support. If there - # is a variable we don't support, then replace that entry with - # "---Unknown Location VSInstallDir---" or something similar, to clue - # people in that we didn't find something, and so env expansion doesn't - # do weird things with the $(xxx)'s - s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') - - def repl(match, paths=paths): - key = string.upper(match.group(1)) - if paths.has_key(key): - return paths[key] - else: - # Now look in the global environment variables - envresult = os.getenv(key) - if not envresult is None: - return envresult + '\\' - else: - return '---Unknown Location %s---' % match.group() - - rv = [] - for entry in p.split(os.pathsep): - entry = s.sub(repl,entry).rstrip('\n\r') - rv.append(entry) - - return string.join(rv,os.pathsep) - -def _get_msvc8_path(path, version, platform, suite): - """ - Get Visual Studio directories from version 8 (MSVS 2005) - (it has a different registry structure than versions before it) - """ - # first, look for a customization of the default values in the - # registry: These are sometimes stored in the Local Settings area - # for Visual Studio, in a file, so we have to parse it. - dirs = _parse_msvc8_overrides(version, platform, suite) - - if dirs.has_key(path): - p = dirs[path] - else: - raise SCons.Errors.InternalError, \ - "Unable to retrieve the %s path from MS VC++."%path - - # collect some useful information for later expansions... - paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite) - - # expand the directory path variables that we support. If there - # is a variable we don't support, then replace that entry with - # "---Unknown Location VSInstallDir---" or something similar, to clue - # people in that we didn't find something, and so env expansion doesn't - # do weird things with the $(xxx)'s - s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') - - def repl(match, paths=paths): - key = string.upper(match.group(1)) - if paths.has_key(key): - return paths[key] - else: - return '---Unknown Location %s---' % match.group() - - rv = [] - for entry in p.split(os.pathsep): - entry = s.sub(repl,entry).rstrip('\n\r') - rv.append(entry) - - return string.join(rv,os.pathsep) - -def get_msvc_path(env, path, version): - """ - Get a list of visualstudio directories (include, lib or path). - Return a string delimited by the os.pathsep separator (';'). An - exception will be raised if unable to access the registry or - appropriate registry keys not found. - """ - - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - # normalize the case for comparisons (since the registry is case - # insensitive) - path = string.upper(path) - - if path=='LIB': - path= 'LIBRARY' - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - platform = env.get('MSVS8_PLATFORM', 'x86') - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - else: - platform = 'x86' - - if version_num >= 8.0: - return _get_msvc8_path(path, str(version_num), platform, suite) - elif version_num >= 7.0: - return _get_msvc7_path(path, str(version_num), platform) - - path = string.upper(path + ' Dirs') - K = ('Software\\Microsoft\\Devstudio\\%s\\' + - 'Build System\\Components\\Platforms\\Win32 (x86)\\Directories') % \ - (version) - for base in (SCons.Util.HKEY_CURRENT_USER, - SCons.Util.HKEY_LOCAL_MACHINE): - try: - k = SCons.Util.RegOpenKeyEx(base,K) - i = 0 - while 1: - try: - (p,v,t) = SCons.Util.RegEnumValue(k,i) - if string.upper(p) == path: - return v - i = i + 1 - except SCons.Util.RegError: - break - except SCons.Util.RegError: - pass - - # if we got here, then we didn't find the registry entries: - raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path - -def _get_msvc6_default_paths(version, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC 6.0 tools properly, if the information wasn't available - from the registry.""" - MVSdir = None - paths = {} - exe_path = '' - lib_path = '' - include_path = '' - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - MVSdir = paths['VSINSTALLDIR'] - except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError): - if os.environ.has_key('MSDEVDIR'): - MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..')) - else: - MVSdir = r'C:\Program Files\Microsoft Visual Studio' - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'VC98') - - MVSCommondir = r'%s\Common' % MVSdir - if use_mfc_dirs: - mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir) - mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir - else: - mfc_include_ = '' - mfc_lib_ = '' - include_path = r'%s%s\include' % (mfc_include_, MVSVCdir) - lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir) - - if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT": - osdir = 'WINNT' - else: - osdir = 'WIN95' - - exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir) - return (include_path, lib_path, exe_path) - -def _get_msvc7_default_paths(env, version, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC .NET tools properly, if the information wasn't available - from the registry.""" - - MVSdir = None - paths = {} - exe_path = '' - lib_path = '' - include_path = '' - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - MVSdir = paths['VSINSTALLDIR'] - except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): - if os.environ.has_key('VSCOMNTOOLS'): - MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) - else: - # last resort -- default install location - MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET' - - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'Vc7') - - MVSCommondir = r'%s\Common7' % MVSdir - if use_mfc_dirs: - mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir - mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir - else: - mfc_include_ = '' - mfc_lib_ = '' - include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir) - lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir) - exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'): - include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR'] - lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR'] - exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR'] - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'): - exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION']) - - return (include_path, lib_path, exe_path) - -def _get_msvc8_default_paths(env, version, suite, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC 8 tools properly, if the information wasn't available - from the registry.""" - - MVSdir = None - paths = {} - exe_paths = [] - lib_paths = [] - include_paths = [] - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite) - MVSdir = paths['VSINSTALLDIR'] - except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): - if os.environ.has_key('VSCOMNTOOLS'): - MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) - else: - # last resort -- default install location - MVSdir = os.getenv('ProgramFiles') + r'\Microsoft Visual Studio 8' - - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'VC') - - MVSCommondir = os.path.join(MVSdir, 'Common7') - include_paths.append( os.path.join(MVSVCdir, 'include') ) - lib_paths.append( os.path.join(MVSVCdir, 'lib') ) - for base, subdir in [(MVSCommondir,'IDE'), (MVSVCdir,'bin'), - (MVSCommondir,'Tools'), (MVSCommondir,r'Tools\bin')]: - exe_paths.append( os.path.join( base, subdir) ) - - if paths.has_key('PLATFORMSDKDIR'): - PlatformSdkDir = paths['PLATFORMSDKDIR'] - else: - PlatformSdkDir = os.path.join(MVSVCdir,'PlatformSDK') - platform_include_path = os.path.join( PlatformSdkDir, 'Include' ) - include_paths.append( platform_include_path ) - lib_paths.append( os.path.join( PlatformSdkDir, 'Lib' ) ) - if use_mfc_dirs: - if paths.has_key('PLATFORMSDKDIR'): - include_paths.append( os.path.join( platform_include_path, 'mfc' ) ) - include_paths.append( os.path.join( platform_include_path, 'atl' ) ) - else: - atlmfc_path = os.path.join( MVSVCdir, 'atlmfc' ) - include_paths.append( os.path.join( atlmfc_path, 'include' ) ) - lib_paths.append( os.path.join( atlmfc_path, 'lib' ) ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'): - fwdir = paths['FRAMEWORKSDKDIR'] - include_paths.append( os.path.join( fwdir, 'include' ) ) - lib_paths.append( os.path.join( fwdir, 'lib' ) ) - exe_paths.append( os.path.join( fwdir, 'bin' ) ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'): - exe_paths.append( os.path.join( paths['FRAMEWORKDIR'], paths['FRAMEWORKVERSION'] ) ) - - include_path = string.join( include_paths, os.pathsep ) - lib_path = string.join(lib_paths, os.pathsep ) - exe_path = string.join(exe_paths, os.pathsep ) - return (include_path, lib_path, exe_path) - -def get_msvc_paths(env, version=None, use_mfc_dirs=0): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values - of those three environment variables that should be set - in order to execute the MSVC tools properly.""" - exe_path = '' - lib_path = '' - include_path = '' - - if not version: - versions = SCons.Tool.msvs.get_visualstudio_versions() - if versions: - version = versions[0] #use highest version by default - else: - version = '6.0' - - # Some of the configured directories only - # appear if the user changes them from the default. - # Therefore, we'll see if we can get the path to the MSDev - # base installation from the registry and deduce the default - # directories. - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs) - elif version_num >= 7.0: - defpaths = _get_msvc7_default_paths(env, version, use_mfc_dirs) - else: - defpaths = _get_msvc6_default_paths(version, use_mfc_dirs) - - try: - include_path = get_msvc_path(env, "include", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - include_path = defpaths[0] - - try: - lib_path = get_msvc_path(env, "lib", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - lib_path = defpaths[1] - - try: - exe_path = get_msvc_path(env, "path", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - exe_path = defpaths[2] - - return (include_path, lib_path, exe_path) - -def get_msvc_default_paths(env, version=None, use_mfc_dirs=0): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC tools properly. This will only return the default - locations for the tools, not the values used by MSVS in their - directory setup area. This can help avoid problems with different - developers having different settings, and should allow the tools - to run in most cases.""" - - if not version and not SCons.Util.can_read_reg: - version = '6.0' - - try: - if not version: - version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version - except KeyboardInterrupt: - raise - except: - pass - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - return _get_msvc8_default_paths(env, version, suite, use_mfc_dirs) - elif version_num >= 7.0: - return _get_msvc7_default_paths(env, version, use_mfc_dirs) - else: - return _get_msvc6_default_paths(version, use_mfc_dirs) - -def validate_vars(env): - """Validate the PCH and PCHSTOP construction variables.""" - if env.has_key('PCH') and env['PCH']: - if not env.has_key('PCHSTOP'): - raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined." - if not SCons.Util.is_String(env['PCHSTOP']): - raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP'] - -def pch_emitter(target, source, env): - """Adds the object file target.""" - - validate_vars(env) - - pch = None - obj = None - - for t in target: - if SCons.Util.splitext(str(t))[1] == '.pch': - pch = t - if SCons.Util.splitext(str(t))[1] == '.obj': - obj = t - - if not obj: - obj = SCons.Util.splitext(str(pch))[0]+'.obj' - - target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work - - return (target, source) - -def object_emitter(target, source, env, parent_emitter): - """Sets up the PCH dependencies for an object file.""" - - validate_vars(env) - - parent_emitter(target, source, env) - - if env.has_key('PCH') and env['PCH']: - env.Depends(target, env['PCH']) - - return (target, source) - -def static_object_emitter(target, source, env): - return object_emitter(target, source, env, - SCons.Defaults.StaticObjectEmitter) - -def shared_object_emitter(target, source, env): - return object_emitter(target, source, env, - SCons.Defaults.SharedObjectEmitter) - -pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') -pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', - emitter=pch_emitter, - source_scanner=SCons.Tool.SourceFileScanner) - - -# Logic to build .rc files into .res files (resource files) -res_scanner = SCons.Scanner.RC.RCScan() -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') -res_builder = SCons.Builder.Builder(action=res_action, - src_suffix='.rc', - suffix='.res', - src_builder=[], - source_scanner=res_scanner) - - -def generate(env): - """Add Builders and construction variables for MSVC++ to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - static_obj.add_emitter(suffix, static_object_emitter) - shared_obj.add_emitter(suffix, shared_object_emitter) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) - static_obj.add_emitter(suffix, static_object_emitter) - shared_obj.add_emitter(suffix, shared_object_emitter) - - env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) - env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}']) - env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' - env['CC'] = 'cl' - env['CCFLAGS'] = SCons.Util.CLVar('/nologo') - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC /Fo$TARGET /c $SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM' - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC /Fo$TARGET /c $SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM' - env['CXX'] = '$CC' - env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)') - env['CXXCOM'] = '$CXX /Fo$TARGET /c $SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM' - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '$SHCXX /Fo$TARGET /c $SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM' - env['CPPDEFPREFIX'] = '/D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '/I' - env['INCSUFFIX'] = '' -# env.Append(OBJEMITTER = [static_object_emitter]) -# env.Append(SHOBJEMITTER = [shared_object_emitter]) - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = 'rc' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCSUFFIXES']=['.rc','.rc2'] - env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' - env['BUILDERS']['RES'] = res_builder - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - - try: - version = SCons.Tool.msvs.get_default_visualstudio_version(env) - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num == 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - - use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0) - if env.get('MSVS_IGNORE_IDE_PATHS', 0): - _get_paths = get_msvc_default_paths - else: - _get_paths = get_msvc_paths - include_path, lib_path, exe_path = _get_paths(env, version, use_mfc_dirs) - - # since other tools can set these, we just make sure that the - # relevant stuff from MSVS is in there somewhere. - env.PrependENVPath('INCLUDE', include_path) - env.PrependENVPath('LIB', lib_path) - env.PrependENVPath('PATH', exe_path) - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass - - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cc' - - env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) - env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' - env['BUILDERS']['PCH'] = pch_builder - - if not env.has_key('ENV'): - env['ENV'] = {} - if not env['ENV'].has_key('SystemRoot'): # required for dlls in the winsxs folders - env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() - -def exists(env): - if SCons.Tool.msvs.is_msvs_installed(): - # there's at least one version of MSVS installed. - return 1 - else: - return env.Detect('cl') - diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/msvs.py b/tools/scons/scons-local-1.2.0/SCons/Tool/msvs.py deleted file mode 100644 index f8a20ea1f0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/msvs.py +++ /dev/null @@ -1,1815 +0,0 @@ -"""SCons.Tool.msvs - -Tool-specific initialization for Microsoft Visual Studio project files. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/msvs.py 3842 2008/12/20 22:59:52 scons" - -import base64 -import hashlib -import os.path -import pickle -import re -import string -import sys - -import SCons.Builder -import SCons.Node.FS -import SCons.Platform.win32 -import SCons.Script.SConscript -import SCons.Util -import SCons.Warnings - -############################################################################## -# Below here are the classes and functions for generation of -# DSP/DSW/SLN/VCPROJ files. -############################################################################## - -def _hexdigest(s): - """Return a string as a string of hex characters. - """ - # NOTE: This routine is a method in the Python 2.0 interface - # of the native md5 module, but we want SCons to operate all - # the way back to at least Python 1.5.2, which doesn't have it. - h = string.hexdigits - r = '' - for c in s: - i = ord(c) - r = r + h[(i >> 4) & 0xF] + h[i & 0xF] - return r - -def xmlify(s): - s = string.replace(s, "&", "&") # do this first - s = string.replace(s, "'", "'") - s = string.replace(s, '"', """) - return s - -external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' - -def _generateGUID(slnfile, name): - """This generates a dummy GUID for the sln file to use. It is - based on the MD5 signatures of the sln filename plus the name of - the project. It basically just needs to be unique, and not - change with each invocation.""" - m = hashlib.md5() - m.update(str(slnfile) + str(name)) - # TODO(1.5) - #solution = m.hexdigest().upper() - solution = string.upper(_hexdigest(m.digest())) - # convert most of the signature to GUID form (discard the rest) - solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" - return solution - -version_re = re.compile(r'(\d+\.\d+)(.*)') - -def msvs_parse_version(s): - """ - Split a Visual Studio version, which may in fact be something like - '7.0Exp', into is version number (returned as a float) and trailing - "suite" portion. - """ - num, suite = version_re.match(s).groups() - return float(num), suite - -# This is how we re-invoke SCons from inside MSVS Project files. -# The problem is that we might have been invoked as either scons.bat -# or scons.py. If we were invoked directly as scons.py, then we could -# use sys.argv[0] to find the SCons "executable," but that doesn't work -# if we were invoked as scons.bat, which uses "python -c" to execute -# things and ends up with "-c" as sys.argv[0]. Consequently, we have -# the MSVS Project file invoke SCons the same way that scons.bat does, -# which works regardless of how we were invoked. -def getExecScriptMain(env, xml=None): - scons_home = env.get('SCONS_HOME') - if not scons_home and os.environ.has_key('SCONS_LIB_DIR'): - scons_home = os.environ['SCONS_LIB_DIR'] - if scons_home: - exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home - else: - version = SCons.__version__ - exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() - if xml: - exec_script_main = xmlify(exec_script_main) - return exec_script_main - -# The string for the Python executable we tell the Project file to use -# is either sys.executable or, if an external PYTHON_ROOT environment -# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to -# pluck the actual executable name from sys.executable). -try: - python_root = os.environ['PYTHON_ROOT'] -except KeyError: - python_executable = sys.executable -else: - python_executable = os.path.join('$$(PYTHON_ROOT)', - os.path.split(sys.executable)[1]) - -class Config: - pass - -def splitFully(path): - dir, base = os.path.split(path) - if dir and dir != '' and dir != path: - return splitFully(dir)+[base] - if base == '': - return [] - return [base] - -def makeHierarchy(sources): - '''Break a list of files into a hierarchy; for each value, if it is a string, - then it is a file. If it is a dictionary, it is a folder. The string is - the original path of the file.''' - - hierarchy = {} - for file in sources: - path = splitFully(file) - if len(path): - dict = hierarchy - for part in path[:-1]: - if not dict.has_key(part): - dict[part] = {} - dict = dict[part] - dict[path[-1]] = file - #else: - # print 'Warning: failed to decompose path for '+str(file) - return hierarchy - -class _DSPGenerator: - """ Base class for DSP generators """ - - srcargs = [ - 'srcs', - 'incs', - 'localincs', - 'resources', - 'misc'] - - def __init__(self, dspfile, source, env): - self.dspfile = str(dspfile) - try: - get_abspath = dspfile.get_abspath - except AttributeError: - self.dspabs = os.path.abspath(dspfile) - else: - self.dspabs = get_abspath() - - if not env.has_key('variant'): - raise SCons.Errors.InternalError, \ - "You must specify a 'variant' argument (i.e. 'Debug' or " +\ - "'Release') to create an MSVSProject." - elif SCons.Util.is_String(env['variant']): - variants = [env['variant']] - elif SCons.Util.is_List(env['variant']): - variants = env['variant'] - - if not env.has_key('buildtarget') or env['buildtarget'] == None: - buildtarget = [''] - elif SCons.Util.is_String(env['buildtarget']): - buildtarget = [env['buildtarget']] - elif SCons.Util.is_List(env['buildtarget']): - if len(env['buildtarget']) != len(variants): - raise SCons.Errors.InternalError, \ - "Sizes of 'buildtarget' and 'variant' lists must be the same." - buildtarget = [] - for bt in env['buildtarget']: - if SCons.Util.is_String(bt): - buildtarget.append(bt) - else: - buildtarget.append(bt.get_abspath()) - else: - buildtarget = [env['buildtarget'].get_abspath()] - if len(buildtarget) == 1: - bt = buildtarget[0] - buildtarget = [] - for _ in variants: - buildtarget.append(bt) - - if not env.has_key('outdir') or env['outdir'] == None: - outdir = [''] - elif SCons.Util.is_String(env['outdir']): - outdir = [env['outdir']] - elif SCons.Util.is_List(env['outdir']): - if len(env['outdir']) != len(variants): - raise SCons.Errors.InternalError, \ - "Sizes of 'outdir' and 'variant' lists must be the same." - outdir = [] - for s in env['outdir']: - if SCons.Util.is_String(s): - outdir.append(s) - else: - outdir.append(s.get_abspath()) - else: - outdir = [env['outdir'].get_abspath()] - if len(outdir) == 1: - s = outdir[0] - outdir = [] - for v in variants: - outdir.append(s) - - if not env.has_key('runfile') or env['runfile'] == None: - runfile = buildtarget[-1:] - elif SCons.Util.is_String(env['runfile']): - runfile = [env['runfile']] - elif SCons.Util.is_List(env['runfile']): - if len(env['runfile']) != len(variants): - raise SCons.Errors.InternalError, \ - "Sizes of 'runfile' and 'variant' lists must be the same." - runfile = [] - for s in env['runfile']: - if SCons.Util.is_String(s): - runfile.append(s) - else: - runfile.append(s.get_abspath()) - else: - runfile = [env['runfile'].get_abspath()] - if len(runfile) == 1: - s = runfile[0] - runfile = [] - for v in variants: - runfile.append(s) - - self.sconscript = env['MSVSSCONSCRIPT'] - - cmdargs = env.get('cmdargs', '') - - self.env = env - - if self.env.has_key('name'): - self.name = self.env['name'] - else: - self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) - self.name = self.env.subst(self.name) - - sourcenames = [ - 'Source Files', - 'Header Files', - 'Local Headers', - 'Resource Files', - 'Other Files'] - - self.sources = {} - for n in sourcenames: - self.sources[n] = [] - - self.configs = {} - - self.nokeep = 0 - if env.has_key('nokeep') and env['variant'] != 0: - self.nokeep = 1 - - if self.nokeep == 0 and os.path.exists(self.dspabs): - self.Parse() - - for t in zip(sourcenames,self.srcargs): - if self.env.has_key(t[1]): - if SCons.Util.is_List(self.env[t[1]]): - for i in self.env[t[1]]: - if not i in self.sources[t[0]]: - self.sources[t[0]].append(i) - else: - if not self.env[t[1]] in self.sources[t[0]]: - self.sources[t[0]].append(self.env[t[1]]) - - for n in sourcenames: - # TODO(1.5): - #self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower())) - self.sources[n].sort(lambda a, b: cmp(string.lower(a), string.lower(b))) - - def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): - config = Config() - config.buildtarget = buildtarget - config.outdir = outdir - config.cmdargs = cmdargs - config.runfile = runfile - - match = re.match('(.*)\|(.*)', variant) - if match: - config.variant = match.group(1) - config.platform = match.group(2) - else: - config.variant = variant - config.platform = 'Win32' - - self.configs[variant] = config - print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" - - for i in range(len(variants)): - AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) - - self.platforms = [] - for key in self.configs.keys(): - platform = self.configs[key].platform - if not platform in self.platforms: - self.platforms.append(platform) - - def Build(self): - pass - -V6DSPHeader = """\ -# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=%(name)s - Win32 %(confkey)s -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "%(name)s.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -""" - -class _GenerateV6DSP(_DSPGenerator): - """Generates a Project file for MSVS 6.0""" - - def PrintHeader(self): - # pick a default config - confkeys = self.configs.keys() - confkeys.sort() - - name = self.name - confkey = confkeys[0] - - self.file.write(V6DSPHeader % locals()) - - for kind in confkeys: - self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) - - self.file.write('!MESSAGE \n\n') - - def PrintProject(self): - name = self.name - self.file.write('# Begin Project\n' - '# PROP AllowPerConfigDependencies 0\n' - '# PROP Scc_ProjName ""\n' - '# PROP Scc_LocalPath ""\n\n') - - first = 1 - confkeys = self.configs.keys() - confkeys.sort() - for kind in confkeys: - outdir = self.configs[kind].outdir - buildtarget = self.configs[kind].buildtarget - if first == 1: - self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) - first = 0 - else: - self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) - - env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET') - if not env_has_buildtarget: - self.env['MSVSBUILDTARGET'] = buildtarget - - # have to write this twice, once with the BASE settings, and once without - for base in ("BASE ",""): - self.file.write('# PROP %sUse_MFC 0\n' - '# PROP %sUse_Debug_Libraries ' % (base, base)) - # TODO(1.5): - #if kind.lower().find('debug') < 0: - if string.find(string.lower(kind), 'debug') < 0: - self.file.write('0\n') - else: - self.file.write('1\n') - self.file.write('# PROP %sOutput_Dir "%s"\n' - '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) - cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) - self.file.write('# PROP %sCmd_Line "%s"\n' - '# PROP %sRebuild_Opt "-c && %s"\n' - '# PROP %sTarget_File "%s"\n' - '# PROP %sBsc_Name ""\n' - '# PROP %sTarget_Dir ""\n'\ - %(base,cmd,base,cmd,base,buildtarget,base,base)) - - if not env_has_buildtarget: - del self.env['MSVSBUILDTARGET'] - - self.file.write('\n!ENDIF\n\n' - '# Begin Target\n\n') - for kind in confkeys: - self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) - self.file.write('\n') - first = 0 - for kind in confkeys: - if first == 0: - self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) - first = 1 - else: - self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) - self.file.write('!ENDIF \n\n') - self.PrintSourceFiles() - self.file.write('# End Target\n' - '# End Project\n') - - if self.nokeep == 0: - # now we pickle some data and add it to the file -- MSDEV will ignore it. - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - pdata = pickle.dumps(self.sources,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - - def PrintSourceFiles(self): - categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', - 'Header Files': 'h|hpp|hxx|hm|inl', - 'Local Headers': 'h|hpp|hxx|hm|inl', - 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', - 'Other Files': ''} - - cats = categories.keys() - # TODO(1.5): - #cats.sort(lambda a, b: cmp(a.lower(), b.lower())) - cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b))) - for kind in cats: - if not self.sources[kind]: - continue # skip empty groups - - self.file.write('# Begin Group "' + kind + '"\n\n') - # TODO(1.5) - #typelist = categories[kind].replace('|', ';') - typelist = string.replace(categories[kind], '|', ';') - self.file.write('# PROP Default_Filter "' + typelist + '"\n') - - for file in self.sources[kind]: - file = os.path.normpath(file) - self.file.write('# Begin Source File\n\n' - 'SOURCE="' + file + '"\n' - '# End Source File\n') - self.file.write('# End Group\n') - - # add the SConscript file outside of the groups - self.file.write('# Begin Source File\n\n' - 'SOURCE="' + str(self.sconscript) + '"\n' - '# End Source File\n') - - def Parse(self): - try: - dspfile = open(self.dspabs,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dspfile.readline() - while line: - # TODO(1.5): - #if line.find("# End Project") > -1: - if string.find(line, "# End Project") > -1: - break - line = dspfile.readline() - - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.configs.update(data) - - data = None - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - # it has a "# " in front of it, so we strip that. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.sources.update(data) - - def Build(self): - try: - self.file = open(self.dspabs,'w') - except IOError, detail: - raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail) - else: - self.PrintHeader() - self.PrintProject() - self.file.close() - -V7DSPHeader = """\ -<?xml version="1.0" encoding = "%(encoding)s"?> -<VisualStudioProject -\tProjectType="Visual C++" -\tVersion="%(versionstr)s" -\tName="%(name)s" -%(scc_attrs)s -\tKeyword="MakeFileProj"> -""" - -V7DSPConfiguration = """\ -\t\t<Configuration -\t\t\tName="%(variant)s|%(platform)s" -\t\t\tOutputDirectory="%(outdir)s" -\t\t\tIntermediateDirectory="%(outdir)s" -\t\t\tConfigurationType="0" -\t\t\tUseOfMFC="0" -\t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE"> -\t\t\t<Tool -\t\t\t\tName="VCNMakeTool" -\t\t\t\tBuildCommandLine="%(buildcmd)s" -\t\t\t\tCleanCommandLine="%(cleancmd)s" -\t\t\t\tRebuildCommandLine="%(rebuildcmd)s" -\t\t\t\tOutput="%(runfile)s"/> -\t\t</Configuration> -""" - -V8DSPHeader = """\ -<?xml version="1.0" encoding="%(encoding)s"?> -<VisualStudioProject -\tProjectType="Visual C++" -\tVersion="%(versionstr)s" -\tName="%(name)s" -%(scc_attrs)s -\tRootNamespace="%(name)s" -\tKeyword="MakeFileProj"> -""" - -V8DSPConfiguration = """\ -\t\t<Configuration -\t\t\tName="%(variant)s|Win32" -\t\t\tConfigurationType="0" -\t\t\tUseOfMFC="0" -\t\t\tATLMinimizesCRunTimeLibraryUsage="false" -\t\t\t> -\t\t\t<Tool -\t\t\t\tName="VCNMakeTool" -\t\t\t\tBuildCommandLine="%(buildcmd)s" -\t\t\t\tReBuildCommandLine="%(rebuildcmd)s" -\t\t\t\tCleanCommandLine="%(cleancmd)s" -\t\t\t\tOutput="%(runfile)s" -\t\t\t\tPreprocessorDefinitions="" -\t\t\t\tIncludeSearchPath="" -\t\t\t\tForcedIncludes="" -\t\t\t\tAssemblySearchPath="" -\t\t\t\tForcedUsingAssemblies="" -\t\t\t\tCompileAsManaged="" -\t\t\t/> -\t\t</Configuration> -""" -class _GenerateV7DSP(_DSPGenerator): - """Generates a Project file for MSVS .NET""" - - def __init__(self, dspfile, source, env): - _DSPGenerator.__init__(self, dspfile, source, env) - self.version = env['MSVS_VERSION'] - self.version_num, self.suite = msvs_parse_version(self.version) - if self.version_num >= 8.0: - self.versionstr = '8.00' - self.dspheader = V8DSPHeader - self.dspconfiguration = V8DSPConfiguration - else: - if self.version_num >= 7.1: - self.versionstr = '7.10' - else: - self.versionstr = '7.00' - self.dspheader = V7DSPHeader - self.dspconfiguration = V7DSPConfiguration - self.file = None - - def PrintHeader(self): - env = self.env - versionstr = self.versionstr - name = self.name - encoding = self.env.subst('$MSVSENCODING') - scc_provider = env.get('MSVS_SCC_PROVIDER', '') - scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') - scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') - scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '') - project_guid = env.get('MSVS_PROJECT_GUID', '') - if self.version_num >= 8.0 and not project_guid: - project_guid = _generateGUID(self.dspfile, '') - if scc_provider != '': - scc_attrs = ('\tProjectGUID="%s"\n' - '\tSccProjectName="%s"\n' - '\tSccAuxPath="%s"\n' - '\tSccLocalPath="%s"\n' - '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider)) - else: - scc_attrs = ('\tProjectGUID="%s"\n' - '\tSccProjectName="%s"\n' - '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path)) - - self.file.write(self.dspheader % locals()) - - self.file.write('\t<Platforms>\n') - for platform in self.platforms: - self.file.write( - '\t\t<Platform\n' - '\t\t\tName="%s"/>\n' % platform) - self.file.write('\t</Platforms>\n') - - if self.version_num >= 8.0: - self.file.write('\t<ToolFiles>\n' - '\t</ToolFiles>\n') - - def PrintProject(self): - self.file.write('\t<Configurations>\n') - - confkeys = self.configs.keys() - confkeys.sort() - for kind in confkeys: - variant = self.configs[kind].variant - platform = self.configs[kind].platform - outdir = self.configs[kind].outdir - buildtarget = self.configs[kind].buildtarget - runfile = self.configs[kind].runfile - cmdargs = self.configs[kind].cmdargs - - env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET') - if not env_has_buildtarget: - self.env['MSVSBUILDTARGET'] = buildtarget - - starting = 'echo Starting SCons && ' - if cmdargs: - cmdargs = ' ' + cmdargs - else: - cmdargs = '' - buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) - rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) - cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) - - if not env_has_buildtarget: - del self.env['MSVSBUILDTARGET'] - - self.file.write(self.dspconfiguration % locals()) - - self.file.write('\t</Configurations>\n') - - if self.version_num >= 7.1: - self.file.write('\t<References>\n' - '\t</References>\n') - - self.PrintSourceFiles() - - self.file.write('</VisualStudioProject>\n') - - if self.nokeep == 0: - # now we pickle some data and add it to the file -- MSDEV will ignore it. - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write('<!-- SCons Data:\n' + pdata + '\n') - pdata = pickle.dumps(self.sources,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '-->\n') - - def printSources(self, hierarchy, commonprefix): - sorteditems = hierarchy.items() - # TODO(1.5): - #sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower())) - sorteditems.sort(lambda a, b: cmp(string.lower(a[0]), string.lower(b[0]))) - - # First folders, then files - for key, value in sorteditems: - if SCons.Util.is_Dict(value): - self.file.write('\t\t\t<Filter\n' - '\t\t\t\tName="%s"\n' - '\t\t\t\tFilter="">\n' % (key)) - self.printSources(value, commonprefix) - self.file.write('\t\t\t</Filter>\n') - - for key, value in sorteditems: - if SCons.Util.is_String(value): - file = value - if commonprefix: - file = os.path.join(commonprefix, value) - file = os.path.normpath(file) - self.file.write('\t\t\t<File\n' - '\t\t\t\tRelativePath="%s">\n' - '\t\t\t</File>\n' % (file)) - - def PrintSourceFiles(self): - categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', - 'Header Files': 'h;hpp;hxx;hm;inl', - 'Local Headers': 'h;hpp;hxx;hm;inl', - 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', - 'Other Files': ''} - - self.file.write('\t<Files>\n') - - cats = categories.keys() - # TODO(1.5) - #cats.sort(lambda a, b: cmp(a.lower(), b.lower())) - cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b))) - cats = filter(lambda k, s=self: s.sources[k], cats) - for kind in cats: - if len(cats) > 1: - self.file.write('\t\t<Filter\n' - '\t\t\tName="%s"\n' - '\t\t\tFilter="%s">\n' % (kind, categories[kind])) - - sources = self.sources[kind] - - # First remove any common prefix - commonprefix = None - if len(sources) > 1: - s = map(os.path.normpath, sources) - # take the dirname because the prefix may include parts - # of the filenames (e.g. if you have 'dir\abcd' and - # 'dir\acde' then the cp will be 'dir\a' ) - cp = os.path.dirname( os.path.commonprefix(s) ) - if cp and s[0][len(cp)] == os.sep: - # +1 because the filename starts after the separator - sources = map(lambda s, l=len(cp)+1: s[l:], sources) - commonprefix = cp - elif len(sources) == 1: - commonprefix = os.path.dirname( sources[0] ) - sources[0] = os.path.basename( sources[0] ) - - hierarchy = makeHierarchy(sources) - self.printSources(hierarchy, commonprefix=commonprefix) - - if len(cats)>1: - self.file.write('\t\t</Filter>\n') - - # add the SConscript file outside of the groups - self.file.write('\t\t<File\n' - '\t\t\tRelativePath="%s">\n' - '\t\t</File>\n' % str(self.sconscript)) - - self.file.write('\t</Files>\n' - '\t<Globals>\n' - '\t</Globals>\n') - - def Parse(self): - try: - dspfile = open(self.dspabs,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dspfile.readline() - while line: - # TODO(1.5) - #if line.find('<!-- SCons Data:') > -1: - if string.find(line, '<!-- SCons Data:') > -1: - break - line = dspfile.readline() - - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.configs.update(data) - - data = None - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.sources.update(data) - - def Build(self): - try: - self.file = open(self.dspabs,'w') - except IOError, detail: - raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail) - else: - self.PrintHeader() - self.PrintProject() - self.file.close() - -class _DSWGenerator: - """ Base class for DSW generators """ - def __init__(self, dswfile, source, env): - self.dswfile = os.path.normpath(str(dswfile)) - self.env = env - - if not env.has_key('projects'): - raise SCons.Errors.UserError, \ - "You must specify a 'projects' argument to create an MSVSSolution." - projects = env['projects'] - if not SCons.Util.is_List(projects): - raise SCons.Errors.InternalError, \ - "The 'projects' argument must be a list of nodes." - projects = SCons.Util.flatten(projects) - if len(projects) < 1: - raise SCons.Errors.UserError, \ - "You must specify at least one project to create an MSVSSolution." - self.dspfiles = map(str, projects) - - if self.env.has_key('name'): - self.name = self.env['name'] - else: - self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0]) - self.name = self.env.subst(self.name) - - def Build(self): - pass - -class _GenerateV7DSW(_DSWGenerator): - """Generates a Solution file for MSVS .NET""" - def __init__(self, dswfile, source, env): - _DSWGenerator.__init__(self, dswfile, source, env) - - self.file = None - self.version = self.env['MSVS_VERSION'] - self.version_num, self.suite = msvs_parse_version(self.version) - self.versionstr = '7.00' - if self.version_num >= 8.0: - self.versionstr = '9.00' - elif self.version_num >= 7.1: - self.versionstr = '8.00' - if self.version_num >= 8.0: - self.versionstr = '9.00' - - if env.has_key('slnguid') and env['slnguid']: - self.slnguid = env['slnguid'] - else: - self.slnguid = _generateGUID(dswfile, self.name) - - self.configs = {} - - self.nokeep = 0 - if env.has_key('nokeep') and env['variant'] != 0: - self.nokeep = 1 - - if self.nokeep == 0 and os.path.exists(self.dswfile): - self.Parse() - - def AddConfig(self, variant, dswfile=dswfile): - config = Config() - - match = re.match('(.*)\|(.*)', variant) - if match: - config.variant = match.group(1) - config.platform = match.group(2) - else: - config.variant = variant - config.platform = 'Win32' - - self.configs[variant] = config - print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'" - - if not env.has_key('variant'): - raise SCons.Errors.InternalError, \ - "You must specify a 'variant' argument (i.e. 'Debug' or " +\ - "'Release') to create an MSVS Solution File." - elif SCons.Util.is_String(env['variant']): - AddConfig(self, env['variant']) - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - AddConfig(self, variant) - - self.platforms = [] - for key in self.configs.keys(): - platform = self.configs[key].platform - if not platform in self.platforms: - self.platforms.append(platform) - - def Parse(self): - try: - dswfile = open(self.dswfile,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dswfile.readline() - while line: - if line[:9] == "EndGlobal": - break - line = dswfile.readline() - - line = dswfile.readline() - datas = line - while line: - line = dswfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.configs.update(data) - - def PrintSolution(self): - """Writes a solution file""" - self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr ) - if self.version_num >= 8.0: - self.file.write('# Visual Studio 2005\n') - for p in self.dspfiles: - name = os.path.basename(p) - base, suffix = SCons.Util.splitext(name) - if suffix == '.vcproj': - name = base - guid = _generateGUID(p, '') - self.file.write('Project("%s") = "%s", "%s", "%s"\n' - % ( external_makefile_guid, name, p, guid ) ) - if self.version_num >= 7.1 and self.version_num < 8.0: - self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' - '\tEndProjectSection\n') - self.file.write('EndProject\n') - - self.file.write('Global\n') - - env = self.env - if env.has_key('MSVS_SCC_PROVIDER'): - dspfile_base = os.path.basename(self.dspfile) - slnguid = self.slnguid - scc_provider = env.get('MSVS_SCC_PROVIDER', '') - scc_provider = string.replace(scc_provider, ' ', r'\u0020') - scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') - # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') - scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '') - scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '') - # project_guid = env.get('MSVS_PROJECT_GUID', '') - - self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n' - '\t\tSccNumberOfProjects = 2\n' - '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n' - '\t\tSccLocalPath0 = %(scc_local_path)s\n' - '\t\tCanCheckoutShared = true\n' - '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n' - '\t\tSccProjectName1 = %(scc_project_name)s\n' - '\t\tSccLocalPath1 = %(scc_local_path)s\n' - '\t\tSccProvider1 = %(scc_provider)s\n' - '\t\tCanCheckoutShared = true\n' - '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n' - '\t\tSolutionUniqueID = %(slnguid)s\n' - '\tEndGlobalSection\n' % locals()) - - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') - else: - self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n') - - confkeys = self.configs.keys() - confkeys.sort() - cnt = 0 - for name in confkeys: - variant = self.configs[name].variant - platform = self.configs[name].platform - if self.version_num >= 8.0: - self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform)) - else: - self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant)) - cnt = cnt + 1 - self.file.write('\tEndGlobalSection\n') - if self.version_num < 7.1: - self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n' - '\tEndGlobalSection\n') - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') - else: - self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') - - for name in confkeys: - variant = self.configs[name].variant - platform = self.configs[name].platform - if self.version_num >= 8.0: - for p in self.dspfiles: - guid = _generateGUID(p, '') - self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) - else: - for p in self.dspfiles: - guid = _generateGUID(p, '') - self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) - - self.file.write('\tEndGlobalSection\n') - - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n' - '\t\tHideSolutionNode = FALSE\n' - '\tEndGlobalSection\n') - else: - self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n' - '\tEndGlobalSection\n' - '\tGlobalSection(ExtensibilityAddIns) = postSolution\n' - '\tEndGlobalSection\n') - self.file.write('EndGlobal\n') - if self.nokeep == 0: - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - - def Build(self): - try: - self.file = open(self.dswfile,'w') - except IOError, detail: - raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail) - else: - self.PrintSolution() - self.file.close() - -V6DSWHeader = """\ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "%(name)s"="%(dspfile)s" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### -""" - -class _GenerateV6DSW(_DSWGenerator): - """Generates a Workspace file for MSVS 6.0""" - - def PrintWorkspace(self): - """ writes a DSW file """ - name = self.name - dspfile = self.dspfiles[0] - self.file.write(V6DSWHeader % locals()) - - def Build(self): - try: - self.file = open(self.dswfile,'w') - except IOError, detail: - raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail) - else: - self.PrintWorkspace() - self.file.close() - - -def GenerateDSP(dspfile, source, env): - """Generates a Project file based on the version of MSVS that is being used""" - - version_num = 6.0 - if env.has_key('MSVS_VERSION'): - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 7.0: - g = _GenerateV7DSP(dspfile, source, env) - g.Build() - else: - g = _GenerateV6DSP(dspfile, source, env) - g.Build() - -def GenerateDSW(dswfile, source, env): - """Generates a Solution/Workspace file based on the version of MSVS that is being used""" - - version_num = 6.0 - if env.has_key('MSVS_VERSION'): - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 7.0: - g = _GenerateV7DSW(dswfile, source, env) - g.Build() - else: - g = _GenerateV6DSW(dswfile, source, env) - g.Build() - - -############################################################################## -# Above here are the classes and functions for generation of -# DSP/DSW/SLN/VCPROJ files. -############################################################################## - -def get_default_visualstudio_version(env): - """Returns the version set in the env, or the latest version - installed, if it can find it, or '6.0' if all else fails. Also - updates the environment with what it found.""" - - versions = ['6.0'] - - if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']): - v = get_visualstudio_versions() - if v: - versions = v - env['MSVS'] = {'VERSIONS' : versions} - else: - versions = env['MSVS'].get('VERSIONS', versions) - - if not env.has_key('MSVS_VERSION'): - env['MSVS_VERSION'] = versions[0] #use highest version by default - - env['MSVS']['VERSION'] = env['MSVS_VERSION'] - - return env['MSVS_VERSION'] - -def get_visualstudio_versions(): - """ - Get list of visualstudio versions from the Windows registry. - Returns a list of strings containing version numbers. An empty list - is returned if we were unable to accees the register (for example, - we couldn't import the registry-access module) or the appropriate - registry keys weren't found. - """ - - if not SCons.Util.can_read_reg: - return [] - - HLM = SCons.Util.HKEY_LOCAL_MACHINE - KEYS = { - r'Software\Microsoft\VisualStudio' : '', - r'Software\Microsoft\VCExpress' : 'Exp', - } - L = [] - for K, suite_suffix in KEYS.items(): - try: - k = SCons.Util.RegOpenKeyEx(HLM, K) - i = 0 - while 1: - try: - p = SCons.Util.RegEnumKey(k,i) - except SCons.Util.RegError: - break - i = i + 1 - if not p[0] in '123456789' or p in L: - continue - # Only add this version number if there is a valid - # registry structure (includes the "Setup" key), - # and at least some of the correct directories - # exist. Sometimes VS uninstall leaves around - # some registry/filesystem turds that we don't - # want to trip over. Also, some valid registry - # entries are MSDN entries, not MSVS ('7.1', - # notably), and we want to skip those too. - try: - SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup') - except SCons.Util.RegError: - continue - - id = [] - idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p) - # This is not always here -- it only exists if the - # user installed into a non-standard location (at - # least in VS6 it works that way -- VS7 seems to - # always write it) - try: - id = SCons.Util.RegQueryValueEx(idk, 'InstallDir') - except SCons.Util.RegError: - pass - - # If the InstallDir key doesn't exist, - # then we check the default locations. - # Note: The IDE's executable is not devenv.exe for VS8 Express. - if not id or not id[0]: - files_dir = SCons.Platform.win32.get_program_files_dir() - version_num, suite = msvs_parse_version(p) - if version_num < 7.0: - vs = r'Microsoft Visual Studio\Common\MSDev98' - elif version_num < 8.0: - vs = r'Microsoft Visual Studio .NET\Common7\IDE' - else: - vs = r'Microsoft Visual Studio 8\Common7\IDE' - id = [ os.path.join(files_dir, vs) ] - if os.path.exists(id[0]): - L.append(p + suite_suffix) - except SCons.Util.RegError: - pass - - if not L: - return [] - - # This is a hack to get around the fact that certain Visual Studio - # patches place a "6.1" version in the registry, which does not have - # any of the keys we need to find include paths, install directories, - # etc. Therefore we ignore it if it is there, since it throws all - # other logic off. - try: - L.remove("6.1") - except ValueError: - pass - - L.sort() - L.reverse() - - return L - -def get_default_visualstudio8_suite(env): - """ - Returns the Visual Studio 2005 suite identifier set in the env, or the - highest suite installed. - """ - if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']): - env['MSVS'] = {} - - if env.has_key('MSVS_SUITE'): - # TODO(1.5) - #suite = env['MSVS_SUITE'].upper() - suite = string.upper(env['MSVS_SUITE']) - suites = [suite] - else: - suite = 'EXPRESS' - suites = [suite] - if SCons.Util.can_read_reg: - suites = get_visualstudio8_suites() - if suites: - suite = suites[0] #use best suite by default - - env['MSVS_SUITE'] = suite - env['MSVS']['SUITES'] = suites - env['MSVS']['SUITE'] = suite - - return suite - -def get_visualstudio8_suites(): - """ - Returns a sorted list of all installed Visual Studio 2005 suites found - in the registry. The highest version should be the first entry in the list. - """ - - suites = [] - - # Detect Standard, Professional and Team edition - try: - idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - r'Software\Microsoft\VisualStudio\8.0') - SCons.Util.RegQueryValueEx(idk, 'InstallDir') - editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions - edition_name = 'STD' - for name, key_suffix in editions.items(): - try: - idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - r'Software\Microsoft\VisualStudio\8.0' + '\\' + key_suffix ) - edition_name = name - except SCons.Util.RegError: - pass - suites.append(edition_name) - except SCons.Util.RegError: - pass - - # Detect Express edition - try: - idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - r'Software\Microsoft\VCExpress\8.0') - SCons.Util.RegQueryValueEx(idk, 'InstallDir') - suites.append('EXPRESS') - except SCons.Util.RegError: - pass - - return suites - -def is_msvs_installed(): - """ - Check the registry for an installed visual studio. - """ - try: - v = SCons.Tool.msvs.get_visualstudio_versions() - return v - except (SCons.Util.RegError, SCons.Errors.InternalError): - return 0 - -def get_msvs_install_dirs(version = None, vs8suite = None): - """ - Get installed locations for various msvc-related products, like the .NET SDK - and the Platform SDK. - """ - - if not SCons.Util.can_read_reg: - return {} - - if not version: - versions = get_visualstudio_versions() - if versions: - version = versions[0] #use highest version by default - else: - return {} - - version_num, suite = msvs_parse_version(version) - - K = 'Software\\Microsoft\\VisualStudio\\' + str(version_num) - if (version_num >= 8.0): - if vs8suite == None: - # We've been given no guidance about which Visual Studio 8 - # suite to use, so attempt to autodetect. - suites = get_visualstudio8_suites() - if suites: - vs8suite = suites[0] - - if vs8suite == 'EXPRESS': - K = 'Software\\Microsoft\\VCExpress\\' + str(version_num) - - # vc++ install dir - rv = {} - if (version_num < 7.0): - key = K + r'\Setup\Microsoft Visual C++\ProductDir' - else: - key = K + r'\Setup\VC\ProductDir' - try: - (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, key) - except SCons.Util.RegError: - pass - - # visual studio install dir - if (version_num < 7.0): - try: - (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - K + r'\Setup\Microsoft Visual Studio\ProductDir') - except SCons.Util.RegError: - pass - - if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']: - if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']: - rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR']) - else: - rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio') - else: - try: - (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - K + r'\Setup\VS\ProductDir') - except SCons.Util.RegError: - pass - - # .NET framework install dir - try: - (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - r'Software\Microsoft\.NETFramework\InstallRoot') - except SCons.Util.RegError: - pass - - if rv.has_key('FRAMEWORKDIR'): - # try and enumerate the installed versions of the .NET framework. - contents = os.listdir(rv['FRAMEWORKDIR']) - l = re.compile('v[0-9]+.*') - installed_framework_versions = filter(lambda e, l=l: l.match(e), contents) - - def versrt(a,b): - # since version numbers aren't really floats... - aa = a[1:] - bb = b[1:] - aal = string.split(aa, '.') - bbl = string.split(bb, '.') - # sequence comparison in python is lexicographical - # which is exactly what we want. - # Note we sort backwards so the highest version is first. - return cmp(bbl,aal) - - installed_framework_versions.sort(versrt) - - rv['FRAMEWORKVERSIONS'] = installed_framework_versions - - # TODO: allow a specific framework version to be set - - # Choose a default framework version based on the Visual - # Studio version. - DefaultFrameworkVersionMap = { - '7.0' : 'v1.0', - '7.1' : 'v1.1', - '8.0' : 'v2.0', - # TODO: Does .NET 3.0 need to be worked into here somewhere? - } - try: - default_framework_version = DefaultFrameworkVersionMap[version[:3]] - except (KeyError, TypeError): - pass - else: - # Look for the first installed directory in FRAMEWORKDIR that - # begins with the framework version string that's appropriate - # for the Visual Studio version we're using. - for v in installed_framework_versions: - if v[:4] == default_framework_version: - rv['FRAMEWORKVERSION'] = v - break - - # If the framework version couldn't be worked out by the previous - # code then fall back to using the latest version of the .NET - # framework - if not rv.has_key('FRAMEWORKVERSION'): - rv['FRAMEWORKVERSION'] = installed_framework_versions[0] - - # .NET framework SDK install dir - if rv.has_key('FRAMEWORKVERSION'): - # The .NET SDK version used must match the .NET version used, - # so we deliberately don't fall back to other .NET framework SDK - # versions that might be present. - ver = rv['FRAMEWORKVERSION'][:4] - key = r'Software\Microsoft\.NETFramework\sdkInstallRoot' + ver - try: - (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - key) - except SCons.Util.RegError: - pass - - # MS Platform SDK dir - try: - (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir') - except SCons.Util.RegError: - pass - - if rv.has_key('PLATFORMSDKDIR'): - # if we have a platform SDK, try and get some info on it. - vers = {} - try: - loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs' - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc) - i = 0 - while 1: - try: - key = SCons.Util.RegEnumKey(k,i) - sdk = SCons.Util.RegOpenKeyEx(k,key) - j = 0 - name = '' - date = '' - version = '' - while 1: - try: - (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j) - # TODO(1.5): - #if vk.lower() == 'keyword': - # name = vv - #if vk.lower() == 'propagation_date': - # date = vv - #if vk.lower() == 'version': - # version = vv - if string.lower(vk) == 'keyword': - name = vv - if string.lower(vk) == 'propagation_date': - date = vv - if string.lower(vk) == 'version': - version = vv - j = j + 1 - except SCons.Util.RegError: - break - if name: - vers[name] = (date, version) - i = i + 1 - except SCons.Util.RegError: - break - rv['PLATFORMSDK_MODULES'] = vers - except SCons.Util.RegError: - pass - - return rv - -def GetMSVSProjectSuffix(target, source, env, for_signature): - return env['MSVS']['PROJECTSUFFIX'] - -def GetMSVSSolutionSuffix(target, source, env, for_signature): - return env['MSVS']['SOLUTIONSUFFIX'] - -def GenerateProject(target, source, env): - # generate the dsp file, according to the version of MSVS. - builddspfile = target[0] - dspfile = builddspfile.srcnode() - - # this detects whether or not we're using a VariantDir - if not dspfile is builddspfile: - try: - bdsp = open(str(builddspfile), "w+") - except IOError, detail: - print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' - raise - - bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) - - GenerateDSP(dspfile, source, env) - - if env.get('auto_build_solution', 1): - builddswfile = target[1] - dswfile = builddswfile.srcnode() - - if not dswfile is builddswfile: - - try: - bdsw = open(str(builddswfile), "w+") - except IOError, detail: - print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' - raise - - bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) - - GenerateDSW(dswfile, source, env) - -def GenerateSolution(target, source, env): - GenerateDSW(target[0], source, env) - -def projectEmitter(target, source, env): - """Sets up the DSP dependencies.""" - - # todo: Not sure what sets source to what user has passed as target, - # but this is what happens. When that is fixed, we also won't have - # to make the user always append env['MSVSPROJECTSUFFIX'] to target. - if source[0] == target[0]: - source = [] - - # make sure the suffix is correct for the version of MSVS we're running. - (base, suff) = SCons.Util.splitext(str(target[0])) - suff = env.subst('$MSVSPROJECTSUFFIX') - target[0] = base + suff - - if not source: - source = 'prj_inputs:' - source = source + env.subst('$MSVSSCONSCOM', 1) - source = source + env.subst('$MSVSENCODING', 1) - - if env.has_key('buildtarget') and env['buildtarget'] != None: - if SCons.Util.is_String(env['buildtarget']): - source = source + ' "%s"' % env['buildtarget'] - elif SCons.Util.is_List(env['buildtarget']): - for bt in env['buildtarget']: - if SCons.Util.is_String(bt): - source = source + ' "%s"' % bt - else: - try: source = source + ' "%s"' % bt.get_abspath() - except AttributeError: raise SCons.Errors.InternalError, \ - "buildtarget can be a string, a node, a list of strings or nodes, or None" - else: - try: source = source + ' "%s"' % env['buildtarget'].get_abspath() - except AttributeError: raise SCons.Errors.InternalError, \ - "buildtarget can be a string, a node, a list of strings or nodes, or None" - - if env.has_key('outdir') and env['outdir'] != None: - if SCons.Util.is_String(env['outdir']): - source = source + ' "%s"' % env['outdir'] - elif SCons.Util.is_List(env['outdir']): - for s in env['outdir']: - if SCons.Util.is_String(s): - source = source + ' "%s"' % s - else: - try: source = source + ' "%s"' % s.get_abspath() - except AttributeError: raise SCons.Errors.InternalError, \ - "outdir can be a string, a node, a list of strings or nodes, or None" - else: - try: source = source + ' "%s"' % env['outdir'].get_abspath() - except AttributeError: raise SCons.Errors.InternalError, \ - "outdir can be a string, a node, a list of strings or nodes, or None" - - if env.has_key('name'): - if SCons.Util.is_String(env['name']): - source = source + ' "%s"' % env['name'] - else: - raise SCons.Errors.InternalError, "name must be a string" - - if env.has_key('variant'): - if SCons.Util.is_String(env['variant']): - source = source + ' "%s"' % env['variant'] - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - if SCons.Util.is_String(variant): - source = source + ' "%s"' % variant - else: - raise SCons.Errors.InternalError, "name must be a string or a list of strings" - else: - raise SCons.Errors.InternalError, "variant must be a string or a list of strings" - else: - raise SCons.Errors.InternalError, "variant must be specified" - - for s in _DSPGenerator.srcargs: - if env.has_key(s): - if SCons.Util.is_String(env[s]): - source = source + ' "%s' % env[s] - elif SCons.Util.is_List(env[s]): - for t in env[s]: - if SCons.Util.is_String(t): - source = source + ' "%s"' % t - else: - raise SCons.Errors.InternalError, s + " must be a string or a list of strings" - else: - raise SCons.Errors.InternalError, s + " must be a string or a list of strings" - - source = source + ' "%s"' % str(target[0]) - source = [SCons.Node.Python.Value(source)] - - targetlist = [target[0]] - sourcelist = source - - if env.get('auto_build_solution', 1): - env['projects'] = targetlist - t, s = solutionEmitter(target, target, env) - targetlist = targetlist + t - - return (targetlist, sourcelist) - -def solutionEmitter(target, source, env): - """Sets up the DSW dependencies.""" - - # todo: Not sure what sets source to what user has passed as target, - # but this is what happens. When that is fixed, we also won't have - # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target. - if source[0] == target[0]: - source = [] - - # make sure the suffix is correct for the version of MSVS we're running. - (base, suff) = SCons.Util.splitext(str(target[0])) - suff = env.subst('$MSVSSOLUTIONSUFFIX') - target[0] = base + suff - - if not source: - source = 'sln_inputs:' - - if env.has_key('name'): - if SCons.Util.is_String(env['name']): - source = source + ' "%s"' % env['name'] - else: - raise SCons.Errors.InternalError, "name must be a string" - - if env.has_key('variant'): - if SCons.Util.is_String(env['variant']): - source = source + ' "%s"' % env['variant'] - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - if SCons.Util.is_String(variant): - source = source + ' "%s"' % variant - else: - raise SCons.Errors.InternalError, "name must be a string or a list of strings" - else: - raise SCons.Errors.InternalError, "variant must be a string or a list of strings" - else: - raise SCons.Errors.InternalError, "variant must be specified" - - if env.has_key('slnguid'): - if SCons.Util.is_String(env['slnguid']): - source = source + ' "%s"' % env['slnguid'] - else: - raise SCons.Errors.InternalError, "slnguid must be a string" - - if env.has_key('projects'): - if SCons.Util.is_String(env['projects']): - source = source + ' "%s"' % env['projects'] - elif SCons.Util.is_List(env['projects']): - for t in env['projects']: - if SCons.Util.is_String(t): - source = source + ' "%s"' % t - - source = source + ' "%s"' % str(target[0]) - source = [SCons.Node.Python.Value(source)] - - return ([target[0]], source) - -projectAction = SCons.Action.Action(GenerateProject, None) - -solutionAction = SCons.Action.Action(GenerateSolution, None) - -projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', - suffix = '$MSVSPROJECTSUFFIX', - emitter = projectEmitter) - -solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', - suffix = '$MSVSSOLUTIONSUFFIX', - emitter = solutionEmitter) - -default_MSVS_SConscript = None - -def generate(env): - """Add Builders and construction variables for Microsoft Visual - Studio project files to an Environment.""" - try: - env['BUILDERS']['MSVSProject'] - except KeyError: - env['BUILDERS']['MSVSProject'] = projectBuilder - - try: - env['BUILDERS']['MSVSSolution'] - except KeyError: - env['BUILDERS']['MSVSSolution'] = solutionBuilder - - env['MSVSPROJECTCOM'] = projectAction - env['MSVSSOLUTIONCOM'] = solutionAction - - if SCons.Script.call_stack: - # XXX Need to find a way to abstract this; the build engine - # shouldn't depend on anything in SCons.Script. - env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript - else: - global default_MSVS_SConscript - if default_MSVS_SConscript is None: - default_MSVS_SConscript = env.File('SConstruct') - env['MSVSSCONSCRIPT'] = default_MSVS_SConscript - - env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) - env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' - env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' - env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' - env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' - env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' - env['MSVSENCODING'] = 'Windows-1252' - - try: - version = get_default_visualstudio_version(env) - # keep a record of some of the MSVS info so the user can use it. - dirs = get_msvs_install_dirs(version) - env['MSVS'].update(dirs) - except (SCons.Util.RegError, SCons.Errors.InternalError): - # we don't care if we can't do this -- if we can't, it's - # because we don't have access to the registry, or because the - # tools aren't installed. In either case, the user will have to - # find them on their own. - pass - - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if (version_num < 7.0): - env['MSVS']['PROJECTSUFFIX'] = '.dsp' - env['MSVS']['SOLUTIONSUFFIX'] = '.dsw' - else: - env['MSVS']['PROJECTSUFFIX'] = '.vcproj' - env['MSVS']['SOLUTIONSUFFIX'] = '.sln' - - env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix - env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix - env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' - env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' - env['SCONS_HOME'] = os.environ.get('SCONS_HOME') - -def exists(env): - if not env['PLATFORM'] in ('win32', 'cygwin'): - return 0 - - try: - v = SCons.Tool.msvs.get_visualstudio_versions() - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass - - if not v: - version_num = 6.0 - if env.has_key('MSVS_VERSION'): - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 7.0: - # The executable is 'devenv' in Visual Studio Pro, - # Team System and others. Express Editions have different - # executable names. Right now we're only going to worry - # about Visual C++ 2005 Express Edition. - return env.Detect('devenv') or env.Detect('vcexpress') - else: - return env.Detect('msdev') - else: - # there's at least one version of MSVS installed. - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/mwcc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/mwcc.py deleted file mode 100644 index 4a6eaaaf1d..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/mwcc.py +++ /dev/null @@ -1,202 +0,0 @@ -"""SCons.Tool.mwcc - -Tool-specific initialization for the Metrowerks CodeWarrior compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/mwcc.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path -import string - -import SCons.Util - -def set_vars(env): - """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars - - MWCW_VERSIONS is set to a list of objects representing installed versions - - MWCW_VERSION is set to the version object that will be used for building. - MWCW_VERSION can be set to a string during Environment - construction to influence which version is chosen, otherwise - the latest one from MWCW_VERSIONS is used. - - Returns true if at least one version is found, false otherwise - """ - desired = env.get('MWCW_VERSION', '') - - # return right away if the variables are already set - if isinstance(desired, MWVersion): - return 1 - elif desired is None: - return 0 - - versions = find_versions() - version = None - - if desired: - for v in versions: - if str(v) == desired: - version = v - elif versions: - version = versions[-1] - - env['MWCW_VERSIONS'] = versions - env['MWCW_VERSION'] = version - - if version is None: - return 0 - - env.PrependENVPath('PATH', version.clpath) - env.PrependENVPath('PATH', version.dllpath) - ENV = env['ENV'] - ENV['CWFolder'] = version.path - ENV['LM_LICENSE_FILE'] = version.license - plus = lambda x: '+%s' % x - ENV['MWCIncludes'] = string.join(map(plus, version.includes), os.pathsep) - ENV['MWLibraries'] = string.join(map(plus, version.libs), os.pathsep) - return 1 - - -def find_versions(): - """Return a list of MWVersion objects representing installed versions""" - versions = [] - - ### This function finds CodeWarrior by reading from the registry on - ### Windows. Some other method needs to be implemented for other - ### platforms, maybe something that calls env.WhereIs('mwcc') - - if SCons.Util.can_read_reg: - try: - HLM = SCons.Util.HKEY_LOCAL_MACHINE - product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions' - product_key = SCons.Util.RegOpenKeyEx(HLM, product) - - i = 0 - while 1: - name = product + '\\' + SCons.Util.RegEnumKey(product_key, i) - name_key = SCons.Util.RegOpenKeyEx(HLM, name) - - try: - version = SCons.Util.RegQueryValueEx(name_key, 'VERSION') - path = SCons.Util.RegQueryValueEx(name_key, 'PATH') - mwv = MWVersion(version[0], path[0], 'Win32-X86') - versions.append(mwv) - except SCons.Util.RegError: - pass - - i = i + 1 - - except SCons.Util.RegError: - pass - - return versions - - -class MWVersion: - def __init__(self, version, path, platform): - self.version = version - self.path = path - self.platform = platform - self.clpath = os.path.join(path, 'Other Metrowerks Tools', - 'Command Line Tools') - self.dllpath = os.path.join(path, 'Bin') - - # The Metrowerks tools don't store any configuration data so they - # are totally dumb when it comes to locating standard headers, - # libraries, and other files, expecting all the information - # to be handed to them in environment variables. The members set - # below control what information scons injects into the environment - - ### The paths below give a normal build environment in CodeWarrior for - ### Windows, other versions of CodeWarrior might need different paths. - - msl = os.path.join(path, 'MSL') - support = os.path.join(path, '%s Support' % platform) - - self.license = os.path.join(path, 'license.dat') - self.includes = [msl, support] - self.libs = [msl, support] - - def __str__(self): - return self.version - - -CSuffixes = ['.c', '.C'] -CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] - - -def generate(env): - """Add Builders and construction variables for the mwcc to an Environment.""" - import SCons.Defaults - import SCons.Tool - - set_vars(env) - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) - - env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' - - env['CC'] = 'mwcc' - env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' - - env['CXX'] = 'mwcc' - env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' - - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = '$CCFLAGS' - env['SHCFLAGS'] = '$CFLAGS' - env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' - - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = '$CXXFLAGS' - env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' - - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cpp' - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - - #env['PCH'] = ? - #env['PCHSTOP'] = ? - - -def exists(env): - return set_vars(env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/mwld.py b/tools/scons/scons-local-1.2.0/SCons/Tool/mwld.py deleted file mode 100644 index 890b6d09fd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/mwld.py +++ /dev/null @@ -1,101 +0,0 @@ -"""SCons.Tool.mwld - -Tool-specific initialization for the Metrowerks CodeWarrior linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/mwld.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool - - -def generate(env): - """Add Builders and construction variables for lib to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['AR'] = 'mwld' - env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES' - - env['LIBDIRPREFIX'] = '-L' - env['LIBDIRSUFFIX'] = '' - env['LIBLINKPREFIX'] = '-l' - env['LIBLINKSUFFIX'] = '.lib' - - env['LINK'] = 'mwld' - env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = '$LINKFLAGS' - env['SHLINKCOM'] = shlib_action - env['SHLIBEMITTER']= shlib_emitter - - -def exists(env): - import SCons.Tool.mwcc - return SCons.Tool.mwcc.set_vars(env) - - -def shlib_generator(target, source, env, for_signature): - cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared'] - - no_import_lib = env.get('no_import_lib', 0) - if no_import_lib: cmd.extend('-noimplib') - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.extend(['-implib', implib.get_string(for_signature)]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - return [cmd] - - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Append an import library to the list of targets. - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX')) - - return target, source - - -shlib_action = SCons.Action.Action(shlib_generator, generator=1) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/nasm.py b/tools/scons/scons-local-1.2.0/SCons/Tool/nasm.py deleted file mode 100644 index db5c1075d4..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/nasm.py +++ /dev/null @@ -1,66 +0,0 @@ -"""SCons.Tool.nasm - -Tool-specific initialization for nasm, the famous Netwide Assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/nasm.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for nasm to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - - env['AS'] = 'nasm' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' - -def exists(env): - return env.Detect('nasm') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/__init__.py deleted file mode 100644 index f5e313cf0a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/__init__.py +++ /dev/null @@ -1,306 +0,0 @@ -"""SCons.Tool.Packaging - -SCons Packaging Tool. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/__init__.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Environment -from SCons.Variables import * -from SCons.Errors import * -from SCons.Util import is_List, make_path_relative -from SCons.Warnings import warn, Warning - -import os, imp -import SCons.Defaults - -__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] - -# -# Utility and Builder function -# -def Tag(env, target, source, *more_tags, **kw_tags): - """ Tag a file with the given arguments, just sets the accordingly named - attribute on the file object. - - TODO: FIXME - """ - if not target: - target=source - first_tag=None - else: - first_tag=source - - if first_tag: - kw_tags[first_tag[0]] = '' - - if len(kw_tags) == 0 and len(more_tags) == 0: - raise UserError, "No tags given." - - # XXX: sanity checks - for x in more_tags: - kw_tags[x] = '' - - if not SCons.Util.is_List(target): - target=[target] - else: - # hmm, sometimes the target list, is a list of a list - # make sure it is flattened prior to processing. - # TODO: perhaps some bug ?!? - target=env.Flatten(target) - - for t in target: - for (k,v) in kw_tags.items(): - # all file tags have to start with PACKAGING_, so we can later - # differentiate between "normal" object attributes and the - # packaging attributes. As the user should not be bothered with - # that, the prefix will be added here if missing. - #if not k.startswith('PACKAGING_'): - if k[:10] != 'PACKAGING_': - k='PACKAGING_'+k - setattr(t, k, v) - -def Package(env, target=None, source=None, **kw): - """ Entry point for the package tool. - """ - # check if we need to find the source files ourself - if not source: - source = env.FindInstalledFiles() - - if len(source)==0: - raise UserError, "No source for Package() given" - - # decide which types of packages shall be built. Can be defined through - # four mechanisms: command line argument, keyword argument, - # environment argument and default selection( zip or tar.gz ) in that - # order. - try: kw['PACKAGETYPE']=env['PACKAGETYPE'] - except KeyError: pass - - if not kw.get('PACKAGETYPE'): - from SCons.Script import GetOption - kw['PACKAGETYPE'] = GetOption('package_type') - - if kw['PACKAGETYPE'] == None: - if env['BUILDERS'].has_key('Tar'): - kw['PACKAGETYPE']='targz' - elif env['BUILDERS'].has_key('Zip'): - kw['PACKAGETYPE']='zip' - else: - raise UserError, "No type for Package() given" - - PACKAGETYPE=kw['PACKAGETYPE'] - if not is_List(PACKAGETYPE): - PACKAGETYPE=string.split(PACKAGETYPE, ',') - - # load the needed packagers. - def load_packager(type): - try: - file,path,desc=imp.find_module(type, __path__) - return imp.load_module(type, file, path, desc) - except ImportError, e: - raise EnvironmentError("packager %s not available: %s"%(type,str(e))) - - packagers=map(load_packager, PACKAGETYPE) - - # set up targets and the PACKAGEROOT - try: - # fill up the target list with a default target name until the PACKAGETYPE - # list is of the same size as the target list. - if not target: target = [] - - size_diff = len(PACKAGETYPE)-len(target) - default_name = "%(NAME)s-%(VERSION)s" - - if size_diff>0: - default_target = default_name%kw - target.extend( [default_target]*size_diff ) - - if not kw.has_key('PACKAGEROOT'): - kw['PACKAGEROOT'] = default_name%kw - - except KeyError, e: - raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) - - # setup the source files - source=env.arg2nodes(source, env.fs.Entry) - - # call the packager to setup the dependencies. - targets=[] - try: - for packager in packagers: - t=[target.pop(0)] - t=apply(packager.package, [env,t,source], kw) - targets.extend(t) - - assert( len(target) == 0 ) - - except KeyError, e: - raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ - % (e.args[0],packager.__name__) ) - except TypeError, e: - # this exception means that a needed argument for the packager is - # missing. As our packagers get their "tags" as named function - # arguments we need to find out which one is missing. - from inspect import getargspec - args,varargs,varkw,defaults=getargspec(packager.package) - if defaults!=None: - args=args[:-len(defaults)] # throw away arguments with default values - map(args.remove, 'env target source'.split()) - # now remove any args for which we have a value in kw. - #args=[x for x in args if not kw.has_key(x)] - args=filter(lambda x, kw=kw: not kw.has_key(x), args) - - if len(args)==0: - raise # must be a different error, so reraise - elif len(args)==1: - raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ - % (args[0],packager.__name__) ) - else: - raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\ - % (", ".join(args),packager.__name__) ) - - target=env.arg2nodes(target, env.fs.Entry) - targets.extend(env.Alias( 'package', targets )) - return targets - -# -# SCons tool initialization functions -# - -added = None - -def generate(env): - from SCons.Script import AddOption - global added - if not added: - added = 1 - AddOption('--package-type', - dest='package_type', - default=None, - type="string", - action="store", - help='The type of package to create.') - - try: - env['BUILDERS']['Package'] - env['BUILDERS']['Tag'] - except KeyError: - env['BUILDERS']['Package'] = Package - env['BUILDERS']['Tag'] = Tag - -def exists(env): - return 1 - -# XXX -def options(opts): - opts.AddVariables( - EnumVariable( 'PACKAGETYPE', - 'the type of package to create.', - None, allowed_values=map( str, __all__ ), - ignorecase=2 - ) - ) - -# -# Internal utility functions -# - -def copy_attr(f1, f2): - """ copies the special packaging file attributes from f1 to f2. - """ - #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ - # x.startswith('PACKAGING_')] - copyit = lambda x, f2=f2: not hasattr(f2, x) and x[:10] == 'PACKAGING_' - pattrs = filter(copyit, dir(f1)) - for attr in pattrs: - setattr(f2, attr, getattr(f1, attr)) -def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): - """ Uses the CopyAs builder to copy all source files to the directory given - in pkgroot. - - If honor_install_location is set and the copied source file has an - PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is - used as the new name of the source file under pkgroot. - - The source file will not be copied if it is already under the the pkgroot - directory. - - All attributes of the source file will be copied to the new file. - """ - # make sure the packageroot is a Dir object. - if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) - if not SCons.Util.is_List(source): source=[source] - - new_source = [] - for file in source: - if SCons.Util.is_String(file): file = env.File(file) - - if file.is_under(pkgroot): - new_source.append(file) - else: - if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ - honor_install_location: - new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) - else: - new_name=make_path_relative(file.get_path()) - - new_file=pkgroot.File(new_name) - new_file=env.CopyAs(new_file, file)[0] - copy_attr(file, new_file) - new_source.append(new_file) - - return (target, new_source) - -def stripinstallbuilder(target, source, env): - """ strips the install builder action from the source list and stores - the final installation location as the "PACKAGING_INSTALL_LOCATION" of - the source of the source file. This effectively removes the final installed - files from the source list while remembering the installation location. - - It also warns about files which have no install builder attached. - """ - def has_no_install_location(file): - return not (file.has_builder() and\ - hasattr(file.builder, 'name') and\ - (file.builder.name=="InstallBuilder" or\ - file.builder.name=="InstallAsBuilder")) - - if len(filter(has_no_install_location, source)): - warn(Warning, "there are files to package which have no\ - InstallBuilder attached, this might lead to irreproducible packages") - - n_source=[] - for s in source: - if has_no_install_location(s): - n_source.append(s) - else: - for ss in s.sources: - n_source.append(ss) - copy_attr(s, ss) - setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) - - return (target, n_source) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/ipk.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/ipk.py deleted file mode 100644 index 0a9c6b9da8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/ipk.py +++ /dev/null @@ -1,179 +0,0 @@ -"""SCons.Tool.Packaging.ipk -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/ipk.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Builder -import SCons.Node.FS -import os - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, - SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, - X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): - """ this function prepares the packageroot directory for packaging with the - ipkg builder. - """ - SCons.Tool.Tool('ipkg').generate(env) - - # setup the Ipkg builder - bld = env['BUILDERS']['Ipkg'] - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - - # This should be overridable from the construction environment, - # which it is by using ARCHITECTURE=. - # Guessing based on what os.uname() returns at least allows it - # to work for both i386 and x86_64 Linux systems. - archmap = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', - } - - buildarchitecture = os.uname()[4] - buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) - - if kw.has_key('ARCHITECTURE'): - buildarchitecture = kw['ARCHITECTURE'] - - # setup the kw to contain the mandatory arguments to this fucntion. - # do this before calling any builder or setup function - loc=locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # generate the specfile - specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) - - # override the default target. - if str(target[0])=="%s-%s"%(NAME, VERSION): - target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] - - # now apply the Ipkg builder - return apply(bld, [env, target, specfile], kw) - -def gen_ipk_dir(proot, source, env, kw): - # make sure the packageroot is a Dir object. - if SCons.Util.is_String(proot): proot=env.Dir(proot) - - # create the specfile builder - s_bld=SCons.Builder.Builder( - action = build_specfiles, - ) - - # create the specfile targets - spec_target=[] - control=proot.Dir('CONTROL') - spec_target.append(control.File('control')) - spec_target.append(control.File('conffiles')) - spec_target.append(control.File('postrm')) - spec_target.append(control.File('prerm')) - spec_target.append(control.File('postinst')) - spec_target.append(control.File('preinst')) - - # apply the builder to the specfile targets - apply(s_bld, [env, spec_target, source], kw) - - # the packageroot directory does now contain the specfiles. - return proot - -def build_specfiles(source, target, env): - """ filter the targets for the needed files and use the variables in env - to create the specfile. - """ - # - # At first we care for the CONTROL/control file, which is the main file for ipk. - # - # For this we need to open multiple files in random order, so we store into - # a dict so they can be easily accessed. - # - # - opened_files={} - def open_file(needle, haystack): - try: - return opened_files[needle] - except KeyError: - file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] - opened_files[needle]=open(file.abspath, 'w') - return opened_files[needle] - - control_file=open_file('control', target) - - if not env.has_key('X_IPK_DESCRIPTION'): - env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], - env['DESCRIPTION'].replace('\n', '\n ')) - - - content = """ -Package: $NAME -Version: $VERSION -Priority: $X_IPK_PRIORITY -Section: $X_IPK_SECTION -Source: $SOURCE_URL -Architecture: $ARCHITECTURE -Maintainer: $X_IPK_MAINTAINER -Depends: $X_IPK_DEPENDS -Description: $X_IPK_DESCRIPTION -""" - - control_file.write(env.subst(content)) - - # - # now handle the various other files, which purpose it is to set post-, - # pre-scripts and mark files as config files. - # - # We do so by filtering the source files for files which are marked with - # the "config" tag and afterwards we do the same for x_ipk_postrm, - # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. - # - # The first one will write the name of the file into the file - # CONTROL/configfiles, the latter add the content of the x_ipk_* variable - # into the same named file. - # - for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: - config=open_file('conffiles') - config.write(f.PACKAGING_INSTALL_LOCATION) - config.write('\n') - - for str in 'POSTRM PRERM POSTINST PREINST'.split(): - name="PACKAGING_X_IPK_%s"%str - for f in [x for x in source if name in dir(x)]: - file=open_file(name) - file.write(env[str]) - - # - # close all opened files - for f in opened_files.values(): - f.close() - - # call a user specified function - if env.has_key('CHANGE_SPECFILE'): - content += env['CHANGE_SPECFILE'](target) - - return 0 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/msi.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/msi.py deleted file mode 100644 index 4929f812ff..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/msi.py +++ /dev/null @@ -1,521 +0,0 @@ -"""SCons.Tool.packaging.msi - -The msi packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/msi.py 3842 2008/12/20 22:59:52 scons" - -import os -import SCons -from SCons.Action import Action -from SCons.Builder import Builder - -from xml.dom.minidom import * -from xml.sax.saxutils import escape - -from SCons.Tool.packaging import stripinstallbuilder - -# -# Utility functions -# -def convert_to_id(s, id_set): - """ Some parts of .wxs need an Id attribute (for example: The File and - Directory directives. The charset is limited to A-Z, a-z, digits, - underscores, periods. Each Id must begin with a letter or with a - underscore. Google for "CNDL0015" for information about this. - - Requirements: - * the string created must only contain chars from the target charset. - * the string created must have a minimal editing distance from the - original string. - * the string created must be unique for the whole .wxs file. - - Observation: - * There are 62 chars in the charset. - - Idea: - * filter out forbidden characters. Check for a collision with the help - of the id_set. Add the number of the number of the collision at the - end of the created string. Furthermore care for a correct start of - the string. - """ - charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' - if s[0] in '0123456789.': - s += '_'+s - id = filter( lambda c : c in charset, s ) - - # did we already generate an id for this file? - try: - return id_set[id][s] - except KeyError: - # no we did not so initialize with the id - if not id_set.has_key(id): id_set[id] = { s : id } - # there is a collision, generate an id which is unique by appending - # the collision number - else: id_set[id][s] = id + str(len(id_set[id])) - - return id_set[id][s] - -def is_dos_short_file_name(file): - """ examine if the given file is in the 8.3 form. - """ - fname, ext = os.path.splitext(file) - proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot - proper_fname = file.isupper() and len(fname) <= 8 - - return proper_ext and proper_fname - -def gen_dos_short_file_name(file, filename_set): - """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 - - These are no complete 8.3 dos short names. The ~ char is missing and - replaced with one character from the filename. WiX warns about such - filenames, since a collision might occur. Google for "CNDL1014" for - more information. - """ - # guard this to not confuse the generation - if is_dos_short_file_name(file): - return file - - fname, ext = os.path.splitext(file) # ext contains the dot - - # first try if it suffices to convert to upper - file = file.upper() - if is_dos_short_file_name(file): - return file - - # strip forbidden characters. - forbidden = '."/[]:;=, ' - fname = filter( lambda c : c not in forbidden, fname ) - - # check if we already generated a filename with the same number: - # thisis1.txt, thisis2.txt etc. - duplicate, num = not None, 1 - while duplicate: - shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ - str(num)) - if len(ext) >= 2: - shortname = "%s%s" % (shortname, ext[:4].upper()) - - duplicate, num = shortname in filename_set, num+1 - - assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) - filename_set.append(shortname) - return shortname - -def create_feature_dict(files): - """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a - hierarchy. This function collects the files into this hierarchy. - """ - dict = {} - - def add_to_dict( feature, file ): - if not SCons.Util.is_List( feature ): - feature = [ feature ] - - for f in feature: - if not dict.has_key( f ): - dict[ f ] = [ file ] - else: - dict[ f ].append( file ) - - for file in files: - if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): - add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) - elif hasattr( file, 'PACKAGING_DOC' ): - add_to_dict( 'PACKAGING_DOC', file ) - else: - add_to_dict( 'default', file ) - - return dict - -def generate_guids(root): - """ generates globally unique identifiers for parts of the xml which need - them. - - Component tags have a special requirement. Their UUID is only allowed to - change if the list of their contained resources has changed. This allows - for clean removal and proper updates. - - To handle this requirement, the uuid is generated with an md5 hashing the - whole subtree of a xml node. - """ - from md5 import md5 - - # specify which tags need a guid and in which attribute this should be stored. - needs_id = { 'Product' : 'Id', - 'Package' : 'Id', - 'Component' : 'Guid', - } - - # find all XMl nodes matching the key, retrieve their attribute, hash their - # subtree, convert hash to string and add as a attribute to the xml node. - for (key,value) in needs_id.items(): - node_list = root.getElementsByTagName(key) - attribute = value - for node in node_list: - hash = md5(node.toxml()).hexdigest() - hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) - node.attributes[attribute] = hash_str - - - -def string_wxsfile(target, source, env): - return "building WiX file %s"%( target[0].path ) - -def build_wxsfile(target, source, env): - """ compiles a .wxs file from the keywords given in env['msi_spec'] and - by analyzing the tree of source nodes and their tags. - """ - file = open(target[0].abspath, 'w') - - try: - # Create a document with the Wix root tag - doc = Document() - root = doc.createElement( 'Wix' ) - root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' - doc.appendChild( root ) - - filename_set = [] # this is to circumvent duplicates in the shortnames - id_set = {} # this is to circumvent duplicates in the ids - - # Create the content - build_wxsfile_header_section(root, env) - build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) - generate_guids(root) - build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) - build_wxsfile_default_gui(root) - build_license_file(target[0].get_dir(), env) - - # write the xml to a file - file.write( doc.toprettyxml() ) - - # call a user specified function - if env.has_key('CHANGE_SPECFILE'): - env['CHANGE_SPECFILE'](target, source) - - except KeyError, e: - raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) - -# -# setup function -# -def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): - """ Create the wix default target directory layout and return the innermost - directory. - - We assume that the XML tree delivered in the root argument already contains - the Product tag. - - Everything is put under the PFiles directory property defined by WiX. - After that a directory with the 'VENDOR' tag is placed and then a - directory with the name of the project and its VERSION. This leads to the - following TARGET Directory Layout: - C:\<PFiles>\<Vendor>\<Projectname-Version>\ - Example: C:\Programme\Company\Product-1.2\ - """ - doc = Document() - d1 = doc.createElement( 'Directory' ) - d1.attributes['Id'] = 'TARGETDIR' - d1.attributes['Name'] = 'SourceDir' - - d2 = doc.createElement( 'Directory' ) - d2.attributes['Id'] = 'ProgramFilesFolder' - d2.attributes['Name'] = 'PFiles' - - d3 = doc.createElement( 'Directory' ) - d3.attributes['Id'] = 'VENDOR_folder' - d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) ) - d3.attributes['LongName'] = escape( VENDOR ) - - d4 = doc.createElement( 'Directory' ) - project_folder = "%s-%s" % ( NAME, VERSION ) - d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' - d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) - d4.attributes['LongName'] = escape( project_folder ) - - d1.childNodes.append( d2 ) - d2.childNodes.append( d3 ) - d3.childNodes.append( d4 ) - - root.getElementsByTagName('Product')[0].childNodes.append( d1 ) - - return d4 - -# -# mandatory and optional file tags -# -def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): - """ builds the Component sections of the wxs file with their included files. - - Files need to be specified in 8.3 format and in the long name format, long - filenames will be converted automatically. - - Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. - """ - root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set ) - components = create_feature_dict( files ) - factory = Document() - - def get_directory( node, dir ): - """ returns the node under the given node representing the directory. - - Returns the component node if dir is None or empty. - """ - if dir == '' or not dir: - return node - - Directory = node - dir_parts = dir.split(os.path.sep) - - # to make sure that our directory ids are unique, the parent folders are - # consecutively added to upper_dir - upper_dir = '' - - # walk down the xml tree finding parts of the directory - dir_parts = filter( lambda d: d != '', dir_parts ) - for d in dir_parts[:]: - already_created = filter( lambda c: c.nodeName == 'Directory' and c.attributes['LongName'].value == escape(d), Directory.childNodes ) - - if already_created != []: - Directory = already_created[0] - dir_parts.remove(d) - upper_dir += d - else: - break - - for d in dir_parts: - nDirectory = factory.createElement( 'Directory' ) - nDirectory.attributes['LongName'] = escape( d ) - nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) - upper_dir += d - nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) - - Directory.childNodes.append( nDirectory ) - Directory = nDirectory - - return Directory - - for file in files: - drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) - filename = os.path.basename( path ) - dirname = os.path.dirname( path ) - - h = { - # tagname : default value - 'PACKAGING_X_MSI_VITAL' : 'yes', - 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), - 'PACKAGING_X_MSI_LONGNAME' : filename, - 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), - 'PACKAGING_X_MSI_SOURCE' : file.get_path(), - } - - # fill in the default tags given above. - for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: - setattr( file, k, v ) - - File = factory.createElement( 'File' ) - File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) - File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) - File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) - File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) - File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) - - # create the <Component> Tag under which this file should appear - Component = factory.createElement('Component') - Component.attributes['DiskId'] = '1' - Component.attributes['Id'] = convert_to_id( filename, id_set ) - - # hang the component node under the root node and the file node - # under the component node. - Directory = get_directory( root, dirname ) - Directory.childNodes.append( Component ) - Component.childNodes.append( File ) - -# -# additional functions -# -def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): - """ This function creates the <features> tag based on the supplied xml tree. - - This is achieved by finding all <component>s and adding them to a default target. - - It should be called after the tree has been built completly. We assume - that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. - - Furthermore a top-level with the name and VERSION of the software will be created. - - An PACKAGING_X_MSI_FEATURE can either be a string, where the feature - DESCRIPTION will be the same as its title or a Tuple, where the first - part will be its title and the second its DESCRIPTION. - """ - factory = Document() - Feature = factory.createElement('Feature') - Feature.attributes['Id'] = 'complete' - Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' - Feature.attributes['Level'] = '1' - Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) - Feature.attributes['Description'] = escape( SUMMARY ) - Feature.attributes['Display'] = 'expand' - - for (feature, files) in create_feature_dict(files).items(): - SubFeature = factory.createElement('Feature') - SubFeature.attributes['Level'] = '1' - - if SCons.Util.is_Tuple(feature): - SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) - SubFeature.attributes['Title'] = escape(feature[0]) - SubFeature.attributes['Description'] = escape(feature[1]) - else: - SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) - if feature=='default': - SubFeature.attributes['Description'] = 'Main Part' - SubFeature.attributes['Title'] = 'Main Part' - elif feature=='PACKAGING_DOC': - SubFeature.attributes['Description'] = 'Documentation' - SubFeature.attributes['Title'] = 'Documentation' - else: - SubFeature.attributes['Description'] = escape(feature) - SubFeature.attributes['Title'] = escape(feature) - - # build the componentrefs. As one of the design decision is that every - # file is also a component we walk the list of files and create a - # reference. - for f in files: - ComponentRef = factory.createElement('ComponentRef') - ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) - SubFeature.childNodes.append(ComponentRef) - - Feature.childNodes.append(SubFeature) - - root.getElementsByTagName('Product')[0].childNodes.append(Feature) - -def build_wxsfile_default_gui(root): - """ this function adds a default GUI to the wxs file - """ - factory = Document() - Product = root.getElementsByTagName('Product')[0] - - UIRef = factory.createElement('UIRef') - UIRef.attributes['Id'] = 'WixUI_Mondo' - Product.childNodes.append(UIRef) - - UIRef = factory.createElement('UIRef') - UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' - Product.childNodes.append(UIRef) - -def build_license_file(directory, spec): - """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" - in the given directory - """ - name, text = '', '' - - try: - name = spec['LICENSE'] - text = spec['X_MSI_LICENSE_TEXT'] - except KeyError: - pass # ignore this as X_MSI_LICENSE_TEXT is optional - - if name!='' or text!='': - file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) - file.write('{\\rtf') - if text!='': - file.write(text.replace('\n', '\\par ')) - else: - file.write(name+'\\par\\par') - file.write('}') - file.close() - -# -# mandatory and optional package tags -# -def build_wxsfile_header_section(root, spec): - """ Adds the xml file node which define the package meta-data. - """ - # Create the needed DOM nodes and add them at the correct position in the tree. - factory = Document() - Product = factory.createElement( 'Product' ) - Package = factory.createElement( 'Package' ) - - root.childNodes.append( Product ) - Product.childNodes.append( Package ) - - # set "mandatory" default values - if not spec.has_key('X_MSI_LANGUAGE'): - spec['X_MSI_LANGUAGE'] = '1033' # select english - - # mandatory sections, will throw a KeyError if the tag is not available - Product.attributes['Name'] = escape( spec['NAME'] ) - Product.attributes['Version'] = escape( spec['VERSION'] ) - Product.attributes['Manufacturer'] = escape( spec['VENDOR'] ) - Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) - Package.attributes['Description'] = escape( spec['SUMMARY'] ) - - # now the optional tags, for which we avoid the KeyErrror exception - if spec.has_key( 'DESCRIPTION' ): - Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) - - if spec.has_key( 'X_MSI_UPGRADE_CODE' ): - Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) - - # We hardcode the media tag as our current model cannot handle it. - Media = factory.createElement('Media') - Media.attributes['Id'] = '1' - Media.attributes['Cabinet'] = 'default.cab' - Media.attributes['EmbedCab'] = 'yes' - root.getElementsByTagName('Product')[0].childNodes.append(Media) - -# this builder is the entry-point for .wxs file compiler. -wxs_builder = Builder( - action = Action( build_wxsfile, string_wxsfile ), - ensure_suffix = '.wxs' ) - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, - DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw): - # make sure that the Wix Builder is in the environment - SCons.Tool.Tool('wix').generate(env) - - # get put the keywords for the specfile compiler. These are the arguments - # given to the package function and all optional ones stored in kw, minus - # the the source, target and env one. - loc = locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # strip the install builder from the source files - target, source = stripinstallbuilder(target, source, env) - - # put the arguments into the env and call the specfile builder. - env['msi_spec'] = kw - specfile = apply( wxs_builder, [env, target, source], kw ) - - # now call the WiX Tool with the built specfile added as a source. - msifile = env.WiX(target, specfile) - - # return the target and source tuple. - return (msifile, source+[specfile]) - diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/rpm.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/rpm.py deleted file mode 100644 index 984155989a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/rpm.py +++ /dev/null @@ -1,362 +0,0 @@ -"""SCons.Tool.Packaging.rpm - -The rpm packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/rpm.py 3842 2008/12/20 22:59:52 scons" - -import os -import string - -import SCons.Builder - -from SCons.Environment import OverrideEnvironment -from SCons.Tool.packaging import stripinstallbuilder, src_targz -from SCons.Errors import UserError - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, - PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, - **kw): - # initialize the rpm tool - SCons.Tool.Tool('rpm').generate(env) - - bld = env['BUILDERS']['Rpm'] - - # Generate a UserError whenever the target name has been set explicitly, - # since rpm does not allow for controlling it. This is detected by - # checking if the target has been set to the default by the Package() - # Environment function. - if str(target[0])!="%s-%s"%(NAME, VERSION): - raise UserError( "Setting target is not supported for rpm." ) - else: - # This should be overridable from the construction environment, - # which it is by using ARCHITECTURE=. - # Guessing based on what os.uname() returns at least allows it - # to work for both i386 and x86_64 Linux systems. - archmap = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', - } - - buildarchitecture = os.uname()[4] - buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) - - if kw.has_key('ARCHITECTURE'): - buildarchitecture = kw['ARCHITECTURE'] - - fmt = '%s-%s-%s.%s.rpm' - srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') - binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) - - target = [ srcrpm, binrpm ] - - # get the correct arguments into the kw hash - loc=locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # if no "SOURCE_URL" tag is given add a default one. - if not kw.has_key('SOURCE_URL'): - #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') - kw['SOURCE_URL']=string.replace(str(target[0])+".tar.gz", '.rpm', '') - - # mangle the source and target list for the rpmbuild - env = OverrideEnvironment(env, kw) - target, source = stripinstallbuilder(target, source, env) - target, source = addspecfile(target, source, env) - target, source = collectintargz(target, source, env) - - # now call the rpm builder to actually build the packet. - return apply(bld, [env, target, source], kw) - -def collectintargz(target, source, env): - """ Puts all source files into a tar.gz file. """ - # the rpm tool depends on a source package, until this is chagned - # this hack needs to be here that tries to pack all sources in. - sources = env.FindSourceFiles() - - # filter out the target we are building the source list for. - #sources = [s for s in sources if not (s in target)] - sources = filter(lambda s, t=target: not (s in t), sources) - - # find the .spec file for rpm and add it since it is not necessarily found - # by the FindSourceFiles function. - #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) - spec_file = lambda s: string.rfind(str(s), '.spec') != -1 - sources.extend( filter(spec_file, source) ) - - # as the source contains the url of the source package this rpm package - # is built from, we extract the target name - #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') - tarball = string.replace(str(target[0])+".tar.gz", '.rpm', '') - try: - #tarball = env['SOURCE_URL'].split('/')[-1] - tarball = string.split(env['SOURCE_URL'], '/')[-1] - except KeyError, e: - raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) - - tarball = src_targz.package(env, source=sources, target=tarball, - PACKAGEROOT=env['PACKAGEROOT'], ) - - return (target, tarball) - -def addspecfile(target, source, env): - specfile = "%s-%s" % (env['NAME'], env['VERSION']) - - bld = SCons.Builder.Builder(action = build_specfile, - suffix = '.spec', - target_factory = SCons.Node.FS.File) - - source.extend(bld(env, specfile, source)) - - return (target,source) - -def build_specfile(target, source, env): - """ Builds a RPM specfile from a dictionary with string metadata and - by analyzing a tree of nodes. - """ - file = open(target[0].abspath, 'w') - str = "" - - try: - file.write( build_specfile_header(env) ) - file.write( build_specfile_sections(env) ) - file.write( build_specfile_filesection(env, source) ) - file.close() - - # call a user specified function - if env.has_key('CHANGE_SPECFILE'): - env['CHANGE_SPECFILE'](target, source) - - except KeyError, e: - raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) - - -# -# mandatory and optional package tag section -# -def build_specfile_sections(spec): - """ Builds the sections of a rpm specfile. - """ - str = "" - - mandatory_sections = { - 'DESCRIPTION' : '\n%%description\n%s\n\n', } - - str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) - - optional_sections = { - 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', - 'CHANGELOG' : '%%changelog\n%s\n\n', - 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', - 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', - 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', - 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', - 'X_RPM_VERIFY' : '%%verify\n%s\n\n', - - # These are for internal use but could possibly be overriden - 'X_RPM_PREP' : '%%prep\n%s\n\n', - 'X_RPM_BUILD' : '%%build\n%s\n\n', - 'X_RPM_INSTALL' : '%%install\n%s\n\n', - 'X_RPM_CLEAN' : '%%clean\n%s\n\n', - } - - # Default prep, build, install and clean rules - # TODO: optimize those build steps, to not compile the project a second time - if not spec.has_key('X_RPM_PREP'): - spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' - - if not spec.has_key('X_RPM_BUILD'): - spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' - - if not spec.has_key('X_RPM_INSTALL'): - spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' - - if not spec.has_key('X_RPM_CLEAN'): - spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' - - str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) - - return str - -def build_specfile_header(spec): - """ Builds all section but the %file of a rpm specfile - """ - str = "" - - # first the mandatory sections - mandatory_header_fields = { - 'NAME' : '%%define name %s\nName: %%{name}\n', - 'VERSION' : '%%define version %s\nVersion: %%{version}\n', - 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', - 'X_RPM_GROUP' : 'Group: %s\n', - 'SUMMARY' : 'Summary: %s\n', - 'LICENSE' : 'License: %s\n', } - - str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) - - # now the optional tags - optional_header_fields = { - 'VENDOR' : 'Vendor: %s\n', - 'X_RPM_URL' : 'Url: %s\n', - 'SOURCE_URL' : 'Source: %s\n', - 'SUMMARY_' : 'Summary(%s): %s\n', - 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', - 'X_RPM_ICON' : 'Icon: %s\n', - 'X_RPM_PACKAGER' : 'Packager: %s\n', - 'X_RPM_GROUP_' : 'Group(%s): %s\n', - - 'X_RPM_REQUIRES' : 'Requires: %s\n', - 'X_RPM_PROVIDES' : 'Provides: %s\n', - 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', - 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', - - 'X_RPM_SERIAL' : 'Serial: %s\n', - 'X_RPM_EPOCH' : 'Epoch: %s\n', - 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', - 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', - 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', - 'X_RPM_PREFIX' : 'Prefix: %s\n', - 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', - - # internal use - 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } - - # fill in default values: - # Adding a BuildRequires renders the .rpm unbuildable under System, which - # are not managed by rpm, since the database to resolve this dependency is - # missing (take Gentoo as an example) -# if not s.has_key('x_rpm_BuildRequires'): -# s['x_rpm_BuildRequires'] = 'scons' - - if not spec.has_key('X_RPM_BUILDROOT'): - spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' - - str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) - return str - -# -# mandatory and optional file tags -# -def build_specfile_filesection(spec, files): - """ builds the %file section of the specfile - """ - str = '%files\n' - - if not spec.has_key('X_RPM_DEFATTR'): - spec['X_RPM_DEFATTR'] = '(-,root,root)' - - str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] - - supported_tags = { - 'PACKAGING_CONFIG' : '%%config %s', - 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', - 'PACKAGING_DOC' : '%%doc %s', - 'PACKAGING_UNIX_ATTR' : '%%attr %s', - 'PACKAGING_LANG_' : '%%lang(%s) %s', - 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', - 'PACKAGING_X_RPM_DIR' : '%%dir %s', - 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', - 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } - - for file in files: - # build the tagset - tags = {} - for k in supported_tags.keys(): - try: - tags[k]=getattr(file, k) - except AttributeError: - pass - - # compile the tagset - str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) - - str = str + ' ' - str = str + file.PACKAGING_INSTALL_LOCATION - str = str + '\n\n' - - return str - -class SimpleTagCompiler: - """ This class is a simple string substition utility: - the replacement specfication is stored in the tagset dictionary, something - like: - { "abc" : "cdef %s ", - "abc_" : "cdef %s %s" } - - the compile function gets a value dictionary, which may look like: - { "abc" : "ghij", - "abc_gh" : "ij" } - - The resulting string will be: - "cdef ghij cdef gh ij" - """ - def __init__(self, tagset, mandatory=1): - self.tagset = tagset - self.mandatory = mandatory - - def compile(self, values): - """ compiles the tagset and returns a str containing the result - """ - def is_international(tag): - #return tag.endswith('_') - return tag[-1:] == '_' - - def get_country_code(tag): - return tag[-2:] - - def strip_country_code(tag): - return tag[:-2] - - replacements = self.tagset.items() - - str = "" - #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] - domestic = filter(lambda t, i=is_international: not i(t[0]), replacements) - for key, replacement in domestic: - try: - str = str + replacement % values[key] - except KeyError, e: - if self.mandatory: - raise e - - #international = [ (k,v) for k,v in replacements if is_international(k) ] - international = filter(lambda t, i=is_international: i(t[0]), replacements) - for key, replacement in international: - try: - #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] - x = filter(lambda t,key=key,s=strip_country_code: s(t[0]) == key, values.items()) - int_values_for_key = map(lambda t,g=get_country_code: (g(t[0]),t[1]), x) - for v in int_values_for_key: - str = str + replacement % v - except KeyError, e: - if self.mandatory: - raise e - - return str - diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_tarbz2.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_tarbz2.py deleted file mode 100644 index 7dafa31ab6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_tarbz2.py +++ /dev/null @@ -1,37 +0,0 @@ -"""SCons.Tool.Packaging.tarbz2 - -The tarbz2 SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.bz2') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source, TARFLAGS='-jc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_targz.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_targz.py deleted file mode 100644 index b60ceee5da..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_targz.py +++ /dev/null @@ -1,37 +0,0 @@ -"""SCons.Tool.Packaging.targz - -The targz SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source, TARFLAGS='-zc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_zip.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_zip.py deleted file mode 100644 index d76f24dfa8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/src_zip.py +++ /dev/null @@ -1,37 +0,0 @@ -"""SCons.Tool.Packaging.zip - -The zip SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Zip'] - bld.set_suffix('.zip') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/tarbz2.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/tarbz2.py deleted file mode 100644 index 69e9737bf3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/tarbz2.py +++ /dev/null @@ -1,38 +0,0 @@ -"""SCons.Tool.Packaging.tarbz2 - -The tarbz2 SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - target, source = stripinstallbuilder(target, source, env) - return bld(env, target, source, TARFLAGS='-jc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/targz.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/targz.py deleted file mode 100644 index 37a8bfebd0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/targz.py +++ /dev/null @@ -1,38 +0,0 @@ -"""SCons.Tool.Packaging.targz - -The targz SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/targz.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - return bld(env, target, source, TARFLAGS='-zc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/zip.py b/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/zip.py deleted file mode 100644 index 3cd4dd8297..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/packaging/zip.py +++ /dev/null @@ -1,38 +0,0 @@ -"""SCons.Tool.Packaging.zip - -The zip SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/packaging/zip.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Zip'] - bld.set_suffix('.zip') - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - return bld(env, target, source) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/pdf.py b/tools/scons/scons-local-1.2.0/SCons/Tool/pdf.py deleted file mode 100644 index bf6a83eec3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/pdf.py +++ /dev/null @@ -1,72 +0,0 @@ -"""SCons.Tool.pdf - -Common PDF Builder definition for various other Tool modules that use it. -Add an explicit action to run epstopdf to convert .eps files to .pdf - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/pdf.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Builder -import SCons.Tool - -PDFBuilder = None - -EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR') - -def generate(env): - try: - env['BUILDERS']['PDF'] - except KeyError: - global PDFBuilder - if PDFBuilder is None: - PDFBuilder = SCons.Builder.Builder(action = {}, - source_scanner = SCons.Tool.PDFLaTeXScanner, - prefix = '$PDFPREFIX', - suffix = '$PDFSUFFIX', - emitter = {}, - source_ext_match = None, - single_source=True) - env['BUILDERS']['PDF'] = PDFBuilder - - env['PDFPREFIX'] = '' - env['PDFSUFFIX'] = '.pdf' - -# put the epstopdf builder in this routine so we can add it after -# the pdftex builder so that one is the default for no source suffix -def generate2(env): - bld = env['BUILDERS']['PDF'] - #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py - bld.add_action('.eps', EpsPdfAction) - - env['EPSTOPDF'] = 'epstopdf' - env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('') - env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} -o ${TARGET}' - -def exists(env): - # This only puts a skeleton Builder in place, so if someone - # references this Tool directly, it's always "available." - return 1 diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/pdflatex.py b/tools/scons/scons-local-1.2.0/SCons/Tool/pdflatex.py deleted file mode 100644 index 5ffb9cb477..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/pdflatex.py +++ /dev/null @@ -1,75 +0,0 @@ -"""SCons.Tool.pdflatex - -Tool-specific initialization for pdflatex. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/pdflatex.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Util -import SCons.Tool.pdf -import SCons.Tool.tex - -PDFLaTeXAction = None - -def PDFLaTeXAuxFunction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) - return result - -PDFLaTeXAuxAction = None - -def generate(env): - """Add Builders and construction variables for pdflatex to an Environment.""" - global PDFLaTeXAction - if PDFLaTeXAction is None: - PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR') - - global PDFLaTeXAuxAction - if PDFLaTeXAuxAction is None: - PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.ltx', PDFLaTeXAuxAction) - bld.add_action('.latex', PDFLaTeXAuxAction) - bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter) - bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter) - - env['PDFLATEX'] = 'pdflatex' - env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['PDFLATEXCOM'] = 'cd ${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 3 - -def exists(env): - return env.Detect('pdflatex') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/pdftex.py b/tools/scons/scons-local-1.2.0/SCons/Tool/pdftex.py deleted file mode 100644 index 2a5bfccd42..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/pdftex.py +++ /dev/null @@ -1,99 +0,0 @@ -"""SCons.Tool.pdftex - -Tool-specific initialization for pdftex. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/pdftex.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Util -import SCons.Tool.tex - -PDFTeXAction = None - -# This action might be needed more than once if we are dealing with -# labels and bibtex. -PDFLaTeXAction = None - -def PDFLaTeXAuxAction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) - return result - -def PDFTeXLaTeXFunction(target = None, source= None, env=None): - """A builder for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then executes the appropriate - program.""" - if SCons.Tool.tex.is_LaTeX(source): - result = PDFLaTeXAuxAction(target,source,env) - else: - result = PDFTeXAction(target,source,env) - return result - -PDFTeXLaTeXAction = None - -def generate(env): - """Add Builders and construction variables for pdftex to an Environment.""" - global PDFTeXAction - if PDFTeXAction is None: - PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR') - - global PDFLaTeXAction - if PDFLaTeXAction is None: - PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR") - - global PDFTeXLaTeXAction - if PDFTeXLaTeXAction is None: - PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.tex', PDFTeXLaTeXAction) - bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter) - - # Add the epstopdf builder after the pdftex builder - # so pdftex is the default for no source suffix - pdf.generate2(env) - - env['PDFTEX'] = 'pdftex' - env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['PDFTEXCOM'] = 'cd ${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}' - - # Duplicate from latex.py. If latex.py goes away, then this is still OK. - env['PDFLATEX'] = 'pdflatex' - env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['PDFLATEXCOM'] = 'cd ${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 3 - -def exists(env): - return env.Detect('pdftex') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/qt.py b/tools/scons/scons-local-1.2.0/SCons/Tool/qt.py deleted file mode 100644 index c4e5ca74f8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/qt.py +++ /dev/null @@ -1,330 +0,0 @@ - -"""SCons.Tool.qt - -Tool-specific initialization for Qt. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/qt.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner -import SCons.Tool -import SCons.Util - -class ToolQtWarning(SCons.Warnings.Warning): - pass - -class GeneratedMocFileNotIncluded(ToolQtWarning): - pass - -class QtdirNotFound(ToolQtWarning): - pass - -SCons.Warnings.enableWarningClass(ToolQtWarning) - -header_extensions = [".h", ".hxx", ".hpp", ".hh"] -if SCons.Util.case_sensitive_suffixes('.h', '.H'): - header_extensions.append('.H') -cplusplus = __import__('c++', globals(), locals(), []) -cxx_suffixes = cplusplus.CXXSuffixes - -def checkMocIncluded(target, source, env): - moc = target[0] - cpp = source[0] - # looks like cpp.includes is cleared before the build stage :-( - # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ - path = SCons.Defaults.CScan.path(env, moc.cwd) - includes = SCons.Defaults.CScan(cpp, env, path) - if not moc in includes: - SCons.Warnings.warn( - GeneratedMocFileNotIncluded, - "Generated moc file '%s' is not included by '%s'" % - (str(moc), str(cpp))) - -def find_file(filename, paths, node_factory): - for dir in paths: - node = node_factory(filename, dir) - if node.rexists(): - return node - return None - -class _Automoc: - """ - Callable class, which works as an emitter for Programs, SharedLibraries and - StaticLibraries. - """ - - def __init__(self, objBuilderName): - self.objBuilderName = objBuilderName - - def __call__(self, target, source, env): - """ - Smart autoscan function. Gets the list of objects for the Program - or Lib. Adds objects and builders for the special qt files. - """ - try: - if int(env.subst('$QT_AUTOSCAN')) == 0: - return target, source - except ValueError: - pass - try: - debug = int(env.subst('$QT_DEBUG')) - except ValueError: - debug = 0 - - # some shortcuts used in the scanner - splitext = SCons.Util.splitext - objBuilder = getattr(env, self.objBuilderName) - - # some regular expressions: - # Q_OBJECT detection - q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') - # cxx and c comment 'eater' - #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') - # CW: something must be wrong with the regexp. See also bug #998222 - # CURRENTLY THERE IS NO TEST CASE FOR THAT - - # The following is kind of hacky to get builders working properly (FIXME) - objBuilderEnv = objBuilder.env - objBuilder.env = env - mocBuilderEnv = env.Moc.env - env.Moc.env = env - - # make a deep copy for the result; MocH objects will be appended - out_sources = source[:] - - for obj in source: - if not obj.has_builder(): - # binary obj file provided - if debug: - print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) - continue - cpp = obj.sources[0] - if not splitext(str(cpp))[1] in cxx_suffixes: - if debug: - print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) - # c or fortran source - continue - #cpp_contents = comment.sub('', cpp.get_contents()) - cpp_contents = cpp.get_contents() - h=None - for h_ext in header_extensions: - # try to find the header file in the corresponding source - # directory - hname = splitext(cpp.name)[0] + h_ext - h = find_file(hname, (cpp.get_dir(),), env.File) - if h: - if debug: - print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) - #h_contents = comment.sub('', h.get_contents()) - h_contents = h.get_contents() - break - if not h and debug: - print "scons: qt: no header for '%s'." % (str(cpp)) - if h and q_object_search.search(h_contents): - # h file with the Q_OBJECT macro found -> add moc_cpp - moc_cpp = env.Moc(h) - moc_o = objBuilder(moc_cpp) - out_sources.append(moc_o) - #moc_cpp.target_scanner = SCons.Defaults.CScan - if debug: - print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) - if cpp and q_object_search.search(cpp_contents): - # cpp file with Q_OBJECT macro found -> add moc - # (to be included in cpp) - moc = env.Moc(cpp) - env.Ignore(moc, moc) - if debug: - print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) - #moc.source_scanner = SCons.Defaults.CScan - # restore the original env attributes (FIXME) - objBuilder.env = objBuilderEnv - env.Moc.env = mocBuilderEnv - - return (target, out_sources) - -AutomocShared = _Automoc('SharedObject') -AutomocStatic = _Automoc('StaticObject') - -def _detect(env): - """Not really safe, but fast method to detect the QT library""" - QTDIR = None - if not QTDIR: - QTDIR = env.get('QTDIR',None) - if not QTDIR: - QTDIR = os.environ.get('QTDIR',None) - if not QTDIR: - moc = env.WhereIs('moc') - if moc: - QTDIR = os.path.dirname(os.path.dirname(moc)) - SCons.Warnings.warn( - QtdirNotFound, - "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) - else: - QTDIR = None - SCons.Warnings.warn( - QtdirNotFound, - "Could not detect qt, using empty QTDIR") - return QTDIR - -def uicEmitter(target, source, env): - adjustixes = SCons.Util.adjustixes - bs = SCons.Util.splitext(str(source[0].name))[0] - bs = os.path.join(str(target[0].get_dir()),bs) - # first target (header) is automatically added by builder - if len(target) < 2: - # second target is implementation - target.append(adjustixes(bs, - env.subst('$QT_UICIMPLPREFIX'), - env.subst('$QT_UICIMPLSUFFIX'))) - if len(target) < 3: - # third target is moc file - target.append(adjustixes(bs, - env.subst('$QT_MOCHPREFIX'), - env.subst('$QT_MOCHSUFFIX'))) - return target, source - -def uicScannerFunc(node, env, path): - lookout = [] - lookout.extend(env['CPPPATH']) - lookout.append(str(node.rfile().dir)) - includes = re.findall("<include.*?>(.*?)</include>", node.get_contents()) - result = [] - for incFile in includes: - dep = env.FindFile(incFile,lookout) - if dep: - result.append(dep) - return result - -uicScanner = SCons.Scanner.Base(uicScannerFunc, - name = "UicScanner", - node_class = SCons.Node.FS.File, - node_factory = SCons.Node.FS.File, - recursive = 0) - -def generate(env): - """Add Builders and construction variables for qt to an Environment.""" - CLVar = SCons.Util.CLVar - Action = SCons.Action.Action - Builder = SCons.Builder.Builder - - env.SetDefault(QTDIR = _detect(env), - QT_BINPATH = os.path.join('$QTDIR', 'bin'), - QT_CPPPATH = os.path.join('$QTDIR', 'include'), - QT_LIBPATH = os.path.join('$QTDIR', 'lib'), - QT_MOC = os.path.join('$QT_BINPATH','moc'), - QT_UIC = os.path.join('$QT_BINPATH','uic'), - QT_LIB = 'qt', # may be set to qt-mt - - QT_AUTOSCAN = 1, # scan for moc'able sources - - # Some QT specific flags. I don't expect someone wants to - # manipulate those ... - QT_UICIMPLFLAGS = CLVar(''), - QT_UICDECLFLAGS = CLVar(''), - QT_MOCFROMHFLAGS = CLVar(''), - QT_MOCFROMCXXFLAGS = CLVar('-i'), - - # suffixes/prefixes for the headers / sources to generate - QT_UICDECLPREFIX = '', - QT_UICDECLSUFFIX = '.h', - QT_UICIMPLPREFIX = 'uic_', - QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', - QT_MOCHPREFIX = 'moc_', - QT_MOCHSUFFIX = '$CXXFILESUFFIX', - QT_MOCCXXPREFIX = '', - QT_MOCCXXSUFFIX = '.moc', - QT_UISUFFIX = '.ui', - - # Commands for the qt support ... - # command to generate header, implementation and moc-file - # from a .ui file - QT_UICCOM = [ - CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), - CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' - '-o ${TARGETS[1]} $SOURCE'), - CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], - # command to generate meta object information for a class - # declarated in a header - QT_MOCFROMHCOM = ( - '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), - # command to generate meta object information for a class - # declarated in a cpp file - QT_MOCFROMCXXCOM = [ - CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), - Action(checkMocIncluded,None)]) - - # ... and the corresponding builders - uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), - emitter=uicEmitter, - src_suffix='$QT_UISUFFIX', - suffix='$QT_UICDECLSUFFIX', - prefix='$QT_UICDECLPREFIX', - source_scanner=uicScanner) - mocBld = Builder(action={}, prefix={}, suffix={}) - for h in header_extensions: - act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') - mocBld.add_action(h, act) - mocBld.prefix[h] = '$QT_MOCHPREFIX' - mocBld.suffix[h] = '$QT_MOCHSUFFIX' - for cxx in cxx_suffixes: - act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') - mocBld.add_action(cxx, act) - mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' - mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' - - # register the builders - env['BUILDERS']['Uic'] = uicBld - env['BUILDERS']['Moc'] = mocBld - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - static_obj.add_src_builder('Uic') - shared_obj.add_src_builder('Uic') - - # We use the emitters of Program / StaticLibrary / SharedLibrary - # to scan for moc'able files - # We can't refer to the builders directly, we have to fetch them - # as Environment attributes because that sets them up to be called - # correctly later by our emitter. - env.AppendUnique(PROGEMITTER =[AutomocStatic], - SHLIBEMITTER=[AutomocShared], - LIBEMITTER =[AutomocStatic], - # Of course, we need to link against the qt libraries - CPPPATH=["$QT_CPPPATH"], - LIBPATH=["$QT_LIBPATH"], - LIBS=['$QT_LIB']) - -def exists(env): - return _detect(env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/rmic.py b/tools/scons/scons-local-1.2.0/SCons/Tool/rmic.py deleted file mode 100644 index 03a228c98b..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/rmic.py +++ /dev/null @@ -1,115 +0,0 @@ -"""SCons.Tool.rmic - -Tool-specific initialization for rmic. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/rmic.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Util - -def emit_rmic_classes(target, source, env): - """Create and return lists of Java RMI stub and skeleton - class files to be created from a set of class files. - """ - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - classdir = env.get('JAVACLASSDIR') - - if not classdir: - try: - s = source[0] - except IndexError: - classdir = '.' - else: - try: - classdir = s.attributes.java_classdir - except AttributeError: - classdir = '.' - classdir = env.Dir(classdir).rdir() - if str(classdir) == '.': - c_ = None - else: - c_ = str(classdir) + os.sep - - slist = [] - for src in source: - try: - classname = src.attributes.java_classname - except AttributeError: - classname = str(src) - if c_ and classname[:len(c_)] == c_: - classname = classname[len(c_):] - if class_suffix and classname[:-len(class_suffix)] == class_suffix: - classname = classname[-len(class_suffix):] - s = src.rfile() - s.attributes.java_classdir = classdir - s.attributes.java_classname = classname - slist.append(s) - - stub_suffixes = ['_Stub'] - if env.get('JAVAVERSION') == '1.4': - stub_suffixes.append('_Skel') - - tlist = [] - for s in source: - for suff in stub_suffixes: - fname = string.replace(s.attributes.java_classname, '.', os.sep) + \ - suff + class_suffix - t = target[0].File(fname) - t.attributes.java_lookupdir = target[0] - tlist.append(t) - - return tlist, source - -RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR') - -RMICBuilder = SCons.Builder.Builder(action = RMICAction, - emitter = emit_rmic_classes, - src_suffix = '$JAVACLASSSUFFIX', - target_factory = SCons.Node.FS.Dir, - source_factory = SCons.Node.FS.File) - -def generate(env): - """Add Builders and construction variables for rmic to an Environment.""" - env['BUILDERS']['RMIC'] = RMICBuilder - - env['RMIC'] = 'rmic' - env['RMICFLAGS'] = SCons.Util.CLVar('') - env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' - env['JAVACLASSSUFFIX'] = '.class' - -def exists(env): - return env.Detect('rmic') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/rpcgen.py b/tools/scons/scons-local-1.2.0/SCons/Tool/rpcgen.py deleted file mode 100644 index a70a6d16a8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/rpcgen.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.rpcgen - -Tool-specific initialization for RPCGEN tools. - -Three normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/rpcgen.py 3842 2008/12/20 22:59:52 scons" - -from SCons.Builder import Builder -import SCons.Util - -cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}" - -rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS') -rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS') -rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') -rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') - -def generate(env): - "Add RPCGEN Builders and construction variables for an Environment." - - client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') - header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') - service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x') - xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x') - env.Append(BUILDERS={'RPCGenClient' : client, - 'RPCGenHeader' : header, - 'RPCGenService' : service, - 'RPCGenXDR' : xdr}) - env['RPCGEN'] = 'rpcgen' - env['RPCGENFLAGS'] = SCons.Util.CLVar('') - env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('') - env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('') - env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('') - env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('') - -def exists(env): - return env.Detect('rpcgen') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/rpm.py b/tools/scons/scons-local-1.2.0/SCons/Tool/rpm.py deleted file mode 100644 index 6aadc948af..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/rpm.py +++ /dev/null @@ -1,126 +0,0 @@ -"""SCons.Tool.rpm - -Tool-specific initialization for rpm. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -The rpm tool calls the rpmbuild command. The first and only argument should a -tar.gz consisting of the source file and a specfile. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/rpm.py 3842 2008/12/20 22:59:52 scons" - -import os -import re -import shutil -import subprocess - -import SCons.Builder -import SCons.Node.FS -import SCons.Util -import SCons.Action -import SCons.Defaults - -def get_cmd(source, env): - tar_file_with_included_specfile = source - if SCons.Util.is_List(source): - tar_file_with_included_specfile = source[0] - return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], - tar_file_with_included_specfile.abspath ) - -def build_rpm(target, source, env): - # create a temporary rpm build root. - tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) - if os.path.exists(tmpdir): - shutil.rmtree(tmpdir) - - # now create the mandatory rpm directory structure. - for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: - os.makedirs( os.path.join( tmpdir, d ) ) - - # set the topdir as an rpmflag. - env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) - - # now call rpmbuild to create the rpm package. - handle = subprocess.Popen(get_cmd(source, env), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - shell=True) - output = handle.stdout.read() - status = handle.wait() - - if status: - raise SCons.Errors.BuildError( node=target[0], - errstr=output, - filename=str(target[0]) ) - else: - # XXX: assume that LC_ALL=c is set while running rpmbuild - output_files = re.compile( 'Wrote: (.*)' ).findall( output ) - - for output, input in zip( output_files, target ): - rpm_output = os.path.basename(output) - expected = os.path.basename(input.get_path()) - - assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) - shutil.copy( output, input.abspath ) - - - # cleanup before leaving. - shutil.rmtree(tmpdir) - - return status - -def string_rpm(target, source, env): - try: - return env['RPMCOMSTR'] - except KeyError: - return get_cmd(source, env) - -rpmAction = SCons.Action.Action(build_rpm, string_rpm) - -RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), - source_scanner = SCons.Defaults.DirScanner, - suffix = '$RPMSUFFIX') - - - -def generate(env): - """Add Builders and construction variables for rpm to an Environment.""" - try: - bld = env['BUILDERS']['Rpm'] - except KeyError: - bld = RpmBuilder - env['BUILDERS']['Rpm'] = bld - - env.SetDefault(RPM = 'LC_ALL=c rpmbuild') - env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) - env.SetDefault(RPMCOM = rpmAction) - env.SetDefault(RPMSUFFIX = '.rpm') - -def exists(env): - return env.Detect('rpmbuild') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sgiar.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sgiar.py deleted file mode 100644 index 0be6780cf4..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sgiar.py +++ /dev/null @@ -1,62 +0,0 @@ -"""SCons.Tool.sgiar - -Tool-specific initialization for SGI ar (library archive). If CC -exists, static libraries should be built with it, so the prelinker has -a chance to resolve C++ template instantiations. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sgiar.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - if env.Detect('CC'): - env['AR'] = 'CC' - env['ARFLAGS'] = SCons.Util.CLVar('-ar') - env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' - else: - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('r') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return env.Detect('CC') or env.Detect('ar') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sgic++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sgic++.py deleted file mode 100644 index da0efb65d6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sgic++.py +++ /dev/null @@ -1,52 +0,0 @@ -"""SCons.Tool.sgic++ - -Tool-specific initialization for MIPSpro C++ on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sgic++.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -def generate(env): - """Add Builders and construction variables for SGI MIPS C++ to an Environment.""" - - cplusplus.generate(env) - - env['CXX'] = 'CC' - env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS -LANG:std') - env['SHCXX'] = '$CXX' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('CC') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sgicc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sgicc.py deleted file mode 100644 index 57115695f8..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sgicc.py +++ /dev/null @@ -1,47 +0,0 @@ -"""SCons.Tool.sgicc - -Tool-specific initialization for MIPSPro cc on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sgicc.py 3842 2008/12/20 22:59:52 scons" - -import cc - -def generate(env): - """Add Builders and construction variables for gcc to an Environment.""" - cc.generate(env) - - env['CXX'] = 'CC' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('cc') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sgilink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sgilink.py deleted file mode 100644 index 0036e26be2..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sgilink.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.sgilink - -Tool-specific initialization for the SGI MIPSPro linker on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sgilink.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import link - -linkers = ['CC', 'cc'] - -def generate(env): - """Add Builders and construction variables for MIPSPro to an Environment.""" - link.generate(env) - - env['LINK'] = env.Detect(linkers) or 'cc' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - - # __RPATH is set to $_RPATH in the platform specification if that - # platform supports it. - env.Append(LINKFLAGS=['$__RPATH']) - env['RPATHPREFIX'] = '-rpath ' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return env.Detect(linkers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunar.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunar.py deleted file mode 100644 index 90dd156bed..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunar.py +++ /dev/null @@ -1,61 +0,0 @@ -"""engine.SCons.Tool.sunar - -Tool-specific initialization for Solaris (Forte) ar (library archive). If CC -exists, static libraries should be built with it, so that template -instantians can be resolved. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunar.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - if env.Detect('CC'): - env['AR'] = 'CC' - env['ARFLAGS'] = SCons.Util.CLVar('-xar') - env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' - else: - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('r') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return env.Detect('CC') or env.Detect('ar') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunc++.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunc++.py deleted file mode 100644 index 91c0efb225..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunc++.py +++ /dev/null @@ -1,100 +0,0 @@ -"""SCons.Tool.sunc++ - -Tool-specific initialization for C++ on SunOS / Solaris. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunc++.py 3842 2008/12/20 22:59:52 scons" - -import SCons - -import os.path - -cplusplus = __import__('c++', globals(), locals(), []) - -# use the package installer tool lslpp to figure out where cppc and what -# version of it is installed -def get_cppc(env): - cxx = env.get('CXX', None) - if cxx: - cppcPath = os.path.dirname(cxx) - else: - cppcPath = None - - cppcVersion = None - - pkginfo = env.subst('$PKGINFO') - pkgchk = env.subst('$PKGCHK') - - def look_pkg_db(pkginfo=pkginfo, pkgchk=pkgchk): - version = None - path = None - for package in ['SPROcpl']: - cmd = "%s -l %s 2>/dev/null | grep '^ *VERSION:'" % (pkginfo, package) - line = os.popen(cmd).readline() - if line: - version = line.split()[-1] - cmd = "%s -l %s 2>/dev/null | grep '^Pathname:.*/bin/CC$' | grep -v '/SC[0-9]*\.[0-9]*/'" % (pkgchk, package) - line = os.popen(cmd).readline() - if line: - path = os.path.dirname(line.split()[-1]) - break - - return path, version - - path, version = look_pkg_db() - if path and version: - cppcPath, cppcVersion = path, version - - return (cppcPath, 'CC', 'CC', cppcVersion) - -def generate(env): - """Add Builders and construction variables for SunPRO C++.""" - path, cxx, shcxx, version = get_cppc(env) - if path: - cxx = os.path.join(path, cxx) - shcxx = os.path.join(path, shcxx) - - cplusplus.generate(env) - - env['CXX'] = cxx - env['SHCXX'] = shcxx - env['CXXVERSION'] = version - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC') - env['SHOBJPREFIX'] = 'so_' - env['SHOBJSUFFIX'] = '.o' - -def exists(env): - path, cxx, shcxx, version = get_cppc(env) - if path and cxx: - cppc = os.path.join(path, cxx) - if os.path.exists(cppc): - return cppc - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/suncc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/suncc.py deleted file mode 100644 index be16238d89..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/suncc.py +++ /dev/null @@ -1,52 +0,0 @@ -"""SCons.Tool.suncc - -Tool-specific initialization for Sun Solaris (Forte) CC and cc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/suncc.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -import cc - -def generate(env): - """ - Add Builders and construction variables for Forte C and C++ compilers - to an Environment. - """ - cc.generate(env) - - env['CXX'] = 'CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC') - env['SHOBJPREFIX'] = 'so_' - env['SHOBJSUFFIX'] = '.o' - -def exists(env): - return env.Detect('CC') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf77.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunf77.py deleted file mode 100644 index f8be781450..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf77.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SCons.Tool.sunf77 - -Tool-specific initialization for sunf77, the Sun Studio F77 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunf77.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf77', 'f77'] - -def generate(env): - """Add Builders and construction variables for sunf77 to an Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f77' - env['FORTRAN'] = fcomp - env['F77'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF77'] = '$F77' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf90.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunf90.py deleted file mode 100644 index 152e22d039..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf90.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.sunf90 - -Tool-specific initialization for sunf90, the Sun Studio F90 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunf90.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf90', 'f90'] - -def generate(env): - """Add Builders and construction variables for sun f90 compiler to an - Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f90' - env['FORTRAN'] = fcomp - env['F90'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF90'] = '$F90' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf95.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunf95.py deleted file mode 100644 index 74ea2daae9..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunf95.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.sunf95 - -Tool-specific initialization for sunf95, the Sun Studio F95 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunf95.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf95', 'f95'] - -def generate(env): - """Add Builders and construction variables for sunf95 to an - Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f95' - env['FORTRAN'] = fcomp - env['F95'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF95'] = '$F95' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/sunlink.py b/tools/scons/scons-local-1.2.0/SCons/Tool/sunlink.py deleted file mode 100644 index 7efa96f5f6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/sunlink.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.Tool.sunlink - -Tool-specific initialization for the Sun Solaris (Forte) linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/sunlink.py 3842 2008/12/20 22:59:52 scons" - -import os -import os.path - -import SCons.Util - -import link - -ccLinker = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for d in dirs: - linker = '/opt/' + d + '/bin/CC' - if os.path.exists(linker): - ccLinker = linker - break - -def generate(env): - """Add Builders and construction variables for Forte to an Environment.""" - link.generate(env) - - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - - env.Append(LINKFLAGS=['$__RPATH']) - env['RPATHPREFIX'] = '-R' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return ccLinker diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/swig.py b/tools/scons/scons-local-1.2.0/SCons/Tool/swig.py deleted file mode 100644 index 000b80e04a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/swig.py +++ /dev/null @@ -1,118 +0,0 @@ -"""SCons.Tool.swig - -Tool-specific initialization for swig. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/swig.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re - -import SCons.Action -import SCons.Defaults -import SCons.Scanner -import SCons.Tool -import SCons.Util - -SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') - -def swigSuffixEmitter(env, source): - if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): - return '$SWIGCXXFILESUFFIX' - else: - return '$SWIGCFILESUFFIX' - -# Match '%module test', as well as '%module(directors="1") test' -_reModule = re.compile(r'%module(?:\s*\(.*\))?\s+(.+)') - -def _swigEmitter(target, source, env): - swigflags = env.subst("$SWIGFLAGS", target=target, source=source) - flags = SCons.Util.CLVar(swigflags) - for src in source: - src = str(src.rfile()) - mnames = None - if "-python" in flags and "-noproxy" not in flags: - if mnames is None: - mnames = _reModule.findall(open(src).read()) - target.extend(map(lambda m, d=target[0].dir: - d.File(m + ".py"), mnames)) - if "-java" in flags: - if mnames is None: - mnames = _reModule.findall(open(src).read()) - java_files = map(lambda m: [m + ".java", m + "JNI.java"], mnames) - java_files = SCons.Util.flatten(java_files) - outdir = env.subst('$SWIGOUTDIR', target=target, source=source) - if outdir: - java_files = map(lambda j, o=outdir: os.path.join(o, j), java_files) - java_files = map(env.fs.File, java_files) - for jf in java_files: - t_from_s = lambda t, p, s, x: t.dir - SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') - target.extend(java_files) - return (target, source) - -def generate(env): - """Add Builders and construction variables for swig to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - c_file.suffix['.i'] = swigSuffixEmitter - cxx_file.suffix['.i'] = swigSuffixEmitter - - c_file.add_action('.i', SwigAction) - c_file.add_emitter('.i', _swigEmitter) - cxx_file.add_action('.i', SwigAction) - cxx_file.add_emitter('.i', _swigEmitter) - - java_file = SCons.Tool.CreateJavaFileBuilder(env) - - java_file.suffix['.i'] = swigSuffixEmitter - - java_file.add_action('.i', SwigAction) - java_file.add_emitter('.i', _swigEmitter) - - env['SWIG'] = 'swig' - env['SWIGFLAGS'] = SCons.Util.CLVar('') - env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' - env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' - env['_SWIGOUTDIR'] = '${"-outdir " + str(SWIGOUTDIR)}' - env['SWIGPATH'] = [] - env['SWIGINCPREFIX'] = '-I' - env['SWIGINCSUFFIX'] = '' - env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' - - expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' - scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) - - env.Append(SCANNERS = scanner) - -def exists(env): - return env.Detect(['swig']) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/tar.py b/tools/scons/scons-local-1.2.0/SCons/Tool/tar.py deleted file mode 100644 index 7d527ee61a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/tar.py +++ /dev/null @@ -1,67 +0,0 @@ -"""SCons.Tool.tar - -Tool-specific initialization for tar. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/tar.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Node.FS -import SCons.Util - -tars = ['tar', 'gtar'] - -TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR') - -TarBuilder = SCons.Builder.Builder(action = TarAction, - source_factory = SCons.Node.FS.Entry, - source_scanner = SCons.Defaults.DirScanner, - suffix = '$TARSUFFIX', - multi = 1) - - -def generate(env): - """Add Builders and construction variables for tar to an Environment.""" - try: - bld = env['BUILDERS']['Tar'] - except KeyError: - bld = TarBuilder - env['BUILDERS']['Tar'] = bld - - env['TAR'] = env.Detect(tars) or 'gtar' - env['TARFLAGS'] = SCons.Util.CLVar('-c') - env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES' - env['TARSUFFIX'] = '.tar' - -def exists(env): - return env.Detect(tars) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/tex.py b/tools/scons/scons-local-1.2.0/SCons/Tool/tex.py deleted file mode 100644 index 5a664efda2..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/tex.py +++ /dev/null @@ -1,661 +0,0 @@ -"""SCons.Tool.tex - -Tool-specific initialization for TeX. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/tex.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import re -import string -import shutil - -import SCons.Action -import SCons.Node -import SCons.Node.FS -import SCons.Util -import SCons.Scanner.LaTeX - -Verbose = False - -must_rerun_latex = True - -# these are files that just need to be checked for changes and then rerun latex -check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm'] - -# these are files that require bibtex or makeindex to be run when they change -all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo'] - -# -# regular expressions used to search for Latex features -# or outputs that require rerunning latex -# -# search for all .aux files opened by latex (recorded in the .log file) -openout_aux_re = re.compile(r"\\openout.*`(.*\.aux)'") - -#printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE) -#printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE) -#printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE) - -# search to find rerun warnings -warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)' -warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE) - -# search to find citation rerun warnings -rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct" -rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE) - -# search to find undefined references or citations warnings -undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)' -undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) - -# used by the emitter -auxfile_re = re.compile(r".", re.MULTILINE) -tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) -makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) -bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) -listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) -listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) -hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) -makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE) -makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE) -beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE) - -# search to find all files included by Latex -include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE) - -# search to find all graphics files included by Latex -includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE) - -# search to find all files opened by Latex (recorded in .log file) -openout_re = re.compile(r"\\openout.*`(.*)'") - -# list of graphics file extensions for TeX and LaTeX -TexGraphics = SCons.Scanner.LaTeX.TexGraphics -LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics - -# An Action sufficient to build any generic tex file. -TeXAction = None - -# An action to build a latex file. This action might be needed more -# than once if we are dealing with labels and bibtex. -LaTeXAction = None - -# An action to run BibTeX on a file. -BibTeXAction = None - -# An action to run MakeIndex on a file. -MakeIndexAction = None - -# An action to run MakeIndex (for nomencl) on a file. -MakeNclAction = None - -# An action to run MakeIndex (for glossary) on a file. -MakeGlossaryAction = None - -# Used as a return value of modify_env_var if the variable is not set. -_null = SCons.Scanner.LaTeX._null - -modify_env_var = SCons.Scanner.LaTeX.modify_env_var - -def FindFile(name,suffixes,paths,env,requireExt=False): - if requireExt: - name = SCons.Util.splitext(name)[0] - if Verbose: - print " searching for '%s' with extensions: " % name,suffixes - - for path in paths: - testName = os.path.join(path,name) - if Verbose: - print " look for '%s'" % testName - if os.path.exists(testName): - if Verbose: - print " found '%s'" % testName - return env.fs.File(testName) - else: - name_ext = SCons.Util.splitext(testName)[1] - if name_ext: - continue - - # if no suffix try adding those passed in - for suffix in suffixes: - testNameExt = testName + suffix - if Verbose: - print " look for '%s'" % testNameExt - - if os.path.exists(testNameExt): - if Verbose: - print " found '%s'" % testNameExt - return env.fs.File(testNameExt) - if Verbose: - print " did not find '%s'" % name - return None - -def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None): - """A builder for LaTeX files that checks the output in the aux file - and decides how many times to use LaTeXAction, and BibTeXAction.""" - - global must_rerun_latex - - # This routine is called with two actions. In this file for DVI builds - # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction - # set this up now for the case where the user requests a different extension - # for the target filename - if (XXXLaTeXAction == LaTeXAction): - callerSuffix = ".dvi" - else: - callerSuffix = env['PDFSUFFIX'] - - basename = SCons.Util.splitext(str(source[0]))[0] - basedir = os.path.split(str(source[0]))[0] - basefile = os.path.split(str(basename))[1] - abspath = os.path.abspath(basedir) - targetext = os.path.splitext(str(target[0]))[1] - targetdir = os.path.split(str(target[0]))[0] - - saved_env = {} - for var in SCons.Scanner.LaTeX.LaTeX.env_variables: - saved_env[var] = modify_env_var(env, var, abspath) - - # Create base file names with the target directory since the auxiliary files - # will be made there. That's because the *COM variables have the cd - # command in the prolog. We check - # for the existence of files before opening them--even ones like the - # aux file that TeX always creates--to make it possible to write tests - # with stubs that don't necessarily generate all of the same files. - - targetbase = os.path.join(targetdir, basefile) - - # if there is a \makeindex there will be a .idx and thus - # we have to run makeindex at least once to keep the build - # happy even if there is no index. - # Same for glossaries and nomenclature - src_content = source[0].get_contents() - run_makeindex = makeindex_re.search(src_content) and not os.path.exists(targetbase + '.idx') - run_nomenclature = makenomenclature_re.search(src_content) and not os.path.exists(targetbase + '.nlo') - run_glossary = makeglossary_re.search(src_content) and not os.path.exists(targetbase + '.glo') - - saved_hashes = {} - suffix_nodes = {} - - for suffix in all_suffixes: - theNode = env.fs.File(targetbase + suffix) - suffix_nodes[suffix] = theNode - saved_hashes[suffix] = theNode.get_csig() - - if Verbose: - print "hashes: ",saved_hashes - - must_rerun_latex = True - - # - # routine to update MD5 hash and compare - # - # TODO(1.5): nested scopes - def check_MD5(filenode, suffix, saved_hashes=saved_hashes, targetbase=targetbase): - global must_rerun_latex - # two calls to clear old csig - filenode.clear_memoized_values() - filenode.ninfo = filenode.new_ninfo() - new_md5 = filenode.get_csig() - - if saved_hashes[suffix] == new_md5: - if Verbose: - print "file %s not changed" % (targetbase+suffix) - return False # unchanged - saved_hashes[suffix] = new_md5 - must_rerun_latex = True - if Verbose: - print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5 - return True # changed - - # generate the file name that latex will generate - resultfilename = targetbase + callerSuffix - - count = 0 - - while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) : - result = XXXLaTeXAction(target, source, env) - if result != 0: - return result - - count = count + 1 - - must_rerun_latex = False - # Decide if various things need to be run, or run again. - - # Read the log file to find all .aux files - logfilename = targetbase + '.log' - logContent = '' - auxfiles = [] - if os.path.exists(logfilename): - logContent = open(logfilename, "rb").read() - auxfiles = openout_aux_re.findall(logContent) - - # Now decide if bibtex will need to be run. - # The information that bibtex reads from the .aux file is - # pass-independent. If we find (below) that the .bbl file is unchanged, - # then the last latex saw a correct bibliography. - # Therefore only do this on the first pass - if count == 1: - for auxfilename in auxfiles: - target_aux = os.path.join(targetdir, auxfilename) - if os.path.exists(target_aux): - content = open(target_aux, "rb").read() - if string.find(content, "bibdata") != -1: - if Verbose: - print "Need to run bibtex" - bibfile = env.fs.File(targetbase) - result = BibTeXAction(bibfile, bibfile, env) - if result != 0: - return result - must_rerun_latex = check_MD5(suffix_nodes['.bbl'],'.bbl') - break - - # Now decide if latex will need to be run again due to index. - if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): - # We must run makeindex - if Verbose: - print "Need to run makeindex" - idxfile = suffix_nodes['.idx'] - result = MakeIndexAction(idxfile, idxfile, env) - if result != 0: - return result - - # TO-DO: need to add a way for the user to extend this list for whatever - # auxiliary files they create in other (or their own) packages - # Harder is case is where an action needs to be called -- that should be rare (I hope?) - - for index in check_suffixes: - check_MD5(suffix_nodes[index],index) - - # Now decide if latex will need to be run again due to nomenclature. - if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature): - # We must run makeindex - if Verbose: - print "Need to run makeindex for nomenclature" - nclfile = suffix_nodes['.nlo'] - result = MakeNclAction(nclfile, nclfile, env) - if result != 0: - return result - - # Now decide if latex will need to be run again due to glossary. - if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossary): - # We must run makeindex - if Verbose: - print "Need to run makeindex for glossary" - glofile = suffix_nodes['.glo'] - result = MakeGlossaryAction(glofile, glofile, env) - if result != 0: - return result - - # Now decide if latex needs to be run yet again to resolve warnings. - if warning_rerun_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to latex or package rerun warning" - - if rerun_citations_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to 'Rerun to get citations correct' warning" - - if undefined_references_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to undefined references or citations" - - if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex): - print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES')) -# end of while loop - - # rename Latex's output to what the target name is - if not (str(target[0]) == resultfilename and os.path.exists(resultfilename)): - if os.path.exists(resultfilename): - print "move %s to %s" % (resultfilename, str(target[0]), ) - shutil.move(resultfilename,str(target[0])) - - # Original comment (when TEXPICTS was not restored): - # The TEXPICTS enviroment variable is needed by a dvi -> pdf step - # later on Mac OSX so leave it - # - # It is also used when searching for pictures (implicit dependencies). - # Why not set the variable again in the respective builder instead - # of leaving local modifications in the environment? What if multiple - # latex builds in different directories need different TEXPICTS? - for var in SCons.Scanner.LaTeX.LaTeX.env_variables: - if var == 'TEXPICTS': - continue - if saved_env[var] is _null: - try: - del env['ENV'][var] - except KeyError: - pass # was never set - else: - env['ENV'][var] = saved_env[var] - - return result - -def LaTeXAuxAction(target = None, source= None, env=None): - result = InternalLaTeXAuxAction( LaTeXAction, target, source, env ) - return result - -LaTeX_re = re.compile("\\\\document(style|class)") - -def is_LaTeX(flist): - # Scan a file list to decide if it's TeX- or LaTeX-flavored. - for f in flist: - content = f.get_contents() - if LaTeX_re.search(content): - return 1 - return 0 - -def TeXLaTeXFunction(target = None, source= None, env=None): - """A builder for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then executes the appropriate - program.""" - if is_LaTeX(source): - result = LaTeXAuxAction(target,source,env) - else: - result = TeXAction(target,source,env) - return result - -def TeXLaTeXStrFunction(target = None, source= None, env=None): - """A strfunction for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then returns the appropriate - command string.""" - if env.GetOption("no_exec"): - if is_LaTeX(source): - result = env.subst('$LATEXCOM',0,target,source)+" ..." - else: - result = env.subst("$TEXCOM",0,target,source)+" ..." - else: - result = '' - return result - -def tex_eps_emitter(target, source, env): - """An emitter for TeX and LaTeX sources when - executing tex or latex. It will accept .ps and .eps - graphics files - """ - (target, source) = tex_emitter_core(target, source, env, TexGraphics) - - return (target, source) - -def tex_pdf_emitter(target, source, env): - """An emitter for TeX and LaTeX sources when - executing pdftex or pdflatex. It will accept graphics - files of types .pdf, .jpg, .png, .gif, and .tif - """ - (target, source) = tex_emitter_core(target, source, env, LatexGraphics) - - return (target, source) - -def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir): - # for theFile (a Node) update any file_tests and search for graphics files - # then find all included files and call ScanFiles for each of them - content = theFile.get_contents() - if Verbose: - print " scanning ",str(theFile) - - for i in range(len(file_tests_search)): - if file_tests[i][0] == None: - file_tests[i][0] = file_tests_search[i].search(content) - - # For each file see if any graphics files are included - # and set up target to create ,pdf graphic - # is this is in pdflatex toolchain - graphic_files = includegraphics_re.findall(content) - if Verbose: - print "graphics files in '%s': "%str(theFile),graphic_files - for graphFile in graphic_files: - graphicNode = FindFile(graphFile,graphics_extensions,paths,env,requireExt=True) - # if building with pdflatex see if we need to build the .pdf version of the graphic file - # I should probably come up with a better way to tell which builder we are using. - if graphics_extensions == LatexGraphics: - # see if we can build this graphics file by epstopdf - graphicSrc = FindFile(graphFile,TexGraphics,paths,env,requireExt=True) - # it seems that FindFile checks with no extension added - # so if the extension is included in the name then both searches find it - # we don't want to try to build a .pdf from a .pdf so make sure src!=file wanted - if (graphicSrc != None) and (graphicSrc != graphicNode): - if Verbose: - if graphicNode == None: - print "need to build '%s' by epstopdf %s -o %s" % (graphFile,graphicSrc,graphFile) - else: - print "no need to build '%s', but source file %s exists" % (graphicNode,graphicSrc) - graphicNode = env.PDF(graphicSrc) - env.Depends(target[0],graphicNode) - - # recursively call this on each of the included files - inc_files = [ ] - inc_files.extend( include_re.findall(content) ) - if Verbose: - print "files included by '%s': "%str(theFile),inc_files - # inc_files is list of file names as given. need to find them - # using TEXINPUTS paths. - - for src in inc_files: - srcNode = srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) - if srcNode != None: - file_test = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir) - if Verbose: - print " done scanning ",str(theFile) - return file_tests - -def tex_emitter_core(target, source, env, graphics_extensions): - """An emitter for TeX and LaTeX sources. - For LaTeX sources we try and find the common created files that - are needed on subsequent runs of latex to finish tables of contents, - bibliographies, indices, lists of figures, and hyperlink references. - """ - targetbase = SCons.Util.splitext(str(target[0]))[0] - basename = SCons.Util.splitext(str(source[0]))[0] - basefile = os.path.split(str(basename))[1] - - basedir = os.path.split(str(source[0]))[0] - targetdir = os.path.split(str(target[0]))[0] - abspath = os.path.abspath(basedir) - target[0].attributes.path = abspath - - # - # file names we will make use of in searching the sources and log file - # - emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg'] + all_suffixes - auxfilename = targetbase + '.aux' - logfilename = targetbase + '.log' - - env.SideEffect(auxfilename,target[0]) - env.SideEffect(logfilename,target[0]) - env.Clean(target[0],auxfilename) - env.Clean(target[0],logfilename) - - content = source[0].get_contents() - - idx_exists = os.path.exists(targetbase + '.idx') - nlo_exists = os.path.exists(targetbase + '.nlo') - glo_exists = os.path.exists(targetbase + '.glo') - - # set up list with the regular expressions - # we use to find features used - file_tests_search = [auxfile_re, - makeindex_re, - bibliography_re, - tableofcontents_re, - listoffigures_re, - listoftables_re, - hyperref_re, - makenomenclature_re, - makeglossary_re, - beamer_re ] - # set up list with the file suffixes that need emitting - # when a feature is found - file_tests_suff = [['.aux'], - ['.idx', '.ind', '.ilg'], - ['.bbl', '.blg'], - ['.toc'], - ['.lof'], - ['.lot'], - ['.out'], - ['.nlo', '.nls', '.nlg'], - ['.glo', '.gls', '.glg'], - ['.nav', '.snm', '.out', '.toc'] ] - # build the list of lists - file_tests = [] - for i in range(len(file_tests_search)): - file_tests.append( [None, file_tests_suff[i]] ) - - # TO-DO: need to add a way for the user to extend this list for whatever - # auxiliary files they create in other (or their own) packages - - # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] - savedpath = modify_env_var(env, 'TEXINPUTS', abspath) - paths = env['ENV']['TEXINPUTS'] - if SCons.Util.is_List(paths): - pass - else: - # Split at os.pathsep to convert into absolute path - # TODO(1.5) - #paths = paths.split(os.pathsep) - paths = string.split(paths, os.pathsep) - - # now that we have the path list restore the env - if savedpath is _null: - try: - del env['ENV']['TEXINPUTS'] - except KeyError: - pass # was never set - else: - env['ENV']['TEXINPUTS'] = savedpath - if Verbose: - print "search path ",paths - - file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir) - - for (theSearch,suffix_list) in file_tests: - if theSearch: - for suffix in suffix_list: - env.SideEffect(targetbase + suffix,target[0]) - env.Clean(target[0],targetbase + suffix) - - # read log file to get all other files that latex creates and will read on the next pass - if os.path.exists(logfilename): - content = open(logfilename, "rb").read() - out_files = openout_re.findall(content) - env.SideEffect(out_files,target[0]) - env.Clean(target[0],out_files) - - return (target, source) - - -TeXLaTeXAction = None - -def generate(env): - """Add Builders and construction variables for TeX to an Environment.""" - - # A generic tex file Action, sufficient for all tex files. - global TeXAction - if TeXAction is None: - TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR") - - # An Action to build a latex file. This might be needed more - # than once if we are dealing with labels and bibtex. - global LaTeXAction - if LaTeXAction is None: - LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR") - - # Define an action to run BibTeX on a file. - global BibTeXAction - if BibTeXAction is None: - BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR") - - # Define an action to run MakeIndex on a file. - global MakeIndexAction - if MakeIndexAction is None: - MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR") - - # Define an action to run MakeIndex on a file for nomenclatures. - global MakeNclAction - if MakeNclAction is None: - MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR") - - # Define an action to run MakeIndex on a file for glossaries. - global MakeGlossaryAction - if MakeGlossaryAction is None: - MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR") - - global TeXLaTeXAction - if TeXLaTeXAction is None: - TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, - strfunction=TeXLaTeXStrFunction) - - import dvi - dvi.generate(env) - - bld = env['BUILDERS']['DVI'] - bld.add_action('.tex', TeXLaTeXAction) - bld.add_emitter('.tex', tex_eps_emitter) - - env['TEX'] = 'tex' - env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['TEXCOM'] = 'cd ${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}' - - # Duplicate from latex.py. If latex.py goes away, then this is still OK. - env['LATEX'] = 'latex' - env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['LATEXCOM'] = 'cd ${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 3 - - env['BIBTEX'] = 'bibtex' - env['BIBTEXFLAGS'] = SCons.Util.CLVar('') - env['BIBTEXCOM'] = 'cd ${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}' - - env['MAKEINDEX'] = 'makeindex' - env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('') - env['MAKEINDEXCOM'] = 'cd ${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}' - - env['MAKEGLOSSARY'] = 'makeindex' - env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist' - env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg') - env['MAKEGLOSSARYCOM'] = 'cd ${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls' - - env['MAKENCL'] = 'makeindex' - env['MAKENCLSTYLE'] = '$nomencl.ist' - env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg' - env['MAKENCLCOM'] = 'cd ${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls' - - # Duplicate from pdflatex.py. If latex.py goes away, then this is still OK. - env['PDFLATEX'] = 'pdflatex' - env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode') - env['PDFLATEXCOM'] = 'cd ${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' - -def exists(env): - return env.Detect('tex') diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/tlib.py b/tools/scons/scons-local-1.2.0/SCons/Tool/tlib.py deleted file mode 100644 index 03fa99c003..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/tlib.py +++ /dev/null @@ -1,47 +0,0 @@ -"""SCons.Tool.tlib - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/tlib.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Tool -import SCons.Tool.bcc32 -import SCons.Util - -def generate(env): - SCons.Tool.bcc32.findIt('tlib', env) - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - env['AR'] = 'tlib' - env['ARFLAGS'] = SCons.Util.CLVar('') - env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - -def exists(env): - return SCons.Tool.bcc32.findIt('tlib', env) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/wix.py b/tools/scons/scons-local-1.2.0/SCons/Tool/wix.py deleted file mode 100644 index 16725e14aa..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/wix.py +++ /dev/null @@ -1,94 +0,0 @@ -"""SCons.Tool.wix - -Tool-specific initialization for wix, the Windows Installer XML Tool. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/wix.py 3842 2008/12/20 22:59:52 scons" - -import SCons.Builder -import SCons.Action -import os -import string - -def generate(env): - """Add Builders and construction variables for WiX to an Environment.""" - if not exists(env): - return - - env['WIXCANDLEFLAGS'] = ['-nologo'] - env['WIXCANDLEINCLUDE'] = [] - env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' - - env['WIXLIGHTFLAGS'].append( '-nologo' ) - env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" - - object_builder = SCons.Builder.Builder( - action = '$WIXCANDLECOM', - suffix = '.wxiobj', - src_suffix = '.wxs') - - linker_builder = SCons.Builder.Builder( - action = '$WIXLIGHTCOM', - src_suffix = '.wxiobj', - src_builder = object_builder) - - env['BUILDERS']['WiX'] = linker_builder - -def exists(env): - env['WIXCANDLE'] = 'candle.exe' - env['WIXLIGHT'] = 'light.exe' - - # try to find the candle.exe and light.exe tools and - # add the install directory to light libpath. - #for path in os.environ['PATH'].split(os.pathsep): - for path in string.split(os.environ['PATH'], os.pathsep): - if not path: - continue - - # workaround for some weird python win32 bug. - if path[0] == '"' and path[-1:]=='"': - path = path[1:-1] - - # normalize the path - path = os.path.normpath(path) - - # search for the tools in the PATH environment variable - try: - if env['WIXCANDLE'] in os.listdir(path) and\ - env['WIXLIGHT'] in os.listdir(path): - env.PrependENVPath('PATH', path) - env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), - '-loc', - os.path.join( path, 'WixUI_en-us.wxl' ) ] - return 1 - except OSError: - pass # ignore this, could be a stale PATH entry. - - return None diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/yacc.py b/tools/scons/scons-local-1.2.0/SCons/Tool/yacc.py deleted file mode 100644 index e06c477bdd..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/yacc.py +++ /dev/null @@ -1,125 +0,0 @@ -"""SCons.Tool.yacc - -Tool-specific initialization for yacc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/yacc.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") - -def _yaccEmitter(target, source, env, ysuf, hsuf): - yaccflags = env.subst("$YACCFLAGS", target=target, source=source) - flags = SCons.Util.CLVar(yaccflags) - targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) - - if '.ym' in ysuf: # If using Objective-C - target = [targetBase + ".m"] # the extension is ".m". - - - # If -d is specified on the command line, yacc will emit a .h - # or .hpp file with the same name as the .c or .cpp output file. - if '-d' in flags: - target.append(targetBase + env.subst(hsuf, target=target, source=source)) - - # If -g is specified on the command line, yacc will emit a .vcg - # file with the same base name as the .y, .yacc, .ym or .yy file. - if "-g" in flags: - base, ext = os.path.splitext(SCons.Util.to_String(source[0])) - target.append(base + env.subst("$YACCVCGFILESUFFIX")) - - # With --defines and --graph, the name of the file is totally defined - # in the options. - fileGenOptions = ["--defines=", "--graph="] - for option in flags: - for fileGenOption in fileGenOptions: - l = len(fileGenOption) - if option[:l] == fileGenOption: - # A file generating option is present, so add the file - # name to the list of targets. - fileName = string.strip(option[l:]) - target.append(fileName) - - return (target, source) - -def yEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') - -def ymEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') - -def yyEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') - -def generate(env): - """Add Builders and construction variables for yacc to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - # C - c_file.add_action('.y', YaccAction) - c_file.add_emitter('.y', yEmitter) - - c_file.add_action('.yacc', YaccAction) - c_file.add_emitter('.yacc', yEmitter) - - # Objective-C - c_file.add_action('.ym', YaccAction) - c_file.add_emitter('.ym', ymEmitter) - - # C++ - cxx_file.add_action('.yy', YaccAction) - cxx_file.add_emitter('.yy', yyEmitter) - - env['YACC'] = env.Detect('bison') or 'yacc' - env['YACCFLAGS'] = SCons.Util.CLVar('') - env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' - env['YACCHFILESUFFIX'] = '.h' - - # Apparently, OS X now creates file.hpp like everybody else - # I have no idea when it changed; it was fixed in 10.4 - #if env['PLATFORM'] == 'darwin': - # # Bison on Mac OS X just appends ".h" to the generated target .cc - # # or .cpp file name. Hooray for delayed expansion of variables. - # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' - #else: - # env['YACCHXXFILESUFFIX'] = '.hpp' - env['YACCHXXFILESUFFIX'] = '.hpp' - - env['YACCVCGFILESUFFIX'] = '.vcg' - -def exists(env): - return env.Detect(['bison', 'yacc']) diff --git a/tools/scons/scons-local-1.2.0/SCons/Tool/zip.py b/tools/scons/scons-local-1.2.0/SCons/Tool/zip.py deleted file mode 100644 index 5765fefac1..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Tool/zip.py +++ /dev/null @@ -1,94 +0,0 @@ -"""SCons.Tool.zip - -Tool-specific initialization for zip. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Tool/zip.py 3842 2008/12/20 22:59:52 scons" - -import os.path - -import SCons.Builder -import SCons.Defaults -import SCons.Node.FS -import SCons.Util - -try: - import zipfile - internal_zip = 1 -except ImportError: - internal_zip = 0 - -if internal_zip: - zipcompression = zipfile.ZIP_DEFLATED - def zip(target, source, env): - def visit(arg, dirname, names): - for name in names: - path = os.path.join(dirname, name) - if os.path.isfile(path): - arg.write(path) - compression = env.get('ZIPCOMPRESSION', 0) - zf = zipfile.ZipFile(str(target[0]), 'w', compression) - for s in source: - if s.isdir(): - os.path.walk(str(s), visit, zf) - else: - zf.write(str(s)) - zf.close() -else: - zipcompression = 0 - zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES" - - -zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION']) - -ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'), - source_factory = SCons.Node.FS.Entry, - source_scanner = SCons.Defaults.DirScanner, - suffix = '$ZIPSUFFIX', - multi = 1) - - -def generate(env): - """Add Builders and construction variables for zip to an Environment.""" - try: - bld = env['BUILDERS']['Zip'] - except KeyError: - bld = ZipBuilder - env['BUILDERS']['Zip'] = bld - - env['ZIP'] = 'zip' - env['ZIPFLAGS'] = SCons.Util.CLVar('') - env['ZIPCOM'] = zipAction - env['ZIPCOMPRESSION'] = zipcompression - env['ZIPSUFFIX'] = '.zip' - -def exists(env): - return internal_zip or env.Detect('zip') diff --git a/tools/scons/scons-local-1.2.0/SCons/Util.py b/tools/scons/scons-local-1.2.0/SCons/Util.py deleted file mode 100644 index 1f33f9757e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Util.py +++ /dev/null @@ -1,1577 +0,0 @@ -"""SCons.Util - -Various utility functions go here. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Util.py 3842 2008/12/20 22:59:52 scons" - -import copy -import os -import os.path -import re -import string -import sys -import types - -from UserDict import UserDict -from UserList import UserList -from UserString import UserString - -# Don't "from types import ..." these because we need to get at the -# types module later to look for UnicodeType. -DictType = types.DictType -InstanceType = types.InstanceType -ListType = types.ListType -StringType = types.StringType -TupleType = types.TupleType - -def dictify(keys, values, result={}): - for k, v in zip(keys, values): - result[k] = v - return result - -_altsep = os.altsep -if _altsep is None and sys.platform == 'win32': - # My ActivePython 2.0.1 doesn't set os.altsep! What gives? - _altsep = '/' -if _altsep: - def rightmost_separator(path, sep, _altsep=_altsep): - rfind = string.rfind - return max(rfind(path, sep), rfind(path, _altsep)) -else: - rightmost_separator = string.rfind - -# First two from the Python Cookbook, just for completeness. -# (Yeah, yeah, YAGNI...) -def containsAny(str, set): - """Check whether sequence str contains ANY of the items in set.""" - for c in set: - if c in str: return 1 - return 0 - -def containsAll(str, set): - """Check whether sequence str contains ALL of the items in set.""" - for c in set: - if c not in str: return 0 - return 1 - -def containsOnly(str, set): - """Check whether sequence str contains ONLY items in set.""" - for c in str: - if c not in set: return 0 - return 1 - -def splitext(path): - "Same as os.path.splitext() but faster." - sep = rightmost_separator(path, os.sep) - dot = string.rfind(path, '.') - # An ext is only real if it has at least one non-digit char - if dot > sep and not containsOnly(path[dot:], "0123456789."): - return path[:dot],path[dot:] - else: - return path,"" - -def updrive(path): - """ - Make the drive letter (if any) upper case. - This is useful because Windows is inconsitent on the case - of the drive letter, which can cause inconsistencies when - calculating command signatures. - """ - drive, rest = os.path.splitdrive(path) - if drive: - path = string.upper(drive) + rest - return path - -class CallableComposite(UserList): - """A simple composite callable class that, when called, will invoke all - of its contained callables with the same arguments.""" - def __call__(self, *args, **kwargs): - retvals = map(lambda x, args=args, kwargs=kwargs: apply(x, - args, - kwargs), - self.data) - if self.data and (len(self.data) == len(filter(callable, retvals))): - return self.__class__(retvals) - return NodeList(retvals) - -class NodeList(UserList): - """This class is almost exactly like a regular list of Nodes - (actually it can hold any object), with one important difference. - If you try to get an attribute from this list, it will return that - attribute from every item in the list. For example: - - >>> someList = NodeList([ ' foo ', ' bar ' ]) - >>> someList.strip() - [ 'foo', 'bar' ] - """ - def __nonzero__(self): - return len(self.data) != 0 - - def __str__(self): - return string.join(map(str, self.data)) - - def __getattr__(self, name): - if not self.data: - # If there is nothing in the list, then we have no attributes to - # pass through, so raise AttributeError for everything. - raise AttributeError, "NodeList has no attribute: %s" % name - - # Return a list of the attribute, gotten from every element - # in the list - attrList = map(lambda x, n=name: getattr(x, n), self.data) - - # Special case. If the attribute is callable, we do not want - # to return a list of callables. Rather, we want to return a - # single callable that, when called, will invoke the function on - # all elements of this list. - if self.data and (len(self.data) == len(filter(callable, attrList))): - return CallableComposite(attrList) - return self.__class__(attrList) - -_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') - -def get_environment_var(varstr): - """Given a string, first determine if it looks like a reference - to a single environment variable, like "$FOO" or "${FOO}". - If so, return that variable with no decorations ("FOO"). - If not, return None.""" - mo=_get_env_var.match(to_String(varstr)) - if mo: - var = mo.group(1) - if var[0] == '{': - return var[1:-1] - else: - return var - else: - return None - -class DisplayEngine: - def __init__(self): - self.__call__ = self.print_it - - def print_it(self, text, append_newline=1): - if append_newline: text = text + '\n' - try: - sys.stdout.write(text) - except IOError: - # Stdout might be connected to a pipe that has been closed - # by now. The most likely reason for the pipe being closed - # is that the user has press ctrl-c. It this is the case, - # then SCons is currently shutdown. We therefore ignore - # IOError's here so that SCons can continue and shutdown - # properly so that the .sconsign is correctly written - # before SCons exits. - pass - - def dont_print(self, text, append_newline=1): - pass - - def set_mode(self, mode): - if mode: - self.__call__ = self.print_it - else: - self.__call__ = self.dont_print - -def render_tree(root, child_func, prune=0, margin=[0], visited={}): - """ - Render a tree of nodes into an ASCII tree view. - root - the root node of the tree - child_func - the function called to get the children of a node - prune - don't visit the same node twice - margin - the format of the left margin to use for children of root. - 1 results in a pipe, and 0 results in no pipe. - visited - a dictionary of visited nodes in the current branch if not prune, - or in the whole tree if prune. - """ - - rname = str(root) - - children = child_func(root) - retval = "" - for pipe in margin[:-1]: - if pipe: - retval = retval + "| " - else: - retval = retval + " " - - if visited.has_key(rname): - return retval + "+-[" + rname + "]\n" - - retval = retval + "+-" + rname + "\n" - if not prune: - visited = copy.copy(visited) - visited[rname] = 1 - - for i in range(len(children)): - margin.append(i<len(children)-1) - retval = retval + render_tree(children[i], child_func, prune, margin, visited -) - margin.pop() - - return retval - -IDX = lambda N: N and 1 or 0 - -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): - """ - Print a tree of nodes. This is like render_tree, except it prints - lines directly instead of creating a string representation in memory, - so that huge trees can be printed. - - root - the root node of the tree - child_func - the function called to get the children of a node - prune - don't visit the same node twice - showtags - print status information to the left of each node line - margin - the format of the left margin to use for children of root. - 1 results in a pipe, and 0 results in no pipe. - visited - a dictionary of visited nodes in the current branch if not prune, - or in the whole tree if prune. - """ - - rname = str(root) - - if showtags: - - if showtags == 2: - print ' E = exists' - print ' R = exists in repository only' - print ' b = implicit builder' - print ' B = explicit builder' - print ' S = side effect' - print ' P = precious' - print ' A = always build' - print ' C = current' - print ' N = no clean' - print ' H = no cache' - print '' - - tags = ['['] - tags.append(' E'[IDX(root.exists())]) - tags.append(' R'[IDX(root.rexists() and not root.exists())]) - tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] + - [0,2][IDX(root.has_builder())]]) - tags.append(' S'[IDX(root.side_effect)]) - tags.append(' P'[IDX(root.precious)]) - tags.append(' A'[IDX(root.always_build)]) - tags.append(' C'[IDX(root.is_up_to_date())]) - tags.append(' N'[IDX(root.noclean)]) - tags.append(' H'[IDX(root.nocache)]) - tags.append(']') - - else: - tags = [] - - def MMM(m): - return [" ","| "][m] - margins = map(MMM, margin[:-1]) - - children = child_func(root) - - if prune and visited.has_key(rname) and children: - print string.join(tags + margins + ['+-[', rname, ']'], '') - return - - print string.join(tags + margins + ['+-', rname], '') - - visited[rname] = 1 - - if children: - margin.append(1) - map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited: - print_tree(C, cf, p, i, m, v), - children[:-1]) - margin[-1] = 0 - print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited) - margin.pop() - - - -# Functions for deciding if things are like various types, mainly to -# handle UserDict, UserList and UserString like their underlying types. -# -# Yes, all of this manual testing breaks polymorphism, and the real -# Pythonic way to do all of this would be to just try it and handle the -# exception, but handling the exception when it's not the right type is -# often too slow. - -try: - class mystr(str): - pass -except TypeError: - # An older Python version without new-style classes. - # - # The actual implementations here have been selected after timings - # coded up in in bench/is_types.py (from the SCons source tree, - # see the scons-src distribution), mostly against Python 1.5.2. - # Key results from those timings: - # - # -- Storing the type of the object in a variable (t = type(obj)) - # slows down the case where it's a native type and the first - # comparison will match, but nicely speeds up the case where - # it's a different native type. Since that's going to be - # common, it's a good tradeoff. - # - # -- The data show that calling isinstance() on an object that's - # a native type (dict, list or string) is expensive enough - # that checking up front for whether the object is of type - # InstanceType is a pretty big win, even though it does slow - # down the case where it really *is* an object instance a - # little bit. - def is_Dict(obj): - t = type(obj) - return t is DictType or \ - (t is InstanceType and isinstance(obj, UserDict)) - - def is_List(obj): - t = type(obj) - return t is ListType \ - or (t is InstanceType and isinstance(obj, UserList)) - - def is_Sequence(obj): - t = type(obj) - return t is ListType \ - or t is TupleType \ - or (t is InstanceType and isinstance(obj, UserList)) - - def is_Tuple(obj): - t = type(obj) - return t is TupleType - - if hasattr(types, 'UnicodeType'): - def is_String(obj): - t = type(obj) - return t is StringType \ - or t is UnicodeType \ - or (t is InstanceType and isinstance(obj, UserString)) - else: - def is_String(obj): - t = type(obj) - return t is StringType \ - or (t is InstanceType and isinstance(obj, UserString)) - - def is_Scalar(obj): - return is_String(obj) or not is_Sequence(obj) - - def flatten(obj, result=None): - """Flatten a sequence to a non-nested list. - - Flatten() converts either a single scalar or a nested sequence - to a non-nested list. Note that flatten() considers strings - to be scalars instead of sequences like Python would. - """ - if is_Scalar(obj): - return [obj] - if result is None: - result = [] - for item in obj: - if is_Scalar(item): - result.append(item) - else: - flatten_sequence(item, result) - return result - - def flatten_sequence(sequence, result=None): - """Flatten a sequence to a non-nested list. - - Same as flatten(), but it does not handle the single scalar - case. This is slightly more efficient when one knows that - the sequence to flatten can not be a scalar. - """ - if result is None: - result = [] - for item in sequence: - if is_Scalar(item): - result.append(item) - else: - flatten_sequence(item, result) - return result - - # - # Generic convert-to-string functions that abstract away whether or - # not the Python we're executing has Unicode support. The wrapper - # to_String_for_signature() will use a for_signature() method if the - # specified object has one. - # - if hasattr(types, 'UnicodeType'): - UnicodeType = types.UnicodeType - def to_String(s): - if isinstance(s, UserString): - t = type(s.data) - else: - t = type(s) - if t is UnicodeType: - return unicode(s) - else: - return str(s) - else: - to_String = str - - def to_String_for_signature(obj): - try: - f = obj.for_signature - except AttributeError: - return to_String_for_subst(obj) - else: - return f() - - def to_String_for_subst(s): - if is_Sequence( s ): - return string.join( map(to_String_for_subst, s) ) - - return to_String( s ) - -else: - # A modern Python version with new-style classes, so we can just use - # isinstance(). - # - # We are using the following trick to speed-up these - # functions. Default arguments are used to take a snapshot of the - # the global functions and constants used by these functions. This - # transforms accesses to global variable into local variables - # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). - - DictTypes = (dict, UserDict) - ListTypes = (list, UserList) - SequenceTypes = (list, tuple, UserList) - - # Empirically, Python versions with new-style classes all have - # unicode. - # - # Note that profiling data shows a speed-up when comparing - # explicitely with str and unicode instead of simply comparing - # with basestring. (at least on Python 2.5.1) - StringTypes = (str, unicode, UserString) - - # Empirically, it is faster to check explicitely for str and - # unicode than for basestring. - BaseStringTypes = (str, unicode) - - def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes): - return isinstance(obj, DictTypes) - - def is_List(obj, isinstance=isinstance, ListTypes=ListTypes): - return isinstance(obj, ListTypes) - - def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes): - return isinstance(obj, SequenceTypes) - - def is_Tuple(obj, isinstance=isinstance, tuple=tuple): - return isinstance(obj, tuple) - - def is_String(obj, isinstance=isinstance, StringTypes=StringTypes): - return isinstance(obj, StringTypes) - - def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes): - # Profiling shows that there is an impressive speed-up of 2x - # when explicitely checking for strings instead of just not - # sequence when the argument (i.e. obj) is already a string. - # But, if obj is a not string than it is twice as fast to - # check only for 'not sequence'. The following code therefore - # assumes that the obj argument is a string must of the time. - return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes) - - def do_flatten(sequence, result, isinstance=isinstance, - StringTypes=StringTypes, SequenceTypes=SequenceTypes): - for item in sequence: - if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): - result.append(item) - else: - do_flatten(item, result) - - def flatten(obj, isinstance=isinstance, StringTypes=StringTypes, - SequenceTypes=SequenceTypes, do_flatten=do_flatten): - """Flatten a sequence to a non-nested list. - - Flatten() converts either a single scalar or a nested sequence - to a non-nested list. Note that flatten() considers strings - to be scalars instead of sequences like Python would. - """ - if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes): - return [obj] - result = [] - for item in obj: - if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): - result.append(item) - else: - do_flatten(item, result) - return result - - def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes, - SequenceTypes=SequenceTypes, do_flatten=do_flatten): - """Flatten a sequence to a non-nested list. - - Same as flatten(), but it does not handle the single scalar - case. This is slightly more efficient when one knows that - the sequence to flatten can not be a scalar. - """ - result = [] - for item in sequence: - if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): - result.append(item) - else: - do_flatten(item, result) - return result - - - # - # Generic convert-to-string functions that abstract away whether or - # not the Python we're executing has Unicode support. The wrapper - # to_String_for_signature() will use a for_signature() method if the - # specified object has one. - # - def to_String(s, - isinstance=isinstance, str=str, - UserString=UserString, BaseStringTypes=BaseStringTypes): - if isinstance(s,BaseStringTypes): - # Early out when already a string! - return s - elif isinstance(s, UserString): - # s.data can only be either a unicode or a regular - # string. Please see the UserString initializer. - return s.data - else: - return str(s) - - def to_String_for_subst(s, - isinstance=isinstance, join=string.join, str=str, to_String=to_String, - BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes, - UserString=UserString): - - # Note that the test cases are sorted by order of probability. - if isinstance(s, BaseStringTypes): - return s - elif isinstance(s, SequenceTypes): - l = [] - for e in s: - l.append(to_String_for_subst(e)) - return join( s ) - elif isinstance(s, UserString): - # s.data can only be either a unicode or a regular - # string. Please see the UserString initializer. - return s.data - else: - return str(s) - - def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, - AttributeError=AttributeError): - try: - f = obj.for_signature - except AttributeError: - return to_String_for_subst(obj) - else: - return f() - - - -# The SCons "semi-deep" copy. -# -# This makes separate copies of lists (including UserList objects) -# dictionaries (including UserDict objects) and tuples, but just copies -# references to anything else it finds. -# -# A special case is any object that has a __semi_deepcopy__() method, -# which we invoke to create the copy, which is used by the BuilderDict -# class because of its extra initialization argument. -# -# The dispatch table approach used here is a direct rip-off from the -# normal Python copy module. - -_semi_deepcopy_dispatch = d = {} - -def _semi_deepcopy_dict(x): - copy = {} - for key, val in x.items(): - # The regular Python copy.deepcopy() also deepcopies the key, - # as follows: - # - # copy[semi_deepcopy(key)] = semi_deepcopy(val) - # - # Doesn't seem like we need to, but we'll comment it just in case. - copy[key] = semi_deepcopy(val) - return copy -d[types.DictionaryType] = _semi_deepcopy_dict - -def _semi_deepcopy_list(x): - return map(semi_deepcopy, x) -d[types.ListType] = _semi_deepcopy_list - -def _semi_deepcopy_tuple(x): - return tuple(map(semi_deepcopy, x)) -d[types.TupleType] = _semi_deepcopy_tuple - -def _semi_deepcopy_inst(x): - if hasattr(x, '__semi_deepcopy__'): - return x.__semi_deepcopy__() - elif isinstance(x, UserDict): - return x.__class__(_semi_deepcopy_dict(x)) - elif isinstance(x, UserList): - return x.__class__(_semi_deepcopy_list(x)) - else: - return x -d[types.InstanceType] = _semi_deepcopy_inst - -def semi_deepcopy(x): - copier = _semi_deepcopy_dispatch.get(type(x)) - if copier: - return copier(x) - else: - return x - - - -class Proxy: - """A simple generic Proxy class, forwarding all calls to - subject. So, for the benefit of the python newbie, what does - this really mean? Well, it means that you can take an object, let's - call it 'objA', and wrap it in this Proxy class, with a statement - like this - - proxyObj = Proxy(objA), - - Then, if in the future, you do something like this - - x = proxyObj.var1, - - since Proxy does not have a 'var1' attribute (but presumably objA does), - the request actually is equivalent to saying - - x = objA.var1 - - Inherit from this class to create a Proxy.""" - - def __init__(self, subject): - """Wrap an object as a Proxy object""" - self.__subject = subject - - def __getattr__(self, name): - """Retrieve an attribute from the wrapped object. If the named - attribute doesn't exist, AttributeError is raised""" - return getattr(self.__subject, name) - - def get(self): - """Retrieve the entire wrapped object""" - return self.__subject - - def __cmp__(self, other): - if issubclass(other.__class__, self.__subject.__class__): - return cmp(self.__subject, other) - return cmp(self.__dict__, other.__dict__) - -# attempt to load the windows registry module: -can_read_reg = 0 -try: - import _winreg - - can_read_reg = 1 - hkey_mod = _winreg - - RegOpenKeyEx = _winreg.OpenKeyEx - RegEnumKey = _winreg.EnumKey - RegEnumValue = _winreg.EnumValue - RegQueryValueEx = _winreg.QueryValueEx - RegError = _winreg.error - -except ImportError: - try: - import win32api - import win32con - can_read_reg = 1 - hkey_mod = win32con - - RegOpenKeyEx = win32api.RegOpenKeyEx - RegEnumKey = win32api.RegEnumKey - RegEnumValue = win32api.RegEnumValue - RegQueryValueEx = win32api.RegQueryValueEx - RegError = win32api.error - - except ImportError: - class _NoError(Exception): - pass - RegError = _NoError - -if can_read_reg: - HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT - HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE - HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER - HKEY_USERS = hkey_mod.HKEY_USERS - - def RegGetValue(root, key): - """This utility function returns a value in the registry - without having to open the key first. Only available on - Windows platforms with a version of Python that can read the - registry. Returns the same thing as - SCons.Util.RegQueryValueEx, except you just specify the entire - path to the value, and don't have to bother opening the key - first. So: - - Instead of: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - r'SOFTWARE\Microsoft\Windows\CurrentVersion') - out = SCons.Util.RegQueryValueEx(k, - 'ProgramFilesDir') - - You can write: - out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, - r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir') - """ - # I would use os.path.split here, but it's not a filesystem - # path... - p = key.rfind('\\') + 1 - keyp = key[:p] - val = key[p:] - k = RegOpenKeyEx(root, keyp) - return RegQueryValueEx(k,val) - -if sys.platform == 'win32': - - def WhereIs(file, path=None, pathext=None, reject=[]): - if path is None: - try: - path = os.environ['PATH'] - except KeyError: - return None - if is_String(path): - path = string.split(path, os.pathsep) - if pathext is None: - try: - pathext = os.environ['PATHEXT'] - except KeyError: - pathext = '.COM;.EXE;.BAT;.CMD' - if is_String(pathext): - pathext = string.split(pathext, os.pathsep) - for ext in pathext: - if string.lower(ext) == string.lower(file[-len(ext):]): - pathext = [''] - break - if not is_List(reject) and not is_Tuple(reject): - reject = [reject] - for dir in path: - f = os.path.join(dir, file) - for ext in pathext: - fext = f + ext - if os.path.isfile(fext): - try: - reject.index(fext) - except ValueError: - return os.path.normpath(fext) - continue - return None - -elif os.name == 'os2': - - def WhereIs(file, path=None, pathext=None, reject=[]): - if path is None: - try: - path = os.environ['PATH'] - except KeyError: - return None - if is_String(path): - path = string.split(path, os.pathsep) - if pathext is None: - pathext = ['.exe', '.cmd'] - for ext in pathext: - if string.lower(ext) == string.lower(file[-len(ext):]): - pathext = [''] - break - if not is_List(reject) and not is_Tuple(reject): - reject = [reject] - for dir in path: - f = os.path.join(dir, file) - for ext in pathext: - fext = f + ext - if os.path.isfile(fext): - try: - reject.index(fext) - except ValueError: - return os.path.normpath(fext) - continue - return None - -else: - - def WhereIs(file, path=None, pathext=None, reject=[]): - import stat - if path is None: - try: - path = os.environ['PATH'] - except KeyError: - return None - if is_String(path): - path = string.split(path, os.pathsep) - if not is_List(reject) and not is_Tuple(reject): - reject = [reject] - for d in path: - f = os.path.join(d, file) - if os.path.isfile(f): - try: - st = os.stat(f) - except OSError: - # os.stat() raises OSError, not IOError if the file - # doesn't exist, so in this case we let IOError get - # raised so as to not mask possibly serious disk or - # network issues. - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0111: - try: - reject.index(f) - except ValueError: - return os.path.normpath(f) - continue - return None - -def PrependPath(oldpath, newpath, sep = os.pathsep, delete_existing=1): - """This prepends newpath elements to the given oldpath. Will only - add any particular path once (leaving the first one it encounters - and ignoring the rest, to preserve path order), and will - os.path.normpath and os.path.normcase all paths to help assure - this. This can also handle the case where the given old path - variable is a list instead of a string, in which case a list will - be returned instead of a string. - - Example: - Old Path: "/foo/bar:/foo" - New Path: "/biz/boom:/foo" - Result: "/biz/boom:/foo:/foo/bar" - - If delete_existing is 0, then adding a path that exists will - not move it to the beginning; it will stay where it is in the - list. - """ - - orig = oldpath - is_list = 1 - paths = orig - if not is_List(orig) and not is_Tuple(orig): - paths = string.split(paths, sep) - is_list = 0 - - if is_List(newpath) or is_Tuple(newpath): - newpaths = newpath - else: - newpaths = string.split(newpath, sep) - - if not delete_existing: - # First uniquify the old paths, making sure to - # preserve the first instance (in Unix/Linux, - # the first one wins), and remembering them in normpaths. - # Then insert the new paths at the head of the list - # if they're not already in the normpaths list. - result = [] - normpaths = [] - for path in paths: - if not path: - continue - normpath = os.path.normpath(os.path.normcase(path)) - if normpath not in normpaths: - result.append(path) - normpaths.append(normpath) - newpaths.reverse() # since we're inserting at the head - for path in newpaths: - if not path: - continue - normpath = os.path.normpath(os.path.normcase(path)) - if normpath not in normpaths: - result.insert(0, path) - normpaths.append(normpath) - paths = result - - else: - newpaths = newpaths + paths # prepend new paths - - normpaths = [] - paths = [] - # now we add them only if they are unique - for path in newpaths: - normpath = os.path.normpath(os.path.normcase(path)) - if path and not normpath in normpaths: - paths.append(path) - normpaths.append(normpath) - - if is_list: - return paths - else: - return string.join(paths, sep) - -def AppendPath(oldpath, newpath, sep = os.pathsep, delete_existing=1): - """This appends new path elements to the given old path. Will - only add any particular path once (leaving the last one it - encounters and ignoring the rest, to preserve path order), and - will os.path.normpath and os.path.normcase all paths to help - assure this. This can also handle the case where the given old - path variable is a list instead of a string, in which case a list - will be returned instead of a string. - - Example: - Old Path: "/foo/bar:/foo" - New Path: "/biz/boom:/foo" - Result: "/foo/bar:/biz/boom:/foo" - - If delete_existing is 0, then adding a path that exists - will not move it to the end; it will stay where it is in the list. - """ - - orig = oldpath - is_list = 1 - paths = orig - if not is_List(orig) and not is_Tuple(orig): - paths = string.split(paths, sep) - is_list = 0 - - if is_List(newpath) or is_Tuple(newpath): - newpaths = newpath - else: - newpaths = string.split(newpath, sep) - - if not delete_existing: - # add old paths to result, then - # add new paths if not already present - # (I thought about using a dict for normpaths for speed, - # but it's not clear hashing the strings would be faster - # than linear searching these typically short lists.) - result = [] - normpaths = [] - for path in paths: - if not path: - continue - result.append(path) - normpaths.append(os.path.normpath(os.path.normcase(path))) - for path in newpaths: - if not path: - continue - normpath = os.path.normpath(os.path.normcase(path)) - if normpath not in normpaths: - result.append(path) - normpaths.append(normpath) - paths = result - else: - # start w/ new paths, add old ones if not present, - # then reverse. - newpaths = paths + newpaths # append new paths - newpaths.reverse() - - normpaths = [] - paths = [] - # now we add them only if they are unique - for path in newpaths: - normpath = os.path.normpath(os.path.normcase(path)) - if path and not normpath in normpaths: - paths.append(path) - normpaths.append(normpath) - paths.reverse() - - if is_list: - return paths - else: - return string.join(paths, sep) - -if sys.platform == 'cygwin': - def get_native_path(path): - """Transforms an absolute path into a native path for the system. In - Cygwin, this converts from a Cygwin path to a Windows one.""" - return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '') -else: - def get_native_path(path): - """Transforms an absolute path into a native path for the system. - Non-Cygwin version, just leave the path alone.""" - return path - -display = DisplayEngine() - -def Split(arg): - if is_List(arg) or is_Tuple(arg): - return arg - elif is_String(arg): - return string.split(arg) - else: - return [arg] - -class CLVar(UserList): - """A class for command-line construction variables. - - This is a list that uses Split() to split an initial string along - white-space arguments, and similarly to split any strings that get - added. This allows us to Do the Right Thing with Append() and - Prepend() (as well as straight Python foo = env['VAR'] + 'arg1 - arg2') regardless of whether a user adds a list or a string to a - command-line construction variable. - """ - def __init__(self, seq = []): - UserList.__init__(self, Split(seq)) - def __add__(self, other): - return UserList.__add__(self, CLVar(other)) - def __radd__(self, other): - return UserList.__radd__(self, CLVar(other)) - def __coerce__(self, other): - return (self, CLVar(other)) - def __str__(self): - return string.join(self.data) - -# A dictionary that preserves the order in which items are added. -# Submitted by David Benjamin to ActiveState's Python Cookbook web site: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 -# Including fixes/enhancements from the follow-on discussions. -class OrderedDict(UserDict): - def __init__(self, dict = None): - self._keys = [] - UserDict.__init__(self, dict) - - def __delitem__(self, key): - UserDict.__delitem__(self, key) - self._keys.remove(key) - - def __setitem__(self, key, item): - UserDict.__setitem__(self, key, item) - if key not in self._keys: self._keys.append(key) - - def clear(self): - UserDict.clear(self) - self._keys = [] - - def copy(self): - dict = OrderedDict() - dict.update(self) - return dict - - def items(self): - return zip(self._keys, self.values()) - - def keys(self): - return self._keys[:] - - def popitem(self): - try: - key = self._keys[-1] - except IndexError: - raise KeyError('dictionary is empty') - - val = self[key] - del self[key] - - return (key, val) - - def setdefault(self, key, failobj = None): - UserDict.setdefault(self, key, failobj) - if key not in self._keys: self._keys.append(key) - - def update(self, dict): - for (key, val) in dict.items(): - self.__setitem__(key, val) - - def values(self): - return map(self.get, self._keys) - -class Selector(OrderedDict): - """A callable ordered dictionary that maps file suffixes to - dictionary values. We preserve the order in which items are added - so that get_suffix() calls always return the first suffix added.""" - def __call__(self, env, source): - try: - ext = source[0].suffix - except IndexError: - ext = "" - try: - return self[ext] - except KeyError: - # Try to perform Environment substitution on the keys of - # the dictionary before giving up. - s_dict = {} - for (k,v) in self.items(): - if not k is None: - s_k = env.subst(k) - if s_dict.has_key(s_k): - # We only raise an error when variables point - # to the same suffix. If one suffix is literal - # and a variable suffix contains this literal, - # the literal wins and we don't raise an error. - raise KeyError, (s_dict[s_k][0], k, s_k) - s_dict[s_k] = (k,v) - try: - return s_dict[ext][1] - except KeyError: - try: - return self[None] - except KeyError: - return None - - -if sys.platform == 'cygwin': - # On Cygwin, os.path.normcase() lies, so just report back the - # fact that the underlying Windows OS is case-insensitive. - def case_sensitive_suffixes(s1, s2): - return 0 -else: - def case_sensitive_suffixes(s1, s2): - return (os.path.normcase(s1) != os.path.normcase(s2)) - -def adjustixes(fname, pre, suf, ensure_suffix=False): - if pre: - path, fn = os.path.split(os.path.normpath(fname)) - if fn[:len(pre)] != pre: - fname = os.path.join(path, pre + fn) - # Only append a suffix if the suffix we're going to add isn't already - # there, and if either we've been asked to ensure the specific suffix - # is present or there's no suffix on it at all. - if suf and fname[-len(suf):] != suf and \ - (ensure_suffix or not splitext(fname)[1]): - fname = fname + suf - return fname - - - -# From Tim Peters, -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 -# ASPN: Python Cookbook: Remove duplicates from a sequence -# (Also in the printed Python Cookbook.) - -def unique(s): - """Return a list of the elements in s, but without duplicates. - - For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], - unique("abcabc") some permutation of ["a", "b", "c"], and - unique(([1, 2], [2, 3], [1, 2])) some permutation of - [[2, 3], [1, 2]]. - - For best speed, all sequence elements should be hashable. Then - unique() will usually work in linear time. - - If not possible, the sequence elements should enjoy a total - ordering, and if list(s).sort() doesn't raise TypeError it's - assumed that they do enjoy a total ordering. Then unique() will - usually work in O(N*log2(N)) time. - - If that's not possible either, the sequence elements must support - equality-testing. Then unique() will usually work in quadratic - time. - """ - - n = len(s) - if n == 0: - return [] - - # Try using a dict first, as that's the fastest and will usually - # work. If it doesn't work, it will usually fail quickly, so it - # usually doesn't cost much to *try* it. It requires that all the - # sequence elements be hashable, and support equality comparison. - u = {} - try: - for x in s: - u[x] = 1 - except TypeError: - pass # move on to the next method - else: - return u.keys() - del u - - # We can't hash all the elements. Second fastest is to sort, - # which brings the equal elements together; then duplicates are - # easy to weed out in a single pass. - # NOTE: Python's list.sort() was designed to be efficient in the - # presence of many duplicate elements. This isn't true of all - # sort functions in all languages or libraries, so this approach - # is more effective in Python than it may be elsewhere. - try: - t = list(s) - t.sort() - except TypeError: - pass # move on to the next method - else: - assert n > 0 - last = t[0] - lasti = i = 1 - while i < n: - if t[i] != last: - t[lasti] = last = t[i] - lasti = lasti + 1 - i = i + 1 - return t[:lasti] - del t - - # Brute force is all that's left. - u = [] - for x in s: - if x not in u: - u.append(x) - return u - - - -# From Alex Martelli, -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 -# ASPN: Python Cookbook: Remove duplicates from a sequence -# First comment, dated 2001/10/13. -# (Also in the printed Python Cookbook.) - -def uniquer(seq, idfun=None): - if idfun is None: - def idfun(x): return x - seen = {} - result = [] - for item in seq: - marker = idfun(item) - # in old Python versions: - # if seen.has_key(marker) - # but in new ones: - if marker in seen: continue - seen[marker] = 1 - result.append(item) - return result - -# A more efficient implementation of Alex's uniquer(), this avoids the -# idfun() argument and function-call overhead by assuming that all -# items in the sequence are hashable. - -def uniquer_hashables(seq): - seen = {} - result = [] - for item in seq: - #if not item in seen: - if not seen.has_key(item): - seen[item] = 1 - result.append(item) - return result - - - -# Much of the logic here was originally based on recipe 4.9 from the -# Python CookBook, but we had to dumb it way down for Python 1.5.2. -class LogicalLines: - - def __init__(self, fileobj): - self.fileobj = fileobj - - def readline(self): - result = [] - while 1: - line = self.fileobj.readline() - if not line: - break - if line[-2:] == '\\\n': - result.append(line[:-2]) - else: - result.append(line) - break - return string.join(result, '') - - def readlines(self): - result = [] - while 1: - line = self.readline() - if not line: - break - result.append(line) - return result - - - -class UniqueList(UserList): - def __init__(self, seq = []): - UserList.__init__(self, seq) - self.unique = True - def __make_unique(self): - if not self.unique: - self.data = uniquer_hashables(self.data) - self.unique = True - def __lt__(self, other): - self.__make_unique() - return UserList.__lt__(self, other) - def __le__(self, other): - self.__make_unique() - return UserList.__le__(self, other) - def __eq__(self, other): - self.__make_unique() - return UserList.__eq__(self, other) - def __ne__(self, other): - self.__make_unique() - return UserList.__ne__(self, other) - def __gt__(self, other): - self.__make_unique() - return UserList.__gt__(self, other) - def __ge__(self, other): - self.__make_unique() - return UserList.__ge__(self, other) - def __cmp__(self, other): - self.__make_unique() - return UserList.__cmp__(self, other) - def __len__(self): - self.__make_unique() - return UserList.__len__(self) - def __getitem__(self, i): - self.__make_unique() - return UserList.__getitem__(self, i) - def __setitem__(self, i, item): - UserList.__setitem__(self, i, item) - self.unique = False - def __getslice__(self, i, j): - self.__make_unique() - return UserList.__getslice__(self, i, j) - def __setslice__(self, i, j, other): - UserList.__setslice__(self, i, j, other) - self.unique = False - def __add__(self, other): - result = UserList.__add__(self, other) - result.unique = False - return result - def __radd__(self, other): - result = UserList.__radd__(self, other) - result.unique = False - return result - def __iadd__(self, other): - result = UserList.__iadd__(self, other) - result.unique = False - return result - def __mul__(self, other): - result = UserList.__mul__(self, other) - result.unique = False - return result - def __rmul__(self, other): - result = UserList.__rmul__(self, other) - result.unique = False - return result - def __imul__(self, other): - result = UserList.__imul__(self, other) - result.unique = False - return result - def append(self, item): - UserList.append(self, item) - self.unique = False - def insert(self, i): - UserList.insert(self, i) - self.unique = False - def count(self, item): - self.__make_unique() - return UserList.count(self, item) - def index(self, item): - self.__make_unique() - return UserList.index(self, item) - def reverse(self): - self.__make_unique() - UserList.reverse(self) - def sort(self, *args, **kwds): - self.__make_unique() - #return UserList.sort(self, *args, **kwds) - return apply(UserList.sort, (self,)+args, kwds) - def extend(self, other): - UserList.extend(self, other) - self.unique = False - - - -class Unbuffered: - """ - A proxy class that wraps a file object, flushing after every write, - and delegating everything else to the wrapped object. - """ - def __init__(self, file): - self.file = file - def write(self, arg): - try: - self.file.write(arg) - self.file.flush() - except IOError: - # Stdout might be connected to a pipe that has been closed - # by now. The most likely reason for the pipe being closed - # is that the user has press ctrl-c. It this is the case, - # then SCons is currently shutdown. We therefore ignore - # IOError's here so that SCons can continue and shutdown - # properly so that the .sconsign is correctly written - # before SCons exits. - pass - def __getattr__(self, attr): - return getattr(self.file, attr) - -def make_path_relative(path): - """ makes an absolute path name to a relative pathname. - """ - if os.path.isabs(path): - drive_s,path = os.path.splitdrive(path) - - import re - if not drive_s: - path=re.compile("/*(.*)").findall(path)[0] - else: - path=path[1:] - - assert( not os.path.isabs( path ) ), path - return path - - - -# The original idea for AddMethod() and RenameFunction() come from the -# following post to the ActiveState Python Cookbook: -# -# ASPN: Python Cookbook : Install bound methods in an instance -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 -# -# That code was a little fragile, though, so the following changes -# have been wrung on it: -# -# * Switched the installmethod() "object" and "function" arguments, -# so the order reflects that the left-hand side is the thing being -# "assigned to" and the right-hand side is the value being assigned. -# -# * Changed explicit type-checking to the "try: klass = object.__class__" -# block in installmethod() below so that it still works with the -# old-style classes that SCons uses. -# -# * Replaced the by-hand creation of methods and functions with use of -# the "new" module, as alluded to in Alex Martelli's response to the -# following Cookbook post: -# -# ASPN: Python Cookbook : Dynamically added methods to a class -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 - -def AddMethod(object, function, name = None): - """ - Adds either a bound method to an instance or an unbound method to - a class. If name is ommited the name of the specified function - is used by default. - Example: - a = A() - def f(self, x, y): - self.z = x + y - AddMethod(f, A, "add") - a.add(2, 4) - print a.z - AddMethod(lambda self, i: self.l[i], a, "listIndex") - print a.listIndex(5) - """ - import new - - if name is None: - name = function.func_name - else: - function = RenameFunction(function, name) - - try: - klass = object.__class__ - except AttributeError: - # "object" is really a class, so it gets an unbound method. - object.__dict__[name] = new.instancemethod(function, None, object) - else: - # "object" is really an instance, so it gets a bound method. - object.__dict__[name] = new.instancemethod(function, object, klass) - -def RenameFunction(function, name): - """ - Returns a function identical to the specified function, but with - the specified name. - """ - import new - - # Compatibility for Python 1.5 and 2.1. Can be removed in favor of - # passing function.func_defaults directly to new.function() once - # we base on Python 2.2 or later. - func_defaults = function.func_defaults - if func_defaults is None: - func_defaults = () - - return new.function(function.func_code, - function.func_globals, - name, - func_defaults) - - -md5 = False -def MD5signature(s): - return str(s) - -def MD5filesignature(fname, chunksize=65536): - f = open(fname, "rb") - result = f.read() - f.close() - return result - -try: - import hashlib -except ImportError: - pass -else: - if hasattr(hashlib, 'md5'): - md5 = True - def MD5signature(s): - m = hashlib.md5() - m.update(str(s)) - return m.hexdigest() - - def MD5filesignature(fname, chunksize=65536): - m = hashlib.md5() - f = open(fname, "rb") - while 1: - blck = f.read(chunksize) - if not blck: - break - m.update(str(blck)) - f.close() - return m.hexdigest() - -def MD5collect(signatures): - """ - Collects a list of signatures into an aggregate signature. - - signatures - a list of signatures - returns - the aggregate signature - """ - if len(signatures) == 1: - return signatures[0] - else: - return MD5signature(string.join(signatures, ', ')) - - - -# From Dinu C. Gherman, -# Python Cookbook, second edition, recipe 6.17, p. 277. -# Also: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 -# ASPN: Python Cookbook: Null Object Design Pattern - -class Null: - """ Null objects always and reliably "do nothging." """ - - def __new__(cls, *args, **kwargs): - if not '_inst' in vars(cls): - #cls._inst = type.__new__(cls, *args, **kwargs) - cls._inst = apply(type.__new__, (cls,) + args, kwargs) - return cls._inst - def __init__(self, *args, **kwargs): - pass - def __call__(self, *args, **kwargs): - return self - def __repr__(self): - return "Null()" - def __nonzero__(self): - return False - def __getattr__(self, mname): - return self - def __setattr__(self, name, value): - return self - def __delattr__(self, name): - return self - - - -del __revision__ diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/BoolVariable.py b/tools/scons/scons-local-1.2.0/SCons/Variables/BoolVariable.py deleted file mode 100644 index 3aaf694c42..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/BoolVariable.py +++ /dev/null @@ -1,85 +0,0 @@ -"""engine.SCons.Variables.BoolVariable - -This file defines the option type for SCons implementing true/false values. - -Usage example: - - opts = Variables() - opts.Add(BoolVariable('embedded', 'build for an embedded system', 0)) - ... - if env['embedded'] == 1: - ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/BoolVariable.py 3842 2008/12/20 22:59:52 scons" - -__all__ = ['BoolVariable',] - -import string - -import SCons.Errors - -__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) -__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') - - -def _text2bool(val): - """ - Converts strings to True/False depending on the 'truth' expressed by - the string. If the string can't be converted, the original value - will be returned. - - See '__true_strings' and '__false_strings' for values considered - 'true' or 'false respectivly. - - This is usable as 'converter' for SCons' Variables. - """ - lval = string.lower(val) - if lval in __true_strings: return True - if lval in __false_strings: return False - raise ValueError("Invalid value for boolean option: %s" % val) - - -def _validator(key, val, env): - """ - Validates the given value to be either '0' or '1'. - - This is usable as 'validator' for SCons' Variables. - """ - if not env[key] in (True, False): - raise SCons.Errors.UserError( - 'Invalid value for boolean option %s: %s' % (key, env[key])) - - -def BoolVariable(key, help, default): - """ - The input parameters describe a boolen option, thus they are - returned with the correct converter and validator appended. The - 'help' text will by appended by '(yes|no) to show the valid - valued. The result is usable for input to opts.Add(). - """ - return (key, '%s (yes|no)' % help, default, - _validator, _text2bool) diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/EnumVariable.py b/tools/scons/scons-local-1.2.0/SCons/Variables/EnumVariable.py deleted file mode 100644 index 6d2252ddf6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/EnumVariable.py +++ /dev/null @@ -1,101 +0,0 @@ -"""engine.SCons.Variables.EnumVariable - -This file defines the option type for SCons allowing only specified -input-values. - -Usage example: - - opts = Variables() - opts.Add(EnumVariable('debug', 'debug output and symbols', 'no', - allowed_values=('yes', 'no', 'full'), - map={}, ignorecase=2)) - ... - if env['debug'] == 'full': - ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/EnumVariable.py 3842 2008/12/20 22:59:52 scons" - -__all__ = ['EnumVariable',] - -import string - -import SCons.Errors - -def _validator(key, val, env, vals): - if not val in vals: - raise SCons.Errors.UserError( - 'Invalid value for option %s: %s' % (key, val)) - - -def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): - """ - The input parameters describe a option with only certain values - allowed. They are returned with an appropriate converter and - validator appended. The result is usable for input to - Variables.Add(). - - 'key' and 'default' are the values to be passed on to Variables.Add(). - - 'help' will be appended by the allowed values automatically - - 'allowed_values' is a list of strings, which are allowed as values - for this option. - - The 'map'-dictionary may be used for converting the input value - into canonical values (eg. for aliases). - - 'ignorecase' defines the behaviour of the validator: - - If ignorecase == 0, the validator/converter are case-sensitive. - If ignorecase == 1, the validator/converter are case-insensitive. - If ignorecase == 2, the validator/converter is case-insensitive and - the converted value will always be lower-case. - - The 'validator' tests whether the value is in the list of allowed - values. The 'converter' converts input values according to the - given 'map'-dictionary (unmapped input values are returned - unchanged). - """ - help = '%s (%s)' % (help, string.join(allowed_values, '|')) - # define validator - if ignorecase >= 1: - validator = lambda key, val, env, vals=allowed_values: \ - _validator(key, string.lower(val), env, vals) - else: - validator = lambda key, val, env, vals=allowed_values: \ - _validator(key, val, env, vals) - # define converter - if ignorecase == 2: - converter = lambda val, map=map: \ - string.lower(map.get(string.lower(val), val)) - elif ignorecase == 1: - converter = lambda val, map=map: \ - map.get(string.lower(val), val) - else: - converter = lambda val, map=map: \ - map.get(val, val) - return (key, help, default, validator, converter) diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/ListVariable.py b/tools/scons/scons-local-1.2.0/SCons/Variables/ListVariable.py deleted file mode 100644 index 72e6fec4e9..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/ListVariable.py +++ /dev/null @@ -1,133 +0,0 @@ -"""engine.SCons.Variables.ListVariable - -This file defines the option type for SCons implementing 'lists'. - -A 'list' option may either be 'all', 'none' or a list of names -separated by comma. After the option has been processed, the option -value holds either the named list elements, all list elemens or no -list elements at all. - -Usage example: - - list_of_libs = Split('x11 gl qt ical') - - opts = Variables() - opts.Add(ListVariable('shared', - 'libraries to build as shared libraries', - 'all', - elems = list_of_libs)) - ... - for lib in list_of_libs: - if lib in env['shared']: - env.SharedObject(...) - else: - env.Object(...) -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/ListVariable.py 3842 2008/12/20 22:59:52 scons" - -# Know Bug: This should behave like a Set-Type, but does not really, -# since elements can occur twice. - -__all__ = ['ListVariable',] - -import string -import UserList - -import SCons.Util - - -class _ListVariable(UserList.UserList): - def __init__(self, initlist=[], allowedElems=[]): - UserList.UserList.__init__(self, filter(None, initlist)) - self.allowedElems = allowedElems[:] - self.allowedElems.sort() - - def __cmp__(self, other): - raise NotImplementedError - def __eq__(self, other): - raise NotImplementedError - def __ge__(self, other): - raise NotImplementedError - def __gt__(self, other): - raise NotImplementedError - def __le__(self, other): - raise NotImplementedError - def __lt__(self, other): - raise NotImplementedError - def __str__(self): - if len(self) == 0: - return 'none' - self.data.sort() - if self.data == self.allowedElems: - return 'all' - else: - return string.join(self, ',') - def prepare_to_store(self): - return self.__str__() - -def _converter(val, allowedElems, mapdict): - """ - """ - if val == 'none': - val = [] - elif val == 'all': - val = allowedElems - else: - val = filter(None, string.split(val, ',')) - val = map(lambda v, m=mapdict: m.get(v, v), val) - notAllowed = filter(lambda v, aE=allowedElems: not v in aE, val) - if notAllowed: - raise ValueError("Invalid value(s) for option: %s" % - string.join(notAllowed, ',')) - return _ListVariable(val, allowedElems) - - -## def _validator(key, val, env): -## """ -## """ -## # todo: write validater for pgk list -## return 1 - - -def ListVariable(key, help, default, names, map={}): - """ - The input parameters describe a 'package list' option, thus they - are returned with the correct converter and validater appended. The - result is usable for input to opts.Add() . - - A 'package list' option may either be 'all', 'none' or a list of - package names (separated by space). - """ - names_str = 'allowed names: %s' % string.join(names, ' ') - if SCons.Util.is_List(default): - default = string.join(default, ',') - help = string.join( - (help, '(all|none|comma-separated list of names)', names_str), - '\n ') - return (key, help, default, - None, #_validator, - lambda val, elems=names, m=map: _converter(val, elems, m)) diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/PackageVariable.py b/tools/scons/scons-local-1.2.0/SCons/Variables/PackageVariable.py deleted file mode 100644 index 5823bf568b..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/PackageVariable.py +++ /dev/null @@ -1,103 +0,0 @@ -"""engine.SCons.Variables.PackageVariable - -This file defines the option type for SCons implementing 'package -activation'. - -To be used whenever a 'package' may be enabled/disabled and the -package path may be specified. - -Usage example: - - Examples: - x11=no (disables X11 support) - x11=yes (will search for the package installation dir) - x11=/usr/local/X11 (will check this path for existance) - - To replace autoconf's --with-xxx=yyy - - opts = Variables() - opts.Add(PackageVariable('x11', - 'use X11 installed here (yes = search some places', - 'yes')) - ... - if env['x11'] == True: - dir = ... search X11 in some standard places ... - env['x11'] = dir - if env['x11']: - ... build with x11 ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/PackageVariable.py 3842 2008/12/20 22:59:52 scons" - -__all__ = ['PackageVariable',] - -import string - -import SCons.Errors - -__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') -__disable_strings = ('0', 'no', 'false', 'off', 'disable') - -def _converter(val): - """ - """ - lval = string.lower(val) - if lval in __enable_strings: return True - if lval in __disable_strings: return False - #raise ValueError("Invalid value for boolean option: %s" % val) - return val - - -def _validator(key, val, env, searchfunc): - # NB: searchfunc is currenty undocumented and unsupported - """ - """ - # todo: write validator, check for path - import os - if env[key] is True: - if searchfunc: - env[key] = searchfunc(key, val) - elif env[key] and not os.path.exists(val): - raise SCons.Errors.UserError( - 'Path does not exist for option %s: %s' % (key, val)) - - -def PackageVariable(key, help, default, searchfunc=None): - # NB: searchfunc is currenty undocumented and unsupported - """ - The input parameters describe a 'package list' option, thus they - are returned with the correct converter and validator appended. The - result is usable for input to opts.Add() . - - A 'package list' option may either be 'all', 'none' or a list of - package names (seperated by space). - """ - help = string.join( - (help, '( yes | no | /path/to/%s )' % key), - '\n ') - return (key, help, default, - lambda k, v, e, f=searchfunc: _validator(k,v,e,f), - _converter) diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/PathVariable.py b/tools/scons/scons-local-1.2.0/SCons/Variables/PathVariable.py deleted file mode 100644 index 752e34790b..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/PathVariable.py +++ /dev/null @@ -1,141 +0,0 @@ -"""SCons.Variables.PathVariable - -This file defines an option type for SCons implementing path settings. - -To be used whenever a a user-specified path override should be allowed. - -Arguments to PathVariable are: - option-name = name of this option on the command line (e.g. "prefix") - option-help = help string for option - option-dflt = default value for this option - validator = [optional] validator for option value. Predefined - validators are: - - PathAccept -- accepts any path setting; no validation - PathIsDir -- path must be an existing directory - PathIsDirCreate -- path must be a dir; will create - PathIsFile -- path must be a file - PathExists -- path must exist (any type) [default] - - The validator is a function that is called and which - should return True or False to indicate if the path - is valid. The arguments to the validator function - are: (key, val, env). The key is the name of the - option, the val is the path specified for the option, - and the env is the env to which the Otions have been - added. - -Usage example: - - Examples: - prefix=/usr/local - - opts = Variables() - - opts = Variables() - opts.Add(PathVariable('qtdir', - 'where the root of Qt is installed', - qtdir, PathIsDir)) - opts.Add(PathVariable('qt_includes', - 'where the Qt includes are installed', - '$qtdir/includes', PathIsDirCreate)) - opts.Add(PathVariable('qt_libraries', - 'where the Qt library is installed', - '$qtdir/lib')) - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/PathVariable.py 3842 2008/12/20 22:59:52 scons" - -__all__ = ['PathVariable',] - -import os -import os.path - -import SCons.Errors - -class _PathVariableClass: - - def PathAccept(self, key, val, env): - """Accepts any path, no checking done.""" - pass - - def PathIsDir(self, key, val, env): - """Validator to check if Path is a directory.""" - if not os.path.isdir(val): - if os.path.isfile(val): - m = 'Directory path for option %s is a file: %s' - else: - m = 'Directory path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def PathIsDirCreate(self, key, val, env): - """Validator to check if Path is a directory, - creating it if it does not exist.""" - if os.path.isfile(val): - m = 'Path for option %s is a file, not a directory: %s' - raise SCons.Errors.UserError(m % (key, val)) - if not os.path.isdir(val): - os.makedirs(val) - - def PathIsFile(self, key, val, env): - """validator to check if Path is a file""" - if not os.path.isfile(val): - if os.path.isdir(val): - m = 'File path for option %s is a directory: %s' - else: - m = 'File path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def PathExists(self, key, val, env): - """validator to check if Path exists""" - if not os.path.exists(val): - m = 'Path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def __call__(self, key, help, default, validator=None): - # NB: searchfunc is currenty undocumented and unsupported - """ - The input parameters describe a 'path list' option, thus they - are returned with the correct converter and validator appended. The - result is usable for input to opts.Add() . - - The 'default' option specifies the default path to use if the - user does not specify an override with this option. - - validator is a validator, see this file for examples - """ - if validator is None: - validator = self.PathExists - - if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): - return (key, '%s ( /path/to/%s )' % (help, key[0]), default, - validator, None) - else: - return (key, '%s ( /path/to/%s )' % (help, key), default, - validator, None) - -PathVariable = _PathVariableClass() diff --git a/tools/scons/scons-local-1.2.0/SCons/Variables/__init__.py b/tools/scons/scons-local-1.2.0/SCons/Variables/__init__.py deleted file mode 100644 index 5d73e10dd0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Variables/__init__.py +++ /dev/null @@ -1,304 +0,0 @@ -"""engine.SCons.Variables - -This file defines the Variables class that is used to add user-friendly -customizable variables to an SCons build. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/Variables/__init__.py 3842 2008/12/20 22:59:52 scons" - -import os.path -import string -import sys - -import SCons.Environment -import SCons.Errors -import SCons.Util -import SCons.Warnings - -from BoolVariable import BoolVariable # okay -from EnumVariable import EnumVariable # okay -from ListVariable import ListVariable # naja -from PackageVariable import PackageVariable # naja -from PathVariable import PathVariable # okay - - -class Variables: - instance=None - - """ - Holds all the options, updates the environment with the variables, - and renders the help text. - """ - def __init__(self, files=[], args={}, is_global=1): - """ - files - [optional] List of option configuration files to load - (backward compatibility) If a single string is passed it is - automatically placed in a file list - """ - self.options = [] - self.args = args - if not SCons.Util.is_List(files): - if files: - files = [ files ] - else: - files = [] - self.files = files - self.unknown = {} - - # create the singleton instance - if is_global: - self=Variables.instance - - if not Variables.instance: - Variables.instance=self - - def _do_add(self, key, help="", default=None, validator=None, converter=None): - class Variable: - pass - - option = Variable() - - # if we get a list or a tuple, we take the first element as the - # option key and store the remaining in aliases. - if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): - option.key = key[0] - option.aliases = key[1:] - else: - option.key = key - option.aliases = [ key ] - option.help = help - option.default = default - option.validator = validator - option.converter = converter - - self.options.append(option) - - def keys(self): - """ - Returns the keywords for the options - """ - return map(lambda o: o.key, self.options) - - def Add(self, key, help="", default=None, validator=None, converter=None, **kw): - """ - Add an option. - - key - the name of the variable, or a list or tuple of arguments - help - optional help text for the options - default - optional default value - validator - optional function that is called to validate the option's value - Called with (key, value, environment) - converter - optional function that is called to convert the option's value before - putting it in the environment. - """ - - if SCons.Util.is_List(key) or type(key) == type(()): - apply(self._do_add, key) - return - - if not SCons.Util.is_String(key) or \ - not SCons.Environment.is_valid_construction_var(key): - raise SCons.Errors.UserError, "Illegal Variables.Add() key `%s'" % str(key) - - self._do_add(key, help, default, validator, converter) - - def AddVariables(self, *optlist): - """ - Add a list of options. - - Each list element is a tuple/list of arguments to be passed on - to the underlying method for adding options. - - Example: - opt.AddVariables( - ('debug', '', 0), - ('CC', 'The C compiler'), - ('VALIDATE', 'An option for testing validation', 'notset', - validator, None), - ) - """ - for o in optlist: - apply(self._do_add, o) - - - def Update(self, env, args=None): - """ - Update an environment with the option variables. - - env - the environment to update. - """ - - values = {} - - # first set the defaults: - for option in self.options: - if not option.default is None: - values[option.key] = option.default - - # next set the value specified in the options file - for filename in self.files: - if os.path.exists(filename): - dir = os.path.split(os.path.abspath(filename))[0] - if dir: - sys.path.insert(0, dir) - try: - values['__name__'] = filename - execfile(filename, {}, values) - finally: - if dir: - del sys.path[0] - del values['__name__'] - - # set the values specified on the command line - if args is None: - args = self.args - - for arg, value in args.items(): - added = False - for option in self.options: - if arg in option.aliases + [ option.key ]: - values[option.key] = value - added = True - if not added: - self.unknown[arg] = value - - # put the variables in the environment: - # (don't copy over variables that are not declared as options) - for option in self.options: - try: - env[option.key] = values[option.key] - except KeyError: - pass - - # Call the convert functions: - for option in self.options: - if option.converter and values.has_key(option.key): - value = env.subst('${%s}'%option.key) - try: - try: - env[option.key] = option.converter(value) - except TypeError: - env[option.key] = option.converter(value, env) - except ValueError, x: - raise SCons.Errors.UserError, 'Error converting option: %s\n%s'%(option.key, x) - - - # Finally validate the values: - for option in self.options: - if option.validator and values.has_key(option.key): - option.validator(option.key, env.subst('${%s}'%option.key), env) - - def UnknownVariables(self): - """ - Returns any options in the specified arguments lists that - were not known, declared options in this object. - """ - return self.unknown - - def Save(self, filename, env): - """ - Saves all the options in the given file. This file can - then be used to load the options next run. This can be used - to create an option cache file. - - filename - Name of the file to save into - env - the environment get the option values from - """ - - # Create the file and write out the header - try: - fh = open(filename, 'w') - - try: - # Make an assignment in the file for each option - # within the environment that was assigned a value - # other than the default. - for option in self.options: - try: - value = env[option.key] - try: - prepare = value.prepare_to_store - except AttributeError: - try: - eval(repr(value)) - except KeyboardInterrupt: - raise - except: - # Convert stuff that has a repr() that - # cannot be evaluated into a string - value = SCons.Util.to_String(value) - else: - value = prepare() - - defaultVal = env.subst(SCons.Util.to_String(option.default)) - if option.converter: - defaultVal = option.converter(defaultVal) - - if str(env.subst('${%s}' % option.key)) != str(defaultVal): - fh.write('%s = %s\n' % (option.key, repr(value))) - except KeyError: - pass - finally: - fh.close() - - except IOError, x: - raise SCons.Errors.UserError, 'Error writing options to file: %s\n%s' % (filename, x) - - def GenerateHelpText(self, env, sort=None): - """ - Generate the help text for the options. - - env - an environment that is used to get the current values - of the options. - """ - - if sort: - options = self.options[:] - options.sort(lambda x,y,func=sort: func(x.key,y.key)) - else: - options = self.options - - def format(opt, self=self, env=env): - if env.has_key(opt.key): - actual = env.subst('${%s}' % opt.key) - else: - actual = None - return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) - lines = filter(None, map(format, options)) - - return string.join(lines, '') - - format = '\n%s: %s\n default: %s\n actual: %s\n' - format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' - - def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]): - # Don't display the key name itself as an alias. - aliases = filter(lambda a, k=key: a != k, aliases) - if len(aliases)==0: - return self.format % (key, help, default, actual) - else: - return self.format_ % (key, help, default, actual, aliases) - diff --git a/tools/scons/scons-local-1.2.0/SCons/Warnings.py b/tools/scons/scons-local-1.2.0/SCons/Warnings.py deleted file mode 100644 index 296d6d3c6f..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/Warnings.py +++ /dev/null @@ -1,193 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -"""SCons.Warnings - -This file implements the warnings framework for SCons. - -""" - -__revision__ = "src/engine/SCons/Warnings.py 3842 2008/12/20 22:59:52 scons" - -import string -import sys - -import SCons.Errors - -class Warning(SCons.Errors.UserError): - pass - - -# NOTE: If you add a new warning class, add it to the man page, too! - -class CacheWriteErrorWarning(Warning): - pass - -class CorruptSConsignWarning(Warning): - pass - -class DependencyWarning(Warning): - pass - -class DeprecatedWarning(Warning): - pass - -class DeprecatedCopyWarning(DeprecatedWarning): - pass - -class DeprecatedOptionsWarning(DeprecatedWarning): - pass - -class DeprecatedSourceSignaturesWarning(DeprecatedWarning): - pass - -class DeprecatedTargetSignaturesWarning(DeprecatedWarning): - pass - -class DuplicateEnvironmentWarning(Warning): - pass - -class FutureReservedVariableWarning(Warning): - pass - -class LinkWarning(Warning): - pass - -class MisleadingKeywordsWarning(Warning): - pass - -class MissingSConscriptWarning(Warning): - pass - -class NoMD5ModuleWarning(Warning): - pass - -class NoMetaclassSupportWarning(Warning): - pass - -class NoObjectCountWarning(Warning): - pass - -class NoParallelSupportWarning(Warning): - pass - -class PythonVersionWarning(DeprecatedWarning): - pass - -class ReservedVariableWarning(Warning): - pass - -class StackSizeWarning(Warning): - pass - -class FortranCxxMixWarning(LinkWarning): - pass - -_warningAsException = 0 - -# The below is a list of 2-tuples. The first element is a class object. -# The second element is true if that class is enabled, false if it is disabled. -_enabled = [] - -_warningOut = None - -def suppressWarningClass(clazz): - """Suppresses all warnings that are of type clazz or - derived from clazz.""" - _enabled.insert(0, (clazz, 0)) - -def enableWarningClass(clazz): - """Suppresses all warnings that are of type clazz or - derived from clazz.""" - _enabled.insert(0, (clazz, 1)) - -def warningAsException(flag=1): - """Turn warnings into exceptions. Returns the old value of the flag.""" - global _warningAsException - old = _warningAsException - _warningAsException = flag - return old - -def warn(clazz, *args): - global _enabled, _warningAsException, _warningOut - - warning = clazz(args) - for clazz, flag in _enabled: - if isinstance(warning, clazz): - if flag: - if _warningAsException: - raise warning - - if _warningOut: - _warningOut(warning) - break - -def process_warn_strings(arguments): - """Process string specifications of enabling/disabling warnings, - as passed to the --warn option or the SetOption('warn') function. - - - An argument to this option should be of the form <warning-class> - or no-<warning-class>. The warning class is munged in order - to get an actual class name from the classes above, which we - need to pass to the {enable,disable}WarningClass() functions. - The supplied <warning-class> is split on hyphens, each element - is capitalized, then smushed back together. Then the string - "Warning" is appended to get the class name. - - For example, 'deprecated' will enable the DeprecatedWarning - class. 'no-dependency' will disable the .DependencyWarning - class. - - As a special case, --warn=all and --warn=no-all will enable or - disable (respectively) the base Warning class of all warnings. - - """ - - def _capitalize(s): - if s[:5] == "scons": - return "SCons" + s[5:] - else: - return string.capitalize(s) - - for arg in arguments: - - elems = string.split(string.lower(arg), '-') - enable = 1 - if elems[0] == 'no': - enable = 0 - del elems[0] - - if len(elems) == 1 and elems[0] == 'all': - class_name = "Warning" - else: - class_name = string.join(map(_capitalize, elems), '') + "Warning" - try: - clazz = globals()[class_name] - except KeyError: - sys.stderr.write("No warning type: '%s'\n" % arg) - else: - if enable: - enableWarningClass(clazz) - else: - suppressWarningClass(clazz) diff --git a/tools/scons/scons-local-1.2.0/SCons/__init__.py b/tools/scons/scons-local-1.2.0/SCons/__init__.py deleted file mode 100644 index c006699bbc..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SCons - -The main package for the SCons software construction utility. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/__init__.py 3842 2008/12/20 22:59:52 scons" - -__version__ = "1.2.0" - -__build__ = "r3842" - -__buildsys__ = "scons-dev" - -__date__ = "2008/12/20 22:59:52" - -__developer__ = "scons" - -# make sure compatibility is always in place -import SCons.compat diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/__init__.py b/tools/scons/scons-local-1.2.0/SCons/compat/__init__.py deleted file mode 100644 index c285db3576..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/__init__.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__doc__ = """ -SCons compatibility package for old Python versions - -This subpackage holds modules that provide backwards-compatible -implementations of various things that we'd like to use in SCons but which -only show up in later versions of Python than the early, old version(s) -we still support. - -Other code will not generally reference things in this package through -the SCons.compat namespace. The modules included here add things to -the __builtin__ namespace or the global module list so that the rest -of our code can use the objects and names imported here regardless of -Python version. - -Simply enough, things that go in the __builtin__ name space come from -our builtins module. - -The rest of the things here will be in individual compatibility modules -that are either: 1) suitably modified copies of the future modules that -we want to use; or 2) backwards compatible re-implementations of the -specific portions of a future module's API that we want to use. - -GENERAL WARNINGS: Implementations of functions in the SCons.compat -modules are *NOT* guaranteed to be fully compliant with these functions in -later versions of Python. We are only concerned with adding functionality -that we actually use in SCons, so be wary if you lift this code for -other uses. (That said, making these more nearly the same as later, -official versions is still a desirable goal, we just don't need to be -obsessive about it.) - -We name the compatibility modules with an initial '_scons_' (for example, -_scons_subprocess.py is our compatibility module for subprocess) so -that we can still try to import the real module name and fall back to -our compatibility module if we get an ImportError. The import_as() -function defined below loads the module as the "real" name (without the -'_scons'), after which all of the "import {module}" statements in the -rest of our code will find our pre-loaded compatibility module. -""" - -__revision__ = "src/engine/SCons/compat/__init__.py 3842 2008/12/20 22:59:52 scons" - -def import_as(module, name): - """ - Imports the specified module (from our local directory) as the - specified name. - """ - import imp - import os.path - dir = os.path.split(__file__)[0] - file, filename, suffix_mode_type = imp.find_module(module, [dir]) - imp.load_module(name, file, filename, suffix_mode_type) - -import builtins - -try: - import hashlib -except ImportError: - # Pre-2.5 Python has no hashlib module. - try: - import_as('_scons_hashlib', 'hashlib') - except ImportError: - # If we failed importing our compatibility module, it probably - # means this version of Python has no md5 module. Don't do - # anything and let the higher layer discover this fact, so it - # can fall back to using timestamp. - pass - -try: - set -except NameError: - # Pre-2.4 Python has no native set type - try: - # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module - # that we grabbed. - import_as('_scons_sets', 'sets') - except (ImportError, SyntaxError): - # Python 1.5 (ImportError, no __future_ module) and 2.1 - # (SyntaxError, no generators in __future__) will blow up - # trying to import the 2.[45] sets module, so back off to a - # custom sets module that can be discarded easily when we - # stop supporting those versions. - import_as('_scons_sets15', 'sets') - import __builtin__ - import sets - __builtin__.set = sets.Set - -import fnmatch -try: - fnmatch.filter -except AttributeError: - # Pre-2.2 Python has no fnmatch.filter() function. - def filter(names, pat): - """Return the subset of the list NAMES that match PAT""" - import os,posixpath - result=[] - pat = os.path.normcase(pat) - if not fnmatch._cache.has_key(pat): - import re - res = fnmatch.translate(pat) - fnmatch._cache[pat] = re.compile(res) - match = fnmatch._cache[pat].match - if os.path is posixpath: - # normcase on posix is NOP. Optimize it away from the loop. - for name in names: - if match(name): - result.append(name) - else: - for name in names: - if match(os.path.normcase(name)): - result.append(name) - return result - fnmatch.filter = filter - del filter - -try: - import itertools -except ImportError: - # Pre-2.3 Python has no itertools module. - import_as('_scons_itertools', 'itertools') - -# If we need the compatibility version of textwrap, it must be imported -# before optparse, which uses it. -try: - import textwrap -except ImportError: - # Pre-2.3 Python has no textwrap module. - import_as('_scons_textwrap', 'textwrap') - -try: - import optparse -except ImportError: - # Pre-2.3 Python has no optparse module. - import_as('_scons_optparse', 'optparse') - -import os -try: - os.devnull -except AttributeError: - # Pre-2.4 Python has no os.devnull attribute - import sys - _names = sys.builtin_module_names - if 'posix' in _names: - os.devnull = '/dev/null' - elif 'nt' in _names: - os.devnull = 'nul' - os.path.devnull = os.devnull - -import shlex -try: - shlex.split -except AttributeError: - # Pre-2.3 Python has no shlex.split() function. - # - # The full white-space splitting semantics of shlex.split() are - # complicated to reproduce by hand, so just use a compatibility - # version of the shlex module cribbed from Python 2.5 with some - # minor modifications for older Python versions. - del shlex - import_as('_scons_shlex', 'shlex') - - -import shutil -try: - shutil.move -except AttributeError: - # Pre-2.3 Python has no shutil.move() function. - # - # Cribbed from Python 2.5. - import os - - def move(src, dst): - """Recursively move a file or directory to another location. - - If the destination is on our current filesystem, then simply use - rename. Otherwise, copy src to the dst and then remove src. - A lot more could be done here... A look at a mv.c shows a lot of - the issues this implementation glosses over. - - """ - try: - os.rename(src, dst) - except OSError: - if os.path.isdir(src): - if shutil.destinsrc(src, dst): - raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst) - shutil.copytree(src, dst, symlinks=True) - shutil.rmtree(src) - else: - shutil.copy2(src,dst) - os.unlink(src) - shutil.move = move - del move - - def destinsrc(src, dst): - src = os.path.abspath(src) - return os.path.abspath(dst)[:len(src)] == src - shutil.destinsrc = destinsrc - del destinsrc - - -try: - import subprocess -except ImportError: - # Pre-2.4 Python has no subprocess module. - import_as('_scons_subprocess', 'subprocess') - -import sys -try: - sys.version_info -except AttributeError: - # Pre-1.6 Python has no sys.version_info - import string - version_string = string.split(sys.version)[0] - version_ints = map(int, string.split(version_string, '.')) - sys.version_info = tuple(version_ints + ['final', 0]) - -try: - import UserString -except ImportError: - # Pre-1.6 Python has no UserString module. - import_as('_scons_UserString', 'UserString') diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_UserString.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_UserString.py deleted file mode 100644 index cde813d9d3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_UserString.py +++ /dev/null @@ -1,92 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/compat/_scons_UserString.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """ -A user-defined wrapper around string objects - -This class is "borrowed" from the Python 2.2 UserString and modified -slightly for use with SCons. It is *NOT* guaranteed to be fully compliant -with the standard UserString class from all later versions of Python. -In particular, it does not necessarily contain all of the methods found -in later versions. -""" - -import types - -StringType = types.StringType - -if hasattr(types, 'UnicodeType'): - UnicodeType = types.UnicodeType - def is_String(obj): - return type(obj) in (StringType, UnicodeType) -else: - def is_String(obj): - return type(obj) is StringType - -class UserString: - def __init__(self, seq): - if is_String(seq): - self.data = seq - elif isinstance(seq, UserString): - self.data = seq.data[:] - else: - self.data = str(seq) - def __str__(self): return str(self.data) - def __repr__(self): return repr(self.data) - def __int__(self): return int(self.data) - def __long__(self): return long(self.data) - def __float__(self): return float(self.data) - def __complex__(self): return complex(self.data) - def __hash__(self): return hash(self.data) - - def __cmp__(self, string): - if isinstance(string, UserString): - return cmp(self.data, string.data) - else: - return cmp(self.data, string) - def __contains__(self, char): - return char in self.data - - def __len__(self): return len(self.data) - def __getitem__(self, index): return self.__class__(self.data[index]) - def __getslice__(self, start, end): - start = max(start, 0); end = max(end, 0) - return self.__class__(self.data[start:end]) - - def __add__(self, other): - if isinstance(other, UserString): - return self.__class__(self.data + other.data) - elif is_String(other): - return self.__class__(self.data + other) - else: - return self.__class__(self.data + str(other)) - def __radd__(self, other): - if is_String(other): - return self.__class__(other + self.data) - else: - return self.__class__(str(other) + self.data) - def __mul__(self, n): - return self.__class__(self.data*n) - __rmul__ = __mul__ diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_hashlib.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_hashlib.py deleted file mode 100644 index 97bf8d94e6..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_hashlib.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__doc__ = """ -hashlib backwards-compatibility module for older (pre-2.5) Python versions - -This does not not NOT (repeat, *NOT*) provide complete hashlib -functionality. It only wraps the portions of MD5 functionality used -by SCons, in an interface that looks like hashlib (or enough for our -purposes, anyway). In fact, this module will raise an ImportError if -the underlying md5 module isn't available. -""" - -__revision__ = "src/engine/SCons/compat/_scons_hashlib.py 3842 2008/12/20 22:59:52 scons" - -import md5 -import string - -class md5obj: - - md5_module = md5 - - def __init__(self, name, string=''): - if not name in ('MD5', 'md5'): - raise ValueError, "unsupported hash type" - self.name = 'md5' - self.m = self.md5_module.md5() - - def __repr__(self): - return '<%s HASH object @ %#x>' % (self.name, id(self)) - - def copy(self): - import copy - result = copy.copy(self) - result.m = self.m.copy() - return result - - def digest(self): - return self.m.digest() - - def update(self, arg): - return self.m.update(arg) - - if hasattr(md5.md5(), 'hexdigest'): - - def hexdigest(self): - return self.m.hexdigest() - - else: - - # Objects created by the underlying md5 module have no native - # hexdigest() method (*cough* 1.5.2 *cough*), so provide an - # equivalent lifted from elsewhere. - def hexdigest(self): - h = string.hexdigits - r = '' - for c in self.digest(): - i = ord(c) - r = r + h[(i >> 4) & 0xF] + h[i & 0xF] - return r - -new = md5obj - -def md5(string=''): - return md5obj('md5', string) diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_itertools.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_itertools.py deleted file mode 100644 index 145a7f9cf3..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_itertools.py +++ /dev/null @@ -1,118 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/compat/_scons_itertools.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """ -Implementations of itertools functions for Python versions that don't -have iterators. - -These implement the functions by creating the entire list, not returning -it element-by-element as the real itertools functions do. This means -that early Python versions won't get the performance benefit of using -the itertools, but we can still use them so the later Python versions -do get the advantages of using iterators. - -Because we return the entire list, we intentionally do not implement the -itertools functions that "return" infinitely-long lists: the count(), -cycle() and repeat() functions. Other functions below have remained -unimplemented simply because they aren't being used (yet) and it wasn't -obvious how to do it. Or, conversely, we only implemented those functions -that *were* easy to implement (mostly because the Python documentation -contained examples of equivalent code). - -Note that these do not have independent unit tests, so it's possible -that there are bugs. -""" - -def chain(*iterables): - result = [] - for x in iterables: - result.extend(list(x)) - return result - -def count(n=0): - # returns infinite length, should not be supported - raise NotImplementedError - -def cycle(iterable): - # returns infinite length, should not be supported - raise NotImplementedError - -def dropwhile(predicate, iterable): - result = [] - for x in iterable: - if not predicate(x): - result.append(x) - break - result.extend(iterable) - return result - -def groupby(iterable, *args): - raise NotImplementedError - -def ifilter(predicate, iterable): - result = [] - if predicate is None: - predicate = bool - for x in iterable: - if predicate(x): - result.append(x) - return result - -def ifilterfalse(predicate, iterable): - result = [] - if predicate is None: - predicate = bool - for x in iterable: - if not predicate(x): - result.append(x) - return result - -def imap(function, *iterables): - return apply(map, (function,) + tuple(iterables)) - -def islice(*args, **kw): - raise NotImplementedError - -def izip(*iterables): - return apply(zip, iterables) - -def repeat(*args, **kw): - # returns infinite length, should not be supported - raise NotImplementedError - -def starmap(*args, **kw): - raise NotImplementedError - -def takewhile(predicate, iterable): - result = [] - for x in iterable: - if predicate(x): - result.append(x) - else: - break - return result - -def tee(*args, **kw): - raise NotImplementedError diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_optparse.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_optparse.py deleted file mode 100644 index 6b376875e7..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_optparse.py +++ /dev/null @@ -1,1719 +0,0 @@ -"""optparse - a powerful, extensible, and easy-to-use option parser. - -By Greg Ward <gward@python.net> - -Originally distributed as Optik; see http://optik.sourceforge.net/ . - -If you have problems with this module, please do not file bugs, -patches, or feature requests with Python; instead, use Optik's -SourceForge project page: - http://sourceforge.net/projects/optik - -For support, use the optik-users@lists.sourceforge.net mailing list -(http://lists.sourceforge.net/lists/listinfo/optik-users). -""" - -# Python developers: please do not make changes to this file, since -# it is automatically generated from the Optik source code. - -__version__ = "1.5.3" - -__all__ = ['Option', - 'SUPPRESS_HELP', - 'SUPPRESS_USAGE', - 'Values', - 'OptionContainer', - 'OptionGroup', - 'OptionParser', - 'HelpFormatter', - 'IndentedHelpFormatter', - 'TitledHelpFormatter', - 'OptParseError', - 'OptionError', - 'OptionConflictError', - 'OptionValueError', - 'BadOptionError'] - -__copyright__ = """ -Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2006 Python Software Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import string -import sys, os -import types -import textwrap - -def _repr(self): - return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) - - -try: - sys.getdefaultencoding -except AttributeError: - def fake_getdefaultencoding(): - return None - sys.getdefaultencoding = fake_getdefaultencoding - -try: - ''.encode -except AttributeError: - def encode_wrapper(s, encoding, replacement): - return s -else: - def encode_wrapper(s, encoding, replacement): - return s.encode(encoding, replacement) - - -# This file was generated from: -# Id: option_parser.py 527 2006-07-23 15:21:30Z greg -# Id: option.py 522 2006-06-11 16:22:03Z gward -# Id: help.py 527 2006-07-23 15:21:30Z greg -# Id: errors.py 509 2006-04-20 00:58:24Z gward - -try: - from gettext import gettext -except ImportError: - def gettext(message): - return message -_ = gettext - - -class OptParseError (Exception): - def __init__(self, msg): - self.msg = msg - - def __str__(self): - return self.msg - - -class OptionError (OptParseError): - """ - Raised if an Option instance is created with invalid or - inconsistent arguments. - """ - - def __init__(self, msg, option): - self.msg = msg - self.option_id = str(option) - - def __str__(self): - if self.option_id: - return "option %s: %s" % (self.option_id, self.msg) - else: - return self.msg - -class OptionConflictError (OptionError): - """ - Raised if conflicting options are added to an OptionParser. - """ - -class OptionValueError (OptParseError): - """ - Raised if an invalid option value is encountered on the command - line. - """ - -class BadOptionError (OptParseError): - """ - Raised if an invalid option is seen on the command line. - """ - def __init__(self, opt_str): - self.opt_str = opt_str - - def __str__(self): - return _("no such option: %s") % self.opt_str - -class AmbiguousOptionError (BadOptionError): - """ - Raised if an ambiguous option is seen on the command line. - """ - def __init__(self, opt_str, possibilities): - BadOptionError.__init__(self, opt_str) - self.possibilities = possibilities - - def __str__(self): - return (_("ambiguous option: %s (%s?)") - % (self.opt_str, string.join(self.possibilities, ", "))) - - -class HelpFormatter: - - """ - Abstract base class for formatting option help. OptionParser - instances should use one of the HelpFormatter subclasses for - formatting help; by default IndentedHelpFormatter is used. - - Instance attributes: - parser : OptionParser - the controlling OptionParser instance - indent_increment : int - the number of columns to indent per nesting level - max_help_position : int - the maximum starting column for option help text - help_position : int - the calculated starting column for option help text; - initially the same as the maximum - width : int - total number of columns for output (pass None to constructor for - this value to be taken from the $COLUMNS environment variable) - level : int - current indentation level - current_indent : int - current indentation level (in columns) - help_width : int - number of columns available for option help text (calculated) - default_tag : str - text to replace with each option's default value, "%default" - by default. Set to false value to disable default value expansion. - option_strings : { Option : str } - maps Option instances to the snippet of help text explaining - the syntax of that option, e.g. "-h, --help" or - "-fFILE, --file=FILE" - _short_opt_fmt : str - format string controlling how short options with values are - printed in help text. Must be either "%s%s" ("-fFILE") or - "%s %s" ("-f FILE"), because those are the two syntaxes that - Optik supports. - _long_opt_fmt : str - similar but for long options; must be either "%s %s" ("--file FILE") - or "%s=%s" ("--file=FILE"). - """ - - NO_DEFAULT_VALUE = "none" - - def __init__(self, - indent_increment, - max_help_position, - width, - short_first): - self.parser = None - self.indent_increment = indent_increment - self.help_position = self.max_help_position = max_help_position - if width is None: - try: - width = int(os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width = width - 2 - self.width = width - self.current_indent = 0 - self.level = 0 - self.help_width = None # computed later - self.short_first = short_first - self.default_tag = "%default" - self.option_strings = {} - self._short_opt_fmt = "%s %s" - self._long_opt_fmt = "%s=%s" - - def set_parser(self, parser): - self.parser = parser - - def set_short_opt_delimiter(self, delim): - if delim not in ("", " "): - raise ValueError( - "invalid metavar delimiter for short options: %r" % delim) - self._short_opt_fmt = "%s" + delim + "%s" - - def set_long_opt_delimiter(self, delim): - if delim not in ("=", " "): - raise ValueError( - "invalid metavar delimiter for long options: %r" % delim) - self._long_opt_fmt = "%s" + delim + "%s" - - def indent(self): - self.current_indent = self.current_indent + self.indent_increment - self.level = self.level + 1 - - def dedent(self): - self.current_indent = self.current_indent - self.indent_increment - assert self.current_indent >= 0, "Indent decreased below 0." - self.level = self.level - 1 - - def format_usage(self, usage): - raise NotImplementedError, "subclasses must implement" - - def format_heading(self, heading): - raise NotImplementedError, "subclasses must implement" - - def _format_text(self, text): - """ - Format a paragraph of free-form text for inclusion in the - help output at the current indentation level. - """ - text_width = self.width - self.current_indent - indent = " "*self.current_indent - return textwrap.fill(text, - text_width, - initial_indent=indent, - subsequent_indent=indent) - - def format_description(self, description): - if description: - return self._format_text(description) + "\n" - else: - return "" - - def format_epilog(self, epilog): - if epilog: - return "\n" + self._format_text(epilog) + "\n" - else: - return "" - - - def expand_default(self, option): - if self.parser is None or not self.default_tag: - return option.help - - default_value = self.parser.defaults.get(option.dest) - if default_value is NO_DEFAULT or default_value is None: - default_value = self.NO_DEFAULT_VALUE - - return string.replace(option.help, self.default_tag, str(default_value)) - - def format_option(self, option): - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("-x", or "-fFILENAME, --file=FILENAME") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # -x turn on expert mode - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # -fFILENAME, --file=FILENAME - # read data from FILENAME - result = [] - opts = self.option_strings[option] - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", opts) - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - help_text = self.expand_default(option) - help_lines = textwrap.wrap(help_text, self.help_width) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - for line in help_lines[1:]: - result.append("%*s%s\n" % (self.help_position, "", line)) - elif opts[-1] != "\n": - result.append("\n") - return string.join(result, "") - - def store_option_strings(self, parser): - self.indent() - max_len = 0 - for opt in parser.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.indent() - for group in parser.option_groups: - for opt in group.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.dedent() - self.dedent() - self.help_position = min(max_len + 2, self.max_help_position) - self.help_width = self.width - self.help_position - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - if option.takes_value(): - metavar = option.metavar or string.upper(option.dest) - short_opts = [] - for sopt in option._short_opts: - short_opts.append(self._short_opt_fmt % (sopt, metavar)) - long_opts = [] - for lopt in option._long_opts: - long_opts.append(self._long_opt_fmt % (lopt, metavar)) - else: - short_opts = option._short_opts - long_opts = option._long_opts - - if self.short_first: - opts = short_opts + long_opts - else: - opts = long_opts + short_opts - - return string.join(opts, ", ") - -class IndentedHelpFormatter (HelpFormatter): - """Format help with indented section bodies. - """ - - def __init__(self, - indent_increment=2, - max_help_position=24, - width=None, - short_first=1): - HelpFormatter.__init__( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return _("Usage: %s\n") % usage - - def format_heading(self, heading): - return "%*s%s:\n" % (self.current_indent, "", heading) - - -class TitledHelpFormatter (HelpFormatter): - """Format help with underlined section headers. - """ - - def __init__(self, - indent_increment=0, - max_help_position=24, - width=None, - short_first=0): - HelpFormatter.__init__ ( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return "%s %s\n" % (self.format_heading(_("Usage")), usage) - - def format_heading(self, heading): - return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) - - -def _parse_num(val, type): - if string.lower(val[:2]) == "0x": # hexadecimal - radix = 16 - elif string.lower(val[:2]) == "0b": # binary - radix = 2 - val = val[2:] or "0" # have to remove "0b" prefix - elif val[:1] == "0": # octal - radix = 8 - else: # decimal - radix = 10 - - return type(val, radix) - -def _parse_int(val): - return _parse_num(val, int) - -def _parse_long(val): - return _parse_num(val, long) - -try: - int('0', 10) -except TypeError: - # Python 1.5.2 doesn't allow a radix value to be passed to int(). - _parse_int = int - -try: - long('0', 10) -except TypeError: - # Python 1.5.2 doesn't allow a radix value to be passed to long(). - _parse_long = long - -_builtin_cvt = { "int" : (_parse_int, _("integer")), - "long" : (_parse_long, _("long integer")), - "float" : (float, _("floating-point")), - "complex" : (complex, _("complex")) } - -def check_builtin(option, opt, value): - (cvt, what) = _builtin_cvt[option.type] - try: - return cvt(value) - except ValueError: - raise OptionValueError( - _("option %s: invalid %s value: %r") % (opt, what, value)) - -def check_choice(option, opt, value): - if value in option.choices: - return value - else: - choices = string.join(map(repr, option.choices), ", ") - raise OptionValueError( - _("option %s: invalid choice: %r (choose from %s)") - % (opt, value, choices)) - -# Not supplying a default is different from a default of None, -# so we need an explicit "not supplied" value. -NO_DEFAULT = ("NO", "DEFAULT") - - -class Option: - """ - Instance attributes: - _short_opts : [string] - _long_opts : [string] - - action : string - type : string - dest : string - default : any - nargs : int - const : any - choices : [string] - callback : function - callback_args : (any*) - callback_kwargs : { string : any } - help : string - metavar : string - """ - - # The list of instance attributes that may be set through - # keyword args to the constructor. - ATTRS = ['action', - 'type', - 'dest', - 'default', - 'nargs', - 'const', - 'choices', - 'callback', - 'callback_args', - 'callback_kwargs', - 'help', - 'metavar'] - - # The set of actions allowed by option parsers. Explicitly listed - # here so the constructor can validate its arguments. - ACTIONS = ("store", - "store_const", - "store_true", - "store_false", - "append", - "append_const", - "count", - "callback", - "help", - "version") - - # The set of actions that involve storing a value somewhere; - # also listed just for constructor argument validation. (If - # the action is one of these, there must be a destination.) - STORE_ACTIONS = ("store", - "store_const", - "store_true", - "store_false", - "append", - "append_const", - "count") - - # The set of actions for which it makes sense to supply a value - # type, ie. which may consume an argument from the command line. - TYPED_ACTIONS = ("store", - "append", - "callback") - - # The set of actions which *require* a value type, ie. that - # always consume an argument from the command line. - ALWAYS_TYPED_ACTIONS = ("store", - "append") - - # The set of actions which take a 'const' attribute. - CONST_ACTIONS = ("store_const", - "append_const") - - # The set of known types for option parsers. Again, listed here for - # constructor argument validation. - TYPES = ("string", "int", "long", "float", "complex", "choice") - - # Dictionary of argument checking functions, which convert and - # validate option arguments according to the option type. - # - # Signature of checking functions is: - # check(option : Option, opt : string, value : string) -> any - # where - # option is the Option instance calling the checker - # opt is the actual option seen on the command-line - # (eg. "-a", "--file") - # value is the option argument seen on the command-line - # - # The return value should be in the appropriate Python type - # for option.type -- eg. an integer if option.type == "int". - # - # If no checker is defined for a type, arguments will be - # unchecked and remain strings. - TYPE_CHECKER = { "int" : check_builtin, - "long" : check_builtin, - "float" : check_builtin, - "complex": check_builtin, - "choice" : check_choice, - } - - - # CHECK_METHODS is a list of unbound method objects; they are called - # by the constructor, in order, after all attributes are - # initialized. The list is created and filled in later, after all - # the methods are actually defined. (I just put it here because I - # like to define and document all class attributes in the same - # place.) Subclasses that add another _check_*() method should - # define their own CHECK_METHODS list that adds their check method - # to those from this class. - CHECK_METHODS = None - - - # -- Constructor/initialization methods ---------------------------- - - def __init__(self, *opts, **attrs): - # Set _short_opts, _long_opts attrs from 'opts' tuple. - # Have to be set now, in case no option strings are supplied. - self._short_opts = [] - self._long_opts = [] - opts = self._check_opt_strings(opts) - self._set_opt_strings(opts) - - # Set all other attrs (action, type, etc.) from 'attrs' dict - self._set_attrs(attrs) - - # Check all the attributes we just set. There are lots of - # complicated interdependencies, but luckily they can be farmed - # out to the _check_*() methods listed in CHECK_METHODS -- which - # could be handy for subclasses! The one thing these all share - # is that they raise OptionError if they discover a problem. - for checker in self.CHECK_METHODS: - checker(self) - - def _check_opt_strings(self, opts): - # Filter out None because early versions of Optik had exactly - # one short option and one long option, either of which - # could be None. - opts = filter(None, opts) - if not opts: - raise TypeError("at least one option string must be supplied") - return opts - - def _set_opt_strings(self, opts): - for opt in opts: - if len(opt) < 2: - raise OptionError( - "invalid option string %r: " - "must be at least two characters long" % opt, self) - elif len(opt) == 2: - if not (opt[0] == "-" and opt[1] != "-"): - raise OptionError( - "invalid short option string %r: " - "must be of the form -x, (x any non-dash char)" % opt, - self) - self._short_opts.append(opt) - else: - if not (opt[0:2] == "--" and opt[2] != "-"): - raise OptionError( - "invalid long option string %r: " - "must start with --, followed by non-dash" % opt, - self) - self._long_opts.append(opt) - - def _set_attrs(self, attrs): - for attr in self.ATTRS: - if attrs.has_key(attr): - setattr(self, attr, attrs[attr]) - del attrs[attr] - else: - if attr == 'default': - setattr(self, attr, NO_DEFAULT) - else: - setattr(self, attr, None) - if attrs: - attrs = attrs.keys() - attrs.sort() - raise OptionError( - "invalid keyword arguments: %s" % string.join(attrs, ", "), - self) - - - # -- Constructor validation methods -------------------------------- - - def _check_action(self): - if self.action is None: - self.action = "store" - elif self.action not in self.ACTIONS: - raise OptionError("invalid action: %r" % self.action, self) - - def _check_type(self): - if self.type is None: - if self.action in self.ALWAYS_TYPED_ACTIONS: - if self.choices is not None: - # The "choices" attribute implies "choice" type. - self.type = "choice" - else: - # No type given? "string" is the most sensible default. - self.type = "string" - else: - # Allow type objects or builtin type conversion functions - # (int, str, etc.) as an alternative to their names. (The - # complicated check of __builtin__ is only necessary for - # Python 2.1 and earlier, and is short-circuited by the - # first check on modern Pythons.) - import __builtin__ - if ( type(self.type) is types.TypeType or - (hasattr(self.type, "__name__") and - getattr(__builtin__, self.type.__name__, None) is self.type) ): - self.type = self.type.__name__ - - if self.type == "str": - self.type = "string" - - if self.type not in self.TYPES: - raise OptionError("invalid option type: %r" % self.type, self) - if self.action not in self.TYPED_ACTIONS: - raise OptionError( - "must not supply a type for action %r" % self.action, self) - - def _check_choice(self): - if self.type == "choice": - if self.choices is None: - raise OptionError( - "must supply a list of choices for type 'choice'", self) - elif type(self.choices) not in (types.TupleType, types.ListType): - raise OptionError( - "choices must be a list of strings ('%s' supplied)" - % string.split(str(type(self.choices)), "'")[1], self) - elif self.choices is not None: - raise OptionError( - "must not supply choices for type %r" % self.type, self) - - def _check_dest(self): - # No destination given, and we need one for this action. The - # self.type check is for callbacks that take a value. - takes_value = (self.action in self.STORE_ACTIONS or - self.type is not None) - if self.dest is None and takes_value: - - # Glean a destination from the first long option string, - # or from the first short option string if no long options. - if self._long_opts: - # eg. "--foo-bar" -> "foo_bar" - self.dest = string.replace(self._long_opts[0][2:], '-', '_') - else: - self.dest = self._short_opts[0][1] - - def _check_const(self): - if self.action not in self.CONST_ACTIONS and self.const is not None: - raise OptionError( - "'const' must not be supplied for action %r" % self.action, - self) - - def _check_nargs(self): - if self.action in self.TYPED_ACTIONS: - if self.nargs is None: - self.nargs = 1 - elif self.nargs is not None: - raise OptionError( - "'nargs' must not be supplied for action %r" % self.action, - self) - - def _check_callback(self): - if self.action == "callback": - if not callable(self.callback): - raise OptionError( - "callback not callable: %r" % self.callback, self) - if (self.callback_args is not None and - type(self.callback_args) is not types.TupleType): - raise OptionError( - "callback_args, if supplied, must be a tuple: not %r" - % self.callback_args, self) - if (self.callback_kwargs is not None and - type(self.callback_kwargs) is not types.DictType): - raise OptionError( - "callback_kwargs, if supplied, must be a dict: not %r" - % self.callback_kwargs, self) - else: - if self.callback is not None: - raise OptionError( - "callback supplied (%r) for non-callback option" - % self.callback, self) - if self.callback_args is not None: - raise OptionError( - "callback_args supplied for non-callback option", self) - if self.callback_kwargs is not None: - raise OptionError( - "callback_kwargs supplied for non-callback option", self) - - - CHECK_METHODS = [_check_action, - _check_type, - _check_choice, - _check_dest, - _check_const, - _check_nargs, - _check_callback] - - - # -- Miscellaneous methods ----------------------------------------- - - def __str__(self): - return string.join(self._short_opts + self._long_opts, "/") - - __repr__ = _repr - - def takes_value(self): - return self.type is not None - - def get_opt_string(self): - if self._long_opts: - return self._long_opts[0] - else: - return self._short_opts[0] - - - # -- Processing methods -------------------------------------------- - - def check_value(self, opt, value): - checker = self.TYPE_CHECKER.get(self.type) - if checker is None: - return value - else: - return checker(self, opt, value) - - def convert_value(self, opt, value): - if value is not None: - if self.nargs == 1: - return self.check_value(opt, value) - else: - return tuple(map(lambda v, o=opt, s=self: s.check_value(o, v), value)) - - def process(self, opt, value, values, parser): - - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - def take_action(self, action, dest, opt, value, values, parser): - if action == "store": - setattr(values, dest, value) - elif action == "store_const": - setattr(values, dest, self.const) - elif action == "store_true": - setattr(values, dest, True) - elif action == "store_false": - setattr(values, dest, False) - elif action == "append": - values.ensure_value(dest, []).append(value) - elif action == "append_const": - values.ensure_value(dest, []).append(self.const) - elif action == "count": - setattr(values, dest, values.ensure_value(dest, 0) + 1) - elif action == "callback": - args = self.callback_args or () - kwargs = self.callback_kwargs or {} - apply(self.callback, (self, opt, value, parser,) + args, kwargs) - elif action == "help": - parser.print_help() - parser.exit() - elif action == "version": - parser.print_version() - parser.exit() - else: - raise RuntimeError, "unknown action %r" % self.action - - return 1 - -# class Option - - -SUPPRESS_HELP = "SUPPRESS"+"HELP" -SUPPRESS_USAGE = "SUPPRESS"+"USAGE" - -# For compatibility with Python 2.2 -try: - True, False -except NameError: - (True, False) = (1, 0) - -try: - types.UnicodeType -except AttributeError: - def isbasestring(x): - return isinstance(x, types.StringType) -else: - def isbasestring(x): - return isinstance(x, types.StringType) or isinstance(x, types.UnicodeType) - -class Values: - - def __init__(self, defaults=None): - if defaults: - for (attr, val) in defaults.items(): - setattr(self, attr, val) - - def __str__(self): - return str(self.__dict__) - - __repr__ = _repr - - def __cmp__(self, other): - if isinstance(other, Values): - return cmp(self.__dict__, other.__dict__) - elif isinstance(other, types.DictType): - return cmp(self.__dict__, other) - else: - return -1 - - def _update_careful(self, dict): - """ - Update the option values from an arbitrary dictionary, but only - use keys from dict that already have a corresponding attribute - in self. Any keys in dict without a corresponding attribute - are silently ignored. - """ - for attr in dir(self): - if dict.has_key(attr): - dval = dict[attr] - if dval is not None: - setattr(self, attr, dval) - - def _update_loose(self, dict): - """ - Update the option values from an arbitrary dictionary, - using all keys from the dictionary regardless of whether - they have a corresponding attribute in self or not. - """ - self.__dict__.update(dict) - - def _update(self, dict, mode): - if mode == "careful": - self._update_careful(dict) - elif mode == "loose": - self._update_loose(dict) - else: - raise ValueError, "invalid update mode: %r" % mode - - def read_module(self, modname, mode="careful"): - __import__(modname) - mod = sys.modules[modname] - self._update(vars(mod), mode) - - def read_file(self, filename, mode="careful"): - vars = {} - execfile(filename, vars) - self._update(vars, mode) - - def ensure_value(self, attr, value): - if not hasattr(self, attr) or getattr(self, attr) is None: - setattr(self, attr, value) - return getattr(self, attr) - - -class OptionContainer: - - """ - Abstract base class. - - Class attributes: - standard_option_list : [Option] - list of standard options that will be accepted by all instances - of this parser class (intended to be overridden by subclasses). - - Instance attributes: - option_list : [Option] - the list of Option objects contained by this OptionContainer - _short_opt : { string : Option } - dictionary mapping short option strings, eg. "-f" or "-X", - to the Option instances that implement them. If an Option - has multiple short option strings, it will appears in this - dictionary multiple times. [1] - _long_opt : { string : Option } - dictionary mapping long option strings, eg. "--file" or - "--exclude", to the Option instances that implement them. - Again, a given Option can occur multiple times in this - dictionary. [1] - defaults : { string : any } - dictionary mapping option destination names to default - values for each destination [1] - - [1] These mappings are common to (shared by) all components of the - controlling OptionParser, where they are initially created. - - """ - - def __init__(self, option_class, conflict_handler, description): - # Initialize the option list and related data structures. - # This method must be provided by subclasses, and it must - # initialize at least the following instance attributes: - # option_list, _short_opt, _long_opt, defaults. - self._create_option_list() - - self.option_class = option_class - self.set_conflict_handler(conflict_handler) - self.set_description(description) - - def _create_option_mappings(self): - # For use by OptionParser constructor -- create the master - # option mappings used by this OptionParser and all - # OptionGroups that it owns. - self._short_opt = {} # single letter -> Option instance - self._long_opt = {} # long option -> Option instance - self.defaults = {} # maps option dest -> default value - - - def _share_option_mappings(self, parser): - # For use by OptionGroup constructor -- use shared option - # mappings from the OptionParser that owns this OptionGroup. - self._short_opt = parser._short_opt - self._long_opt = parser._long_opt - self.defaults = parser.defaults - - def set_conflict_handler(self, handler): - if handler not in ("error", "resolve"): - raise ValueError, "invalid conflict_resolution value %r" % handler - self.conflict_handler = handler - - def set_description(self, description): - self.description = description - - def get_description(self): - return self.description - - - def destroy(self): - """see OptionParser.destroy().""" - del self._short_opt - del self._long_opt - del self.defaults - - - # -- Option-adding methods ----------------------------------------- - - def _check_conflict(self, option): - conflict_opts = [] - for opt in option._short_opts: - if self._short_opt.has_key(opt): - conflict_opts.append((opt, self._short_opt[opt])) - for opt in option._long_opts: - if self._long_opt.has_key(opt): - conflict_opts.append((opt, self._long_opt[opt])) - - if conflict_opts: - handler = self.conflict_handler - if handler == "error": - raise OptionConflictError( - "conflicting option string(s): %s" - % string.join(map(lambda co: co[0], conflict_opts), ", "), - option) - elif handler == "resolve": - for (opt, c_option) in conflict_opts: - if opt[:2] == "--": - c_option._long_opts.remove(opt) - del self._long_opt[opt] - else: - c_option._short_opts.remove(opt) - del self._short_opt[opt] - if not (c_option._short_opts or c_option._long_opts): - c_option.container.option_list.remove(c_option) - - def add_option(self, *args, **kwargs): - """add_option(Option) - add_option(opt_str, ..., kwarg=val, ...) - """ - if type(args[0]) is types.StringType: - option = apply(self.option_class, args, kwargs) - elif len(args) == 1 and not kwargs: - option = args[0] - if not isinstance(option, Option): - raise TypeError, "not an Option instance: %r" % option - else: - raise TypeError, "invalid arguments" - - self._check_conflict(option) - - self.option_list.append(option) - option.container = self - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - if option.dest is not None: # option has a dest, we need a default - if option.default is not NO_DEFAULT: - self.defaults[option.dest] = option.default - elif not self.defaults.has_key(option.dest): - self.defaults[option.dest] = None - - return option - - def add_options(self, option_list): - for option in option_list: - self.add_option(option) - - # -- Option query/removal methods ---------------------------------- - - def get_option(self, opt_str): - return (self._short_opt.get(opt_str) or - self._long_opt.get(opt_str)) - - def has_option(self, opt_str): - return (self._short_opt.has_key(opt_str) or - self._long_opt.has_key(opt_str)) - - def remove_option(self, opt_str): - option = self._short_opt.get(opt_str) - if option is None: - option = self._long_opt.get(opt_str) - if option is None: - raise ValueError("no such option %r" % opt_str) - - for opt in option._short_opts: - del self._short_opt[opt] - for opt in option._long_opts: - del self._long_opt[opt] - option.container.option_list.remove(option) - - - # -- Help-formatting methods --------------------------------------- - - def format_option_help(self, formatter): - if not self.option_list: - return "" - result = [] - for option in self.option_list: - if not option.help is SUPPRESS_HELP: - result.append(formatter.format_option(option)) - return string.join(result, "") - - def format_description(self, formatter): - return formatter.format_description(self.get_description()) - - def format_help(self, formatter): - result = [] - if self.description: - result.append(self.format_description(formatter)) - if self.option_list: - result.append(self.format_option_help(formatter)) - return string.join(result, "\n") - - -class OptionGroup (OptionContainer): - - def __init__(self, parser, title, description=None): - self.parser = parser - OptionContainer.__init__( - self, parser.option_class, parser.conflict_handler, description) - self.title = title - - def _create_option_list(self): - self.option_list = [] - self._share_option_mappings(self.parser) - - def set_title(self, title): - self.title = title - - def destroy(self): - """see OptionParser.destroy().""" - OptionContainer.destroy(self) - del self.option_list - - # -- Help-formatting methods --------------------------------------- - - def format_help(self, formatter): - result = formatter.format_heading(self.title) - formatter.indent() - result = result + OptionContainer.format_help(self, formatter) - formatter.dedent() - return result - - -class OptionParser (OptionContainer): - - """ - Class attributes: - standard_option_list : [Option] - list of standard options that will be accepted by all instances - of this parser class (intended to be overridden by subclasses). - - Instance attributes: - usage : string - a usage string for your program. Before it is displayed - to the user, "%prog" will be expanded to the name of - your program (self.prog or os.path.basename(sys.argv[0])). - prog : string - the name of the current program (to override - os.path.basename(sys.argv[0])). - epilog : string - paragraph of help text to print after option help - - option_groups : [OptionGroup] - list of option groups in this parser (option groups are - irrelevant for parsing the command-line, but very useful - for generating help) - - allow_interspersed_args : bool = true - if true, positional arguments may be interspersed with options. - Assuming -a and -b each take a single argument, the command-line - -ablah foo bar -bboo baz - will be interpreted the same as - -ablah -bboo -- foo bar baz - If this flag were false, that command line would be interpreted as - -ablah -- foo bar -bboo baz - -- ie. we stop processing options as soon as we see the first - non-option argument. (This is the tradition followed by - Python's getopt module, Perl's Getopt::Std, and other argument- - parsing libraries, but it is generally annoying to users.) - - process_default_values : bool = true - if true, option default values are processed similarly to option - values from the command line: that is, they are passed to the - type-checking function for the option's type (as long as the - default value is a string). (This really only matters if you - have defined custom types; see SF bug #955889.) Set it to false - to restore the behaviour of Optik 1.4.1 and earlier. - - rargs : [string] - the argument list currently being parsed. Only set when - parse_args() is active, and continually trimmed down as - we consume arguments. Mainly there for the benefit of - callback options. - largs : [string] - the list of leftover arguments that we have skipped while - parsing options. If allow_interspersed_args is false, this - list is always empty. - values : Values - the set of option values currently being accumulated. Only - set when parse_args() is active. Also mainly for callbacks. - - Because of the 'rargs', 'largs', and 'values' attributes, - OptionParser is not thread-safe. If, for some perverse reason, you - need to parse command-line arguments simultaneously in different - threads, use different OptionParser instances. - - """ - - standard_option_list = [] - - def __init__(self, - usage=None, - option_list=None, - option_class=Option, - version=None, - conflict_handler="error", - description=None, - formatter=None, - add_help_option=True, - prog=None, - epilog=None): - OptionContainer.__init__( - self, option_class, conflict_handler, description) - self.set_usage(usage) - self.prog = prog - self.version = version - self.allow_interspersed_args = True - self.process_default_values = True - if formatter is None: - formatter = IndentedHelpFormatter() - self.formatter = formatter - self.formatter.set_parser(self) - self.epilog = epilog - - # Populate the option list; initial sources are the - # standard_option_list class attribute, the 'option_list' - # argument, and (if applicable) the _add_version_option() and - # _add_help_option() methods. - self._populate_option_list(option_list, - add_help=add_help_option) - - self._init_parsing_state() - - - def destroy(self): - """ - Declare that you are done with this OptionParser. This cleans up - reference cycles so the OptionParser (and all objects referenced by - it) can be garbage-collected promptly. After calling destroy(), the - OptionParser is unusable. - """ - OptionContainer.destroy(self) - for group in self.option_groups: - group.destroy() - del self.option_list - del self.option_groups - del self.formatter - - - # -- Private methods ----------------------------------------------- - # (used by our or OptionContainer's constructor) - - def _create_option_list(self): - self.option_list = [] - self.option_groups = [] - self._create_option_mappings() - - def _add_help_option(self): - self.add_option("-h", "--help", - action="help", - help=_("show this help message and exit")) - - def _add_version_option(self): - self.add_option("--version", - action="version", - help=_("show program's version number and exit")) - - def _populate_option_list(self, option_list, add_help=True): - if self.standard_option_list: - self.add_options(self.standard_option_list) - if option_list: - self.add_options(option_list) - if self.version: - self._add_version_option() - if add_help: - self._add_help_option() - - def _init_parsing_state(self): - # These are set in parse_args() for the convenience of callbacks. - self.rargs = None - self.largs = None - self.values = None - - - # -- Simple modifier methods --------------------------------------- - - def set_usage(self, usage): - if usage is None: - self.usage = _("%prog [options]") - elif usage is SUPPRESS_USAGE: - self.usage = None - # For backwards compatibility with Optik 1.3 and earlier. - elif string.lower(usage)[:7] == "usage: ": - self.usage = usage[7:] - else: - self.usage = usage - - def enable_interspersed_args(self): - self.allow_interspersed_args = True - - def disable_interspersed_args(self): - self.allow_interspersed_args = False - - def set_process_default_values(self, process): - self.process_default_values = process - - def set_default(self, dest, value): - self.defaults[dest] = value - - def set_defaults(self, **kwargs): - self.defaults.update(kwargs) - - def _get_all_options(self): - options = self.option_list[:] - for group in self.option_groups: - options.extend(group.option_list) - return options - - def get_default_values(self): - if not self.process_default_values: - # Old, pre-Optik 1.5 behaviour. - return Values(self.defaults) - - defaults = self.defaults.copy() - for option in self._get_all_options(): - default = defaults.get(option.dest) - if isbasestring(default): - opt_str = option.get_opt_string() - defaults[option.dest] = option.check_value(opt_str, default) - - return Values(defaults) - - - # -- OptionGroup methods ------------------------------------------- - - def add_option_group(self, *args, **kwargs): - # XXX lots of overlap with OptionContainer.add_option() - if type(args[0]) is types.StringType: - group = apply(OptionGroup, (self,) + args, kwargs) - elif len(args) == 1 and not kwargs: - group = args[0] - if not isinstance(group, OptionGroup): - raise TypeError, "not an OptionGroup instance: %r" % group - if group.parser is not self: - raise ValueError, "invalid OptionGroup (wrong parser)" - else: - raise TypeError, "invalid arguments" - - self.option_groups.append(group) - return group - - def get_option_group(self, opt_str): - option = (self._short_opt.get(opt_str) or - self._long_opt.get(opt_str)) - if option and option.container is not self: - return option.container - return None - - - # -- Option-parsing methods ---------------------------------------- - - def _get_args(self, args): - if args is None: - return sys.argv[1:] - else: - return args[:] # don't modify caller's list - - def parse_args(self, args=None, values=None): - """ - parse_args(args : [string] = sys.argv[1:], - values : Values = None) - -> (values : Values, args : [string]) - - Parse the command-line options found in 'args' (default: - sys.argv[1:]). Any errors result in a call to 'error()', which - by default prints the usage message to stderr and calls - sys.exit() with an error message. On success returns a pair - (values, args) where 'values' is an Values instance (with all - your option values) and 'args' is the list of arguments left - over after parsing options. - """ - rargs = self._get_args(args) - if values is None: - values = self.get_default_values() - - # Store the halves of the argument list as attributes for the - # convenience of callbacks: - # rargs - # the rest of the command-line (the "r" stands for - # "remaining" or "right-hand") - # largs - # the leftover arguments -- ie. what's left after removing - # options and their arguments (the "l" stands for "leftover" - # or "left-hand") - self.rargs = rargs - self.largs = largs = [] - self.values = values - - try: - stop = self._process_args(largs, rargs, values) - except (BadOptionError, OptionValueError), err: - self.error(str(err)) - - args = largs + rargs - return self.check_values(values, args) - - def check_values(self, values, args): - """ - check_values(values : Values, args : [string]) - -> (values : Values, args : [string]) - - Check that the supplied option values and leftover arguments are - valid. Returns the option values and leftover arguments - (possibly adjusted, possibly completely new -- whatever you - like). Default implementation just returns the passed-in - values; subclasses may override as desired. - """ - return (values, args) - - def _process_args(self, largs, rargs, values): - """_process_args(largs : [string], - rargs : [string], - values : Values) - - Process command-line arguments and populate 'values', consuming - options and arguments from 'rargs'. If 'allow_interspersed_args' is - false, stop at the first non-option argument. If true, accumulate any - interspersed non-option arguments in 'largs'. - """ - while rargs: - arg = rargs[0] - # We handle bare "--" explicitly, and bare "-" is handled by the - # standard arg handler since the short arg case ensures that the - # len of the opt string is greater than 1. - if arg == "--": - del rargs[0] - return - elif arg[0:2] == "--": - # process a single long option (possibly with value(s)) - self._process_long_opt(rargs, values) - elif arg[:1] == "-" and len(arg) > 1: - # process a cluster of short options (possibly with - # value(s) for the last one only) - self._process_short_opts(rargs, values) - elif self.allow_interspersed_args: - largs.append(arg) - del rargs[0] - else: - return # stop now, leave this arg in rargs - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt(self, opt): - """_match_long_opt(opt : string) -> string - - Determine which long option string 'opt' matches, ie. which one - it is an unambiguous abbrevation for. Raises BadOptionError if - 'opt' doesn't unambiguously match any long option string. - """ - return _match_abbrev(opt, self._long_opt) - - def _process_long_opt(self, rargs, values): - arg = rargs.pop(0) - - # Value explicitly attached to arg? Pretend it's the next - # argument. - if "=" in arg: - (opt, next_arg) = string.split(arg, "=", 1) - rargs.insert(0, next_arg) - had_explicit_value = True - else: - opt = arg - had_explicit_value = False - - opt = self._match_long_opt(opt) - option = self._long_opt[opt] - if option.takes_value(): - nargs = option.nargs - if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - elif had_explicit_value: - self.error(_("%s option does not take a value") % opt) - - else: - value = None - - option.process(opt, value, values, self) - - def _process_short_opts(self, rargs, values): - arg = rargs.pop(0) - stop = False - i = 1 - for ch in arg[1:]: - opt = "-" + ch - option = self._short_opt.get(opt) - i = i + 1 # we have consumed a character - - if not option: - raise BadOptionError(opt) - if option.takes_value(): - # Any characters left in arg? Pretend they're the - # next arg, and stop consuming characters of arg. - if i < len(arg): - rargs.insert(0, arg[i:]) - stop = True - - nargs = option.nargs - if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - else: # option doesn't take a value - value = None - - option.process(opt, value, values, self) - - if stop: - break - - - # -- Feedback methods ---------------------------------------------- - - def get_prog_name(self): - if self.prog is None: - return os.path.basename(sys.argv[0]) - else: - return self.prog - - def expand_prog_name(self, s): - return string.replace(s, "%prog", self.get_prog_name()) - - def get_description(self): - return self.expand_prog_name(self.description) - - def exit(self, status=0, msg=None): - if msg: - sys.stderr.write(msg) - sys.exit(status) - - def error(self, msg): - """error(msg : string) - - Print a usage message incorporating 'msg' to stderr and exit. - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(sys.stderr) - self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg)) - - def get_usage(self): - if self.usage: - return self.formatter.format_usage( - self.expand_prog_name(self.usage)) - else: - return "" - - def print_usage(self, file=None): - """print_usage(file : file = stdout) - - Print the usage message for the current program (self.usage) to - 'file' (default stdout). Any occurence of the string "%prog" in - self.usage is replaced with the name of the current program - (basename of sys.argv[0]). Does nothing if self.usage is empty - or not defined. - """ - if self.usage: - file.write(self.get_usage() + '\n') - - def get_version(self): - if self.version: - return self.expand_prog_name(self.version) - else: - return "" - - def print_version(self, file=None): - """print_version(file : file = stdout) - - Print the version message for this program (self.version) to - 'file' (default stdout). As with print_usage(), any occurence - of "%prog" in self.version is replaced by the current program's - name. Does nothing if self.version is empty or undefined. - """ - if self.version: - file.write(self.get_version() + '\n') - - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading(_("Options"))) - formatter.indent() - if self.option_list: - result.append(OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return string.join(result[:-1], "") - - def format_epilog(self, formatter): - return formatter.format_epilog(self.epilog) - - def format_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - result = [] - if self.usage: - result.append(self.get_usage() + "\n") - if self.description: - result.append(self.format_description(formatter) + "\n") - result.append(self.format_option_help(formatter)) - result.append(self.format_epilog(formatter)) - return string.join(result, "") - - # used by test suite - def _get_encoding(self, file): - encoding = getattr(file, "encoding", None) - if not encoding: - encoding = sys.getdefaultencoding() - return encoding - - def print_help(self, file=None): - """print_help(file : file = stdout) - - Print an extended help message, listing all options and any - help text provided with them, to 'file' (default stdout). - """ - if file is None: - file = sys.stdout - encoding = self._get_encoding(file) - file.write(encode_wrapper(self.format_help(), encoding, "replace")) - -# class OptionParser - - -def _match_abbrev(s, wordmap): - """_match_abbrev(s : string, wordmap : {string : Option}) -> string - - Return the string key in 'wordmap' for which 's' is an unambiguous - abbreviation. If 's' is found to be ambiguous or doesn't match any of - 'words', raise BadOptionError. - """ - # Is there an exact match? - if wordmap.has_key(s): - return s - else: - # Isolate all words with s as a prefix. - possibilities = filter(lambda w, s=s: w[:len(s)] == s, wordmap.keys()) - # No exact match, so there had better be just one possibility. - if len(possibilities) == 1: - return possibilities[0] - elif not possibilities: - raise BadOptionError(s) - else: - # More than one possible completion: ambiguous prefix. - possibilities.sort() - raise AmbiguousOptionError(s, possibilities) - - -# Some day, there might be many Option classes. As of Optik 1.3, the -# preferred way to instantiate Options is indirectly, via make_option(), -# which will become a factory function when there are many Option -# classes. -make_option = Option diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets.py deleted file mode 100644 index 32a0dd64ff..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets.py +++ /dev/null @@ -1,577 +0,0 @@ -"""Classes to represent arbitrary sets (including sets of sets). - -This module implements sets using dictionaries whose values are -ignored. The usual operations (union, intersection, deletion, etc.) -are provided as both methods and operators. - -Important: sets are not sequences! While they support 'x in s', -'len(s)', and 'for x in s', none of those operations are unique for -sequences; for example, mappings support all three as well. The -characteristic operation for sequences is subscripting with small -integers: s[i], for i in range(len(s)). Sets don't support -subscripting at all. Also, sequences allow multiple occurrences and -their elements have a definite order; sets on the other hand don't -record multiple occurrences and don't remember the order of element -insertion (which is why they don't support s[i]). - -The following classes are provided: - -BaseSet -- All the operations common to both mutable and immutable - sets. This is an abstract class, not meant to be directly - instantiated. - -Set -- Mutable sets, subclass of BaseSet; not hashable. - -ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. - An iterable argument is mandatory to create an ImmutableSet. - -_TemporarilyImmutableSet -- A wrapper around a Set, hashable, - giving the same hash value as the immutable set equivalent - would have. Do not use this class directly. - -Only hashable objects can be added to a Set. In particular, you cannot -really add a Set as an element to another Set; if you try, what is -actually added is an ImmutableSet built from it (it compares equal to -the one you tried adding). - -When you ask if `x in y' where x is a Set and y is a Set or -ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and -what's tested is actually `z in y'. - -""" - -# Code history: -# -# - Greg V. Wilson wrote the first version, using a different approach -# to the mutable/immutable problem, and inheriting from dict. -# -# - Alex Martelli modified Greg's version to implement the current -# Set/ImmutableSet approach, and make the data an attribute. -# -# - Guido van Rossum rewrote much of the code, made some API changes, -# and cleaned up the docstrings. -# -# - Raymond Hettinger added a number of speedups and other -# improvements. - -from __future__ import generators -try: - from itertools import ifilter, ifilterfalse -except ImportError: - # Code to make the module run under Py2.2 - def ifilter(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if predicate(x): - yield x - def ifilterfalse(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if not predicate(x): - yield x - try: - True, False - except NameError: - True, False = (0==0, 0!=0) - -__all__ = ['BaseSet', 'Set', 'ImmutableSet'] - -class BaseSet(object): - """Common base class for mutable and immutable sets.""" - - __slots__ = ['_data'] - - # Constructor - - def __init__(self): - """This is an abstract class.""" - # Don't call this from a concrete subclass! - if self.__class__ is BaseSet: - raise TypeError, ("BaseSet is an abstract class. " - "Use Set or ImmutableSet.") - - # Standard protocols: __len__, __repr__, __str__, __iter__ - - def __len__(self): - """Return the number of elements of a set.""" - return len(self._data) - - def __repr__(self): - """Return string representation of a set. - - This looks like 'Set([<list of elements>])'. - """ - return self._repr() - - # __str__ is the same as __repr__ - __str__ = __repr__ - - def _repr(self, sorted=False): - elements = self._data.keys() - if sorted: - elements.sort() - return '%s(%r)' % (self.__class__.__name__, elements) - - def __iter__(self): - """Return an iterator over the elements or a set. - - This is the keys iterator for the underlying dict. - """ - return self._data.iterkeys() - - # Three-way comparison is not supported. However, because __eq__ is - # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and - # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this - # case). - - def __cmp__(self, other): - raise TypeError, "can't compare sets using cmp()" - - # Equality comparisons using the underlying dicts. Mixed-type comparisons - # are allowed here, where Set == z for non-Set z always returns False, - # and Set != z always True. This allows expressions like "x in y" to - # give the expected result when y is a sequence of mixed types, not - # raising a pointless TypeError just because y contains a Set, or x is - # a Set and y contain's a non-set ("in" invokes only __eq__). - # Subtle: it would be nicer if __eq__ and __ne__ could return - # NotImplemented instead of True or False. Then the other comparand - # would get a chance to determine the result, and if the other comparand - # also returned NotImplemented then it would fall back to object address - # comparison (which would always return False for __eq__ and always - # True for __ne__). However, that doesn't work, because this type - # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, - # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. - - def __eq__(self, other): - if isinstance(other, BaseSet): - return self._data == other._data - else: - return False - - def __ne__(self, other): - if isinstance(other, BaseSet): - return self._data != other._data - else: - return True - - # Copying operations - - def copy(self): - """Return a shallow copy of a set.""" - result = self.__class__() - result._data.update(self._data) - return result - - __copy__ = copy # For the copy module - - def __deepcopy__(self, memo): - """Return a deep copy of a set; used by copy module.""" - # This pre-creates the result and inserts it in the memo - # early, in case the deep copy recurses into another reference - # to this same set. A set can't be an element of itself, but - # it can certainly contain an object that has a reference to - # itself. - from copy import deepcopy - result = self.__class__() - memo[id(self)] = result - data = result._data - value = True - for elt in self: - data[deepcopy(elt, memo)] = value - return result - - # Standard set operations: union, intersection, both differences. - # Each has an operator version (e.g. __or__, invoked with |) and a - # method version (e.g. union). - # Subtle: Each pair requires distinct code so that the outcome is - # correct when the type of other isn't suitable. For example, if - # we did "union = __or__" instead, then Set().union(3) would return - # NotImplemented instead of raising TypeError (albeit that *why* it - # raises TypeError as-is is also a bit subtle). - - def __or__(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.union(other) - - def union(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - result = self.__class__(self) - result._update(other) - return result - - def __and__(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.intersection(other) - - def intersection(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - other = Set(other) - if len(self) <= len(other): - little, big = self, other - else: - little, big = other, self - common = ifilter(big._data.has_key, little) - return self.__class__(common) - - def __xor__(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.symmetric_difference(other) - - def symmetric_difference(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - result = self.__class__() - data = result._data - value = True - selfdata = self._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - for elt in ifilterfalse(otherdata.has_key, selfdata): - data[elt] = value - for elt in ifilterfalse(selfdata.has_key, otherdata): - data[elt] = value - return result - - def __sub__(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.difference(other) - - def difference(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - result = self.__class__() - data = result._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - value = True - for elt in ifilterfalse(otherdata.has_key, self): - data[elt] = value - return result - - # Membership test - - def __contains__(self, element): - """Report whether an element is a member of a set. - - (Called in response to the expression `element in self'.) - """ - try: - return element in self._data - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - return transform() in self._data - - # Subset and superset test - - def issubset(self, other): - """Report whether another set contains this set.""" - self._binary_sanity_check(other) - if len(self) > len(other): # Fast check for obvious cases - return False - for elt in ifilterfalse(other._data.has_key, self): - return False - return True - - def issuperset(self, other): - """Report whether this set contains another set.""" - self._binary_sanity_check(other) - if len(self) < len(other): # Fast check for obvious cases - return False - for elt in ifilterfalse(self._data.has_key, other): - return False - return True - - # Inequality comparisons using the is-subset relation. - __le__ = issubset - __ge__ = issuperset - - def __lt__(self, other): - self._binary_sanity_check(other) - return len(self) < len(other) and self.issubset(other) - - def __gt__(self, other): - self._binary_sanity_check(other) - return len(self) > len(other) and self.issuperset(other) - - # Assorted helpers - - def _binary_sanity_check(self, other): - # Check that the other argument to a binary operation is also - # a set, raising a TypeError otherwise. - if not isinstance(other, BaseSet): - raise TypeError, "Binary operation only permitted between sets" - - def _compute_hash(self): - # Calculate hash code for a set by xor'ing the hash codes of - # the elements. This ensures that the hash code does not depend - # on the order in which elements are added to the set. This is - # not called __hash__ because a BaseSet should not be hashable; - # only an ImmutableSet is hashable. - result = 0 - for elt in self: - result ^= hash(elt) - return result - - def _update(self, iterable): - # The main loop for update() and the subclass __init__() methods. - data = self._data - - # Use the fast update() method when a dictionary is available. - if isinstance(iterable, BaseSet): - data.update(iterable._data) - return - - value = True - - if type(iterable) in (list, tuple, xrange): - # Optimized: we know that __iter__() and next() can't - # raise TypeError, so we can move 'try:' out of the loop. - it = iter(iterable) - while True: - try: - for element in it: - data[element] = value - return - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - else: - # Safe: only catch TypeError where intended - for element in iterable: - try: - data[element] = value - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - - -class ImmutableSet(BaseSet): - """Immutable set class.""" - - __slots__ = ['_hashcode'] - - # BaseSet + hashing - - def __init__(self, iterable=None): - """Construct an immutable set from an optional iterable.""" - self._hashcode = None - self._data = {} - if iterable is not None: - self._update(iterable) - - def __hash__(self): - if self._hashcode is None: - self._hashcode = self._compute_hash() - return self._hashcode - - def __getstate__(self): - return self._data, self._hashcode - - def __setstate__(self, state): - self._data, self._hashcode = state - -class Set(BaseSet): - """ Mutable set class.""" - - __slots__ = [] - - # BaseSet + operations requiring mutability; no hashing - - def __init__(self, iterable=None): - """Construct a set from an optional iterable.""" - self._data = {} - if iterable is not None: - self._update(iterable) - - def __getstate__(self): - # getstate's results are ignored if it is not - return self._data, - - def __setstate__(self, data): - self._data, = data - - def __hash__(self): - """A Set cannot be hashed.""" - # We inherit object.__hash__, so we must deny this explicitly - raise TypeError, "Can't hash a Set, only an ImmutableSet." - - # In-place union, intersection, differences. - # Subtle: The xyz_update() functions deliberately return None, - # as do all mutating operations on built-in container types. - # The __xyz__ spellings have to return self, though. - - def __ior__(self, other): - """Update a set with the union of itself and another.""" - self._binary_sanity_check(other) - self._data.update(other._data) - return self - - def union_update(self, other): - """Update a set with the union of itself and another.""" - self._update(other) - - def __iand__(self, other): - """Update a set with the intersection of itself and another.""" - self._binary_sanity_check(other) - self._data = (self & other)._data - return self - - def intersection_update(self, other): - """Update a set with the intersection of itself and another.""" - if isinstance(other, BaseSet): - self &= other - else: - self._data = (self.intersection(other))._data - - def __ixor__(self, other): - """Update a set with the symmetric difference of itself and another.""" - self._binary_sanity_check(other) - self.symmetric_difference_update(other) - return self - - def symmetric_difference_update(self, other): - """Update a set with the symmetric difference of itself and another.""" - data = self._data - value = True - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in other: - if elt in data: - del data[elt] - else: - data[elt] = value - - def __isub__(self, other): - """Remove all elements of another set from this set.""" - self._binary_sanity_check(other) - self.difference_update(other) - return self - - def difference_update(self, other): - """Remove all elements of another set from this set.""" - data = self._data - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in ifilter(data.has_key, other): - del data[elt] - - # Python dict-like mass mutations: update, clear - - def update(self, iterable): - """Add all values from an iterable (such as a list or file).""" - self._update(iterable) - - def clear(self): - """Remove all elements from this set.""" - self._data.clear() - - # Single-element mutations: add, remove, discard - - def add(self, element): - """Add an element to a set. - - This has no effect if the element is already present. - """ - try: - self._data[element] = True - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - self._data[transform()] = True - - def remove(self, element): - """Remove an element from a set; it must be a member. - - If the element is not a member, raise a KeyError. - """ - try: - del self._data[element] - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - del self._data[transform()] - - def discard(self, element): - """Remove an element from a set if it is a member. - - If the element is not a member, do nothing. - """ - try: - self.remove(element) - except KeyError: - pass - - def pop(self): - """Remove and return an arbitrary set element.""" - return self._data.popitem()[0] - - def __as_immutable__(self): - # Return a copy of self as an immutable set - return ImmutableSet(self) - - def __as_temporarily_immutable__(self): - # Return self wrapped in a temporarily immutable set - return _TemporarilyImmutableSet(self) - - -class _TemporarilyImmutableSet(BaseSet): - # Wrap a mutable set as if it was temporarily immutable. - # This only supplies hashing and equality comparisons. - - def __init__(self, set): - self._set = set - self._data = set._data # Needed by ImmutableSet.__eq__() - - def __hash__(self): - return self._set._compute_hash() diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets15.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets15.py deleted file mode 100644 index 1fe5a4f77a..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_sets15.py +++ /dev/null @@ -1,170 +0,0 @@ -# -# A Set class that works all the way back to Python 1.5. From: -# -# Python Cookbook: Yet another Set class for Python -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469 -# Goncalo Rodriques -# -# This is a pure Pythonic implementation of a set class. The syntax -# and methods implemented are, for the most part, borrowed from -# PEP 218 by Greg Wilson. -# -# Note that this class violates the formal definition of a set() by adding -# a __getitem__() method so we can iterate over a set's elements under -# Python 1.5 and 2.1, which don't support __iter__() and iterator types. -# - -import string - -class Set: - """The set class. It can contain mutable objects.""" - - def __init__(self, seq = None): - """The constructor. It can take any object giving an iterator as an optional - argument to populate the new set.""" - self.elems = [] - if seq: - for elem in seq: - if elem not in self.elems: - hash(elem) - self.elems.append(elem) - - def __str__(self): - return "set([%s])" % string.join(map(str, self.elems), ", ") - - - def copy(self): - """Shallow copy of a set object.""" - return Set(self.elems) - - def __contains__(self, elem): - return elem in self.elems - - def __len__(self): - return len(self.elems) - - def __getitem__(self, index): - # Added so that Python 1.5 can iterate over the elements. - # The cookbook recipe's author didn't like this because there - # really isn't any order in a set object, but this is necessary - # to make the class work well enough for our purposes. - return self.elems[index] - - def items(self): - """Returns a list of the elements in the set.""" - return self.elems - - def add(self, elem): - """Add one element to the set.""" - if elem not in self.elems: - hash(elem) - self.elems.append(elem) - - def remove(self, elem): - """Remove an element from the set. Return an error if elem is not in the set.""" - try: - self.elems.remove(elem) - except ValueError: - raise LookupError, "Object %s is not a member of the set." % str(elem) - - def discard(self, elem): - """Remove an element from the set. Do nothing if elem is not in the set.""" - try: - self.elems.remove(elem) - except ValueError: - pass - - def sort(self, func=cmp): - self.elems.sort(func) - - #Define an iterator for a set. - def __iter__(self): - return iter(self.elems) - - #The basic binary operations with sets. - def __or__(self, other): - """Union of two sets.""" - ret = self.copy() - for elem in other.elems: - if elem not in ret: - ret.elems.append(elem) - return ret - - def __sub__(self, other): - """Difference of two sets.""" - ret = self.copy() - for elem in other.elems: - ret.discard(elem) - return ret - - def __and__(self, other): - """Intersection of two sets.""" - ret = Set() - for elem in self.elems: - if elem in other.elems: - ret.elems.append(elem) - return ret - - def __add__(self, other): - """Symmetric difference of two sets.""" - ret = Set() - temp = other.copy() - for elem in self.elems: - if elem in temp.elems: - temp.elems.remove(elem) - else: - ret.elems.append(elem) - #Add remaining elements. - for elem in temp.elems: - ret.elems.append(elem) - return ret - - def __mul__(self, other): - """Cartesian product of two sets.""" - ret = Set() - for elemself in self.elems: - x = map(lambda other, s=elemself: (s, other), other.elems) - ret.elems.extend(x) - return ret - - #Some of the binary comparisons. - def __lt__(self, other): - """Returns 1 if the lhs set is contained but not equal to the rhs set.""" - if len(self.elems) < len(other.elems): - temp = other.copy() - for elem in self.elems: - if elem in temp.elems: - temp.remove(elem) - else: - return 0 - return len(temp.elems) == 0 - else: - return 0 - - def __le__(self, other): - """Returns 1 if the lhs set is contained in the rhs set.""" - if len(self.elems) <= len(other.elems): - ret = 1 - for elem in self.elems: - if elem not in other.elems: - ret = 0 - break - return ret - else: - return 0 - - def __eq__(self, other): - """Returns 1 if the sets are equal.""" - if len(self.elems) != len(other.elems): - return 0 - else: - return len(self - other) == 0 - - def __cmp__(self, other): - """Returns 1 if the sets are equal.""" - if self.__lt__(other): - return -1 - elif other.__lt__(self): - return 1 - else: - return 0 diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_shlex.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_shlex.py deleted file mode 100644 index d6c10357ad..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_shlex.py +++ /dev/null @@ -1,319 +0,0 @@ -# -*- coding: iso-8859-1 -*- -"""A lexical analyzer class for simple shell-like syntaxes.""" - -# Module and documentation by Eric S. Raymond, 21 Dec 1998 -# Input stacking and error message cleanup added by ESR, March 2000 -# push_source() and pop_source() made explicit by ESR, January 2001. -# Posix compliance, split(), string arguments, and -# iterator interface by Gustavo Niemeyer, April 2003. - -import os.path -import sys -#from collections import deque - -class deque: - def __init__(self): - self.data = [] - def __len__(self): - return len(self.data) - def appendleft(self, item): - self.data.insert(0, item) - def popleft(self): - return self.data.pop(0) - -try: - basestring -except NameError: - import types - def is_basestring(s): - return type(s) is types.StringType -else: - def is_basestring(s): - return isinstance(s, basestring) - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -__all__ = ["shlex", "split"] - -class shlex: - "A lexical analyzer class for simple shell-like syntaxes." - def __init__(self, instream=None, infile=None, posix=False): - if is_basestring(instream): - instream = StringIO(instream) - if instream is not None: - self.instream = instream - self.infile = infile - else: - self.instream = sys.stdin - self.infile = None - self.posix = posix - if posix: - self.eof = None - else: - self.eof = '' - self.commenters = '#' - self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_') - if self.posix: - self.wordchars = self.wordchars + ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' - 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') - self.whitespace = ' \t\r\n' - self.whitespace_split = False - self.quotes = '\'"' - self.escape = '\\' - self.escapedquotes = '"' - self.state = ' ' - self.pushback = deque() - self.lineno = 1 - self.debug = 0 - self.token = '' - self.filestack = deque() - self.source = None - if self.debug: - print 'shlex: reading from %s, line %d' \ - % (self.instream, self.lineno) - - def push_token(self, tok): - "Push a token onto the stack popped by the get_token method" - if self.debug >= 1: - print "shlex: pushing token " + repr(tok) - self.pushback.appendleft(tok) - - def push_source(self, newstream, newfile=None): - "Push an input source onto the lexer's input source stack." - if is_basestring(newstream): - newstream = StringIO(newstream) - self.filestack.appendleft((self.infile, self.instream, self.lineno)) - self.infile = newfile - self.instream = newstream - self.lineno = 1 - if self.debug: - if newfile is not None: - print 'shlex: pushing to file %s' % (self.infile,) - else: - print 'shlex: pushing to stream %s' % (self.instream,) - - def pop_source(self): - "Pop the input source stack." - self.instream.close() - (self.infile, self.instream, self.lineno) = self.filestack.popleft() - if self.debug: - print 'shlex: popping to %s, line %d' \ - % (self.instream, self.lineno) - self.state = ' ' - - def get_token(self): - "Get a token from the input stream (or from stack if it's nonempty)" - if self.pushback: - tok = self.pushback.popleft() - if self.debug >= 1: - print "shlex: popping token " + repr(tok) - return tok - # No pushback. Get a token. - raw = self.read_token() - # Handle inclusions - if self.source is not None: - while raw == self.source: - spec = self.sourcehook(self.read_token()) - if spec: - (newfile, newstream) = spec - self.push_source(newstream, newfile) - raw = self.get_token() - # Maybe we got EOF instead? - while raw == self.eof: - if not self.filestack: - return self.eof - else: - self.pop_source() - raw = self.get_token() - # Neither inclusion nor EOF - if self.debug >= 1: - if raw != self.eof: - print "shlex: token=" + repr(raw) - else: - print "shlex: token=EOF" - return raw - - def read_token(self): - quoted = False - escapedstate = ' ' - while True: - nextchar = self.instream.read(1) - if nextchar == '\n': - self.lineno = self.lineno + 1 - if self.debug >= 3: - print "shlex: in state", repr(self.state), \ - "I see character:", repr(nextchar) - if self.state is None: - self.token = '' # past end of file - break - elif self.state == ' ': - if not nextchar: - self.state = None # end of file - break - elif nextchar in self.whitespace: - if self.debug >= 2: - print "shlex: I see whitespace in whitespace state" - if self.token or (self.posix and quoted): - break # emit current token - else: - continue - elif nextchar in self.commenters: - self.instream.readline() - self.lineno = self.lineno + 1 - elif self.posix and nextchar in self.escape: - escapedstate = 'a' - self.state = nextchar - elif nextchar in self.wordchars: - self.token = nextchar - self.state = 'a' - elif nextchar in self.quotes: - if not self.posix: - self.token = nextchar - self.state = nextchar - elif self.whitespace_split: - self.token = nextchar - self.state = 'a' - else: - self.token = nextchar - if self.token or (self.posix and quoted): - break # emit current token - else: - continue - elif self.state in self.quotes: - quoted = True - if not nextchar: # end of file - if self.debug >= 2: - print "shlex: I see EOF in quotes state" - # XXX what error should be raised here? - raise ValueError, "No closing quotation" - if nextchar == self.state: - if not self.posix: - self.token = self.token + nextchar - self.state = ' ' - break - else: - self.state = 'a' - elif self.posix and nextchar in self.escape and \ - self.state in self.escapedquotes: - escapedstate = self.state - self.state = nextchar - else: - self.token = self.token + nextchar - elif self.state in self.escape: - if not nextchar: # end of file - if self.debug >= 2: - print "shlex: I see EOF in escape state" - # XXX what error should be raised here? - raise ValueError, "No escaped character" - # In posix shells, only the quote itself or the escape - # character may be escaped within quotes. - if escapedstate in self.quotes and \ - nextchar != self.state and nextchar != escapedstate: - self.token = self.token + self.state - self.token = self.token + nextchar - self.state = escapedstate - elif self.state == 'a': - if not nextchar: - self.state = None # end of file - break - elif nextchar in self.whitespace: - if self.debug >= 2: - print "shlex: I see whitespace in word state" - self.state = ' ' - if self.token or (self.posix and quoted): - break # emit current token - else: - continue - elif nextchar in self.commenters: - self.instream.readline() - self.lineno = self.lineno + 1 - if self.posix: - self.state = ' ' - if self.token or (self.posix and quoted): - break # emit current token - else: - continue - elif self.posix and nextchar in self.quotes: - self.state = nextchar - elif self.posix and nextchar in self.escape: - escapedstate = 'a' - self.state = nextchar - elif nextchar in self.wordchars or nextchar in self.quotes \ - or self.whitespace_split: - self.token = self.token + nextchar - else: - self.pushback.appendleft(nextchar) - if self.debug >= 2: - print "shlex: I see punctuation in word state" - self.state = ' ' - if self.token: - break # emit current token - else: - continue - result = self.token - self.token = '' - if self.posix and not quoted and result == '': - result = None - if self.debug > 1: - if result: - print "shlex: raw token=" + repr(result) - else: - print "shlex: raw token=EOF" - return result - - def sourcehook(self, newfile): - "Hook called on a filename to be sourced." - if newfile[0] == '"': - newfile = newfile[1:-1] - # This implements cpp-like semantics for relative-path inclusion. - if is_basestring(self.infile) and not os.path.isabs(newfile): - newfile = os.path.join(os.path.dirname(self.infile), newfile) - return (newfile, open(newfile, "r")) - - def error_leader(self, infile=None, lineno=None): - "Emit a C-compiler-like, Emacs-friendly error-message leader." - if infile is None: - infile = self.infile - if lineno is None: - lineno = self.lineno - return "\"%s\", line %d: " % (infile, lineno) - - def __iter__(self): - return self - - def next(self): - token = self.get_token() - if token == self.eof: - raise StopIteration - return token - -def split(s, comments=False): - lex = shlex(s, posix=True) - lex.whitespace_split = True - if not comments: - lex.commenters = '' - #return list(lex) - result = [] - while True: - token = lex.get_token() - if token == lex.eof: - break - result.append(token) - return result - -if __name__ == '__main__': - if len(sys.argv) == 1: - lexer = shlex() - else: - file = sys.argv[1] - lexer = shlex(open(file), file) - while 1: - tt = lexer.get_token() - if tt: - print "Token: " + repr(tt) - else: - break diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_subprocess.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_subprocess.py deleted file mode 100644 index 68d0e4c850..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_subprocess.py +++ /dev/null @@ -1,1290 +0,0 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# This module should remain compatible with Python 2.2, see PEP 291. -# -# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se> -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""subprocess - Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. This module -intends to replace several other, older modules and functions, like: - -os.system -os.spawn* -os.popen* -popen2.* -commands.* - -Information about how the subprocess module can be used to replace these -modules and functions can be found below. - - - -Using the subprocess module -=========================== -This module defines one class called Popen: - -class Popen(args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - - -Arguments are: - -args should be a string, or a sequence of program arguments. The -program to execute is normally the first item in the args sequence or -string, but can be explicitly set by using the executable argument. - -On UNIX, with shell=False (default): In this case, the Popen class -uses os.execvp() to execute the child program. args should normally -be a sequence. A string will be treated as a sequence with the string -as the only item (the program to execute). - -On UNIX, with shell=True: If args is a string, it specifies the -command string to execute through the shell. If args is a sequence, -the first item specifies the command string, and any additional items -will be treated as additional shell arguments. - -On Windows: the Popen class uses CreateProcess() to execute the child -program, which operates on strings. If args is a sequence, it will be -converted to a string using the list2cmdline method. Please note that -not all MS Windows applications interpret the command line the same -way: The list2cmdline is designed for applications using the same -rules as the MS C runtime. - -bufsize, if given, has the same meaning as the corresponding argument -to the built-in open() function: 0 means unbuffered, 1 means line -buffered, any other positive value means use a buffer of -(approximately) that size. A negative bufsize means to use the system -default, which usually means fully buffered. The default value for -bufsize is 0 (unbuffered). - -stdin, stdout and stderr specify the executed programs' standard -input, standard output and standard error file handles, respectively. -Valid values are PIPE, an existing file descriptor (a positive -integer), an existing file object, and None. PIPE indicates that a -new pipe to the child should be created. With None, no redirection -will occur; the child's file handles will be inherited from the -parent. Additionally, stderr can be STDOUT, which indicates that the -stderr data from the applications should be captured into the same -file handle as for stdout. - -If preexec_fn is set to a callable object, this object will be called -in the child process just before the child is executed. - -If close_fds is true, all file descriptors except 0, 1 and 2 will be -closed before the child process is executed. - -if shell is true, the specified command will be executed through the -shell. - -If cwd is not None, the current directory will be changed to cwd -before the child is executed. - -If env is not None, it defines the environment variables for the new -process. - -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Note: This feature is only -available if Python is built with universal newline support (the -default). Also, the newlines attribute of the file objects stdout, -stdin and stderr are not updated by the communicate() method. - -The startupinfo and creationflags, if given, will be passed to the -underlying CreateProcess() function. They can specify things such as -appearance of the main window and priority for the new process. -(Windows only) - - -This module also defines two shortcut functions: - -call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - -check_call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete. If the - exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - -Exceptions ----------- -Exceptions raised in the child process, before the new program has -started to execute, will be re-raised in the parent. Additionally, -the exception object will have one extra attribute called -'child_traceback', which is a string containing traceback information -from the childs point of view. - -The most common exception raised is OSError. This occurs, for -example, when trying to execute a non-existent file. Applications -should prepare for OSErrors. - -A ValueError will be raised if Popen is called with invalid arguments. - -check_call() will raise CalledProcessError, if the called process -returns a non-zero return code. - - -Security --------- -Unlike some other popen functions, this implementation will never call -/bin/sh implicitly. This means that all characters, including shell -metacharacters, can safely be passed to child processes. - - -Popen objects -============= -Instances of the Popen class have the following methods: - -poll() - Check if child process has terminated. Returns returncode - attribute. - -wait() - Wait for child process to terminate. Returns returncode attribute. - -communicate(input=None) - Interact with process: Send data to stdin. Read data from stdout - and stderr, until end-of-file is reached. Wait for process to - terminate. The optional stdin argument should be a string to be - sent to the child process, or None, if no data should be sent to - the child. - - communicate() returns a tuple (stdout, stderr). - - Note: The data read is buffered in memory, so do not use this - method if the data size is large or unlimited. - -The following attributes are also available: - -stdin - If the stdin argument is PIPE, this attribute is a file object - that provides input to the child process. Otherwise, it is None. - -stdout - If the stdout argument is PIPE, this attribute is a file object - that provides output from the child process. Otherwise, it is - None. - -stderr - If the stderr argument is PIPE, this attribute is file object that - provides error output from the child process. Otherwise, it is - None. - -pid - The process ID of the child process. - -returncode - The child return code. A None value indicates that the process - hasn't terminated yet. A negative value -N indicates that the - child was terminated by signal N (UNIX only). - - -Replacing older functions with the subprocess module -==================================================== -In this section, "a ==> b" means that b can be used as a replacement -for a. - -Note: All functions in this section fail (more or less) silently if -the executed program cannot be found; this module raises an OSError -exception. - -In the following examples, we assume that the subprocess module is -imported with "from subprocess import *". - - -Replacing /bin/sh shell backquote ---------------------------------- -output=`mycmd myarg` -==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] - - -Replacing shell pipe line -------------------------- -output=`dmesg | grep hda` -==> -p1 = Popen(["dmesg"], stdout=PIPE) -p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) -output = p2.communicate()[0] - - -Replacing os.system() ---------------------- -sts = os.system("mycmd" + " myarg") -==> -p = Popen("mycmd" + " myarg", shell=True) -pid, sts = os.waitpid(p.pid, 0) - -Note: - -* Calling the program through the shell is usually not required. - -* It's easier to look at the returncode attribute than the - exitstatus. - -A more real-world example would look like this: - -try: - retcode = call("mycmd" + " myarg", shell=True) - if retcode < 0: - print >>sys.stderr, "Child was terminated by signal", -retcode - else: - print >>sys.stderr, "Child returned", retcode -except OSError, e: - print >>sys.stderr, "Execution failed:", e - - -Replacing os.spawn* -------------------- -P_NOWAIT example: - -pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") -==> -pid = Popen(["/bin/mycmd", "myarg"]).pid - - -P_WAIT example: - -retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") -==> -retcode = call(["/bin/mycmd", "myarg"]) - - -Vector example: - -os.spawnvp(os.P_NOWAIT, path, args) -==> -Popen([path] + args[1:]) - - -Environment example: - -os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) -==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) - - -Replacing os.popen* -------------------- -pipe = os.popen(cmd, mode='r', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout - -pipe = os.popen(cmd, mode='w', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin - - -(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdin, child_stdout) = (p.stdin, p.stdout) - - -(child_stdin, - child_stdout, - child_stderr) = os.popen3(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -(child_stdin, - child_stdout, - child_stderr) = (p.stdin, p.stdout, p.stderr) - - -(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) -(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) - - -Replacing popen2.* ------------------- -Note: If the cmd argument to popen2 functions is a string, the command -is executed through /bin/sh. If it is a list, the command is directly -executed. - -(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) -==> -p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - - -(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) -==> -p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - -The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, -except that: - -* subprocess.Popen raises an exception if the execution fails -* the capturestderr argument is replaced with the stderr argument. -* stdin=PIPE and stdout=PIPE must be specified. -* popen2 closes all filedescriptors by default, but you have to specify - close_fds=True with subprocess.Popen. - - -""" - -import sys -mswindows = (sys.platform == "win32") - -import os -import string -import types -import traceback - -# Exception classes used by this module. -class CalledProcessError(Exception): - """This exception is raised when a process run by check_call() returns - a non-zero exit status. The exit status will be stored in the - returncode attribute.""" - def __init__(self, returncode, cmd): - self.returncode = returncode - self.cmd = cmd - def __str__(self): - return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - - -if mswindows: - try: - import threading - except ImportError: - # SCons: the threading module is only used by the communicate() - # method, which we don't actually use, so don't worry if we - # can't import it. - pass - import msvcrt - if 0: # <-- change this to use pywin32 instead of the _subprocess driver - import pywintypes - from win32api import GetStdHandle, STD_INPUT_HANDLE, \ - STD_OUTPUT_HANDLE, STD_ERROR_HANDLE - from win32api import GetCurrentProcess, DuplicateHandle, \ - GetModuleFileName, GetVersion - from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE - from win32pipe import CreatePipe - from win32process import CreateProcess, STARTUPINFO, \ - GetExitCodeProcess, STARTF_USESTDHANDLES, \ - STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE - from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 - else: - # SCons: don't die on Python versions that don't have _subprocess. - try: - from _subprocess import * - except ImportError: - pass - class STARTUPINFO: - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes: - error = IOError -else: - import select - import errno - import fcntl - import pickle - - try: - fcntl.F_GETFD - except AttributeError: - fcntl.F_GETFD = 1 - - try: - fcntl.F_SETFD - except AttributeError: - fcntl.F_SETFD = 2 - -__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] - -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts -except: - MAXFD = 256 - -# True/False does not exist on 2.2.0 -try: - False -except NameError: - False = 0 - True = 1 - -try: - isinstance(1, int) -except TypeError: - def is_int(obj): - return type(obj) == type(1) - def is_int_or_long(obj): - return type(obj) in (type(1), type(1L)) -else: - def is_int(obj): - return isinstance(obj, int) - def is_int_or_long(obj): - return isinstance(obj, (int, long)) - -try: - types.StringTypes -except AttributeError: - try: - types.StringTypes = (types.StringType, types.UnicodeType) - except AttributeError: - types.StringTypes = (types.StringType,) - def is_string(obj): - return type(obj) in types.StringTypes -else: - def is_string(obj): - return isinstance(obj, types.StringTypes) - -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 0: - try: - _active.remove(inst) - except ValueError: - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass - -PIPE = -1 -STDOUT = -2 - - -def call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - return apply(Popen, popenargs, kwargs).wait() - - -def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - """ - retcode = apply(call, popenargs, kwargs) - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - if retcode: - raise CalledProcessError(retcode, cmd) - return retcode - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp - result = [] - needquote = False - for arg in seq: - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backspaces. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backspaces, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return string.join(result, '') - - -try: - object -except NameError: - class object: - pass - -class Popen(object): - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - """Create new Popen instance.""" - _cleanup() - - self._child_created = False - if not is_int_or_long(bufsize): - raise TypeError("bufsize must be an integer") - - if mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - if close_fds: - raise ValueError("close_fds is not supported on Windows " - "platforms") - else: - # POSIX - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.universal_newlines = universal_newlines - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are None when not using PIPEs. The child objects are None - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - self._execute_child(args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) - if c2pread: - if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) - else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) - if errread: - if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) - else: - self.stderr = os.fdopen(errread, 'rb', bufsize) - - - def _translate_newlines(self, data): - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") - return data - - - def __del__(self): - if not self._child_created: - # We didn't get to successfully create a child process. - return - # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxint) - if self.returncode is None and _active is not None: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - - # Optimization: If we are only using one pipe, or no pipe at - # all, using select() or threads is unnecessary. - if [self.stdin, self.stdout, self.stderr].count(None) >= 2: - stdout = None - stderr = None - if self.stdin: - if input: - self.stdin.write(input) - self.stdin.close() - elif self.stdout: - stdout = self.stdout.read() - elif self.stderr: - stderr = self.stderr.read() - self.wait() - return (stdout, stderr) - - return self._communicate(input) - - - if mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin is None and stdout is None and stderr is None: - return (None, None, None, None, None, None) - - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - p2cread = GetStdHandle(STD_INPUT_HANDLE) - elif stdin == PIPE: - p2cread, p2cwrite = CreatePipe(None, 0) - # Detach and turn into fd - p2cwrite = p2cwrite.Detach() - p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) - elif is_int(stdin): - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout is None: - c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) - elif stdout == PIPE: - c2pread, c2pwrite = CreatePipe(None, 0) - # Detach and turn into fd - c2pread = c2pread.Detach() - c2pread = msvcrt.open_osfhandle(c2pread, 0) - elif is_int(stdout): - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr is None: - errwrite = GetStdHandle(STD_ERROR_HANDLE) - elif stderr == PIPE: - errread, errwrite = CreatePipe(None, 0) - # Detach and turn into fd - errread = errread.Detach() - errread = msvcrt.open_osfhandle(errread, 0) - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = msvcrt.get_osfhandle(stderr) - else: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - return DuplicateHandle(GetCurrentProcess(), handle, - GetCurrentProcess(), 0, 1, - DUPLICATE_SAME_ACCESS) - - - def _find_w9xpopen(self): - """Find and return absolut path to w9xpopen.exe""" - w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - # Eeek - file-not-found - possibly an embedding - # situation - see if we can locate it in sys.exec_prefix - w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - raise RuntimeError("Cannot locate w9xpopen.exe, which is " - "needed for Popen to work with your " - "shell or platform.") - return w9xpopen - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (MS Windows version)""" - - if not isinstance(args, types.StringTypes): - args = list2cmdline(args) - - # Process startup details - if startupinfo is None: - startupinfo = STARTUPINFO() - if None not in (p2cread, c2pwrite, errwrite): - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - if shell: - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = comspec + " /c " + args - if (GetVersion() >= 0x80000000L or - os.path.basename(comspec).lower() == "command.com"): - # Win9x, or using command.com on NT. We need to - # use the w9xpopen intermediate program. For more - # information, see KB Q150956 - # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) - w9xpopen = self._find_w9xpopen() - args = '"%s" %s' % (w9xpopen, args) - # Not passing CREATE_NEW_CONSOLE has been known to - # cause random failures on win9x. Specifically a - # dialog: "Your program accessed mem currently in - # use at xxx" and a hopeful warning about the - # stability of your system. Cost is Ctrl+C wont - # kill children. - creationflags = creationflags | CREATE_NEW_CONSOLE - - # Start the process - try: - hp, ht, pid, tid = CreateProcess(executable, args, - # no special security - None, None, - # must inherit handles to pass std - # handles - 1, - creationflags, - env, - cwd, - startupinfo) - except pywintypes.error, e: - # Translate pywintypes.error to WindowsError, which is - # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but - # how can this be done from Python? - raise apply(WindowsError, e.args) - - # Retain the process handle, but close the thread handle - self._child_created = True - self._handle = hp - self.pid = pid - ht.Close() - - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. - if p2cread is not None: - p2cread.Close() - if c2pwrite is not None: - c2pwrite.Close() - if errwrite is not None: - errwrite.Close() - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - obj = WaitForSingleObject(self._handle, INFINITE) - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - - - def _communicate(self, input): - stdout = None # Return - stderr = None # Return - - if self.stdout: - stdout = [] - stdout_thread = threading.Thread(target=self._readerthread, - args=(self.stdout, stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() - if self.stderr: - stderr = [] - stderr_thread = threading.Thread(target=self._readerthread, - args=(self.stderr, stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() - - if self.stdin: - if input is not None: - self.stdin.write(input) - self.stdin.close() - - if self.stdout: - stdout_thread.join() - if self.stderr: - stderr_thread.join() - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = stdout[0] - if stderr is not None: - stderr = stderr[0] - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif is_int(stdin): - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout is None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif is_int(stdout): - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr is None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _set_cloexec_flag(self, fd): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - - old = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) - - - def _close_fds(self, but): - for i in xrange(3, MAXFD): - if i == but: - continue - try: - os.close(i) - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - except: - pass - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (POSIX version)""" - - if is_string(args): - args = [args] - - if shell: - args = ["/bin/sh", "-c"] + args - - if executable is None: - executable = args[0] - - # For transferring possible exec failure from child to parent - # The first char specifies the exception type: 0 means - # OSError, 1 means some other error. - errpipe_read, errpipe_write = os.pipe() - self._set_cloexec_flag(errpipe_write) - - self.pid = os.fork() - self._child_created = True - if self.pid == 0: - # Child - try: - # Close parent's pipe ends - if p2cwrite: - os.close(p2cwrite) - if c2pread: - os.close(c2pread) - if errread: - os.close(errread) - os.close(errpipe_read) - - # Dup fds for child - if p2cread: - os.dup2(p2cread, 0) - if c2pwrite: - os.dup2(c2pwrite, 1) - if errwrite: - os.dup2(errwrite, 2) - - # Close pipe fds. Make sure we don't close the same - # fd more than once, or standard fds. - try: - set - except NameError: - # Fall-back for earlier Python versions, so epydoc - # can use this module directly to execute things. - if p2cread: - os.close(p2cread) - if c2pwrite and c2pwrite not in (p2cread,): - os.close(c2pwrite) - if errwrite and errwrite not in (p2cread, c2pwrite): - os.close(errwrite) - else: - for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): - if fd: os.close(fd) - - # Close all other fds, if asked for - if close_fds: - self._close_fds(but=errpipe_write) - - if cwd is not None: - os.chdir(cwd) - - if preexec_fn: - apply(preexec_fn) - - if env is None: - os.execvp(executable, args) - else: - os.execvpe(executable, args, env) - - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - - except: - exc_type, exc_value, tb = sys.exc_info() - # Save the traceback and attach it to the exception object - exc_lines = traceback.format_exception(exc_type, - exc_value, - tb) - exc_value.child_traceback = string.join(exc_lines, '') - os.write(errpipe_write, pickle.dumps(exc_value)) - - # This exitcode won't be reported to applications, so it - # really doesn't matter what we return. - os._exit(255) - - # Parent - os.close(errpipe_write) - if p2cread and p2cwrite: - os.close(p2cread) - if c2pwrite and c2pread: - os.close(c2pwrite) - if errwrite and errread: - os.close(errwrite) - - # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB - os.close(errpipe_read) - if data != "": - os.waitpid(self.pid, 0) - child_exception = pickle.loads(data) - raise child_exception - - - def _handle_exitstatus(self, sts): - if os.WIFSIGNALED(sts): - self.returncode = -os.WTERMSIG(sts) - elif os.WIFEXITED(sts): - self.returncode = os.WEXITSTATUS(sts) - else: - # Should never happen - raise RuntimeError("Unknown child exit status!") - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except os.error: - if _deadstate is not None: - self.returncode = _deadstate - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - pid, sts = os.waitpid(self.pid, 0) - self._handle_exitstatus(sts) - return self.returncode - - - def _communicate(self, input): - read_set = [] - write_set = [] - stdout = None # Return - stderr = None # Return - - if self.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - self.stdin.flush() - if input: - write_set.append(self.stdin) - else: - self.stdin.close() - if self.stdout: - read_set.append(self.stdout) - stdout = [] - if self.stderr: - read_set.append(self.stderr) - stderr = [] - - input_offset = 0 - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if self.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512)) - input_offset = input_offset + bytes_written - if input_offset >= len(input): - self.stdin.close() - write_set.remove(self.stdin) - - if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) - if data == "": - self.stdout.close() - read_set.remove(self.stdout) - stdout.append(data) - - if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) - if data == "": - self.stderr.close() - read_set.remove(self.stderr) - stderr.append(data) - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = string.join(stdout, '') - if stderr is not None: - stderr = string.join(stderr, '') - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - -def _demo_posix(): - # - # Example 1: Simple redirection: Get process list - # - plist = Popen(["ps"], stdout=PIPE).communicate()[0] - print "Process list:" - print plist - - # - # Example 2: Change uid before executing child - # - if os.getuid() == 0: - p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) - p.wait() - - # - # Example 3: Connecting several subprocesses - # - print "Looking for 'hda'..." - p1 = Popen(["dmesg"], stdout=PIPE) - p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 4: Catch execution error - # - print - print "Trying a weird file..." - try: - print Popen(["/this/path/does/not/exist"]).communicate() - except OSError, e: - if e.errno == errno.ENOENT: - print "The file didn't exist. I thought so..." - print "Child traceback:" - print e.child_traceback - else: - print "Error", e.errno - else: - sys.stderr.write( "Gosh. No error.\n" ) - - -def _demo_windows(): - # - # Example 1: Connecting several subprocesses - # - print "Looking for 'PROMPT' in set output..." - p1 = Popen("set", stdout=PIPE, shell=True) - p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 2: Simple execution of program - # - print "Executing calc..." - p = Popen("calc") - p.wait() - - -if __name__ == "__main__": - if mswindows: - _demo_windows() - else: - _demo_posix() diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_textwrap.py b/tools/scons/scons-local-1.2.0/SCons/compat/_scons_textwrap.py deleted file mode 100644 index 72ed9b9c5e..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/_scons_textwrap.py +++ /dev/null @@ -1,376 +0,0 @@ -"""Text wrapping and filling. -""" - -# Copyright (C) 1999-2001 Gregory P. Ward. -# Copyright (C) 2002, 2003 Python Software Foundation. -# Written by Greg Ward <gward@python.net> - -__revision__ = "$Id: textwrap.py,v 1.32.8.2 2004/05/13 01:48:15 gward Exp $" - -import string, re - -try: - unicode -except NameError: - class unicode: - pass - -# Do the right thing with boolean values for all known Python versions -# (so this module can be copied to projects that don't depend on Python -# 2.3, e.g. Optik and Docutils). -try: - True, False -except NameError: - (True, False) = (1, 0) - -__all__ = ['TextWrapper', 'wrap', 'fill'] - -# Hardcode the recognized whitespace characters to the US-ASCII -# whitespace characters. The main reason for doing this is that in -# ISO-8859-1, 0xa0 is non-breaking whitespace, so in certain locales -# that character winds up in string.whitespace. Respecting -# string.whitespace in those cases would 1) make textwrap treat 0xa0 the -# same as any other whitespace char, which is clearly wrong (it's a -# *non-breaking* space), 2) possibly cause problems with Unicode, -# since 0xa0 is not in range(128). -_whitespace = '\t\n\x0b\x0c\r ' - -class TextWrapper: - """ - Object for wrapping/filling text. The public interface consists of - the wrap() and fill() methods; the other methods are just there for - subclasses to override in order to tweak the default behaviour. - If you want to completely replace the main wrapping algorithm, - you'll probably have to override _wrap_chunks(). - - Several instance attributes control various aspects of wrapping: - width (default: 70) - the maximum width of wrapped lines (unless break_long_words - is false) - initial_indent (default: "") - string that will be prepended to the first line of wrapped - output. Counts towards the line's width. - subsequent_indent (default: "") - string that will be prepended to all lines save the first - of wrapped output; also counts towards each line's width. - expand_tabs (default: true) - Expand tabs in input text to spaces before further processing. - Each tab will become 1 .. 8 spaces, depending on its position in - its line. If false, each tab is treated as a single character. - replace_whitespace (default: true) - Replace all whitespace characters in the input text by spaces - after tab expansion. Note that if expand_tabs is false and - replace_whitespace is true, every tab will be converted to a - single space! - fix_sentence_endings (default: false) - Ensure that sentence-ending punctuation is always followed - by two spaces. Off by default because the algorithm is - (unavoidably) imperfect. - break_long_words (default: true) - Break words longer than 'width'. If false, those words will not - be broken, and some lines might be longer than 'width'. - """ - - whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace)) - - unicode_whitespace_trans = {} - try: - uspace = eval("ord(u' ')") - except SyntaxError: - # Python1.5 doesn't understand u'' syntax, in which case we - # won't actually use the unicode translation below, so it - # doesn't matter what value we put in the table. - uspace = ord(' ') - for x in map(ord, _whitespace): - unicode_whitespace_trans[x] = uspace - - # This funky little regex is just the trick for splitting - # text up into word-wrappable chunks. E.g. - # "Hello there -- you goof-ball, use the -b option!" - # splits into - # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option! - # (after stripping out empty strings). - try: - wordsep_re = re.compile(r'(\s+|' # any whitespace - r'[^\s\w]*\w{2,}-(?=\w{2,})|' # hyphenated words - r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash - except re.error: - # Pre-2.0 Python versions don't have the (?<= negative look-behind - # assertion. It mostly doesn't matter for the simple input - # SCons is going to give it, so just leave it out. - wordsep_re = re.compile(r'(\s+|' # any whitespace - r'-*\w{2,}-(?=\w{2,}))') # hyphenated words - - # XXX will there be a locale-or-charset-aware version of - # string.lowercase in 2.3? - sentence_end_re = re.compile(r'[%s]' # lowercase letter - r'[\.\!\?]' # sentence-ending punct. - r'[\"\']?' # optional end-of-quote - % string.lowercase) - - - def __init__(self, - width=70, - initial_indent="", - subsequent_indent="", - expand_tabs=True, - replace_whitespace=True, - fix_sentence_endings=False, - break_long_words=True): - self.width = width - self.initial_indent = initial_indent - self.subsequent_indent = subsequent_indent - self.expand_tabs = expand_tabs - self.replace_whitespace = replace_whitespace - self.fix_sentence_endings = fix_sentence_endings - self.break_long_words = break_long_words - - - # -- Private methods ----------------------------------------------- - # (possibly useful for subclasses to override) - - def _munge_whitespace(self, text): - """_munge_whitespace(text : string) -> string - - Munge whitespace in text: expand tabs and convert all other - whitespace characters to spaces. Eg. " foo\tbar\n\nbaz" - becomes " foo bar baz". - """ - if self.expand_tabs: - text = string.expandtabs(text) - if self.replace_whitespace: - if type(text) == type(''): - text = string.translate(text, self.whitespace_trans) - elif isinstance(text, unicode): - text = string.translate(text, self.unicode_whitespace_trans) - return text - - - def _split(self, text): - """_split(text : string) -> [string] - - Split the text to wrap into indivisible chunks. Chunks are - not quite the same as words; see wrap_chunks() for full - details. As an example, the text - Look, goof-ball -- use the -b option! - breaks into the following chunks: - 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ', - 'use', ' ', 'the', ' ', '-b', ' ', 'option!' - """ - chunks = self.wordsep_re.split(text) - chunks = filter(None, chunks) - return chunks - - def _fix_sentence_endings(self, chunks): - """_fix_sentence_endings(chunks : [string]) - - Correct for sentence endings buried in 'chunks'. Eg. when the - original text contains "... foo.\nBar ...", munge_whitespace() - and split() will convert that to [..., "foo.", " ", "Bar", ...] - which has one too few spaces; this method simply changes the one - space to two. - """ - i = 0 - pat = self.sentence_end_re - while i < len(chunks)-1: - if chunks[i+1] == " " and pat.search(chunks[i]): - chunks[i+1] = " " - i = i + 2 - else: - i = i + 1 - - def _handle_long_word(self, chunks, cur_line, cur_len, width): - """_handle_long_word(chunks : [string], - cur_line : [string], - cur_len : int, width : int) - - Handle a chunk of text (most likely a word, not whitespace) that - is too long to fit in any line. - """ - space_left = max(width - cur_len, 1) - - # If we're allowed to break long words, then do so: put as much - # of the next chunk onto the current line as will fit. - if self.break_long_words: - cur_line.append(chunks[0][0:space_left]) - chunks[0] = chunks[0][space_left:] - - # Otherwise, we have to preserve the long word intact. Only add - # it to the current line if there's nothing already there -- - # that minimizes how much we violate the width constraint. - elif not cur_line: - cur_line.append(chunks.pop(0)) - - # If we're not allowed to break long words, and there's already - # text on the current line, do nothing. Next time through the - # main loop of _wrap_chunks(), we'll wind up here again, but - # cur_len will be zero, so the next line will be entirely - # devoted to the long word that we can't handle right now. - - def _wrap_chunks(self, chunks): - """_wrap_chunks(chunks : [string]) -> [string] - - Wrap a sequence of text chunks and return a list of lines of - length 'self.width' or less. (If 'break_long_words' is false, - some lines may be longer than this.) Chunks correspond roughly - to words and the whitespace between them: each chunk is - indivisible (modulo 'break_long_words'), but a line break can - come between any two chunks. Chunks should not have internal - whitespace; ie. a chunk is either all whitespace or a "word". - Whitespace chunks will be removed from the beginning and end of - lines, but apart from that whitespace is preserved. - """ - lines = [] - if self.width <= 0: - raise ValueError("invalid width %r (must be > 0)" % self.width) - - while chunks: - - # Start the list of chunks that will make up the current line. - # cur_len is just the length of all the chunks in cur_line. - cur_line = [] - cur_len = 0 - - # Figure out which static string will prefix this line. - if lines: - indent = self.subsequent_indent - else: - indent = self.initial_indent - - # Maximum width for this line. - width = self.width - len(indent) - - # First chunk on line is whitespace -- drop it, unless this - # is the very beginning of the text (ie. no lines started yet). - if string.strip(chunks[0]) == '' and lines: - del chunks[0] - - while chunks: - l = len(chunks[0]) - - # Can at least squeeze this chunk onto the current line. - if cur_len + l <= width: - cur_line.append(chunks.pop(0)) - cur_len = cur_len + l - - # Nope, this line is full. - else: - break - - # The current line is full, and the next chunk is too big to - # fit on *any* line (not just this one). - if chunks and len(chunks[0]) > width: - self._handle_long_word(chunks, cur_line, cur_len, width) - - # If the last chunk on this line is all whitespace, drop it. - if cur_line and string.strip(cur_line[-1]) == '': - del cur_line[-1] - - # Convert current line back to a string and store it in list - # of all lines (return value). - if cur_line: - lines.append(indent + string.join(cur_line, '')) - - return lines - - - # -- Public interface ---------------------------------------------- - - def wrap(self, text): - """wrap(text : string) -> [string] - - Reformat the single paragraph in 'text' so it fits in lines of - no more than 'self.width' columns, and return a list of wrapped - lines. Tabs in 'text' are expanded with string.expandtabs(), - and all other whitespace characters (including newline) are - converted to space. - """ - text = self._munge_whitespace(text) - indent = self.initial_indent - chunks = self._split(text) - if self.fix_sentence_endings: - self._fix_sentence_endings(chunks) - return self._wrap_chunks(chunks) - - def fill(self, text): - """fill(text : string) -> string - - Reformat the single paragraph in 'text' to fit in lines of no - more than 'self.width' columns, and return a new string - containing the entire wrapped paragraph. - """ - return string.join(self.wrap(text), "\n") - - -# -- Convenience interface --------------------------------------------- - -def wrap(text, width=70, **kwargs): - """Wrap a single paragraph of text, returning a list of wrapped lines. - - Reformat the single paragraph in 'text' so it fits in lines of no - more than 'width' columns, and return a list of wrapped lines. By - default, tabs in 'text' are expanded with string.expandtabs(), and - all other whitespace characters (including newline) are converted to - space. See TextWrapper class for available keyword args to customize - wrapping behaviour. - """ - kw = kwargs.copy() - kw['width'] = width - w = apply(TextWrapper, (), kw) - return w.wrap(text) - -def fill(text, width=70, **kwargs): - """Fill a single paragraph of text, returning a new string. - - Reformat the single paragraph in 'text' to fit in lines of no more - than 'width' columns, and return a new string containing the entire - wrapped paragraph. As with wrap(), tabs are expanded and other - whitespace characters converted to space. See TextWrapper class for - available keyword args to customize wrapping behaviour. - """ - kw = kwargs.copy() - kw['width'] = width - w = apply(TextWrapper, (), kw) - return w.fill(text) - - -# -- Loosely related functionality ------------------------------------- - -def dedent(text): - """dedent(text : string) -> string - - Remove any whitespace than can be uniformly removed from the left - of every line in `text`. - - This can be used e.g. to make triple-quoted strings line up with - the left edge of screen/whatever, while still presenting it in the - source code in indented form. - - For example: - - def test(): - # end first line with \ to avoid the empty line! - s = '''\ - hello - world - ''' - print repr(s) # prints ' hello\n world\n ' - print repr(dedent(s)) # prints 'hello\n world\n' - """ - lines = text.expandtabs().split('\n') - margin = None - for line in lines: - content = line.lstrip() - if not content: - continue - indent = len(line) - len(content) - if margin is None: - margin = indent - else: - margin = min(margin, indent) - - if margin is not None and margin > 0: - for i in range(len(lines)): - lines[i] = lines[i][margin:] - - return string.join(lines, '\n') diff --git a/tools/scons/scons-local-1.2.0/SCons/compat/builtins.py b/tools/scons/scons-local-1.2.0/SCons/compat/builtins.py deleted file mode 100644 index 8ae38b6ffa..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/compat/builtins.py +++ /dev/null @@ -1,181 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -# Portions of the following are derived from the compat.py file in -# Twisted, under the following copyright: -# -# Copyright (c) 2001-2004 Twisted Matrix Laboratories - -__doc__ = """ -Compatibility idioms for __builtin__ names - -This module adds names to the __builtin__ module for things that we want -to use in SCons but which don't show up until later Python versions than -the earliest ones we support. - -This module checks for the following __builtin__ names: - - all() - any() - bool() - dict() - True - False - zip() - -Implementations of functions are *NOT* guaranteed to be fully compliant -with these functions in later versions of Python. We are only concerned -with adding functionality that we actually use in SCons, so be wary -if you lift this code for other uses. (That said, making these more -nearly the same as later, official versions is still a desirable goal, -we just don't need to be obsessive about it.) - -If you're looking at this with pydoc and various names don't show up in -the FUNCTIONS or DATA output, that means those names are already built in -to this version of Python and we don't need to add them from this module. -""" - -__revision__ = "src/engine/SCons/compat/builtins.py 3842 2008/12/20 22:59:52 scons" - -import __builtin__ - -try: - all -except NameError: - # Pre-2.5 Python has no all() function. - def all(iterable): - """ - Returns True if all elements of the iterable are true. - """ - for element in iterable: - if not element: - return False - return True - __builtin__.all = all - all = all - -try: - any -except NameError: - # Pre-2.5 Python has no any() function. - def any(iterable): - """ - Returns True if any element of the iterable is true. - """ - for element in iterable: - if element: - return True - return False - __builtin__.any = any - any = any - -try: - bool -except NameError: - # Pre-2.2 Python has no bool() function. - def bool(value): - """Demote a value to 0 or 1, depending on its truth value. - - This is not to be confused with types.BooleanType, which is - way too hard to duplicate in early Python versions to be - worth the trouble. - """ - return not not value - __builtin__.bool = bool - bool = bool - -try: - dict -except NameError: - # Pre-2.2 Python has no dict() keyword. - def dict(seq=[], **kwargs): - """ - New dictionary initialization. - """ - d = {} - for k, v in seq: - d[k] = v - d.update(kwargs) - return d - __builtin__.dict = dict - -try: - False -except NameError: - # Pre-2.2 Python has no False keyword. - __builtin__.False = not 1 - # Assign to False in this module namespace so it shows up in pydoc output. - False = False - -try: - True -except NameError: - # Pre-2.2 Python has no True keyword. - __builtin__.True = not 0 - # Assign to True in this module namespace so it shows up in pydoc output. - True = True - -try: - file -except NameError: - # Pre-2.2 Python has no file() function. - __builtin__.file = open - -# -try: - zip -except NameError: - # Pre-2.2 Python has no zip() function. - def zip(*lists): - """ - Emulates the behavior we need from the built-in zip() function - added in Python 2.2. - - Returns a list of tuples, where each tuple contains the i-th - element rom each of the argument sequences. The returned - list is truncated in length to the length of the shortest - argument sequence. - """ - result = [] - for i in xrange(min(map(len, lists))): - result.append(tuple(map(lambda l, i=i: l[i], lists))) - return result - __builtin__.zip = zip - - - -#if sys.version_info[:3] in ((2, 2, 0), (2, 2, 1)): -# def lstrip(s, c=string.whitespace): -# while s and s[0] in c: -# s = s[1:] -# return s -# def rstrip(s, c=string.whitespace): -# while s and s[-1] in c: -# s = s[:-1] -# return s -# def strip(s, c=string.whitespace, l=lstrip, r=rstrip): -# return l(r(s, c), c) -# -# object.__setattr__(str, 'lstrip', lstrip) -# object.__setattr__(str, 'rstrip', rstrip) -# object.__setattr__(str, 'strip', strip) diff --git a/tools/scons/scons-local-1.2.0/SCons/cpp.py b/tools/scons/scons-local-1.2.0/SCons/cpp.py deleted file mode 100644 index 19809560a0..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/cpp.py +++ /dev/null @@ -1,592 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/cpp.py 3842 2008/12/20 22:59:52 scons" - -__doc__ = """ -SCons C Pre-Processor module -""" - -# TODO(1.5): remove this import -# This module doesn't use anything from SCons by name, but we import SCons -# here to pull in zip() from the SCons.compat layer for early Pythons. -import SCons - -import os -import re -import string - -# -# First "subsystem" of regular expressions that we set up: -# -# Stuff to turn the C preprocessor directives in a file's contents into -# a list of tuples that we can process easily. -# - -# A table of regular expressions that fetch the arguments from the rest of -# a C preprocessor line. Different directives have different arguments -# that we want to fetch, using the regular expressions to which the lists -# of preprocessor directives map. -cpp_lines_dict = { - # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, - # separated from the keyword by white space. - ('if', 'elif', 'ifdef', 'ifndef',) - : '\s+(.+)', - - # Fetch the rest of a #import/#include/#include_next line as one - # argument, with white space optional. - ('import', 'include', 'include_next',) - : '\s*(.+)', - - # We don't care what comes after a #else or #endif line. - ('else', 'endif',) : '', - - # Fetch three arguments from a #define line: - # 1) The #defined keyword. - # 2) The optional parentheses and arguments (if it's a function-like - # macro, '' if it's not). - # 3) The expansion value. - ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]+)(\([^)]*\))?\s*(.*)', - - # Fetch the #undefed keyword from a #undef line. - ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]+)', -} - -# Create a table that maps each individual C preprocessor directive to -# the corresponding compiled regular expression that fetches the arguments -# we care about. -Table = {} -for op_list, expr in cpp_lines_dict.items(): - e = re.compile(expr) - for op in op_list: - Table[op] = e -del e -del op -del op_list - -# Create a list of the expressions we'll use to match all of the -# preprocessor directives. These are the same as the directives -# themselves *except* that we must use a negative lookahead assertion -# when matching "if" so it doesn't match the "if" in "ifdef." -override = { - 'if' : 'if(?!def)', -} -l = map(lambda x, o=override: o.get(x, x), Table.keys()) - - -# Turn the list of expressions into one big honkin' regular expression -# that will match all the preprocessor lines at once. This will return -# a list of tuples, one for each preprocessor line. The preprocessor -# directive will be the first element in each tuple, and the rest of -# the line will be the second element. -e = '^\s*#\s*(' + string.join(l, '|') + ')(.*)$' - -# And last but not least, compile the expression. -CPP_Expression = re.compile(e, re.M) - - - - -# -# Second "subsystem" of regular expressions that we set up: -# -# Stuff to translate a C preprocessor expression (as found on a #if or -# #elif line) into an equivalent Python expression that we can eval(). -# - -# A dictionary that maps the C representation of Boolean operators -# to their Python equivalents. -CPP_to_Python_Ops_Dict = { - '!' : ' not ', - '!=' : ' != ', - '&&' : ' and ', - '||' : ' or ', - '?' : ' and ', - ':' : ' or ', - '\r' : '', -} - -CPP_to_Python_Ops_Sub = lambda m, d=CPP_to_Python_Ops_Dict: d[m.group(0)] - -# We have to sort the keys by length so that longer expressions -# come *before* shorter expressions--in particular, "!=" must -# come before "!" in the alternation. Without this, the Python -# re module, as late as version 2.2.2, empirically matches the -# "!" in "!=" first, instead of finding the longest match. -# What's up with that? -l = CPP_to_Python_Ops_Dict.keys() -l.sort(lambda a, b: cmp(len(b), len(a))) - -# Turn the list of keys into one regular expression that will allow us -# to substitute all of the operators at once. -expr = string.join(map(re.escape, l), '|') - -# ...and compile the expression. -CPP_to_Python_Ops_Expression = re.compile(expr) - -# A separate list of expressions to be evaluated and substituted -# sequentially, not all at once. -CPP_to_Python_Eval_List = [ - ['defined\s+(\w+)', '__dict__.has_key("\\1")'], - ['defined\s*\((\w+)\)', '__dict__.has_key("\\1")'], - ['/\*.*\*/', ''], - ['/\*.*', ''], - ['//.*', ''], - ['(0x[0-9A-Fa-f]*)[UL]+', '\\1L'], -] - -# Replace the string representations of the regular expressions in the -# list with compiled versions. -for l in CPP_to_Python_Eval_List: - l[0] = re.compile(l[0]) - -# Wrap up all of the above into a handy function. -def CPP_to_Python(s): - """ - Converts a C pre-processor expression into an equivalent - Python expression that can be evaluated. - """ - s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) - for expr, repl in CPP_to_Python_Eval_List: - s = expr.sub(repl, s) - return s - - - -del expr -del l -del override - - - -class FunctionEvaluator: - """ - Handles delayed evaluation of a #define function call. - """ - def __init__(self, name, args, expansion): - """ - Squirrels away the arguments and expansion value of a #define - macro function for later evaluation when we must actually expand - a value that uses it. - """ - self.name = name - self.args = function_arg_separator.split(args) - try: - expansion = string.split(expansion, '##') - except (AttributeError, TypeError): - # Python 1.5 throws TypeError if "expansion" isn't a string, - # later versions throw AttributeError. - pass - self.expansion = expansion - def __call__(self, *values): - """ - Evaluates the expansion of a #define macro function called - with the specified values. - """ - if len(self.args) != len(values): - raise ValueError, "Incorrect number of arguments to `%s'" % self.name - # Create a dictionary that maps the macro arguments to the - # corresponding values in this "call." We'll use this when we - # eval() the expansion so that arguments will get expanded to - # the right values. - locals = {} - for k, v in zip(self.args, values): - locals[k] = v - - parts = [] - for s in self.expansion: - if not s in self.args: - s = repr(s) - parts.append(s) - statement = string.join(parts, ' + ') - - return eval(statement, globals(), locals) - - - -# Find line continuations. -line_continuations = re.compile('\\\\\r?\n') - -# Search for a "function call" macro on an expansion. Returns the -# two-tuple of the "function" name itself, and a string containing the -# arguments within the call parentheses. -function_name = re.compile('(\S+)\(([^)]*)\)') - -# Split a string containing comma-separated function call arguments into -# the separate arguments. -function_arg_separator = re.compile(',\s*') - - - -class PreProcessor: - """ - The main workhorse class for handling C pre-processing. - """ - def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0): - global Table - - cpppath = tuple(cpppath) - - self.searchpath = { - '"' : (current,) + cpppath, - '<' : cpppath + (current,), - } - - # Initialize our C preprocessor namespace for tracking the - # values of #defined keywords. We use this namespace to look - # for keywords on #ifdef/#ifndef lines, and to eval() the - # expressions on #if/#elif lines (after massaging them from C to - # Python). - self.cpp_namespace = dict.copy() - self.cpp_namespace['__dict__'] = self.cpp_namespace - - if all: - self.do_include = self.all_include - - # For efficiency, a dispatch table maps each C preprocessor - # directive (#if, #define, etc.) to the method that should be - # called when we see it. We accomodate state changes (#if, - # #ifdef, #ifndef) by pushing the current dispatch table on a - # stack and changing what method gets called for each relevant - # directive we might see next at this level (#else, #elif). - # #endif will simply pop the stack. - d = { - 'scons_current_file' : self.scons_current_file - } - for op in Table.keys(): - d[op] = getattr(self, 'do_' + op) - self.default_table = d - - # Controlling methods. - - def tupleize(self, contents): - """ - Turns the contents of a file into a list of easily-processed - tuples describing the CPP lines in the file. - - The first element of each tuple is the line's preprocessor - directive (#if, #include, #define, etc., minus the initial '#'). - The remaining elements are specific to the type of directive, as - pulled apart by the regular expression. - """ - global CPP_Expression, Table - contents = line_continuations.sub('', contents) - cpp_tuples = CPP_Expression.findall(contents) - return map(lambda m, t=Table: - (m[0],) + t[m[0]].match(m[1]).groups(), - cpp_tuples) - - def __call__(self, file): - """ - Pre-processes a file. - - This is the main public entry point. - """ - self.current_file = file - return self.process_contents(self.read_file(file), file) - - def process_contents(self, contents, fname=None): - """ - Pre-processes a file contents. - - This is the main internal entry point. - """ - self.stack = [] - self.dispatch_table = self.default_table.copy() - self.current_file = fname - self.tuples = self.tupleize(contents) - - self.initialize_result(fname) - while self.tuples: - t = self.tuples.pop(0) - # Uncomment to see the list of tuples being processed (e.g., - # to validate the CPP lines are being translated correctly). - #print t - self.dispatch_table[t[0]](t) - return self.finalize_result(fname) - - # Dispatch table stack manipulation methods. - - def save(self): - """ - Pushes the current dispatch table on the stack and re-initializes - the current dispatch table to the default. - """ - self.stack.append(self.dispatch_table) - self.dispatch_table = self.default_table.copy() - - def restore(self): - """ - Pops the previous dispatch table off the stack and makes it the - current one. - """ - try: self.dispatch_table = self.stack.pop() - except IndexError: pass - - # Utility methods. - - def do_nothing(self, t): - """ - Null method for when we explicitly want the action for a - specific preprocessor directive to do nothing. - """ - pass - - def scons_current_file(self, t): - self.current_file = t[1] - - def eval_expression(self, t): - """ - Evaluates a C preprocessor expression. - - This is done by converting it to a Python equivalent and - eval()ing it in the C preprocessor namespace we use to - track #define values. - """ - t = CPP_to_Python(string.join(t[1:])) - try: return eval(t, self.cpp_namespace) - except (NameError, TypeError): return 0 - - def initialize_result(self, fname): - self.result = [fname] - - def finalize_result(self, fname): - return self.result[1:] - - def find_include_file(self, t): - """ - Finds the #include file for a given preprocessor tuple. - """ - fname = t[2] - for d in self.searchpath[t[1]]: - if d == os.curdir: - f = fname - else: - f = os.path.join(d, fname) - if os.path.isfile(f): - return f - return None - - def read_file(self, file): - return open(file).read() - - # Start and stop processing include lines. - - def start_handling_includes(self, t=None): - """ - Causes the PreProcessor object to start processing #import, - #include and #include_next lines. - - This method will be called when a #if, #ifdef, #ifndef or #elif - evaluates True, or when we reach the #else in a #if, #ifdef, - #ifndef or #elif block where a condition already evaluated - False. - - """ - d = self.dispatch_table - d['import'] = self.do_import - d['include'] = self.do_include - d['include_next'] = self.do_include - - def stop_handling_includes(self, t=None): - """ - Causes the PreProcessor object to stop processing #import, - #include and #include_next lines. - - This method will be called when a #if, #ifdef, #ifndef or #elif - evaluates False, or when we reach the #else in a #if, #ifdef, - #ifndef or #elif block where a condition already evaluated True. - """ - d = self.dispatch_table - d['import'] = self.do_nothing - d['include'] = self.do_nothing - d['include_next'] = self.do_nothing - - # Default methods for handling all of the preprocessor directives. - # (Note that what actually gets called for a given directive at any - # point in time is really controlled by the dispatch_table.) - - def _do_if_else_condition(self, condition): - """ - Common logic for evaluating the conditions on #if, #ifdef and - #ifndef lines. - """ - self.save() - d = self.dispatch_table - if condition: - self.start_handling_includes() - d['elif'] = self.stop_handling_includes - d['else'] = self.stop_handling_includes - else: - self.stop_handling_includes() - d['elif'] = self.do_elif - d['else'] = self.start_handling_includes - - def do_ifdef(self, t): - """ - Default handling of a #ifdef line. - """ - self._do_if_else_condition(self.cpp_namespace.has_key(t[1])) - - def do_ifndef(self, t): - """ - Default handling of a #ifndef line. - """ - self._do_if_else_condition(not self.cpp_namespace.has_key(t[1])) - - def do_if(self, t): - """ - Default handling of a #if line. - """ - self._do_if_else_condition(self.eval_expression(t)) - - def do_elif(self, t): - """ - Default handling of a #elif line. - """ - d = self.dispatch_table - if self.eval_expression(t): - self.start_handling_includes() - d['elif'] = self.stop_handling_includes - d['else'] = self.stop_handling_includes - - def do_else(self, t): - """ - Default handling of a #else line. - """ - pass - - def do_endif(self, t): - """ - Default handling of a #endif line. - """ - self.restore() - - def do_define(self, t): - """ - Default handling of a #define line. - """ - _, name, args, expansion = t - try: - expansion = int(expansion) - except (TypeError, ValueError): - pass - if args: - evaluator = FunctionEvaluator(name, args[1:-1], expansion) - self.cpp_namespace[name] = evaluator - else: - self.cpp_namespace[name] = expansion - - def do_undef(self, t): - """ - Default handling of a #undef line. - """ - try: del self.cpp_namespace[t[1]] - except KeyError: pass - - def do_import(self, t): - """ - Default handling of a #import line. - """ - # XXX finish this -- maybe borrow/share logic from do_include()...? - pass - - def do_include(self, t): - """ - Default handling of a #include line. - """ - t = self.resolve_include(t) - include_file = self.find_include_file(t) - if include_file: - #print "include_file =", include_file - self.result.append(include_file) - contents = self.read_file(include_file) - new_tuples = [('scons_current_file', include_file)] + \ - self.tupleize(contents) + \ - [('scons_current_file', self.current_file)] - self.tuples[:] = new_tuples + self.tuples - - # Date: Tue, 22 Nov 2005 20:26:09 -0500 - # From: Stefan Seefeld <seefeld@sympatico.ca> - # - # By the way, #include_next is not the same as #include. The difference - # being that #include_next starts its search in the path following the - # path that let to the including file. In other words, if your system - # include paths are ['/foo', '/bar'], and you are looking at a header - # '/foo/baz.h', it might issue an '#include_next <baz.h>' which would - # correctly resolve to '/bar/baz.h' (if that exists), but *not* see - # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html - # for more reasoning. - # - # I have no idea in what context 'import' might be used. - - # XXX is #include_next really the same as #include ? - do_include_next = do_include - - # Utility methods for handling resolution of include files. - - def resolve_include(self, t): - """Resolve a tuple-ized #include line. - - This handles recursive expansion of values without "" or <> - surrounding the name until an initial " or < is found, to handle - #include FILE - where FILE is a #define somewhere else. - """ - s = t[1] - while not s[0] in '<"': - #print "s =", s - try: - s = self.cpp_namespace[s] - except KeyError: - m = function_name.search(s) - s = self.cpp_namespace[m.group(1)] - if callable(s): - args = function_arg_separator.split(m.group(2)) - s = apply(s, args) - if not s: - return None - return (t[0], s[0], s[1:-1]) - - def all_include(self, t): - """ - """ - self.result.append(self.resolve_include(t)) - -class DumbPreProcessor(PreProcessor): - """A preprocessor that ignores all #if/#elif/#else/#endif directives - and just reports back *all* of the #include files (like the classic - SCons scanner did). - - This is functionally equivalent to using a regular expression to - find all of the #include lines, only slower. It exists mainly as - an example of how the main PreProcessor class can be sub-classed - to tailor its behavior. - """ - def __init__(self, *args, **kw): - apply(PreProcessor.__init__, (self,)+args, kw) - d = self.default_table - for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']: - d[func] = d[func] = self.do_nothing - -del __revision__ diff --git a/tools/scons/scons-local-1.2.0/SCons/dblite.py b/tools/scons/scons-local-1.2.0/SCons/dblite.py deleted file mode 100644 index 437f05a37d..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/dblite.py +++ /dev/null @@ -1,219 +0,0 @@ -# dblite.py module contributed by Ralf W. Grosse-Kunstleve. -# Extended for Unicode by Steven Knight. - -import cPickle -import time -import shutil -import os -import types -import __builtin__ - -keep_all_files = 00000 -ignore_corrupt_dbfiles = 0 - -def corruption_warning(filename): - print "Warning: Discarding corrupt database:", filename - -if hasattr(types, 'UnicodeType'): - def is_string(s): - t = type(s) - return t is types.StringType or t is types.UnicodeType -else: - def is_string(s): - return type(s) is types.StringType - -try: - unicode('a') -except NameError: - def unicode(s): return s - -dblite_suffix = '.dblite' -tmp_suffix = '.tmp' - -class dblite: - - # Squirrel away references to the functions in various modules - # that we'll use when our __del__() method calls our sync() method - # during shutdown. We might get destroyed when Python is in the midst - # of tearing down the different modules we import in an essentially - # arbitrary order, and some of the various modules's global attributes - # may already be wiped out from under us. - # - # See the discussion at: - # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html - - _open = __builtin__.open - _cPickle_dump = cPickle.dump - _os_chmod = os.chmod - _os_rename = os.rename - _os_unlink = os.unlink - _shutil_copyfile = shutil.copyfile - _time_time = time.time - - def __init__(self, file_base_name, flag, mode): - assert flag in (None, "r", "w", "c", "n") - if (flag is None): flag = "r" - base, ext = os.path.splitext(file_base_name) - if ext == dblite_suffix: - # There's already a suffix on the file name, don't add one. - self._file_name = file_base_name - self._tmp_name = base + tmp_suffix - else: - self._file_name = file_base_name + dblite_suffix - self._tmp_name = file_base_name + tmp_suffix - self._flag = flag - self._mode = mode - self._dict = {} - self._needs_sync = 00000 - if (self._flag == "n"): - self._open(self._file_name, "wb", self._mode) - else: - try: - f = self._open(self._file_name, "rb") - except IOError, e: - if (self._flag != "c"): - raise e - self._open(self._file_name, "wb", self._mode) - else: - p = f.read() - if (len(p) > 0): - try: - self._dict = cPickle.loads(p) - except (cPickle.UnpicklingError, EOFError): - if (ignore_corrupt_dbfiles == 0): raise - if (ignore_corrupt_dbfiles == 1): - corruption_warning(self._file_name) - - def __del__(self): - if (self._needs_sync): - self.sync() - - def sync(self): - self._check_writable() - f = self._open(self._tmp_name, "wb", self._mode) - self._cPickle_dump(self._dict, f, 1) - f.close() - # Windows doesn't allow renaming if the file exists, so unlink - # it first, chmod'ing it to make sure we can do so. On UNIX, we - # may not be able to chmod the file if it's owned by someone else - # (e.g. from a previous run as root). We should still be able to - # unlink() the file if the directory's writable, though, so ignore - # any OSError exception thrown by the chmod() call. - try: self._os_chmod(self._file_name, 0777) - except OSError: pass - self._os_unlink(self._file_name) - self._os_rename(self._tmp_name, self._file_name) - self._needs_sync = 00000 - if (keep_all_files): - self._shutil_copyfile( - self._file_name, - self._file_name + "_" + str(int(self._time_time()))) - - def _check_writable(self): - if (self._flag == "r"): - raise IOError("Read-only database: %s" % self._file_name) - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - self._check_writable() - if (not is_string(key)): - raise TypeError, "key `%s' must be a string but is %s" % (key, type(key)) - if (not is_string(value)): - raise TypeError, "value `%s' must be a string but is %s" % (value, type(value)) - self._dict[key] = value - self._needs_sync = 0001 - - def keys(self): - return self._dict.keys() - - def has_key(self, key): - return key in self._dict - - def __contains__(self, key): - return key in self._dict - - def iterkeys(self): - return self._dict.iterkeys() - - __iter__ = iterkeys - - def __len__(self): - return len(self._dict) - -def open(file, flag=None, mode=0666): - return dblite(file, flag, mode) - -def _exercise(): - db = open("tmp", "n") - assert len(db) == 0 - db["foo"] = "bar" - assert db["foo"] == "bar" - db[unicode("ufoo")] = unicode("ubar") - assert db[unicode("ufoo")] == unicode("ubar") - db.sync() - db = open("tmp", "c") - assert len(db) == 2, len(db) - assert db["foo"] == "bar" - db["bar"] = "foo" - assert db["bar"] == "foo" - db[unicode("ubar")] = unicode("ufoo") - assert db[unicode("ubar")] == unicode("ufoo") - db.sync() - db = open("tmp", "r") - assert len(db) == 4, len(db) - assert db["foo"] == "bar" - assert db["bar"] == "foo" - assert db[unicode("ufoo")] == unicode("ubar") - assert db[unicode("ubar")] == unicode("ufoo") - try: - db.sync() - except IOError, e: - assert str(e) == "Read-only database: tmp.dblite" - else: - raise RuntimeError, "IOError expected." - db = open("tmp", "w") - assert len(db) == 4 - db["ping"] = "pong" - db.sync() - try: - db[(1,2)] = "tuple" - except TypeError, e: - assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e) - else: - raise RuntimeError, "TypeError exception expected" - try: - db["list"] = [1,2] - except TypeError, e: - assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e) - else: - raise RuntimeError, "TypeError exception expected" - db = open("tmp", "r") - assert len(db) == 5 - db = open("tmp", "n") - assert len(db) == 0 - _open("tmp.dblite", "w") - db = open("tmp", "r") - _open("tmp.dblite", "w").write("x") - try: - db = open("tmp", "r") - except cPickle.UnpicklingError: - pass - else: - raise RuntimeError, "cPickle exception expected." - global ignore_corrupt_dbfiles - ignore_corrupt_dbfiles = 2 - db = open("tmp", "r") - assert len(db) == 0 - os.unlink("tmp.dblite") - try: - db = open("tmp", "w") - except IOError, e: - assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) - else: - raise RuntimeError, "IOError expected." - print "OK" - -if (__name__ == "__main__"): - _exercise() diff --git a/tools/scons/scons-local-1.2.0/SCons/exitfuncs.py b/tools/scons/scons-local-1.2.0/SCons/exitfuncs.py deleted file mode 100644 index 2feb86c941..0000000000 --- a/tools/scons/scons-local-1.2.0/SCons/exitfuncs.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.exitfuncs - -Register functions which are executed when SCons exits for any reason. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/engine/SCons/exitfuncs.py 3842 2008/12/20 22:59:52 scons" - - - -_exithandlers = [] -def _run_exitfuncs(): - """run any registered exit functions - - _exithandlers is traversed in reverse order so functions are executed - last in, first out. - """ - - while _exithandlers: - func, targs, kargs = _exithandlers.pop() - apply(func, targs, kargs) - -def register(func, *targs, **kargs): - """register a function to be executed upon normal program termination - - func - function to be called at exit - targs - optional arguments to pass to func - kargs - optional keyword arguments to pass to func - """ - _exithandlers.append((func, targs, kargs)) - -import sys - -try: - x = sys.exitfunc - - # if x isn't our own exit func executive, assume it's another - # registered exit function - append it to our list... - if x != _run_exitfuncs: - register(x) - -except AttributeError: - pass - -# make our exit function get run by python when it exits: -sys.exitfunc = _run_exitfuncs - -del sys diff --git a/tools/scons/scons-time.py b/tools/scons/scons-time.py deleted file mode 100755 index 67b6643bf7..0000000000 --- a/tools/scons/scons-time.py +++ /dev/null @@ -1,1513 +0,0 @@ -#!/usr/bin/env python -# -# scons-time - run SCons timings and collect statistics -# -# A script for running a configuration through SCons with a standard -# set of invocations to collect timing and memory statistics and to -# capture the results in a consistent set of output files for display -# and analysis. -# - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -from __future__ import nested_scopes - -__revision__ = "src/script/scons-time.py 3842 2008/12/20 22:59:52 scons" - -import getopt -import glob -import os -import os.path -import re -import shutil -import string -import sys -import tempfile -import time - -try: - False -except NameError: - # Pre-2.2 Python has no False keyword. - import __builtin__ - __builtin__.False = not 1 - -try: - True -except NameError: - # Pre-2.2 Python has no True keyword. - import __builtin__ - __builtin__.True = not 0 - -def make_temp_file(**kw): - try: - result = tempfile.mktemp(**kw) - try: - result = os.path.realpath(result) - except AttributeError: - # Python 2.1 has no os.path.realpath() method. - pass - except TypeError: - try: - save_template = tempfile.template - prefix = kw['prefix'] - del kw['prefix'] - tempfile.template = prefix - result = tempfile.mktemp(**kw) - finally: - tempfile.template = save_template - return result - -class Plotter: - def increment_size(self, largest): - """ - Return the size of each horizontal increment line for a specified - maximum value. This returns a value that will provide somewhere - between 5 and 9 horizontal lines on the graph, on some set of - boundaries that are multiples of 10/100/1000/etc. - """ - i = largest / 5 - if not i: - return largest - multiplier = 1 - while i >= 10: - i = i / 10 - multiplier = multiplier * 10 - return i * multiplier - - def max_graph_value(self, largest): - # Round up to next integer. - largest = int(largest) + 1 - increment = self.increment_size(largest) - return ((largest + increment - 1) / increment) * increment - -class Line: - def __init__(self, points, type, title, label, comment, fmt="%s %s"): - self.points = points - self.type = type - self.title = title - self.label = label - self.comment = comment - self.fmt = fmt - - def print_label(self, inx, x, y): - if self.label: - print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) - - def plot_string(self): - if self.title: - title_string = 'title "%s"' % self.title - else: - title_string = 'notitle' - return "'-' %s with lines lt %s" % (title_string, self.type) - - def print_points(self, fmt=None): - if fmt is None: - fmt = self.fmt - if self.comment: - print '# %s' % self.comment - for x, y in self.points: - # If y is None, it usually represents some kind of break - # in the line's index number. We might want to represent - # this some way rather than just drawing the line straight - # between the two points on either side. - if not y is None: - print fmt % (x, y) - print 'e' - - def get_x_values(self): - return [ p[0] for p in self.points ] - - def get_y_values(self): - return [ p[1] for p in self.points ] - -class Gnuplotter(Plotter): - - def __init__(self, title, key_location): - self.lines = [] - self.title = title - self.key_location = key_location - - def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'): - if points: - line = Line(points, type, title, label, comment, fmt) - self.lines.append(line) - - def plot_string(self, line): - return line.plot_string() - - def vertical_bar(self, x, type, label, comment): - if self.get_min_x() <= x and x <= self.get_max_x(): - points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))] - self.line(points, type, label, comment) - - def get_all_x_values(self): - result = [] - for line in self.lines: - result.extend(line.get_x_values()) - return filter(lambda r: not r is None, result) - - def get_all_y_values(self): - result = [] - for line in self.lines: - result.extend(line.get_y_values()) - return filter(lambda r: not r is None, result) - - def get_min_x(self): - try: - return self.min_x - except AttributeError: - try: - self.min_x = min(self.get_all_x_values()) - except ValueError: - self.min_x = 0 - return self.min_x - - def get_max_x(self): - try: - return self.max_x - except AttributeError: - try: - self.max_x = max(self.get_all_x_values()) - except ValueError: - self.max_x = 0 - return self.max_x - - def get_min_y(self): - try: - return self.min_y - except AttributeError: - try: - self.min_y = min(self.get_all_y_values()) - except ValueError: - self.min_y = 0 - return self.min_y - - def get_max_y(self): - try: - return self.max_y - except AttributeError: - try: - self.max_y = max(self.get_all_y_values()) - except ValueError: - self.max_y = 0 - return self.max_y - - def draw(self): - - if not self.lines: - return - - if self.title: - print 'set title "%s"' % self.title - print 'set key %s' % self.key_location - - min_y = self.get_min_y() - max_y = self.max_graph_value(self.get_max_y()) - range = max_y - min_y - incr = range / 10.0 - start = min_y + (max_y / 2.0) + (2.0 * incr) - position = [ start - (i * incr) for i in xrange(5) ] - - inx = 1 - for line in self.lines: - line.print_label(inx, line.points[0][0]-1, - position[(inx-1) % len(position)]) - inx += 1 - - plot_strings = [ self.plot_string(l) for l in self.lines ] - print 'plot ' + ', \\\n '.join(plot_strings) - - for line in self.lines: - line.print_points() - - - -def untar(fname): - import tarfile - tar = tarfile.open(name=fname, mode='r') - for tarinfo in tar: - tar.extract(tarinfo) - tar.close() - -def unzip(fname): - import zipfile - zf = zipfile.ZipFile(fname, 'r') - for name in zf.namelist(): - dir = os.path.dirname(name) - try: - os.makedirs(dir) - except: - pass - open(name, 'w').write(zf.read(name)) - -def read_tree(dir): - def read_files(arg, dirname, fnames): - for fn in fnames: - fn = os.path.join(dirname, fn) - if os.path.isfile(fn): - open(fn, 'rb').read() - os.path.walk('.', read_files, None) - -def redirect_to_file(command, log): - return '%s > %s 2>&1' % (command, log) - -def tee_to_file(command, log): - return '%s 2>&1 | tee %s' % (command, log) - - - -class SConsTimer: - """ - Usage: scons-time SUBCOMMAND [ARGUMENTS] - Type "scons-time help SUBCOMMAND" for help on a specific subcommand. - - Available subcommands: - func Extract test-run data for a function - help Provides help - mem Extract --debug=memory data from test runs - obj Extract --debug=count data from test runs - time Extract --debug=time data from test runs - run Runs a test configuration - """ - - name = 'scons-time' - name_spaces = ' '*len(name) - - def makedict(**kw): - return kw - - default_settings = makedict( - aegis = 'aegis', - aegis_project = None, - chdir = None, - config_file = None, - initial_commands = [], - key_location = 'bottom left', - orig_cwd = os.getcwd(), - outdir = None, - prefix = '', - python = '"%s"' % sys.executable, - redirect = redirect_to_file, - scons = None, - scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer', - scons_lib_dir = None, - scons_wrapper = None, - startup_targets = '--help', - subdir = None, - subversion_url = None, - svn = 'svn', - svn_co_flag = '-q', - tar = 'tar', - targets = '', - targets0 = None, - targets1 = None, - targets2 = None, - title = None, - unzip = 'unzip', - verbose = False, - vertical_bars = [], - - unpack_map = { - '.tar.gz' : (untar, '%(tar)s xzf %%s'), - '.tgz' : (untar, '%(tar)s xzf %%s'), - '.tar' : (untar, '%(tar)s xf %%s'), - '.zip' : (unzip, '%(unzip)s %%s'), - }, - ) - - run_titles = [ - 'Startup', - 'Full build', - 'Up-to-date build', - ] - - run_commands = [ - '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s', - '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s', - '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s', - ] - - stages = [ - 'pre-read', - 'post-read', - 'pre-build', - 'post-build', - ] - - stage_strings = { - 'pre-read' : 'Memory before reading SConscript files:', - 'post-read' : 'Memory after reading SConscript files:', - 'pre-build' : 'Memory before building targets:', - 'post-build' : 'Memory after building targets:', - } - - memory_string_all = 'Memory ' - - default_stage = stages[-1] - - time_strings = { - 'total' : 'Total build time', - 'SConscripts' : 'Total SConscript file execution time', - 'SCons' : 'Total SCons execution time', - 'commands' : 'Total command execution time', - } - - time_string_all = 'Total .* time' - - # - - def __init__(self): - self.__dict__.update(self.default_settings) - - # Functions for displaying and executing commands. - - def subst(self, x, dictionary): - try: - return x % dictionary - except TypeError: - # x isn't a string (it's probably a Python function), - # so just return it. - return x - - def subst_variables(self, command, dictionary): - """ - Substitutes (via the format operator) the values in the specified - dictionary into the specified command. - - The command can be an (action, string) tuple. In all cases, we - perform substitution on strings and don't worry if something isn't - a string. (It's probably a Python function to be executed.) - """ - try: - command + '' - except TypeError: - action = command[0] - string = command[1] - args = command[2:] - else: - action = command - string = action - args = (()) - action = self.subst(action, dictionary) - string = self.subst(string, dictionary) - return (action, string, args) - - def _do_not_display(self, msg, *args): - pass - - def display(self, msg, *args): - """ - Displays the specified message. - - Each message is prepended with a standard prefix of our name - plus the time. - """ - if callable(msg): - msg = msg(*args) - else: - msg = msg % args - if msg is None: - return - fmt = '%s[%s]: %s\n' - sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) - - def _do_not_execute(self, action, *args): - pass - - def execute(self, action, *args): - """ - Executes the specified action. - - The action is called if it's a callable Python function, and - otherwise passed to os.system(). - """ - if callable(action): - action(*args) - else: - os.system(action % args) - - def run_command_list(self, commands, dict): - """ - Executes a list of commands, substituting values from the - specified dictionary. - """ - commands = [ self.subst_variables(c, dict) for c in commands ] - for action, string, args in commands: - self.display(string, *args) - sys.stdout.flush() - status = self.execute(action, *args) - if status: - sys.exit(status) - - def log_display(self, command, log): - command = self.subst(command, self.__dict__) - if log: - command = self.redirect(command, log) - return command - - def log_execute(self, command, log): - command = self.subst(command, self.__dict__) - output = os.popen(command).read() - if self.verbose: - sys.stdout.write(output) - open(log, 'wb').write(output) - - # - - def archive_splitext(self, path): - """ - Splits an archive name into a filename base and extension. - - This is like os.path.splitext() (which it calls) except that it - also looks for '.tar.gz' and treats it as an atomic extensions. - """ - if path.endswith('.tar.gz'): - return path[:-7], path[-7:] - else: - return os.path.splitext(path) - - def args_to_files(self, args, tail=None): - """ - Takes a list of arguments, expands any glob patterns, and - returns the last "tail" files from the list. - """ - files = [] - for a in args: - g = glob.glob(a) - g.sort() - files.extend(g) - - if tail: - files = files[-tail:] - - return files - - def ascii_table(self, files, columns, - line_function, file_function=lambda x: x, - *args, **kw): - - header_fmt = ' '.join(['%12s'] * len(columns)) - line_fmt = header_fmt + ' %s' - - print header_fmt % columns - - for file in files: - t = line_function(file, *args, **kw) - if t is None: - t = [] - diff = len(columns) - len(t) - if diff > 0: - t += [''] * diff - t.append(file_function(file)) - print line_fmt % tuple(t) - - def collect_results(self, files, function, *args, **kw): - results = {} - - for file in files: - base = os.path.splitext(file)[0] - run, index = string.split(base, '-')[-2:] - - run = int(run) - index = int(index) - - value = function(file, *args, **kw) - - try: - r = results[index] - except KeyError: - r = [] - results[index] = r - r.append((run, value)) - - return results - - def doc_to_help(self, obj): - """ - Translates an object's __doc__ string into help text. - - This strips a consistent number of spaces from each line in the - help text, essentially "outdenting" the text to the left-most - column. - """ - doc = obj.__doc__ - if doc is None: - return '' - return self.outdent(doc) - - def find_next_run_number(self, dir, prefix): - """ - Returns the next run number in a directory for the specified prefix. - - Examines the contents the specified directory for files with the - specified prefix, extracts the run numbers from each file name, - and returns the next run number after the largest it finds. - """ - x = re.compile(re.escape(prefix) + '-([0-9]+).*') - matches = map(lambda e, x=x: x.match(e), os.listdir(dir)) - matches = filter(None, matches) - if not matches: - return 0 - run_numbers = map(lambda m: int(m.group(1)), matches) - return int(max(run_numbers)) + 1 - - def gnuplot_results(self, results, fmt='%s %.3f'): - """ - Prints out a set of results in Gnuplot format. - """ - gp = Gnuplotter(self.title, self.key_location) - - indices = results.keys() - indices.sort() - - for i in indices: - try: - t = self.run_titles[i] - except IndexError: - t = '??? %s ???' % i - results[i].sort() - gp.line(results[i], i+1, t, None, t, fmt=fmt) - - for bar_tuple in self.vertical_bars: - try: - x, type, label, comment = bar_tuple - except ValueError: - x, type, label = bar_tuple - comment = label - gp.vertical_bar(x, type, label, comment) - - gp.draw() - - def logfile_name(self, invocation): - """ - Returns the absolute path of a log file for the specificed - invocation number. - """ - name = self.prefix_run + '-%d.log' % invocation - return os.path.join(self.outdir, name) - - def outdent(self, s): - """ - Strip as many spaces from each line as are found at the beginning - of the first line in the list. - """ - lines = s.split('\n') - if lines[0] == '': - lines = lines[1:] - spaces = re.match(' *', lines[0]).group(0) - def strip_initial_spaces(l, s=spaces): - if l.startswith(spaces): - l = l[len(spaces):] - return l - return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' - - def profile_name(self, invocation): - """ - Returns the absolute path of a profile file for the specified - invocation number. - """ - name = self.prefix_run + '-%d.prof' % invocation - return os.path.join(self.outdir, name) - - def set_env(self, key, value): - os.environ[key] = value - - # - - def get_debug_times(self, file, time_string=None): - """ - Fetch times from the --debug=time strings in the specified file. - """ - if time_string is None: - search_string = self.time_string_all - else: - search_string = time_string - contents = open(file).read() - if not contents: - sys.stderr.write('file %s has no contents!\n' % repr(file)) - return None - result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] - result = [ float(r) for r in result ] - if not time_string is None: - try: - result = result[0] - except IndexError: - sys.stderr.write('file %s has no results!\n' % repr(file)) - return None - return result - - def get_function_profile(self, file, function): - """ - Returns the file, line number, function name, and cumulative time. - """ - try: - import pstats - except ImportError, e: - sys.stderr.write('%s: func: %s\n' % (self.name, e)) - sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces) - sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces) - sys.exit(1) - statistics = pstats.Stats(file).stats - matches = [ e for e in statistics.items() if e[0][2] == function ] - r = matches[0] - return r[0][0], r[0][1], r[0][2], r[1][3] - - def get_function_time(self, file, function): - """ - Returns just the cumulative time for the specified function. - """ - return self.get_function_profile(file, function)[3] - - def get_memory(self, file, memory_string=None): - """ - Returns a list of integers of the amount of memory used. The - default behavior is to return all the stages. - """ - if memory_string is None: - search_string = self.memory_string_all - else: - search_string = memory_string - lines = open(file).readlines() - lines = [ l for l in lines if l.startswith(search_string) ][-4:] - result = [ int(l.split()[-1]) for l in lines[-4:] ] - if len(result) == 1: - result = result[0] - return result - - def get_object_counts(self, file, object_name, index=None): - """ - Returns the counts of the specified object_name. - """ - object_string = ' ' + object_name + '\n' - lines = open(file).readlines() - line = [ l for l in lines if l.endswith(object_string) ][0] - result = [ int(field) for field in line.split()[:4] ] - if not index is None: - result = result[index] - return result - - # - - command_alias = {} - - def execute_subcommand(self, argv): - """ - Executes the do_*() function for the specified subcommand (argv[0]). - """ - if not argv: - return - cmdName = self.command_alias.get(argv[0], argv[0]) - try: - func = getattr(self, 'do_' + cmdName) - except AttributeError: - return self.default(argv) - try: - return func(argv) - except TypeError, e: - sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) - import traceback - traceback.print_exc(file=sys.stderr) - sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) - - def default(self, argv): - """ - The default behavior for an unknown subcommand. Prints an - error message and exits. - """ - sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])) - sys.stderr.write('Type "%s help" for usage.\n' % self.name) - sys.exit(1) - - # - - def do_help(self, argv): - """ - """ - if argv[1:]: - for arg in argv[1:]: - try: - func = getattr(self, 'do_' + arg) - except AttributeError: - sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)) - else: - try: - help = getattr(self, 'help_' + arg) - except AttributeError: - sys.stdout.write(self.doc_to_help(func)) - sys.stdout.flush() - else: - help() - else: - doc = self.doc_to_help(self.__class__) - if doc: - sys.stdout.write(doc) - sys.stdout.flush() - return None - - # - - def help_func(self): - help = """\ - Usage: scons-time func [OPTIONS] FILE [...] - - -C DIR, --chdir=DIR Change to DIR before looking for files - -f FILE, --file=FILE Read configuration from specified FILE - --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT - --func=NAME, --function=NAME Report time for function NAME - -h, --help Print this help and exit - -p STRING, --prefix=STRING Use STRING as log file/profile prefix - -t NUMBER, --tail=NUMBER Only report the last NUMBER files - --title=TITLE Specify the output plot TITLE - """ - sys.stdout.write(self.outdent(help)) - sys.stdout.flush() - - def do_func(self, argv): - """ - """ - format = 'ascii' - function_name = '_main' - tail = None - - short_opts = '?C:f:hp:t:' - - long_opts = [ - 'chdir=', - 'file=', - 'fmt=', - 'format=', - 'func=', - 'function=', - 'help', - 'prefix=', - 'tail=', - 'title=', - ] - - opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - - for o, a in opts: - if o in ('-C', '--chdir'): - self.chdir = a - elif o in ('-f', '--file'): - self.config_file = a - elif o in ('--fmt', '--format'): - format = a - elif o in ('--func', '--function'): - function_name = a - elif o in ('-?', '-h', '--help'): - self.do_help(['help', 'func']) - sys.exit(0) - elif o in ('--max',): - max_time = int(a) - elif o in ('-p', '--prefix'): - self.prefix = a - elif o in ('-t', '--tail'): - tail = int(a) - elif o in ('--title',): - self.title = a - - if self.config_file: - execfile(self.config_file, self.__dict__) - - if self.chdir: - os.chdir(self.chdir) - - if not args: - - pattern = '%s*.prof' % self.prefix - args = self.args_to_files([pattern], tail) - - if not args: - if self.chdir: - directory = self.chdir - else: - directory = os.getcwd() - - sys.stderr.write('%s: func: No arguments specified.\n' % self.name) - sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) - sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - else: - - args = self.args_to_files(args, tail) - - cwd_ = os.getcwd() + os.sep - - if format == 'ascii': - - def print_function_timing(file, func): - try: - f, line, func, time = self.get_function_profile(file, func) - except ValueError, e: - sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e)) - else: - if f.startswith(cwd_): - f = f[len(cwd_):] - print "%.3f %s:%d(%s)" % (time, f, line, func) - - for file in args: - print_function_timing(file, function_name) - - elif format == 'gnuplot': - - results = self.collect_results(args, self.get_function_time, - function_name) - - self.gnuplot_results(results) - - else: - - sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format)) - sys.exit(1) - - # - - def help_mem(self): - help = """\ - Usage: scons-time mem [OPTIONS] FILE [...] - - -C DIR, --chdir=DIR Change to DIR before looking for files - -f FILE, --file=FILE Read configuration from specified FILE - --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT - -h, --help Print this help and exit - -p STRING, --prefix=STRING Use STRING as log file/profile prefix - --stage=STAGE Plot memory at the specified stage: - pre-read, post-read, pre-build, - post-build (default: post-build) - -t NUMBER, --tail=NUMBER Only report the last NUMBER files - --title=TITLE Specify the output plot TITLE - """ - sys.stdout.write(self.outdent(help)) - sys.stdout.flush() - - def do_mem(self, argv): - - format = 'ascii' - logfile_path = lambda x: x - stage = self.default_stage - tail = None - - short_opts = '?C:f:hp:t:' - - long_opts = [ - 'chdir=', - 'file=', - 'fmt=', - 'format=', - 'help', - 'prefix=', - 'stage=', - 'tail=', - 'title=', - ] - - opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - - for o, a in opts: - if o in ('-C', '--chdir'): - self.chdir = a - elif o in ('-f', '--file'): - self.config_file = a - elif o in ('--fmt', '--format'): - format = a - elif o in ('-?', '-h', '--help'): - self.do_help(['help', 'mem']) - sys.exit(0) - elif o in ('-p', '--prefix'): - self.prefix = a - elif o in ('--stage',): - if not a in self.stages: - sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a)) - sys.exit(1) - stage = a - elif o in ('-t', '--tail'): - tail = int(a) - elif o in ('--title',): - self.title = a - - if self.config_file: - execfile(self.config_file, self.__dict__) - - if self.chdir: - os.chdir(self.chdir) - logfile_path = lambda x, c=self.chdir: os.path.join(c, x) - - if not args: - - pattern = '%s*.log' % self.prefix - args = self.args_to_files([pattern], tail) - - if not args: - if self.chdir: - directory = self.chdir - else: - directory = os.getcwd() - - sys.stderr.write('%s: mem: No arguments specified.\n' % self.name) - sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) - sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - else: - - args = self.args_to_files(args, tail) - - cwd_ = os.getcwd() + os.sep - - if format == 'ascii': - - self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path) - - elif format == 'gnuplot': - - results = self.collect_results(args, self.get_memory, - self.stage_strings[stage]) - - self.gnuplot_results(results) - - else: - - sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format)) - sys.exit(1) - - return 0 - - # - - def help_obj(self): - help = """\ - Usage: scons-time obj [OPTIONS] OBJECT FILE [...] - - -C DIR, --chdir=DIR Change to DIR before looking for files - -f FILE, --file=FILE Read configuration from specified FILE - --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT - -h, --help Print this help and exit - -p STRING, --prefix=STRING Use STRING as log file/profile prefix - --stage=STAGE Plot memory at the specified stage: - pre-read, post-read, pre-build, - post-build (default: post-build) - -t NUMBER, --tail=NUMBER Only report the last NUMBER files - --title=TITLE Specify the output plot TITLE - """ - sys.stdout.write(self.outdent(help)) - sys.stdout.flush() - - def do_obj(self, argv): - - format = 'ascii' - logfile_path = lambda x: x - stage = self.default_stage - tail = None - - short_opts = '?C:f:hp:t:' - - long_opts = [ - 'chdir=', - 'file=', - 'fmt=', - 'format=', - 'help', - 'prefix=', - 'stage=', - 'tail=', - 'title=', - ] - - opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - - for o, a in opts: - if o in ('-C', '--chdir'): - self.chdir = a - elif o in ('-f', '--file'): - self.config_file = a - elif o in ('--fmt', '--format'): - format = a - elif o in ('-?', '-h', '--help'): - self.do_help(['help', 'obj']) - sys.exit(0) - elif o in ('-p', '--prefix'): - self.prefix = a - elif o in ('--stage',): - if not a in self.stages: - sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a)) - sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - stage = a - elif o in ('-t', '--tail'): - tail = int(a) - elif o in ('--title',): - self.title = a - - if not args: - sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name) - sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - object_name = args.pop(0) - - if self.config_file: - execfile(self.config_file, self.__dict__) - - if self.chdir: - os.chdir(self.chdir) - logfile_path = lambda x, c=self.chdir: os.path.join(c, x) - - if not args: - - pattern = '%s*.log' % self.prefix - args = self.args_to_files([pattern], tail) - - if not args: - if self.chdir: - directory = self.chdir - else: - directory = os.getcwd() - - sys.stderr.write('%s: obj: No arguments specified.\n' % self.name) - sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) - sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - else: - - args = self.args_to_files(args, tail) - - cwd_ = os.getcwd() + os.sep - - if format == 'ascii': - - self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name) - - elif format == 'gnuplot': - - stage_index = 0 - for s in self.stages: - if stage == s: - break - stage_index = stage_index + 1 - - results = self.collect_results(args, self.get_object_counts, - object_name, stage_index) - - self.gnuplot_results(results) - - else: - - sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format)) - sys.exit(1) - - return 0 - - # - - def help_run(self): - help = """\ - Usage: scons-time run [OPTIONS] [FILE ...] - - --aegis=PROJECT Use SCons from the Aegis PROJECT - --chdir=DIR Name of unpacked directory for chdir - -f FILE, --file=FILE Read configuration from specified FILE - -h, --help Print this help and exit - -n, --no-exec No execute, just print command lines - --number=NUMBER Put output in files for run NUMBER - --outdir=OUTDIR Put output files in OUTDIR - -p STRING, --prefix=STRING Use STRING as log file/profile prefix - --python=PYTHON Time using the specified PYTHON - -q, --quiet Don't print command lines - --scons=SCONS Time using the specified SCONS - --svn=URL, --subversion=URL Use SCons from Subversion URL - -v, --verbose Display output of commands - """ - sys.stdout.write(self.outdent(help)) - sys.stdout.flush() - - def do_run(self, argv): - """ - """ - run_number_list = [None] - - short_opts = '?f:hnp:qs:v' - - long_opts = [ - 'aegis=', - 'file=', - 'help', - 'no-exec', - 'number=', - 'outdir=', - 'prefix=', - 'python=', - 'quiet', - 'scons=', - 'svn=', - 'subdir=', - 'subversion=', - 'verbose', - ] - - opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - - for o, a in opts: - if o in ('--aegis',): - self.aegis_project = a - elif o in ('-f', '--file'): - self.config_file = a - elif o in ('-?', '-h', '--help'): - self.do_help(['help', 'run']) - sys.exit(0) - elif o in ('-n', '--no-exec'): - self.execute = self._do_not_execute - elif o in ('--number',): - run_number_list = self.split_run_numbers(a) - elif o in ('--outdir',): - self.outdir = a - elif o in ('-p', '--prefix'): - self.prefix = a - elif o in ('--python',): - self.python = a - elif o in ('-q', '--quiet'): - self.display = self._do_not_display - elif o in ('-s', '--subdir'): - self.subdir = a - elif o in ('--scons',): - self.scons = a - elif o in ('--svn', '--subversion'): - self.subversion_url = a - elif o in ('-v', '--verbose'): - self.redirect = tee_to_file - self.verbose = True - self.svn_co_flag = '' - - if not args and not self.config_file: - sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name) - sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - if self.config_file: - execfile(self.config_file, self.__dict__) - - if args: - self.archive_list = args - - archive_file_name = os.path.split(self.archive_list[0])[1] - - if not self.subdir: - self.subdir = self.archive_splitext(archive_file_name)[0] - - if not self.prefix: - self.prefix = self.archive_splitext(archive_file_name)[0] - - prepare = None - if self.subversion_url: - prepare = self.prep_subversion_run - elif self.aegis_project: - prepare = self.prep_aegis_run - - for run_number in run_number_list: - self.individual_run(run_number, self.archive_list, prepare) - - def split_run_numbers(self, s): - result = [] - for n in s.split(','): - try: - x, y = n.split('-') - except ValueError: - result.append(int(n)) - else: - result.extend(range(int(x), int(y)+1)) - return result - - def scons_path(self, dir): - return os.path.join(dir, 'src', 'script', 'scons.py') - - def scons_lib_dir_path(self, dir): - return os.path.join(dir, 'src', 'engine') - - def prep_aegis_run(self, commands, removals): - self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') - removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) - - self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] - self.scons = self.scons_path(self.aegis_tmpdir) - self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) - - commands.extend([ - 'mkdir %(aegis_tmpdir)s', - (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), - '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', - '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', - ]) - - def prep_subversion_run(self, commands, removals): - self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') - removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) - - self.scons = self.scons_path(self.svn_tmpdir) - self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) - - commands.extend([ - 'mkdir %(svn_tmpdir)s', - '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s', - ]) - - def individual_run(self, run_number, archive_list, prepare=None): - """ - Performs an individual run of the default SCons invocations. - """ - - commands = [] - removals = [] - - if prepare: - prepare(commands, removals) - - save_scons = self.scons - save_scons_wrapper = self.scons_wrapper - save_scons_lib_dir = self.scons_lib_dir - - if self.outdir is None: - self.outdir = self.orig_cwd - elif not os.path.isabs(self.outdir): - self.outdir = os.path.join(self.orig_cwd, self.outdir) - - if self.scons is None: - self.scons = self.scons_path(self.orig_cwd) - - if self.scons_lib_dir is None: - self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd) - - if self.scons_wrapper is None: - self.scons_wrapper = self.scons - - if not run_number: - run_number = self.find_next_run_number(self.outdir, self.prefix) - - self.run_number = str(run_number) - - self.prefix_run = self.prefix + '-%03d' % run_number - - if self.targets0 is None: - self.targets0 = self.startup_targets - if self.targets1 is None: - self.targets1 = self.targets - if self.targets2 is None: - self.targets2 = self.targets - - self.tmpdir = make_temp_file(prefix = self.name + '-') - - commands.extend([ - 'mkdir %(tmpdir)s', - - (os.chdir, 'cd %%s', self.tmpdir), - ]) - - for archive in archive_list: - if not os.path.isabs(archive): - archive = os.path.join(self.orig_cwd, archive) - if os.path.isdir(archive): - dest = os.path.split(archive)[1] - commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest)) - else: - suffix = self.archive_splitext(archive)[1] - unpack_command = self.unpack_map.get(suffix) - if not unpack_command: - dest = os.path.split(archive)[1] - commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest)) - else: - commands.append(unpack_command + (archive,)) - - commands.extend([ - (os.chdir, 'cd %%s', self.subdir), - ]) - - commands.extend(self.initial_commands) - - commands.extend([ - (lambda: read_tree('.'), - 'find * -type f | xargs cat > /dev/null'), - - (self.set_env, 'export %%s=%%s', - 'SCONS_LIB_DIR', self.scons_lib_dir), - - '%(python)s %(scons_wrapper)s --version', - ]) - - index = 0 - for run_command in self.run_commands: - setattr(self, 'prof%d' % index, self.profile_name(index)) - c = ( - self.log_execute, - self.log_display, - run_command, - self.logfile_name(index), - ) - commands.append(c) - index = index + 1 - - commands.extend([ - (os.chdir, 'cd %%s', self.orig_cwd), - ]) - - if not os.environ.get('PRESERVE'): - commands.extend(removals) - - commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) - - self.run_command_list(commands, self.__dict__) - - self.scons = save_scons - self.scons_lib_dir = save_scons_lib_dir - self.scons_wrapper = save_scons_wrapper - - # - - def help_time(self): - help = """\ - Usage: scons-time time [OPTIONS] FILE [...] - - -C DIR, --chdir=DIR Change to DIR before looking for files - -f FILE, --file=FILE Read configuration from specified FILE - --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT - -h, --help Print this help and exit - -p STRING, --prefix=STRING Use STRING as log file/profile prefix - -t NUMBER, --tail=NUMBER Only report the last NUMBER files - --which=TIMER Plot timings for TIMER: total, - SConscripts, SCons, commands. - """ - sys.stdout.write(self.outdent(help)) - sys.stdout.flush() - - def do_time(self, argv): - - format = 'ascii' - logfile_path = lambda x: x - tail = None - which = 'total' - - short_opts = '?C:f:hp:t:' - - long_opts = [ - 'chdir=', - 'file=', - 'fmt=', - 'format=', - 'help', - 'prefix=', - 'tail=', - 'title=', - 'which=', - ] - - opts, args = getopt.getopt(argv[1:], short_opts, long_opts) - - for o, a in opts: - if o in ('-C', '--chdir'): - self.chdir = a - elif o in ('-f', '--file'): - self.config_file = a - elif o in ('--fmt', '--format'): - format = a - elif o in ('-?', '-h', '--help'): - self.do_help(['help', 'time']) - sys.exit(0) - elif o in ('-p', '--prefix'): - self.prefix = a - elif o in ('-t', '--tail'): - tail = int(a) - elif o in ('--title',): - self.title = a - elif o in ('--which',): - if not a in self.time_strings.keys(): - sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a)) - sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - which = a - - if self.config_file: - execfile(self.config_file, self.__dict__) - - if self.chdir: - os.chdir(self.chdir) - logfile_path = lambda x, c=self.chdir: os.path.join(c, x) - - if not args: - - pattern = '%s*.log' % self.prefix - args = self.args_to_files([pattern], tail) - - if not args: - if self.chdir: - directory = self.chdir - else: - directory = os.getcwd() - - sys.stderr.write('%s: time: No arguments specified.\n' % self.name) - sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) - sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) - sys.exit(1) - - else: - - args = self.args_to_files(args, tail) - - cwd_ = os.getcwd() + os.sep - - if format == 'ascii': - - columns = ("Total", "SConscripts", "SCons", "commands") - self.ascii_table(args, columns, self.get_debug_times, logfile_path) - - elif format == 'gnuplot': - - results = self.collect_results(args, self.get_debug_times, - self.time_strings[which]) - - self.gnuplot_results(results, fmt='%s %.6f') - - else: - - sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format)) - sys.exit(1) - -if __name__ == '__main__': - opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version']) - - ST = SConsTimer() - - for o, a in opts: - if o in ('-?', '-h', '--help'): - ST.do_help(['help']) - sys.exit(0) - elif o in ('-V', '--version'): - sys.stdout.write('scons-time version\n') - sys.exit(0) - - if not args: - sys.stderr.write('Type "%s help" for usage.\n' % ST.name) - sys.exit(1) - - ST.execute_subcommand(args) diff --git a/tools/scons/scons.py b/tools/scons/scons.py deleted file mode 100755 index 88276a47e0..0000000000 --- a/tools/scons/scons.py +++ /dev/null @@ -1,165 +0,0 @@ -#! /usr/bin/env python -# -# SCons - a Software Constructor -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/script/scons.py 3842 2008/12/20 22:59:52 scons" - -__version__ = "1.2.0" - -__build__ = "r3842" - -__buildsys__ = "scons-dev" - -__date__ = "2008/12/20 22:59:52" - -__developer__ = "scons" - -import os -import os.path -import sys - -############################################################################## -# BEGIN STANDARD SCons SCRIPT HEADER -# -# This is the cut-and-paste logic so that a self-contained script can -# interoperate correctly with different SCons versions and installation -# locations for the engine. If you modify anything in this section, you -# should also change other scripts that use this same header. -############################################################################## - -# Strip the script directory from sys.path() so on case-insensitive -# (WIN32) systems Python doesn't think that the "scons" script is the -# "SCons" package. Replace it with our own library directories -# (version-specific first, in case they installed by hand there, -# followed by generic) so we pick up the right version of the build -# engine modules if they're in either directory. - -script_dir = sys.path[0] - -if script_dir in sys.path: - sys.path.remove(script_dir) - -libs = [] - -if os.environ.has_key("SCONS_LIB_DIR"): - libs.append(os.environ["SCONS_LIB_DIR"]) - -local_version = 'scons-local-' + __version__ -local = 'scons-local' -if script_dir: - local_version = os.path.join(script_dir, local_version) - local = os.path.join(script_dir, local) -libs.append(os.path.abspath(local_version)) -libs.append(os.path.abspath(local)) - -scons_version = 'scons-%s' % __version__ - -prefs = [] - -if sys.platform == 'win32': - # sys.prefix is (likely) C:\Python*; - # check only C:\Python*. - prefs.append(sys.prefix) - prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) -else: - # On other (POSIX) platforms, things are more complicated due to - # the variety of path names and library locations. Try to be smart - # about it. - if script_dir == 'bin': - # script_dir is `pwd`/bin; - # check `pwd`/lib/scons*. - prefs.append(os.getcwd()) - else: - if script_dir == '.' or script_dir == '': - script_dir = os.getcwd() - head, tail = os.path.split(script_dir) - if tail == "bin": - # script_dir is /foo/bin; - # check /foo/lib/scons*. - prefs.append(head) - - head, tail = os.path.split(sys.prefix) - if tail == "usr": - # sys.prefix is /foo/usr; - # check /foo/usr/lib/scons* first, - # then /foo/usr/local/lib/scons*. - prefs.append(sys.prefix) - prefs.append(os.path.join(sys.prefix, "local")) - elif tail == "local": - h, t = os.path.split(head) - if t == "usr": - # sys.prefix is /foo/usr/local; - # check /foo/usr/local/lib/scons* first, - # then /foo/usr/lib/scons*. - prefs.append(sys.prefix) - prefs.append(head) - else: - # sys.prefix is /foo/local; - # check only /foo/local/lib/scons*. - prefs.append(sys.prefix) - else: - # sys.prefix is /foo (ends in neither /usr or /local); - # check only /foo/lib/scons*. - prefs.append(sys.prefix) - - temp = map(lambda x: os.path.join(x, 'lib'), prefs) - temp.extend(map(lambda x: os.path.join(x, - 'lib', - 'python' + sys.version[:3], - 'site-packages'), - prefs)) - prefs = temp - - # Add the parent directory of the current python's library to the - # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, - # not /usr/lib. - try: - libpath = os.__file__ - except AttributeError: - pass - else: - # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. - libpath, tail = os.path.split(libpath) - # Split /usr/libfoo/python* to /usr/libfoo - libpath, tail = os.path.split(libpath) - # Check /usr/libfoo/scons*. - prefs.append(libpath) - -# Look first for 'scons-__version__' in all of our preference libs, -# then for 'scons'. -libs.extend(map(lambda x: os.path.join(x, scons_version), prefs)) -libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs)) - -sys.path = libs + sys.path - -############################################################################## -# END STANDARD SCons SCRIPT HEADER -############################################################################## - -if __name__ == "__main__": - import SCons.Script - # this does all the work, and calls sys.exit - # with the proper exit status when done. - SCons.Script.main() diff --git a/tools/scons/sconsign.py b/tools/scons/sconsign.py deleted file mode 100755 index a33b7b4221..0000000000 --- a/tools/scons/sconsign.py +++ /dev/null @@ -1,502 +0,0 @@ -#! /usr/bin/env python -# -# SCons - a Software Constructor -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "src/script/sconsign.py 3842 2008/12/20 22:59:52 scons" - -__version__ = "1.2.0" - -__build__ = "r3842" - -__buildsys__ = "scons-dev" - -__date__ = "2008/12/20 22:59:52" - -__developer__ = "scons" - -import os -import os.path -import sys -import time - -############################################################################## -# BEGIN STANDARD SCons SCRIPT HEADER -# -# This is the cut-and-paste logic so that a self-contained script can -# interoperate correctly with different SCons versions and installation -# locations for the engine. If you modify anything in this section, you -# should also change other scripts that use this same header. -############################################################################## - -# Strip the script directory from sys.path() so on case-insensitive -# (WIN32) systems Python doesn't think that the "scons" script is the -# "SCons" package. Replace it with our own library directories -# (version-specific first, in case they installed by hand there, -# followed by generic) so we pick up the right version of the build -# engine modules if they're in either directory. - -script_dir = sys.path[0] - -if script_dir in sys.path: - sys.path.remove(script_dir) - -libs = [] - -if os.environ.has_key("SCONS_LIB_DIR"): - libs.append(os.environ["SCONS_LIB_DIR"]) - -local_version = 'scons-local-' + __version__ -local = 'scons-local' -if script_dir: - local_version = os.path.join(script_dir, local_version) - local = os.path.join(script_dir, local) -libs.append(os.path.abspath(local_version)) -libs.append(os.path.abspath(local)) - -scons_version = 'scons-%s' % __version__ - -prefs = [] - -if sys.platform == 'win32': - # sys.prefix is (likely) C:\Python*; - # check only C:\Python*. - prefs.append(sys.prefix) - prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) -else: - # On other (POSIX) platforms, things are more complicated due to - # the variety of path names and library locations. Try to be smart - # about it. - if script_dir == 'bin': - # script_dir is `pwd`/bin; - # check `pwd`/lib/scons*. - prefs.append(os.getcwd()) - else: - if script_dir == '.' or script_dir == '': - script_dir = os.getcwd() - head, tail = os.path.split(script_dir) - if tail == "bin": - # script_dir is /foo/bin; - # check /foo/lib/scons*. - prefs.append(head) - - head, tail = os.path.split(sys.prefix) - if tail == "usr": - # sys.prefix is /foo/usr; - # check /foo/usr/lib/scons* first, - # then /foo/usr/local/lib/scons*. - prefs.append(sys.prefix) - prefs.append(os.path.join(sys.prefix, "local")) - elif tail == "local": - h, t = os.path.split(head) - if t == "usr": - # sys.prefix is /foo/usr/local; - # check /foo/usr/local/lib/scons* first, - # then /foo/usr/lib/scons*. - prefs.append(sys.prefix) - prefs.append(head) - else: - # sys.prefix is /foo/local; - # check only /foo/local/lib/scons*. - prefs.append(sys.prefix) - else: - # sys.prefix is /foo (ends in neither /usr or /local); - # check only /foo/lib/scons*. - prefs.append(sys.prefix) - - temp = map(lambda x: os.path.join(x, 'lib'), prefs) - temp.extend(map(lambda x: os.path.join(x, - 'lib', - 'python' + sys.version[:3], - 'site-packages'), - prefs)) - prefs = temp - - # Add the parent directory of the current python's library to the - # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, - # not /usr/lib. - try: - libpath = os.__file__ - except AttributeError: - pass - else: - # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. - libpath, tail = os.path.split(libpath) - # Split /usr/libfoo/python* to /usr/libfoo - libpath, tail = os.path.split(libpath) - # Check /usr/libfoo/scons*. - prefs.append(libpath) - -# Look first for 'scons-__version__' in all of our preference libs, -# then for 'scons'. -libs.extend(map(lambda x: os.path.join(x, scons_version), prefs)) -libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs)) - -sys.path = libs + sys.path - -############################################################################## -# END STANDARD SCons SCRIPT HEADER -############################################################################## - -import cPickle -import imp -import string -import whichdb - -import SCons.SConsign - -def my_whichdb(filename): - if filename[-7:] == ".dblite": - return "SCons.dblite" - try: - f = open(filename + ".dblite", "rb") - f.close() - return "SCons.dblite" - except IOError: - pass - return _orig_whichdb(filename) - -_orig_whichdb = whichdb.whichdb -whichdb.whichdb = my_whichdb - -def my_import(mname): - if '.' in mname: - i = string.rfind(mname, '.') - parent = my_import(mname[:i]) - fp, pathname, description = imp.find_module(mname[i+1:], - parent.__path__) - else: - fp, pathname, description = imp.find_module(mname) - return imp.load_module(mname, fp, pathname, description) - -class Flagger: - default_value = 1 - def __setitem__(self, item, value): - self.__dict__[item] = value - self.default_value = 0 - def __getitem__(self, item): - return self.__dict__.get(item, self.default_value) - -Do_Call = None -Print_Directories = [] -Print_Entries = [] -Print_Flags = Flagger() -Verbose = 0 -Readable = 0 - -def default_mapper(entry, name): - try: - val = eval("entry."+name) - except: - val = None - return str(val) - -def map_action(entry, name): - try: - bact = entry.bact - bactsig = entry.bactsig - except AttributeError: - return None - return '%s [%s]' % (bactsig, bact) - -def map_timestamp(entry, name): - try: - timestamp = entry.timestamp - except AttributeError: - timestamp = None - if Readable and timestamp: - return "'" + time.ctime(timestamp) + "'" - else: - return str(timestamp) - -def map_bkids(entry, name): - try: - bkids = entry.bsources + entry.bdepends + entry.bimplicit - bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs - except AttributeError: - return None - result = [] - for i in xrange(len(bkids)): - result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) - if result == []: - return None - return string.join(result, "\n ") - -map_field = { - 'action' : map_action, - 'timestamp' : map_timestamp, - 'bkids' : map_bkids, -} - -map_name = { - 'implicit' : 'bkids', -} - -def field(name, entry, verbose=Verbose): - if not Print_Flags[name]: - return None - fieldname = map_name.get(name, name) - mapper = map_field.get(fieldname, default_mapper) - val = mapper(entry, name) - if verbose: - val = name + ": " + val - return val - -def nodeinfo_raw(name, ninfo, prefix=""): - # This just formats the dictionary, which we would normally use str() - # to do, except that we want the keys sorted for deterministic output. - d = ninfo.__dict__ - try: - keys = ninfo.field_list + ['_version_id'] - except AttributeError: - keys = d.keys() - keys.sort() - l = [] - for k in keys: - l.append('%s: %s' % (repr(k), repr(d.get(k)))) - if '\n' in name: - name = repr(name) - return name + ': {' + string.join(l, ', ') + '}' - -def nodeinfo_cooked(name, ninfo, prefix=""): - try: - field_list = ninfo.field_list - except AttributeError: - field_list = [] - f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v) - if '\n' in name: - name = repr(name) - outlist = [name+':'] + filter(None, map(f, field_list)) - if Verbose: - sep = '\n ' + prefix - else: - sep = ' ' - return string.join(outlist, sep) - -nodeinfo_string = nodeinfo_cooked - -def printfield(name, entry, prefix=""): - outlist = field("implicit", entry, 0) - if outlist: - if Verbose: - print " implicit:" - print " " + outlist - outact = field("action", entry, 0) - if outact: - if Verbose: - print " action: " + outact - else: - print " " + outact - -def printentries(entries, location): - if Print_Entries: - for name in Print_Entries: - try: - entry = entries[name] - except KeyError: - sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location)) - else: - try: - ninfo = entry.ninfo - except AttributeError: - print name + ":" - else: - print nodeinfo_string(name, entry.ninfo) - printfield(name, entry.binfo) - else: - names = entries.keys() - names.sort() - for name in names: - entry = entries[name] - try: - ninfo = entry.ninfo - except AttributeError: - print name + ":" - else: - print nodeinfo_string(name, entry.ninfo) - printfield(name, entry.binfo) - -class Do_SConsignDB: - def __init__(self, dbm_name, dbm): - self.dbm_name = dbm_name - self.dbm = dbm - - def __call__(self, fname): - # The *dbm modules stick their own file suffixes on the names - # that are passed in. This is causes us to jump through some - # hoops here to be able to allow the user - try: - # Try opening the specified file name. Example: - # SPECIFIED OPENED BY self.dbm.open() - # --------- ------------------------- - # .sconsign => .sconsign.dblite - # .sconsign.dblite => .sconsign.dblite.dblite - db = self.dbm.open(fname, "r") - except (IOError, OSError), e: - print_e = e - try: - # That didn't work, so try opening the base name, - # so that if the actually passed in 'sconsign.dblite' - # (for example), the dbm module will put the suffix back - # on for us and open it anyway. - db = self.dbm.open(os.path.splitext(fname)[0], "r") - except (IOError, OSError): - # That didn't work either. See if the file name - # they specified just exists (independent of the dbm - # suffix-mangling). - try: - open(fname, "r") - except (IOError, OSError), e: - # Nope, that file doesn't even exist, so report that - # fact back. - print_e = e - sys.stderr.write("sconsign: %s\n" % (print_e)) - return - except KeyboardInterrupt: - raise - except cPickle.UnpicklingError: - sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname)) - return - except Exception, e: - sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) - return - - if Print_Directories: - for dir in Print_Directories: - try: - val = db[dir] - except KeyError: - sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0])) - else: - self.printentries(dir, val) - else: - keys = db.keys() - keys.sort() - for dir in keys: - self.printentries(dir, db[dir]) - - def printentries(self, dir, val): - print '=== ' + dir + ':' - printentries(cPickle.loads(val), dir) - -def Do_SConsignDir(name): - try: - fp = open(name, 'rb') - except (IOError, OSError), e: - sys.stderr.write("sconsign: %s\n" % (e)) - return - try: - sconsign = SCons.SConsign.Dir(fp) - except KeyboardInterrupt: - raise - except cPickle.UnpicklingError: - sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name)) - return - except Exception, e: - sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)) - return - printentries(sconsign.entries, args[0]) - -############################################################################## - -import getopt - -helpstr = """\ -Usage: sconsign [OPTIONS] FILE [...] -Options: - -a, --act, --action Print build action information. - -c, --csig Print content signature information. - -d DIR, --dir=DIR Print only info about DIR. - -e ENTRY, --entry=ENTRY Print only info about ENTRY. - -f FORMAT, --format=FORMAT FILE is in the specified FORMAT. - -h, --help Print this message and exit. - -i, --implicit Print implicit dependency information. - -r, --readable Print timestamps in human-readable form. - --raw Print raw Python object representations. - -s, --size Print file sizes. - -t, --timestamp Print timestamp information. - -v, --verbose Verbose, describe each field. -""" - -opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", - ['act', 'action', - 'csig', 'dir=', 'entry=', - 'format=', 'help', 'implicit', - 'raw', 'readable', - 'size', 'timestamp', 'verbose']) - - -for o, a in opts: - if o in ('-a', '--act', '--action'): - Print_Flags['action'] = 1 - elif o in ('-c', '--csig'): - Print_Flags['csig'] = 1 - elif o in ('-d', '--dir'): - Print_Directories.append(a) - elif o in ('-e', '--entry'): - Print_Entries.append(a) - elif o in ('-f', '--format'): - Module_Map = {'dblite' : 'SCons.dblite', - 'sconsign' : None} - dbm_name = Module_Map.get(a, a) - if dbm_name: - try: - dbm = my_import(dbm_name) - except: - sys.stderr.write("sconsign: illegal file format `%s'\n" % a) - print helpstr - sys.exit(2) - Do_Call = Do_SConsignDB(a, dbm) - else: - Do_Call = Do_SConsignDir - elif o in ('-h', '--help'): - print helpstr - sys.exit(0) - elif o in ('-i', '--implicit'): - Print_Flags['implicit'] = 1 - elif o in ('--raw',): - nodeinfo_string = nodeinfo_raw - elif o in ('-r', '--readable'): - Readable = 1 - elif o in ('-s', '--size'): - Print_Flags['size'] = 1 - elif o in ('-t', '--timestamp'): - Print_Flags['timestamp'] = 1 - elif o in ('-v', '--verbose'): - Verbose = 1 - -if Do_Call: - for a in args: - Do_Call(a) -else: - for a in args: - dbm_name = whichdb.whichdb(a) - if dbm_name: - Map_Module = {'SCons.dblite' : 'dblite'} - dbm = my_import(dbm_name) - Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) - else: - Do_SConsignDir(a) - -sys.exit(0) diff --git a/tools/wafadmin/Tools/node_addon.py b/tools/wafadmin/Tools/node_addon.py index afe25951fe..6ea65aec36 100644 --- a/tools/wafadmin/Tools/node_addon.py +++ b/tools/wafadmin/Tools/node_addon.py @@ -1,5 +1,5 @@ import os -import TaskGen, Utils, Utils, Runner, Options, Build +import TaskGen, Utils, Runner, Options, Build from TaskGen import extension, taskgen, before, after, feature from Configure import conf, conftest @@ -26,6 +26,7 @@ def detect(conf): conf.env['PREFIX_NODE'] = get_prefix() prefix = conf.env['PREFIX_NODE'] lib = join(prefix, 'lib') + nodebin = join(prefix, 'bin', 'node') conf.env['LIBPATH_NODE'] = lib conf.env['CPPPATH_NODE'] = join(prefix, 'include', 'node') @@ -49,14 +50,20 @@ def detect(conf): found = os.path.exists(conf.env['NODE_PATH']) conf.check_message('node path', '', found, conf.env['NODE_PATH']) - found = os.path.exists(join(prefix, 'bin', 'node')) + found = os.path.exists(nodebin) conf.check_message('node prefix', '', found, prefix) ## On Cygwin we need to link to the generated symbol definitions if Options.platform.startswith('cygwin'): conf.env['LIB_NODE'] = 'node' ## On Mac OSX we need to use mac bundles - if Options.platform == 'darwin': conf.check_tool('osx') + if Options.platform == 'darwin': + if 'i386' in Utils.cmd_output(['file', nodebin]): + conf.env.append_value('CPPFLAGS_NODE', ['-arch', 'i386']) + conf.env.append_value('CXXFLAGS_NODE', ['-arch', 'i386']) + conf.env.append_value('LINKFLAGS', ['-arch', 'i386']) + conf.env['DEST_CPU'] = 'i386' + conf.check_tool('osx') def get_node_path(): join = os.path.join diff --git a/vcbuild.bat b/vcbuild.bat index f6d0fefe37..54f579dce1 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -91,7 +91,7 @@ goto run :msbuild-found @rem Build the sln with msbuild. -msbuild node.sln /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +msbuild node.sln /m /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto msi @@ -104,7 +104,7 @@ python "%~dp0tools\getnodeversion.py" > "%temp%\node_version.txt" if not errorlevel 0 echo Cannot determine current version of node.js & goto exit for /F "tokens=*" %%i in (%temp%\node_version.txt) do set NODE_VERSION=%%i heat dir deps\npm -var var.NPMSourceDir -dr NodeModulesFolder -cg NPMFiles -gg -template fragment -nologo -out npm.wxs -msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /t:Clean,Build /p:Configuration=%config% /p:NodeVersion=%NODE_VERSION% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build /p:Configuration=%config% /p:NodeVersion=%NODE_VERSION% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto run diff --git a/wscript b/wscript deleted file mode 100644 index 2b04358559..0000000000 --- a/wscript +++ /dev/null @@ -1,1045 +0,0 @@ -#!/usr/bin/env python - -# Copyright Joyent, Inc. and other Node contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to the -# following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -# USE OR OTHER DEALINGS IN THE SOFTWARE. - -import re -import Options -import sys, os, shutil, glob -import Utils -from Utils import cmd_output -from os.path import join, dirname, abspath, normpath -from logging import fatal - -cwd = os.getcwd() -APPNAME="node.js" - -# Use the directory that this file is found in to find the tools -# directory where the js2c.py file can be found. -sys.path.append(sys.argv[0] + '/tools'); -import js2c - -if sys.platform.startswith("cygwin"): - print "cygwin not supported" - sys.exit(1) - -srcdir = '.' -blddir = 'out' -supported_archs = ('arm', 'ia32', 'x64') # 'mips' supported by v8, but not node - -jobs=1 -if os.environ.has_key('JOBS'): - jobs = int(os.environ['JOBS']) - -def safe_path(path): - return path.replace("\\", "/") - -def canonical_cpu_type(arch): - m = {'x86': 'ia32', 'i386':'ia32', 'x86_64':'x64', 'amd64':'x64'} - if arch in m: arch = m[arch] - if not arch in supported_archs: - raise Exception("supported architectures are "+', '.join(supported_archs)+\ - " but NOT '" + arch + "'.") - return arch - -def set_options(opt): - # the gcc module provides a --debug-level option - opt.tool_options('compiler_cxx') - opt.tool_options('compiler_cc') - opt.tool_options('misc') - opt.add_option( '--libdir' - , action='store' - , type='string' - , default=False - , help='Install into this libdir [Release: ${PREFIX}/lib]' - ) - opt.add_option( '--debug' - , action='store_true' - , default=False - , help='Build debug variant [Release: False]' - , dest='debug' - ) - opt.add_option( '--profile' - , action='store_true' - , default=False - , help='Enable profiling [Release: False]' - , dest='profile' - ) - opt.add_option( '--efence' - , action='store_true' - , default=False - , help='Build with -lefence for debugging [Release: False]' - , dest='efence' - ) - - opt.add_option( '--without-npm' - , action='store_true' - , default=False - , help='Don\'t install the bundled npm package manager [Release: False]' - , dest='without_npm' - ) - - opt.add_option( '--without-snapshot' - , action='store_true' - , default=False - , help='Build without snapshotting V8 libraries. You might want to set this for cross-compiling. [Release: False]' - , dest='without_snapshot' - ) - - opt.add_option( '--without-ssl' - , action='store_true' - , default=False - , help='Build without SSL' - , dest='without_ssl' - ) - - - opt.add_option('--shared-v8' - , action='store_true' - , default=False - , help='Link to a shared V8 DLL instead of static linking' - , dest='shared_v8' - ) - - opt.add_option( '--shared-v8-includes' - , action='store' - , default=False - , help='Directory containing V8 header files' - , dest='shared_v8_includes' - ) - - opt.add_option( '--shared-v8-libpath' - , action='store' - , default=False - , help='A directory to search for the shared V8 DLL' - , dest='shared_v8_libpath' - ) - - opt.add_option( '--shared-v8-libname' - , action='store' - , default=False - , help="Alternative lib name to link to (default: 'v8')" - , dest='shared_v8_libname' - ) - - opt.add_option( '--openssl-includes' - , action='store' - , default=False - , help='A directory to search for the OpenSSL includes' - , dest='openssl_includes' - ) - - opt.add_option( '--openssl-libpath' - , action='store' - , default=False - , help="A directory to search for the OpenSSL libraries" - , dest='openssl_libpath' - ) - - opt.add_option( '--no-ssl2' - , action='store_true' - , default=False - , help="Disable OpenSSL v2" - , dest='openssl_nov2' - ) - - opt.add_option( '--gdb' - , action='store_true' - , default=False - , help="add gdb support" - , dest='use_gdbjit' - ) - - - opt.add_option( '--shared-zlib' - , action='store_true' - , default=False - , help='Link to a shared zlib DLL instead of static linking' - , dest='shared_zlib' - ) - - opt.add_option( '--shared-zlib-includes' - , action='store' - , default=False - , help='Directory containing zlib header files' - , dest='shared_zlib_includes' - ) - - opt.add_option( '--shared-zlib-libpath' - , action='store' - , default=False - , help='A directory to search for the shared zlib DLL' - , dest='shared_zlib_libpath' - ) - - - opt.add_option( '--shared-cares' - , action='store_true' - , default=False - , help='Link to a shared C-Ares DLL instead of static linking' - , dest='shared_cares' - ) - - opt.add_option( '--shared-cares-includes' - , action='store' - , default=False - , help='Directory containing C-Ares header files' - , dest='shared_cares_includes' - ) - - opt.add_option( '--shared-cares-libpath' - , action='store' - , default=False - , help='A directory to search for the shared C-Ares DLL' - , dest='shared_cares_libpath' - ) - - - opt.add_option( '--with-dtrace' - , action='store_true' - , default=False - , help='Build with DTrace (experimental)' - , dest='dtrace' - ) - - - opt.add_option( '--product-type' - , action='store' - , default='program' - , help='What kind of product to produce (program, cstaticlib '\ - 'or cshlib) [default: %default]' - , dest='product_type' - ) - - opt.add_option( '--dest-cpu' - , action='store' - , default=None - , help='CPU architecture to build for. Valid values are: '+\ - ', '.join(supported_archs) - , dest='dest_cpu' - ) - -def get_node_version(): - def get_define_value(lines, define): - for line in lines: - if define in line: - return line.split()[-1] #define <NAME> <VALUE> - - lines = open("src/node_version.h").readlines() - node_major_version = get_define_value(lines, 'NODE_MAJOR_VERSION') - node_minor_version = get_define_value(lines, 'NODE_MINOR_VERSION') - node_patch_version = get_define_value(lines, 'NODE_PATCH_VERSION') - node_is_release = get_define_value(lines, 'NODE_VERSION_IS_RELEASE') - - return "%s.%s.%s%s" % ( node_major_version, - node_minor_version, - node_patch_version, - node_is_release == "0" and "-pre" or "" - ) - - - -def configure(conf): - conf.check_tool('compiler_cxx') - if not conf.env.CXX: conf.fatal('c++ compiler not found') - conf.check_tool('compiler_cc') - if not conf.env.CC: conf.fatal('c compiler not found') - - o = Options.options - - if o.libdir: - conf.env['LIBDIR'] = o.libdir - else: - conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib' - - conf.env["USE_DEBUG"] = o.debug - # Snapshot building does noet seem to work on mingw32 - conf.env["SNAPSHOT_V8"] = not o.without_snapshot and not sys.platform.startswith("win32") - if sys.platform.startswith("sunos"): - conf.env["SNAPSHOT_V8"] = False - conf.env["USE_PROFILING"] = o.profile - - conf.env["USE_SHARED_V8"] = o.shared_v8 or o.shared_v8_includes or o.shared_v8_libpath or o.shared_v8_libname - conf.env["USE_SHARED_CARES"] = o.shared_cares or o.shared_cares_includes or o.shared_cares_libpath - conf.env["USE_SHARED_ZLIB"] = o.shared_zlib or o.shared_zlib_includes or o.shared_zlib_libpath - - conf.env["USE_GDBJIT"] = o.use_gdbjit - conf.env['USE_NPM'] = not o.without_npm - - if not conf.env["USE_SHARED_ZLIB"] and not sys.platform.startswith("win32"): - conf.env.append_value("LINKFLAGS", "-lz") - - conf.check(lib='dl', uselib_store='DL') - if not sys.platform.startswith("sunos") and not sys.platform.startswith("win32"): - conf.env.append_value("CCFLAGS", "-rdynamic") - conf.env.append_value("LINKFLAGS_DL", "-rdynamic") - - if 'bsd' in sys.platform: - conf.check(lib='kvm', uselib_store='KVM') - - #if Options.options.debug: - # conf.check(lib='profiler', uselib_store='PROFILER') - - if Options.options.dtrace: - if not sys.platform.startswith("sunos"): - conf.fatal('DTrace support only currently available on Solaris') - - conf.find_program('dtrace', var='DTRACE', mandatory=True) - conf.env["USE_DTRACE"] = True - conf.env.append_value("CXXFLAGS", "-DHAVE_DTRACE=1") - - if Options.options.efence: - conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE') - - if 'bsd' in sys.platform: - if not conf.check(lib="execinfo", - includes=['/usr/include', '/usr/local/include'], - libpath=['/usr/lib', '/usr/local/lib'], - uselib_store="EXECINFO"): - conf.fatal("Install the libexecinfo port from /usr/ports/devel/libexecinfo.") - - if not Options.options.without_ssl: - # Don't override explicitly supplied openssl paths with pkg-config results. - explicit_openssl = o.openssl_includes or o.openssl_libpath - - # Disable ssl v2 methods - if o.openssl_nov2: - conf.env.append_value("CPPFLAGS", "-DOPENSSL_NO_SSL2=1") - - if not explicit_openssl and conf.check_cfg(package='openssl', - args='--cflags --libs', - uselib_store='OPENSSL'): - Options.options.use_openssl = conf.env["USE_OPENSSL"] = True - conf.env.append_value("CPPFLAGS", "-DHAVE_OPENSSL=1") - else: - if o.openssl_libpath: - openssl_libpath = [o.openssl_libpath] - elif not sys.platform.startswith('win32'): - openssl_libpath = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/usr/sfw/lib'] - else: - openssl_libpath = [normpath(join(cwd, '../openssl'))] - - if o.openssl_includes: - openssl_includes = [o.openssl_includes] - elif not sys.platform.startswith('win32'): - openssl_includes = []; - else: - openssl_includes = [normpath(join(cwd, '../openssl/include'))]; - - openssl_lib_names = ['ssl', 'crypto'] - if sys.platform.startswith('win32'): - openssl_lib_names += ['ws2_32', 'gdi32'] - - libssl = conf.check_cc(lib=openssl_lib_names, - header_name='openssl/ssl.h', - function_name='SSL_library_init', - includes=openssl_includes, - libpath=openssl_libpath, - uselib_store='OPENSSL') - - libcrypto = conf.check_cc(lib='crypto', - header_name='openssl/crypto.h', - includes=openssl_includes, - libpath=openssl_libpath, - uselib_store='OPENSSL') - - if libcrypto and libssl: - conf.env["USE_OPENSSL"] = Options.options.use_openssl = True - conf.env.append_value("CPPFLAGS", "-DHAVE_OPENSSL=1") - elif sys.platform.startswith('win32'): - conf.fatal("Could not autodetect OpenSSL support. " + - "Use the --openssl-libpath and --openssl-includes options to set the search path. " + - "Use configure --without-ssl to disable this message.") - else: - conf.fatal("Could not autodetect OpenSSL support. " + - "Make sure OpenSSL development packages are installed. " + - "Use configure --without-ssl to disable this message.") - else: - Options.options.use_openssl = conf.env["USE_OPENSSL"] = False - - conf.check(lib='util', libpath=['/usr/lib', '/usr/local/lib'], - uselib_store='UTIL') - - # normalize DEST_CPU from --dest-cpu, DEST_CPU or built-in value - if Options.options.dest_cpu and Options.options.dest_cpu: - conf.env['DEST_CPU'] = canonical_cpu_type(Options.options.dest_cpu) - elif 'DEST_CPU' in os.environ and os.environ['DEST_CPU']: - conf.env['DEST_CPU'] = canonical_cpu_type(os.environ['DEST_CPU']) - elif 'DEST_CPU' in conf.env and conf.env['DEST_CPU']: - conf.env['DEST_CPU'] = canonical_cpu_type(conf.env['DEST_CPU']) - - have_librt = conf.check(lib='rt', uselib_store='RT') - - if sys.platform.startswith("sunos"): - code = """ - #include <ifaddrs.h> - int main(void) { - struct ifaddrs hello; - return 0; - } - """ - - if conf.check_cc(msg="Checking for ifaddrs on solaris", fragment=code): - conf.env.append_value('CPPFLAGS', '-DSUNOS_HAVE_IFADDRS') - - if not conf.check(lib='socket', uselib_store="SOCKET"): - conf.fatal("Cannot find socket library") - if not conf.check(lib='nsl', uselib_store="NSL"): - conf.fatal("Cannot find nsl library") - if not conf.check(lib='kstat', uselib_store="KSTAT"): - conf.fatal("Cannot find kstat library") - - if conf.env['USE_SHARED_V8']: - v8_includes = []; - if o.shared_v8_includes: v8_includes.append(o.shared_v8_includes); - - v8_libpath = []; - if o.shared_v8_libpath: v8_libpath.append(o.shared_v8_libpath); - - if not o.shared_v8_libname: o.shared_v8_libname = 'v8' - - if not conf.check_cxx(lib=o.shared_v8_libname, header_name='v8.h', - uselib_store='V8', - includes=v8_includes, - libpath=v8_libpath): - conf.fatal("Cannot find v8") - - if o.debug: - if not conf.check_cxx(lib=o.shared_v8_libname + '_g', header_name='v8.h', - uselib_store='V8_G', - includes=v8_includes, - libpath=v8_libpath): - conf.fatal("Cannot find v8_g") - - conf.define("HAVE_CONFIG_H", 1) - - if sys.platform.startswith("sunos"): - conf.env.append_value ('CCFLAGS', '-threads') - conf.env.append_value ('CXXFLAGS', '-threads') - #conf.env.append_value ('LINKFLAGS', ' -threads') - elif not sys.platform.startswith("win32"): - threadflags='-pthread' - conf.env.append_value ('CCFLAGS', threadflags) - conf.env.append_value ('CXXFLAGS', threadflags) - conf.env.append_value ('LINKFLAGS', threadflags) - if sys.platform.startswith("darwin"): - # used by platform_darwin_*.cc - conf.env.append_value('LINKFLAGS', ['-framework','Carbon']) - # cross compile for architecture specified by DEST_CPU - if 'DEST_CPU' in conf.env: - arch = conf.env['DEST_CPU'] - # map supported_archs to GCC names: - arch_mappings = {'ia32': 'i386', 'x64': 'x86_64'} - if arch in arch_mappings: - arch = arch_mappings[arch] - flags = ['-arch', arch] - conf.env.append_value('CCFLAGS', flags) - conf.env.append_value('CXXFLAGS', flags) - conf.env.append_value('LINKFLAGS', flags) - if 'DEST_CPU' in conf.env: - arch = conf.env['DEST_CPU'] - # TODO: -m32 is only available on 64 bit machines, so check host type - flags = None - if arch == 'ia32': - flags = '-m32' - if flags: - conf.env.append_value('CCFLAGS', flags) - conf.env.append_value('CXXFLAGS', flags) - conf.env.append_value('LINKFLAGS', flags) - - # LFS - conf.env.append_value('CPPFLAGS', '-D_LARGEFILE_SOURCE') - conf.env.append_value('CPPFLAGS', '-D_FILE_OFFSET_BITS=64') - - # Makes select on windows support more than 64 FDs - if sys.platform.startswith("win32"): - conf.env.append_value('CPPFLAGS', '-DFD_SETSIZE=1024'); - - ## needed for node_file.cc fdatasync - ## Strangely on OSX 10.6 the g++ doesn't see fdatasync but gcc does? - code = """ - #include <unistd.h> - int main(void) - { - int fd = 0; - fdatasync (fd); - return 0; - } - """ - if conf.check_cxx(msg="Checking for fdatasync(2) with c++", fragment=code): - conf.env.append_value('CPPFLAGS', '-DHAVE_FDATASYNC=1') - else: - conf.env.append_value('CPPFLAGS', '-DHAVE_FDATASYNC=0') - - # arch - conf.env.append_value('CPPFLAGS', '-DARCH="' + conf.env['DEST_CPU'] + '"') - - # platform - conf.env.append_value('CPPFLAGS', '-DPLATFORM="' + conf.env['DEST_OS'] + '"') - - # posix? - if not sys.platform.startswith('win'): - conf.env.append_value('CPPFLAGS', '-D__POSIX__=1') - - platform_file = "src/platform_%s.cc" % conf.env['DEST_OS'] - if os.path.exists(join(cwd, platform_file)): - Options.options.platform_file = True - conf.env["PLATFORM_FILE"] = platform_file - else: - Options.options.platform_file = False - conf.env["PLATFORM_FILE"] = "src/platform_none.cc" - - if conf.env['USE_PROFILING'] == True: - conf.env.append_value('CPPFLAGS', '-pg') - conf.env.append_value('LINKFLAGS', '-pg') - - if sys.platform.startswith("win32"): - conf.env.append_value('LIB', 'psapi') - conf.env.append_value('LIB', 'winmm') - # This enforces ws2_32 to be linked after crypto, otherwise the linker - # will run into undefined references from libcrypto.a - if not Options.options.use_openssl: - conf.env.append_value('LIB', 'ws2_32') - - conf.env.append_value('CPPFLAGS', '-Wno-unused-parameter'); - conf.env.append_value('CPPFLAGS', '-D_FORTIFY_SOURCE=2'); - - # Split off debug variant before adding variant specific defines - debug_env = conf.env.copy() - conf.set_env_name('Debug', debug_env) - - # Configure debug variant - conf.setenv('Debug') - debug_env.set_variant('Debug') - debug_env.append_value('CPPFLAGS', '-DDEBUG') - debug_compile_flags = ['-g', '-O0', '-Wall', '-Wextra'] - debug_env.append_value('CCFLAGS', debug_compile_flags) - debug_env.append_value('CXXFLAGS', debug_compile_flags) - conf.write_config_header("config.h") - - # Configure default variant - conf.setenv('Release') - default_compile_flags = ['-g', '-O3'] - conf.env.append_value('CCFLAGS', default_compile_flags) - conf.env.append_value('CXXFLAGS', default_compile_flags) - conf.write_config_header("config.h") - - -def v8_cmd(bld, variant): - scons = join(cwd, 'tools/scons/scons.py') - deps_src = join(bld.path.abspath(),"deps") - v8dir_src = join(deps_src,"v8") - - # NOTE: We want to compile V8 to export its symbols. I.E. Do not want - # -fvisibility=hidden. When using dlopen() it seems that the loaded DSO - # cannot see symbols in the executable which are hidden, even if the - # executable is statically linked together... - - # XXX Change this when v8 defaults x86_64 to native builds - # Possible values are (arm, ia32, x64, mips). - arch = "" - if bld.env['DEST_CPU']: - arch = "arch="+bld.env['DEST_CPU'] - - toolchain = "gcc" - - if variant == "Release": - mode = "release" - else: - mode = "debug" - - if bld.env["SNAPSHOT_V8"]: - snapshot = "snapshot=on" - else: - snapshot = "" - - cmd_R = sys.executable + ' "%s" -j %d -C "%s" -Y "%s" visibility=default mode=%s %s toolchain=%s library=static %s' - - cmd = cmd_R % ( scons - , Options.options.jobs - , safe_path(bld.srcnode.abspath(bld.env_of_name(variant))) - , safe_path(v8dir_src) - , mode - , arch - , toolchain - , snapshot - ) - - if bld.env["USE_GDBJIT"]: - cmd += ' gdbjit=on ' - - if sys.platform.startswith("sunos"): - cmd += ' toolchain=gcc strictaliasing=off' - - - - return ("echo '%s' && " % cmd) + cmd - - -def build_v8(bld): - v8 = bld.new_task_gen( - source = 'deps/v8/SConstruct ' - + bld.path.ant_glob('v8/include/*') - + bld.path.ant_glob('v8/src/*'), - target = bld.env["staticlib_PATTERN"] % "v8", - rule = v8_cmd(bld, "Release"), - before = "cxx", - install_path = None) - - v8.env.env = dict(os.environ) - v8.env.env['CC'] = sh_escape(bld.env['CC'][0]) - v8.env.env['CXX'] = sh_escape(bld.env['CXX'][0]) - - v8.uselib = "EXECINFO" - bld.env["CPPPATH_V8"] = "deps/v8/include" - t = join(bld.srcnode.abspath(bld.env_of_name("Release")), v8.target) - bld.env_of_name('Release').append_value("LINKFLAGS_V8", t) - - ### v8 debug - if bld.env["USE_DEBUG"]: - v8_debug = v8.clone("Debug") - v8_debug.rule = v8_cmd(bld, "Debug") - v8_debug.target = bld.env["staticlib_PATTERN"] % "v8_g" - v8_debug.uselib = "EXECINFO" - bld.env["CPPPATH_V8_G"] = "deps/v8/include" - t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), v8_debug.target) - bld.env_of_name('Debug').append_value("LINKFLAGS_V8_G", t) - - bld.install_files('${PREFIX}/include/node/', 'deps/v8/include/*.h') - -def sh_escape(s): - if sys.platform.startswith('win32'): - return '"' + s + '"' - else: - return s.replace("\\", "\\\\").replace("(","\\(").replace(")","\\)").replace(" ","\\ ") - -def uv_cmd(bld, variant): - srcdeps = join(bld.path.abspath(), "deps") - srcdir = join(srcdeps, "uv") - blddir = bld.srcnode.abspath(bld.env_of_name(variant)) + '/deps/uv' - # - # FIXME This is awful! We're copying the entire source directory into the - # build directory before each compile. This could be much improved by - # modifying libuv's build to send object files to a separate directory. - # - cmd = 'cp -r ' + sh_escape(srcdir) + '/* ' + sh_escape(blddir) - if not sys.platform.startswith('win32'): - make = ('if [ -z "$NODE_MAKE" ]; then NODE_MAKE=make; fi; ' - '$NODE_MAKE -C ' + sh_escape(blddir)) - else: - make = 'make -C ' + sh_escape(blddir) - return '%s && (%s clean) && (%s all)' % (cmd, make, make) - - -def build_uv(bld): - uv = bld.new_task_gen( - name = 'uv', - source = 'deps/uv/include/uv.h', - target = 'deps/uv/uv.a', - before = "cxx", - rule = uv_cmd(bld, 'Release') - ) - - uv.env.env = dict(os.environ) - uv.env.env['CC'] = sh_escape(bld.env['CC'][0]) - uv.env.env['CXX'] = sh_escape(bld.env['CXX'][0]) - - t = join(bld.srcnode.abspath(bld.env_of_name("Release")), uv.target) - bld.env_of_name('Release').append_value("LINKFLAGS_UV", t) - - if bld.env["USE_DEBUG"]: - uv_debug = uv.clone("Debug") - uv_debug.rule = uv_cmd(bld, 'Debug') - uv_debug.env.env = dict(os.environ) - - t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), uv_debug.target) - bld.env_of_name('Debug').append_value("LINKFLAGS_UV", t) - - bld.install_files('${PREFIX}/include/node/', 'deps/uv/include/*.h') - bld.install_files('${PREFIX}/include/node/uv-private', 'deps/uv/include/uv-private/*.h') - bld.install_files('${PREFIX}/include/node/ev', 'deps/uv/src/ev/*.h') - bld.install_files('${PREFIX}/include/node/c-ares', """ - deps/uv/include/ares.h - deps/uv/include/ares_version.h - """) - - -def build(bld): - ## This snippet is to show full commands as WAF executes - import Build - old = Build.BuildContext.exec_command - def exec_command(self, cmd, **kw): - if isinstance(cmd, list): print(" ".join(cmd)) - return old(self, cmd, **kw) - Build.BuildContext.exec_command = exec_command - - Options.options.jobs=jobs - product_type = Options.options.product_type - product_type_is_lib = product_type != 'program' - - print "DEST_OS: " + bld.env['DEST_OS'] - print "DEST_CPU: " + bld.env['DEST_CPU'] - print "Parallel Jobs: " + str(Options.options.jobs) - print "Product type: " + product_type - - build_uv(bld) - - if not bld.env['USE_SHARED_V8']: build_v8(bld) - - - ### http_parser - http_parser = bld.new_task_gen("cc") - http_parser.source = "deps/http_parser/http_parser.c" - http_parser.includes = "deps/http_parser/" - http_parser.name = "http_parser" - http_parser.target = "http_parser" - http_parser.install_path = None - if bld.env["USE_DEBUG"]: - http_parser.clone("Debug") - if product_type_is_lib: - http_parser.ccflags = '-fPIC' - - ### src/native.cc - def make_macros(loc, content): - f = open(loc, 'a') - f.write(content) - f.close - - macros_loc_debug = join( - bld.srcnode.abspath(bld.env_of_name("Debug")), - "macros.py" - ) - - macros_loc_default = join( - bld.srcnode.abspath(bld.env_of_name("Release")), - "macros.py" - ) - - ### We need to truncate the macros.py file - f = open(macros_loc_debug, 'w') - f.close - f = open(macros_loc_default, 'w') - f.close - - make_macros(macros_loc_debug, "") # leave debug(x) as is in debug build - # replace debug(x) with nothing in release build - make_macros(macros_loc_default, "macro debug(x) = ;\n") - make_macros(macros_loc_default, "macro assert(x) = ;\n") - - if not bld.env["USE_DTRACE"]: - probes = [ - 'DTRACE_HTTP_CLIENT_REQUEST', - 'DTRACE_HTTP_CLIENT_RESPONSE', - 'DTRACE_HTTP_SERVER_REQUEST', - 'DTRACE_HTTP_SERVER_RESPONSE', - 'DTRACE_NET_SERVER_CONNECTION', - 'DTRACE_NET_STREAM_END', - 'DTRACE_NET_SOCKET_READ', - 'DTRACE_NET_SOCKET_WRITE' - ] - - for probe in probes: - make_macros(macros_loc_default, "macro %s(x) = ;\n" % probe) - make_macros(macros_loc_debug, "macro %s(x) = ;\n" % probe) - - def javascript_in_c(task): - env = task.env - source = map(lambda x: x.srcpath(env), task.inputs) - targets = map(lambda x: x.srcpath(env), task.outputs) - source.append(macros_loc_default) - js2c.JS2C(source, targets) - - def javascript_in_c_debug(task): - env = task.env - source = map(lambda x: x.srcpath(env), task.inputs) - targets = map(lambda x: x.srcpath(env), task.outputs) - source.append(macros_loc_debug) - js2c.JS2C(source, targets) - - native_cc = bld.new_task_gen( - source='src/node.js ' + bld.path.ant_glob('lib/*.js'), - target="src/node_natives.h", - before="cxx", - install_path=None - ) - - # Add the rule /after/ cloning the debug - # This is a work around for an error had in python 2.4.3 (I'll paste the - # error that was had into the git commit meessage. git-blame to find out - # where.) - if bld.env["USE_DEBUG"]: - native_cc_debug = native_cc.clone("Debug") - native_cc_debug.rule = javascript_in_c_debug - - native_cc.rule = javascript_in_c_debug - - if bld.env["USE_DTRACE"]: - dtrace_usdt = bld.new_task_gen( - name = "dtrace_usdt", - source = "src/node_provider.d", - target = "src/node_provider.h", - rule = "%s -x nolibs -h -o ${TGT} -s ${SRC}" % (bld.env.DTRACE), - before = "cxx", - ) - - if bld.env["USE_DEBUG"]: - dtrace_usdt_g = dtrace_usdt.clone("Debug") - - bld.install_files('${LIBDIR}/dtrace', 'src/node.d') - - if sys.platform.startswith("sunos"): - # - # The USDT DTrace provider works slightly differently on Solaris than on - # the Mac; on Solaris, any objects that have USDT DTrace probes must be - # post-processed with the DTrace command. (This is not true on the - # Mac, which has first-class linker support for USDT probes.) On - # Solaris, we must therefore post-process our object files. Waf doesn't - # seem to really have a notion for this, so we inject a task after - # compiling and before linking, and then find all of the node object - # files and shuck them off to dtrace (which will modify them in place - # as appropriate). - # - def dtrace_postprocess(task): - abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant())) - objs = glob.glob(abspath + 'src/*.o') - source = task.inputs[0].srcpath(task.env) - target = task.outputs[0].srcpath(task.env) - cmd = '%s -G -x nolibs -s %s -o %s %s' % (task.env.DTRACE, - source, - target, - ' '.join(objs)) - Utils.exec_command(cmd) - - # - # ustack helpers do not currently work on MacOS. We currently only - # support 32-bit x86. - # - def dtrace_do_ustack(task): - abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant())) - source = task.inputs[0].srcpath(task.env) - target = task.outputs[0].srcpath(task.env) - cmd = '%s -32 -I../src -C -G -s %s -o %s' % (task.env.DTRACE, source, target) - Utils.exec_command(cmd) - - dtrace_ustack = bld.new_task_gen( - name = "dtrace_ustack-postprocess", - source = "src/v8ustack.d", - target = "v8ustack.o", - always = True, - before = "cxx_link", - after = "cxx", - rule = dtrace_do_ustack - ) - - dtracepost = bld.new_task_gen( - name = "dtrace_usdt-postprocess", - source = "src/node_provider.d", - target = "node_provider.o", - always = True, - before = "cxx_link", - after = "cxx", - rule = dtrace_postprocess - ) - - t = join(bld.srcnode.abspath(bld.env_of_name("Release")), dtracepost.target) - bld.env_of_name('Release').append_value('LINKFLAGS', t) - - t = join(bld.srcnode.abspath(bld.env_of_name("Release")), dtrace_ustack.target) - bld.env_of_name('Release').append_value('LINKFLAGS', t) - - # - # Note that for the same (mysterious) issue outlined above with respect - # to assigning the rule to native_cc/native_cc_debug, we must apply the - # rule to dtracepost/dtracepost_g only after they have been cloned. We - # also must put node_provider.o on the link line, but because we - # (apparently?) lack LINKFLAGS in debug, we (shamelessly) stowaway on - # LINKFLAGS_V8_G. - # - if bld.env["USE_DEBUG"]: - dtracepost_g = dtracepost.clone("Debug") - dtracepost_g.rule = dtrace_postprocess - t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), dtracepost.target) - bld.env_of_name("Debug").append_value('LINKFLAGS_V8_G', t) - - - ### node lib - node = bld.new_task_gen("cxx", product_type) - node.name = "node" - node.target = "node" - node.uselib = 'RT OPENSSL ZLIB CARES EXECINFO DL KVM SOCKET NSL KSTAT UTIL OPROFILE' - node.add_objects = 'http_parser' - if product_type_is_lib: - node.install_path = '${LIBDIR}' - else: - node.install_path = '${PREFIX}/bin' - node.chmod = 0755 - node.source = """ - src/node.cc - src/node_buffer.cc - src/node_javascript.cc - src/node_extensions.cc - src/node_http_parser.cc - src/node_constants.cc - src/node_file.cc - src/node_script.cc - src/node_os.cc - src/node_dtrace.cc - src/node_string.cc - src/node_zlib.cc - src/timer_wrap.cc - src/handle_wrap.cc - src/stream_wrap.cc - src/tcp_wrap.cc - src/udp_wrap.cc - src/pipe_wrap.cc - src/cares_wrap.cc - src/tty_wrap.cc - src/fs_event_wrap.cc - src/process_wrap.cc - src/v8_typed_array.cc - """ - - if bld.env["USE_DTRACE"]: - node.source += " src/node_dtrace.cc " - - if not sys.platform.startswith("win32"): - node.source += " src/node_signal_watcher.cc " - node.source += " src/node_stat_watcher.cc " - node.source += " src/node_io_watcher.cc " - - node.source += bld.env["PLATFORM_FILE"] - if not product_type_is_lib: - node.source = 'src/node_main.cc '+node.source - - if bld.env["USE_OPENSSL"]: node.source += " src/node_crypto.cc " - - node.includes = """ - src/ - deps/http_parser - deps/uv/include - deps/uv/src/ev - deps/uv/src/ares - """ - - if not bld.env["USE_SHARED_V8"]: node.includes += ' deps/v8/include ' - - if os.environ.has_key('RPATH'): - node.rpath = os.environ['RPATH'] - - if (sys.platform.startswith("win32")): - # Static libgcc - bld.env.append_value('LINKFLAGS', '-static-libgcc') - bld.env.append_value('LINKFLAGS', '-static-libstdc++') - - def subflags(program): - x = { 'CCFLAGS' : " ".join(program.env["CCFLAGS"]).replace('"', '\\"') - , 'CPPFLAGS' : " ".join(program.env["CPPFLAGS"]).replace('"', '\\"') - , 'LIBFLAGS' : " ".join(program.env["LIBFLAGS"]).replace('"', '\\"') - , 'PREFIX' : safe_path(program.env["PREFIX"]) - , 'VERSION' : get_node_version() - } - return x - - # process file.pc.in -> file.pc - - node_conf = bld.new_task_gen('subst', before="cxx") - node_conf.source = 'src/node_config.h.in' - node_conf.target = 'src/node_config.h' - node_conf.dict = subflags(node) - node_conf.install_path = '${PREFIX}/include/node' - - if bld.env["USE_DEBUG"]: - node_g = node.clone("Debug") - node_g.target = "node" - node_g.uselib += ' V8_G UV ' - node_g.install_path = None - - node_conf_g = node_conf.clone("Debug") - node_conf_g.dict = subflags(node_g) - node_conf_g.install_path = None - - # After creating the debug clone, append the V8 dep - node.uselib += ' V8 UV ' - - bld.install_files('${PREFIX}/include/node/', """ - config.h - src/node.h - src/node_object_wrap.h - src/node_buffer.h - src/node_version.h - """) - - # Only install the man page if it exists. - # Do 'make doc install' to build and install it. - if os.path.exists('doc/node.1'): - prefix = 'bsd' in sys.platform and '${PREFIX}' or '${PREFIX}/share' - bld.install_files(prefix + '/man/man1/', 'doc/node.1') - - bld.install_files('${PREFIX}/bin/', 'tools/node-waf', chmod=0755) - bld.install_files('${LIBDIR}/node/wafadmin', 'tools/wafadmin/*.py') - bld.install_files('${LIBDIR}/node/wafadmin/Tools', 'tools/wafadmin/Tools/*.py') - - if bld.env['USE_NPM']: - install_npm(bld) - -def install_npm(bld): - start_dir = bld.path.find_dir('deps/npm') - # The chmod=-1 is a Node hack. We changed WAF so that when chmod was set to - # -1 that the same permission in this tree are used. Necessary to get - # npm-cli.js to be executable without having to list every file in npm. - bld.install_files('${LIBDIR}/node_modules/npm', - start_dir.ant_glob('**/*'), - cwd=start_dir, - relative_trick=True, - chmod=-1) - bld.symlink_as('${PREFIX}/bin/npm', - '../lib/node_modules/npm/bin/npm-cli.js') - -def shutdown(): - Options.options.debug - # HACK to get binding.node out of build directory. - # better way to do this? - if Options.commands['configure']: - if not Options.options.use_openssl: - print "WARNING WARNING WARNING" - print "OpenSSL not found. Will compile Node without crypto support!" - - if not Options.options.platform_file: - print "WARNING: Platform not fully supported. Using src/platform_none.cc" - - elif not Options.commands['clean']: - if sys.platform.startswith("win32"): - if os.path.exists('out/Release/node.exe'): - os.system('cp out/Release/node.exe .') - if os.path.exists('out/Debug/node.exe'): - os.system('cp out/Debug/node.exe node_g.exe') - else: - if os.path.exists('out/Release/node') and not os.path.islink('node'): - os.symlink('out/Release/node', 'node') - if os.path.exists('out/Debug/node') and not os.path.islink('node_g'): - os.symlink('out/Debug/node', 'node_g') - else: - if sys.platform.startswith("win32"): - if os.path.exists('node.exe'): os.unlink('node.exe') - if os.path.exists('node_g.exe'): os.unlink('node_g.exe') - else: - if os.path.exists('node'): os.unlink('node') - if os.path.exists('node_g'): os.unlink('node_g') |